MCP
Connect Model Context Protocol servers and expose their tools to agents.
@drover/mcp wraps @modelcontextprotocol/sdk with drover’s tool
contract. Configure servers, build a runtime, drover prefixes every
tool so cross-server collisions are impossible.
Config
import type { McpServerConfig } from "@drover/mcp";
const configs: McpServerConfig[] = [
{
id: "fixture",
transport: "stdio",
command: "bun",
args: ["./mcp-servers/fixture/server.ts"],
},
{
id: "linear",
transport: "http",
url: "https://mcp.linear.app",
headers: { Authorization: `Bearer ${process.env.LINEAR_TOKEN}` },
},
];The discriminated union: stdio spawns a child process; http connects
via streamable HTTP (also handles SSE).
Boot the runtime
import { createMcpRuntime } from "@drover/mcp";
const mcpRuntime = await createMcpRuntime(configs);
console.log(mcpRuntime.servers());
// [{ id: "fixture", transport: "stdio", toolCount: 2 }, ...]Each server connects in parallel and lists its tools. Failed connects are isolated — one bad server doesn’t block the others, it just contributes 0 tools.
Declare in a spec
const spec = defineAgent({
/* ... */
mcpServers: ["fixture", "linear"],
});spec.mcpServers is the allowlist for this agent — tools only show up
from servers on the list.
Wire on the run
runAgent(spec, input, { mcpRuntime });Tools come through prefixed: a server named fixture exposing a tool
named compute shows up to the model as fixture__compute. Prefixing
is opaque to the model — it sees the description and decides — but
guarantees no collision when two servers both ship a query tool.
Tool schemas
MCP tools advertise JSON Schema. drover wraps via Type.Unsafe(jsonSchema)
— the schema is preserved verbatim so the model sees the right arg
shape; validation is the MCP server’s responsibility.
OAuth (planned)
For OAuth-protected servers, drover’s MCP runtime accepts headers up
front but doesn’t yet auto-refresh expired tokens. Workaround: handle
token refresh in your headers factory (re-create the runtime on
expiry, or use a header-emitting closure once McpServerConfig accepts
one).
Full OAuth refresh will migrate into @drover/mcp once the runtime
layer gets HTTP control-plane support.
Lifetime
Connect once at app start, reuse across runs. Shutdown when the process exits:
process.on("SIGTERM", async () => {
await mcpRuntime.close();
});close() is best-effort — it tears down transports and clients but
doesn’t fail if some are already gone.