Skip to content

Commit 4b39063

Browse files
authored
add in initial version of secret example (#97)
* add in initial version of secret example * update tests and package.json * force add dev.vars file for example * revert dependent-agent/worker-configuration.d.ts * udpate README and move cursor rules to top level --------- Co-authored-by: Allen Wyma <>
1 parent f3896c2 commit 4b39063

26 files changed

+9238
-0
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
description: "setupRoutes method for custom HTTP endpoints in MCP servers"
3+
globs: src/server.ts
4+
---
5+
6+
# MCP Custom Routes with setupRoutes
7+
8+
## Override setupRoutes for Custom Endpoints
9+
10+
When you need custom HTTP endpoints beyond MCP functionality, override the `setupRoutes` method:
11+
12+
```typescript
13+
export class YourMcpServer extends McpHonoServerDO<Env> {
14+
// ... other methods ...
15+
16+
protected setupRoutes(app: Hono<{ Bindings: Env }>): void {
17+
// CRITICAL: Always call parent first to preserve MCP WebSocket/SSE endpoints
18+
super.setupRoutes(app);
19+
20+
// Now add your custom HTTP routes
21+
app.get('/api/health', (c) => {
22+
return c.json({
23+
status: 'healthy',
24+
timestamp: new Date().toISOString(),
25+
version: this.getImplementation().version
26+
});
27+
});
28+
29+
app.post('/api/webhook', async (c) => {
30+
try {
31+
const payload = await c.req.json();
32+
// Process webhook payload
33+
await this.processWebhook(payload);
34+
return c.json({ success: true });
35+
} catch (error) {
36+
return c.json({ error: error.message }, 400);
37+
}
38+
});
39+
40+
// Access Cloudflare bindings via c.env
41+
app.get('/api/data/:id', async (c) => {
42+
const id = c.req.param('id');
43+
try {
44+
// Access D1, KV, R2, etc. via c.env
45+
const result = await c.env.DATABASE?.prepare(
46+
'SELECT * FROM items WHERE id = ?'
47+
).bind(id).first();
48+
49+
return c.json(result || { error: 'Not found' });
50+
} catch (error) {
51+
return c.json({ error: error.message }, 500);
52+
}
53+
});
54+
55+
// CORS handling for browser clients
56+
app.options('*', (c) => {
57+
return c.text('', 204, {
58+
'Access-Control-Allow-Origin': '*',
59+
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
60+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
61+
});
62+
});
63+
}
64+
65+
private async processWebhook(payload: any) {
66+
// Webhook processing logic
67+
}
68+
}
69+
```
70+
71+
## Common Route Patterns
72+
73+
### Health Check Endpoint
74+
```typescript
75+
app.get('/api/health', (c) => {
76+
return c.json({
77+
status: 'ok',
78+
timestamp: Date.now(),
79+
uptime: process.uptime?.() || 0
80+
});
81+
});
82+
```
83+
84+
## Key Rules
85+
86+
1. **Always call `super.setupRoutes(app)` first** - This preserves MCP functionality
87+
2. **Use environment bindings via `c.env`** - Access D1, KV, R2, etc.
88+
3. **Handle errors gracefully** - Return appropriate HTTP status codes
89+
4. **Add CORS headers** if supporting browser clients
90+
5. **Use TypeScript types** for request/response bodies
91+
6. **Add authentication** for sensitive endpoints
92+
7. **Follow REST conventions** for API design
93+
94+
## When to Use setupRoutes
95+
96+
Use `setupRoutes` when you need:
97+
- REST API endpoints for web/mobile clients
98+
- Webhook receivers
99+
- File upload/download endpoints
100+
- Health/status monitoring endpoints
101+
- Integration with external services
102+
- Custom authentication flows
103+
104+
The MCP protocol handles AI client communication, while custom routes handle other HTTP clients.

.cursor/rules/mcp-development.mdc

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
---
2+
alwaysApply: true
3+
description: "MCP Server Development Patterns for Tools, Resources, Prompts, and Custom Routes"
4+
---
5+
6+
# MCP Server Development Guide
7+
8+
This project follows specific patterns for building MCP (Model Context Protocol) servers using Cloudflare Workers and Durable Objects.
9+
10+
## Core Architecture
11+
12+
- Main server class extends `McpHonoServerDO<Env>` from `@nullshot/mcp`
13+
- Server configuration is split into three modules: [tools.ts](mdc:src/tools.ts), [resources.ts](mdc:src/resources.ts), [prompts.ts](mdc:src/prompts.ts)
14+
- Entry point is [server.ts](mdc:src/server.ts) which coordinates everything
15+
16+
## MCP Server Class Pattern
17+
18+
```typescript
19+
export class YourMcpServer extends McpHonoServerDO<Env> {
20+
constructor(ctx: DurableObjectState, env: Env) {
21+
super(ctx, env);
22+
}
23+
24+
getImplementation(): Implementation {
25+
return {
26+
name: 'YourMcpServer',
27+
version: '1.0.0',
28+
};
29+
}
30+
31+
configureServer(server: McpServer): void {
32+
setupServerTools(server);
33+
setupServerResources(server);
34+
setupServerPrompts(server);
35+
}
36+
37+
// OPTIONAL: Override for custom HTTP endpoints
38+
protected setupRoutes(app: Hono<{ Bindings: Env }>): void {
39+
super.setupRoutes(app); // Call parent to maintain MCP functionality
40+
41+
// Add custom routes here
42+
app.get('/api/custom', (c) => c.json({ message: 'custom endpoint' }));
43+
}
44+
}
45+
```
46+
47+
## Tools Pattern (src/tools.ts)
48+
49+
Tools are functions that clients can call. Always use this exact pattern:
50+
51+
```typescript
52+
export function setupServerTools(server: McpServer) {
53+
server.tool(
54+
'tool_name', // Unique tool identifier
55+
'Brief tool description', // Human-readable description
56+
{ // Zod schema for parameters
57+
param1: z.string().describe('Parameter description'),
58+
param2: z.number().optional().describe('Optional parameter'),
59+
},
60+
async ({ param1, param2 }) => { // Implementation function
61+
// Tool logic here
62+
return {
63+
content: [
64+
{
65+
type: "text",
66+
text: `Result: ${param1}`
67+
}
68+
],
69+
// Optional: return additional data
70+
metadata: { /* any extra data */ }
71+
};
72+
}
73+
);
74+
}
75+
```
76+
77+
**Key Requirements:**
78+
- Use Zod schemas with `.describe()` for all parameters
79+
- Return content array with type "text"
80+
- Handle errors gracefully with try/catch
81+
- Use descriptive tool names (snake_case)
82+
83+
## Resources Pattern (src/resources.ts)
84+
85+
Resources provide persistent data access. Always use this pattern:
86+
87+
```typescript
88+
export function setupServerResources(server: McpServer) {
89+
server.resource(
90+
'resource_name',
91+
'protocol://path/pattern/{id}', // URI pattern with placeholders
92+
async (uri: URL) => {
93+
try {
94+
// Parse URI to extract parameters
95+
const parts = uri.pathname.split('/');
96+
const id = parts[parts.length - 1];
97+
98+
// Fetch/compute resource data
99+
const data = await fetchResourceData(id);
100+
101+
return {
102+
contents: [
103+
{
104+
text: `Resource content: ${JSON.stringify(data)}`,
105+
uri: uri.href
106+
}
107+
]
108+
};
109+
} catch (error) {
110+
throw new Error(`Failed to fetch resource: ${error.message}`);
111+
}
112+
}
113+
);
114+
}
115+
```
116+
117+
**Key Requirements:**
118+
- Use descriptive URI patterns with placeholders
119+
- Parse URI to extract parameters
120+
- Return contents array with text and uri
121+
- Always wrap in try/catch for error handling
122+
123+
## Prompts Pattern (src/prompts.ts)
124+
125+
Prompts provide reusable message templates. Always use this pattern:
126+
127+
```typescript
128+
export function setupServerPrompts(server: McpServer) {
129+
server.prompt(
130+
'prompt_name',
131+
'Brief prompt description',
132+
(args?: { param?: string }) => ({ // Optional parameters
133+
messages: [{
134+
role: 'assistant', // or 'user', 'system'
135+
content: {
136+
type: 'text',
137+
text: `Your prompt content here. ${args?.param || ''}`
138+
}
139+
}]
140+
})
141+
);
142+
}
143+
```
144+
145+
**Key Requirements:**
146+
- Use descriptive prompt names (snake_case)
147+
- Support optional parameters via args
148+
- Always return messages array
149+
- Use appropriate role (assistant/user/system)
150+
151+
## Custom Routes with setupRoutes
152+
153+
To add custom HTTP endpoints beyond MCP functionality:
154+
155+
```typescript
156+
protected setupRoutes(app: Hono<{ Bindings: Env }>): void {
157+
// CRITICAL: Always call parent first to maintain MCP functionality
158+
super.setupRoutes(app);
159+
160+
// Add custom endpoints
161+
app.get('/api/health', (c) => {
162+
return c.json({ status: 'healthy', timestamp: new Date().toISOString() });
163+
});
164+
165+
app.post('/api/webhook', async (c) => {
166+
const body = await c.req.json();
167+
// Process webhook
168+
return c.json({ received: true });
169+
});
170+
171+
// Access environment bindings via c.env
172+
app.get('/api/data', async (c) => {
173+
const result = await c.env.DATABASE.prepare('SELECT * FROM items').all();
174+
return c.json(result);
175+
});
176+
}
177+
```
178+
179+
## Development Checklist
180+
181+
When implementing MCP functionality:
182+
183+
1. **Tools**: Define in `setupServerTools()` with Zod schemas
184+
2. **Resources**: Define in `setupServerResources()` with URI patterns
185+
3. **Prompts**: Define in `setupServerPrompts()` with message templates
186+
4. **Custom Routes**: Override `setupRoutes()` if HTTP endpoints needed
187+
5. **Error Handling**: Always use try/catch in async operations
188+
6. **Types**: Use TypeScript interfaces for complex data structures
189+
7. **Testing**: Test tools, resources, and prompts independently
190+
191+
## Common Imports
192+
193+
```typescript
194+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
195+
import { McpHonoServerDO } from '@nullshot/mcp';
196+
import { Implementation } from '@modelcontextprotocol/sdk/types.js';
197+
import { z } from 'zod';
198+
import { Hono } from 'hono';
199+
```

0 commit comments

Comments
 (0)