Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/secure-exec-mcp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@secure-exec/mcp": minor
---

Add a secure-exec executor for Cloudflare Code Mode and re-export the Code Mode MCP server builders.
129 changes: 129 additions & 0 deletions packages/secure-exec-mcp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# `@secure-exec/mcp`

Run Cloudflare Code Mode MCP servers in secure-exec instead of Cloudflare
Dynamic Workers. Each execution gets a fresh secure-exec VM, denied guest
network access by default, and controlled host capabilities for Code Mode
providers and connectors.

This package re-exports Cloudflare's `codeMcpServer` and `openApiMcpServer`
wrappers with a `SecureExecExecutor` implementation. It runs on Node.js and
does not require Cloudflare Workers or the Dynamic Worker Loader API.

## Install

The package is currently available from this fork and has not been published to
npm yet. From another pnpm workspace, point the dependency at a local checkout:

```sh
pnpm add @secure-exec/mcp@file:../agents/packages/secure-exec-mcp
```

After the first release, the normal install command will be
`pnpm add @secure-exec/mcp`.

## OpenAPI Code Mode MCP server

```ts
import { SecureExecExecutor, openApiMcpServer } from "@secure-exec/mcp";

const server = openApiMcpServer({
name: "acme-api",
version: "1.0.0",
spec: await fetch("https://api.acme.test/openapi.json").then((response) =>
response.json()
),
executor: new SecureExecExecutor(),
request: async (options, context) => {
const url = new URL(options.path, "https://api.acme.test");
for (const [name, value] of Object.entries(options.query ?? {})) {
if (value !== undefined) url.searchParams.set(name, String(value));
}

const response = await fetch(url, {
method: options.method,
headers: {
authorization: `Bearer ${process.env.ACME_API_TOKEN}`,
"content-type": options.contentType ?? "application/json"
},
body:
options.body === undefined
? undefined
: options.rawBody
? (options.body as BodyInit)
: JSON.stringify(options.body),
signal: context.signal
});

return response.json();
}
});
```

The MCP server exposes `search({ code })` and `execute({ code })`. API
credentials stay in the trusted `request` callback and are never projected into
the VM.

## Wrap an existing MCP server

```ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { codeMcpServer, SecureExecExecutor } from "@secure-exec/mcp";

const upstream = new McpServer({
name: "acme-tools",
version: "1.0.0"
});

// Register the normal upstream tools on `upstream`, then replace their large
// tool catalog with one Code Mode tool backed by a secure-exec VM.
const server = await codeMcpServer({
server: upstream,
executor: new SecureExecExecutor()
});
```

`codeMcpServer` discovers the upstream server through an in-memory MCP
transport. Generated code sees the discovered tools as typed `codemode.*`
methods, while the actual handlers and their credentials stay in trusted host
code.

## Use the executor directly

`SecureExecExecutor` implements the portable `Executor` contract from
`@cloudflare/codemode`:

```ts
const executor = new SecureExecExecutor({ timeout: 30_000 });

const result = await executor.execute(
`async () => {
const users = await api.listUsers({ active: true });
return users.map(({ id, email }) => ({ id, email }));
}`,
[
{
name: "api",
fns: {
listUsers: async (input) => trustedApiClient.listUsers(input)
}
}
]
);
```

Provider callbacks run on the trusted host. Generated code runs in the VM and
can only reach those callbacks through secure-exec's schema-validated binding
path.

## Validate the integration

```sh
pnpm --dir packages/secure-exec-mcp test
pnpm --dir packages/secure-exec-mcp build
```

The integration suite connects an actual MCP SDK client to both Cloudflare Code
Mode wrappers. It verifies the OpenAPI `search` and `execute` tools, wraps and
calls a tool on an ordinary MCP server through the `code` tool, executes the
generated JavaScript in secure-exec, and confirms that privileged handlers run
through trusted host callbacks.
45 changes: 45 additions & 0 deletions packages/secure-exec-mcp/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "@secure-exec/mcp",
"version": "0.0.1",
"description": "Run Cloudflare Code Mode MCP servers with secure-exec",
"type": "module",
"license": "MIT",
"repository": {
"directory": "packages/secure-exec-mcp",
"type": "git",
"url": "git+https://github.com/rivet-dev/cloudflare-agents.git"
},
"bugs": {
"url": "https://github.com/rivet-dev/cloudflare-agents/issues"
},
"author": "Rivet",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist",
"README.md"
],
"scripts": {
"build": "tsx ./scripts/build.ts",
"pretest": "pnpm --filter @cloudflare/codemode build",
"test": "vitest run"
},
"dependencies": {
"@cloudflare/codemode": "workspace:*",
"@modelcontextprotocol/sdk": "1.29.0",
"acorn": "^8.17.0",
"secure-exec": "^0.3.3"
},
"devDependencies": {
"tsdown": "^0.22.3",
"vitest": "4.1.9"
},
"engines": {
"node": ">=22"
}
}
24 changes: 24 additions & 0 deletions packages/secure-exec-mcp/scripts/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { build } from "tsdown";
import { formatDeclarationFiles } from "../../../scripts/format-declarations";

async function main() {
await build({
clean: true,
dts: true,
entry: ["src/index.ts"],
deps: {
skipNodeModulesBundle: true
},
format: "esm",
sourcemap: true,
fixedExtension: false,
platform: "node"
});

formatDeclarationFiles();
}

main().catch((error) => {
console.error(error);
process.exit(1);
});
13 changes: 13 additions & 0 deletions packages/secure-exec-mcp/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export {
codeMcpServer,
openApiMcpServer,
type CodeMcpServerOptions,
type OpenApiMcpRequestContext,
type OpenApiMcpServerOptions,
type RequestOptions
} from "@cloudflare/codemode/mcp";

export {
SecureExecExecutor,
type SecureExecExecutorOptions
} from "./secure-exec-executor";
Loading