Publish an MCP server
Ship a durable, named MCP endpoint your customers add to Claude, Cursor, or any MCP client — backed by a server you curate, guarded by OAuth or API keys.
Scenario: Acme wants a headline feature: "Add Acme to Claude." Every Acme customer gets an MCP endpoint that exposes Acme's actions — search tickets, file a ticket, pull a report — usable from any MCP client their team already runs. No MCP protocol code, no hosting, no token plumbing on Acme's side.
Per-user sessions are ephemeral credentials you mint per conversation. A published server is the opposite: a durable, named endpoint with its own inbound auth, meant to be configured once in someone's client and kept.
1. Curate the server it serves
A public endpoint serves exactly one server — decide the surface first:
const toolset = await protobox.toolsets.create({
name: 'Acme for AI clients',
description: 'Search, file, and update Acme tickets.',
tools: [/* tool ids — e.g. from your registered private connector */],
});Your own product's actions come from registering your API as a connector; catalog tools can sit in the same server.
2. Publish
const server = await protobox.mcpServers.create({
slug: 'acme',
name: 'Acme',
toolsetId: toolset.id,
preset: 'direct', // serve tools flat (default)
inboundAuth: { mode: 'protobox_oauth' }, // or 'api_key' — see below
});
console.log(server.url);
// e.g. https://<your-workspace>.protobox.app/mcp/srv/acmeThat URL is live immediately. preset: 'direct' serves the server's tools flat — what most clients expect; 'meta' serves search/execute meta-tools instead, which scales better past ~40 tools.
3. Choose inbound auth
The fully-served option: clients hitting the URL get a standards-compliant MCP OAuth flow, hosted end-to-end. Best for endpoints humans configure in Claude or Cursor — nothing to copy around.
inboundAuth: { mode: 'protobox_oauth' }For machine callers and simple rollouts: mint named keys, hand them out, revoke per-customer.
const server = await protobox.mcpServers.create({
slug: 'acme',
name: 'Acme',
toolsetId: toolset.id,
inboundAuth: { mode: 'api_key' },
});
const key = await protobox.mcpServers.keys.create('acme', { label: 'initech-corp' });
// key.key is the plaintext — shown ONCE, hashed at rest.
// Callers present it as `X-API-Key: <key>` or `Authorization: Bearer <key>`.Operate the keyring per customer:
await protobox.mcpServers.keys.list('acme'); // prefixes + last4, never secrets
await protobox.mcpServers.keys.revoke('acme', key.id); // offboarding — immediateServing one customer per server
If you keep your own customers apart by Protobox caller — one workspace, one caller id per customer, apps connected with protobox.entity(customerId).connect(...) — say who the server acts for:
const server = await protobox.mcpServers.create({
slug: 'initech',
name: 'Initech',
toolsetId: toolset.id,
inboundAuth: { mode: 'api_key' },
callerId: 'initech-corp', // the same id you connect that customer's apps under
});Every request authenticated by one of this server's keys now executes as that caller, so a tool reaches the accounts that customer connected. Leave callerId out and the acting identity is the key itself — which owns no connections, so credential-backed tools answer auth_required.
Already published a server without it? Bind it in place; the URL and the toolset are untouched:
await protobox.mcpServers.update('initech', { callerId: 'initech-corp' });callerId applies to the API-key path only. On protobox_oauth, the caller is the human who authenticated, and this never overrides them.
Operate it
// Evolve the surface — served live, no client reconfiguration:
await protobox.mcpServers.update('acme', { toolsetId: newToolset.id });
// Kill switch — resolution stops immediately:
await protobox.mcpServers.update('acme', { status: 'disabled' });
// Inventory:
const { items } = await protobox.mcpServers.list({ status: 'active' });Because the endpoint binds a server reference, editing the server (adding tools, renaming via overrides) updates what every connected client sees — no re-publish step.
What your customer does
One-time setup in their client, using your onboarding doc:
{
"mcpServers": {
"acme": { "url": "https://<workspace>.protobox.app/mcp/srv/acme" }
}
}With protobox_oauth, their client walks them through sign-in on first use. With api_key, they add the header with the key you issued.
On the roadmap: serving published servers from your own domain (mcp.acme.com), and federating inbound OAuth to your existing identity provider so sign-in is your login page. Today the endpoint lives under *.protobox.app and OAuth is Protobox-hosted.
Production notes
- One endpoint per audience. A
supportserver for agents, anadminserver for internal ops — separate servers, separate auth, separate blast radius. - Label keys by customer (
label: 'initech-corp') so revocation maps to offboarding without a lookup table. - Watch it: executions served through the endpoint appear in the same execution log as everything else.
Related
- Your API as agent tools — put your product's actions behind the endpoint
- MCP overview — the workspace MCP endpoint and serving model
White-label
Keep Protobox invisible: consent screens that carry your brand, tool names that speak your product's vocabulary, and connect flows that live in your UI.
Per-user MCP sessions
Mint a scoped, revocable MCP endpoint for each of your end-users or conversations — one URL that serves exactly the tools that user may use, with their credentials.