DEV Community

Namish Saxena
Namish Saxena

Posted on • Originally published at namishsaxena.com

How to generate docs for your MCP server in 30 seconds

You built an MCP server. Now someone asks: "What tools does it expose?"

And you point them at a README. Or tell them to run tools/list and stare at raw JSON. Or you copy-paste the schema into a doc that will drift from reality within a week.

There's a better way. Here's how to get this instead:

mcpspec generated docs

Install

# TypeScript
npm install @mcpspec-dev/typescript

# Python
pip install mcpspec-dev
Enter fullscreen mode Exit fullscreen mode

Add one line to your server

TypeScript:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { mcpspec } from "@mcpspec-dev/typescript";

const server = new McpServer({ name: "my-server", version: "1.0.0" });

// Your existing tools, resources, prompts — untouched

const app = mcpspec(server, {
  info: { title: "My MCP Server", version: "1.0.0" },
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

Python (FastMCP):

from mcp.server.fastmcp import FastMCP
from mcpspec_dev import McpSpec

mcp = FastMCP("my-server")

# Your existing tools — untouched

spec = McpSpec(mcp, info={"title": "My Server", "version": "1.0.0"})
mcp.run(transport="streamable-http")
Enter fullscreen mode Exit fullscreen mode

That's it. Your server now has three new endpoints.

What you get

/docs — interactive HTML documentation. Dark, light, and high-contrast themes. Collapsible tool/resource/prompt groups. Every input schema rendered with copy-to-clipboard. Generated automatically from what your server actually exposes, so it never drifts.

/mcpspec.yaml — a machine-readable spec in a standardized format. This is the portable artifact: commit it to your repo, feed it to a registry, use it for contract testing, hand it to a security team for audit.

/mcp — your original MCP endpoint, proxied through. Existing clients keep working with zero changes.

A complete example

Here's a real task manager server wired up with MCPSpec:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { mcpspec } from "@mcpspec-dev/typescript";
import { z } from "zod";

const server = new McpServer({ name: "task-manager", version: "1.0.0" });

server.tool("create_task", "Create a new task", {
  title: z.string().describe("Task title"),
  description: z.string().optional().describe("Optional details"),
  priority: z.enum(["low", "medium", "high"]).default("medium"),
}, async ({ title, description, priority }) => {
  // ... your implementation
  return { content: [{ type: "text", text: `Task created: ${title}` }] };
});

server.tool("list_tasks", "List all tasks", {
  status: z.enum(["pending", "done", "all"]).default("all"),
}, async ({ status }) => {
  // ... your implementation
  return { content: [{ type: "text", text: "[]" }] };
});

const app = mcpspec(server, {
  info: {
    title: "Task Manager",
    version: "1.0.0",
    description: "Create and track tasks via MCP",
    repository: "https://github.com/you/task-manager",
    license: "MIT",
  },
  groups: {
    "Tasks": ["create_task", "list_tasks"],
  },
  examples: {
    create_task: [{
      title: "Create a high-priority task",
      input: {
        title: "Fix production bug",
        description: "Users seeing 500 errors on /api/checkout",
        priority: "high",
      },
    }],
  },
});

app.listen(3000, () => {
  console.log("Docs: http://localhost:3000/docs");
  console.log("Spec: http://localhost:3000/mcpspec.yaml");
});
Enter fullscreen mode Exit fullscreen mode

Groups let you organize tools into sections in the docs UI. Examples show up inline next to each tool's schema. Both are optional — the bare minimum is just info.title and info.version.

The spec file

The /mcpspec.yaml output looks like this:

mcpspec: 0.1.0
$schema: "https://mcpspec.dev/schema/0.1.0.json"
info:
  name: task-manager
  version: "1.0.0"
  title: Task Manager
  description: Create and track tasks via MCP
tools:
  - name: create_task
    description: Create a new task
    inputSchema:
      type: object
      properties:
        title:
          type: string
          description: Task title
        priority:
          type: string
          enum: [low, medium, high]
          default: medium
      required: [title]
  - name: list_tasks
    description: List all tasks
    inputSchema:
      type: object
      properties:
        status:
          type: string
          enum: [pending, done, all]
          default: all
resources: []
prompts: []
Enter fullscreen mode Exit fullscreen mode

Commit this file to your repo. Now any agent, tool, or developer can understand your server's surface area without running it.

Filtering sensitive tools

Not every tool should be public. Use exclude with glob patterns to keep internal capabilities out of the spec:

const app = mcpspec(server, {
  info: { title: "My Server", version: "1.0.0" },
  exclude: ["internal_*", "admin_*"],
});
Enter fullscreen mode Exit fullscreen mode

Or use include for allowlist mode — only the tools you explicitly name appear in the docs:

const app = mcpspec(server, {
  info: { title: "My Server", version: "1.0.0" },
  include: ["get_*", "list_*", "search_*"],
});
Enter fullscreen mode Exit fullscreen mode

MCPSpec introspects via in-memory transport and only calls tools/list, resources/list, and prompts/list. It never executes tools or reads resource content. Your auth layer is completely bypassed during introspection — which means the docs endpoint stays accessible even when the MCP endpoint requires a token.

Python (low-level Server API)

If you're not using FastMCP:

from mcp.server.lowlevel import Server
from mcpspec_dev import McpSpec
import uvicorn

server = Server("my-server")

# Register tools via @server.list_tools(), @server.call_tool(), etc.

spec = McpSpec(server, info={"title": "My Server", "version": "1.0.0"})
app = spec.create_app()
uvicorn.run(app, port=3000)
Enter fullscreen mode Exit fullscreen mode

Why this exists

REST APIs had the same problem before OpenAPI. Every API was documented in its own format, or not at all. OpenAPI didn't fix a broken ecosystem — it gave a working one a standard that unlocked everything else: code generators, interactive explorers, contract tests, API registries.

MCP hit 10,000 public servers in under two years. The protocol is solid. But right now every server describes itself differently, or not at all. mcpspec.yaml is the portable spec format that changes that.

The project is MIT licensed. TypeScript on npm, Python on PyPI, source on GitHub.

If you try it and something's missing, open an issue. The spec format is at v0.1.0 — early enough that real usage shapes what it becomes.

Top comments (0)