Skip to content

Commit 895bf01

Browse files
Merge pull request #11 from Longhorn-Developers/fixing-readme
Read me Updated and now using server instead of agent as primary term.
2 parents 7584c18 + 320e26e commit 895bf01

File tree

14 files changed

+676
-345
lines changed

14 files changed

+676
-345
lines changed

.oxfmtrc.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

README.md

Lines changed: 33 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,53 @@
1-
# Building a Remote MCP Server on Cloudflare (Without Auth)
1+
# Spark Plus
22

3-
This example allows you to deploy a remote MCP server that doesn't require authentication on Cloudflare Workers.
3+
A **collection of multiple separate but related MCP servers** on Cloudflare Workers for UT's AI Spark system. Each server runs as a Durable Object with its own tools and URL path, so it is treated as a separate MCP server for all intents and purposes.
44

5-
## Get started:
5+
## What’s in this repo
66

7-
[Deploy to Workers](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-authless)
7+
- **Servers** – MCP servers defined with `defineMcpServer` / `defineTool`, each bound to a Durable Object and a route.
8+
- **Shared** – All shared funcionality between servers.`src/shared/mcp-server-creator.ts` is where the `defineMcpServer` function which is how all servers are created.
89

9-
This will deploy your MCP server to a URL like: `remote-mcp-server-authless.<your-account>.workers.dev/sse`
10-
11-
Alternatively, you can use the command line below to get the remote MCP Server created on your local machine:
10+
## Quick start
1211

1312
```bash
14-
npm create cloudflare@latest -- my-mcp-server --template=cloudflare/ai/demos/remote-mcp-authless
13+
npm install
14+
npm run dev
1515
```
1616

17-
## Customizing your MCP Server
18-
19-
To add your own [tools](https://developers.cloudflare.com/agents/model-context-protocol/tools/) to the MCP server, define each tool inside the `init()` method of `src/index.ts` using `this.server.tool(...)`.
17+
The Worker runs at `http://localhost:8787`. Each MCP server is served at its path (e.g. `http://localhost:8787/basic-tester`, `http://localhost:8787/other`).
2018

21-
## Test with MCP Inspector (local)
19+
## Deploy
2220

23-
This server uses **Streamable HTTP** at `/mcp`, not stdio. In the Inspector:
24-
25-
1. Run `npm run dev` so the Worker is at `http://localhost:8787` (or the port Wrangler prints).
26-
2. Run `npx @modelcontextprotocol/inspector@latest` and open the URL it prints.
27-
3. In the Inspector, choose **"Enter URL"** (or equivalent) — do **not** use "Run command (stdio)".
28-
4. Enter `http://localhost:8787/mcp` and click Connect, then List tools.
21+
```bash
22+
npm run deploy
23+
```
2924

30-
Using "Run command" / stdio will fail with a 500 because this server is HTTP-only.
25+
Uses the `spark-plus` Worker name in `wrangler.jsonc`. After deploy, server URLs are `https://spark-plus.<your-subdomain>.workers.dev/<server-path>`.
3126

32-
## Connect to Cloudflare AI Playground
27+
## Adding a server
3328

34-
You can connect to your MCP server from the Cloudflare AI Playground, which is a remote MCP client:
29+
1. **Implement the server** in `src/servers/<name>/main.ts` using `defineMcpServer` and `defineTool` from `src/shared/mcp-server-creator.ts`.
30+
2. **Register the Durable Object** in `wrangler.jsonc`: add a migration for the new class and a binding in `durable_objects.bindings`.
31+
3. **Wire the route** in `src/project-source.ts`: import the server class and its metadata, add the metadata to the `MCP_SERVERS` array, and export the class in the `export { ... }` at the bottom.
3532

36-
1. Go to [https://playground.ai.cloudflare.com/](https://playground.ai.cloudflare.com/)
37-
2. Enter your deployed MCP server URL (`remote-mcp-server-authless.<your-account>.workers.dev/sse`)
38-
3. You can now use your MCP tools directly from the playground!
33+
The request handler in `src/project-source.ts` matches the request path to each server’s `url_prefix` and forwards to that server.
3934

40-
## Connect Claude Desktop to your MCP server
35+
## Testing with MCP Inspector
4136

42-
You can also connect to your remote MCP server from local MCP clients, by using the [mcp-remote proxy](https://www.npmjs.com/package/mcp-remote).
37+
This project uses **Streamable HTTP**, not stdio:
4338

44-
To connect to your MCP server from Claude Desktop, follow [Anthropic's Quickstart](https://modelcontextprotocol.io/quickstart/user) and within Claude Desktop go to Settings > Developer > Edit Config.
39+
1. Run `npm run dev` so the Worker is at `http://localhost:8787`.
40+
2. Run `npx @modelcontextprotocol/inspector@latest` and open the URL it prints.
41+
3. In the Inspector, choose **“Enter URL”** (do not use “Run command (stdio)”).
42+
4. Enter a server URL, e.g. `http://localhost:8787/basic-tester`, then Connect and List tools.
4543

46-
Update with this configuration:
44+
## Scripts
4745

48-
```json
49-
{
50-
"mcpServers": {
51-
"calculator": {
52-
"command": "npx",
53-
"args": [
54-
"mcp-remote",
55-
"http://localhost:8787/sse" // or remote-mcp-server-authless.your-account.workers.dev/sse
56-
]
57-
}
58-
}
59-
}
60-
```
46+
| Command | Purpose |
47+
| -------------------- | --------------------------------------------- |
48+
| `npm run dev` | Local development |
49+
| `npm run cf-typegen` | Regenerate Worker types after binding changes |
50+
| `npm run type-check` | TypeScript check |
51+
| `npm run lint` | Lint; `npm run lint:fix` to fix |
6152

62-
Restart Claude and you should see the tools become available.
53+
For Cloudflare Workers and product limits, see [AGENTS.md](./AGENTS.md).

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"version": "0.0.0",
44
"private": true,
55
"scripts": {
6-
"deploy": "wrangler deploy",
76
"dev": "wrangler dev",
87
"format": "oxfmt --write .",
98
"lint": "eslint .",

src/agents/basic-tester/index.ts

Lines changed: 0 additions & 70 deletions
This file was deleted.

src/agents/other/index.ts

Lines changed: 0 additions & 43 deletions
This file was deleted.

src/index.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/project-source.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { BasicTester, basicTesterData } from "./servers/basic-tester/main";
2+
import { OtherServer, otherData } from "./servers/other/main";
3+
4+
import { McpServerMetadata } from "./shared/general-types";
5+
6+
// + ------------------------------------------------------------------------------------------------ +
7+
// | An array of all the active MCP servers. To add a new MCP server, add the metadata to this array. |
8+
// + ------------------------------------------------------------------------------------------------ +
9+
const MCP_SERVERS = [basicTesterData, otherData] as McpServerMetadata[];
10+
11+
export default {
12+
fetch(request: Request, env: Env, ctx: ExecutionContext) {
13+
const url = new URL(request.url);
14+
15+
for (const mcpServer of MCP_SERVERS) {
16+
if (url.pathname === mcpServer.url_prefix) {
17+
return mcpServer.server
18+
.serve(mcpServer.url_prefix, { binding: mcpServer.binding })
19+
.fetch(request, env, ctx);
20+
}
21+
}
22+
23+
return new Response("Not found!!", { status: 404 });
24+
},
25+
};
26+
27+
// + ------------------------------------------------------------- +
28+
// | NOTE: Update this with all the MCP servers' class exports |
29+
// + ------------------------------------------------------------- +
30+
export { BasicTester, OtherServer }; // Required for cloudflare behind the scenes functionality.

src/servers/basic-tester/main.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { z } from "zod";
2+
import { defineMcpServer, defineTool } from "../../shared/mcp-server-creator";
3+
4+
function calculate(
5+
operation: "add" | "subtract" | "multiply" | "divide",
6+
a: number,
7+
b: number,
8+
): string {
9+
let result: number;
10+
switch (operation) {
11+
case "add":
12+
result = a + b;
13+
break;
14+
case "subtract":
15+
result = a - b;
16+
break;
17+
case "multiply":
18+
result = a * b;
19+
break;
20+
case "divide":
21+
if (b === 0) return "Error: Cannot divide by zero";
22+
result = a / b;
23+
break;
24+
}
25+
return String(result);
26+
}
27+
28+
export const { McpServerClass: BasicTester, metadata: basicTesterData } =
29+
defineMcpServer({
30+
name: "Basic Tester",
31+
version: "1.0.0",
32+
binding: "basic",
33+
url_prefix: "/basic-tester",
34+
tools: [
35+
defineTool({
36+
name: "add",
37+
inputSchema: { a: z.number(), b: z.number() },
38+
function: ({ a, b }) => ({
39+
content: [{ type: "text", text: String(a + b) }],
40+
}),
41+
}),
42+
defineTool({
43+
name: "subtract",
44+
inputSchema: { a: z.number(), b: z.number() },
45+
function: ({ a, b }) => ({
46+
content: [{ type: "text", text: String(a - b) }],
47+
}),
48+
}),
49+
defineTool({
50+
name: "calculate",
51+
inputSchema: {
52+
operation: z.enum(["add", "subtract", "multiply", "divide"]),
53+
a: z.number(),
54+
b: z.number(),
55+
},
56+
function: ({ operation, a, b }) => ({
57+
content: [{ type: "text", text: calculate(operation, a, b) }],
58+
}),
59+
}),
60+
],
61+
});

src/servers/other/main.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { z } from "zod";
2+
import { defineMcpServer, defineTool } from "../../shared/mcp-server-creator";
3+
4+
async function getQuote(page: number) {
5+
const response = await fetch(
6+
`https://gutenberg.org/cache/epub/1342/pg1342.txt`,
7+
);
8+
const text = await response.text();
9+
const lines = text.split("\n");
10+
return lines[page];
11+
}
12+
13+
export const { McpServerClass: OtherServer, metadata: otherData } =
14+
defineMcpServer({
15+
name: "Other Server",
16+
version: "1.0.0",
17+
binding: "other",
18+
url_prefix: "/other",
19+
tools: [
20+
defineTool({
21+
name: "randomNumber",
22+
inputSchema: { range: z.number().min(0).max(100) },
23+
function: async ({ range }) => ({
24+
content: [
25+
{
26+
type: "text",
27+
text: Math.floor(Math.random() * range).toString(),
28+
},
29+
],
30+
}),
31+
}),
32+
defineTool({
33+
name: "getQuote",
34+
inputSchema: { page: z.number() },
35+
function: async ({ page }) => ({
36+
content: [{ type: "text", text: await getQuote(page) }],
37+
}),
38+
}),
39+
defineTool({
40+
name: "Capitalize",
41+
inputSchema: { name: z.string() },
42+
function: async ({ name }) => ({
43+
content: [{ type: "text", text: name.toUpperCase() }],
44+
}),
45+
}),
46+
],
47+
});

0 commit comments

Comments
 (0)