How Continue Implements the Model Context Protocol (MCP): Architecture and Code Deep Dive
Continue implements the Model Context Protocol through a layered CLI architecture that validates configurations with Zod, abstracts STDIO/SSE/HTTP transports, and manages server lifecycles via the MCPService class to expose tools and prompts to the UI.
Continue.dev integrates the Model Context Protocol (MCP) as a first-class extension in its CLI, enabling language models to discover and invoke external tools at runtime. The implementation spans configuration validation, transport abstraction, and lifecycle management, allowing seamless integration of STDIO, SSE, or HTTP-based MCP servers. All MCP functionality resides in the CLI extension and follows a strict service-oriented architecture centered around the MCPService class.
MCP Configuration Schema and Validation
Every MCP integration begins with configuration validation. Continue defines its MCP server schema using Zod in packages/config-yaml/src/schemas/mcp/index.ts, ensuring type safety for server definitions.
The schema supports three transport types—STDIO, SSE, and Streamable HTTP—each with specific required fields:
- STDIO servers: Require a
commandfield specifying the executable - SSE servers: Require a
urlandtype: "sse" - HTTP servers: Require a
urlandtype: "streamable-http"
Configuration entries also support optional apiKey resolution from secrets and TLS verification controls via requestOptions.verifySsl. When an assistant loads, doInitialize iterates over assistant.mcpServers and validates each entry against this schema before attempting connection.
Transport Layer Abstraction
The transport layer, implemented in extensions/cli/src/services/mcpTransports.ts, decouples connection logic from protocol handling. The getConnectedClient method selects the appropriate transport based on server configuration:
constructStdioTransport: Used when"command" in serverConfig, spawning a child processconstructSseTransport: Used whenserverConfig.type === "sse", establishing Server-Sent Events connectionconstructHttpTransport: Used whenserverConfig.type === "streamable-http", implementing the streamable HTTP specification
Each transport constructor handles authentication headers, including optional Authorization: Bearer <apiKey> tokens, and respects requestOptions.verifySsl for TLS configuration. The transport layer also implements fallback logic that attempts HTTP before SSE when auto-detecting server capabilities.
The MCPService Lifecycle
MCPService, extending BaseService in extensions/cli/src/services/MCPService.ts, orchestrates the entire MCP lifecycle from connection to execution.
Server Connection Initialization
When connectServer is invoked, it constructs a ServerConnection object that tracks state:
const connection: ServerConnection = {
config: serverConfig,
client: null,
prompts: [],
tools: [],
status: "connecting",
warnings: [],
};
The service then instantiates a Client from @modelcontextprotocol/sdk/client with empty capabilities and attempts connection using the selected transport. If authentication fails with a 401 status and the CLI is not running headless, Continue automatically falls back to launching the mcp-remote CLI via STDIO transport, enabling OAuth-based flows such as Supabase MCP integration.
Authentication and Token Refresh
For HTTP and SSE connections, withTokenRefresh wraps all client operations to handle authentication expiration. While the current implementation short-circuits to the operation (with refresh logic performed elsewhere), the wrapper ensures that any 401 response triggers a token refresh before retrying the request. This mechanism is critical for long-running sessions where MCP server credentials may expire.
Capability Discovery
After establishing a connection, MCPService discovers available capabilities through client.getServerCapabilities(). The service conditionally loads resources based on server advertisements:
if (capabilities?.tools) {
connection.tools = await this.withTokenRefresh(serverName, async () => {
const conn = this.connections.get(serverName)!;
return (await conn.client.listTools()).tools;
});
}
Prompt discovery follows an identical pattern using listPrompts(). Any errors during discovery generate warnings that surface in the UI rather than failing silently, ensuring users understand when specific tools are unavailable.
Runtime Tool Execution
The runTool(name, args) method serves as the public entry point for tool invocation. It iterates through all active connections to locate the requested tool by name:
return await conn.client.callTool({ name, arguments: args });
If no matching tool exists across registered servers, the method throws an error. This execution path is consumed by the TUI slash-command picker and other UI components that read from the global MCP state.
Global State Management and UI Integration
updateState() aggregates data from all connections into a serializable MCPServiceState object, stripping non-serializable client instances before publication:
const newState: MCPServiceState = {
mcpService: this,
connections,
tools,
prompts,
};
this.setState(newState);
serviceContainer.set(SERVICE_NAMES.MCP, newState);
UI components access this state through serviceContainer.get(SERVICE_NAMES.MCP), enabling real-time display of available tools and prompts without maintaining direct references to transport clients.
Error Handling and Lifecycle Management
Headless Mode: When running without a UI (e.g., CI environments), any server connection error aborts initialization immediately, ensuring broken MCP configurations fail fast rather than degrading silently.
Secret Resolution: The service detects unresolved secrets.* variables during initialization. In headless mode, these raise exceptions; in interactive mode, they generate UI warnings.
Graceful Shutdown: The shutdownConnections method iterates through all active clients, closing connections and resetting status flags to prevent resource leaks during CLI exit.
Summary
- Configuration Validation: MCP server definitions are validated against Zod schemas in
packages/config-yaml/src/schemas/mcp/index.ts, supporting STDIO, SSE, and HTTP transports - Transport Abstraction:
extensions/cli/src/services/mcpTransports.tsprovidesconstructStdioTransport,constructSseTransport, andconstructHttpTransportfor protocol-specific connection handling - Service Orchestration:
MCPServiceinextensions/cli/src/services/MCPService.tsmanages the full lifecycle, fromconnectServerinitialization torunToolexecution - State Management: Global state is maintained through
MCPServiceStateand exposed viaserviceContainerusingSERVICE_NAMES.MCPfor UI consumption - Authentication:
withTokenRefreshwrappers handle 401 responses, with automatic fallback tomcp-remoteCLI for OAuth flows - Capability Discovery: Dynamic loading of tools and prompts via
listTools()andlistPrompts()after connection establishment
Frequently Asked Questions
How does Continue handle authentication for MCP servers?
Continue supports authentication through the apiKey configuration field, which resolves to Authorization: Bearer headers. The withTokenRefresh method in MCPService.ts wraps client operations to handle 401 responses, and for OAuth-based servers, it automatically falls back to the mcp-remote CLI using STDIO transport when HTTP authentication fails in interactive mode.
What transport protocols does Continue's MCP implementation support?
Continue supports three transport protocols: STDIO for local process spawning, SSE (Server-Sent Events) for persistent HTTP connections, and Streamable HTTP for request-response HTTP communication. The transport selection logic in mcpTransports.ts automatically constructs the appropriate client based on the type field in the server configuration or falls back to auto-detection.
How does Continue discover tools from an MCP server?
After establishing a connection, MCPService calls client.getServerCapabilities() to check for tool support. If advertised, it invokes client.listTools() within a withTokenRefresh wrapper to retrieve the tool definitions. These are stored in the connection's tools array and aggregated into the global MCPServiceState for UI consumption.
Can Continue run MCP servers in CI or headless environments?
Yes, Continue fully supports headless operation. When running without a UI, the CLI validates all secrets at startup and aborts immediately if any MCP server fails to connect, ensuring fast failure rather than partial functionality. The shutdownConnections method ensures clean resource cleanup even in automated environments.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →