MCP vs. HTTP APIs (and when to use which)
Use MCP for agent intent. Use product APIs for product services, UI calls, backend integrations, and the ADR-0019 agent carve-outs.
Decision table
| Workload | Surface |
|---|---|
| LLM or IDE agent reading tenant state | MCP by default |
| Agent proposing or executing a customer-facing financial write | MCP |
| Agent code-mode orchestration with many reads or non-financial writes | Product APIs, with MCP calls for any financial-state write |
| Read-heavy dashboard or export | Product APIs |
| Bulk job, batch endpoint, streaming response, or ETL | Product APIs |
| Human product UI | Product APIs |
| Webhook, callback, storefront SDK, UCP/ACP shopping protocol | Product APIs or protocol routes |
| Internal platform diagnostics | Internal MCP or internal APIs, depending on the route |
What MCP gives agents
MCP is more than transport. The Worker catalog and router add:
- Typed tool inputs from
apps/ai-coo-cf/src/tools/catalog.ts. - Tenant and role filtered visibility.
- T1 through T4 risk tiers.
- Generated
inblack_simulate_*tools for write previews. - Pending actions for T3 and allowed T4 writes.
- Hard blocks for curated destructive T4 tools.
INBLACK_*error codes.- Provenance and audit hooks for writes.
Use MCP when those semantics matter, especially when the caller is an agent making decisions on behalf of a customer.
What APIs give code
The product APIs are the service layer. They are better for deterministic software that already knows the exact query, route, or mutation it needs.
Use APIs when you need:
- GraphQL field selection.
- REST routes with stable resource paths.
- Batch or streaming behavior.
- Storefront SDK traffic.
- UCP/ACP signed shopping traffic.
- Code-mode execution where many API calls run as one reviewed unit.
Code-mode is still bound by the financial-write rule. If the code needs to post a journal entry, issue a refund, adjust an account, or otherwise mutate financial state, it must invoke the MCP tool rather than calling the underlying GraphQL/REST mutation directly.
Example: read-heavy API work
An agent generating a custom analytics report can use the reporting REST API for bulk data reads:
GET /api/v1/reporting/report-runs?limit=100Authorization: Bearer <token>X-Tenant-ID: <tenant-id>That is allowed because the workload is read-heavy and API-shaped.
Example: financial write
An agent that needs to post a manual journal entry uses MCP:
{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "inblack_create_journal_entry", "arguments": { "memo": "Stripe payout reconciliation", "lines": [ { "account_id": "acct_cash", "type": "debit", "amount": 500 }, { "account_id": "acct_revenue", "type": "credit", "amount": 500 } ] } }}The tool is T3 and creates a pending action. Direct GraphQL may exist for first-party product flows, but an agent should not bypass MCP for this class of write.
Per-service API map
| Service | Surface |
|---|---|
portal-api | GraphQL /graphql; REST /api/billing/*, /api/external-agent/signup, model negotiation routes |
accounting-api | GraphQL /graphql |
commerce-api | GraphQL /graphql; imports REST under /api/v1/; storefront REST under /storefront/v1/; signed agentic commerce under /agentic/ |
customer360-api | GraphQL /graphql |
studio-api | GraphQL /graphql; GitHub webhook at /api/v1/webhooks/github |
reporting-api | REST under /api/v1/reporting |