Skip to content

MCP Integration

Execbox has two MCP adoption paths. Use MCP providers when guest code should call upstream MCP tools. Use the code MCP server when an MCP client should call execbox.

Use @execbox/core/mcp to adapt an MCP tool catalog into a resolved provider. The provider can then be passed to any execbox executor.

import { openMcpToolProvider } from "@execbox/core/mcp";
import { QuickJsExecutor } from "@execbox/quickjs";
const handle = await openMcpToolProvider({ server: upstreamServer });
try {
const executor = new QuickJsExecutor();
const result = await executor.execute(
'(await mcp.search_docs({ query: "quickjs" })).structuredContent',
[handle.provider],
);
console.log(result);
} finally {
await handle.close();
}

Use createMcpToolProvider({ client }) when the caller owns an already connected MCP client. Use openMcpToolProvider({ server }) when execbox opens a local server connection and should return a cleanup handle.

Use codeMcpServer() when downstream MCP clients should execute code against a wrapped tool namespace.

import { codeMcpServer } from "@execbox/core/mcp";
import { QuickJsExecutor } from "@execbox/quickjs";
const server = await codeMcpServer(
{ client: upstreamClient },
{ executor: new QuickJsExecutor() },
);

The wrapper server exposes:

ToolPurpose
mcp_search_toolsSearch the wrapped MCP catalog with concise metadata
mcp_get_tool_detailsInspect schemas and generated types for one selected tool
mcp_execute_codeExecute guest JavaScript against the wrapped catalog

The default codeMcpServer() mode is progressive: MCP clients can search the catalog, inspect only the tools they need, then execute code. Use mode: "single" only when a client needs the legacy all-in-one mcp_code tool whose description embeds the full generated namespace. Use mode: "both" to expose the progressive tools and mcp_code together.

const search = await client.callTool({
name: "mcp_search_tools",
arguments: { query: "search docs" },
});
const details = await client.callTool({
name: "mcp_get_tool_details",
arguments: { safeName: "search_docs" },
});

mcp_search_tools returns only names, descriptions, and annotations. mcp_get_tool_details returns the selected tool’s input schema, output schema, and generated TypeScript declaration.

Wrapped MCP tools preserve MCP CallToolResult envelopes. Guest code can read structuredContent first and fall back to content when a tool only returns text or other MCP content items.

const result = await mcp.search_docs({ query: "quickjs" });
result.structuredContent?.hits ?? result.content;

MCP integration changes where tools come from or how execbox is exposed. The provider surface remains the capability boundary:

  • wrap only the MCP tools a caller should be able to invoke
  • keep upstream clients, secrets, and tenant routing in host code
  • close handles returned by openMcpToolProvider()
  • choose inline or worker-hosted QuickJS separately from the MCP adapter shape

The code-execution tools are annotated as potentially destructive because MCP tool annotations are static while guest code can call any wrapped tool exposed through the provider. Search and details tools are annotated read-only.

Next: