Skip to content

Commit 6b27ef2

Browse files
committed
remove authInfo
1 parent 314421b commit 6b27ef2

File tree

4 files changed

+29
-201
lines changed

4 files changed

+29
-201
lines changed

src/examples/server/honoFetchStreamableHttp.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { Hono } from 'hono';
3030
import { cors } from 'hono/cors';
3131
import { serve } from '@hono/node-server';
3232
import { McpServer } from '../../server/mcp.js';
33-
import { FetchStreamableHTTPServerTransport, type AuthenticatedRequest } from '../../experimental/fetch-streamable-http/index.js';
33+
import { FetchStreamableHTTPServerTransport } from '../../experimental/fetch-streamable-http/index.js';
3434
import { CallToolResult, GetPromptResult, ReadResourceResult } from '../../types.js';
3535
import { z } from 'zod';
3636

@@ -234,16 +234,25 @@ app.use(
234234
})
235235
);
236236

237-
app.all('/mcp', async c => {
238-
const request = c.req.raw as AuthenticatedRequest;
237+
// Example auth middleware (uncomment to enable authentication):
238+
// app.use('/mcp', async (c, next) => {
239+
// const token = c.req.header('Authorization')?.replace('Bearer ', '');
240+
// if (token) {
241+
// // Validate token and set auth info in context
242+
// c.set('auth', { token, clientId: 'example-client' });
243+
// }
244+
// await next();
245+
// });
239246

247+
app.all('/mcp', async c => {
240248
// Check for existing session
241-
const sessionId = request.headers.get('mcp-session-id');
249+
const sessionId = c.req.header('mcp-session-id');
242250

243251
if (sessionId && transports.has(sessionId)) {
244252
// Reuse existing transport for this session
245253
const transport = transports.get(sessionId)!;
246-
return transport.handleRequest(request);
254+
// Pass auth from context if using auth middleware: { auth: c.get('auth') }
255+
return transport.handleRequest(c.req.raw);
247256
}
248257

249258
// For new sessions or initialization, create new transport and server
@@ -264,7 +273,8 @@ app.all('/mcp', async c => {
264273

265274
await server.connect(transport);
266275

267-
return transport.handleRequest(request);
276+
// Pass auth from context if using auth middleware: { auth: c.get('auth') }
277+
return transport.handleRequest(c.req.raw);
268278
});
269279

270280
// Health check endpoint

src/experimental/fetch-streamable-http/fetchStreamableHttpServerTransport.test.ts

Lines changed: 0 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,11 @@ import {
1212
EventStore,
1313
EventId,
1414
StreamId,
15-
AuthenticatedRequest,
1615
SessionStore,
1716
SessionState
1817
} from './fetchStreamableHttpServerTransport.js';
1918
import { McpServer } from '../../server/mcp.js';
2019
import { CallToolResult, JSONRPCMessage } from '../../types.js';
21-
import { AuthInfo } from '../../server/auth/types.js';
2220
import { zodTestMatrix, type ZodMatrixEntry } from '../../__fixtures__/zodTestMatrix.js';
2321

2422
async function getFreePort() {
@@ -270,70 +268,6 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
270268
const baseUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
271269
const webRequest = await nodeRequestToWebRequest(req, baseUrl);
272270

273-
// Handle with transport
274-
const webResponse = await transport.handleRequest(webRequest as AuthenticatedRequest);
275-
276-
// Convert Web Standard Response to Node.js response
277-
webResponseToNodeResponse(webResponse, res);
278-
} catch (error) {
279-
console.error('Error handling request:', error);
280-
if (!res.headersSent) res.writeHead(500).end();
281-
}
282-
});
283-
284-
const baseUrl = await new Promise<URL>(resolve => {
285-
// Use specified port or 0 for random port
286-
server.listen(config.port ?? 0, '127.0.0.1', () => {
287-
const addr = server.address() as AddressInfo;
288-
resolve(new URL(`http://127.0.0.1:${addr.port}`));
289-
});
290-
});
291-
292-
return { server, transport, mcpServer, baseUrl };
293-
}
294-
295-
/**
296-
* Helper to create and start authenticated test HTTP server with MCP setup
297-
*/
298-
async function createTestAuthServer(config: TestServerConfig = { sessionIdGenerator: () => crypto.randomUUID() }): Promise<{
299-
server: Server;
300-
transport: FetchStreamableHTTPServerTransport;
301-
mcpServer: McpServer;
302-
baseUrl: URL;
303-
}> {
304-
const mcpServer = new McpServer({ name: 'test-server', version: '1.0.0' }, { capabilities: { logging: {} } });
305-
306-
mcpServer.tool(
307-
'profile',
308-
'A user profile data tool',
309-
{ active: z.boolean().describe('Profile status') },
310-
async ({ active }, { authInfo }): Promise<CallToolResult> => {
311-
return { content: [{ type: 'text', text: `${active ? 'Active' : 'Inactive'} profile from token: ${authInfo?.token}!` }] };
312-
}
313-
);
314-
315-
const transport = new FetchStreamableHTTPServerTransport({
316-
sessionIdGenerator: config.sessionIdGenerator,
317-
enableJsonResponse: config.enableJsonResponse ?? false,
318-
eventStore: config.eventStore,
319-
onsessioninitialized: config.onsessioninitialized,
320-
onsessionclosed: config.onsessionclosed
321-
});
322-
323-
await mcpServer.connect(transport);
324-
325-
const server = createServer(async (req, res) => {
326-
try {
327-
// Convert Node.js request to Web Standard Request
328-
const baseUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
329-
const webRequest = (await nodeRequestToWebRequest(req, baseUrl)) as AuthenticatedRequest;
330-
331-
// Add auth info from Authorization header
332-
const authHeader = req.headers['authorization'];
333-
if (authHeader?.startsWith('Bearer ')) {
334-
webRequest.auth = { token: authHeader.split(' ')[1] } as AuthInfo;
335-
}
336-
337271
// Handle with transport
338272
const webResponse = await transport.handleRequest(webRequest);
339273

@@ -1058,105 +992,6 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
1058992
});
1059993
});
1060994

1061-
describe('FetchStreamableHTTPServerTransport with AuthInfo', () => {
1062-
let server: Server;
1063-
let transport: FetchStreamableHTTPServerTransport;
1064-
let baseUrl: URL;
1065-
let sessionId: string;
1066-
1067-
beforeEach(async () => {
1068-
const result = await createTestAuthServer();
1069-
server = result.server;
1070-
transport = result.transport;
1071-
baseUrl = result.baseUrl;
1072-
});
1073-
1074-
afterEach(async () => {
1075-
await stopTestServer({ server, transport });
1076-
});
1077-
1078-
async function initializeServer(): Promise<string> {
1079-
const response = await sendPostRequest(baseUrl, TEST_MESSAGES.initialize);
1080-
1081-
expect(response.status).toBe(200);
1082-
const newSessionId = response.headers.get('mcp-session-id');
1083-
expect(newSessionId).toBeDefined();
1084-
return newSessionId as string;
1085-
}
1086-
1087-
it('should call a tool with authInfo', async () => {
1088-
sessionId = await initializeServer();
1089-
1090-
const toolCallMessage: JSONRPCMessage = {
1091-
jsonrpc: '2.0',
1092-
method: 'tools/call',
1093-
params: {
1094-
name: 'profile',
1095-
arguments: { active: true }
1096-
},
1097-
id: 'call-1'
1098-
};
1099-
1100-
const response = await sendPostRequest(baseUrl, toolCallMessage, sessionId, { authorization: 'Bearer test-token' });
1101-
expect(response.status).toBe(200);
1102-
1103-
const text = await readSSEEvent(response);
1104-
const eventLines = text.split('\n');
1105-
const dataLine = eventLines.find(line => line.startsWith('data:'));
1106-
expect(dataLine).toBeDefined();
1107-
1108-
const eventData = JSON.parse(dataLine!.substring(5));
1109-
expect(eventData).toMatchObject({
1110-
jsonrpc: '2.0',
1111-
result: {
1112-
content: [
1113-
{
1114-
type: 'text',
1115-
text: 'Active profile from token: test-token!'
1116-
}
1117-
]
1118-
},
1119-
id: 'call-1'
1120-
});
1121-
});
1122-
1123-
it('should calls tool without authInfo when it is optional', async () => {
1124-
sessionId = await initializeServer();
1125-
1126-
const toolCallMessage: JSONRPCMessage = {
1127-
jsonrpc: '2.0',
1128-
method: 'tools/call',
1129-
params: {
1130-
name: 'profile',
1131-
arguments: { active: false }
1132-
},
1133-
id: 'call-1'
1134-
};
1135-
1136-
const response = await sendPostRequest(baseUrl, toolCallMessage, sessionId);
1137-
expect(response.status).toBe(200);
1138-
1139-
const text = await readSSEEvent(response);
1140-
const eventLines = text.split('\n');
1141-
const dataLine = eventLines.find(line => line.startsWith('data:'));
1142-
expect(dataLine).toBeDefined();
1143-
1144-
const eventData = JSON.parse(dataLine!.substring(5));
1145-
expect(eventData).toMatchObject({
1146-
jsonrpc: '2.0',
1147-
result: {
1148-
content: [
1149-
{
1150-
type: 'text',
1151-
text: 'Inactive profile from token: undefined!'
1152-
}
1153-
]
1154-
},
1155-
id: 'call-1'
1156-
});
1157-
});
1158-
});
1159-
1160995
// Test JSON Response Mode
1161996
describe('FetchStreamableHTTPServerTransport with JSON Response Mode', () => {
1162997
let server: Server;

src/experimental/fetch-streamable-http/fetchStreamableHttpServerTransport.ts

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import {
2222
SUPPORTED_PROTOCOL_VERSIONS,
2323
DEFAULT_NEGOTIATED_PROTOCOL_VERSION
2424
} from '../../types.js';
25-
import { AuthInfo } from '../../server/auth/types.js';
2625

2726
export type StreamId = string;
2827
export type EventId = string;
@@ -229,13 +228,6 @@ export interface FetchStreamableHTTPServerTransportOptions {
229228
sessionStore?: SessionStore;
230229
}
231230

232-
/**
233-
* Extended Request type that may include auth information
234-
*/
235-
export interface AuthenticatedRequest extends Request {
236-
auth?: AuthInfo;
237-
}
238-
239231
/**
240232
* Server transport for Web Standards Streamable HTTP: this implements the MCP Streamable HTTP transport specification
241233
* using Web Standard APIs (Request, Response, TransformStream).
@@ -244,22 +236,19 @@ export interface AuthenticatedRequest extends Request {
244236
*
245237
* ```typescript
246238
* // Stateful mode - server sets the session ID
247-
* const statefulTransport = new WSStreamableHTTPServerTransport({
239+
* const statefulTransport = new FetchStreamableHTTPServerTransport({
248240
* sessionIdGenerator: () => crypto.randomUUID(),
249241
* });
250242
*
251243
* // Stateless mode - explicitly set session ID to undefined
252-
* const statelessTransport = new WSStreamableHTTPServerTransport({
244+
* const statelessTransport = new FetchStreamableHTTPServerTransport({
253245
* sessionIdGenerator: undefined,
254246
* });
255247
*
256-
* // Using with Hono.js
248+
* // Hono.js usage
257249
* app.all('/mcp', async (c) => {
258250
* return transport.handleRequest(c.req.raw);
259251
* });
260-
*
261-
* // Using with pre-parsed request body
262-
* const response = await transport.handleRequest(request, await request.json());
263252
* ```
264253
*
265254
* In stateful mode:
@@ -380,7 +369,7 @@ export class FetchStreamableHTTPServerTransport implements Transport {
380369
* Handles an incoming HTTP request, whether GET, POST, or DELETE
381370
* Returns a Response object (Web Standard)
382371
*/
383-
async handleRequest(req: AuthenticatedRequest, parsedBody?: unknown): Promise<Response> {
372+
async handleRequest(req: Request): Promise<Response> {
384373
// Validate request headers for DNS rebinding protection
385374
const validationError = this.validateRequestHeaders(req);
386375
if (validationError) {
@@ -389,7 +378,7 @@ export class FetchStreamableHTTPServerTransport implements Transport {
389378

390379
switch (req.method) {
391380
case 'POST':
392-
return this.handlePostRequest(req, parsedBody);
381+
return this.handlePostRequest(req);
393382
case 'GET':
394383
return this.handleGetRequest(req);
395384
case 'DELETE':
@@ -627,7 +616,7 @@ export class FetchStreamableHTTPServerTransport implements Transport {
627616
/**
628617
* Handles POST requests containing JSON-RPC messages
629618
*/
630-
private async handlePostRequest(req: AuthenticatedRequest, parsedBody?: unknown): Promise<Response> {
619+
private async handlePostRequest(req: Request): Promise<Response> {
631620
try {
632621
// Validate the Accept header
633622
const acceptHeader = req.headers.get('accept');
@@ -645,20 +634,15 @@ export class FetchStreamableHTTPServerTransport implements Transport {
645634
return this.createJsonErrorResponse(415, -32000, 'Unsupported Media Type: Content-Type must be application/json');
646635
}
647636

648-
const authInfo: AuthInfo | undefined = req.auth;
649637
const requestInfo: RequestInfo = {
650638
headers: Object.fromEntries(req.headers.entries())
651639
};
652640

653641
let rawMessage;
654-
if (parsedBody !== undefined) {
655-
rawMessage = parsedBody;
656-
} else {
657-
try {
658-
rawMessage = await req.json();
659-
} catch {
660-
return this.createJsonErrorResponse(400, -32700, 'Parse error: Invalid JSON');
661-
}
642+
try {
643+
rawMessage = await req.json();
644+
} catch {
645+
return this.createJsonErrorResponse(400, -32700, 'Parse error: Invalid JSON');
662646
}
663647

664648
let messages: JSONRPCMessage[];
@@ -726,7 +710,7 @@ export class FetchStreamableHTTPServerTransport implements Transport {
726710
if (!hasRequests) {
727711
// if it only contains notifications or responses, return 202
728712
for (const message of messages) {
729-
this.onmessage?.(message, { authInfo, requestInfo });
713+
this.onmessage?.(message, { requestInfo });
730714
}
731715
return new Response(null, { status: 202 });
732716
}
@@ -752,7 +736,7 @@ export class FetchStreamableHTTPServerTransport implements Transport {
752736
}
753737

754738
for (const message of messages) {
755-
this.onmessage?.(message, { authInfo, requestInfo });
739+
this.onmessage?.(message, { requestInfo });
756740
}
757741
});
758742
}
@@ -819,7 +803,7 @@ export class FetchStreamableHTTPServerTransport implements Transport {
819803
};
820804
}
821805

822-
this.onmessage?.(message, { authInfo, requestInfo, closeSSEStream, closeStandaloneSSEStream });
806+
this.onmessage?.(message, { requestInfo, closeSSEStream, closeStandaloneSSEStream });
823807
}
824808
// The server SHOULD NOT close the SSE stream before sending all JSON-RPC responses
825809
// This will be handled by the send() method when responses are ready

src/experimental/fetch-streamable-http/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ export {
1313
type EventStore,
1414
type EventId,
1515
type StreamId,
16-
type AuthenticatedRequest,
1716
type SessionStore,
1817
type SessionState
1918
} from './fetchStreamableHttpServerTransport.js';

0 commit comments

Comments
 (0)