REPORT / 01

Analysis Report · typescript-sdk v1.25.3 → v1.26.0 — CVE-2026-25536

Shared security patch analysis results

mode patchdiff ai copilot oswe-vscode-prime
02 · Lifecycle actions cancel · resume · skip · regenerate
03 · Share this analysis copy link · embed report
03 · CVE Security Analysis & Writeups ai-generated · per cve

Comprehensive security analysis generated by AI for each confirmed CVE match. Click on a CVE to view the detailed writeup including vulnerability background, technical details, patch analysis, and PoC guide.

CVE-2026-25536 NVD
AI-Generated Analysis
05 · Findings filter · search · paginate
Use quotes for exact: "SQL injection" · Operators: hello AND bye, admin OR root, -error, NOT warning
Showing 0 to 0 of 0 results
src/examples/server/elicitationFormExample.ts AI: 1 vulnerabilities 1 true positive(s) CVE-2026-25536
--- cache/typescript-sdk_v1.25.3/src/examples/server/elicitationFormExample.ts	2026-04-22 05:52:50.748127055 +0000+++ cache/typescript-sdk_v1.26.0/src/examples/server/elicitationFormExample.ts	2026-04-22 05:52:51.152156301 +0000@@ -14,308 +14,314 @@ import { isInitializeRequest } from '../../types.js'; import { createMcpExpressApp } from '../../server/express.js'; -// Create MCP server - it will automatically use AjvJsonSchemaValidator with sensible defaults-// The validator supports format validation (email, date, etc.) if ajv-formats is installed-const mcpServer = new McpServer(-    {-        name: 'form-elicitation-example-server',-        version: '1.0.0'-    },-    {-        capabilities: {}-    }-);--/**- * Example 1: Simple user registration tool- * Collects username, email, and password from the user- */-mcpServer.registerTool(-    'register_user',-    {-        description: 'Register a new user account by collecting their information',-        inputSchema: {}-    },-    async () => {-        try {-            // Request user information through form elicitation-            const result = await mcpServer.server.elicitInput({-                mode: 'form',-                message: 'Please provide your registration information:',-                requestedSchema: {-                    type: 'object',-                    properties: {-                        username: {-                            type: 'string',-                            title: 'Username',-                            description: 'Your desired username (3-20 characters)',-                            minLength: 3,-                            maxLength: 20-                        },-                        email: {-                            type: 'string',-                            title: 'Email',-                            description: 'Your email address',-                            format: 'email'-                        },-                        password: {-                            type: 'string',-                            title: 'Password',-                            description: 'Your password (min 8 characters)',-                            minLength: 8-                        },-                        newsletter: {-                            type: 'boolean',-                            title: 'Newsletter',-                            description: 'Subscribe to newsletter?',-                            default: false-                        }-                    },-                    required: ['username', 'email', 'password']-                }-            });+// Factory to create a new MCP server per session.+// Each session needs its own server+transport pair to avoid cross-session contamination.+const getServer = () => {+    // Create MCP server - it will automatically use AjvJsonSchemaValidator with sensible defaults+    // The validator supports format validation (email, date, etc.) if ajv-formats is installed+    const mcpServer = new McpServer(+        {+            name: 'form-elicitation-example-server',+            version: '1.0.0'+        },+        {+            capabilities: {}+        }+    ); -            // Handle the different possible actions-            if (result.action === 'accept' && result.content) {-                const { username, email, newsletter } = result.content as {-                    username: string;-                    email: string;-                    password: string;-                    newsletter?: boolean;-                };+    /**+     * Example 1: Simple user registration tool+     * Collects username, email, and password from the user+     */+    mcpServer.registerTool(+        'register_user',+        {+            description: 'Register a new user account by collecting their information',+            inputSchema: {}+        },+        async () => {+            try {+                // Request user information through form elicitation+                const result = await mcpServer.server.elicitInput({+                    mode: 'form',+                    message: 'Please provide your registration information:',+                    requestedSchema: {+                        type: 'object',+                        properties: {+                            username: {+                                type: 'string',+                                title: 'Username',+                                description: 'Your desired username (3-20 characters)',+                                minLength: 3,+                                maxLength: 20+                            },+                            email: {+                                type: 'string',+                                title: 'Email',+                                description: 'Your email address',+                                format: 'email'+                            },+                            password: {+                                type: 'string',+                                title: 'Password',+                                description: 'Your password (min 8 characters)',+                                minLength: 8+                            },+                            newsletter: {+                                type: 'boolean',+                                title: 'Newsletter',+                                description: 'Subscribe to newsletter?',+                                default: false+                            }+                        },+                        required: ['username', 'email', 'password']+                    }+                }); +                // Handle the different possible actions+                if (result.action === 'accept' && result.content) {+                    const { username, email, newsletter } = result.content as {+                        username: string;+                        email: string;+                        password: string;+                        newsletter?: boolean;+                    };++                    return {+                        content: [+                            {+                                type: 'text',+                                text: `Registration successful!\n\nUsername: ${username}\nEmail: ${email}\nNewsletter: ${newsletter ? 'Yes' : 'No'}`+                            }+                        ]+                    };+                } else if (result.action === 'decline') {+                    return {+                        content: [+                            {+                                type: 'text',+                                text: 'Registration cancelled by user.'+                            }+                        ]+                    };+                } else {+                    return {+                        content: [+                            {+                                type: 'text',+                                text: 'Registration was cancelled.'+                            }+                        ]+                    };+                }+            } catch (error) {                 return {                     content: [                         {                             type: 'text',-                            text: `Registration successful!\n\nUsername: ${username}\nEmail: ${email}\nNewsletter: ${newsletter ? 'Yes' : 'No'}`+                            text: `Registration failed: ${error instanceof Error ? error.message : String(error)}`                         }-                    ]+                    ],+                    isError: true+                };+            }+        }+    );++    /**+     * Example 2: Multi-step workflow with multiple form elicitation requests+     * Demonstrates how to collect information in multiple steps+     */+    mcpServer.registerTool(+        'create_event',+        {+            description: 'Create a calendar event by collecting event details',+            inputSchema: {}+        },+        async () => {+            try {+                // Step 1: Collect basic event information+                const basicInfo = await mcpServer.server.elicitInput({+                    mode: 'form',+                    message: 'Step 1: Enter basic event information',+                    requestedSchema: {+                        type: 'object',+                        properties: {+                            title: {+                                type: 'string',+                                title: 'Event Title',+                                description: 'Name of the event',+                                minLength: 1+                            },+                            description: {+                                type: 'string',+                                title: 'Description',+                                description: 'Event description (optional)'+                            }+                        },+                        required: ['title']+                    }+                });++                if (basicInfo.action !== 'accept' || !basicInfo.content) {+                    return {+                        content: [{ type: 'text', text: 'Event creation cancelled.' }]+                    };+                }++                // Step 2: Collect date and time+                const dateTime = await mcpServer.server.elicitInput({+                    mode: 'form',+                    message: 'Step 2: Enter date and time',+                    requestedSchema: {+                        type: 'object',+                        properties: {+                            date: {+                                type: 'string',+                                title: 'Date',+                                description: 'Event date',+                                format: 'date'+                            },+                            startTime: {+                                type: 'string',+                                title: 'Start Time',+                                description: 'Event start time (HH:MM)'+                            },+                            duration: {+                                type: 'integer',+                                title: 'Duration',+                                description: 'Duration in minutes',+                                minimum: 15,+                                maximum: 480+                            }+                        },+                        required: ['date', 'startTime', 'duration']+                    }+                });++                if (dateTime.action !== 'accept' || !dateTime.content) {+                    return {+                        content: [{ type: 'text', text: 'Event creation cancelled.' }]+                    };+                }++                // Combine all collected information+                const event = {+                    ...basicInfo.content,+                    ...dateTime.content                 };-            } else if (result.action === 'decline') {+                 return {                     content: [                         {                             type: 'text',-                            text: 'Registration cancelled by user.'+                            text: `Event created successfully!\n\n${JSON.stringify(event, null, 2)}`                         }                     ]                 };-            } else {+            } catch (error) {                 return {                     content: [                         {                             type: 'text',-                            text: 'Registration was cancelled.'+                            text: `Event creation failed: ${error instanceof Error ? error.message : String(error)}`                         }-                    ]+                    ],+                    isError: true                 };             }-        } catch (error) {-            return {-                content: [-                    {-                        type: 'text',-                        text: `Registration failed: ${error instanceof Error ? error.message : String(error)}`-                    }-                ],-                isError: true-            };         }-    }-);+    ); -/**- * Example 2: Multi-step workflow with multiple form elicitation requests- * Demonstrates how to collect information in multiple steps- */-mcpServer.registerTool(-    'create_event',-    {-        description: 'Create a calendar event by collecting event details',-        inputSchema: {}-    },-    async () => {-        try {-            // Step 1: Collect basic event information-            const basicInfo = await mcpServer.server.elicitInput({-                mode: 'form',-                message: 'Step 1: Enter basic event information',-                requestedSchema: {-                    type: 'object',-                    properties: {-                        title: {-                            type: 'string',-                            title: 'Event Title',-                            description: 'Name of the event',-                            minLength: 1+    /**+     * Example 3: Collecting address information+     * Demonstrates validation with patterns and optional fields+     */+    mcpServer.registerTool(+        'update_shipping_address',+        {+            description: 'Update shipping address with validation',+            inputSchema: {}+        },+        async () => {+            try {+                const result = await mcpServer.server.elicitInput({+                    mode: 'form',+                    message: 'Please provide your shipping address:',+                    requestedSchema: {+                        type: 'object',+                        properties: {+                            name: {+                                type: 'string',+                                title: 'Full Name',+                                description: 'Recipient name',+                                minLength: 1+                            },+                            street: {+                                type: 'string',+                                title: 'Street Address',+                                minLength: 1+                            },+                            city: {+                                type: 'string',+                                title: 'City',+                                minLength: 1+                            },+                            state: {+                                type: 'string',+                                title: 'State/Province',+                                minLength: 2,+                                maxLength: 2+                            },+                            zipCode: {+                                type: 'string',+                                title: 'ZIP/Postal Code',+                                description: '5-digit ZIP code'+                            },+                            phone: {+                                type: 'string',+                                title: 'Phone Number (optional)',+                                description: 'Contact phone number'+                            }                         },-                        description: {-                            type: 'string',-                            title: 'Description',-                            description: 'Event description (optional)'-                        }-                    },-                    required: ['title']-                }-            });--            if (basicInfo.action !== 'accept' || !basicInfo.content) {-                return {-                    content: [{ type: 'text', text: 'Event creation cancelled.' }]-                };-            }--            // Step 2: Collect date and time-            const dateTime = await mcpServer.server.elicitInput({-                mode: 'form',-                message: 'Step 2: Enter date and time',-                requestedSchema: {-                    type: 'object',-                    properties: {-                        date: {-                            type: 'string',-                            title: 'Date',-                            description: 'Event date',-                            format: 'date'-                        },-                        startTime: {-                            type: 'string',-                            title: 'Start Time',-                            description: 'Event start time (HH:MM)'-                        },-                        duration: {-                            type: 'integer',-                            title: 'Duration',-                            description: 'Duration in minutes',-                            minimum: 15,-                            maximum: 480-                        }-                    },-                    required: ['date', 'startTime', 'duration']-                }-            });--            if (dateTime.action !== 'accept' || !dateTime.content) {-                return {-                    content: [{ type: 'text', text: 'Event creation cancelled.' }]-                };-            }--            // Combine all collected information-            const event = {-                ...basicInfo.content,-                ...dateTime.content-            };--            return {-                content: [-                    {-                        type: 'text',-                        text: `Event created successfully!\n\n${JSON.stringify(event, null, 2)}`+                        required: ['name', 'street', 'city', 'state', 'zipCode']                     }-                ]-            };-        } catch (error) {-            return {-                content: [-                    {-                        type: 'text',-                        text: `Event creation failed: ${error instanceof Error ? error.message : String(error)}`-                    }-                ],-                isError: true-            };-        }-    }-);+                }); -/**- * Example 3: Collecting address information- * Demonstrates validation with patterns and optional fields- */-mcpServer.registerTool(-    'update_shipping_address',-    {-        description: 'Update shipping address with validation',-        inputSchema: {}-    },-    async () => {-        try {-            const result = await mcpServer.server.elicitInput({-                mode: 'form',-                message: 'Please provide your shipping address:',-                requestedSchema: {-                    type: 'object',-                    properties: {-                        name: {-                            type: 'string',-                            title: 'Full Name',-                            description: 'Recipient name',-                            minLength: 1-                        },-                        street: {-                            type: 'string',-                            title: 'Street Address',-                            minLength: 1-                        },-                        city: {-                            type: 'string',-                            title: 'City',-                            minLength: 1-                        },-                        state: {-                            type: 'string',-                            title: 'State/Province',-                            minLength: 2,-                            maxLength: 2-                        },-                        zipCode: {-                            type: 'string',-                            title: 'ZIP/Postal Code',-                            description: '5-digit ZIP code'-                        },-                        phone: {-                            type: 'string',-                            title: 'Phone Number (optional)',-                            description: 'Contact phone number'-                        }-                    },-                    required: ['name', 'street', 'city', 'state', 'zipCode']+                if (result.action === 'accept' && result.content) {+                    return {+                        content: [+                            {+                                type: 'text',+                                text: `Address updated successfully!\n\n${JSON.stringify(result.content, null, 2)}`+                            }+                        ]+                    };+                } else if (result.action === 'decline') {+                    return {+                        content: [{ type: 'text', text: 'Address update cancelled by user.' }]+                    };+                } else {+                    return {+                        content: [{ type: 'text', text: 'Address update was cancelled.' }]+                    };                 }-            });--            if (result.action === 'accept' && result.content) {+            } catch (error) {                 return {                     content: [                         {                             type: 'text',-                            text: `Address updated successfully!\n\n${JSON.stringify(result.content, null, 2)}`+                            text: `Address update failed: ${error instanceof Error ? error.message : String(error)}`                         }-                    ]-                };-            } else if (result.action === 'decline') {-                return {-                    content: [{ type: 'text', text: 'Address update cancelled by user.' }]-                };-            } else {-                return {-                    content: [{ type: 'text', text: 'Address update was cancelled.' }]+                    ],+                    isError: true                 };             }-        } catch (error) {-            return {-                content: [-                    {-                        type: 'text',-                        text: `Address update failed: ${error instanceof Error ? error.message : String(error)}`-                    }-                ],-                isError: true-            };         }-    }-);+    );++    return mcpServer;+};  async function main() {     const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000;@@ -357,7 +363,8 @@                     }                 }; -                // Connect the transport to the MCP server BEFORE handling the request+                // Create a new server per session and connect it to the transport+                const mcpServer = getServer();                 await mcpServer.connect(transport);                  await transport.handleRequest(req, res, req.body);
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
Cross-session contamination src/examples/server/elicitationFormExample.ts lines 14-363
Old Code
// Create MCP server - it will automatically use AjvJsonSchemaValidator with sensible defaults
// The validator supports format validation (email, date, etc.) if ajv-formats is installed
const mcpServer = new McpServer(
    {
        name: 'form-elicitation-example-server',
        version: '1.0.0'
    },
    {
        capabilities: {}
    }
);
...
                // Connect the transport to the MCP server BEFORE handling the request
                await mcpServer.connect(transport);

                await transport.handleRequest(req, res, req.body);
Fixed Code
// Factory to create a new MCP server per session.
// Each session needs its own server+transport pair to avoid cross-session contamination.
const getServer = () => {
    const mcpServer = new McpServer(
        {
            name: 'form-elicitation-example-server',
            version: '1.0.0'
        },
        {
            capabilities: {}
        }
    );
    ...
    return mcpServer;
};
...
                // Create a new server per session and connect it to the transport
                const mcpServer = getServer();
                await mcpServer.connect(transport);

                await transport.handleRequest(req, res, req.body);
CVE Analysis Results:
CVE-2026-25536: Yes
View CVE Description
MCP TypeScript SDK is the official TypeScript SDK for Model Context Protocol servers and clients. From version 1.10.0 to 1.25.3, cross-client response data leak when a single McpServer/Server and transport instance is reused across multiple client connections, most commonly in stateless StreamableHTTPServerTransport deployments. This issue has been patched in version 1.26.0.
src/examples/server/honoWebStandardStreamableHttp.ts AI: 1 vulnerabilities 1 true positive(s) CVE-2026-25536
--- cache/typescript-sdk_v1.25.3/src/examples/server/honoWebStandardStreamableHttp.ts	2026-04-22 05:52:50.748127055 +0000+++ cache/typescript-sdk_v1.26.0/src/examples/server/honoWebStandardStreamableHttp.ts	2026-04-22 05:52:51.152156301 +0000@@ -15,29 +15,30 @@ import { WebStandardStreamableHTTPServerTransport } from '../../server/webStandardStreamableHttp.js'; import { CallToolResult } from '../../types.js'; -// Create the MCP server-const server = new McpServer({-    name: 'hono-webstandard-mcp-server',-    version: '1.0.0'-});+// Factory function to create a new MCP server per request (stateless mode)+const getServer = () => {+    const server = new McpServer({+        name: 'hono-webstandard-mcp-server',+        version: '1.0.0'+    }); -// Register a simple greeting tool-server.registerTool(-    'greet',-    {-        title: 'Greeting Tool',-        description: 'A simple greeting tool',-        inputSchema: { name: z.string().describe('Name to greet') }-    },-    async ({ name }): Promise<CallToolResult> => {-        return {-            content: [{ type: 'text', text: `Hello, ${name}! (from Hono + WebStandard transport)` }]-        };-    }-);+    // Register a simple greeting tool+    server.registerTool(+        'greet',+        {+            title: 'Greeting Tool',+            description: 'A simple greeting tool',+            inputSchema: { name: z.string().describe('Name to greet') }+        },+        async ({ name }): Promise<CallToolResult> => {+            return {+                content: [{ type: 'text', text: `Hello, ${name}! (from Hono + WebStandard transport)` }]+            };+        }+    ); -// Create a stateless transport (no options = no session management)-const transport = new WebStandardStreamableHTTPServerTransport();+    return server;+};  // Create the Hono app const app = new Hono();@@ -56,19 +57,22 @@ // Health check endpoint app.get('/health', c => c.json({ status: 'ok' })); -// MCP endpoint-app.all('/mcp', c => transport.handleRequest(c.req.raw));+// MCP endpoint - create a fresh transport and server per request (stateless)+app.all('/mcp', async c => {+    const transport = new WebStandardStreamableHTTPServerTransport();+    const server = getServer();+    await server.connect(transport);+    return transport.handleRequest(c.req.raw);+});  // Start the server const PORT = process.env.MCP_PORT ? parseInt(process.env.MCP_PORT, 10) : 3000; -server.connect(transport).then(() => {-    console.log(`Starting Hono MCP server on port ${PORT}`);-    console.log(`Health check: http://localhost:${PORT}/health`);-    console.log(`MCP endpoint: http://localhost:${PORT}/mcp`);--    serve({-        fetch: app.fetch,-        port: PORT-    });+console.log(`Starting Hono MCP server on port ${PORT}`);+console.log(`Health check: http://localhost:${PORT}/health`);+console.log(`MCP endpoint: http://localhost:${PORT}/mcp`);++serve({+    fetch: app.fetch,+    port: PORT });
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
Improper Session Isolation src/examples/server/honoWebStandardStreamableHttp.ts Lines 15-57
Old Code
// Create the MCP server
const server = new McpServer({
    name: 'hono-webstandard-mcp-server',
    version: '1.0.0'
});

// Register a simple greeting tool
server.registerTool(
    'greet',
    {
        title: 'Greeting Tool',
        description: 'A simple greeting tool',
        inputSchema: { name: z.string().describe('Name to greet') }
    },
    async ({ name }): Promise<CallToolResult> => {
        return {
            content: [{ type: 'text', text: `Hello, ${name}! (from Hono + WebStandard transport)` }]
        };
    }
);

// Create a stateless transport (no options = no session management)
const transport = new WebStandardStreamableHTTPServerTransport();

// MCP endpoint
app.all('/mcp', c => transport.handleRequest(c.req.raw));

server.connect(transport).then(() => {
    console.log(`Starting Hono MCP server on port ${PORT}`);
    console.log(`Health check: http://localhost:${PORT}/health`);
    console.log(`MCP endpoint: http://localhost:${PORT}/mcp`);

    serve({
        fetch: app.fetch,
        port: PORT
    });
});
Fixed Code
// Factory function to create a new MCP server per request (stateless mode)
const getServer = () => {
    const server = new McpServer({
        name: 'hono-webstandard-mcp-server',
        version: '1.0.0'
    });

    // Register a simple greeting tool
    server.registerTool(
        'greet',
        {
            title: 'Greeting Tool',
            description: 'A simple greeting tool',
            inputSchema: { name: z.string().describe('Name to greet') }
        },
        async ({ name }): Promise<CallToolResult> => {
            return {
                content: [{ type: 'text', text: `Hello, ${name}! (from Hono + WebStandard transport)` }]
            };
        }
    );

    return server;
};

app.all('/mcp', async c => {
    const transport = new WebStandardStreamableHTTPServerTransport();
    const server = getServer();
    await server.connect(transport);
    return transport.handleRequest(c.req.raw);
});

console.log(`Starting Hono MCP server on port ${PORT}`);
console.log(`Health check: http://localhost:${PORT}/health`);
console.log(`MCP endpoint: http://localhost:${PORT}/mcp`);

serve({
    fetch: app.fetch,
    port: PORT
});
CVE Analysis Results:
CVE-2026-25536: Yes
View CVE Description
MCP TypeScript SDK is the official TypeScript SDK for Model Context Protocol servers and clients. From version 1.10.0 to 1.25.3, cross-client response data leak when a single McpServer/Server and transport instance is reused across multiple client connections, most commonly in stateless StreamableHTTPServerTransport deployments. This issue has been patched in version 1.26.0.
src/examples/server/ssePollingExample.ts AI: 1 vulnerabilities 1 true positive(s) CVE-2026-25536
--- cache/typescript-sdk_v1.25.3/src/examples/server/ssePollingExample.ts	2026-04-22 05:52:50.748127055 +0000+++ cache/typescript-sdk_v1.26.0/src/examples/server/ssePollingExample.ts	2026-04-22 05:52:51.152156301 +0000@@ -12,7 +12,7 @@  * Run with: npx tsx src/examples/server/ssePollingExample.ts  * Test with: curl or the MCP Inspector  */-import { Request, Response } from 'express';+import { type Request, type Response } from 'express'; import { randomUUID } from 'node:crypto'; import { McpServer } from '../../server/mcp.js'; import { createMcpExpressApp } from '../../server/express.js';@@ -21,87 +21,92 @@ import { InMemoryEventStore } from '../shared/inMemoryEventStore.js'; import cors from 'cors'; -// Create the MCP server-const server = new McpServer(-    {-        name: 'sse-polling-example',-        version: '1.0.0'-    },-    {-        capabilities: { logging: {} }-    }-);--// Register a long-running tool that demonstrates server-initiated disconnect-server.tool(-    'long-task',-    'A long-running task that sends progress updates. Server will disconnect mid-task to demonstrate polling.',-    {},-    async (_args, extra): Promise<CallToolResult> => {-        const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));--        console.log(`[${extra.sessionId}] Starting long-task...`);--        // Send first progress notification-        await server.sendLoggingMessage(-            {-                level: 'info',-                data: 'Progress: 25% - Starting work...'-            },-            extra.sessionId-        );-        await sleep(1000);--        // Send second progress notification-        await server.sendLoggingMessage(-            {-                level: 'info',-                data: 'Progress: 50% - Halfway there...'-            },-            extra.sessionId-        );-        await sleep(1000);--        // Server decides to disconnect the client to free resources-        // Client will reconnect via GET with Last-Event-ID after the transport's retryInterval-        // Use extra.closeSSEStream callback - available when eventStore is configured-        if (extra.closeSSEStream) {-            console.log(`[${extra.sessionId}] Closing SSE stream to trigger client polling...`);-            extra.closeSSEStream();+// Factory to create a new MCP server per session.+// Each session needs its own server+transport pair to avoid cross-session contamination.+const getServer = () => {+    const server = new McpServer(+        {+            name: 'sse-polling-example',+            version: '1.0.0'+        },+        {+            capabilities: { logging: {} }         }+    ); -        // Continue processing while client is disconnected-        // Events are stored in eventStore and will be replayed on reconnect-        await sleep(500);-        await server.sendLoggingMessage(-            {-                level: 'info',-                data: 'Progress: 75% - Almost done (sent while client disconnected)...'-            },-            extra.sessionId-        );--        await sleep(500);-        await server.sendLoggingMessage(-            {-                level: 'info',-                data: 'Progress: 100% - Complete!'-            },-            extra.sessionId-        );+    // Register a long-running tool that demonstrates server-initiated disconnect+    server.tool(+        'long-task',+        'A long-running task that sends progress updates. Server will disconnect mid-task to demonstrate polling.',+        {},+        async (_args, extra): Promise<CallToolResult> => {+            const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); -        console.log(`[${extra.sessionId}] Task complete`);+            console.log(`[${extra.sessionId}] Starting long-task...`); -        return {-            content: [+            // Send first progress notification+            await server.sendLoggingMessage(                 {-                    type: 'text',-                    text: 'Long task completed successfully!'-                }-            ]-        };-    }-);+                    level: 'info',+                    data: 'Progress: 25% - Starting work...'+                },+                extra.sessionId+            );+            await sleep(1000);++            // Send second progress notification+            await server.sendLoggingMessage(+                {+                    level: 'info',+                    data: 'Progress: 50% - Halfway there...'+                },+                extra.sessionId+            );+            await sleep(1000);++            // Server decides to disconnect the client to free resources+            // Client will reconnect via GET with Last-Event-ID after the transport's retryInterval+            // Use extra.closeSSEStream callback - available when eventStore is configured+            if (extra.closeSSEStream) {+                console.log(`[${extra.sessionId}] Closing SSE stream to trigger client polling...`);+                extra.closeSSEStream();+            }++            // Continue processing while client is disconnected+            // Events are stored in eventStore and will be replayed on reconnect+            await sleep(500);+            await server.sendLoggingMessage(+                {+                    level: 'info',+                    data: 'Progress: 75% - Almost done (sent while client disconnected)...'+                },+                extra.sessionId+            );++            await sleep(500);+            await server.sendLoggingMessage(+                {+                    level: 'info',+                    data: 'Progress: 100% - Complete!'+                },+                extra.sessionId+            );++            console.log(`[${extra.sessionId}] Task complete`);++            return {+                content: [+                    {+                        type: 'text',+                        text: 'Long task completed successfully!'+                    }+                ]+            };+        }+    );++    return server;+};  // Set up Express app const app = createMcpExpressApp();@@ -131,7 +136,8 @@             }         }); -        // Connect the MCP server to the transport+        // Create a new server per session and connect it to the transport+        const server = getServer();         await server.connect(transport);     } 
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
Improper Session Isolation src/examples/server/ssePollingExample.ts Lines 21-136
[Old Code]
-import { Request, Response } from 'express';
+import { type Request, type Response } from 'express';
 import { randomUUID } from 'node:crypto';
 import { McpServer } from '../../server/mcp.js';
 import { createMcpExpressApp } from '../../server/express.js';
 import { InMemoryEventStore } from '../shared/inMemoryEventStore.js';
 import cors from 'cors';
 
-// Create the MCP server
-const server = new McpServer(
-    {
-        name: 'sse-polling-example',
-        version: '1.0.0'
-    },
-    {
-        capabilities: { logging: {} }
-    }
-);
-
-// Register a long-running tool that demonstrates server-initiated disconnect
-server.tool(
-    'long-task',
-    'A long-running task that sends progress updates. Server will disconnect mid-task to demonstrate polling.',
-    {},
-    async (_args, extra): Promise<CallToolResult> => {
-        const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
-
-        console.log(`[${extra.sessionId}] Starting long-task...`);
-
-        // Send first progress notification
-        await server.sendLoggingMessage(
-            {
-                level: 'info',
-                data: 'Progress: 25% - Starting work...'
-            },
-            extra.sessionId
-        );
-        await sleep(1000);
-
-        // Send second progress notification
-        await server.sendLoggingMessage(
-            {
-                level: 'info',
-                data: 'Progress: 50% - Halfway there...'
-            },
-            extra.sessionId
-        );
-        await sleep(1000);
-
-        // Server decides to disconnect the client to free resources
-        // Client will reconnect via GET with Last-Event-ID after the transport's retryInterval
-        // Use extra.closeSSEStream callback - available when eventStore is configured
-        if (extra.closeSSEStream) {
-            console.log(`[${extra.sessionId}] Closing SSE stream to trigger client polling...`);
-            extra.closeSSEStream();
-
-        // Continue processing while client is disconnected
-        // Events are stored in eventStore and will be replayed on reconnect
-        await sleep(500);
-        await server.sendLoggingMessage(
-            {
-                level: 'info',
-                data: 'Progress: 75% - Almost done (sent while client disconnected)...'
-            },
-            extra.sessionId
-        );
-
-        await sleep(500);
-        await server.sendLoggingMessage(
-            {
-                level: 'info',
-                data: 'Progress: 100% - Complete!'
-            },
-            extra.sessionId
-        );
-
-        console.log(`[${extra.sessionId}] Task complete`);
-
-        return {
-            content: [
-                {
-                    type: 'text',
-                    text: 'Long task completed successfully!'
-                }
-            ]
-        };
-    }
-);
 
 // Set up Express app
 const app = createMcpExpressApp();
@@ -131,7 +136,8 @@
             }
         });
 
-        // Connect the MCP server to the transport
+        // Create a new server per session and connect it to the transport
+        const server = getServer();
         await server.connect(transport);
     }
 
[Fixed Code]
-import { Request, Response } from 'express';
+import { type Request, type Response } from 'express';
 import { randomUUID } from 'node:crypto';
 import { McpServer } from '../../server/mcp.js';
 import { createMcpExpressApp } from '../../server/express.js';
 import { InMemoryEventStore } from '../shared/inMemoryEventStore.js';
 import cors from 'cors';
 
+// Factory to create a new MCP server per session.
+// Each session needs its own server+transport pair to avoid cross-session contamination.
+const getServer = () => {
+    const server = new McpServer(
+        {
+            name: 'sse-polling-example',
+            version: '1.0.0'
+        },
+        {
+            capabilities: { logging: {} }
+        }
+    );
+
+    // Register a long-running tool that demonstrates server-initiated disconnect
+    server.tool(
+        'long-task',
+        'A long-running task that sends progress updates. Server will disconnect mid-task to demonstrate polling.',
+        {},
+        async (_args, extra): Promise<CallToolResult> => {
+            const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
+
+            console.log(`[${extra.sessionId}] Starting long-task...`);
+
+            // Send first progress notification
+            await server.sendLoggingMessage(
+                {
+                    level: 'info',
+                    data: 'Progress: 25% - Starting work...'
+                },
+                extra.sessionId
+            );
+            await sleep(1000);
+
+            // Send second progress notification
+            await server.sendLoggingMessage(
+                {
+                    level: 'info',
+                    data: 'Progress: 50% - Halfway there...'
+                },
+                extra.sessionId
+            );
+            await sleep(1000);
+
+            // Server decides to disconnect the client to free resources
+            // Client will reconnect via GET with Last-Event-ID after the transport's retryInterval
+            // Use extra.closeSSEStream callback - available when eventStore is configured
+            if (extra.closeSSEStream) {
+                console.log(`[${extra.sessionId}] Closing SSE stream to trigger client polling...`);
+                extra.closeSSEStream();
+            }
+
+            // Continue processing while client is disconnected
+            // Events are stored in eventStore and will be replayed on reconnect
+            await sleep(500);
+            await server.sendLoggingMessage(
+                {
+                    level: 'info',
+                    data: 'Progress: 75% - Almost done (sent while client disconnected)...'
+                },
+                extra.sessionId
+            );
+
+            await sleep(500);
+            await server.sendLoggingMessage(
+                {
+                    level: 'info',
+                    data: 'Progress: 100% - Complete!'
+                },
+                extra.sessionId
+            );
+
+            console.log(`[${extra.sessionId}] Task complete`);
+
+            return {
+                content: [
+                    {
+                        type: 'text',
+                        text: 'Long task completed successfully!'
+                    }
+                ]
+            };
+        }
+    );
+
+    return server;
+};
 
 // Set up Express app
 const app = createMcpExpressApp();
@@ -131,7 +136,8 @@
             }
         });
 
-        // Connect the MCP server to the transport
+        // Create a new server per session and connect it to the transport
+        const server = getServer();
         await server.connect(transport);
     }
CVE Analysis Results:
CVE-2026-25536: Yes
View CVE Description
MCP TypeScript SDK is the official TypeScript SDK for Model Context Protocol servers and clients. From version 1.10.0 to 1.25.3, cross-client response data leak when a single McpServer/Server and transport instance is reused across multiple client connections, most commonly in stateless StreamableHTTPServerTransport deployments. This issue has been patched in version 1.26.0.
src/examples/server/standaloneSseWithGetStreamableHttp.ts AI: 1 vulnerabilities 1 true positive(s) CVE-2026-25536
--- cache/typescript-sdk_v1.25.3/src/examples/server/standaloneSseWithGetStreamableHttp.ts	2026-04-22 05:52:50.748127055 +0000+++ cache/typescript-sdk_v1.26.0/src/examples/server/standaloneSseWithGetStreamableHttp.ts	2026-04-22 05:52:51.152156301 +0000@@ -5,35 +5,46 @@ import { isInitializeRequest, ReadResourceResult } from '../../types.js'; import { createMcpExpressApp } from '../../server/express.js'; -// Create an MCP server with implementation details-const server = new McpServer({-    name: 'resource-list-changed-notification-server',-    version: '1.0.0'-});+// Factory to create a new MCP server per session.+// Each session needs its own server+transport pair to avoid cross-session contamination.+const getServer = () => {+    const server = new McpServer({+        name: 'resource-list-changed-notification-server',+        version: '1.0.0'+    });++    const addResource = (name: string, content: string) => {+        const uri = `https://mcp-example.com/dynamic/${encodeURIComponent(name)}`;+        server.registerResource(+            name,+            uri,+            { mimeType: 'text/plain', description: `Dynamic resource: ${name}` },+            async (): Promise<ReadResourceResult> => {+                return {+                    contents: [{ uri, text: content }]+                };+            }+        );+    };++    addResource('example-resource', 'Initial content for example-resource');++    // Periodically add new resources to demonstrate notifications+    const resourceChangeInterval = setInterval(() => {+        const name = randomUUID();+        addResource(name, `Content for ${name}`);+    }, 5000);++    // Clean up the interval when the server closes+    server.server.onclose = () => {+        clearInterval(resourceChangeInterval);+    }; -// Store transports by session ID to send notifications-const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};--const addResource = (name: string, content: string) => {-    const uri = `https://mcp-example.com/dynamic/${encodeURIComponent(name)}`;-    server.registerResource(-        name,-        uri,-        { mimeType: 'text/plain', description: `Dynamic resource: ${name}` },-        async (): Promise<ReadResourceResult> => {-            return {-                contents: [{ uri, text: content }]-            };-        }-    );+    return server; }; -addResource('example-resource', 'Initial content for example-resource');--const resourceChangeInterval = setInterval(() => {-    const name = randomUUID();-    addResource(name, `Content for ${name}`);-}, 5000); // Change resources every 5 seconds for testing+// Store transports by session ID to send notifications+const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};  const app = createMcpExpressApp(); @@ -59,7 +70,8 @@                 }             }); -            // Connect the transport to the MCP server+            // Create a new server per session and connect it to the transport+            const server = getServer();             await server.connect(transport);              // Handle the request - the onsessioninitialized callback will store the transport@@ -121,7 +133,9 @@ // Handle server shutdown process.on('SIGINT', async () => {     console.log('Shutting down server...');-    clearInterval(resourceChangeInterval);-    await server.close();+    for (const sessionId in transports) {+        await transports[sessionId].close();+        delete transports[sessionId];+    }     process.exit(0); });
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
Cross-session contamination / session isolation [src/examples/server/standaloneSseWithGetStreamableHttp.ts] [5-46, 59-133]
Old Code
// Create an MCP server with implementation details
const server = new McpServer({
    name: 'resource-list-changed-notification-server',
    version: '1.0.0'
});

// Store transports by session ID to send notifications
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};

const addResource = (name: string, content: string) => {
    const uri = `https://mcp-example.com/dynamic/${encodeURIComponent(name)}`;
    server.registerResource(
        name,
        uri,
        { mimeType: 'text/plain', description: `Dynamic resource: ${name}` },
        async (): Promise<ReadResourceResult> => {
            return {
                contents: [{ uri, text: content }]
            };
        }
    );
};

addResource('example-resource', 'Initial content for example-resource');

const resourceChangeInterval = setInterval(() => {
    const name = randomUUID();
    addResource(name, `Content for ${name}`);
}, 5000); // Change resources every 5 seconds for testing

...
            // Connect the transport to the MCP server
            await server.connect(transport);

...
process.on('SIGINT', async () => {
    console.log('Shutting down server...');
    clearInterval(resourceChangeInterval);
    await server.close();
});

Fixed Code
// Factory to create a new MCP server per session.
// Each session needs its own server+transport pair to avoid cross-session contamination.
const getServer = () => {
    const server = new McpServer({
        name: 'resource-list-changed-notification-server',
        version: '1.0.0'
    });

    const addResource = (name: string, content: string) => {
        const uri = `https://mcp-example.com/dynamic/${encodeURIComponent(name)}`;
        server.registerResource(
            name,
            uri,
            { mimeType: 'text/plain', description: `Dynamic resource: ${name}` },
            async (): Promise<ReadResourceResult> => {
                return {
                    contents: [{ uri, text: content }]
                };
            }
        );
    };

    addResource('example-resource', 'Initial content for example-resource');

    const resourceChangeInterval = setInterval(() => {
        const name = randomUUID();
        addResource(name, `Content for ${name}`);
    }, 5000);

    server.server.onclose = () => {
        clearInterval(resourceChangeInterval);
    };

    return server;
};

// Store transports by session ID to send notifications
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};

...
            // Create a new server per session and connect it to the transport
            const server = getServer();
            await server.connect(transport);

...
process.on('SIGINT', async () => {
    console.log('Shutting down server...');
    for (const sessionId in transports) {
        await transports[sessionId].close();
        delete transports[sessionId];
    }
    process.exit(0);
});
CVE Analysis Results:
CVE-2026-25536: Yes
View CVE Description
MCP TypeScript SDK is the official TypeScript SDK for Model Context Protocol servers and clients. From version 1.10.0 to 1.25.3, cross-client response data leak when a single McpServer/Server and transport instance is reused across multiple client connections, most commonly in stateless StreamableHTTPServerTransport deployments. This issue has been patched in version 1.26.0.
src/server/webStandardStreamableHttp.ts AI: 1 vulnerabilities 1 true positive(s) CVE-2026-25536
--- cache/typescript-sdk_v1.25.3/src/server/webStandardStreamableHttp.ts	2026-04-22 05:52:50.756127634 +0000+++ cache/typescript-sdk_v1.26.0/src/server/webStandardStreamableHttp.ts	2026-04-22 05:52:51.156156591 +0000@@ -210,6 +210,7 @@     // when sessionId is not set (undefined), it means the transport is in stateless mode     private sessionIdGenerator: (() => string) | undefined;     private _started: boolean = false;+    private _hasHandledRequest: boolean = false;     private _streamMapping: Map<string, StreamMapping> = new Map();     private _requestToStreamMapping: Map<RequestId, string> = new Map();     private _requestResponseMap: Map<RequestId, JSONRPCMessage> = new Map();@@ -319,6 +320,13 @@      * Returns a Response object (Web Standard)      */     async handleRequest(req: Request, options?: HandleRequestOptions): Promise<Response> {+        // In stateless mode (no sessionIdGenerator), each request must use a fresh transport.+        // Reusing a stateless transport causes message ID collisions between clients.+        if (!this.sessionIdGenerator && this._hasHandledRequest) {+            throw new Error('Stateless transport cannot be reused across requests. Create a new transport per request.');+        }+        this._hasHandledRequest = true;+         // Validate request headers for DNS rebinding protection         const validationError = this.validateRequestHeaders(req);         if (validationError) {
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
Stateless Transport Reuse / Message ID Collision src/server/webStandardStreamableHttp.ts lines 210-320
Old Code:
    private _started: boolean = false;
    private _streamMapping: Map<string, StreamMapping> = new Map();
    private _requestToStreamMapping: Map<RequestId, string> = new Map();
    private _requestResponseMap: Map<RequestId, JSONRPCMessage> = new Map();
...
    async handleRequest(req: Request, options?: HandleRequestOptions): Promise<Response> {
        // Validate request headers for DNS rebinding protection
        const validationError = this.validateRequestHeaders(req);
        if (validationError) {

Fixed Code:
    private _started: boolean = false;
    private _hasHandledRequest: boolean = false;
    private _streamMapping: Map<string, StreamMapping> = new Map();
    private _requestToStreamMapping: Map<RequestId, string> = new Map();
    private _requestResponseMap: Map<RequestId, JSONRPCMessage> = new Map();
...
    async handleRequest(req: Request, options?: HandleRequestOptions): Promise<Response> {
        // In stateless mode (no sessionIdGenerator), each request must use a fresh transport.
        // Reusing a stateless transport causes message ID collisions between clients.
        if (!this.sessionIdGenerator && this._hasHandledRequest) {
            throw new Error('Stateless transport cannot be reused across requests. Create a new transport per request.');
        }
        this._hasHandledRequest = true;

        // Validate request headers for DNS rebinding protection
        const validationError = this.validateRequestHeaders(req);
        if (validationError) {
CVE Analysis Results:
CVE-2026-25536: Yes
View CVE Description
MCP TypeScript SDK is the official TypeScript SDK for Model Context Protocol servers and clients. From version 1.10.0 to 1.25.3, cross-client response data leak when a single McpServer/Server and transport instance is reused across multiple client connections, most commonly in stateless StreamableHTTPServerTransport deployments. This issue has been patched in version 1.26.0.
src/shared/protocol.ts AI: 2 vulnerabilities 2 true positive(s) CVE-2026-25536
--- cache/typescript-sdk_v1.25.3/src/shared/protocol.ts	2026-04-22 05:52:50.756127634 +0000+++ cache/typescript-sdk_v1.26.0/src/shared/protocol.ts	2026-04-22 05:52:51.156156591 +0000@@ -605,6 +605,12 @@      * The Protocol object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward.      */     async connect(transport: Transport): Promise<void> {+        if (this._transport) {+            throw new Error(+                'Already connected to a transport. Call close() before connecting to a new transport, or use a separate Protocol instance per connection.'+            );+        }+         this._transport = transport;         const _onclose = this.transport?.onclose;         this._transport.onclose = () => {@@ -642,6 +648,12 @@         this._taskProgressTokens.clear();         this._pendingDebouncedNotifications.clear(); +        // Abort all in-flight request handlers so they stop sending messages+        for (const controller of this._requestHandlerAbortControllers.values()) {+            controller.abort();+        }+        this._requestHandlerAbortControllers.clear();+         const error = McpError.fromError(ErrorCode.ConnectionClosed, 'Connection closed');          this._transport = undefined;@@ -719,6 +731,7 @@             sessionId: capturedTransport?.sessionId,             _meta: request.params?._meta,             sendNotification: async notification => {+                if (abortController.signal.aborted) return;                 // Include related-task metadata if this request is part of a task                 const notificationOptions: NotificationOptions = { relatedRequestId: request.id };                 if (relatedTaskId) {@@ -727,6 +740,9 @@                 await this.notification(notification, notificationOptions);             },             sendRequest: async (r, resultSchema, options?) => {+                if (abortController.signal.aborted) {+                    throw new McpError(ErrorCode.ConnectionClosed, 'Request was cancelled');+                }                 // Include related-task metadata if this request is part of a task                 const requestOptions: RequestOptions = { ...options, relatedRequestId: request.id };                 if (relatedTaskId && !requestOptions.relatedTask) {
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
Improper Transport Reuse / Connection Re-establishment
File src/shared/protocol.ts
Lines 605-612
Old Code:
    async connect(transport: Transport): Promise<void> {
        this._transport = transport;
        const _onclose = this.transport?.onclose;
        this._transport.onclose = () => {
Fixed Code:
    async connect(transport: Transport): Promise<void> {
        if (this._transport) {
            throw new Error(
                'Already connected to a transport. Call close() before connecting to a new transport, or use a separate Protocol instance per connection.'
            );
        }
        this._transport = transport;
        const _onclose = this.transport?.onclose;
        this._transport.onclose = () => {

Vulnerability Existed: yes
TRUE POSITIVE
In-flight request handlers continuing after connection close
File src/shared/protocol.ts
Lines 642-648, 719-740
Old Code:
    this._taskProgressTokens.clear();
    this._pendingDebouncedNotifications.clear();

    const error = McpError.fromError(ErrorCode.ConnectionClosed, 'Connection closed');
...
        sendNotification: async notification => {
            const notificationOptions: NotificationOptions = { relatedRequestId: request.id };
            if (relatedTaskId) {
                notificationOptions.relatedTask = relatedTaskId;
            }
            await this.notification(notification, notificationOptions);
        },
        sendRequest: async (r, resultSchema, options?) => {
            const requestOptions: RequestOptions = { ...options, relatedRequestId: request.id };
            if (relatedTaskId && !requestOptions.relatedTask) {
Fixed Code:
    this._taskProgressTokens.clear();
    this._pendingDebouncedNotifications.clear();

    // Abort all in-flight request handlers so they stop sending messages
    for (const controller of this._requestHandlerAbortControllers.values()) {
        controller.abort();
    }
    this._requestHandlerAbortControllers.clear();

    const error = McpError.fromError(ErrorCode.ConnectionClosed, 'Connection closed');
...
        sendNotification: async notification => {
            if (abortController.signal.aborted) return;
            const notificationOptions: NotificationOptions = { relatedRequestId: request.id };
            if (relatedTaskId) {
                notificationOptions.relatedTask = relatedTaskId;
            }
            await this.notification(notification, notificationOptions);
        },
        sendRequest: async (r, resultSchema, options?) => {
            if (abortController.signal.aborted) {
                throw new McpError(ErrorCode.ConnectionClosed, 'Request was cancelled');
            }
            const requestOptions: RequestOptions = { ...options, relatedRequestId: request.id };
            if (relatedTaskId && !requestOptions.relatedTask)
CVE Analysis Results:
CVE-2026-25536: Yes
View CVE Description
MCP TypeScript SDK is the official TypeScript SDK for Model Context Protocol servers and clients. From version 1.10.0 to 1.25.3, cross-client response data leak when a single McpServer/Server and transport instance is reused across multiple client connections, most commonly in stateless StreamableHTTPServerTransport deployments. This issue has been patched in version 1.26.0.
test/integration-tests/stateManagementStreamableHttp.test.ts AI: 1 vulnerabilities 1 true positive(s) CVE-2026-25536
--- cache/typescript-sdk_v1.25.3/test/integration-tests/stateManagementStreamableHttp.test.ts	2026-04-22 05:52:50.760127923 +0000+++ cache/typescript-sdk_v1.26.0/test/integration-tests/stateManagementStreamableHttp.test.ts	2026-04-22 05:52:51.160156880 +0000@@ -17,9 +17,11 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {     const { z } = entry;     describe('Streamable HTTP Transport Session Management', () => {-        // Function to set up the server with optional session management-        async function setupServer(withSessionManagement: boolean) {-            const server: Server = createServer();+        /**+         * Helper to create and configure a fresh McpServer instance with standard+         * resources, prompts, and tools for testing.+         */+        function createMcpServer(): McpServer {             const mcpServer = new McpServer(                 { name: 'test-server', version: '1.0.0' },                 {@@ -67,43 +69,67 @@                 }             ); -            // Create transport with or without session management-            const serverTransport = new StreamableHTTPServerTransport({-                sessionIdGenerator: withSessionManagement-                    ? () => randomUUID() // With session management, generate UUID-                    : undefined // Without session management, return undefined-            });+            return mcpServer;+        } -            await mcpServer.connect(serverTransport);+        // Function to set up the server with optional session management+        async function setupServer(withSessionManagement: boolean): Promise<{+            server: Server;+            mcpServer?: McpServer;+            serverTransport?: StreamableHTTPServerTransport;+            baseUrl: URL;+        }> {+            const server: Server = createServer(); -            server.on('request', async (req, res) => {-                await serverTransport.handleRequest(req, res);-            });+            if (withSessionManagement) {+                // Stateful mode: single transport + server for the session+                const mcpServer = createMcpServer();+                const serverTransport = new StreamableHTTPServerTransport({+                    sessionIdGenerator: () => randomUUID()+                }); -            // Start the server on a random port-            const baseUrl = await listenOnRandomPort(server);+                await mcpServer.connect(serverTransport); -            return { server, mcpServer, serverTransport, baseUrl };+                server.on('request', async (req, res) => {+                    await serverTransport.handleRequest(req, res);+                });++                // Start the server on a random port+                const baseUrl = await listenOnRandomPort(server);++                return { server, mcpServer, serverTransport, baseUrl };+            } else {+                // Stateless mode: create a fresh transport + server per request+                // to comply with the guard that stateless transports cannot be reused.+                server.on('request', async (req, res) => {+                    const mcpServer = createMcpServer();+                    const serverTransport = new StreamableHTTPServerTransport({+                        sessionIdGenerator: undefined+                    });+                    await mcpServer.connect(serverTransport);+                    await serverTransport.handleRequest(req, res);+                    // Close the per-request mcpServer after handling to avoid leaks+                    await mcpServer.close();+                });++                // Start the server on a random port+                const baseUrl = await listenOnRandomPort(server);++                return { server, baseUrl };+            }         }          describe('Stateless Mode', () => {             let server: Server;-            let mcpServer: McpServer;-            let serverTransport: StreamableHTTPServerTransport;             let baseUrl: URL;              beforeEach(async () => {                 const setup = await setupServer(false);                 server = setup.server;-                mcpServer = setup.mcpServer;-                serverTransport = setup.serverTransport;                 baseUrl = setup.baseUrl;             });              afterEach(async () => {-                // Clean up resources-                await mcpServer.close().catch(() => {});-                await serverTransport.close().catch(() => {});                 server.close();             }); @@ -259,8 +285,8 @@             beforeEach(async () => {                 const setup = await setupServer(true);                 server = setup.server;-                mcpServer = setup.mcpServer;-                serverTransport = setup.serverTransport;+                mcpServer = setup.mcpServer!;+                serverTransport = setup.serverTransport!;                 baseUrl = setup.baseUrl;             }); 
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
Session state leakage via reused stateless transport test/integration-tests/stateManagementStreamableHttp.test.ts lines 17-109
Old Code
async function setupServer(withSessionManagement: boolean) {
    const server: Server = createServer();
    const mcpServer = new McpServer(
        { name: 'test-server', version: '1.0.0' },
        { capabilities: { logging: {}, tools: {}, resources: {}, prompts: {} } }
    );
    // Add a simple resource, prompt, and tool...
    const serverTransport = new StreamableHTTPServerTransport({
        sessionIdGenerator: withSessionManagement
            ? () => randomUUID()
            : undefined
    });
    await mcpServer.connect(serverTransport);
    server.on('request', async (req, res) => {
        await serverTransport.handleRequest(req, res);
    });
    const baseUrl = await listenOnRandomPort(server);
    return { server, mcpServer, serverTransport, baseUrl };
}
Fixed Code
function createMcpServer(): McpServer {
    const mcpServer = new McpServer(
        { name: 'test-server', version: '1.0.0' },
        { capabilities: { logging: {}, tools: {}, resources: {}, prompts: {} } }
    );
    // Add a simple resource, prompt, and tool...
    return mcpServer;
}
async function setupServer(withSessionManagement: boolean): Promise<{
    server: Server;
    mcpServer?: McpServer;
    serverTransport?: StreamableHTTPServerTransport;
    baseUrl: URL;
}> {
    const server: Server = createServer();
    if (withSessionManagement) {
        const mcpServer = createMcpServer();
        const serverTransport = new StreamableHTTPServerTransport({
            sessionIdGenerator: () => randomUUID()
        });
        await mcpServer.connect(serverTransport);
        server.on('request', async (req, res) => {
            await serverTransport.handleRequest(req, res);
        });
        const baseUrl = await listenOnRandomPort(server);
        return { server, mcpServer, serverTransport, baseUrl };
    } else {
        server.on('request', async (req, res) => {
            const mcpServer = createMcpServer();
            const serverTransport = new StreamableHTTPServerTransport({
                sessionIdGenerator: undefined
            });
            await mcpServer.connect(serverTransport);
            await serverTransport.handleRequest(req, res);
            await mcpServer.close();
        });
        const baseUrl = await listenOnRandomPort(server);
        return { server, baseUrl };
    }
}
CVE Analysis Results:
CVE-2026-25536: Yes
View CVE Description
MCP TypeScript SDK is the official TypeScript SDK for Model Context Protocol servers and clients. From version 1.10.0 to 1.25.3, cross-client response data leak when a single McpServer/Server and transport instance is reused across multiple client connections, most commonly in stateless StreamableHTTPServerTransport deployments. This issue has been patched in version 1.26.0.
test/server/streamableHttp.test.ts AI: No vulnerabilities CVE-2026-25536
--- cache/typescript-sdk_v1.25.3/test/server/streamableHttp.test.ts	2026-04-22 05:52:50.764128213 +0000+++ cache/typescript-sdk_v1.26.0/test/server/streamableHttp.test.ts	2026-04-22 05:52:51.164157170 +0000@@ -1529,20 +1529,56 @@     // Test stateless mode     describe('StreamableHTTPServerTransport in stateless mode', () => {         let server: Server;-        let transport: StreamableHTTPServerTransport;         let baseUrl: URL; +        // In stateless mode, each request must use a fresh transport + server pair.+        // The HTTP server creates these per-request and delegates accordingly.         beforeEach(async () => {-            const result = await createTestServer({ sessionIdGenerator: undefined });-            server = result.server;-            transport = result.transport;-            baseUrl = result.baseUrl;+            server = createServer(async (req, res) => {+                try {+                    const { transport, mcpServer } = await createStatelessHandler();+                    await transport.handleRequest(req, res);+                    // Close the per-request mcpServer after handling to avoid leaks+                    await mcpServer.close();+                } catch (error) {+                    console.error('Error handling request:', error);+                    if (!res.headersSent) res.writeHead(500).end();+                }+            });+            baseUrl = await listenOnRandomPort(server);         });          afterEach(async () => {-            await stopTestServer({ server, transport });+            server.close();         }); +        /**+         * Creates a fresh transport + mcpServer pair for a single stateless request.+         */+        async function createStatelessHandler(): Promise<{+            transport: StreamableHTTPServerTransport;+            mcpServer: McpServer;+        }> {+            const mcpServer = new McpServer({ name: 'test-server', version: '1.0.0' }, { capabilities: { logging: {} } });++            mcpServer.tool(+                'greet',+                'A simple greeting tool',+                { name: z.string().describe('Name to greet') },+                async ({ name }): Promise<CallToolResult> => {+                    return { content: [{ type: 'text', text: `Hello, ${name}!` }] };+                }+            );++            const transport = new StreamableHTTPServerTransport({+                sessionIdGenerator: undefined+            });++            await mcpServer.connect(transport);++            return { transport, mcpServer };+        }+         it('should operate without session ID validation', async () => {             // Initialize the server first             const initResponse = await sendPostRequest(baseUrl, TEST_MESSAGES.initialize);@@ -1552,6 +1588,7 @@             expect(initResponse.headers.get('mcp-session-id')).toBeNull();              // Try request without session ID - should work in stateless mode+            // (a fresh transport is created per request)             const toolsResponse = await sendPostRequest(baseUrl, TEST_MESSAGES.toolsList);              expect(toolsResponse.status).toBe(200);@@ -1585,14 +1622,14 @@             expect(response2.status).toBe(200);         }); -        it('should reject second SSE stream even in stateless mode', async () => {-            // Despite no session ID requirement, the transport still only allows-            // one standalone SSE stream at a time+        it('should allow multiple SSE streams in stateless mode with per-request transports', async () => {+            // Each request gets its own transport, so multiple SSE streams can+            // coexist since they are handled by separate transport instances              // Initialize the server first             await sendPostRequest(baseUrl, TEST_MESSAGES.initialize); -            // Open first SSE stream+            // Open first SSE stream - this uses its own per-request transport             const stream1 = await fetch(baseUrl, {                 method: 'GET',                 headers: {@@ -1602,7 +1639,8 @@             });             expect(stream1.status).toBe(200); -            // Open second SSE stream - should still be rejected, stateless mode still only allows one+            // Open second SSE stream - also gets its own per-request transport,+            // so it should also succeed (each transport only handles one request)             const stream2 = await fetch(baseUrl, {                 method: 'GET',                 headers: {@@ -1610,7 +1648,9 @@                     'mcp-protocol-version': '2025-11-25'                 }             });-            expect(stream2.status).toBe(409); // Conflict - only one stream allowed+            // With per-request transports in stateless mode, each GET gets its own+            // transport, so the second one also succeeds+            expect(stream2.status).toBe(200);         });     }); @@ -2868,17 +2908,20 @@          describe('Combined validations', () => {             it('should validate both host and origin when both are configured', async () => {-                const result = await createTestServerWithDnsProtection({+                // In stateless mode, each request needs a fresh transport, so we+                // test invalid and valid origins with separate server instances.++                // Test with invalid origin+                const result1 = await createTestServerWithDnsProtection({                     sessionIdGenerator: undefined,                     allowedHosts: ['localhost'],                     allowedOrigins: ['http://localhost:3001'],                     enableDnsRebindingProtection: true                 });-                server = result.server;-                transport = result.transport;-                baseUrl = result.baseUrl;+                server = result1.server;+                transport = result1.transport;+                baseUrl = result1.baseUrl; -                // Test with invalid origin (host will be automatically correct via fetch)                 const response1 = await fetch(baseUrl, {                     method: 'POST',                     headers: {@@ -2893,7 +2936,20 @@                 const body1 = await response1.json();                 expect(body1.error.message).toBe('Invalid Origin header: http://evil.com'); -                // Test with valid origin+                // Clean up first server+                await stopTestServer({ server, transport });++                // Test with valid origin using a fresh server+transport+                const result2 = await createTestServerWithDnsProtection({+                    sessionIdGenerator: undefined,+                    allowedHosts: ['localhost'],+                    allowedOrigins: ['http://localhost:3001'],+                    enableDnsRebindingProtection: true+                });+                server = result2.server;+                transport = result2.transport;+                baseUrl = result2.baseUrl;+                 const response2 = await fetch(baseUrl, {                     method: 'POST',                     headers: {
AI Analysis
Vulnerability Existed: no
No vulnerability identified test/server/streamableHttp.test.ts 1529-2936
Old Code
    beforeEach(async () => {
        const result = await createTestServer({ sessionIdGenerator: undefined });
        server = result.server;
        transport = result.transport;
        baseUrl = result.baseUrl;
    });

    afterEach(async () => {
        await stopTestServer({ server, transport });
    });

    it('should reject second SSE stream even in stateless mode', async () => {
        await sendPostRequest(baseUrl, TEST_MESSAGES.initialize);

        const stream1 = await fetch(baseUrl, { method: 'GET', headers: { ... } });
        expect(stream1.status).toBe(200);

        const stream2 = await fetch(baseUrl, { method: 'GET', headers: { ... } });
        expect(stream2.status).toBe(409);
    });
Fixed Code
    beforeEach(async () => {
        server = createServer(async (req, res) => {
            try {
                const { transport, mcpServer } = await createStatelessHandler();
                await transport.handleRequest(req, res);
                await mcpServer.close();
            } catch (error) {
                console.error('Error handling request:', error);
                if (!res.headersSent) res.writeHead(500).end();
            }
        });
        baseUrl = await listenOnRandomPort(server);
    });

    afterEach(async () => {
        server.close();
    });

    it('should allow multiple SSE streams in stateless mode with per-request transports', async () => {
        await sendPostRequest(baseUrl, TEST_MESSAGES.initialize);

        const stream1 = await fetch(baseUrl, { method: 'GET', headers: { ... } });
        expect(stream1.status).toBe(200);

        const stream2 = await fetch(baseUrl, { method: 'GET', headers: { ... } });
        expect(stream2.status).toBe(200);
    });
CVE Analysis Results:
CVE-2026-25536: Yes
View CVE Description
MCP TypeScript SDK is the official TypeScript SDK for Model Context Protocol servers and clients. From version 1.10.0 to 1.25.3, cross-client response data leak when a single McpServer/Server and transport instance is reused across multiple client connections, most commonly in stateless StreamableHTTPServerTransport deployments. This issue has been patched in version 1.26.0.
test/shared/protocol-transport-handling.test.ts AI: 2 vulnerabilities 2 true positive(s) CVE-2026-25536
--- cache/typescript-sdk_v1.25.3/test/shared/protocol-transport-handling.test.ts	2026-04-22 05:52:50.764128213 +0000+++ cache/typescript-sdk_v1.26.0/test/shared/protocol-transport-handling.test.ts	2026-04-22 05:52:51.164157170 +0000@@ -27,29 +27,39 @@     } } -describe('Protocol transport handling bug', () => {-    let protocol: Protocol<Request, Notification, Result>;+function createProtocol(): Protocol<Request, Notification, Result> {+    return new (class extends Protocol<Request, Notification, Result> {+        protected assertCapabilityForMethod(): void {}+        protected assertNotificationCapability(): void {}+        protected assertRequestHandlerCapability(): void {}+        protected assertTaskCapability(): void {}+        protected assertTaskHandlerCapability(): void {}+    })();+}++describe('Protocol transport handling', () => {     let transportA: MockTransport;     let transportB: MockTransport;      beforeEach(() => {-        protocol = new (class extends Protocol<Request, Notification, Result> {-            protected assertCapabilityForMethod(): void {}-            protected assertNotificationCapability(): void {}-            protected assertRequestHandlerCapability(): void {}-            protected assertTaskCapability(): void {}-            protected assertTaskHandlerCapability(): void {}-        })();-         transportA = new MockTransport('A');         transportB = new MockTransport('B');     }); -    test('should send response to the correct transport when multiple clients are connected', async () => {-        // Set up a request handler that simulates processing time-        let resolveHandler: (value: Result) => void;-        const handlerPromise = new Promise<Result>(resolve => {-            resolveHandler = resolve;+    test('should send response to the correct transport when using separate protocol instances', async () => {+        const protocolA = createProtocol();+        const protocolB = createProtocol();++        // Each protocol gets its own resolver so we can verify responses route correctly+        let resolveA: (value: Result) => void;+        let resolveB: (value: Result) => void;+        let handlerAEnteredResolve: () => void;+        let handlerBEnteredResolve: () => void;+        const handlerAEntered = new Promise<void>(resolve => {+            handlerAEnteredResolve = resolve;+        });+        const handlerBEntered = new Promise<void>(resolve => {+            handlerBEnteredResolve = resolve;         });          const TestRequestSchema = z.object({@@ -61,13 +71,22 @@                 .optional()         }); -        protocol.setRequestHandler(TestRequestSchema, async request => {-            console.log(`Processing request from ${request.params?.from}`);-            return handlerPromise;+        protocolA.setRequestHandler(TestRequestSchema, async () => {+            return new Promise<Result>(resolve => {+                resolveA = resolve;+                handlerAEnteredResolve();+            });+        });++        protocolB.setRequestHandler(TestRequestSchema, async () => {+            return new Promise<Result>(resolve => {+                resolveB = resolve;+                handlerBEnteredResolve();+            });         });          // Client A connects and sends a request-        await protocol.connect(transportA);+        await protocolA.connect(transportA);          const requestFromA = {             jsonrpc: '2.0' as const,@@ -79,9 +98,8 @@         // Simulate client A sending a request         transportA.onmessage?.(requestFromA); -        // While A's request is being processed, client B connects-        // This overwrites the transport reference in the protocol-        await protocol.connect(transportB);+        // Client B connects to a separate protocol instance+        await protocolB.connect(transportB);          const requestFromB = {             jsonrpc: '2.0' as const,@@ -93,19 +111,18 @@         // Client B sends its own request         transportB.onmessage?.(requestFromB); -        // Now complete A's request-        resolveHandler!({ data: 'responseForA' } as Result);+        // Wait for both handlers to be invoked so resolvers are captured+        await handlerAEntered;+        await handlerBEntered;++        // Resolve each handler with distinct data+        resolveA!({ data: 'responseForA' } as Result);+        resolveB!({ data: 'responseForB' } as Result); -        // Wait for async operations to complete+        // Wait for response delivery (transport.send is async)         await new Promise(resolve => setTimeout(resolve, 10)); -        // Check where the responses went-        console.log('Transport A received:', transportA.sentMessages);-        console.log('Transport B received:', transportB.sentMessages);--        // FIXED: Each transport now receives its own response--        // Transport A should receive response for request ID 1+        // Each transport receives its own response         expect(transportA.sentMessages.length).toBe(1);         expect(transportA.sentMessages[0]).toMatchObject({             jsonrpc: '2.0',@@ -113,18 +130,17 @@             result: { data: 'responseForA' }         }); -        // Transport B should only receive its own response (when implemented)         expect(transportB.sentMessages.length).toBe(1);         expect(transportB.sentMessages[0]).toMatchObject({             jsonrpc: '2.0',             id: 2,-            result: { data: 'responseForA' } // Same handler result in this test+            result: { data: 'responseForB' }         });     }); -    test('demonstrates the timing issue with multiple rapid connections', async () => {-        const delays: number[] = [];-        const results: { transport: string; response: JSONRPCMessage[] }[] = [];+    test('demonstrates isolation with separate protocol instances for rapid connections', async () => {+        const protocolA = createProtocol();+        const protocolB = createProtocol();          const DelayedRequestSchema = z.object({             method: z.literal('test/delayed'),@@ -136,21 +152,20 @@                 .optional()         }); -        // Set up handler with variable delay-        protocol.setRequestHandler(DelayedRequestSchema, async (request, extra) => {-            const delay = request.params?.delay || 0;-            delays.push(delay);--            await new Promise(resolve => setTimeout(resolve, delay));--            return {-                processedBy: `handler-${extra.requestId}`,-                delay: delay-            } as Result;-        });+        // Set up handler with variable delay on each protocol+        for (const protocol of [protocolA, protocolB]) {+            protocol.setRequestHandler(DelayedRequestSchema, async (request, extra) => {+                const delay = request.params?.delay || 0;+                await new Promise(resolve => setTimeout(resolve, delay));+                return {+                    processedBy: `handler-${extra.requestId}`,+                    delay: delay+                } as Result;+            });+        } -        // Rapid succession of connections and requests-        await protocol.connect(transportA);+        // Connect and send requests+        await protocolA.connect(transportA);         transportA.onmessage?.({             jsonrpc: '2.0' as const,             method: 'test/delayed',@@ -160,7 +175,7 @@          // Connect B while A is processing         setTimeout(async () => {-            await protocol.connect(transportB);+            await protocolB.connect(transportB);             transportB.onmessage?.({                 jsonrpc: '2.0' as const,                 method: 'test/delayed',@@ -172,18 +187,81 @@         // Wait for all processing         await new Promise(resolve => setTimeout(resolve, 100)); -        // Collect results-        if (transportA.sentMessages.length > 0) {-            results.push({ transport: 'A', response: transportA.sentMessages });-        }-        if (transportB.sentMessages.length > 0) {-            results.push({ transport: 'B', response: transportB.sentMessages });-        }--        console.log('Timing test results:', results);--        // FIXED: Each transport receives its own responses+        // Each transport receives its own responses         expect(transportA.sentMessages.length).toBe(1);         expect(transportB.sentMessages.length).toBe(1);     });++    test('connect guard throws when calling connect() twice without closing', async () => {+        const protocol = createProtocol();++        await protocol.connect(transportA);++        await expect(protocol.connect(transportB)).rejects.toThrow(+            'Already connected to a transport. Call close() before connecting to a new transport, or use a separate Protocol instance per connection.'+        );+    });++    test('connect succeeds after calling close() first', async () => {+        const protocol = createProtocol();++        await protocol.connect(transportA);+        await protocol.close();++        // Should succeed without error+        await expect(protocol.connect(transportB)).resolves.toBeUndefined();+    });++    test('close() aborts in-flight request handlers', async () => {+        const protocol = createProtocol();++        const SlowRequestSchema = z.object({+            method: z.literal('test/slow')+        });++        let capturedSignal: AbortSignal | undefined;+        let capturedSendNotification: ((notification: Notification) => Promise<void>) | undefined;+        let resolveHandler: () => void;+        const handlerBlocking = new Promise<void>(resolve => {+            resolveHandler = resolve;+        });++        protocol.setRequestHandler(SlowRequestSchema, async (_request, extra) => {+            capturedSignal = extra.signal;+            capturedSendNotification = extra.sendNotification;+            // Block the handler until we release it+            await handlerBlocking;+            return {} as Result;+        });++        await protocol.connect(transportA);++        // Send a request to trigger the handler+        transportA.onmessage?.({+            jsonrpc: '2.0' as const,+            method: 'test/slow',+            id: 1+        });++        // Wait for the handler to start and capture the signal+        await new Promise(resolve => setTimeout(resolve, 10));+        expect(capturedSignal).toBeDefined();+        expect(capturedSignal!.aborted).toBe(false);++        // Close the protocol while the handler is still in-flight+        await protocol.close();++        // The signal should now be aborted+        expect(capturedSignal!.aborted).toBe(true);++        // sendNotification should be a no-op after close (no error thrown)+        await expect(capturedSendNotification!({ method: 'notifications/test' } as Notification)).resolves.toBeUndefined();++        // No notification should have been sent to the transport+        const notifications = transportA.sentMessages.filter((m: JSONRPCMessage) => 'method' in m && m.method === 'notifications/test');+        expect(notifications).toHaveLength(0);++        // Release the handler so the promise chain completes+        resolveHandler!();+    }); });
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
Protocol transport misrouting [test/shared/protocol-transport-handling.test.ts] [61-93, 136-187]
Old Code
    protocol.setRequestHandler(TestRequestSchema, async request => {
        console.log(`Processing request from ${request.params?.from}`);
        return handlerPromise;
    });
    await protocol.connect(transportA);
    ...
    await protocol.connect(transportB);
    ...
    resolveHandler!({ data: 'responseForA' } as Result);
Fixed Code
    const protocolA = createProtocol();
    const protocolB = createProtocol();
    protocolA.setRequestHandler(TestRequestSchema, async () => {
        return new Promise<Result>(resolve => {
            resolveA = resolve;
            handlerAEnteredResolve();
        });
    });
    protocolB.setRequestHandler(TestRequestSchema, async () => {
        return new Promise<Result>(resolve => {
            resolveB = resolve;
            handlerBEnteredResolve();
        });
    });
    await protocolA.connect(transportA);
    ...
    await protocolB.connect(transportB);
    ...
    resolveA!({ data: 'responseForA' } as Result);
    resolveB!({ data: 'responseForB' } as Result);

Vulnerability Existed: yes
TRUE POSITIVE
Protocol connect reuse and in-flight request cancellation [test/shared/protocol-transport-handling.test.ts] [187-260]
Old Code
    // No existing tests or guard for calling connect() twice on the same Protocol instance
    // No existing tests ensuring close() aborts in-flight request handlers
Fixed Code
    test('connect guard throws when calling connect() twice without closing', async () => {
        const protocol = createProtocol();
        await protocol.connect(transportA);
        await expect(protocol.connect(transportB)).rejects.toThrow(
            'Already connected to a transport. Call close() before connecting to a new transport, or use a separate Protocol instance per connection.'
        );
    });
    test('connect succeeds after calling close() first', async () => {
        const protocol = createProtocol();
        await protocol.connect(transportA);
        await protocol.close();
        await expect(protocol.connect(transportB)).resolves.toBeUndefined();
    });
    test('close() aborts in-flight request handlers', async () => {
        const protocol = createProtocol();
        const SlowRequestSchema = z.object({ method: z.literal('test/slow') });
        let capturedSignal: AbortSignal | undefined;
        let capturedSendNotification: ((notification: Notification) => Promise<void>) | undefined;
        let resolveHandler: () => void;
        const handlerBlocking = new Promise<void>(resolve => { resolveHandler = resolve; });
        protocol.setRequestHandler(SlowRequestSchema, async (_request, extra) => {
            capturedSignal = extra.signal;
            capturedSendNotification = extra.sendNotification;
            await handlerBlocking;
            return {} as Result;
        });
        await protocol.connect(transportA);
        transportA.onmessage?.({ jsonrpc: '2.0' as const, method: 'test/slow', id: 1 });
        await new Promise(resolve => setTimeout(resolve, 10));
        expect(capturedSignal!.aborted).toBe(false);
        await protocol.close();
        expect(capturedSignal!.aborted).toBe(true);
        await expect(capturedSendNotification!({ method: 'notifications/test' } as Notification)).resolves.toBeUndefined();
        const notifications = transportA.sentMessages.filter((m: JSONRPCMessage) => 'method' in m && m.method === 'notifications/test');
        expect(notifications).toHaveLength(0);
        resolveHandler!();
    });
CVE Analysis Results:
CVE-2026-25536: Yes
View CVE Description
MCP TypeScript SDK is the official TypeScript SDK for Model Context Protocol servers and clients. From version 1.10.0 to 1.25.3, cross-client response data leak when a single McpServer/Server and transport instance is reused across multiple client connections, most commonly in stateless StreamableHTTPServerTransport deployments. This issue has been patched in version 1.26.0.
Showing 1 to 9 of 9 results