> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP endpoint discovery — worked sample

> The full .well-known server.json manifest an MCP client reads before it connects, field by field.

## 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.

<Note>
  `GET /api/v1/public/mcp/.well-known/mcp.json` — public, no API key required.
</Note>

<ResponseExample>
  ```json 200 theme={null}
  {
    "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
    "name": "io.devotel/orbit-cpaas",
    "description": "Orbit's hosted remote MCP server: drive a caller's own Orbit CPaaS tenant (SMS, WhatsApp, voice, contacts, campaigns, analytics) over Streamable-HTTP JSON-RPC — no local process.",
    "version": "1.0.0",
    "protocolVersion": "2025-06-18",
    "remotes": [
      {
        "type": "streamable-http",
        "url": "https://api.orbit.devotel.io/mcp"
      }
    ],
    "documentation": "https://docs.orbit.devotel.io/guides/mcp-claude-cursor",
    "auth": {
      "type": "api-key",
      "headers": ["Authorization: Bearer <key>", "X-API-Key: <key>"],
      "docs": "https://docs.orbit.devotel.io/authentication",
      "note": "Bring your own Orbit API key (dv_live_sk_* / dv_test_sk_* for a zero-charge sandbox trial). Every tool call runs with that key's own scopes and tenant — there is no separate MCP-specific credential."
    }
  }
  ```
</ResponseExample>

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](/guides/mcp-claude-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:

| Class                      | Meaning                                                                                                 | Branch response                                                                                                                             |
| -------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| **429 `RATE_LIMITED`**     | The public-read ceiling for unsigned callers tripped.                                                   | **Retry after `error.details.retry_after`.** Identical for every caller, so cached copies are usually fresher than the retry window anyway. |
| **401/403 once connected** | Not from this endpoint — from `POST /api/v1/mcp` when the key you configured is missing or read-scoped. | **Fix the key, not the manifest.** The manifest stays valid; the session handshake is what rejected it.                                     |

The retry-vs-terminal decision table lives in the [error-handling guide](/guides/error-handling-examples).
