# How Continue Implements the Model Context Protocol (MCP): Architecture and Code Deep Dive

> Explore how Continue implements the Model Context Protocol with a deep dive into its layered CLI architecture, Zod validation, transport abstraction, and MCPService for UI tool and prompt exposure.

- Repository: [Continue/continue](https://github.com/continuedev/continue)
- Tags: architecture
- Published: 2026-06-24

---

**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`](https://github.com/continuedev/continue/blob/main/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 `command` field specifying the executable
- **SSE servers**: Require a `url` and `type: "sse"`
- **HTTP servers**: Require a `url` and `type: "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`](https://github.com/continuedev/continue/blob/main/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 process
- **`constructSseTransport`**: Used when `serverConfig.type === "sse"`, establishing Server-Sent Events connection
- **`constructHttpTransport`**: Used when `serverConfig.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`](https://github.com/continuedev/continue/blob/main/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:

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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`](https://github.com/continuedev/continue/blob/main/packages/config-yaml/src/schemas/mcp/index.ts), supporting STDIO, SSE, and HTTP transports
- **Transport Abstraction**: [`extensions/cli/src/services/mcpTransports.ts`](https://github.com/continuedev/continue/blob/main/extensions/cli/src/services/mcpTransports.ts) provides `constructStdioTransport`, `constructSseTransport`, and `constructHttpTransport` for protocol-specific connection handling
- **Service Orchestration**: `MCPService` in [`extensions/cli/src/services/MCPService.ts`](https://github.com/continuedev/continue/blob/main/extensions/cli/src/services/MCPService.ts) manages the full lifecycle, from `connectServer` initialization to `runTool` execution
- **State Management**: Global state is maintained through `MCPServiceState` and exposed via `serviceContainer` using `SERVICE_NAMES.MCP` for UI consumption
- **Authentication**: `withTokenRefresh` wrappers handle 401 responses, with automatic fallback to `mcp-remote` CLI for OAuth flows
- **Capability Discovery**: Dynamic loading of tools and prompts via `listTools()` and `listPrompts()` 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`](https://github.com/continuedev/continue/blob/main/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`](https://github.com/continuedev/continue/blob/main/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.