The MCP (Model Context Protocol) server is the AI-facing parallel to the REST API. Where /api/v1/* is shaped for human-written code (one URL per resource, query strings, REST verbs), /api/mcp is shaped for LLMs: a single JSON-RPC 2.0 endpoint that exposes a catalog of tools the model can call and resources it can read. Use it to wire Platform Health into Claude Desktop, Claude Code, custom Anthropic SDK apps, ChatGPT custom GPTs, or any other MCP-aware client.
Endpoint
POST https://api.joinplatformhealth.com/api/mcp
- Auth. Same
Authorization: Bearer ph_{env}_… key as the REST API. Keys carry the same scopes; each tool requires the matching scope. Forbidden calls return JSON-RPC code -32002. - Transport. JSON-RPC 2.0 over HTTPS POST. One request per HTTP call; response is a single JSON-RPC envelope. SSE/WebSocket streaming isn’t implemented in v1 and isn’t required for any current client.
- Rate limit. 600 req/min/key, separate bucket from REST. 429 returns JSON-RPC code
-32003 and a Retry-After header. - Audit + telemetry. Every tool call lands in the same audit log as REST, and the per-key usage counters on
Settings → API keys include MCP traffic.
Methods
initialize — handshake. Returns protocolVersion, capabilities, and serverInfo.tools/list — return the catalog (name, description, JSON-Schema input). Call this from your client at session start.tools/call — invoke a tool. Params: { name, arguments }. Result: { content: [{ type: "text", text }], isError? }.resources/list — list read-only documents the agent can pull into context.resources/read — fetch one resource by URI.
Tool catalog
Authoritative list comes from tools/list at runtime; this table is for browsing.
| Tool | Scope | Summary |
|---|
| list_orders | orders.read | List recent orders. Supports `limit` + `cursor` keyset pagination. |
| get_order | orders.read | Fetch one order by id. |
| list_products | products.read | List the calling tenant’s product catalog. |
| get_product | products.read | Fetch one product by id. |
| create_product | products.write | Create a new product. Args: title, summary, description, priceMinorUnits, category. |
| update_product | products.write | Partial-update a product. Pass productId + any of the editable fields. |
| list_customers | customers.read | List storefront customers (PII included; key-gated). |
| list_subscriptions | subscriptions.read | List the calling tenant’s recurring subscriptions. |
| pause_subscription | subscriptions.write | Pause a subscription indefinitely. |
| cancel_subscription | subscriptions.write | Cancel a subscription. Terminal. |
| list_tickets | support.read | List support tickets. Optional `status` filter. |
| reply_to_ticket | support.write | Append a reply (or `internal: true` note) to a support ticket. |
Resources
| URI | MIME | Summary |
|---|
| ph://orders | application/json | Up to 100 most recent orders, newest first. |
| ph://products | application/json | Current product catalog. |
| ph://customers | application/json | Storefront customer roster. |
| ph://docs/api | text/markdown | A condensed Markdown view of this REST reference so the agent can ground itself before calling tools. |
Examples
Discover the catalog:
curl -X POST https://api.joinplatformhealth.com/api/mcp \
-H "Authorization: Bearer ph_live_..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}'Call a tool:
curl -X POST https://api.joinplatformhealth.com/api/mcp \
-H "Authorization: Bearer ph_live_..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "list_orders",
"arguments": { "limit": 10 }
}
}'Claude Desktop
Add an entry to claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json) and restart the app. The desktop client will pick up the tool catalog automatically.
{
"mcpServers": {
"ph": {
"url": "https://api.joinplatformhealth.com/api/mcp",
"headers": {
"Authorization": "Bearer ph_live_..."
}
}
}
}Errors
-32700 parse error — body wasn’t valid JSON.-32600 invalid request — envelope wasn’t a JSON-RPC 2.0 request.-32601 method not found — unknown method, tool, or resource URI.-32602 invalid params — arguments failed schema validation.-32603 internal error — unexpected server-side failure.-32001 auth_required — missing/invalid bearer.-32002 forbidden — key is missing the tool’s required scope.-32003 rate_limited — 600 req/min/key budget exhausted; back off per Retry-After.
Note: tool-level errors (a not-found id, a validation message from the handler) come back inside a successful JSON-RPC response with isError: true on the content payload — that lets the model see the message and recover, rather than treating it as a protocol failure.