Debug Log
diff --git a/examples/servers/cf-agents/package.json b/examples/servers/cf-agents/package.json
index 180b46c..a9f909a 100644
--- a/examples/servers/cf-agents/package.json
+++ b/examples/servers/cf-agents/package.json
@@ -4,7 +4,7 @@
"private": true,
"scripts": {
"deploy": "wrangler deploy",
- "dev": "wrangler dev",
+ "dev": "wrangler dev --port 5102",
"format": "biome format --write",
"lint:fix": "biome lint --fix",
"start": "wrangler dev",
@@ -12,7 +12,7 @@
"type-check": "tsc --noEmit"
},
"dependencies": {
- "@modelcontextprotocol/sdk": "^1.13.0",
+ "@modelcontextprotocol/sdk": "^1.13.3",
"zod": "^3.25.67"
},
"devDependencies": {
@@ -22,6 +22,6 @@
"agents": "^0.0.100",
"hono": "^4.8.3",
"typescript": "^5.8.3",
- "wrangler": "^4.22.0"
+ "wrangler": "^4.23.0"
}
}
diff --git a/examples/servers/cf-agents/pnpm-lock.yaml b/examples/servers/cf-agents/pnpm-lock.yaml
index 7c3d7be..f0ff232 100644
--- a/examples/servers/cf-agents/pnpm-lock.yaml
+++ b/examples/servers/cf-agents/pnpm-lock.yaml
@@ -9,8 +9,8 @@ importers:
.:
dependencies:
'@modelcontextprotocol/sdk':
- specifier: ^1.13.0
- version: 1.13.2
+ specifier: ^1.13.3
+ version: 1.13.3
zod:
specifier: ^3.25.67
version: 3.25.67
@@ -34,8 +34,8 @@ importers:
specifier: ^5.8.3
version: 5.8.3
wrangler:
- specifier: ^4.22.0
- version: 4.22.0(@cloudflare/workers-types@4.20250628.0)
+ specifier: ^4.23.0
+ version: 4.23.0(@cloudflare/workers-types@4.20250628.0)
packages:
@@ -443,8 +443,8 @@ packages:
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
- '@modelcontextprotocol/sdk@1.13.2':
- resolution: {integrity: sha512-Vx7qOcmoKkR3qhaQ9qf3GxiVKCEu+zfJddHv6x3dY/9P6+uIwJnmuAur5aB+4FDXf41rRrDnOEGkviX5oYZ67w==}
+ '@modelcontextprotocol/sdk@1.13.3':
+ resolution: {integrity: sha512-bGwA78F/U5G2jrnsdRkPY3IwIwZeWUEfb5o764b79lb0rJmMT76TLwKhdNZOWakOQtedYefwIR4emisEMvInKA==}
engines: {node: '>=18'}
'@opentelemetry/api@1.9.0':
@@ -765,8 +765,8 @@ packages:
engines: {node: '>=10.0.0'}
hasBin: true
- miniflare@4.20250617.4:
- resolution: {integrity: sha512-IAoApFKxOJlaaFkym5ETstVX3qWzVt3xyqCDj6vSSTgEH3zxZJ5417jZGg8iQfMHosKCcQH1doPPqqnOZm/yrw==}
+ miniflare@4.20250617.5:
+ resolution: {integrity: sha512-Qqn30jR6dCjXaKVizT6vH4KOb+GyLccoxLNOJEfu63yBPn8eoXa7PrdiSGTmjs2RY8/tr7eTO8Wu/Yr14k0xVA==}
engines: {node: '>=18.0.0'}
hasBin: true
@@ -1009,8 +1009,8 @@ packages:
engines: {node: '>=16'}
hasBin: true
- wrangler@4.22.0:
- resolution: {integrity: sha512-m8qVO3YxhUTII+4U889G/f5UuLSvMkUkCNatupV2f/SJ+iqaWtP1QbuQII8bs2J/O4rqxsz46Wu2S50u7tKB5Q==}
+ wrangler@4.23.0:
+ resolution: {integrity: sha512-JSeDt3IwA4TEmg/V3tRblImPjdxynBt9PUVO/acQJ83XGlMMSwswDKL1FuwvbFzgX6+JXc3GMHeu7r8AQIxw9w==}
engines: {node: '>=18.0.0'}
hasBin: true
peerDependencies:
@@ -1314,13 +1314,14 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.2
- '@modelcontextprotocol/sdk@1.13.2':
+ '@modelcontextprotocol/sdk@1.13.3':
dependencies:
ajv: 6.12.6
content-type: 1.0.5
cors: 2.8.5
cross-spawn: 7.0.6
eventsource: 3.0.7
+ eventsource-parser: 3.0.3
express: 5.1.0
express-rate-limit: 7.5.1(express@5.1.0)
pkce-challenge: 5.0.0
@@ -1349,7 +1350,7 @@ snapshots:
agents@0.0.100(@cloudflare/workers-types@4.20250628.0)(react@19.1.0):
dependencies:
- '@modelcontextprotocol/sdk': 1.13.2
+ '@modelcontextprotocol/sdk': 1.13.3
ai: 4.3.16(react@19.1.0)(zod@3.25.67)
cron-schedule: 5.0.4
nanoid: 5.1.5
@@ -1672,7 +1673,7 @@ snapshots:
mime@3.0.0: {}
- miniflare@4.20250617.4:
+ miniflare@4.20250617.5:
dependencies:
'@cspotcode/source-map-support': 0.8.1
acorn: 8.14.0
@@ -1942,13 +1943,13 @@ snapshots:
'@cloudflare/workerd-linux-arm64': 1.20250617.0
'@cloudflare/workerd-windows-64': 1.20250617.0
- wrangler@4.22.0(@cloudflare/workers-types@4.20250628.0):
+ wrangler@4.23.0(@cloudflare/workers-types@4.20250628.0):
dependencies:
'@cloudflare/kv-asset-handler': 0.4.0
'@cloudflare/unenv-preset': 2.3.3(unenv@2.0.0-rc.17)(workerd@1.20250617.0)
blake3-wasm: 2.1.5
esbuild: 0.25.4
- miniflare: 4.20250617.4
+ miniflare: 4.20250617.5
path-to-regexp: 6.3.0
unenv: 2.0.0-rc.17
workerd: 1.20250617.0
diff --git a/examples/servers/cf-agents/src/index.ts b/examples/servers/cf-agents/src/index.ts
index 417e3b9..81f43ae 100644
--- a/examples/servers/cf-agents/src/index.ts
+++ b/examples/servers/cf-agents/src/index.ts
@@ -1,7 +1,7 @@
import { McpAgent } from 'agents/mcp'
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { z } from 'zod'
-import OAuthProvider, { OAuthHelpers } from '@cloudflare/workers-oauth-provider'
+import OAuthProvider, { type OAuthHelpers } from '@cloudflare/workers-oauth-provider'
import { Hono } from 'hono'
// Define our MCP agent with tools
@@ -53,6 +53,95 @@ export class MyMCP extends McpAgent {
return { content: [{ type: 'text', text: String(result) }] }
},
)
+
+ // Register sample resources using the agents framework API
+ this.server.resource('calc://history', 'Calculation History', async () => ({
+ contents: [
+ {
+ uri: 'calc://history',
+ mimeType: 'application/json',
+ text: JSON.stringify(
+ {
+ calculations: [
+ { operation: 'add', a: 5, b: 3, result: 8, timestamp: '2024-01-01T10:00:00Z' },
+ { operation: 'multiply', a: 4, b: 7, result: 28, timestamp: '2024-01-01T10:01:00Z' },
+ ],
+ },
+ null,
+ 2,
+ ),
+ },
+ ],
+ }))
+
+ this.server.resource('calc://settings', 'Calculator Settings', async () => ({
+ contents: [
+ {
+ uri: 'calc://settings',
+ mimeType: 'application/json',
+ text: JSON.stringify(
+ {
+ precision: 2,
+ allowNegative: true,
+ maxValue: 1000000,
+ },
+ null,
+ 2,
+ ),
+ },
+ ],
+ }))
+
+ // Register a dynamic resource
+ this.server.resource('calc://stats', 'Calculation Statistics', async () => ({
+ contents: [
+ {
+ uri: 'calc://stats',
+ mimeType: 'application/json',
+ text: JSON.stringify(
+ {
+ totalCalculations: Math.floor(Math.random() * 100),
+ lastUpdated: new Date().toISOString(),
+ },
+ null,
+ 2,
+ ),
+ },
+ ],
+ }))
+
+ // Register sample prompts using the agents framework API
+ this.server.prompt(
+ 'math_problem',
+ 'Generate a math problem',
+ {
+ difficulty: z.string(),
+ topic: z.string().optional(),
+ },
+ async ({ difficulty, topic }) => ({
+ messages: [
+ {
+ role: 'user',
+ content: {
+ type: 'text',
+ text: `Generate a ${difficulty} math problem${topic ? ` about ${topic}` : ''}`,
+ },
+ },
+ ],
+ }),
+ )
+
+ this.server.prompt('explain_calculation', 'Explain a calculation', { operation: z.string() }, async ({ operation }) => ({
+ messages: [
+ {
+ role: 'user',
+ content: {
+ type: 'text',
+ text: `Please explain how to perform ${operation} step by step with examples`,
+ },
+ },
+ ],
+ }))
}
}
@@ -66,7 +155,53 @@ const app = new Hono<{
app.get('/authorize', async (c) => {
const oauthReqInfo = await c.env.OAUTH_PROVIDER.parseAuthRequest(c.req.raw)
+ const url = new URL(c.req.url)
+
+ const approveHref = url.toString()
+ return c.html(/*html*/ `
+
+
+
Authorize access
+
+
+
Authorize access
+
Allow Unknown client to access your calculator?
+
+
+ Deny
+
+
User: example@dotcom.com
+
+ `)
+})
+
+app.post('/authorize', async (c) => {
const email = 'example@dotcom.com'
+ const formData = await c.req.formData()
+ const oauthReqInfoRaw = formData.get('oauthReqInfo')
+ if (!oauthReqInfoRaw || typeof oauthReqInfoRaw !== 'string') {
+ return c.text('Missing oauthReqInfo', 400)
+ }
+ let oauthReqInfo
+ try {
+ oauthReqInfo = JSON.parse(decodeURIComponent(oauthReqInfoRaw))
+ } catch (e) {
+ return c.text('Invalid oauthReqInfo', 400)
+ }
const { redirectTo } = await c.env.OAUTH_PROVIDER.completeAuthorization({
request: oauthReqInfo,
userId: email,
diff --git a/examples/servers/hono-mcp/package.json b/examples/servers/hono-mcp/package.json
index 7e7adb3..6f91211 100644
--- a/examples/servers/hono-mcp/package.json
+++ b/examples/servers/hono-mcp/package.json
@@ -1,7 +1,7 @@
{
"name": "vengabus",
"scripts": {
- "dev": "wrangler dev",
+ "dev": "wrangler dev --port 5101",
"deploy": "wrangler deploy --minify",
"cf-typegen": "wrangler types --env-interface CloudflareBindings"
},
@@ -10,8 +10,8 @@
},
"devDependencies": {
"@hono/mcp": "^0.1.0",
- "@modelcontextprotocol/sdk": "^1.13.1",
- "wrangler": "^4.21.0",
+ "@modelcontextprotocol/sdk": "^1.13.3",
+ "wrangler": "^4.23.0",
"zod": "^3.25.67"
}
}
diff --git a/examples/servers/hono-mcp/pnpm-lock.yaml b/examples/servers/hono-mcp/pnpm-lock.yaml
index d03e141..4139153 100644
--- a/examples/servers/hono-mcp/pnpm-lock.yaml
+++ b/examples/servers/hono-mcp/pnpm-lock.yaml
@@ -14,13 +14,13 @@ importers:
devDependencies:
'@hono/mcp':
specifier: ^0.1.0
- version: 0.1.0(@modelcontextprotocol/sdk@1.13.1)(hono@4.8.2)
+ version: 0.1.0(@modelcontextprotocol/sdk@1.13.3)(hono@4.8.2)
'@modelcontextprotocol/sdk':
- specifier: ^1.13.1
- version: 1.13.1
+ specifier: ^1.13.3
+ version: 1.13.3
wrangler:
- specifier: ^4.21.0
- version: 4.21.0
+ specifier: ^4.23.0
+ version: 4.23.0
zod:
specifier: ^3.25.67
version: 3.25.67
@@ -352,8 +352,8 @@ packages:
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
- '@modelcontextprotocol/sdk@1.13.1':
- resolution: {integrity: sha512-8q6+9aF0yA39/qWT/uaIj6zTpC+Qu07DnN/lb9mjoquCJsAh6l3HyYqc9O3t2j7GilseOQOQimLg7W3By6jqvg==}
+ '@modelcontextprotocol/sdk@1.13.3':
+ resolution: {integrity: sha512-bGwA78F/U5G2jrnsdRkPY3IwIwZeWUEfb5o764b79lb0rJmMT76TLwKhdNZOWakOQtedYefwIR4emisEMvInKA==}
engines: {node: '>=18'}
accepts@2.0.0:
@@ -623,8 +623,8 @@ packages:
engines: {node: '>=10.0.0'}
hasBin: true
- miniflare@4.20250617.3:
- resolution: {integrity: sha512-j+LZycT11UdlVeNdaqD0XdNnYnqAL+wXmboz+tNPFgTq6zhD489Ujj3BfSDyEHDCA9UFBLbkc5ByGWBh+pYZ5Q==}
+ miniflare@4.20250617.5:
+ resolution: {integrity: sha512-Qqn30jR6dCjXaKVizT6vH4KOb+GyLccoxLNOJEfu63yBPn8eoXa7PrdiSGTmjs2RY8/tr7eTO8Wu/Yr14k0xVA==}
engines: {node: '>=18.0.0'}
hasBin: true
@@ -820,8 +820,8 @@ packages:
engines: {node: '>=16'}
hasBin: true
- wrangler@4.21.0:
- resolution: {integrity: sha512-37xm0CG2qMvsJUNZYQKje6HbCsJFYuE8dQSnu7981iDRT4DLrEIL1DAUnZJG9HiXteKPvrSj96AkZyomi5sYHw==}
+ wrangler@4.23.0:
+ resolution: {integrity: sha512-JSeDt3IwA4TEmg/V3tRblImPjdxynBt9PUVO/acQJ83XGlMMSwswDKL1FuwvbFzgX6+JXc3GMHeu7r8AQIxw9w==}
engines: {node: '>=18.0.0'}
hasBin: true
peerDependencies:
@@ -972,9 +972,9 @@ snapshots:
'@fastify/busboy@2.1.1': {}
- '@hono/mcp@0.1.0(@modelcontextprotocol/sdk@1.13.1)(hono@4.8.2)':
+ '@hono/mcp@0.1.0(@modelcontextprotocol/sdk@1.13.3)(hono@4.8.2)':
dependencies:
- '@modelcontextprotocol/sdk': 1.13.1
+ '@modelcontextprotocol/sdk': 1.13.3
hono: 4.8.2
'@img/sharp-darwin-arm64@0.33.5':
@@ -1061,13 +1061,14 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
- '@modelcontextprotocol/sdk@1.13.1':
+ '@modelcontextprotocol/sdk@1.13.3':
dependencies:
ajv: 6.12.6
content-type: 1.0.5
cors: 2.8.5
cross-spawn: 7.0.6
eventsource: 3.0.7
+ eventsource-parser: 3.0.2
express: 5.1.0
express-rate-limit: 7.5.1(express@5.1.0)
pkce-challenge: 5.0.0
@@ -1367,7 +1368,7 @@ snapshots:
mime@3.0.0: {}
- miniflare@4.20250617.3:
+ miniflare@4.20250617.5:
dependencies:
'@cspotcode/source-map-support': 0.8.1
acorn: 8.14.0
@@ -1604,13 +1605,13 @@ snapshots:
'@cloudflare/workerd-linux-arm64': 1.20250617.0
'@cloudflare/workerd-windows-64': 1.20250617.0
- wrangler@4.21.0:
+ wrangler@4.23.0:
dependencies:
'@cloudflare/kv-asset-handler': 0.4.0
'@cloudflare/unenv-preset': 2.3.3(unenv@2.0.0-rc.17)(workerd@1.20250617.0)
blake3-wasm: 2.1.5
esbuild: 0.25.4
- miniflare: 4.20250617.3
+ miniflare: 4.20250617.5
path-to-regexp: 6.3.0
unenv: 2.0.0-rc.17
workerd: 1.20250617.0
diff --git a/examples/servers/hono-mcp/src/index.ts b/examples/servers/hono-mcp/src/index.ts
index 5fc2067..74ece33 100644
--- a/examples/servers/hono-mcp/src/index.ts
+++ b/examples/servers/hono-mcp/src/index.ts
@@ -1,4 +1,4 @@
-import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
+import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StreamableHTTPTransport } from '@hono/mcp'
import { Hono } from 'hono'
import { z } from 'zod'
@@ -17,19 +17,27 @@ app.use(
// Your MCP server implementation
const mcpServer = new McpServer({
- name: 'my-mcp-server',
+ name: 'vengabus-mcp-server',
version: '1.0.0',
})
mcpServer.registerTool(
- 'add',
+ 'calculate_party_capacity',
{
- title: 'Addition Tool',
- description: 'Add two numbers',
- inputSchema: { a: z.number(), b: z.number() },
+ title: 'Party Bus Capacity Calculator',
+ description: 'Calculate how many people can fit on the Vengabus',
+ inputSchema: {
+ busCount: z.number().describe('Number of Vengabuses'),
+ peoplePerBus: z.number().describe('People per bus'),
+ },
},
- async ({ a, b }) => ({
- content: [{ type: 'text', text: String(a + b) }],
+ async ({ busCount, peoplePerBus }) => ({
+ content: [
+ {
+ type: 'text',
+ text: `đ ${busCount} Vengabuses can transport ${busCount * peoplePerBus} party people! The wheels of steel are turning! đ`,
+ },
+ ],
}),
)
@@ -42,18 +50,183 @@ mcpServer.registerTool(
inputSchema: {},
},
async () => {
- await scheduler.wait(100 + Math.random() * 100)
+ // Simulate some async work
+ await new Promise((resolve) => setTimeout(resolve, 100 + Math.random() * 100))
return {
content: [
{
type: 'text',
- text: `Next Vengabus: imminent. Current user's route: New York to San Francisco. Route name: "Intercity Disco".`,
+ text: `đ BOOM BOOM BOOM BOOM! Next Vengabus: IMMINENT! đ
+
+Current Route: "Intercity Disco Express"
+From: New York
+To: IBIZA - THE MAGIC ISLAND! â¨
+
+Schedule:
+- Departure: When the beat drops
+- Via: San Francisco (quick party stop)
+- Arrival: When the sun comes up
+
+Status: The party bus is jumping!
+Passengers: Maximum capacity!
+BPM: 142 and rising!
+
+We're going to Ibiza! Back to the island! đī¸`,
},
],
}
},
)
+// Register sample resources
+mcpServer.registerResource(
+ 'config',
+ 'config://vengabus',
+ {
+ title: 'Vengabus Fleet Configuration',
+ description: 'Current Vengabus fleet settings and party parameters',
+ mimeType: 'application/json',
+ },
+ async (uri: URL) => {
+ const normalizedUri = uri.href.replace(/\/$/, '')
+ return {
+ contents: [
+ {
+ uri: normalizedUri,
+ mimeType: 'application/json',
+ text: JSON.stringify(
+ {
+ version: '1.0.0',
+ fleet: 'intercity-disco',
+ features: {
+ bassBoost: true,
+ strobeEnabled: true,
+ maxBPM: 160,
+ wheelsOfSteel: 'turning',
+ },
+ routes: ['New York to Ibiza', 'Back to the Magic Island', 'Like an Intercity Disco', 'The place to be'],
+ },
+ null,
+ 2,
+ ),
+ },
+ ],
+ }
+ },
+)
+
+mcpServer.registerResource(
+ 'readme',
+ 'docs://party-manual.md',
+ {
+ title: 'Vengabus Party Manual',
+ description: 'Official guide to the Vengabus experience',
+ mimeType: 'text/markdown',
+ },
+ () => ({
+ contents: [
+ {
+ uri: 'docs://party-manual.md',
+ mimeType: 'text/markdown',
+ text: "# đ Vengabus MCP Server\n\nBOOM BOOM BOOM BOOM! Welcome aboard the Vengabus! We like to party!\n\n## Features\n- đ Party capacity calculator - How many can we take to Ibiza?\n- đ Vengabus schedule checker - Next stop: The Magic Island!\n- đĩ Fleet configuration with maximum bass boost\n- đ Party statistics tracker (BPM, passengers, energy levels)\n\n## Routes\n- New York to Ibiza (via San Francisco)\n- Back to the Magic Island\n- Like an Intercity Disco\n- The place to be\n\n## Current Status\nThe wheels of steel are turning, and traffic lights are burning!\nWe're going to Ibiza! Woah! We're going to Ibiza!\n\n*Up, up and away we go!*",
+ },
+ ],
+ }),
+)
+
+// Register a resource template
+mcpServer.registerResource(
+ 'stats',
+ new ResourceTemplate('party://stats/{metric}', { list: undefined }),
+ {
+ title: 'Vengabus Party Statistics',
+ description: 'Get party metrics (bpm, passengers, energy)',
+ mimeType: 'application/json',
+ },
+ async (uri: URL, { metric }) => {
+ return {
+ contents: [
+ {
+ uri: uri.href,
+ mimeType: 'application/json',
+ text: JSON.stringify(
+ {
+ metric: metric,
+ value:
+ metric === 'bpm'
+ ? 140 + Math.floor(Math.random() * 20)
+ : metric === 'passengers'
+ ? Math.floor(Math.random() * 50) + 20
+ : metric === 'energy'
+ ? Math.floor(Math.random() * 100)
+ : 0,
+ unit:
+ metric === 'bpm'
+ ? 'beats per minute'
+ : metric === 'passengers'
+ ? 'party people'
+ : metric === 'energy'
+ ? 'party power %'
+ : 'unknown',
+ status: 'The party is jumping!',
+ lastUpdated: new Date().toISOString(),
+ },
+ null,
+ 2,
+ ),
+ },
+ ],
+ }
+ },
+)
+
+// Register sample prompts
+mcpServer.registerPrompt(
+ 'party_invitation',
+ {
+ title: 'Vengabus Party Invitation',
+ description: 'Generate a party invitation for the Vengabus',
+ argsSchema: {
+ name: z.string().describe('Name of the party person'),
+ destination: z.string().optional().describe('Where the Vengabus is heading'),
+ },
+ },
+ ({ name, destination }) => ({
+ messages: [
+ {
+ role: 'user',
+ content: {
+ type: 'text',
+ text: `Create a party invitation for ${name} to join the Vengabus${destination ? ` heading to ${destination}` : ''}`,
+ },
+ },
+ ],
+ }),
+)
+
+mcpServer.registerPrompt(
+ 'party_announcement',
+ {
+ title: 'Party Bus Announcement',
+ description: 'Generate party-themed announcements for various occasions',
+ argsSchema: {
+ event: z.string().describe('Type of event (deployment, release, milestone, etc.)'),
+ details: z.string().optional().describe('Additional details about the event'),
+ },
+ },
+ ({ event, details }) => ({
+ messages: [
+ {
+ role: 'user',
+ content: {
+ type: 'text',
+ text: `Create a party bus announcement for a ${event}${details ? `: ${details}` : ''}`,
+ },
+ },
+ ],
+ }),
+)
+
app.post('/mcp', async (c) => {
const transport = new StreamableHTTPTransport()
await mcpServer.connect(transport)
diff --git a/package.json b/package.json
index 11a9649..6a7cd6b 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "use-mcp",
"repository": "https://github.com/modelcontextprotocol/use-mcp",
- "version": "0.0.15",
+ "version": "0.0.21",
"type": "module",
"files": [
"dist",
@@ -21,22 +21,27 @@
}
},
"scripts": {
- "dev": "tsup --watch",
+ "install:all": "concurrently 'pnpm install' 'cd examples/chat-ui && pnpm install' 'cd examples/inspector && pnpm install' 'cd examples/servers/hono-mcp && pnpm install' 'cd examples/servers/cf-agents && pnpm install'",
+ "dev": "concurrently 'pnpm:build:watch' 'cd examples/chat-ui && pnpm dev' 'cd examples/inspector && pnpm dev' 'sleep 1 && cd examples/servers/hono-mcp && pnpm dev' 'sleep 2 && cd examples/servers/cf-agents && pnpm dev'",
+ "deploy:all": "concurrently 'pnpm build:site && pnpm deploy:site' 'cd examples/chat-ui && pnpm run deploy' 'cd examples/inspector && pnpm run deploy'",
+ "build:watch": "tsup --watch",
"build": "tsup",
"check": "prettier --check . && tsc",
"prettier:fix": "prettier --write .",
"fix:oranda": "sed -i 's/```tsx/```ts/g' README.md",
"build:site": "npx @axodotdev/oranda build",
"deploy:site": "npx wrangler deploy",
- "postinstall": "ln -sf ../../scripts/pre-commit .git/hooks/pre-commit"
+ "prepare": "husky"
},
"dependencies": {
+ "@modelcontextprotocol/sdk": "^1.13.3",
"strict-url-sanitise": "^0.0.1"
},
"devDependencies": {
"@axodotdev/oranda": "^0.6.5",
- "@modelcontextprotocol/sdk": "^1.13.3",
"@types/react": "^19.0.12",
+ "concurrently": "^9.2.0",
+ "husky": "^9.1.7",
"prettier": "^3.5.3",
"react": "^19.0.0",
"tsup": "^8.4.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b26e6b1..36932dc 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,6 +8,9 @@ importers:
.:
dependencies:
+ '@modelcontextprotocol/sdk':
+ specifier: ^1.13.3
+ version: 1.13.3
strict-url-sanitise:
specifier: ^0.0.1
version: 0.0.1
@@ -15,12 +18,15 @@ importers:
'@axodotdev/oranda':
specifier: ^0.6.5
version: 0.6.5
- '@modelcontextprotocol/sdk':
- specifier: ^1.13.3
- version: 1.13.3
'@types/react':
specifier: ^19.0.12
version: 19.0.12
+ concurrently:
+ specifier: ^9.2.0
+ version: 9.2.0
+ husky:
+ specifier: ^9.1.7
+ version: 9.1.7
prettier:
specifier: ^3.5.3
version: 3.5.3
@@ -726,6 +732,10 @@ packages:
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
engines: {node: '>= 0.4'}
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
chokidar@4.0.3:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
@@ -734,6 +744,10 @@ packages:
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
engines: {node: '>=18'}
+ cliui@8.0.1:
+ resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+ engines: {node: '>=12'}
+
clone@1.0.4:
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
engines: {node: '>=0.8'}
@@ -760,6 +774,11 @@ packages:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
+ concurrently@9.2.0:
+ resolution: {integrity: sha512-IsB/fiXTupmagMW4MNp2lx2cdSN2FfZq78vF90LBB+zZHArbIQZjQtzXCiXnvTxCZSvXanTqFLWBjw2UkLx1SQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+
consola@3.4.0:
resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==}
engines: {node: ^14.18.0 || >=16.10.0}
@@ -874,6 +893,10 @@ packages:
engines: {node: '>=18'}
hasBin: true
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
@@ -957,6 +980,10 @@ packages:
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
@@ -982,6 +1009,10 @@ packages:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
@@ -998,6 +1029,11 @@ packages:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
+ husky@9.1.7:
+ resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
@@ -1046,6 +1082,9 @@ packages:
lodash.sortby@4.7.0:
resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
+ lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
@@ -1233,6 +1272,10 @@ packages:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
+ require-directory@2.1.1:
+ resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+ engines: {node: '>=0.10.0'}
+
resolve-from@5.0.0:
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
engines: {node: '>=8'}
@@ -1253,6 +1296,9 @@ packages:
resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
engines: {node: '>= 18'}
+ rxjs@7.8.2:
+ resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
+
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
@@ -1287,6 +1333,10 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
+ shell-quote@1.8.3:
+ resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
+ engines: {node: '>= 0.4'}
+
side-channel-list@1.0.0:
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
engines: {node: '>= 0.4'}
@@ -1357,6 +1407,14 @@ packages:
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-color@8.1.1:
+ resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+ engines: {node: '>=10'}
+
tar@7.4.3:
resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
engines: {node: '>=18'}
@@ -1502,10 +1560,22 @@ packages:
utf-8-validate:
optional: true
+ y18n@5.0.8:
+ resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+ engines: {node: '>=10'}
+
yallist@5.0.0:
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
engines: {node: '>=18'}
+ yargs-parser@21.1.1:
+ resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+ engines: {node: '>=12'}
+
+ yargs@17.7.2:
+ resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+ engines: {node: '>=12'}
+
youch@3.3.4:
resolution: {integrity: sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==}
@@ -1999,12 +2069,23 @@ snapshots:
call-bind-apply-helpers: 1.0.2
get-intrinsic: 1.3.0
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
chokidar@4.0.3:
dependencies:
readdirp: 4.1.2
chownr@3.0.0: {}
+ cliui@8.0.1:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
clone@1.0.4:
optional: true
@@ -2030,6 +2111,16 @@ snapshots:
commander@4.1.1: {}
+ concurrently@9.2.0:
+ dependencies:
+ chalk: 4.1.2
+ lodash: 4.17.21
+ rxjs: 7.8.2
+ shell-quote: 1.8.3
+ supports-color: 8.1.1
+ tree-kill: 1.2.2
+ yargs: 17.7.2
+
consola@3.4.0: {}
console.table@0.10.0:
@@ -2169,6 +2260,8 @@ snapshots:
'@esbuild/win32-ia32': 0.25.4
'@esbuild/win32-x64': 0.25.4
+ escalade@3.2.0: {}
+
escape-html@1.0.3: {}
etag@1.8.1: {}
@@ -2262,6 +2355,8 @@ snapshots:
function-bind@1.1.2: {}
+ get-caller-file@2.0.5: {}
+
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -2302,6 +2397,8 @@ snapshots:
gopd@1.2.0: {}
+ has-flag@4.0.0: {}
+
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
@@ -2320,6 +2417,8 @@ snapshots:
statuses: 2.0.1
toidentifier: 1.0.1
+ husky@9.1.7: {}
+
iconv-lite@0.6.3:
dependencies:
safer-buffer: 2.1.2
@@ -2354,6 +2453,8 @@ snapshots:
lodash.sortby@4.7.0: {}
+ lodash@4.17.21: {}
+
lru-cache@10.4.3: {}
math-intrinsics@1.1.0: {}
@@ -2493,6 +2594,8 @@ snapshots:
readdirp@4.1.2: {}
+ require-directory@2.1.1: {}
+
resolve-from@5.0.0: {}
resolve-pkg-maps@1.0.0: {}
@@ -2536,6 +2639,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ rxjs@7.8.2:
+ dependencies:
+ tslib: 2.8.1
+
safe-buffer@5.2.1: {}
safer-buffer@2.1.2: {}
@@ -2601,6 +2708,8 @@ snapshots:
shebang-regex@3.0.0: {}
+ shell-quote@1.8.3: {}
+
side-channel-list@1.0.0:
dependencies:
es-errors: 1.3.0
@@ -2684,6 +2793,14 @@ snapshots:
pirates: 4.0.6
ts-interface-checker: 0.1.13
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-color@8.1.1:
+ dependencies:
+ has-flag: 4.0.0
+
tar@7.4.3:
dependencies:
'@isaacs/fs-minipass': 4.0.1
@@ -2718,8 +2835,7 @@ snapshots:
ts-interface-checker@0.1.13: {}
- tslib@2.8.1:
- optional: true
+ tslib@2.8.1: {}
tsup@8.4.0(tsx@4.19.3)(typescript@5.8.2):
dependencies:
@@ -2843,8 +2959,22 @@ snapshots:
ws@8.18.0: {}
+ y18n@5.0.8: {}
+
yallist@5.0.0: {}
+ yargs-parser@21.1.1: {}
+
+ yargs@17.7.2:
+ dependencies:
+ cliui: 8.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 21.1.1
+
youch@3.3.4:
dependencies:
cookie: 0.7.1
diff --git a/src/auth/browser-provider.ts b/src/auth/browser-provider.ts
index f644a0e..0d660bb 100644
--- a/src/auth/browser-provider.ts
+++ b/src/auth/browser-provider.ts
@@ -15,6 +15,8 @@ export class BrowserOAuthClientProvider implements OAuthClientProvider {
readonly clientName: string
readonly clientUri: string
readonly callbackUrl: string
+ private preventAutoAuth?: boolean
+ readonly onPopupWindow: ((url: string, features: string, window: Window | null) => void) | undefined
constructor(
serverUrl: string,
@@ -23,6 +25,8 @@ export class BrowserOAuthClientProvider implements OAuthClientProvider {
clientName?: string
clientUri?: string
callbackUrl?: string
+ preventAutoAuth?: boolean
+ onPopupWindow?: (url: string, features: string, window: Window | null) => void
} = {},
) {
this.serverUrl = serverUrl
@@ -34,6 +38,8 @@ export class BrowserOAuthClientProvider implements OAuthClientProvider {
options.callbackUrl ||
(typeof window !== 'undefined' ? new URL('/oauth/callback', window.location.origin).toString() : '/oauth/callback'),
)
+ this.preventAutoAuth = options.preventAutoAuth
+ this.onPopupWindow = options.onPopupWindow
}
// --- SDK Interface Methods ---
@@ -117,11 +123,12 @@ export class BrowserOAuthClientProvider implements OAuthClientProvider {
}
/**
- * Redirects the user agent to the authorization URL, storing necessary state.
- * This now adheres to the SDK's void return type expectation for the interface.
+ * Generates and stores the authorization URL with state, without opening a popup.
+ * Used when preventAutoAuth is enabled to provide the URL for manual navigation.
* @param authorizationUrl The fully constructed authorization URL from the SDK.
+ * @returns The full authorization URL with state parameter.
*/
- async redirectToAuthorization(authorizationUrl: URL): Promise
{
+ async prepareAuthorizationUrl(authorizationUrl: URL): Promise {
// Generate a unique state parameter for this authorization request
const state = crypto.randomUUID()
const stateKey = `${this.storageKeyPrefix}:state_${state}`
@@ -151,11 +158,31 @@ export class BrowserOAuthClientProvider implements OAuthClientProvider {
// Persist the exact auth URL in case the popup fails and manual navigation is needed
localStorage.setItem(this.getKey('last_auth_url'), sanitizedAuthUrl)
+ return sanitizedAuthUrl
+ }
+
+ /**
+ * Redirects the user agent to the authorization URL, storing necessary state.
+ * This now adheres to the SDK's void return type expectation for the interface.
+ * @param authorizationUrl The fully constructed authorization URL from the SDK.
+ */
+ async redirectToAuthorization(authorizationUrl: URL): Promise {
+ // Ideally we should catch things before we get here, but if we don't, let's not show everyone we are dum
+ if (this.preventAutoAuth) return
+
+ // Prepare the authorization URL with state
+ const sanitizedAuthUrl = await this.prepareAuthorizationUrl(authorizationUrl)
+
// Attempt to open the popup
const popupFeatures = 'width=600,height=700,resizable=yes,scrollbars=yes,status=yes' // Make configurable if needed
try {
const popup = window.open(sanitizedAuthUrl, `mcp_auth_${this.serverUrlHash}`, popupFeatures)
+ // If a callback is provided, invoke it after opening the popup
+ if (this.onPopupWindow) {
+ this.onPopupWindow(sanitizedAuthUrl, popupFeatures, popup)
+ }
+
if (!popup || popup.closed || typeof popup.closed === 'undefined') {
console.warn(
`[${this.storageKeyPrefix}] Popup likely blocked by browser. Manual navigation might be required using the stored URL.`,
diff --git a/src/react/README.md b/src/react/README.md
index 3e15c47..f523ea5 100644
--- a/src/react/README.md
+++ b/src/react/README.md
@@ -23,7 +23,7 @@ The useMcp hook manages the connection to an MCP server, handles authentication
```tsx
import { useMcp } from 'use-mcp'
// Optional: Import types if needed
-import type { UseMcpOptions, UseMcpResult, Tool } from 'use-mcp'
+import type { UseMcpOptions, UseMcpResult, Tool, Resource, ResourceTemplate, Prompt } from 'use-mcp'
```
## Usage
@@ -101,7 +101,12 @@ function MyChatComponent() {
)}
{mcp.state === 'authenticating' && Waiting for authentication...
}
- {mcp.state === 'ready' &&
+ Connected! Tools: {mcp.tools.length}, Resources: {mcp.resources.length + mcp.resourceTemplates.length},
+ Prompts: {mcp.prompts.length}
+
+ )}
{/* Your Chat UI */}