{
  "type": "guide",
  "id": "publish-an-mcp-server",
  "title": "Publish an MCP server so agents can find it",
  "summary": "Build a small MCP server with FastMCP, test it with the MCP Inspector CLI, publish its metadata to the official MCP Registry with mcp-publisher, then get it into GitHub's registry, Glama, Smithery, the Docker MCP Catalog and Index Agentica.",
  "author": "Agentica Author",
  "tags": [
    "mcp",
    "mcp-registry",
    "publishing",
    "discovery",
    "fastmcp",
    "mcp-inspector",
    "streamable-http"
  ],
  "published": "2026-10-17",
  "last_verified": "2026-10-17",
  "difficulty": "intermediate",
  "time_estimate": "45 min",
  "entries": [
    "fastmcp",
    "mcp-inspector",
    "official-mcp-registry",
    "github-mcp-registry",
    "glama-mcp",
    "smithery",
    "docker-mcp-catalog",
    "model-context-protocol",
    "mcp-authorization",
    "indexagentica-mcp"
  ],
  "links": {
    "html": "https://indexagentica.com/guides/publish-an-mcp-server/",
    "markdown": "https://indexagentica.com/guides/publish-an-mcp-server.md",
    "json": "https://indexagentica.com/api/longform/guides/publish-an-mcp-server.json",
    "source": "https://github.com/Drudley/indexagentica/blob/main/content-long/guides/publish-an-mcp-server.md"
  },
  "status": "published",
  "prerequisites": [
    "Python 3.10 or later and uv (for FastMCP)",
    "Node.js 22.19 or later (for the MCP Inspector)",
    "A GitHub account, or a domain whose DNS or web root you control",
    "A public HTTPS host for a remote server, or an account on npm, PyPI or a container registry for a package"
  ],
  "entries_detail": [
    {
      "id": "fastmcp",
      "name": "FastMCP",
      "summary": "The fast, Pythonic framework for building MCP servers, clients and interactive applications.",
      "url": "https://indexagentica.com/entries/fastmcp/",
      "json": "https://indexagentica.com/api/entries/fastmcp.json"
    },
    {
      "id": "mcp-inspector",
      "name": "MCP Inspector",
      "summary": "Official interactive developer tool for testing and debugging MCP servers in the browser, on the command line or in the terminal.",
      "url": "https://indexagentica.com/entries/mcp-inspector/",
      "json": "https://indexagentica.com/api/entries/mcp-inspector.json"
    },
    {
      "id": "official-mcp-registry",
      "name": "Official MCP Registry",
      "summary": "Community-driven registry service from the MCP project listing published MCP servers, with a public REST API (like an app store for MCP servers).",
      "url": "https://indexagentica.com/entries/official-mcp-registry/",
      "json": "https://indexagentica.com/api/entries/official-mcp-registry.json"
    },
    {
      "id": "github-mcp-registry",
      "name": "GitHub MCP Registry",
      "summary": "GitHub's registry centralizing MCP servers for discovery and integration with GitHub Copilot and other tools.",
      "url": "https://indexagentica.com/entries/github-mcp-registry/",
      "json": "https://indexagentica.com/api/entries/github-mcp-registry.json"
    },
    {
      "id": "glama-mcp",
      "name": "Glama MCP Directory",
      "summary": "Large directory of MCP servers, clients and remote connectors, each scanned, scored and testable in the browser, with a public API.",
      "url": "https://indexagentica.com/entries/glama-mcp/",
      "json": "https://indexagentica.com/api/entries/glama-mcp.json"
    },
    {
      "id": "smithery",
      "name": "Smithery",
      "summary": "Platform for connecting agents to thousands of tools and services (MCP), with auth, credentials and sessions handled for you.",
      "url": "https://indexagentica.com/entries/smithery/",
      "json": "https://indexagentica.com/api/entries/smithery.json"
    },
    {
      "id": "docker-mcp-catalog",
      "name": "Docker MCP Catalog",
      "summary": "Docker Hub's catalog of containerized, ready-to-use MCP servers, backed by the open docker/mcp-registry repo.",
      "url": "https://indexagentica.com/entries/docker-mcp-catalog/",
      "json": "https://indexagentica.com/api/entries/docker-mcp-catalog.json"
    },
    {
      "id": "model-context-protocol",
      "name": "Model Context Protocol (MCP)",
      "summary": "Open protocol that standardizes how AI applications connect to external tools, data sources and prompts via MCP servers.",
      "url": "https://indexagentica.com/entries/model-context-protocol/",
      "json": "https://indexagentica.com/api/entries/model-context-protocol.json"
    },
    {
      "id": "mcp-authorization",
      "name": "MCP Authorization (OAuth 2.1 for MCP)",
      "summary": "The MCP specification's transport-level authorization flow for HTTP-based MCP servers, based on a subset of OAuth 2.1 and related RFCs.",
      "url": "https://indexagentica.com/entries/mcp-authorization/",
      "json": "https://indexagentica.com/api/entries/mcp-authorization.json"
    },
    {
      "id": "indexagentica-mcp",
      "name": "Index Agentica MCP Server",
      "summary": "Remote, read-only MCP server for searching this directory (Index Agentica): search, get_entry, get_content and list_categories over Streamable HTTP, no auth.",
      "url": "https://indexagentica.com/entries/indexagentica-mcp/",
      "json": "https://indexagentica.com/api/entries/indexagentica-mcp.json"
    }
  ],
  "related": [
    {
      "type": "guide",
      "id": "add-oauth-to-your-mcp-server",
      "title": "Add OAuth authorization to your own MCP server",
      "url": "https://indexagentica.com/guides/add-oauth-to-your-mcp-server/",
      "json": "https://indexagentica.com/api/longform/guides/add-oauth-to-your-mcp-server.json"
    },
    {
      "type": "guide",
      "id": "connect-an-agent-to-a-remote-mcp-server",
      "title": "Connect an agent to a remote MCP server",
      "url": "https://indexagentica.com/guides/connect-an-agent-to-a-remote-mcp-server/",
      "json": "https://indexagentica.com/api/longform/guides/connect-an-agent-to-a-remote-mcp-server.json"
    },
    {
      "type": "guide",
      "id": "agent-auth-and-delegated-access",
      "title": "Agent auth and delegated access, step by step",
      "url": "https://indexagentica.com/guides/agent-auth-and-delegated-access/",
      "json": "https://indexagentica.com/api/longform/guides/agent-auth-and-delegated-access.json"
    },
    {
      "type": "comparison",
      "id": "agent-identity-and-mcp-auth",
      "title": "Agent identity and MCP authorization platforms",
      "url": "https://indexagentica.com/compare/agent-identity-and-mcp-auth/",
      "json": "https://indexagentica.com/api/longform/compare/agent-identity-and-mcp-auth.json"
    },
    {
      "type": "skill",
      "id": "pick-an-mcp-server",
      "title": "Pick an MCP server",
      "url": "https://indexagentica.com/skills/pick-an-mcp-server/",
      "json": "https://indexagentica.com/api/longform/skills/pick-an-mcp-server.json"
    },
    {
      "type": "guide",
      "id": "add-mcp-servers-to-claude-code",
      "title": "Add MCP servers to Claude Code",
      "url": "https://indexagentica.com/guides/add-mcp-servers-to-claude-code/",
      "json": "https://indexagentica.com/api/longform/guides/add-mcp-servers-to-claude-code.json"
    },
    {
      "type": "guide",
      "id": "test-your-mcp-server",
      "title": "Test your MCP server with in-process tests and Inspector CI checks",
      "url": "https://indexagentica.com/guides/test-your-mcp-server/",
      "json": "https://indexagentica.com/api/longform/guides/test-your-mcp-server.json"
    },
    {
      "type": "comparison",
      "id": "mcp-gateways",
      "title": "MCP gateways compared",
      "url": "https://indexagentica.com/compare/mcp-gateways/",
      "json": "https://indexagentica.com/api/longform/compare/mcp-gateways.json"
    }
  ],
  "sources": [
    {
      "title": "FastMCP installation",
      "url": "https://gofastmcp.com/getting-started/installation",
      "accessed": "2026-10-17"
    },
    {
      "title": "FastMCP quickstart",
      "url": "https://gofastmcp.com/getting-started/quickstart",
      "accessed": "2026-10-17"
    },
    {
      "title": "FastMCP HTTP deployment",
      "url": "https://gofastmcp.com/deployment/http",
      "accessed": "2026-10-17"
    },
    {
      "title": "fastmcp on PyPI",
      "url": "https://pypi.org/project/fastmcp/",
      "accessed": "2026-10-17"
    },
    {
      "title": "MCP Inspector README",
      "url": "https://github.com/modelcontextprotocol/inspector",
      "accessed": "2026-10-17"
    },
    {
      "title": "MCP Inspector CLI README",
      "url": "https://github.com/modelcontextprotocol/inspector/blob/main/clients/cli/README.md",
      "accessed": "2026-10-17"
    },
    {
      "title": "Quickstart: Publish an MCP server to the MCP Registry",
      "url": "https://modelcontextprotocol.io/registry/quickstart",
      "accessed": "2026-10-17"
    },
    {
      "title": "MCP Registry authentication",
      "url": "https://modelcontextprotocol.io/registry/authentication",
      "accessed": "2026-10-17"
    },
    {
      "title": "MCP Registry package types",
      "url": "https://modelcontextprotocol.io/registry/package-types",
      "accessed": "2026-10-17"
    },
    {
      "title": "Publishing remote servers to the MCP Registry",
      "url": "https://modelcontextprotocol.io/registry/remote-servers",
      "accessed": "2026-10-17"
    },
    {
      "title": "Versioning published MCP servers",
      "url": "https://modelcontextprotocol.io/registry/versioning",
      "accessed": "2026-10-17"
    },
    {
      "title": "Publishing to the MCP Registry with GitHub Actions",
      "url": "https://modelcontextprotocol.io/registry/github-actions",
      "accessed": "2026-10-17"
    },
    {
      "title": "MCP Registry aggregators",
      "url": "https://modelcontextprotocol.io/registry/registry-aggregators",
      "accessed": "2026-10-17"
    },
    {
      "title": "MCP Registry FAQ",
      "url": "https://modelcontextprotocol.io/registry/faq",
      "accessed": "2026-10-17"
    },
    {
      "title": "GitHub blog: How to find, install, and manage MCP servers with the GitHub MCP Registry",
      "url": "https://github.blog/ai-and-ml/generative-ai/how-to-find-install-and-manage-mcp-servers-with-the-github-mcp-registry/",
      "accessed": "2026-10-17"
    },
    {
      "title": "github/github-mcp-server discussion #1257 (GitHub MCP Registry onboarding)",
      "url": "https://github.com/github/github-mcp-server/discussions/1257",
      "accessed": "2026-10-17"
    },
    {
      "title": "Glama llms.txt",
      "url": "https://glama.ai/llms.txt",
      "accessed": "2026-10-17"
    },
    {
      "title": "Glama glama.json schema",
      "url": "https://glama.ai/mcp/schemas/server.json",
      "accessed": "2026-10-17"
    },
    {
      "title": "Glama connector ownership schema",
      "url": "https://glama.ai/mcp/schemas/connector.json",
      "accessed": "2026-10-17"
    },
    {
      "title": "Smithery, Publish",
      "url": "https://smithery.ai/docs/build/publish",
      "accessed": "2026-10-17"
    },
    {
      "title": "Docker MCP Registry README",
      "url": "https://github.com/docker/mcp-registry",
      "accessed": "2026-10-17"
    },
    {
      "title": "Contributing to the Docker MCP Registry",
      "url": "https://github.com/docker/mcp-registry/blob/main/CONTRIBUTING.md",
      "accessed": "2026-10-17"
    },
    {
      "title": "Index Agentica contribute.json",
      "url": "https://indexagentica.com/contribute.json",
      "accessed": "2026-10-17"
    }
  ],
  "front_matter": {
    "id": "publish-an-mcp-server",
    "type": "guide",
    "title": "Publish an MCP server so agents can find it",
    "summary": "Build a small MCP server with FastMCP, test it with the MCP Inspector CLI, publish its metadata to the official MCP Registry with mcp-publisher, then get it into GitHub's registry, Glama, Smithery, the Docker MCP Catalog and Index Agentica.",
    "description": "A practical path from a working MCP server to a discoverable one. It builds and runs a FastMCP server over Streamable HTTP, checks it with the MCP Inspector CLI, explains hosting and packaging choices, walks through server.json, namespace authentication and publishing on the official MCP Registry, and lists what each downstream directory needs. Steps come from each project's docs, checked on 2026-10-17. The build and test commands were run; nothing was published.",
    "author": "Agentica Author",
    "difficulty": "intermediate",
    "time_estimate": "45 min",
    "prerequisites": [
      "Python 3.10 or later and uv (for FastMCP)",
      "Node.js 22.19 or later (for the MCP Inspector)",
      "A GitHub account, or a domain whose DNS or web root you control",
      "A public HTTPS host for a remote server, or an account on npm, PyPI or a container registry for a package"
    ],
    "tags": [
      "mcp",
      "mcp-registry",
      "publishing",
      "discovery",
      "fastmcp",
      "mcp-inspector",
      "streamable-http"
    ],
    "entries": [
      "fastmcp",
      "mcp-inspector",
      "official-mcp-registry",
      "github-mcp-registry",
      "glama-mcp",
      "smithery",
      "docker-mcp-catalog",
      "model-context-protocol",
      "mcp-authorization",
      "indexagentica-mcp"
    ],
    "sources": [
      {
        "title": "FastMCP installation",
        "url": "https://gofastmcp.com/getting-started/installation",
        "accessed": "2026-10-17"
      },
      {
        "title": "FastMCP quickstart",
        "url": "https://gofastmcp.com/getting-started/quickstart",
        "accessed": "2026-10-17"
      },
      {
        "title": "FastMCP HTTP deployment",
        "url": "https://gofastmcp.com/deployment/http",
        "accessed": "2026-10-17"
      },
      {
        "title": "fastmcp on PyPI",
        "url": "https://pypi.org/project/fastmcp/",
        "accessed": "2026-10-17"
      },
      {
        "title": "MCP Inspector README",
        "url": "https://github.com/modelcontextprotocol/inspector",
        "accessed": "2026-10-17"
      },
      {
        "title": "MCP Inspector CLI README",
        "url": "https://github.com/modelcontextprotocol/inspector/blob/main/clients/cli/README.md",
        "accessed": "2026-10-17"
      },
      {
        "title": "Quickstart: Publish an MCP server to the MCP Registry",
        "url": "https://modelcontextprotocol.io/registry/quickstart",
        "accessed": "2026-10-17"
      },
      {
        "title": "MCP Registry authentication",
        "url": "https://modelcontextprotocol.io/registry/authentication",
        "accessed": "2026-10-17"
      },
      {
        "title": "MCP Registry package types",
        "url": "https://modelcontextprotocol.io/registry/package-types",
        "accessed": "2026-10-17"
      },
      {
        "title": "Publishing remote servers to the MCP Registry",
        "url": "https://modelcontextprotocol.io/registry/remote-servers",
        "accessed": "2026-10-17"
      },
      {
        "title": "Versioning published MCP servers",
        "url": "https://modelcontextprotocol.io/registry/versioning",
        "accessed": "2026-10-17"
      },
      {
        "title": "Publishing to the MCP Registry with GitHub Actions",
        "url": "https://modelcontextprotocol.io/registry/github-actions",
        "accessed": "2026-10-17"
      },
      {
        "title": "MCP Registry aggregators",
        "url": "https://modelcontextprotocol.io/registry/registry-aggregators",
        "accessed": "2026-10-17"
      },
      {
        "title": "MCP Registry FAQ",
        "url": "https://modelcontextprotocol.io/registry/faq",
        "accessed": "2026-10-17"
      },
      {
        "title": "GitHub blog: How to find, install, and manage MCP servers with the GitHub MCP Registry",
        "url": "https://github.blog/ai-and-ml/generative-ai/how-to-find-install-and-manage-mcp-servers-with-the-github-mcp-registry/",
        "accessed": "2026-10-17"
      },
      {
        "title": "github/github-mcp-server discussion #1257 (GitHub MCP Registry onboarding)",
        "url": "https://github.com/github/github-mcp-server/discussions/1257",
        "accessed": "2026-10-17"
      },
      {
        "title": "Glama llms.txt",
        "url": "https://glama.ai/llms.txt",
        "accessed": "2026-10-17"
      },
      {
        "title": "Glama glama.json schema",
        "url": "https://glama.ai/mcp/schemas/server.json",
        "accessed": "2026-10-17"
      },
      {
        "title": "Glama connector ownership schema",
        "url": "https://glama.ai/mcp/schemas/connector.json",
        "accessed": "2026-10-17"
      },
      {
        "title": "Smithery, Publish",
        "url": "https://smithery.ai/docs/build/publish",
        "accessed": "2026-10-17"
      },
      {
        "title": "Docker MCP Registry README",
        "url": "https://github.com/docker/mcp-registry",
        "accessed": "2026-10-17"
      },
      {
        "title": "Contributing to the Docker MCP Registry",
        "url": "https://github.com/docker/mcp-registry/blob/main/CONTRIBUTING.md",
        "accessed": "2026-10-17"
      },
      {
        "title": "Index Agentica contribute.json",
        "url": "https://indexagentica.com/contribute.json",
        "accessed": "2026-10-17"
      }
    ],
    "related": [
      "add-oauth-to-your-mcp-server",
      "connect-an-agent-to-a-remote-mcp-server",
      "agent-auth-and-delegated-access",
      "agent-identity-and-mcp-auth",
      "pick-an-mcp-server",
      "add-mcp-servers-to-claude-code",
      "test-your-mcp-server",
      "mcp-gateways"
    ],
    "last_verified": "2026-10-17",
    "published": "2026-10-17"
  },
  "markdown": "\nA working MCP server is only half the job. Agents and their humans find servers through registries and directories, and most of those now read from one place: the [official MCP Registry](https://indexagentica.com/entries/official-mcp-registry/). This guide takes a small server from code to a published listing, then covers the directories that sit downstream.\n\nWhat was actually run for this guide, on 2026-10-17: the FastMCP server below (FastMCP 4.1.0), the MCP Inspector CLI calls against it over HTTP and stdio (Inspector 2.11.0), and `mcp-publisher validate` on the example `server.json`. Nothing was published to any registry.\n\n```mermaid\nflowchart LR\n  A[Build: FastMCP] --> B[Test: MCP Inspector]\n  B --> C{Ship how?}\n  C -->|remote| D[Host over Streamable HTTP]\n  C -->|local| E[Publish package: npm / PyPI / OCI]\n  D --> F[server.json + mcp-publisher]\n  E --> F\n  F --> G[Official MCP Registry]\n  G --> H[GitHub MCP Registry, Glama, other aggregators]\n  D --> I[Smithery, Docker MCP Catalog, Index Agentica]\n```\n\n## 1. Build a small server with FastMCP\n\n[FastMCP](https://indexagentica.com/entries/fastmcp/) is a Python framework for MCP servers. Install it with `uv add fastmcp` or `pip install fastmcp` (it needs Python 3.10 or later), and check with `fastmcp version`.\n\nA tool is a decorated function. Type hints become the input schema and the docstring becomes the description that agents read, so write it for them:\n\n```python\n# server.py\nfrom fastmcp import FastMCP\n\nmcp = FastMCP(\"Unit Converter\")\n\n@mcp.tool\ndef km_to_miles(km: float) -> float:\n    \"\"\"Convert kilometres to miles.\"\"\"\n    return round(km * 0.621371, 4)\n\nif __name__ == \"__main__\":\n    mcp.run(transport=\"http\", host=\"127.0.0.1\", port=8765)\n```\n\nRun it with `python server.py`. With the HTTP transport the endpoint is at `/mcp`, here `http://127.0.0.1:8765/mcp`. Calling `mcp.run()` with no arguments uses stdio instead, which is what local, package-based installs use. The CLI can choose at launch: `fastmcp run server.py:mcp --transport http --port 8000`.\n\n## 2. Test it with the MCP Inspector\n\nThe [MCP Inspector](https://indexagentica.com/entries/mcp-inspector/) has a web UI (`npx @modelcontextprotocol/inspector`), a TUI (`--tui`) and a scriptable CLI (`--cli`). The CLI is the one to use from an agent or in CI:\n\n```bash\n# List tools (a URL ending in /mcp means Streamable HTTP)\nnpx @modelcontextprotocol/inspector --cli http://127.0.0.1:8765/mcp --method tools/list\n\n# Call one\nnpx @modelcontextprotocol/inspector --cli http://127.0.0.1:8765/mcp \\\n  --method tools/call --tool-name km_to_miles --tool-arg km=42\n```\n\nAgainst the server above, the call returned `\"structuredContent\": {\"result\": 26.0976}` and `\"isError\": false`. `tools/list` showed the `km` input schema and an output schema that FastMCP generated from the return type. Other useful methods are `initialize` (a connect-only probe), `resources/list` and `prompts/list`. For stdio, pass a command instead of a URL. For example, `--cli fastmcp run server.py:mcp` starts the same server over stdio, because the FastMCP CLI ignores the `__main__` block. Add `--header \"X-API-Key: ...\"` for an authenticated remote.\n\nThe Inspector stores OAuth tokens and stdio `env:` secrets in the OS keychain. On a machine without one, such as a headless Linux box or a container, they go to `~/.mcp-inspector/secrets.json`, unencrypted unless you supply a key.\n\n## 3. Decide how it ships: remote or package\n\nThe registry lists two kinds of install, and one server can offer both.\n\n**Remote (hosted) server.** Clients connect to a URL, and nothing is installed. Remote servers should use Streamable HTTP; SSE is deprecated and worth publishing only for existing clients. For production, FastMCP can give you an ASGI app to run under Uvicorn or similar:\n\n```python\napp = mcp.http_app()          # serve with: uvicorn server:app --host 0.0.0.0 --port 8000\n```\n\nFastMCP's deployment guide calls authentication \"highly recommended\" for remote servers and notes that some clients refuse to connect without it. Bearer tokens, JWT and OAuth are covered in its auth docs, and the protocol side is in [MCP authorization](https://indexagentica.com/entries/mcp-authorization/). The guide also documents an opt-in Host and Origin check against DNS rebinding (`host_origin_protection=True` with `allowed_hosts`). It is off by default, so turn it on or enforce the same checks in your reverse proxy. For managed hosting, the FastMCP docs point to Prefect Horizon, which they describe as free for personal projects.\n\n**Package (local) server.** Users run it with `npx`, `uvx`, `dnx`, Docker and similar tools. The registry hosts only metadata, so publish the package first. Each package type proves that it belongs to your registry name differently:\n\n| Package type | `registryType` | Ownership marker |\n|---|---|---|\n| npm | `npm` | `\"mcpName\": \"<server name>\"` in `package.json` |\n| PyPI | `pypi` | `mcp-name: <server name>` in the README, which can be hidden in an HTML comment |\n| NuGet | `nuget` | `mcp-name: <server name>` in the README |\n| Cargo | `cargo` | `mcp-name: <server name>` as visible text, because crates.io strips HTML comments |\n| Docker/OCI | `oci` | `io.modelcontextprotocol.server.name` annotation on the image |\n\n## 4. Publish to the official MCP Registry\n\nThe registry is in preview, and its docs warn that breaking changes or data resets may happen before general availability.\n\n**Install the publisher** (a prebuilt binary, or `brew install mcp-publisher`):\n\n```bash\ncurl -L \"https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz\" | tar xz mcp-publisher\n```\n\n**Write `server.json`.** `mcp-publisher init` makes a template from your project. For a remote-only server, replace the generated `packages` array with `remotes`:\n\n```json\n{\n  \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n  \"name\": \"io.github.your-username/unit-converter\",\n  \"title\": \"Unit Converter\",\n  \"description\": \"Converts kilometres to miles. A minimal example remote MCP server.\",\n  \"repository\": { \"url\": \"https://github.com/your-username/unit-converter-mcp\", \"source\": \"github\" },\n  \"version\": \"1.0.0\",\n  \"remotes\": [\n    { \"type\": \"streamable-http\", \"url\": \"https://mcp.example.com/mcp\" }\n  ]\n}\n```\n\nRules worth knowing before you publish:\n\n- `description` is limited to 100 characters.\n- A remote URL must be publicly reachable, and each URL can belong to only one server name.\n- Remotes can declare required `headers` (such as an API key marked `isSecret`) and URL template `variables` (such as `{tenant_id}`), and they can sit alongside `packages`.\n- Each publish needs a new, unique `version`. Published metadata is immutable, so you fix mistakes by publishing a new version. Semantic versions are recommended, and version ranges are rejected.\n- Custom data goes under `_meta.io.modelcontextprotocol.registry/publisher-provided`, up to 4 KB.\n\nRun `mcp-publisher validate` to check the file without publishing.\n\n**Authenticate.** The method decides which names you may use:\n\n| Method | Command | Names you can publish |\n|---|---|---|\n| GitHub OAuth | `mcp-publisher login github` (device code) | `io.github.<user>/*`, or `io.github.<org>/*` only if you are an **Owner** of the org |\n| DNS | `mcp-publisher login dns --domain example.com --private-key ...` | `com.example/*` and subdomains, with an Ed25519 or ECDSA P-384 key in a TXT record on the domain **apex** |\n| HTTP | `mcp-publisher login http --domain example.com --private-key ...` | `com.example/*`, with the public key at `https://example.com/.well-known/mcp-registry-auth` |\n| GitHub Actions | `mcp-publisher login github-oidc` (the docs' recommended CI option; the job needs `id-token: write`) | The example workflow publishes on version tags without a stored token; PAT and DNS logins also work in CI |\n\nThe registry checks that you control the namespace, not the remote URL. If the server runs on your own domain, use DNS or HTTP auth so the name and the URL visibly belong to the same publisher.\n\n**Publish and verify:**\n\n```bash\nmcp-publisher publish\ncurl \"https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.your-username/unit-converter\"\n```\n\nTo retire a version, run `mcp-publisher status --status deleted --message \"...\" <name> <version>`, or add `--all-versions`. Deleted entries are hidden from default listings but never removed, and setting the status back to `active` restores them.\n\n## 5. Get into the downstream directories\n\nThe registry exposes an unauthenticated read API that aggregators are expected to poll about hourly, so some directories pick your server up from there. Others need a step from you.\n\n| Directory | How a server gets in |\n|---|---|\n| [GitHub MCP Registry](https://indexagentica.com/entries/github-mcp-registry/) | Publish to the official registry first (status `active`, latest). Adding a new server to github.com/mcp is a manual curation step: GitHub's blog says to email partnerships@github.com, and requests are also posted in discussion #1257. After onboarding, new versions sync automatically. Check with `curl \"https://api.mcp.github.com/v0.1/servers?search=<name>\"`. |\n| [Glama](https://indexagentica.com/entries/glama-mcp/) | Indexes servers from GitHub, the official registry and community submissions, then scans and scores them. To claim a repository listing, commit a `glama.json` with `maintainers` (GitHub usernames). To claim a remote connector, serve `/.well-known/glama.json` with the claim token from Glama's claim panel on your domain. |\n| [Smithery](https://indexagentica.com/entries/smithery/) | Go to smithery.ai/new and enter your public HTTPS URL, or use `smithery mcp publish \"<url>\" -n @org/server`. It requires Streamable HTTP and OAuth if your server needs auth. If Smithery's scan can't read your tools, serve a static card at `/.well-known/mcp/server-card.json`. |\n| [Docker MCP Catalog](https://indexagentica.com/entries/docker-mcp-catalog/) | Open a pull request on `docker/mcp-registry` with `servers/<name>/server.yaml`, made with `task wizard` or `task create`, or `task remote-wizard` for remote servers. The Docker team reviews every PR. After approval, a Docker-built image is signed and published under `mcp/` on Docker Hub, and the entry appears within 24 hours. |\n| Index Agentica | Open a pull request adding `content/mcp-servers/<id>.json`, or use the structured New entry issue. A maintainer reviews it, and `contribute.json` has the schema and rules. Agents are welcome to submit, as long as they say they are an agent and who they act for. Our own server is listed as [indexagentica-mcp](https://indexagentica.com/entries/indexagentica-mcp/). |\n\n## Checklist\n\n- [ ] Tool names and docstrings tell an agent when to use each tool.\n- [ ] `tools/list` and a real `tools/call` pass in the Inspector CLI, ideally in CI.\n- [ ] Remote: Streamable HTTP, authentication on, Host/Origin checks on (in FastMCP or your proxy), HTTPS.\n- [ ] Package: the ownership marker matches the `server.json` name exactly.\n- [ ] `server.json` passes `mcp-publisher validate`; the description fits in 100 characters; the version is new.\n- [ ] You published under a namespace you control (GitHub user or org you own, or your domain).\n- [ ] You requested GitHub onboarding, claimed the Glama listing, and submitted to Smithery, Docker and Index Agentica if they fit.\n\n## Troubleshooting (from the registry docs)\n\n| Error | Fix |\n|---|---|\n| \"Registry validation failed for package\" | The ownership marker is missing or doesn't match (`mcpName`, `mcp-name:` or the OCI annotation). |\n| \"Invalid or expired Registry JWT token\" | Log in again with `mcp-publisher login ...`. |\n| \"You do not have permission to publish this server\" | The name doesn't match your auth method, for example a GitHub login publishing outside `io.github.<you>/`. |\n| DNS auth fails with a signature error | The TXT record must be on the apex, not under a selector, and stale records from old keys must be removed. |\n\nOnce you're listed, the other side of the flow is in [Connect an agent to a remote MCP server](https://indexagentica.com/guides/connect-an-agent-to-a-remote-mcp-server/). For auth design, see [Agent auth and delegated access](https://indexagentica.com/guides/agent-auth-and-delegated-access/).\n",
  "raw": "---\nid: publish-an-mcp-server\ntype: guide\ntitle: Publish an MCP server so agents can find it\nsummary: \"Build a small MCP server with FastMCP, test it with the MCP Inspector CLI, publish its metadata to the official MCP Registry with mcp-publisher, then get it into GitHub's registry, Glama, Smithery, the Docker MCP Catalog and Index Agentica.\"\ndescription: \"A practical path from a working MCP server to a discoverable one. It builds and runs a FastMCP server over Streamable HTTP, checks it with the MCP Inspector CLI, explains hosting and packaging choices, walks through server.json, namespace authentication and publishing on the official MCP Registry, and lists what each downstream directory needs. Steps come from each project's docs, checked on 2026-10-17. The build and test commands were run; nothing was published.\"\nauthor: Agentica Author\ndifficulty: intermediate\ntime_estimate: 45 min\nprerequisites:\n  - Python 3.10 or later and uv (for FastMCP)\n  - Node.js 22.19 or later (for the MCP Inspector)\n  - A GitHub account, or a domain whose DNS or web root you control\n  - A public HTTPS host for a remote server, or an account on npm, PyPI or a container registry for a package\ntags: [mcp, mcp-registry, publishing, discovery, fastmcp, mcp-inspector, streamable-http]\nentries: [fastmcp, mcp-inspector, official-mcp-registry, github-mcp-registry, glama-mcp, smithery, docker-mcp-catalog, model-context-protocol, mcp-authorization, indexagentica-mcp]\nsources:\n  - title: FastMCP installation\n    url: https://gofastmcp.com/getting-started/installation\n    accessed: 2026-10-17\n  - title: FastMCP quickstart\n    url: https://gofastmcp.com/getting-started/quickstart\n    accessed: 2026-10-17\n  - title: FastMCP HTTP deployment\n    url: https://gofastmcp.com/deployment/http\n    accessed: 2026-10-17\n  - title: fastmcp on PyPI\n    url: https://pypi.org/project/fastmcp/\n    accessed: 2026-10-17\n  - title: MCP Inspector README\n    url: https://github.com/modelcontextprotocol/inspector\n    accessed: 2026-10-17\n  - title: MCP Inspector CLI README\n    url: https://github.com/modelcontextprotocol/inspector/blob/main/clients/cli/README.md\n    accessed: 2026-10-17\n  - title: \"Quickstart: Publish an MCP server to the MCP Registry\"\n    url: https://modelcontextprotocol.io/registry/quickstart\n    accessed: 2026-10-17\n  - title: MCP Registry authentication\n    url: https://modelcontextprotocol.io/registry/authentication\n    accessed: 2026-10-17\n  - title: MCP Registry package types\n    url: https://modelcontextprotocol.io/registry/package-types\n    accessed: 2026-10-17\n  - title: Publishing remote servers to the MCP Registry\n    url: https://modelcontextprotocol.io/registry/remote-servers\n    accessed: 2026-10-17\n  - title: Versioning published MCP servers\n    url: https://modelcontextprotocol.io/registry/versioning\n    accessed: 2026-10-17\n  - title: Publishing to the MCP Registry with GitHub Actions\n    url: https://modelcontextprotocol.io/registry/github-actions\n    accessed: 2026-10-17\n  - title: MCP Registry aggregators\n    url: https://modelcontextprotocol.io/registry/registry-aggregators\n    accessed: 2026-10-17\n  - title: MCP Registry FAQ\n    url: https://modelcontextprotocol.io/registry/faq\n    accessed: 2026-10-17\n  - title: \"GitHub blog: How to find, install, and manage MCP servers with the GitHub MCP Registry\"\n    url: https://github.blog/ai-and-ml/generative-ai/how-to-find-install-and-manage-mcp-servers-with-the-github-mcp-registry/\n    accessed: 2026-10-17\n  - title: \"github/github-mcp-server discussion #1257 (GitHub MCP Registry onboarding)\"\n    url: https://github.com/github/github-mcp-server/discussions/1257\n    accessed: 2026-10-17\n  - title: Glama llms.txt\n    url: https://glama.ai/llms.txt\n    accessed: 2026-10-17\n  - title: Glama glama.json schema\n    url: https://glama.ai/mcp/schemas/server.json\n    accessed: 2026-10-17\n  - title: Glama connector ownership schema\n    url: https://glama.ai/mcp/schemas/connector.json\n    accessed: 2026-10-17\n  - title: Smithery, Publish\n    url: https://smithery.ai/docs/build/publish\n    accessed: 2026-10-17\n  - title: Docker MCP Registry README\n    url: https://github.com/docker/mcp-registry\n    accessed: 2026-10-17\n  - title: Contributing to the Docker MCP Registry\n    url: https://github.com/docker/mcp-registry/blob/main/CONTRIBUTING.md\n    accessed: 2026-10-17\n  - title: Index Agentica contribute.json\n    url: https://indexagentica.com/contribute.json\n    accessed: 2026-10-17\nrelated: [add-oauth-to-your-mcp-server, connect-an-agent-to-a-remote-mcp-server, agent-auth-and-delegated-access, agent-identity-and-mcp-auth, pick-an-mcp-server, add-mcp-servers-to-claude-code, test-your-mcp-server, mcp-gateways]\nlast_verified: 2026-10-17\npublished: 2026-10-17\n---\n\nA working MCP server is only half the job. Agents and their humans find servers through registries and directories, and most of those now read from one place: the [official MCP Registry](https://indexagentica.com/entries/official-mcp-registry/). This guide takes a small server from code to a published listing, then covers the directories that sit downstream.\n\nWhat was actually run for this guide, on 2026-10-17: the FastMCP server below (FastMCP 4.1.0), the MCP Inspector CLI calls against it over HTTP and stdio (Inspector 2.11.0), and `mcp-publisher validate` on the example `server.json`. Nothing was published to any registry.\n\n```mermaid\nflowchart LR\n  A[Build: FastMCP] --> B[Test: MCP Inspector]\n  B --> C{Ship how?}\n  C -->|remote| D[Host over Streamable HTTP]\n  C -->|local| E[Publish package: npm / PyPI / OCI]\n  D --> F[server.json + mcp-publisher]\n  E --> F\n  F --> G[Official MCP Registry]\n  G --> H[GitHub MCP Registry, Glama, other aggregators]\n  D --> I[Smithery, Docker MCP Catalog, Index Agentica]\n```\n\n## 1. Build a small server with FastMCP\n\n[FastMCP](https://indexagentica.com/entries/fastmcp/) is a Python framework for MCP servers. Install it with `uv add fastmcp` or `pip install fastmcp` (it needs Python 3.10 or later), and check with `fastmcp version`.\n\nA tool is a decorated function. Type hints become the input schema and the docstring becomes the description that agents read, so write it for them:\n\n```python\n# server.py\nfrom fastmcp import FastMCP\n\nmcp = FastMCP(\"Unit Converter\")\n\n@mcp.tool\ndef km_to_miles(km: float) -> float:\n    \"\"\"Convert kilometres to miles.\"\"\"\n    return round(km * 0.621371, 4)\n\nif __name__ == \"__main__\":\n    mcp.run(transport=\"http\", host=\"127.0.0.1\", port=8765)\n```\n\nRun it with `python server.py`. With the HTTP transport the endpoint is at `/mcp`, here `http://127.0.0.1:8765/mcp`. Calling `mcp.run()` with no arguments uses stdio instead, which is what local, package-based installs use. The CLI can choose at launch: `fastmcp run server.py:mcp --transport http --port 8000`.\n\n## 2. Test it with the MCP Inspector\n\nThe [MCP Inspector](https://indexagentica.com/entries/mcp-inspector/) has a web UI (`npx @modelcontextprotocol/inspector`), a TUI (`--tui`) and a scriptable CLI (`--cli`). The CLI is the one to use from an agent or in CI:\n\n```bash\n# List tools (a URL ending in /mcp means Streamable HTTP)\nnpx @modelcontextprotocol/inspector --cli http://127.0.0.1:8765/mcp --method tools/list\n\n# Call one\nnpx @modelcontextprotocol/inspector --cli http://127.0.0.1:8765/mcp \\\n  --method tools/call --tool-name km_to_miles --tool-arg km=42\n```\n\nAgainst the server above, the call returned `\"structuredContent\": {\"result\": 26.0976}` and `\"isError\": false`. `tools/list` showed the `km` input schema and an output schema that FastMCP generated from the return type. Other useful methods are `initialize` (a connect-only probe), `resources/list` and `prompts/list`. For stdio, pass a command instead of a URL. For example, `--cli fastmcp run server.py:mcp` starts the same server over stdio, because the FastMCP CLI ignores the `__main__` block. Add `--header \"X-API-Key: ...\"` for an authenticated remote.\n\nThe Inspector stores OAuth tokens and stdio `env:` secrets in the OS keychain. On a machine without one, such as a headless Linux box or a container, they go to `~/.mcp-inspector/secrets.json`, unencrypted unless you supply a key.\n\n## 3. Decide how it ships: remote or package\n\nThe registry lists two kinds of install, and one server can offer both.\n\n**Remote (hosted) server.** Clients connect to a URL, and nothing is installed. Remote servers should use Streamable HTTP; SSE is deprecated and worth publishing only for existing clients. For production, FastMCP can give you an ASGI app to run under Uvicorn or similar:\n\n```python\napp = mcp.http_app()          # serve with: uvicorn server:app --host 0.0.0.0 --port 8000\n```\n\nFastMCP's deployment guide calls authentication \"highly recommended\" for remote servers and notes that some clients refuse to connect without it. Bearer tokens, JWT and OAuth are covered in its auth docs, and the protocol side is in [MCP authorization](https://indexagentica.com/entries/mcp-authorization/). The guide also documents an opt-in Host and Origin check against DNS rebinding (`host_origin_protection=True` with `allowed_hosts`). It is off by default, so turn it on or enforce the same checks in your reverse proxy. For managed hosting, the FastMCP docs point to Prefect Horizon, which they describe as free for personal projects.\n\n**Package (local) server.** Users run it with `npx`, `uvx`, `dnx`, Docker and similar tools. The registry hosts only metadata, so publish the package first. Each package type proves that it belongs to your registry name differently:\n\n| Package type | `registryType` | Ownership marker |\n|---|---|---|\n| npm | `npm` | `\"mcpName\": \"<server name>\"` in `package.json` |\n| PyPI | `pypi` | `mcp-name: <server name>` in the README, which can be hidden in an HTML comment |\n| NuGet | `nuget` | `mcp-name: <server name>` in the README |\n| Cargo | `cargo` | `mcp-name: <server name>` as visible text, because crates.io strips HTML comments |\n| Docker/OCI | `oci` | `io.modelcontextprotocol.server.name` annotation on the image |\n\n## 4. Publish to the official MCP Registry\n\nThe registry is in preview, and its docs warn that breaking changes or data resets may happen before general availability.\n\n**Install the publisher** (a prebuilt binary, or `brew install mcp-publisher`):\n\n```bash\ncurl -L \"https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz\" | tar xz mcp-publisher\n```\n\n**Write `server.json`.** `mcp-publisher init` makes a template from your project. For a remote-only server, replace the generated `packages` array with `remotes`:\n\n```json\n{\n  \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n  \"name\": \"io.github.your-username/unit-converter\",\n  \"title\": \"Unit Converter\",\n  \"description\": \"Converts kilometres to miles. A minimal example remote MCP server.\",\n  \"repository\": { \"url\": \"https://github.com/your-username/unit-converter-mcp\", \"source\": \"github\" },\n  \"version\": \"1.0.0\",\n  \"remotes\": [\n    { \"type\": \"streamable-http\", \"url\": \"https://mcp.example.com/mcp\" }\n  ]\n}\n```\n\nRules worth knowing before you publish:\n\n- `description` is limited to 100 characters.\n- A remote URL must be publicly reachable, and each URL can belong to only one server name.\n- Remotes can declare required `headers` (such as an API key marked `isSecret`) and URL template `variables` (such as `{tenant_id}`), and they can sit alongside `packages`.\n- Each publish needs a new, unique `version`. Published metadata is immutable, so you fix mistakes by publishing a new version. Semantic versions are recommended, and version ranges are rejected.\n- Custom data goes under `_meta.io.modelcontextprotocol.registry/publisher-provided`, up to 4 KB.\n\nRun `mcp-publisher validate` to check the file without publishing.\n\n**Authenticate.** The method decides which names you may use:\n\n| Method | Command | Names you can publish |\n|---|---|---|\n| GitHub OAuth | `mcp-publisher login github` (device code) | `io.github.<user>/*`, or `io.github.<org>/*` only if you are an **Owner** of the org |\n| DNS | `mcp-publisher login dns --domain example.com --private-key ...` | `com.example/*` and subdomains, with an Ed25519 or ECDSA P-384 key in a TXT record on the domain **apex** |\n| HTTP | `mcp-publisher login http --domain example.com --private-key ...` | `com.example/*`, with the public key at `https://example.com/.well-known/mcp-registry-auth` |\n| GitHub Actions | `mcp-publisher login github-oidc` (the docs' recommended CI option; the job needs `id-token: write`) | The example workflow publishes on version tags without a stored token; PAT and DNS logins also work in CI |\n\nThe registry checks that you control the namespace, not the remote URL. If the server runs on your own domain, use DNS or HTTP auth so the name and the URL visibly belong to the same publisher.\n\n**Publish and verify:**\n\n```bash\nmcp-publisher publish\ncurl \"https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.your-username/unit-converter\"\n```\n\nTo retire a version, run `mcp-publisher status --status deleted --message \"...\" <name> <version>`, or add `--all-versions`. Deleted entries are hidden from default listings but never removed, and setting the status back to `active` restores them.\n\n## 5. Get into the downstream directories\n\nThe registry exposes an unauthenticated read API that aggregators are expected to poll about hourly, so some directories pick your server up from there. Others need a step from you.\n\n| Directory | How a server gets in |\n|---|---|\n| [GitHub MCP Registry](https://indexagentica.com/entries/github-mcp-registry/) | Publish to the official registry first (status `active`, latest). Adding a new server to github.com/mcp is a manual curation step: GitHub's blog says to email partnerships@github.com, and requests are also posted in discussion #1257. After onboarding, new versions sync automatically. Check with `curl \"https://api.mcp.github.com/v0.1/servers?search=<name>\"`. |\n| [Glama](https://indexagentica.com/entries/glama-mcp/) | Indexes servers from GitHub, the official registry and community submissions, then scans and scores them. To claim a repository listing, commit a `glama.json` with `maintainers` (GitHub usernames). To claim a remote connector, serve `/.well-known/glama.json` with the claim token from Glama's claim panel on your domain. |\n| [Smithery](https://indexagentica.com/entries/smithery/) | Go to smithery.ai/new and enter your public HTTPS URL, or use `smithery mcp publish \"<url>\" -n @org/server`. It requires Streamable HTTP and OAuth if your server needs auth. If Smithery's scan can't read your tools, serve a static card at `/.well-known/mcp/server-card.json`. |\n| [Docker MCP Catalog](https://indexagentica.com/entries/docker-mcp-catalog/) | Open a pull request on `docker/mcp-registry` with `servers/<name>/server.yaml`, made with `task wizard` or `task create`, or `task remote-wizard` for remote servers. The Docker team reviews every PR. After approval, a Docker-built image is signed and published under `mcp/` on Docker Hub, and the entry appears within 24 hours. |\n| Index Agentica | Open a pull request adding `content/mcp-servers/<id>.json`, or use the structured New entry issue. A maintainer reviews it, and `contribute.json` has the schema and rules. Agents are welcome to submit, as long as they say they are an agent and who they act for. Our own server is listed as [indexagentica-mcp](https://indexagentica.com/entries/indexagentica-mcp/). |\n\n## Checklist\n\n- [ ] Tool names and docstrings tell an agent when to use each tool.\n- [ ] `tools/list` and a real `tools/call` pass in the Inspector CLI, ideally in CI.\n- [ ] Remote: Streamable HTTP, authentication on, Host/Origin checks on (in FastMCP or your proxy), HTTPS.\n- [ ] Package: the ownership marker matches the `server.json` name exactly.\n- [ ] `server.json` passes `mcp-publisher validate`; the description fits in 100 characters; the version is new.\n- [ ] You published under a namespace you control (GitHub user or org you own, or your domain).\n- [ ] You requested GitHub onboarding, claimed the Glama listing, and submitted to Smithery, Docker and Index Agentica if they fit.\n\n## Troubleshooting (from the registry docs)\n\n| Error | Fix |\n|---|---|\n| \"Registry validation failed for package\" | The ownership marker is missing or doesn't match (`mcpName`, `mcp-name:` or the OCI annotation). |\n| \"Invalid or expired Registry JWT token\" | Log in again with `mcp-publisher login ...`. |\n| \"You do not have permission to publish this server\" | The name doesn't match your auth method, for example a GitHub login publishing outside `io.github.<you>/`. |\n| DNS auth fails with a signature error | The TXT record must be on the apex, not under a selector, and stale records from old keys must be removed. |\n\nOnce you're listed, the other side of the flow is in [Connect an agent to a remote MCP server](https://indexagentica.com/guides/connect-an-agent-to-a-remote-mcp-server/). For auth design, see [Agent auth and delegated access](https://indexagentica.com/guides/agent-auth-and-delegated-access/).\n",
  "html": "<p>A working MCP server is only half the job. Agents and their humans find servers through registries and directories, and most of those now read from one place: the <a href=\"/entries/official-mcp-registry/\">official MCP Registry</a>. This guide takes a small server from code to a published listing, then covers the directories that sit downstream.</p>\n<p>What was actually run for this guide, on 2026-10-17: the FastMCP server below (FastMCP 4.1.0), the MCP Inspector CLI calls against it over HTTP and stdio (Inspector 2.11.0), and <code>mcp-publisher validate</code> on the example <code>server.json</code>. Nothing was published to any registry.</p>\n<figure class=\"diagram\"><pre class=\"mermaid\">flowchart LR\n  A[Build: FastMCP] --&gt; B[Test: MCP Inspector]\n  B --&gt; C{Ship how?}\n  C --&gt;|remote| D[Host over Streamable HTTP]\n  C --&gt;|local| E[Publish package: npm / PyPI / OCI]\n  D --&gt; F[server.json + mcp-publisher]\n  E --&gt; F\n  F --&gt; G[Official MCP Registry]\n  G --&gt; H[GitHub MCP Registry, Glama, other aggregators]\n  D --&gt; I[Smithery, Docker MCP Catalog, Index Agentica]</pre><figcaption>Mermaid diagram (source shown; not rendered client-side)</figcaption></figure>\n<h2>1. Build a small server with FastMCP</h2>\n<p><a href=\"/entries/fastmcp/\">FastMCP</a> is a Python framework for MCP servers. Install it with <code>uv add fastmcp</code> or <code>pip install fastmcp</code> (it needs Python 3.10 or later), and check with <code>fastmcp version</code>.</p>\n<p>A tool is a decorated function. Type hints become the input schema and the docstring becomes the description that agents read, so write it for them:</p>\n<pre><code class=\"language-python\"># server.py\nfrom fastmcp import FastMCP\n\nmcp = FastMCP(&quot;Unit Converter&quot;)\n\n@mcp.tool\ndef km_to_miles(km: float) -&gt; float:\n    &quot;&quot;&quot;Convert kilometres to miles.&quot;&quot;&quot;\n    return round(km * 0.621371, 4)\n\nif __name__ == &quot;__main__&quot;:\n    mcp.run(transport=&quot;http&quot;, host=&quot;127.0.0.1&quot;, port=8765)</code></pre>\n<p>Run it with <code>python server.py</code>. With the HTTP transport the endpoint is at <code>/mcp</code>, here <code>http://127.0.0.1:8765/mcp</code>. Calling <code>mcp.run()</code> with no arguments uses stdio instead, which is what local, package-based installs use. The CLI can choose at launch: <code>fastmcp run server.py:mcp --transport http --port 8000</code>.</p>\n<h2>2. Test it with the MCP Inspector</h2>\n<p>The <a href=\"/entries/mcp-inspector/\">MCP Inspector</a> has a web UI (<code>npx @modelcontextprotocol/inspector</code>), a TUI (<code>--tui</code>) and a scriptable CLI (<code>--cli</code>). The CLI is the one to use from an agent or in CI:</p>\n<pre><code class=\"language-bash\"># List tools (a URL ending in /mcp means Streamable HTTP)\nnpx @modelcontextprotocol/inspector --cli http://127.0.0.1:8765/mcp --method tools/list\n\n# Call one\nnpx @modelcontextprotocol/inspector --cli http://127.0.0.1:8765/mcp \\\n  --method tools/call --tool-name km_to_miles --tool-arg km=42</code></pre>\n<p>Against the server above, the call returned <code>&quot;structuredContent&quot;: {&quot;result&quot;: 26.0976}</code> and <code>&quot;isError&quot;: false</code>. <code>tools/list</code> showed the <code>km</code> input schema and an output schema that FastMCP generated from the return type. Other useful methods are <code>initialize</code> (a connect-only probe), <code>resources/list</code> and <code>prompts/list</code>. For stdio, pass a command instead of a URL. For example, <code>--cli fastmcp run server.py:mcp</code> starts the same server over stdio, because the FastMCP CLI ignores the <code>__main__</code> block. Add <code>--header &quot;X-API-Key: ...&quot;</code> for an authenticated remote.</p>\n<p>The Inspector stores OAuth tokens and stdio <code>env:</code> secrets in the OS keychain. On a machine without one, such as a headless Linux box or a container, they go to <code>~/.mcp-inspector/secrets.json</code>, unencrypted unless you supply a key.</p>\n<h2>3. Decide how it ships: remote or package</h2>\n<p>The registry lists two kinds of install, and one server can offer both.</p>\n<p><strong>Remote (hosted) server.</strong> Clients connect to a URL, and nothing is installed. Remote servers should use Streamable HTTP; SSE is deprecated and worth publishing only for existing clients. For production, FastMCP can give you an ASGI app to run under Uvicorn or similar:</p>\n<pre><code class=\"language-python\">app = mcp.http_app()          # serve with: uvicorn server:app --host 0.0.0.0 --port 8000</code></pre>\n<p>FastMCP&#39;s deployment guide calls authentication &quot;highly recommended&quot; for remote servers and notes that some clients refuse to connect without it. Bearer tokens, JWT and OAuth are covered in its auth docs, and the protocol side is in <a href=\"/entries/mcp-authorization/\">MCP authorization</a>. The guide also documents an opt-in Host and Origin check against DNS rebinding (<code>host_origin_protection=True</code> with <code>allowed_hosts</code>). It is off by default, so turn it on or enforce the same checks in your reverse proxy. For managed hosting, the FastMCP docs point to Prefect Horizon, which they describe as free for personal projects.</p>\n<p><strong>Package (local) server.</strong> Users run it with <code>npx</code>, <code>uvx</code>, <code>dnx</code>, Docker and similar tools. The registry hosts only metadata, so publish the package first. Each package type proves that it belongs to your registry name differently:</p>\n<table><thead><tr><th scope=\"col\">Package type</th><th scope=\"col\"><code>registryType</code></th><th scope=\"col\">Ownership marker</th></tr></thead><tbody><tr><td>npm</td><td><code>npm</code></td><td><code>&quot;mcpName&quot;: &quot;&lt;server name&gt;&quot;</code> in <code>package.json</code></td></tr><tr><td>PyPI</td><td><code>pypi</code></td><td><code>mcp-name: &lt;server name&gt;</code> in the README, which can be hidden in an HTML comment</td></tr><tr><td>NuGet</td><td><code>nuget</code></td><td><code>mcp-name: &lt;server name&gt;</code> in the README</td></tr><tr><td>Cargo</td><td><code>cargo</code></td><td><code>mcp-name: &lt;server name&gt;</code> as visible text, because crates.io strips HTML comments</td></tr><tr><td>Docker/OCI</td><td><code>oci</code></td><td><code>io.modelcontextprotocol.server.name</code> annotation on the image</td></tr></tbody></table>\n<h2>4. Publish to the official MCP Registry</h2>\n<p>The registry is in preview, and its docs warn that breaking changes or data resets may happen before general availability.</p>\n<p><strong>Install the publisher</strong> (a prebuilt binary, or <code>brew install mcp-publisher</code>):</p>\n<pre><code class=\"language-bash\">curl -L &quot;https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr &#39;[:upper:]&#39; &#39;[:lower:]&#39;)_$(uname -m | sed &#39;s/x86_64/amd64/;s/aarch64/arm64/&#39;).tar.gz&quot; | tar xz mcp-publisher</code></pre>\n<p><strong>Write <code>server.json</code>.</strong> <code>mcp-publisher init</code> makes a template from your project. For a remote-only server, replace the generated <code>packages</code> array with <code>remotes</code>:</p>\n<pre><code class=\"language-json\">{\n  &quot;$schema&quot;: &quot;https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json&quot;,\n  &quot;name&quot;: &quot;io.github.your-username/unit-converter&quot;,\n  &quot;title&quot;: &quot;Unit Converter&quot;,\n  &quot;description&quot;: &quot;Converts kilometres to miles. A minimal example remote MCP server.&quot;,\n  &quot;repository&quot;: { &quot;url&quot;: &quot;https://github.com/your-username/unit-converter-mcp&quot;, &quot;source&quot;: &quot;github&quot; },\n  &quot;version&quot;: &quot;1.0.0&quot;,\n  &quot;remotes&quot;: [\n    { &quot;type&quot;: &quot;streamable-http&quot;, &quot;url&quot;: &quot;https://mcp.example.com/mcp&quot; }\n  ]\n}</code></pre>\n<p>Rules worth knowing before you publish:</p>\n<ul><li><code>description</code> is limited to 100 characters.</li><li>A remote URL must be publicly reachable, and each URL can belong to only one server name.</li><li>Remotes can declare required <code>headers</code> (such as an API key marked <code>isSecret</code>) and URL template <code>variables</code> (such as <code>{tenant_id}</code>), and they can sit alongside <code>packages</code>.</li><li>Each publish needs a new, unique <code>version</code>. Published metadata is immutable, so you fix mistakes by publishing a new version. Semantic versions are recommended, and version ranges are rejected.</li><li>Custom data goes under <code>_meta.io.modelcontextprotocol.registry/publisher-provided</code>, up to 4 KB.</li></ul>\n<p>Run <code>mcp-publisher validate</code> to check the file without publishing.</p>\n<p><strong>Authenticate.</strong> The method decides which names you may use:</p>\n<table><thead><tr><th scope=\"col\">Method</th><th scope=\"col\">Command</th><th scope=\"col\">Names you can publish</th></tr></thead><tbody><tr><td>GitHub OAuth</td><td><code>mcp-publisher login github</code> (device code)</td><td><code>io.github.&lt;user&gt;/*</code>, or <code>io.github.&lt;org&gt;/*</code> only if you are an <strong>Owner</strong> of the org</td></tr><tr><td>DNS</td><td><code>mcp-publisher login dns --domain example.com --private-key ...</code></td><td><code>com.example/*</code> and subdomains, with an Ed25519 or ECDSA P-384 key in a TXT record on the domain <strong>apex</strong></td></tr><tr><td>HTTP</td><td><code>mcp-publisher login http --domain example.com --private-key ...</code></td><td><code>com.example/*</code>, with the public key at <code>https://example.com/.well-known/mcp-registry-auth</code></td></tr><tr><td>GitHub Actions</td><td><code>mcp-publisher login github-oidc</code> (the docs&#39; recommended CI option; the job needs <code>id-token: write</code>)</td><td>The example workflow publishes on version tags without a stored token; PAT and DNS logins also work in CI</td></tr></tbody></table>\n<p>The registry checks that you control the namespace, not the remote URL. If the server runs on your own domain, use DNS or HTTP auth so the name and the URL visibly belong to the same publisher.</p>\n<p><strong>Publish and verify:</strong></p>\n<pre><code class=\"language-bash\">mcp-publisher publish\ncurl &quot;https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.your-username/unit-converter&quot;</code></pre>\n<p>To retire a version, run <code>mcp-publisher status --status deleted --message &quot;...&quot; &lt;name&gt; &lt;version&gt;</code>, or add <code>--all-versions</code>. Deleted entries are hidden from default listings but never removed, and setting the status back to <code>active</code> restores them.</p>\n<h2>5. Get into the downstream directories</h2>\n<p>The registry exposes an unauthenticated read API that aggregators are expected to poll about hourly, so some directories pick your server up from there. Others need a step from you.</p>\n<table><thead><tr><th scope=\"col\">Directory</th><th scope=\"col\">How a server gets in</th></tr></thead><tbody><tr><td><a href=\"/entries/github-mcp-registry/\">GitHub MCP Registry</a></td><td>Publish to the official registry first (status <code>active</code>, latest). Adding a new server to github.com/mcp is a manual curation step: GitHub&#39;s blog says to email partnerships@github.com, and requests are also posted in discussion #1257. After onboarding, new versions sync automatically. Check with <code>curl &quot;https://api.mcp.github.com/v0.1/servers?search=&lt;name&gt;&quot;</code>.</td></tr><tr><td><a href=\"/entries/glama-mcp/\">Glama</a></td><td>Indexes servers from GitHub, the official registry and community submissions, then scans and scores them. To claim a repository listing, commit a <code>glama.json</code> with <code>maintainers</code> (GitHub usernames). To claim a remote connector, serve <code>/.well-known/glama.json</code> with the claim token from Glama&#39;s claim panel on your domain.</td></tr><tr><td><a href=\"/entries/smithery/\">Smithery</a></td><td>Go to smithery.ai/new and enter your public HTTPS URL, or use <code>smithery mcp publish &quot;&lt;url&gt;&quot; -n @org/server</code>. It requires Streamable HTTP and OAuth if your server needs auth. If Smithery&#39;s scan can&#39;t read your tools, serve a static card at <code>/.well-known/mcp/server-card.json</code>.</td></tr><tr><td><a href=\"/entries/docker-mcp-catalog/\">Docker MCP Catalog</a></td><td>Open a pull request on <code>docker/mcp-registry</code> with <code>servers/&lt;name&gt;/server.yaml</code>, made with <code>task wizard</code> or <code>task create</code>, or <code>task remote-wizard</code> for remote servers. The Docker team reviews every PR. After approval, a Docker-built image is signed and published under <code>mcp/</code> on Docker Hub, and the entry appears within 24 hours.</td></tr><tr><td>Index Agentica</td><td>Open a pull request adding <code>content/mcp-servers/&lt;id&gt;.json</code>, or use the structured New entry issue. A maintainer reviews it, and <code>contribute.json</code> has the schema and rules. Agents are welcome to submit, as long as they say they are an agent and who they act for. Our own server is listed as <a href=\"/entries/indexagentica-mcp/\">indexagentica-mcp</a>.</td></tr></tbody></table>\n<h2>Checklist</h2>\n<ul><li>☐ Tool names and docstrings tell an agent when to use each tool.</li><li>☐ <code>tools/list</code> and a real <code>tools/call</code> pass in the Inspector CLI, ideally in CI.</li><li>☐ Remote: Streamable HTTP, authentication on, Host/Origin checks on (in FastMCP or your proxy), HTTPS.</li><li>☐ Package: the ownership marker matches the <code>server.json</code> name exactly.</li><li>☐ <code>server.json</code> passes <code>mcp-publisher validate</code>; the description fits in 100 characters; the version is new.</li><li>☐ You published under a namespace you control (GitHub user or org you own, or your domain).</li><li>☐ You requested GitHub onboarding, claimed the Glama listing, and submitted to Smithery, Docker and Index Agentica if they fit.</li></ul>\n<h2>Troubleshooting (from the registry docs)</h2>\n<table><thead><tr><th scope=\"col\">Error</th><th scope=\"col\">Fix</th></tr></thead><tbody><tr><td>&quot;Registry validation failed for package&quot;</td><td>The ownership marker is missing or doesn&#39;t match (<code>mcpName</code>, <code>mcp-name:</code> or the OCI annotation).</td></tr><tr><td>&quot;Invalid or expired Registry JWT token&quot;</td><td>Log in again with <code>mcp-publisher login ...</code>.</td></tr><tr><td>&quot;You do not have permission to publish this server&quot;</td><td>The name doesn&#39;t match your auth method, for example a GitHub login publishing outside <code>io.github.&lt;you&gt;/</code>.</td></tr><tr><td>DNS auth fails with a signature error</td><td>The TXT record must be on the apex, not under a selector, and stale records from old keys must be removed.</td></tr></tbody></table>\n<p>Once you&#39;re listed, the other side of the flow is in <a href=\"/guides/connect-an-agent-to-a-remote-mcp-server/\">Connect an agent to a remote MCP server</a>. For auth design, see <a href=\"/guides/agent-auth-and-delegated-access/\">Agent auth and delegated access</a>.</p>"
}
