When failing to connect to an MCP server, its very hard to understand why. There is only an opaque "Connection Error" message with no additional info. This also doesn't change at all on repeated attempts, so there is no indication we attempted again and failed. This change introduces a `toast` to visualize the error, and adds the `cause` into the error message for further debugging
82 lines
2.4 KiB
TypeScript
82 lines
2.4 KiB
TypeScript
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
import { isJSONRPCRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
|
|
function onClientError(error: Error) {
|
|
console.error("Error from inspector client:", error);
|
|
}
|
|
|
|
function onServerError(error: Error) {
|
|
if (error?.cause && JSON.stringify(error.cause).includes("ECONNREFUSED")) {
|
|
console.error("Connection refused. Is the MCP server running?");
|
|
} else if (error.message && error.message.includes("404")) {
|
|
console.error("Error accessing endpoint (HTTP 404)");
|
|
} else {
|
|
console.error("Error from MCP server:", error);
|
|
}
|
|
}
|
|
|
|
export default function mcpProxy({
|
|
transportToClient,
|
|
transportToServer,
|
|
}: {
|
|
transportToClient: Transport;
|
|
transportToServer: Transport;
|
|
}) {
|
|
let transportToClientClosed = false;
|
|
let transportToServerClosed = false;
|
|
|
|
let reportedServerSession = false;
|
|
|
|
transportToClient.onmessage = (message) => {
|
|
transportToServer.send(message).catch((error) => {
|
|
// Send error response back to client if it was a request (has id) and connection is still open
|
|
if (isJSONRPCRequest(message) && !transportToClientClosed) {
|
|
const errorResponse = {
|
|
jsonrpc: "2.0" as const,
|
|
id: message.id,
|
|
error: {
|
|
code: -32001,
|
|
message: error.cause
|
|
? `${error.message} (cause: ${error.cause})`
|
|
: error.message,
|
|
data: error,
|
|
},
|
|
};
|
|
transportToClient.send(errorResponse).catch(onClientError);
|
|
}
|
|
});
|
|
};
|
|
|
|
transportToServer.onmessage = (message) => {
|
|
if (!reportedServerSession) {
|
|
if (transportToServer.sessionId) {
|
|
// Can only report for StreamableHttp
|
|
console.error(
|
|
"Proxy <-> Server sessionId: " + transportToServer.sessionId,
|
|
);
|
|
}
|
|
reportedServerSession = true;
|
|
}
|
|
transportToClient.send(message).catch(onClientError);
|
|
};
|
|
|
|
transportToClient.onclose = () => {
|
|
if (transportToServerClosed) {
|
|
return;
|
|
}
|
|
|
|
transportToClientClosed = true;
|
|
transportToServer.close().catch(onServerError);
|
|
};
|
|
|
|
transportToServer.onclose = () => {
|
|
if (transportToClientClosed) {
|
|
return;
|
|
}
|
|
transportToServerClosed = true;
|
|
transportToClient.close().catch(onClientError);
|
|
};
|
|
|
|
transportToClient.onerror = onClientError;
|
|
transportToServer.onerror = onServerError;
|
|
}
|