Skip to main content

Overview

MCPClientManager is the central orchestration class for managing multiple MCP (Model Context Protocol) server connections within MCPJam Inspector. It provides a high-level abstraction over the @modelcontextprotocol/sdk Client class, handling connection lifecycle, transport selection, and unified access to MCP capabilities across multiple servers. Location: sdk/src/mcp-client-manager/index.ts Key Responsibilities:
  • Managing multiple MCP server connections with unique identifiers
  • Auto-detecting and configuring appropriate transports (STDIO, SSE, Streamable HTTP)
  • Providing unified APIs for tools, resources, prompts, and elicitations
  • Handling connection state, reconnection, and error recovery
  • Integrating with AI frameworks (Vercel AI SDK)
  • Supporting elicitation (interactive prompts from MCP servers)

Architecture

Class Structure

Server Configuration Types

The manager supports two transport types, automatically selected based on configuration:

STDIO Configuration

HTTP/SSE Configuration

Base Configuration

Core Concepts

1. Connection Lifecycle

The manager maintains three connection states:
  • disconnected: No client exists, no connection attempt in progress
  • connecting: Connection attempt in progress (tracked via state.promise)
  • connected: Client successfully connected and ready
State transitions:
Connection Status Verification: The manager verifies connection status by attempting a ping to the server each time getConnectionStatus() is called. This ensures the status reflects the actual server availability rather than cached state.

2. Transport Selection

The manager automatically selects the appropriate transport:
  1. STDIO Transport: Used when config has command property
    • Spawns subprocess with StdioClientTransport
    • Manages stdin/stdout/stderr streams
    • Includes default environment variables
  2. HTTP Transport: Used when config has url property
    • Streamable HTTP (default): Bidirectional streaming over HTTP
    • SSE (fallback): Server-Sent Events for unidirectional streaming
    • Auto-fallback: Tries Streamable HTTP first, falls back to SSE on failure
    • Force SSE: Set preferSSE: true or use URL ending in /sse

3. Tool Metadata Caching

The manager caches tool _meta fields for OpenAI Apps SDK compatibility:
Access via getAllToolsMetadata(serverId) to get all tool metadata for a server.

4. Elicitation Support

Elicitation allows MCP servers to request interactive input during tool execution: Two modes:
  1. Server-specific handler: Set per-server via setElicitationHandler(serverId, handler)
  2. Global callback: Set globally via setElicitationCallback(callback)
Pending elicitation pattern (used in chat endpoint):

5. JSON-RPC Logging

RPC logging can be enabled at three levels:
  1. Global default: new MCPClientManager({}, { defaultLogJsonRpc: true })
  2. Global custom logger: new MCPClientManager({}, { rpcLogger: (event) => {...} })
  3. Per-server: { serverId: { ..., logJsonRpc: true } } or rpcLogger: (event) => {...}
The manager wraps transports with a LoggingTransport that intercepts all JSON-RPC messages.

Usage Patterns in MCPJam Inspector

1. App Initialization

2. Chat Endpoint

3. HTTP Bridge

4. Managing Server Connections

API Reference

Connection Management

(serverId: string, config: MCPServerConfig) => Promise<Client>
Connect to an MCP server. Throws if server ID already exists. Returns the connected Client instance.
(serverId: string) => Promise<void>
Disconnect from a server and clean up resources.
() => Promise<void>
Disconnect from all servers and reset state.
(serverId: string) => void
Remove server from state without attempting disconnection.
() => string[]
Get array of all server IDs.
(serverId: string) => boolean
Check if a server is registered.
() => ServerSummary[]
Get status and config for all servers.
(serverId: string) => MCPConnectionStatus
Get connection status by attempting a ping to verify server availability: "connected" | "connecting" | "disconnected". This method actively checks the connection rather than relying on cached state.
(serverId: string) => MCPServerConfig | undefined
Get configuration for a server.

Tools

(serverId: string, params?, options?) => Promise<ListToolsResult>
List tools for a single server. Caches metadata. Returns empty list if unsupported.
(serverIds?: string[]) => Promise<ListToolsResult>
Get tools from multiple servers (or all if not specified). Returns flattened list.
(serverId: string, toolName: string, args: Record<string, unknown>, options?) => Promise<CallToolResult>
Execute a tool on a specific server.
(serverIds?: string[] | string, options?) => Promise<ToolSet>
Get tools in Vercel AI SDK format. Automatically wires up tool execution. Each tool has _serverId metadata attached. App-only tools (_meta.ui.visibility = ["app"]) are excluded by default per SEP-1865.
Options:
ToolSchemaOverrides | 'automatic'
  • schemas?: ToolSchemaOverrides | "automatic" - Control schema conversion
boolean
  • includeAppOnly?: boolean - When true, includes SEP-1865 app-only tools (_meta.ui.visibility = ["app"]) in the returned tool set. Defaults to false. Use only when intentionally mirroring a host that does not implement visibility filtering.
(serverId: string) => Record<string, Record<string, any>>
Get all tool _meta fields for OpenAI Apps SDK.
(serverId: string, options?) => void
Send ping to server.

Resources

(serverId: string, params?, options?) => Promise<ResourceListResult>
List available resources. Returns empty if unsupported.
(serverId: string, params: { uri: string }, options?) => Promise<ReadResourceResult>
Read a resource by URI.
(serverId: string, params: { uri: string }, options?) => Promise<void>
Subscribe to resource updates.
(serverId: string, params: { uri: string }, options?) => Promise<void>
Unsubscribe from resource updates.
(serverId: string, params?, options?) => Promise<ResourceTemplateListResult>
List resource templates.

Prompts

(serverId: string, params?, options?) => Promise<PromptListResult>
List available prompts. Returns empty if unsupported.
(serverId: string, params: { name: string, arguments?: Record<string, string> }, options?) => Promise<GetPromptResult>
Get a prompt with optional arguments.

Notifications

(serverId: string, schema: NotificationSchema, handler: NotificationHandler) => void
Add a notification handler for a server.
(serverId: string, handler: NotificationHandler) => void
Handle resources/list_changed notifications.
(serverId: string, handler: NotificationHandler) => void
Handle resources/updated notifications.
(serverId: string, handler: NotificationHandler) => void
Handle prompts/list_changed notifications.

Elicitation

(serverId: string, handler: ElicitationHandler) => void
Set server-specific elicitation handler.
(serverId: string) => void
Remove server-specific handler.
(callback: (request: {...}) => Promise<ElicitResult>) => void
Set global elicitation callback (used if no server-specific handler).
() => void
Remove global callback.
() => Map<string, { resolve, reject }>
Get map of pending elicitation promise resolvers.
(requestId: string, response: ElicitResult) => boolean
Resolve a pending elicitation. Returns true if found.

Advanced

(serverId: string) => Client | undefined
Get raw MCP SDK Client instance for advanced usage.
(serverId: string) => string | undefined
Get session ID for Streamable HTTP servers.

Examples

Example 1: Basic Setup with Multiple Servers

Example 2: Vercel AI SDK Integration

Example 3: Dynamic Server Management

Example 4: Resource Subscriptions

Example 5: Custom RPC Logging

Best Practices

Connection Management

DO:
  • Use unique, descriptive server IDs
  • Check connection status with ping verification before operations
  • Handle connection errors gracefully
  • Clean up connections when no longer needed
DON’T:
  • Reuse server IDs without disconnecting first
  • Assume connections are always ready without verification
  • Leave connections open indefinitely
  • Ignore connection state changes

Tool Execution

DO:
  • Validate tool arguments before execution
  • Set appropriate timeouts for long-running tools
  • Handle tool errors with meaningful messages
  • Use getToolsForAiSdk for AI framework integration
DON’T:
  • Execute tools without checking server status
  • Use hardcoded tool names without verification
  • Ignore tool execution errors
  • Mix manual tool calling with AI SDK integration

Elicitation Handling

DO:
  • Set timeouts for elicitation responses
  • Clean up pending elicitations on errors
  • Use server-specific handlers for custom logic
  • Clear callbacks when done to prevent leaks
DON’T:
  • Leave elicitations pending indefinitely
  • Forget to respond to elicitation requests
  • Mix server-specific and global handlers unexpectedly
  • Ignore elicitation errors

Performance

DO:
  • Cache tool metadata when possible
  • Reuse connections across requests
  • Use parallel operations with getTools()
  • Monitor connection health
DON’T:
  • Create new connections for each request
  • Poll for updates without subscriptions
  • Ignore connection pool limits
  • Skip cleanup on shutdown

Troubleshooting

Cause: Attempting to connect with a server ID that’s already in use.Solution: Disconnect first or use a different ID.
Cause: Attempting operations on a disconnected server.Solution: Check getConnectionStatus() and connect if needed.
Cause: Server doesn’t support the requested capability.Solution: The manager returns empty results for unsupported methods (tools/list, resources/list, prompts/list).
Cause: Network issues, server not running, or incorrect configuration.Solution:
  • Verify server is accessible
  • Check configuration (URL, command, args)
  • Review server logs for errors
  • Use RPC logging to debug protocol issues

See Also

  • sdk/mcp-client-manager/README.md - Public-facing documentation
  • sdk/mcp-client-manager/goal.md - Original design goals
  • sdk/mcp-client-manager/tool-converters.ts - AI SDK conversion logic
  • server/routes/mcp/chat.ts - Chat endpoint usage
  • server/services/mcp-http-bridge.ts - HTTP bridge implementation