Skip to main content
When you want your agent to read files, query databases, call external APIs, or use specialized computation, you have two choices: write a tool() for each operation, or connect to an MCP server. MCP — the Model Context Protocol — is an open standard that lets any MCP-compatible server expose tools to any MCP-compatible client. Connect once and your agent gets access to every tool that server offers, without writing a single tool definition. The practical benefit is ecosystem leverage. There are MCP servers for the filesystem, GitHub, Postgres, Puppeteer, Slack, and hundreds more. Instead of writing a GitHub integration from scratch, you connect to a GitHub MCP server and your agent can search repos, read files, and open issues immediately. Vibes implements the MCP client side in four classes: MCPStdioClient, MCPHttpClient, MCPToolset, and MCPManager. Each has a single job and they compose cleanly.

How the pieces fit together

  • MCPStdioClient — spawns a local subprocess and speaks MCP over stdin/stdout
  • MCPHttpClient — connects to a remote MCP server over HTTP with streaming
  • MCPToolset — wraps one client and bridges it to the Toolset interface agents expect
  • MCPManager — manages multiple toolsets, connects them all at once, merges their tool lists
Each layer adds one thing. You can use them individually or let MCPManager handle the whole stack.

Connecting to a local server with MCPStdioClient

The most common pattern is connecting to a local MCP server that runs as a subprocess. The filesystem MCP server is a good first example: you point it at a directory and your agent can list, read, and write files.
(1) MCPStdioConfig takes command, optional args, and optional env. The filesystem server accepts a path as its last argument — tools from that server will be scoped to that directory.
(2) connect() spawns the subprocess and completes the MCP initialization handshake. Always await it before doing anything else.
(3) MCPToolset discovers available tools lazily on first use and caches them for 60 seconds by default. You never call listTools() manually — the toolset handles that inside the agent’s turn loop.
(4) Pass toolsets not tools. The toolset interface allows dynamic per-turn tool discovery with caching, which raw tools arrays don’t support.
(5) disconnect() terminates the subprocess. Without this, the process keeps running after your code exits. Always call it in a finally block.
Always call connect() before using a client, and always call disconnect() in a finally block. Forgetting connect() causes all tool calls to throw immediately. Forgetting disconnect() leaks subprocess resources.

MCPStdioClient options

MCPStdioClient accepts a config and an optional options object:
The env field merges additional variables into the subprocess environment — it does not replace the inherited environment.

Connecting to a remote server with MCPHttpClient

MCPHttpClient connects to an MCP server running over HTTP with Server-Sent Events. The API is identical to MCPStdioClient — you swap the config shape and the transport handles the rest.
(1) MCPHttpConfig takes a url and optional headers.
(2) Authentication headers go here — Authorization, API keys, session tokens. Headers are sent with every request to the server.
MCPHttpClient uses the MCP Streamable HTTP transport, which supports both standard HTTP/2 and SSE-based streaming responses.

MCPToolset — controlling caching and instructions

MCPToolset is the bridge between a raw MCPClient and the Toolset interface. Its two configuration options control performance and agent context:
(1) The server’s tool list doesn’t change often, so the toolset caches it. For servers whose tools change frequently (dynamic tool registration), lower the TTL or call toolset.invalidateCache() to force a re-fetch on the next turn.
(2) Some MCP servers provide an instruction text during initialization — guidance the server author wrote for the AI agent consuming it. When instructions: true (the default), this text is available via toolset.getServerInstructions() and can be appended to your system prompt if desired.

MCPManager — multiple servers at once

Most real applications need more than one MCP server. You might want the filesystem server for reading local files and a remote search server for web queries simultaneously. MCPManager handles this: register servers, call connect() once, and the manager connects all of them in parallel and merges their tool lists.
(1) new MCPManager() takes no arguments.
(2) addServer() returns this so you can chain calls. The second argument takes any MCPToolsetOptions plus an optional name.
(3) Naming a server matters when servers provide instruction text — named servers prefix their instructions with [name] in the aggregated output.
(4) manager.connect() calls connect() on every registered client in parallel. One call to connect everything.
(5) MCPManager implements the Toolset interface directly. Pass it to toolsets the same way you’d pass a MCPToolset.
(6) manager.disconnect() disconnects all servers in parallel. If any fail to disconnect, all failures are collected and thrown as a single AggregateError — so you see all problems, not just the first.
Do NOT use new MCPManager(client) — the constructor takes no arguments. Register clients with addServer() after construction.Do NOT call manager.connectAll() — the method is manager.connect().Do NOT wrap the manager in another MCPToolset — pass manager directly to toolsets. The manager is already a Toolset.

Handling tool name collisions

If two servers expose a tool with the same name, the last registered server wins. Control this by ordering addServer() calls from lowest to highest priority, or prefix server names when registering:

Declarative config with MCPConfig

For production deployments, store your MCP server list in a JSON config file and load it at startup. createManagerFromConfig reads the file, creates clients, connects them, and returns a ready MCPManager:
(1) createManagerFromConfig is a convenience wrapper around loadMCPConfig + createClientsFromConfig + MCPManager. The returned manager is already connected.

Config file format

Two JSON formats are supported. The simpler array format:
The Claude Desktop format (if you want to share config with Claude Desktop):
String values in the config support ${ENV_VAR} interpolation. Variables are read from Deno.env at load time. If a referenced variable is not set, loadMCPConfig throws immediately with a clear error message — your app fails fast at startup rather than silently at tool call time.

Lower-level config loading

If you need more control over the setup process, use loadMCPConfig and createClientsFromConfig individually:

Connection lifecycle

Connecting and disconnecting correctly is the most error-prone part of MCP integration. The pattern is always the same:
For long-running applications (HTTP servers, CLI tools that stay alive), connect at startup and disconnect in a shutdown handler:

MCP Server

Expose your Vibes agent as an MCP server

Toolsets

Compose and conditionally expose groups of tools