18 MCP servers to 1
How antidrift replaced 18 separate MCP connector packages with one ZeroMCP process. 262 tools. Zero runtime dependencies.
The problem
Antidrift ships MCP connectors for 18 services: Gmail, Drive, Calendar, Attio, HubSpot CRM, HubSpot Marketing, Pipedrive, Jira, Linear, ClickUp, Notion, GitHub, AWS, Stripe, Cloudflare, Vercel, Netlify, and a Google bundle.
Each connector was its own npm package. Its own MCP server class. Its own transport setup. Its own dependency tree. Its own process.
If a user connected 5 services, they ran 5 MCP server processes. Each one reimplemented the same boilerplate: JSON-RPC handling, stdio transport, schema conversion, error wrapping. The only thing that changed between packages was the API calls.
# 18 separate npm packages, 18 processes
@antidrift/mcp-gmail
@antidrift/mcp-drive
@antidrift/mcp-calendar
@antidrift/mcp-attio
@antidrift/mcp-hubspot-crm
@antidrift/mcp-hubspot-marketing
@antidrift/mcp-pipedrive
@antidrift/mcp-jira
@antidrift/mcp-linear
@antidrift/mcp-clickup
@antidrift/mcp-notion
@antidrift/mcp-github
@antidrift/mcp-aws
@antidrift/mcp-stripe
@antidrift/mcp-cloudflare
@antidrift/mcp-vercel
@antidrift/mcp-netlify
@antidrift/mcp-google # bundle of gmail + drive + calendar
# Each one:
# - Its own MCP server class
# - Its own transport setup
# - Its own dependency tree
# - Its own process
# - Its own config entry in claude_desktop_config.json What changed
ZeroMCP separates the runtime from the tools. The runtime handles MCP protocol, transport, schema conversion, credential injection, and sandboxing. The tools just make API calls.
Each connector package became a folder of tool files. The Stripe connector isn't a 500-line npm package anymore. It's 3 files in tools/stripe/, each under 30 lines. The tool imports the Stripe SDK. ZeroMCP handles everything else.
# 1 ZeroMCP process, tools loaded as needed
zeromcp serve
# Each connector is now a folder of tool files:
tools/
gmail/
search.js # imports googleapis
send.js
read.js
stripe/
list_customers.js # imports stripe
create_charge.js
github/
list_issues.js # imports @octokit/rest
create_pr.js
jira/
list_tickets.js # imports jira-client
create_issue.js
...
# The runtime has 0 dependencies.
# Each tool imports its own SDK.
# Tools load when called, not at startup. The numbers
| Before (18 packages) | After (ZeroMCP) | |
|---|---|---|
| Processes | 1 per connector (up to 18) | 1 total |
| Runtime dependencies | 17 per package (@modelcontextprotocol/sdk) | 0 |
| Boilerplate per connector | ~200 lines (server class, transport, schema) | 0 (runtime handles it) |
| Tool code per connector | Buried inside boilerplate | Standalone files, 15-30 lines each |
| Config | 1 entry per connector in claude_desktop_config.json | 1 zeromcp.config.json |
| Credential handling | Each package reads env vars differently | ctx.credentials, mapped in one config |
| Total tools | 262 | 262 (same tools, less infrastructure) |
How it works
One config file maps credentials per service directory. Tools load when called, not at startup. A user who only uses Stripe and GitHub never loads the Gmail or Jira tools.
// zeromcp.config.json
{
"tools": "./tools",
"credentials": {
"gmail": {
"file": "~/.config/google/credentials.json"
},
"stripe": {
"env": "STRIPE_SECRET_KEY"
},
"github": {
"env": "GITHUB_TOKEN"
},
"jira": {
"env": "JIRA_API_TOKEN"
}
}
} Each tool is a standalone file. It imports whatever SDK it needs. The runtime provides ctx.credentials and ctx.fetch. That's the entire interface.
// tools/stripe/list_customers.js
import Stripe from 'stripe';
export default {
description: "List Stripe customers",
permissions: {
network: ["api.stripe.com"]
},
input: {
limit: {
type: "number",
optional: true,
description: "Max results"
}
},
execute: async ({ limit = 10 }, ctx) => {
const stripe = new Stripe(ctx.credentials);
const customers = await stripe.customers.list({
limit
});
return customers.data;
},
} What this means for users
- 1 process instead of 18. No zombie accumulation. One
SIGINTstops everything. - Tools load on demand. Connect 18 services, only the ones you call load into memory.
- Runtime and tool dependencies are separated. ZeroMCP has 0 deps. Each tool brings its own SDK. Updating the Stripe SDK doesn't touch the Gmail tools.
- Adding a service is adding a folder. No new npm package. No new MCP server class. Drop files in a directory.
- One config for credentials. Not 18 different env var patterns across 18 packages.