diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index f56f4444..a74cebe8 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,13 @@ # @mcp-use/cli +## 2.1.8 + +### Patch Changes + +- Updated dependencies + - @mcp-use/inspector@0.3.8 + - mcp-use@1.0.4 + ## 2.1.7 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 57ee2a5e..96831b15 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@mcp-use/cli", - "version": "2.1.7", + "version": "2.1.8", "description": "Build tool for MCP UI widgets - bundles React components into standalone HTML pages for Model Context Protocol servers", "author": "mcp-use, Inc.", "license": "MIT", diff --git a/packages/inspector/CHANGELOG.md b/packages/inspector/CHANGELOG.md index c93d6a57..a08ec166 100644 --- a/packages/inspector/CHANGELOG.md +++ b/packages/inspector/CHANGELOG.md @@ -1,5 +1,13 @@ # @mcp-use/inspector +## 0.3.8 + +### Patch Changes + +- fix: support multiple clients per server +- Updated dependencies + - mcp-use@1.0.4 + ## 0.3.7 ### Patch Changes diff --git a/packages/inspector/package.json b/packages/inspector/package.json index c2c25957..560fefcc 100644 --- a/packages/inspector/package.json +++ b/packages/inspector/package.json @@ -1,7 +1,7 @@ { "name": "@mcp-use/inspector", "type": "module", - "version": "0.3.7", + "version": "0.3.8", "description": "MCP Inspector - A tool for inspecting and debugging MCP servers", "author": "", "license": "MIT", diff --git a/packages/inspector/src/client/components/InspectorDashboard.tsx b/packages/inspector/src/client/components/InspectorDashboard.tsx index 40046c70..70d9801d 100644 --- a/packages/inspector/src/client/components/InspectorDashboard.tsx +++ b/packages/inspector/src/client/components/InspectorDashboard.tsx @@ -124,8 +124,13 @@ export function InspectorDashboard() { }, {} as Record), } + // Map UI transport type to actual transport type + // "SSE" in UI means "Streamable HTTP" which uses 'http' transport + // "WebSocket" in UI means "WebSocket" which uses 'sse' transport + const actualTransportType = transportType === 'SSE' ? 'http' : 'sse' + // For now, use URL as both ID and name - this will need proper implementation - addConnection(url, url, proxyConfig) + addConnection(url, url, proxyConfig, actualTransportType) } const handleClearAllConnections = () => { @@ -222,7 +227,7 @@ export function InspectorDashboard() { > v - {typeof window !== 'undefined' && (window as any).__INSPECTOR_VERSION__ || '1.0.0'} + {(typeof window !== 'undefined' && (window as any).__INSPECTOR_VERSION__) || '1.0.0'} @@ -268,7 +273,7 @@ export function InspectorDashboard() {

{connection.name}

- {connection.error + {connection.error && connection.state !== 'ready' ? ( @@ -399,7 +404,7 @@ export function InspectorDashboard() { Streamable HTTP - WebSocket + Server-Sent Events (SSE)
diff --git a/packages/inspector/src/client/components/Layout.tsx b/packages/inspector/src/client/components/Layout.tsx index 001e3fa6..fea366a1 100644 --- a/packages/inspector/src/client/components/Layout.tsx +++ b/packages/inspector/src/client/components/Layout.tsx @@ -233,7 +233,7 @@ export function Layout({ children }: LayoutProps) { const existing = connections.find(c => c.url === autoConnectUrl) if (!existing) { setIsAutoConnecting(true) - addConnection(autoConnectUrl, 'Local MCP Server') + addConnection(autoConnectUrl, 'Local MCP Server', undefined, 'http') // Navigate immediately but keep loading screen visible a bit longer to avoid flash navigate(`/servers/${encodeURIComponent(autoConnectUrl)}`) setTimeout(() => { @@ -254,7 +254,7 @@ export function Layout({ children }: LayoutProps) { const existing = connections.find(c => c.url === config.autoConnectUrl) if (!existing) { // Auto-connect to the local server - addConnection(config.autoConnectUrl, 'Local MCP Server') + addConnection(config.autoConnectUrl, 'Local MCP Server', undefined, 'http') } } }) @@ -383,7 +383,7 @@ export function Layout({ children }: LayoutProps) { {selectedServer && (
- {selectedServer.error + {selectedServer.error && selectedServer.state !== 'ready' ? ( diff --git a/packages/inspector/src/client/context/McpContext.tsx b/packages/inspector/src/client/context/McpContext.tsx index b3945974..284800ce 100644 --- a/packages/inspector/src/client/context/McpContext.tsx +++ b/packages/inspector/src/client/context/McpContext.tsx @@ -21,7 +21,7 @@ interface MCPConnection { interface McpContextType { connections: MCPConnection[] - addConnection: (url: string, name?: string, proxyConfig?: { proxyAddress?: string, proxyToken?: string, customHeaders?: Record }) => void + addConnection: (url: string, name?: string, proxyConfig?: { proxyAddress?: string, proxyToken?: string, customHeaders?: Record }, transportType?: 'http' | 'sse') => void removeConnection: (id: string) => void getConnection: (id: string) => MCPConnection | undefined } @@ -33,12 +33,14 @@ interface SavedConnection { url: string name: string proxyConfig?: { proxyAddress?: string, proxyToken?: string, customHeaders?: Record } + transportType?: 'http' | 'sse' } -function McpConnectionWrapper({ url, name, proxyConfig, onUpdate, onRemove: _onRemove }: { +function McpConnectionWrapper({ url, name, proxyConfig, transportType, onUpdate, onRemove: _onRemove }: { url: string name: string proxyConfig?: { proxyAddress?: string, proxyToken?: string, customHeaders?: Record } + transportType?: 'http' | 'sse' onUpdate: (connection: MCPConnection) => void onRemove: () => void }) { @@ -79,6 +81,7 @@ function McpConnectionWrapper({ url, name, proxyConfig, onUpdate, onRemove: _onR url: finalUrl, callbackUrl, customHeaders: Object.keys(customHeaders).length > 0 ? customHeaders : undefined, + transportType: transportType || 'http', // Default to 'http' for Streamable HTTP }) const onUpdateRef = useRef(onUpdate) const prevConnectionRef = useRef(null) @@ -194,12 +197,21 @@ export function McpProvider({ children }: { children: ReactNode }) { && typeof conn.id === 'string' && typeof conn.url === 'string' && typeof conn.name === 'string' + }).map((conn: any) => { + // Migrate existing connections to include transportType + if (!conn.transportType) { + conn.transportType = 'http' // Default to 'http' for Streamable HTTP + } + return conn }) : [] - // If we filtered out any invalid connections, update localStorage - if (validConnections.length !== parsed.length) { - console.warn('Cleaned up invalid connections from localStorage') + // If we filtered out any invalid connections or migrated transport types, update localStorage + const hasChanges = validConnections.length !== parsed.length + || validConnections.some((conn: any) => conn.transportType === 'http' && !parsed.find((p: any) => p.id === conn.id && p.transportType)) + + if (hasChanges) { + console.warn('Updated connections in localStorage with transport type migration') localStorage.setItem('mcp-inspector-connections', JSON.stringify(validConnections)) } @@ -218,13 +230,14 @@ export function McpProvider({ children }: { children: ReactNode }) { setConnectionVersion(v => v + 1) }, []) - const addConnection = useCallback((url: string, name?: string, proxyConfig?: { proxyAddress?: string, proxyToken?: string, customHeaders?: Record }) => { + const addConnection = useCallback((url: string, name?: string, proxyConfig?: { proxyAddress?: string, proxyToken?: string, customHeaders?: Record }, transportType?: 'http' | 'sse') => { const connectionName = name || url const newConnection: SavedConnection = { id: url, url, name: connectionName, proxyConfig, + transportType: transportType || 'http', // Default to 'http' for Streamable HTTP } setSavedConnections((prev) => { @@ -289,6 +302,7 @@ export function McpProvider({ children }: { children: ReactNode }) { url={saved.url} name={saved.name} proxyConfig={saved.proxyConfig} + transportType={saved.transportType} onUpdate={updateConnection} onRemove={() => removeConnection(saved.id)} /> diff --git a/packages/mcp-use/CHANGELOG.md b/packages/mcp-use/CHANGELOG.md index 99d6075c..3a6db034 100644 --- a/packages/mcp-use/CHANGELOG.md +++ b/packages/mcp-use/CHANGELOG.md @@ -1,5 +1,13 @@ # mcp-use +## 1.0.4 + +### Patch Changes + +- fix: support multiple clients per server +- Updated dependencies + - @mcp-use/inspector@0.3.8 + ## 1.0.3 ### Patch Changes diff --git a/packages/mcp-use/package.json b/packages/mcp-use/package.json index 36fb768c..d7cca0f2 100644 --- a/packages/mcp-use/package.json +++ b/packages/mcp-use/package.json @@ -1,7 +1,7 @@ { "name": "mcp-use", "type": "module", - "version": "1.0.3", + "version": "1.0.4", "packageManager": "pnpm@10.6.1", "description": "Opinionated MCP Framework for TypeScript (@modelcontextprotocol/sdk compatible) - Build MCP Agents and Clients + MCP Servers with support for MCP-UI.", "author": "mcp-use, Inc.", @@ -88,7 +88,7 @@ "example:observability": "tsx examples/client/observability.ts" }, "peerDependencies": { - "@mcp-use/inspector": "^0.3.7", + "@mcp-use/inspector": "^0.3.8", "cors": "^2.8.5", "express": "^4.18.2", "langfuse": "^3.32.0", diff --git a/packages/mcp-use/src/server/mcp-server.ts b/packages/mcp-use/src/server/mcp-server.ts index 20b6bb50..7ef56dd9 100644 --- a/packages/mcp-use/src/server/mcp-server.ts +++ b/packages/mcp-use/src/server/mcp-server.ts @@ -46,7 +46,7 @@ export class McpServer { this.app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*') res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS') - res.header('Access-Control-Allow-Headers', 'Content-Type') + res.header('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, mcp-protocol-version, mcp-session-id, X-Proxy-Token, X-Target-URL') next() }) @@ -296,7 +296,8 @@ export class McpServer { * * Sets up the HTTP transport layer for the MCP server, creating endpoints for * Server-Sent Events (SSE) streaming, POST message handling, and DELETE session cleanup. - * Uses stateless mode for session management, making it suitable for stateless deployments. + * Each request gets its own transport instance to prevent state conflicts between + * concurrent client connections. * * This method is called automatically when the server starts listening and ensures * that MCP clients can communicate with the server over HTTP. @@ -314,30 +315,53 @@ export class McpServer { if (this.mcpMounted) return const { StreamableHTTPServerTransport } = await import('@modelcontextprotocol/sdk/server/streamableHttp.js') - - // Create StreamableHTTPServerTransport in stateless mode - const httpTransport = new StreamableHTTPServerTransport({ - sessionIdGenerator: undefined // Stateless mode - }) - - // Connect the MCP server to the transport - await this.server.connect(httpTransport) const endpoint = '/mcp' + // POST endpoint for messages + // Create a new transport for each request to support multiple concurrent clients + this.app.post(endpoint, express.json(), async (req, res) => { + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + enableJsonResponse: true + }) + + res.on('close', () => { + transport.close() + }) + + await this.server.connect(transport) + await transport.handleRequest(req, res, req.body) + }) + // GET endpoint for SSE streaming this.app.get(endpoint, async (req, res) => { - await httpTransport.handleRequest(req, res) - }) + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + enableJsonResponse: true + }) - // POST endpoint for messages - this.app.post(endpoint, express.json(), async (req, res) => { - await httpTransport.handleRequest(req, res, req.body) + res.on('close', () => { + transport.close() + }) + + await this.server.connect(transport) + await transport.handleRequest(req, res) }) // DELETE endpoint for session cleanup this.app.delete(endpoint, async (req, res) => { - await httpTransport.handleRequest(req, res) + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + enableJsonResponse: true + }) + + res.on('close', () => { + transport.close() + }) + + await this.server.connect(transport) + await transport.handleRequest(req, res) }) this.mcpMounted = true diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 10c81d68..2c82159d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -521,6 +521,9 @@ importers: '@mcp-ui/server': specifier: ^5.11.0 version: 5.11.0(patch_hash=5d24b4104a7d6d537b7de1dc183b27e42d9c1b3fde96a28dbeb722713ba85f11) + '@modelcontextprotocol/sdk': + specifier: ^1.0.0 + version: 1.20.0 cors: specifier: ^2.8.5 version: 2.8.5 @@ -530,6 +533,9 @@ importers: mcp-use: specifier: workspace:* version: link:../packages/mcp-use + zod: + specifier: ^3.22.0 + version: 3.25.76 devDependencies: '@mcp-use/cli': specifier: workspace:*