platform-mcp-stub
platform-mcp-stub
A minimal MCP server (TypeScript, stdio transport, official
@modelcontextprotocol/sdk) that demonstrates agent-native platform
onboarding with human-in-the-loop provisioning: an agent can discover its
usage and limits on its own, but getting a credential always routes through a
visible human approval gate.
Tools
Tool | What it does | Real vs fixture |
| Files a pending approval request in a local | The gate is real (ledger + CLI); the key is intentionally fake — this stub never touches real credential minting. |
| Last 7 days of org token usage, daily buckets grouped by model, via the Anthropic Usage Admin API ( | Live when |
| The org's configured rate-limit groups (per-model-group RPM / input-TPM / output-TPM, batch queue limits) via the Rate Limits Admin API ( | Same pattern: live with an admin key, labeled fixture without. |
The human-approval gate is the whole point: the agent's request, the human's decision, and the resulting (fake) credential are all visible in one plain-JSON ledger.
What is real vs fixture
Real: the MCP protocol surface (official SDK, stdio JSON-RPC), the approvals ledger and CLI flow, and — when
ANTHROPIC_ADMIN_KEYis present — the HTTP calls to the two Admin API endpoints above (correct headers:anthropic-version: 2023-06-01,x-api-key).Fixture: without an admin key,
get_usage_summary/get_limit_statusreturn synthetic data labeled_fixture: truewith a_note, matching the real response schemas so downstream handling is identical.Always fake: issued keys. Format
sk-ant-demo-<hex>; they mint nothing and authenticate nowhere.
Note: the Admin API requires an Admin API key (sk-ant-admin01-...),
which is a different credential from a regular Claude API key, and is only
available on organization (not individual) accounts.
Run it
npm install
npm run build
npm start # MCP server on stdio (Ctrl-C to stop)Demo the approval flow end to end:
# 1. In an MCP client (or via raw JSON-RPC — see below), call:
# request_api_key(scope="usage:read", justification="dashboard prototype")
# -> "pending human approval: apprq_xxxxxxxx"
# 2. As the human operator:
npm run approve -- list
npm run approve -- apprq_xxxxxxxx
# -> approved; prints the FAKE sk-ant-demo-... key
# (to reject instead: npm run approve -- deny apprq_xxxxxxxx)
# 3. Agent re-calls request_api_key (same scope, or scope="status:apprq_xxxxxxxx")
# -> gets the demo keyNo MCP client handy? Drive the server with raw JSON-RPC over stdio. Pipe
into node dist/server.js directly — not npm start, because npm prints
its script banner to stdout, which pollutes the JSON-RPC stream:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"probe","version":"0.0.1"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"request_api_key","arguments":{"scope":"usage:read","justification":"dashboard prototype"}}}' \
| node dist/server.jsThe same pattern works for get_usage_summary and get_limit_status
(empty arguments).
To exercise the live Admin API paths:
export ANTHROPIC_ADMIN_KEY=sk-ant-admin01-...
npm startWire into Claude Code
claude mcp add platform-stub -- node /absolute/path/to/platform-mcp-stub/dist/server.jsOptionally pass the admin key through:
claude mcp add platform-stub -e ANTHROPIC_ADMIN_KEY=sk-ant-admin01-... -- \
node /absolute/path/to/platform-mcp-stub/dist/server.jsThen in a Claude Code session: "Request an API key for scope messages:write
and check our current usage and limits." Approve from a second terminal with
npm run approve -- <id>.
Honest scope notes (v1)
Evening-sized prototype, not a provisioning product: single-file JSON ledger, no auth on the approve CLI, no expiry/rotation, no real key minting (by design — the Admin API does support key management, which a v2 could gate behind the same approval flow).
Usage summary is a fixed query (7 days,
1dbuckets,group_by[]=model); the real endpoint supports much richer filtering/grouping.approvals.jsonlives at the repo root by default; override withAPPROVALS_FILE.
Requirements
Node 20+
ANTHROPIC_ADMIN_KEY(admin key,sk-ant-admin01-...) only for live usage/limit data — everything else works offline.