Hosted MCP server
Devotel Orbit can be exposed as an MCP (Model Context Protocol) server, so an external MCP client — Claude, ChatGPT, or an IDE agent — operates your tenant through a curated catalog of platform operations. This page is the conceptual anchor for that surface: why it exists, what the catalog contains, how scope resolution decides which tools a given API key can see, and how the authorize layer composes with the platform’s normal role and scope gates. Task-level walkthroughs (client setup, protocol handshake) link out at the end; this page defines the model they all reference.Section 1 — Why Orbit hosts an MCP server
Most integrations talking to Orbit are outbound MCP consumers: your Orbit agents register third-party MCP servers (for example, a CRM or a calendar provider) and call their tools. This surface is the reverse direction. One curated JSON-RPC endpoint —POST /api/v1/mcp (with a GET probe that
returns the capability descriptor) — lets an external client drive your
tenant through a fixed catalog of Orbit’s own platform operations.
The design choice is deliberate: an MCP client gets a catalog, not
arbitrary tool injection. Every tool in the catalog dispatches to an
already-shipped, already-gated REST route — sender resolution, quota
checks, TCPA/quiet-hours gates, provider routing, and audit writes all run
through the same code path a normal API call would. The MCP layer is a
protocol adapter over the platform, not a second implementation of it.
Nothing here depends on a local npx process or a client-side broker —
the server is hosted behind the API gateway, and authentication is the
platform’s normal API-key bearer (X-API-Key or
Authorization: Bearer <key>).
Section 2 — The curated tool catalog
The catalog is intentionally small and read-heavy: one write tool per chargeable surface (send an SMS, place a call) plus read tools for campaigns, numbers, analytics, and CDP profiles. Each tool declares the same OAuth-style scope string the underlying REST route enforces — there is no MCP-specific scope namespace to keep in sync.
Two names cover the same voice-verb dispatch (
place_call, and the
Twilio-parity alias send_voice_call with optional answer_url
call-control). Outbound voice still exits only via the Devotel softswitch —
these tools call back into the already-gated REST route; they wire nothing
new to a carrier.
Each tool’s inputSchema is a JSON-Schema subset (type, properties,
required) so an MCP client can render argument forms or validate input
before it calls. A required-argument check runs before dispatch, so a
missing campaign_id on get_campaign errors cleanly instead of
silently collapsing onto the list route.
Section 3 — Default-deny scope resolution
Scope resolution answers “which of the catalog’s tools does this key’sscopes array grant access to” at tools/list time, and again at
tools/call time:
- A key with no scopes sees zero tools. An empty or absent scopes
array unlocks nothing —
tools/listreturns an emptytools[], and everytools/callis rejected. - The
*wildcard scope opens the whole catalog. Full-access keys enumerate every catalogued tool, matching what*means everywhere else on the platform. - A scope the catalog doesn’t carry unlocks nothing. Granting
voice:readto a key whose tools only acceptvoice:writedoesn’t leak voice tools into the list.
tool not found —
so a disallowed tool’s mere existence is never leaked.
The legacy “scopeless key = full access” grandfather window that the
platform’s general-purpose API routes honour for long-lived keys is
deliberately not honoured here. The MCP surface is new, so every
caller must opt in with explicit scopes — a brand-new attack surface for
external LLM agents gets tightened defaults, not the laxest historical
semantics.
tools/list to refresh the catalog for that key.
Section 4 — Transport and handshake
The endpoint is a single JSON-RPC 2.0 service that always replies with a plain JSON body — it never upgrades to an SSE stream. The session flows:- initialize — capability negotiation; returns the protocol version
and the server descriptor (
name: orbit, plus its version). - tools/list — enumerate the scoped catalog (Section 3).
- tools/call — dispatch one tool by name with arguments.
- notifications/initialized — acknowledged per JSON-RPC notification semantics (no response body required).
initialize, and only on success does the page
issue tools/list to render the catalog rows. An empty result renders
an explicit “no tools in scope” message rather than hanging on an empty
list.
Section 5 — Authorization vs role gates
Two distinct gates stack on this surface:- Scope gates (
requireScope) decide which catalog entries the key may see and call. The scope claim lives on the API key and is resolved per tool, as in Section 3. - Role gates (
requireRole) decide whether the caller’s derived role is allowed onto a given class of route at all — for example ananalytics:readtool still routes through a handler that may require a minimum dashboard role in addition to the scope.
req.ctx.tenant from the same credential, so an MCP caller
can never reach another tenant’s data.
Section 6 — Relationship to the guides
This page is the conceptual anchor for the surface. The task-level walkthroughs build on it:- Hosted server handshake → — protocol payload shapes and the dashboard probe flow in practice.
- Claude / Cursor client setup → — per-client configuration (Claude Desktop, Cursor, Claude Code, VS Code, Windsurf, Cline, Zed) against this endpoint.
- Connected apps → — managing third-party OAuth app scopes in the same scope taxonomy the catalog uses.
See also
- Tenant isolation → — why an injected request can’t reach another tenant.
- Developer portal → — the dashboard pillar hosting Developer → MCP Server.
- Connect Orbit to your AI coding assistant → — per-client walkthroughs for the surface this page defines.