Agent ReadySign in

How to publish an MCP server card

A step-by-step guide to serving a valid /.well-known/mcp.json per SEP-1649. Required fields, transport, and OAuth metadata.

Last updated

What is an MCP server card?

An MCP server card is a JSON document published at /.well-known/mcp.json that advertises a Model Context Protocol server’s transport, endpoint, capabilities, and authentication requirements. Clients like Claude Desktop, Cursor, and Cline fetch the card to auto-configure without users typing connection details by hand. The discovery spec is SEP-1649, ratified 2025-11-25. See our validator for the full field-by-field requirements.

Where should mcp.json live?

Always at /.well-known/mcp.json on the domain serving your MCP endpoint. The /.well-known/ path is reserved by RFC 8615 for machine-readable discovery documents. If your server runs at api.example.com/mcp, the card belongs at api.example.com/.well-known/mcp.json — same origin as the server.

Step 1 — Decide your transport and endpoint

MCP supports several transports. Pick one that matches how clients reach your server:

  • streamable-http — remote HTTP server (recommended for public servers).
  • stdio — local server spawned by the client (Claude Desktop, Cursor).
  • websocket — bidirectional streaming, less common.

Decide the endpoint URL agents will connect to — usually a path like https://api.yourdomain.com/mcp. You’ll reference this in the card’s transport.endpoint field.

Step 2 — Create public/.well-known/mcp.json

In a Next.js project, add the file under public/ at the .well-known/ subpath. Next.js serves it at /.well-known/mcp.json with no additional config. For non-Next.js stacks, drop the file at whichever directory maps to your domain root and ensure your static server doesn’t hide dotfile-prefixed paths.

# from your Next.js project root
mkdir -p public/.well-known
touch public/.well-known/mcp.json

Step 3 — Add the required SEP-1649 fields

The minimum valid card contains five required fields: protocolVersion, serverInfo (with name and version), transport (with type and endpoint), capabilities (an object — empty is fine), and authentication.required.

{
  "protocolVersion": "2025-11-25",
  "serverInfo": {
    "name": "acme-weather",
    "version": "1.0.0",
    "description": "Live weather data for AI agents"
  },
  "transport": {
    "type": "streamable-http",
    "endpoint": "https://api.acme.dev/mcp"
  },
  "capabilities": {
    "tools": true,
    "resources": false,
    "prompts": false
  },
  "authentication": {
    "required": false
  }
}

capabilities fields are booleans that advertise which MCP feature surfaces your server implements: tools (callable functions), resources (read-only data), prompts (prompt templates).

Step 4 — Declare your authentication model

If your server is anonymous, set authentication.required to false and you’re done. If your server requires auth, set it to true and publish an OAuth Protected Resource metadata document at /.well-known/oauth-protected-resource per RFC 9728 and SEP-985 (also ratified 2025-11-25). That document declares your authorization server, scopes, and token endpoints.

Step 5 — Verify with curl and validate

Deploy or start your dev server, then send a HEAD request to confirm the file returns HTTP 200 with Content-Type: application/json.

curl -I https://api.acme.dev/.well-known/mcp.json

# Expected response:
# HTTP/2 200
# content-type: application/json

Then run your URL through the MCP card validator to confirm SEP-1649 compliance — all required fields present, transport type recognized, and (if applicable) OAuth metadata reachable.

How do I generate mcp.json dynamically?

When your server’s capabilities or endpoint vary by environment, generate the card from code instead of shipping a static file. Use a Next.js route handler at src/app/.well-known/mcp.json/route.ts:

// src/app/.well-known/mcp.json/route.ts
import { getServerInfo, getCapabilities } from "@/lib/mcp";

export async function GET() {
  const card = {
    protocolVersion: "2025-11-25",
    serverInfo: await getServerInfo(),
    transport: {
      type: "streamable-http",
      endpoint: process.env.MCP_ENDPOINT,
    },
    capabilities: await getCapabilities(),
    authentication: { required: false },
  };

  return Response.json(card, {
    headers: {
      "Cache-Control": "public, max-age=3600",
    },
  });
}

Response.json() sets the correct application/json Content-Type automatically. Add a sensible Cache-Control header so clients don’t hammer the route on every connect.

Common pitfalls when publishing mcp.json

  • Card on a different origin than the server. If your MCP endpoint is api.example.com/mcp, the card must be at api.example.com/.well-known/mcp.json — not on the apex domain.
  • Missing OAuth metadata when auth is required. If authentication.required is true but /.well-known/oauth-protected-resource returns 404, clients can’t complete the handshake.
  • Wrong Content-Type. If your CDN serves the card as text/plain or text/html, clients may refuse to parse it. Confirm application/json in the response headers.
  • Outdated protocolVersion. Old drafts used different date strings. The current ratified value is 2025-11-25. Update it when the spec version you support changes.
  • Auth-gated paths under /.well-known/. Some frameworks apply auth middleware to every route by default. Confirm /.well-known/mcp.json is publicly fetchable without credentials.

Frequently asked questions

Does my MCP server need a server card?
Yes if you want auto-discovery. Without /.well-known/mcp.json, users have to type your server's URL and config by hand into their MCP client. With the card, clients like Claude Desktop or Cursor can fetch your domain, find the card, and offer a one-click install. It's the same dynamic as robots.txt — voluntary, but expected for production servers.
What's the difference between mcp.json and server-card.json?
Older drafts proposed /.well-known/mcp/server-card.json. SEP-1649 ratified the simpler /.well-known/mcp.json path. Some clients still try both — publish at /.well-known/mcp.json and you cover the canonical path; add a copy at /.well-known/mcp/server-card.json if you want belt-and-braces.
Do I need to publish OAuth metadata too?
Only if your server requires authentication. When authentication.required is true in your card, MCP clients also fetch /.well-known/oauth-protected-resource per RFC 9728. That document declares your authorization server, supported scopes, and token endpoints. Anonymous servers can skip it entirely.
Where exactly should the file be served from?
At /.well-known/mcp.json on the domain agents will use to reach your MCP server. If your server runs at api.example.com/mcp, publish the card at api.example.com/.well-known/mcp.json (not example.com/.well-known/mcp.json). The card and the server must share an origin.
Can I version my MCP server card?
Yes, but versioning is per-field, not per-document. protocolVersion declares which MCP spec version your server supports (currently 2025-11-25). serverInfo.version is your server's own version. Update either independently when the underlying thing changes — they don't have to move in lockstep.