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/stdoutMCPHttpClient— connects to a remote MCP server over HTTP with streamingMCPToolset— wraps one client and bridges it to theToolsetinterface agents expectMCPManager— manages multiple toolsets, connects them all at once, merges their tool lists
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.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.
MCPStdioClient options
MCPStdioClient accepts a config and an optional options object:
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.
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:
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.
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.
Handling tool name collisions
If two servers expose a tool with the same name, the last registered server wins. Control this by orderingaddServer() 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:
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: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, useloadMCPConfig and createClientsFromConfig individually:
Connection lifecycle
Connecting and disconnecting correctly is the most error-prone part of MCP integration. The pattern is always the same:MCP Server
Expose your Vibes agent as an MCP server
Toolsets
Compose and conditionally expose groups of tools