Oauth tests (#13)

* Moving the non-authed cfagents tests to /public

* Added OAuth CF Agents tests

---------

Co-authored-by: Glen Maddern <glen@glenmaddern.com>
This commit is contained in:
Glen Maddern 2025-07-01 13:26:26 +10:00 • committed by GitHub
commit 0ea0bc48c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 127 additions and 19 deletions

View file

@ -17,8 +17,10 @@
},
"devDependencies": {
"@biomejs/biome": "^2.0.4",
"@cloudflare/workers-oauth-provider": "^0.0.5",
"@types/node": "^24.0.7",
"agents": "^0.0.100",
"hono": "^4.8.3",
"typescript": "^5.8.3",
"wrangler": "^4.22.0"
}

View file

@ -18,12 +18,18 @@ importers:
'@biomejs/biome':
specifier: ^2.0.4
version: 2.0.6
'@cloudflare/workers-oauth-provider':
specifier: ^0.0.5
version: 0.0.5
'@types/node':
specifier: ^24.0.7
version: 24.0.7
agents:
specifier: ^0.0.100
version: 0.0.100(@cloudflare/workers-types@4.20250628.0)(react@19.1.0)
hono:
specifier: ^4.8.3
version: 4.8.3
typescript:
specifier: ^5.8.3
version: 5.8.3
@ -155,6 +161,9 @@ packages:
cpu: [x64]
os: [win32]
'@cloudflare/workers-oauth-provider@0.0.5':
resolution: {integrity: sha512-t1x5KAzsubCvb4APnJ93z407X1x7SGj/ga5ziRnwIb/iLy4PMkT/hgd1y5z7Bbsdy5Fy6mywhCP4lym24bX66w==}
'@cloudflare/workers-types@4.20250628.0':
resolution: {integrity: sha512-V4HthfhtQU2vTpwLeUic8FTLgGSjglsGZMJc9jKBNYEU/k0A1rE55UgQoTb5blKQdGtpQKfVKs3FROeY/lXmbw==}
@ -692,6 +701,10 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
hono@4.8.3:
resolution: {integrity: sha512-jYZ6ZtfWjzBdh8H/0CIFfCBHaFL75k+KMzaM177hrWWm2TWL39YMYaJgB74uK/niRc866NMlH9B8uCvIo284WQ==}
engines: {node: '>=16.9.0'}
http-errors@2.0.0:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
@ -1125,6 +1138,10 @@ snapshots:
'@cloudflare/workerd-windows-64@1.20250617.0':
optional: true
'@cloudflare/workers-oauth-provider@0.0.5':
dependencies:
'@cloudflare/workers-types': 4.20250628.0
'@cloudflare/workers-types@4.20250628.0': {}
'@cspotcode/source-map-support@0.8.1':
@ -1607,6 +1624,8 @@ snapshots:
dependencies:
function-bind: 1.1.2
hono@4.8.3: {}
http-errors@2.0.0:
dependencies:
depd: 2.0.0

View file

@ -1,6 +1,8 @@
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 { Hono } from 'hono'
// Define our MCP agent with tools
export class MyMCP extends McpAgent {
@ -54,18 +56,59 @@ export class MyMCP extends McpAgent {
}
}
export type Bindings = Env & {
OAUTH_PROVIDER: OAuthHelpers
}
const app = new Hono<{
Bindings: Bindings
}>()
app.get('/authorize', async (c) => {
const oauthReqInfo = await c.env.OAUTH_PROVIDER.parseAuthRequest(c.req.raw)
const email = 'example@dotcom.com'
const { redirectTo } = await c.env.OAUTH_PROVIDER.completeAuthorization({
request: oauthReqInfo,
userId: email,
metadata: {
label: 'Test User',
},
scope: oauthReqInfo.scope,
props: {
userEmail: email,
},
})
return Response.redirect(redirectTo)
})
export default {
fetch(request: Request, env: Env, ctx: ExecutionContext) {
const url = new URL(request.url)
if (url.pathname === '/sse' || url.pathname === '/sse/message') {
return MyMCP.serveSSE('/sse').fetch(request, env, ctx)
if (url.pathname === '/public/sse' || url.pathname === '/public/sse/message') {
return MyMCP.serveSSE('/public/sse').fetch(request, env, ctx)
}
if (url.pathname === '/mcp') {
return MyMCP.serve('/mcp').fetch(request, env, ctx)
if (url.pathname === '/public/mcp') {
return MyMCP.serve('/public/mcp').fetch(request, env, ctx)
}
return new Response('Not found', { status: 404 })
return new OAuthProvider({
apiRoute: ['/sse', '/mcp'],
apiHandler: {
// @ts-ignore
fetch: (request, env, ctx) => {
const { pathname } = new URL(request.url)
if (pathname.startsWith('/sse')) return MyMCP.serveSSE('/sse').fetch(request as any, env, ctx)
if (pathname === '/mcp') return MyMCP.serve('/mcp').fetch(request as any, env, ctx)
return new Response('Not found', { status: 404 })
},
},
// @ts-ignore
defaultHandler: app,
authorizeEndpoint: '/authorize',
tokenEndpoint: '/token',
clientRegistrationEndpoint: '/register',
}).fetch(request, env, ctx)
},
}

View file

@ -1,8 +1,9 @@
/* eslint-disable */
// Generated by Wrangler by running `wrangler types` (hash: 15dd420dfccac8afafeb181790b9eddb)
// Generated by Wrangler by running `wrangler types` (hash: 71a1247d25eb5ef8b66311103d5f9180)
// Runtime types generated with workerd@1.20250617.0 2025-03-10 nodejs_compat
declare namespace Cloudflare {
interface Env {
OAUTH_KV: KVNamespace
MCP_OBJECT: DurableObjectNamespace<import('./src/index').MyMCP>
}
}

View file

@ -24,7 +24,13 @@
},
"observability": {
"enabled": true
}
},
"kv_namespaces": [
{
"binding": "OAUTH_KV",
"id": "<Add-KV-ID>"
}
]
/**
* Smart Placement
* Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement