{"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you ship an MCP (Model Context Protocol) server, that server is an API surface of its own: tools with input schemas, prompts with arguments, resources with URIs."," ","AI agents discover all of it at runtime, but the humans evaluating your API usually can't, because that surface lives only in the server code."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/docs/realm/content/api-docs/openapi-extensions/x-mcp"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x-mcp"]}," OpenAPI extension"]}," records the MCP server's capabilities in the same OpenAPI description as the rest of your API."," ","You can then render it for humans with ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/docs/realm"},"children":["Redocly Realm"]},", next to your API reference."," ","The new experimental ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"../docs/cli/commands/introspect-mcp"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["introspect-mcp"]}]}," command in Redocly CLI fills that extension in for you, by asking the server itself."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"ask-the-server-not-the-source-code","__idx":0},"children":["Ask the server, not the source code"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Point the command at a running MCP server and tell it which file to write:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"npx @redocly/cli@latest introspect-mcp https://learn.microsoft.com/api/mcp -o openapi.yaml\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["That's the public MCP server of Microsoft Learn, Microsoft's documentation and training platform, so you can run this exact command right now."," ","The CLI connects over Streamable HTTP, falling back to the legacy HTTP+SSE transport for older servers."," ","It negotiates the protocol version, lists every tool, prompt, and resource — following pagination — and writes the result."," ","If ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["openapi.yaml"]}," doesn't exist yet, it's scaffolded from the server's own name, version, and instructions."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A trimmed excerpt from a real run:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"yaml","header":{"controls":{"copy":{}}},"source":"x-mcp:\n  protocolVersion: '2025-06-18'\n  servers:\n    - url: https://learn.microsoft.com/api/mcp\n  capabilities:\n    # ...the logging, prompts, and resources capabilities\n    tools:\n      listChanged: true\n  tools:\n    - name: microsoft_docs_search\n      title: Microsoft Docs Search\n      # ...the tool's long description\n      inputSchema:\n        type: object\n        properties:\n          query:\n            description: >-\n              a query or topic about Microsoft/Azure products, services,\n              platforms, developer tools, frameworks, or APIs\n            type: string\n            default: null\n      # ...the tool's outputSchema, and the other two tools\n","lang":"yaml"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For servers that require authentication, pass headers the same way you would with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["curl"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"npx @redocly/cli@latest introspect-mcp https://example.com/mcp -H \"Authorization: Bearer $MCP_TOKEN\" -o openapi.yaml\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"local-servers-work-too","__idx":1},"children":["Local servers work too"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Most published MCP servers packages you launch with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["npx"]}," instead of HTTP endpoints."," ","The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--command"]}," option starts one as a local process and introspects it over stdio:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"npx @redocly/cli@latest introspect-mcp --command \"npx -y @modelcontextprotocol/server-everything\" -o openapi.yaml\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Running that against the MCP reference server records 13 tools, 4 prompts, and 7 resources."," ","The spawned process inherits your environment, so a server that reads its API key from an environment variable behaves exactly as it does in your shell."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"refresh-without-losing-your-edits","__idx":2},"children":["Refresh without losing your edits"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The command updates the description in place: your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["info"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["paths"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["servers"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["components"]}," stay untouched, and only the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x-mcp"]}," section changes."," ","On every refresh, the tool, prompt, and resource lists are replaced with what the server reports."," ","This way, renamed and removed entries are cleaned up."," ","The annotations the MCP protocol doesn't carry are preserved by entry name: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tags"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["security"]}," on tools, prompts, and resources, and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["example"]}," on prompt arguments."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Suppose the ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"https://cafe.redocly.com/openapi/cafe"},"children":["Redocly Cafe API"]}," shipped an MCP server for order management."," ","Its OpenAPI description already defines an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["OAuth2"]}," security scheme and an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Orders"]}," tag, so you annotate the introspected tool to match:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"yaml","header":{"controls":{"copy":{}}},"source":"x-mcp:\n  tools:\n    - name: orders/create\n      description: Create an order.\n      inputSchema:\n        type: object\n        properties:\n          customerName:\n            type: string\n        required:\n          - customerName\n      tags:\n        - Orders\n      security:\n        - OAuth2:\n            - orders:write\n","lang":"yaml"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When the server's schemas or descriptions change, rerun the command: the introspected data is updated, and your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tags"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["security"]}," are kept."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"fail-the-build-when-the-docs-drift","__idx":3},"children":["Fail the build when the docs drift"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["MCP servers change, and the documented snapshot gets outdated."," ","The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--check"]}," flag makes the command usable as a CI check: it compares the file with what an introspection run would produce, writes nothing, and exits with code ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["1"]}," when they differ."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"npx @redocly/cli@latest introspect-mcp https://learn.microsoft.com/api/mcp -o openapi.yaml --check\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For example, after the server renames a tool, the command reports:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"openapi.yaml is out of date with the MCP server:\n  - tools - added: microsoft_docs_search; removed: microsoft_docs_search_v1\n\nRun the command without --check to update it.\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Add this command to your CI pipeline to fail the build when the published description no longer matches the MCP server."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"from-yaml-to-rendered-docs","__idx":4},"children":["From YAML to rendered docs"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Once ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x-mcp"]}," is in the description, it's regular OpenAPI: lint it, bundle it, version it in Git."," ","And ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/docs/realm"},"children":["Redocly Realm"]}," renders the extension as MCP documentation right next to your API reference."," ","The tools, prompts, and resources you just introspected become reader-facing docs — no extra authoring step."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To learn more, see the ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"../docs/cli/commands/introspect-mcp"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["introspect-mcp"]}," documentation"]}," and the ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"/docs/realm/content/api-docs/openapi-extensions/x-mcp"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x-mcp"]}," extension reference"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Have you tried it against your own MCP server? ",{"$$mdtype":"Tag","name":"Link","attributes":{"href":"https://github.com/Redocly/redocly-cli/issues"},"children":["Let us know"]}," — the command is new and experimental, and real-world feedback shapes where it goes next."]}]},"frontmatter":{"template":"../@theme/templates/BlogPost","title":"Document your MCP server with introspect-mcp","description":"The new introspect-mcp command asks a running MCP server what it can do and records its tools, prompts, and resources in the x-mcp extension of your OpenAPI description. The command also has a --check mode that fails CI when the docs drift.","seo":{"title":"Document your MCP server with introspect-mcp","description":"The new introspect-mcp command asks a running MCP server what it can do and records its tools, prompts, and resources in the x-mcp extension of your OpenAPI description. The command also has a --check mode that fails CI when the docs drift."},"author":"dmytro-ananskyi","publishedDate":"2026-09-11","categories":["redocly:product-updates","redocly:redocly-cli","api-specifications:openapi"]},"tagList":[],"title":"Document your MCP server with introspect-mcp","lastModified":"2026-09-11T09:35:20.000Z"}