MCP server (Model Context Protocol)
An MCP server (Model Context Protocol server) exposes tools an AI coding agent can call directly over a standard transport, instead of a bespoke per-agent integration. Caisson's buyer-facing MCP server authenticates every call with a timing-safe Bearer token, gates each tool to the caller's owned entitlements, rate-limits per account, and lets an agent generate a licensed project without a browser.
In code
async function handleToolCall(
session: McpSession,
tool: string,
args: unknown,
): Promise<unknown> {
const registration = registry.get(tool);
// An unregistered tool, and a tool the caller is not entitled to, are both 404: the edition
// tool is invisible, never leaking that it exists to a non-entitled caller.
if (
registration === undefined ||
!isEntitled(session, registration.requiredEntitlement)
) {
const retired = retiredTools.get(tool);
if (retired !== undefined) throw new RetiredToolError(retired);
throw new NotFoundError(`Unknown tool: ${tool}`);
}
// Abuse-throttle gate (ADR-0112): awaited before dispatching any tool, base or edition. A
// genuine deny throws RateLimitError (429); a store fault resolves fail-open (ADR-0112 lock 5).
if (options.checkRateLimit !== undefined) {
await options.checkRateLimit(session.accountId);
}
return registration.handler({ session, args });
}How it holds
Tools are registered, not hardcoded
Bundles call registerTool() to add their own buyer tools through the same seam the three base tools (list_modules, describe_module, generate) use; retireTool() marks a name retired (410) instead of silently vanishing, so a name is always exactly one of active, retired, or unknown.
Constant-time entitlement gate
isEntitled() scans every owned entitlement with no early return and compares each one timing-safe, so a bundle tool a buyer does not own renders the identical 404 a nonexistent tool would, never leaking which is which.
Rate-limited before every dispatch, not just auth-gated
The checkRateLimit hook is awaited before any tool handler runs, for both base and bundle tools. A genuine over-limit throws a 429, but a rate-limit store fault fails open (resolves and alerts) so an infrastructure blip never locks out a paying buyer.
One core, two transports
The same createMcpServer core binds to a local stdio process (one connection, one buyer) and a network-reachable Streamable-HTTP listener that re-authenticates every request statelessly and refuses to start without an explicit host and origin allowlist (ADR-0161).