Skip to content

Workers bindings mcp #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 15, 2025
Merged
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
3 changes: 2 additions & 1 deletion apps/sandbox-container/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"build": "docker build .",
"start": "wrangler dev",
"start:container": "tsx container/index.ts",
"postinstall": "mkdir -p workdir"
"postinstall": "mkdir -p workdir",
"types": "wrangler types"
},
"dependencies": {
"@cloudflare/workers-oauth-provider": "0.0.2",
Expand Down
8 changes: 5 additions & 3 deletions apps/sandbox-container/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import OAuthProvider from '@cloudflare/workers-oauth-provider'
import { env } from 'cloudflare:workers'

import {
AccountSchema,
CloudflareAuthHandler,
handleTokenExchangeCallback,
UserSchema,
} from '@repo/mcp-common/src/cloudflare-oauth-handler'

import { ContainerManager } from './containerManager'
import { ContainerMcpAgent } from './containerMcp'

import type { AccountSchema, UserSchema } from '@repo/mcp-common/src/cloudflare-oauth-handler'

export { ContainerManager, ContainerMcpAgent }

export type Env = {
Expand All @@ -34,7 +35,8 @@ export default new OAuthProvider({
defaultHandler: CloudflareAuthHandler,
authorizeEndpoint: '/oauth/authorize',
tokenEndpoint: '/token',
tokenExchangeCallback: handleTokenExchangeCallback,
tokenExchangeCallback: (options) =>
handleTokenExchangeCallback(options, env.CLOUDFLARE_CLIENT_ID, env.CLOUDFLARE_CLIENT_SECRET),
// Cloudflare access token TTL
accessTokenTTL: 3600,
clientRegistrationEndpoint: '/register',
Expand Down
2 changes: 1 addition & 1 deletion apps/sandbox-container/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "bundler",
"types": ["@cloudflare/workers-types/2023-07-01"],
"types": ["./worker-configuration.d.ts", "@cloudflare/workers-types/2023-07-01"],
"noEmit": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
Expand Down
5,706 changes: 5,706 additions & 0 deletions apps/sandbox-container/worker-configuration.d.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions apps/workers-bindings/.dev.vars.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CLOUDFLARE_CLIENT_ID=
CLOUDFLARE_CLIENT_SECRET=
5 changes: 5 additions & 0 deletions apps/workers-bindings/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
root: true,
extends: ['@repo/eslint-config/default.cjs'],
}
178 changes: 178 additions & 0 deletions apps/workers-bindings/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
node_modules

.nx
.idea
.vscode
.zed
# Logs

logs
_.log
npm-debug.log_
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)

report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json

# Runtime data

pids
_.pid
_.seed
\*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover

lib-cov

# Coverage directory used by tools like istanbul

coverage
\*.lcov

# nyc test coverage

.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)

.grunt

# Bower dependency directory (https://bower.io/)

bower_components

# node-waf configuration

.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)

build/Release

# Dependency directories

node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)

web_modules/

# TypeScript cache

\*.tsbuildinfo

# Optional npm cache directory

.npm

# Optional eslint cache

.eslintcache

# Optional stylelint cache

.stylelintcache

# Microbundle cache

.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history

.node_repl_history

# Output of 'npm pack'

\*.tgz

# Yarn Integrity file

.yarn-integrity

# dotenv environment variable files

.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)

.cache
.parcel-cache

# Next.js build output

.next
out

# Nuxt.js build / generate output

.nuxt
dist

# Gatsby files

.cache/

# Comment in the public line in if your project uses Gatsby and not Next.js

# https://nextjs.org/blog/next-9-1#public-directory-support

# public

# vuepress build output

.vuepress/dist

# vuepress v2.x temp and cache directory

.temp
.cache

# Docusaurus cache and generated files

.docusaurus

# Serverless directories

.serverless/

# FuseBox cache

.fusebox/

# DynamoDB Local files

.dynamodb/

# TernJS port file

.tern-port

# Stores VSCode versions used for testing VSCode extensions

.vscode-test

# yarn v2

.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.\*

# wrangler project

.dev.vars
.wrangler/
111 changes: 111 additions & 0 deletions apps/workers-bindings/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Remote MCP Server on Cloudflare

Let's get a remote MCP server up-and-running on Cloudflare Workers complete with OAuth login!

## Develop locally

```bash
# clone the repository
git clone [email protected]:cloudflare/ai.git

# install dependencies
cd ai
npm install

# run locally
npx nx dev remote-mcp-server
```

You should be able to open [`http://localhost:8787/`](http://localhost:8787/) in your browser

## Connect the MCP inspector to your server

To explore your new MCP api, you can use the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector).

- Start it with `npx @modelcontextprotocol/inspector`
- [Within the inspector](http://localhost:5173), switch the Transport Type to `SSE` and enter `http://localhost:8787/sse` as the URL of the MCP server to connect to, and click "Connect"
- You will navigate to a (mock) user/password login screen. Input any email and pass to login.
- You should be redirected back to the MCP Inspector and you can now list and call any defined tools!

<div align="center">
<img src="img/mcp-inspector-sse-config.png" alt="MCP Inspector with the above config" width="600"/>
</div>

<div align="center">
<img src="img/mcp-inspector-successful-tool-call.png" alt="MCP Inspector with after a tool call" width="600"/>
</div>

## Connect Claude Desktop to your local MCP server

The MCP inspector is great, but we really want to connect this to Claude! Follow [Anthropic's Quickstart](https://modelcontextprotocol.io/quickstart/user) and within Claude Desktop go to Settings > Developer > Edit Config to find your configuration file.

Open the file in your text editor and replace it with this configuration:

```json
{
"mcpServers": {
"math": {
"command": "npx",
"args": ["mcp-remote", "http://localhost:8787/sse"]
}
}
}
```

This will run a local proxy and let Claude talk to your MCP server over HTTP

When you open Claude a browser window should open and allow you to login. You should see the tools available in the bottom right. Given the right prompt Claude should ask to call the tool.

<div align="center">
<img src="img/available-tools.png" alt="Clicking on the hammer icon shows a list of available tools" width="600"/>
</div>

<div align="center">
<img src="img/claude-does-math-the-fancy-way.png" alt="Claude answers the prompt 'I seem to have lost my calculator and have run out of fingers. Could you use the math tool to add 23 and 19?' by invoking the MCP add tool" width="600"/>
</div>

## Deploy to Cloudflare

1. `npx wrangler kv namespace create OAUTH_KV`
2. Follow the guidance to add the kv namespace ID to `wrangler.jsonc`
3. `npm run deploy`

## Call your newly deployed remote MCP server from a remote MCP client

Just like you did above in "Develop locally", run the MCP inspector:

`npx @modelcontextprotocol/inspector@latest`

Then enter the `workers.dev` URL (ex: `worker-name.account-name.workers.dev/sse`) of your Worker in the inspector as the URL of the MCP server to connect to, and click "Connect".

You've now connected to your MCP server from a remote MCP client.

## Connect Claude Desktop to your remote MCP server

Update the Claude configuration file to point to your `workers.dev` URL (ex: `worker-name.account-name.workers.dev/sse`) and restart Claude

```json
{
"mcpServers": {
"math": {
"command": "npx",
"args": ["mcp-remote", "https://worker-name.account-name.workers.dev/sse"]
}
}
}
```

## Debugging

Should anything go wrong it can be helpful to restart Claude, or to try connecting directly to your
MCP server on the command line with the following command.

```bash
npx mcp-remote http://localhost:8787/sse
```

In some rare cases it may help to clear the files added to `~/.mcp-auth`

```bash
rm -rf ~/.mcp-auth
```
31 changes: 31 additions & 0 deletions apps/workers-bindings/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "workers-bindings",
"version": "0.0.0",
"private": true,
"scripts": {
"check:lint": "run-eslint-workers",
"check:types": "run-tsc",
"deploy": "wrangler deploy",
"dev": "wrangler dev",
"start": "wrangler dev",
"types": "wrangler types",
"test": "vitest"
},
"devDependencies": {
"@cloudflare/vitest-pool-workers": "0.8.14",
"@cloudflare/workers-types": "4.20250410.0",
"@types/node": "22.14.1",
"marked": "15.0.7",
"typescript": "5.5.4",
"vitest": "3.0.9",
"wrangler": "4.10.0"
},
"dependencies": {
"@cloudflare/workers-oauth-provider": "0.0.2",
"@modelcontextprotocol/sdk": "1.8.0",
"@repo/mcp-common": "workspace:*",
"agents": "0.0.49",
"hono": "4.7.6",
"zod": "3.24.2"
}
}
Loading