diff --git a/client/src/App.tsx b/client/src/App.tsx index 5df8f4e..58f9656 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -12,17 +12,26 @@ import { ListPromptsResultSchema, ListResourcesResultSchema, ListResourceTemplatesResultSchema, + Request, ListRootsRequestSchema, ListToolsResultSchema, ProgressNotificationSchema, ReadResourceResultSchema, Resource, ResourceTemplate, + Result, Root, ServerNotification, Tool, } from "@modelcontextprotocol/sdk/types.js"; import { useCallback, useEffect, useRef, useState } from "react"; + +import { + StderrNotificationSchema, + StdErrNotification, + Notification, +} from "./lib/notificationTypes"; + // Add dark mode class based on system preference if (window.matchMedia("(prefers-color-scheme: dark)").matches) { document.documentElement.classList.add("dark"); @@ -87,6 +96,9 @@ const App = () => { >([]); const [mcpClient, setMcpClient] = useState(null); const [notifications, setNotifications] = useState([]); + const [stdErrNotifications, setStdErrNotifications] = useState< + StdErrNotification[] + >([]); const [roots, setRoots] = useState([]); const [env, setEnv] = useState>({}); @@ -383,7 +395,7 @@ const App = () => { const connectMcpServer = async () => { try { - const client = new Client( + const client = new Client( { name: "mcp-inspector", version: "0.0.1", @@ -411,8 +423,6 @@ const App = () => { } const clientTransport = new SSEClientTransport(backendUrl); - await client.connect(clientTransport); - client.setNotificationHandler( ProgressNotificationSchema, (notification) => { @@ -423,6 +433,18 @@ const App = () => { }, ); + client.setNotificationHandler( + StderrNotificationSchema, + (notification) => { + setStdErrNotifications((prevErrorNotifications) => [ + ...prevErrorNotifications, + notification, + ]); + }, + ); + + await client.connect(clientTransport); + client.setRequestHandler(CreateMessageRequestSchema, (request) => { return new Promise((resolve, reject) => { setPendingSampleRequests((prev) => [ @@ -459,6 +481,7 @@ const App = () => { env={env} setEnv={setEnv} onConnect={connectMcpServer} + stdErrNotifications={stdErrNotifications} />
diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx index ba73c38..7d8a781 100644 --- a/client/src/components/Sidebar.tsx +++ b/client/src/components/Sidebar.tsx @@ -10,6 +10,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { StdErrNotification } from "@/lib/notificationTypes"; interface SidebarProps { connectionStatus: "disconnected" | "connected" | "error"; @@ -24,6 +25,7 @@ interface SidebarProps { env: Record; setEnv: (env: Record) => void; onConnect: () => void; + stdErrNotifications: StdErrNotification[]; } const Sidebar = ({ @@ -39,6 +41,7 @@ const Sidebar = ({ env, setEnv, onConnect, + stdErrNotifications, }: SidebarProps) => { const [showEnvVars, setShowEnvVars] = useState(false); @@ -187,6 +190,25 @@ const Sidebar = ({ : "Disconnected"}
+ {stdErrNotifications.length > 0 && ( + <> +
+

+ Error output from MCP server +

+
+ {stdErrNotifications.map((notification, index) => ( +
+ {notification.params.content} +
+ ))} +
+
+ + )}
diff --git a/client/src/lib/notificationTypes.ts b/client/src/lib/notificationTypes.ts new file mode 100644 index 0000000..3d7f97a --- /dev/null +++ b/client/src/lib/notificationTypes.ts @@ -0,0 +1,19 @@ +import { + ClientNotificationSchema, + NotificationSchema as BaseNotificationSchema, +} from "@modelcontextprotocol/sdk/types.js"; +import { z } from "zod"; + +export const StderrNotificationSchema = BaseNotificationSchema.extend({ + method: z.literal("notifications/stderr"), + params: z.object({ + content: z.string(), + }), +}); + +export const NotificationSchema = ClientNotificationSchema.or( + StderrNotificationSchema, +); + +export type StdErrNotification = z.infer; +export type Notification = z.infer; diff --git a/server/src/index.ts b/server/src/index.ts index 59399e8..9806875 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -42,7 +42,12 @@ const createTransport = async (query: express.Request["query"]) => { console.log( `Stdio transport: command=${command}, args=${args}, env=${JSON.stringify(env)}`, ); - const transport = new StdioClientTransport({ command, args, env }); + const transport = new StdioClientTransport({ + command, + args, + env, + stderr: "pipe", + }); await transport.start(); console.log("Spawned stdio transport"); return transport; @@ -75,6 +80,18 @@ app.get("/sse", async (req, res) => { await webAppTransport.start(); + if (backingServerTransport.stderr) { + backingServerTransport.stderr.on("data", (chunk) => { + webAppTransport.send({ + jsonrpc: "2.0", + method: "notifications/stderr", + params: { + content: chunk.toString(), + }, + }); + }); + } + mcpProxy({ transportToClient: webAppTransport, transportToServer: backingServerTransport,