Skip to main content

Worked MCP discovery sample

An MCP client (Claude, Cursor, a registry crawler) reads this manifest before it ever connects: it learns the hosted server’s transport URL, the MCP protocol revision it speaks, and exactly how to authenticate — then hands the user a one-click “add server” instead of asking for a URL.
GET /api/v1/public/mcp/.well-known/mcp.json — public, no API key required.
The body follows the official MCP Registry server.json shape, so aggregators can ingest it without a bespoke parser. Each field tells the client something actionable:
  • $schema / name / version — the registry identity of this server. name is the io.devotel/orbit-cpaas namespace the entry is published under; version tracks the hosted tool catalog and bumps when the catalog changes shape.
  • protocolVersion — the MCP protocol revision the server speaks (2025-06-18). A client checks this before negotiating a session, so an outdated client warns instead of failing mid-handshake.
  • remotes — the transport the client connects to. Single entry today: streamable-http at POST /api/v1/mcp — no stdio subprocess, no SSE fallback to configure.
  • documentation — the setup walkthrough (Claude and Cursor config blocks) at Connect Claude or Cursor.
  • auth — driven off your ordinary Orbit API key, passed as either Authorization: Bearer <key> or X-API-Key: <key>. Tool calls inherit the key’s scopes and tenant, so a read-scoped key gets read-scoped tools and nothing else.
The response sends Cache-Control: public, max-age=300, stale-while-revalidate=600, so aggregators can cache it at the edge and re-fetch at most every five minutes. Content is identical for every caller — the manifest describes the server, never your tenant; no tenant-specific data is exposed on this unsigned path.

Deciding errors

Unauthenticated discovery failures are rare. When a request does fail, the branch is one of these: The retry-vs-terminal decision table lives in the error-handling guide.