fix: create fresh Server per request in auth test helpers (#141)
The auth test server helper and no-dns-rebinding-protection example reused a single Server instance across requests, calling connect() on each new transport. SDK v1.26.0's security fix (GHSA-345p-7cg4-v4c7) now throws "Already connected to a transport" in this case. Fix by wrapping Server creation in a factory function called per request, matching the pattern already used by tools_call.ts and everything-server.ts. Also updates no-dns-rebinding-protection.ts to use registerTool() instead of the deprecated tool() API. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a1b8aaae5e
commit
37225cef7d
2 changed files with 61 additions and 47 deletions
|
|
@ -13,21 +13,27 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||||
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
|
||||||
// Create minimal MCP server
|
// Create a fresh MCP server per request to avoid "Already connected" errors
|
||||||
const server = new McpServer({
|
// after the v1.26.0 security fix (GHSA-345p-7cg4-v4c7)
|
||||||
name: 'no-dns-rebinding-protection-server',
|
function createMcpServer() {
|
||||||
version: '1.0.0'
|
const server = new McpServer({
|
||||||
});
|
name: 'no-dns-rebinding-protection-server',
|
||||||
|
version: '1.0.0'
|
||||||
|
});
|
||||||
|
|
||||||
// Add a simple tool
|
server.registerTool(
|
||||||
server.tool(
|
'echo',
|
||||||
'echo',
|
{
|
||||||
'Echo the input back',
|
description: 'Echo the input back',
|
||||||
{ message: { type: 'string' } },
|
inputSchema: { message: { type: 'string' } }
|
||||||
async ({ message }) => ({
|
},
|
||||||
content: [{ type: 'text', text: `Echo: ${message}` }]
|
async ({ message }) => ({
|
||||||
})
|
content: [{ type: 'text', text: `Echo: ${message}` }]
|
||||||
);
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
// === VULNERABLE EXPRESS APP ===
|
// === VULNERABLE EXPRESS APP ===
|
||||||
// This intentionally does NOT use createMcpExpressApp() or localhostHostValidation()
|
// This intentionally does NOT use createMcpExpressApp() or localhostHostValidation()
|
||||||
|
|
@ -37,6 +43,7 @@ app.use(express.json());
|
||||||
|
|
||||||
app.post('/mcp', async (req, res) => {
|
app.post('/mcp', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
const server = createMcpServer();
|
||||||
// Stateless: no session ID
|
// Stateless: no session ID
|
||||||
const transport = new StreamableHTTPServerTransport({
|
const transport = new StreamableHTTPServerTransport({
|
||||||
sessionIdGenerator: undefined
|
sessionIdGenerator: undefined
|
||||||
|
|
|
||||||
|
|
@ -41,43 +41,49 @@ export function createServer(
|
||||||
tokenVerifier,
|
tokenVerifier,
|
||||||
prmResourceOverride
|
prmResourceOverride
|
||||||
} = options;
|
} = options;
|
||||||
const server = new Server(
|
// Factory: create a fresh Server per request to avoid "Already connected" errors
|
||||||
{
|
// after the v1.26.0 security fix (GHSA-345p-7cg4-v4c7)
|
||||||
name: 'auth-prm-pathbased-server',
|
function createMcpServer() {
|
||||||
version: '1.0.0'
|
const server = new Server(
|
||||||
},
|
{
|
||||||
{
|
name: 'auth-prm-pathbased-server',
|
||||||
capabilities: {
|
version: '1.0.0'
|
||||||
tools: {}
|
},
|
||||||
}
|
{
|
||||||
}
|
capabilities: {
|
||||||
);
|
tools: {}
|
||||||
|
|
||||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
||||||
return {
|
|
||||||
tools: [
|
|
||||||
{
|
|
||||||
name: 'test-tool',
|
|
||||||
inputSchema: { type: 'object' }
|
|
||||||
}
|
}
|
||||||
]
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
server.setRequestHandler(
|
|
||||||
CallToolRequestSchema,
|
|
||||||
async (request): Promise<CallToolResult> => {
|
|
||||||
if (request.params.name === 'test-tool') {
|
|
||||||
return {
|
|
||||||
content: [{ type: 'text', text: 'test' }]
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
throw new McpError(
|
);
|
||||||
ErrorCode.InvalidParams,
|
|
||||||
`Tool ${request.params.name} not found`
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||||
);
|
return {
|
||||||
}
|
tools: [
|
||||||
);
|
{
|
||||||
|
name: 'test-tool',
|
||||||
|
inputSchema: { type: 'object' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
server.setRequestHandler(
|
||||||
|
CallToolRequestSchema,
|
||||||
|
async (request): Promise<CallToolResult> => {
|
||||||
|
if (request.params.name === 'test-tool') {
|
||||||
|
return {
|
||||||
|
content: [{ type: 'text', text: 'test' }]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new McpError(
|
||||||
|
ErrorCode.InvalidParams,
|
||||||
|
`Tool ${request.params.name} not found`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
@ -151,6 +157,7 @@ export function createServer(
|
||||||
|
|
||||||
authMiddleware(req, res, async (err?: any) => {
|
authMiddleware(req, res, async (err?: any) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
const server = createMcpServer();
|
||||||
const transport = new StreamableHTTPServerTransport({
|
const transport = new StreamableHTTPServerTransport({
|
||||||
sessionIdGenerator: undefined
|
sessionIdGenerator: undefined
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue