modelcontextprotocol / modelcontextprotocol/typescript-sdk
Cannot destructure property 'requestInfo' of 'undefined' as it is undefined.
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 2.2k
- Avg merge
- 3d 15h
- Merged PRs (30d)
- 4
Description
Describe the bug
If destruct requestInfo form the secon parameter of a tool, it may encounter error when it is null although the parameter is not nullable.
For example:
server.registerTool("get_guide", {
title: "Get guide",
annotations: {
readOnlyHint: true
}
}, async ({ guide = "all" }, { requestInfo }) => {
//...
});
in secnarios when this tool is called for the first time, this parameter is null and the client gets the Cannot destructure property 'requestInfo' of 'undefined' as it is undefined. error.
To Reproduce
Steps to reproduce the behavior:
1.
Expected behavior
If it is nullable, it should be defined in type definition and then it should not be destructable in above way.
Logs
Cannot destructure property 'requestInfo' of 'undefined' as it is undefined.
Additional context
Here is the server implementation (based on HTTP+ Session server):
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
import express from "express";
import { randomUUID } from "node:crypto";
import { setupServer } from "./server.js";
import { trackEvent } from "./utils.js";
const PORT = 3300;
const ALLOWED_HOSTS = ["127.0.0.1", "localhost", `localhost:${PORT}`, `127.0.0.1:${PORT}`];
console.log(`Hopper MCP server\nlistening on port ${PORT}...`);
const app = express();
app.use(express.json());
trackEvent("server_started", {});
// Map to store transports by session ID
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
// Handle POST requests for client-to-server communication
app.post("/mcp", async (req, res) => {
// Check for existing session ID
const sessionId = req.headers["mcp-session-id"] as string | undefined;
let transport: StreamableHTTPServerTransport;
if (sessionId && transports[sessionId]) {
// Reuse existing transport
transport = transports[sessionId];
} else if (!sessionId && isInitializeRequest(req.body)) {
// New initialization request
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
onsessioninitialized: sId => {
// Store the transport by session ID
transports[sId] = transport;
},
// DNS rebinding protection is disabled by default for backwards compatibility. If you are running this server
// locally, make sure to set:
enableDnsRebindingProtection: true,
allowedHosts: ALLOWED_HOSTS
});
// Clean up transport when closed
transport.onclose = () => {
if (transport.sessionId) {
delete transports[transport.sessionId];
}
};
trackEvent("session_initialized", {}, req);
const server = setupServer();
// Connect to the MCP server
await server.connect(transport);
} else {
// Invalid request
res.status(400).json({
jsonrpc: "2.0",
error: {
code: -32000,
message: "Bad Request: No valid session ID provided"
},
id: null
});
return;
}
// Handle the request
await transport.handleRequest(req, res, req.body);
});
// Reusable handler for GET and DELETE requests
const handleSessionRequest = async (req: express.Request, res: express.Response) => {
const sessionId = req.headers["mcp-session-id"] as string | undefined;
if (!sessionId || !transports[sessionId]) {
res.status(400).send("Invalid or missing session ID");
return;
}
const transport = transports[sessionId];
await transport.handleRequest(req, res);
};
// Handle GET requests for server-to-client notifications via SSE
app.get("/mcp", handleSessionRequest);
// Handle DELETE requests for session termination
app.delete("/mcp", handleSessionRequest);
app.listen(PORT);
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the registerTool callback API used by the get_guide example and inspect how the second callback parameter is supplied during the first tool call. Reproduce the failure with the provided StreamableHTTPServerTransport setup, then check the relevant type definition and runtime dispatch behavior. Done means the callback no longer throws for the documented invocation and its nullability is represented consistently.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100