diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 383f4c1aa..2665e9d98 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,6 +4,8 @@ on: - main pull_request: + release: + types: [published] jobs: build: @@ -21,3 +23,30 @@ jobs: # - run: npm ci - run: npm install --no-package-lock - run: npm run build + + publish: + runs-on: ubuntu-latest + if: github.event_name == 'release' + environment: release + needs: build + + permissions: + contents: read + id-token: write + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 18 + cache: npm + registry-url: "https://registry.npmjs.org" + + # Working around https://github.com/npm/cli/issues/4828 + # - run: npm ci + - run: npm install --no-package-lock + + # TODO: Add --provenance once the repo is public + - run: npm run publish-all + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/client/package.json b/client/package.json index 9720f0b2f..805797867 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/inspector-client", - "version": "0.1.7", + "version": "0.2.0", "description": "Client-side application for the Model Context Protocol inspector", "license": "MIT", "author": "Anthropic, PBC (https://anthropic.com)", @@ -21,7 +21,7 @@ "preview": "vite preview" }, "dependencies": { - "@modelcontextprotocol/sdk": "0.5.0", + "@modelcontextprotocol/sdk": "0.7.0", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-select": "^2.1.2", diff --git a/client/src/App.tsx b/client/src/App.tsx index 8bcc78b2f..b2132c3ed 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -16,14 +16,22 @@ import { ListToolsResultSchema, ProgressNotificationSchema, ReadResourceResultSchema, + Request, Resource, ResourceTemplate, + Result, Root, ServerNotification, Tool, } from "@modelcontextprotocol/sdk/types.js"; import { useCallback, useEffect, useRef, useState } from "react"; +import { + Notification, + StdErrNotification, + StdErrNotificationSchema +} from "./lib/notificationTypes"; + import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Bell, @@ -82,6 +90,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>({}); @@ -380,7 +391,7 @@ const App = () => { const connectMcpServer = async () => { try { - const client = new Client( + const client = new Client( { name: "mcp-inspector", version: "0.0.1", @@ -408,8 +419,6 @@ const App = () => { } const clientTransport = new SSEClientTransport(backendUrl); - await client.connect(clientTransport); - client.setNotificationHandler( ProgressNotificationSchema, (notification) => { @@ -420,6 +429,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) => [ @@ -456,6 +477,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 ba73c38c0..7d8a781be 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 000000000..7aa651828 --- /dev/null +++ b/client/src/lib/notificationTypes.ts @@ -0,0 +1,19 @@ +import { + NotificationSchema as BaseNotificationSchema, + ClientNotificationSchema, +} 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/package-lock.json b/package-lock.json index 627367253..bf2b4e8d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,20 @@ { "name": "@modelcontextprotocol/inspector", - "version": "0.1.7", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@modelcontextprotocol/inspector", - "version": "0.1.7", + "version": "0.2.0", "license": "MIT", "workspaces": [ "client", "server" ], "dependencies": { - "@modelcontextprotocol/inspector-client": "0.1.0", - "@modelcontextprotocol/inspector-server": "0.1.0", + "@modelcontextprotocol/inspector-client": "0.2.0", + "@modelcontextprotocol/inspector-server": "0.2.0", "concurrently": "^9.0.1" }, "bin": { @@ -27,10 +27,10 @@ }, "client": { "name": "@modelcontextprotocol/inspector-client", - "version": "0.1.7", + "version": "0.2.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "0.5.0", + "@modelcontextprotocol/sdk": "0.7.0", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-select": "^2.1.2", @@ -866,9 +866,9 @@ "link": true }, "node_modules/@modelcontextprotocol/sdk": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.5.0.tgz", - "integrity": "sha512-RXgulUX6ewvxjAG0kOpLMEdXXWkzWgaoCGaA2CwNW7cQCIphjpJhjpHSiaPdVCnisjRF/0Cm9KWHUuIoeiAblQ==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.7.0.tgz", + "integrity": "sha512-YlnQf8//eDHClUM607vb/6+GHmCdMnIfOkN2pcpexN4go9sYHm2JfNnqc5ILS7M8enUlwe9dQO9886l3NO3rUw==", "dependencies": { "content-type": "^1.0.5", "raw-body": "^3.0.0", @@ -5867,10 +5867,10 @@ }, "server": { "name": "@modelcontextprotocol/inspector-server", - "version": "0.1.7", + "version": "0.2.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "0.5.0", + "@modelcontextprotocol/sdk": "0.7.0", "cors": "^2.8.5", "eventsource": "^2.0.2", "express": "^4.21.0", diff --git a/package.json b/package.json index d1eaccb33..17e928698 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/inspector", - "version": "0.1.7", + "version": "0.2.0", "description": "Model Context Protocol inspector", "license": "MIT", "author": "Anthropic, PBC (https://anthropic.com)", @@ -29,11 +29,12 @@ "start-client": "cd client && npm run preview", "start": "./bin/cli.js", "prepare": "npm run build", - "prettier-fix": "prettier --write ." + "prettier-fix": "prettier --write .", + "publish-all": "npm publish --workspaces --access public && npm publish --access public" }, "dependencies": { - "@modelcontextprotocol/inspector-client": "0.1.0", - "@modelcontextprotocol/inspector-server": "0.1.0", + "@modelcontextprotocol/inspector-client": "0.2.0", + "@modelcontextprotocol/inspector-server": "0.2.0", "concurrently": "^9.0.1" }, "devDependencies": { diff --git a/server/package.json b/server/package.json index fd43209c4..3389ca5c7 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/inspector-server", - "version": "0.1.7", + "version": "0.2.0", "description": "Server-side application for the Model Context Protocol inspector", "license": "MIT", "author": "Anthropic, PBC (https://anthropic.com)", @@ -27,7 +27,7 @@ "typescript": "^5.6.2" }, "dependencies": { - "@modelcontextprotocol/sdk": "0.5.0", + "@modelcontextprotocol/sdk": "0.7.0", "cors": "^2.8.5", "eventsource": "^2.0.2", "express": "^4.21.0", diff --git a/server/src/index.ts b/server/src/index.ts index 59399e8b1..9a200eb00 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -1,8 +1,8 @@ #!/usr/bin/env node -import { parseArgs } from "node:util"; import cors from "cors"; import EventSource from "eventsource"; +import { parseArgs } from "node:util"; import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; import { @@ -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 instanceof StdioClientTransport) { + backingServerTransport.stderr!.on("data", (chunk) => { + webAppTransport.send({ + jsonrpc: "2.0", + method: "notifications/stderr", + params: { + content: chunk.toString(), + }, + }); + }); + } + mcpProxy({ transportToClient: webAppTransport, transportToServer: backingServerTransport, @@ -121,4 +138,4 @@ app.get("/config", (req, res) => { }); const PORT = process.env.PORT || 3000; -app.listen(PORT, () => {}); +app.listen(PORT, () => { });