Skip to content

Commit ea02f3f

Browse files
committed
Add the Hono with @vercel/mcp-adapter
1 parent 77a4b0c commit ea02f3f

11 files changed

Lines changed: 351 additions & 413 deletions

File tree

mpc/clients/mastra-mcp-client/src/mastra/agents/index.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,11 @@ import { bedrock } from '@ai-sdk/amazon-bedrock';
22
import { Agent } from '@mastra/core/agent';
33
import { mcp } from '../mcp';
44

5-
export const weatherAgent = new Agent({
6-
name: 'Weather Agent',
5+
export const agent = new Agent({
6+
name: 'Agent',
77
instructions: `
8-
You are a helpful weather assistant that provides accurate weather information.
9-
10-
Your primary function is to help users get weather details for specific locations. When responding:
11-
- Always ask for a location if none is provided
12-
- If the location name isn’t in English, please translate it
13-
- If giving a location with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York")
14-
- Include relevant details like humidity, wind conditions, and precipitation
15-
- Keep responses concise but informative
16-
17-
Use the weatherTool to fetch current weather data.
18-
`,
8+
あなたは PostgreSQLデータベースを管理しています。
9+
`,
1910
model: bedrock('us.amazon.nova-premier-v1:0'),
2011
tools: {...await mcp.getTools()},
2112
});

mpc/clients/mastra-mcp-client/src/mastra/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
import { Mastra } from '@mastra/core/mastra';
33
import { ConsoleLogger } from '@mastra/core/logger';
44
import { LangfuseExporter } from 'langfuse-vercel';
5-
import { weatherAgent } from './agents';
5+
import { agent } from './agents';
66

77
export const mastra = new Mastra({
8-
agents: { weatherAgent },
8+
agents: { agent },
99
logger: new ConsoleLogger({
1010
name: 'Mastra',
1111
level: 'info',

mpc/clients/mastra-mcp-client/src/mastra/mcp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const url = process.env.URL ?? 'http://localhost:3000/mcp';
55
// Configure MCPClient to connect to your server(s)
66
export const mcp = new MCPClient({
77
servers: {
8-
weather: {
8+
mcp: {
99
url: new URL(url),
1010
},
1111
},
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
```shell
3+
docker compose up -d
4+
```

mpc/servers/postgresql-http/package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
"test": "vitest run --passWithNoTests",
1515
"lint": "eslint",
1616
"lint-fix": "eslint --fix",
17-
"dev": "tsx watch src/index.ts",
18-
"start": "node -r source-map-support/register build/index.js"
17+
"dev": "tsx watch src/server.ts",
18+
"start": "node -r source-map-support/register build/server.js"
1919
},
2020
"devDependencies": {
2121
"@eslint/compat": "^1.3.1",
@@ -26,7 +26,6 @@
2626
"@stylistic/eslint-plugin-ts": "^4.4.1",
2727
"@types/aws-lambda": "^8.10.150",
2828
"@types/csurf": "^1.11.5",
29-
"@types/express": "^5.0.3",
3029
"@types/node": "^22.15.33",
3130
"@types/pg": "^8.15.4",
3231
"@types/source-map-support": "^0.5.10",
@@ -47,10 +46,11 @@
4746
},
4847
"dependencies": {
4948
"@aws-lambda-powertools/logger": "^2.22.0",
49+
"@hono/node-server": "^1.14.4",
5050
"@modelcontextprotocol/sdk": "^1.13.2",
51-
"csurf": "^1.11.0",
52-
"express": "^5.1.0",
53-
"pg": "^8.16.2",
51+
"@vercel/mcp-adapter": "^0.11.1",
52+
"hono": "^4.8.3",
53+
"pg": "^8.16.3",
5454
"zod": "^3.25.67"
5555
}
5656
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { Logger } from '@aws-lambda-powertools/logger';
2+
import { handler } from './route';
3+
import { Hono } from 'hono';
4+
5+
const logger = new Logger();
6+
7+
export const app = new Hono();
8+
9+
app.post('/mcp', async (c) => {
10+
// In stateless mode, create a new instance of transport and server for each request
11+
// to ensure complete isolation. A single instance would cause request ID collisions
12+
// when multiple clients connect concurrently.
13+
14+
try {
15+
return await handler(c.req.raw);
16+
} catch (error) {
17+
console.error('Error handling MCP request:', error);
18+
return c.json(
19+
{
20+
jsonrpc: '2.0',
21+
error: {
22+
code: -32603,
23+
message: 'Internal server error',
24+
},
25+
id: null,
26+
},
27+
500,
28+
);
29+
}
30+
});
31+
32+
app.get('/mcp', async (c) => {
33+
logger.info('Received GET MCP request');
34+
return c.json(
35+
{
36+
jsonrpc: '2.0',
37+
error: {
38+
code: -32000,
39+
message: 'Method not allowed.',
40+
},
41+
id: null,
42+
},
43+
405,
44+
);
45+
});
46+
47+
app.delete('/mcp', async (c) => {
48+
logger.info('Received DELETE MCP request');
49+
return c.json(
50+
{
51+
jsonrpc: '2.0',
52+
error: {
53+
code: -32000,
54+
message: 'Method not allowed.',
55+
},
56+
id: null,
57+
},
58+
405,
59+
);
60+
});
Lines changed: 3 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,156 +1,5 @@
1-
import express, { Request, Response } from 'express';
2-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3-
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
4-
import { Logger } from '@aws-lambda-powertools/logger';
5-
import { pgMcp } from './pgMcp';
6-
// Import csrf from 'csurf' for CSRF protection
7-
// This middleware adds CSRF protection to the DELETE route
8-
import csrf from 'csurf';
9-
const csrfProtection = csrf({ cookie: true });
10-
11-
const logger = new Logger();
12-
13-
const app = express();
14-
app.use(express.json());
15-
16-
// Create server instance
17-
const server = new McpServer({
18-
name: 'weather',
19-
version: '1.0.0',
20-
capabilities: {},
21-
});
22-
23-
app.post('/mcp', async (req: Request, res: Response) => {
24-
// In stateless mode, create a new instance of transport and server for each request
25-
// to ensure complete isolation. A single instance would cause request ID collisions
26-
// when multiple clients connect concurrently.
27-
28-
try {
29-
const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
30-
sessionIdGenerator: undefined,
31-
});
32-
await server.connect(transport);
33-
const body = req.body;
34-
logger.info('Received MCP request:', body);
35-
await transport.handleRequest(req, res, body);
36-
res.on('close', () => {
37-
logger.info('Request closed');
38-
transport.close();
39-
server.close();
40-
});
41-
} catch (error) {
42-
console.error('Error handling MCP request:', error);
43-
if (!res.headersSent) {
44-
res.status(500).json({
45-
jsonrpc: '2.0',
46-
error: {
47-
code: -32603,
48-
message: 'Internal server error',
49-
},
50-
id: null,
51-
});
52-
}
53-
}
54-
});
55-
56-
app.get('/mcp', async (req: Request, res: Response) => {
57-
logger.info('Received GET MCP request');
58-
res.writeHead(405).end(JSON.stringify({
59-
jsonrpc: '2.0',
60-
error: {
61-
code: -32000,
62-
message: 'Method not allowed.',
63-
},
64-
id: null,
65-
}));
66-
});
67-
68-
app.delete('/mcp', csrfProtection, async (req: Request, res: Response) => {
69-
logger.info('Received DELETE MCP request');
70-
res.writeHead(405).end(JSON.stringify({
71-
jsonrpc: '2.0',
72-
error: {
73-
code: -32000,
74-
message: 'Method not allowed.',
75-
},
76-
id: null,
77-
}));
78-
});
79-
80-
const resouces = [pgMcp.resources.schema, pgMcp.resources.status];
81-
resouces.forEach((resource) => {
82-
server.resource(
83-
resource.name,
84-
resource.uri,
85-
async () => {
86-
const content = await resource.load();
87-
return { contents: [{ ...content, uri: resource.uri, mimeType: resource.mimeType }] };
88-
},
89-
);
90-
});
91-
92-
server.tool(
93-
pgMcp.tools.query.name,
94-
pgMcp.tools.query.description,
95-
pgMcp.tools.query.parameters.shape,
96-
pgMcp.tools.query.annotations,
97-
async (params) => {
98-
const resp = await pgMcp.tools.query.execute(params);
99-
return {
100-
...resp,
101-
};
102-
});
103-
104-
server.tool(
105-
pgMcp.tools['list-tables'].name,
106-
pgMcp.tools['list-tables'].description,
107-
pgMcp.tools['list-tables'].parameters.shape,
108-
pgMcp.tools['list-tables'].annotations,
109-
async (params) => {
110-
const resp = await pgMcp.tools['list-tables'].execute(params);
111-
return {
112-
...resp,
113-
};
114-
});
115-
116-
server.tool(
117-
pgMcp.tools['describe-table'].name,
118-
pgMcp.tools['describe-table'].description,
119-
pgMcp.tools['describe-table'].parameters.shape,
120-
pgMcp.tools['describe-table'].annotations,
121-
async (params) => {
122-
const resp = await pgMcp.tools['describe-table'].execute(params);
123-
return {
124-
...resp,
125-
};
126-
});
127-
128-
server.tool(
129-
pgMcp.tools['call-procedure'].name,
130-
pgMcp.tools['call-procedure'].description,
131-
pgMcp.tools['call-procedure'].parameters.shape,
132-
pgMcp.tools['call-procedure'].annotations,
133-
async (params) => {
134-
const resp = await pgMcp.tools['call-procedure'].execute(params);
135-
return {
136-
...resp,
137-
};
138-
});
139-
140-
server.tool(
141-
pgMcp.tools['export-schema'].name,
142-
pgMcp.tools['export-schema'].description,
143-
pgMcp.tools['export-schema'].parameters.shape,
144-
pgMcp.tools['export-schema'].annotations,
145-
async (params) => {
146-
const resp = await pgMcp.tools['export-schema'].execute(params);
147-
return {
148-
...resp,
149-
};
150-
});
1+
import { handle } from 'hono/aws-lambda';
2+
import { app } from './app';
1513

1524
// Start the server
153-
const PORT = 3000;
154-
app.listen(PORT, () => {
155-
logger.info(`MCP Stateless Streamable HTTP Server listening on port ${PORT}`);
156-
});
5+
export const handler = handle(app);

mpc/servers/postgresql-http/src/pgMcp.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ const queryTool = {
8181

8282
// List tables tool
8383
const listTablesTool = {
84-
name: 'list-tables',
84+
name: 'list_tables',
8585
description: 'List all tables in the specified database schema',
8686
annotations: {
8787
title: 'List Database Tables',
@@ -138,7 +138,7 @@ const listTablesTool = {
138138

139139
// Describe table tool
140140
const describeTableTool = {
141-
name: 'describe-table',
141+
name: 'describe_table',
142142
description: 'Get detailed column information and structure for a specific table',
143143
annotations: {
144144
title: 'Describe Table Structure',
@@ -209,7 +209,7 @@ const describeTableTool = {
209209

210210
// Execute stored procedure tool
211211
const callProcedureTool = {
212-
name: 'call-procedure',
212+
name: 'call_procedure',
213213
description: 'Execute a stored procedure or function with parameters',
214214
annotations: {
215215
title: 'Call Stored Procedure',
@@ -278,7 +278,7 @@ const callProcedureTool = {
278278

279279
// Database export tool
280280
const exportSchemaTool = {
281-
name: 'export-schema',
281+
name: 'export_schema',
282282
description: 'Export database schema information in a structured format',
283283
annotations: {
284284
title: 'Export Database Schema',

0 commit comments

Comments
 (0)