Building a seven-server MCP ecosystem as a developer product, not a code dump
Seven Model Context Protocol servers published on npm, six in Rust and one in TypeScript. The interesting decisions were about credentials, install friction, and tool granularity — not about the protocol.
Context
Through 2025 and 2026 I built and published seven Model Context Protocol servers under the @kembec scope on npm. Six are written in Rust; one, the email server, is TypeScript. They connect MCP-compatible clients — Claude Desktop, Cursor, Codex CLI — to email, calendars, a Peruvian tax registry, LinkedIn, screenshots, and Substack.
This started as personal tooling. It became something closer to a small product line, which is why it is worth writing up as a case rather than a repo list.
Problem
The protocol part of MCP was never the hard problem. Once the specification stabilised, wiring a server was mechanical.
The real friction was credentials. To connect an agent to a real email account or calendar, the common pattern was pasting a long-lived token or password directly into a client config file — a plain-text file, on disk, often synced, that the user would then forget about. Every person I watched try this either did it and felt uneasy, or stopped.
Second problem: install friction. The audience for these tools is developers using AI clients, and their tolerance for a multi-step setup is close to zero. A README with six steps means the tool does not get used.
Third: the tools an agent is offered are a design surface. A server exposing one do_everything tool forces the agent to guess. A server exposing forty tools burns context on discovery and makes the agent worse at choosing.
Product decision
Three decisions shaped the set.
Credentials never live in the agent config. Each server handles authentication itself — OAuth2 flows for Gmail, Outlook, Google Calendar and LinkedIn; app-specific passwords for iCloud — and caches tokens locally, outside the client configuration. The config contains a command name and nothing sensitive. This is the decision I would defend hardest, because it is what makes the tools usable by someone who takes their own security seriously.
One install command, no build step. Global install or npx, then a config block of three or four lines. Nothing else.
Tools mirror user intent, not API endpoints. The email server is the clearest example: list_messages, get_message, send_message, search_messages, plus explicit account management (list_accounts, auth_start, auth_add_icloud, auth_remove). Eight tools, each corresponding to something a person would ask for. Multi-account support is exposed as a first-class concept because the actual use case is “summarise everything across my accounts,” not “query one mailbox.”
Technical decision
Rust for six of seven, TypeScript for email. Rust gave small, dependency-free binaries with fast startup — which matters, because MCP servers are spawned per session and a slow start is felt directly. Email went to TypeScript because the OAuth and IMAP library ecosystem there was more mature, and the maintenance cost of reimplementing that in Rust was not justified by any user-visible gain.
That inconsistency is deliberate. A uniform stack would have been more elegant and slightly worse.
Distribution through npm regardless of language. This is the decision most people question. The Rust servers ship as npm packages because the audience already has Node installed and already knows npx. Publishing to crates.io would have been more idiomatic and would have put a compile step in front of every user. I optimised for where the audience was, not for where the code came from.
stdio transport, with one remote exception. Local stdio servers avoid hosting, avoid an auth boundary, and keep data on the user’s machine. The Peruvian tax lookup is the exception: it queries a public registry, so it also runs as a remote MCP endpoint alongside a REST API, backed by Cloudflare Workers and D1.
Distribution model
- Published on npm under
@kembec, one package per server. - Install with npm or
npx; the binary is fetched, not compiled. - Config snippets provided per client, including Claude Desktop, Cursor and Codex CLI in TOML.
- Licensing is MIT, except iCloud Calendar MCP which is Apache-2.0.
- Each server has its own page on this site with its tool list, configuration, and requirements — because a GitHub README is not a product page and does not get found by someone searching for the capability.
Evidence
Version numbers, monthly npm downloads, stars and forks for all seven servers are fetched live from the npm registry and the GitHub API when this site builds, and shown on the AI section. I deliberately do not restate those numbers in prose: they change, and a number frozen in a case study is a number that will eventually be wrong.
What the code itself evidences: seven maintained packages, two languages, two license models, per-client configuration for three MCP clients, and OAuth2 implementations across four providers.
Constraints
- Solo maintenance. Everything here is built and maintained by one person alongside a full-time CTO role. That caps scope and means response time to issues is best-effort.
- A moving specification. MCP evolved during this period. Some early design choices were made against a version of the spec that no longer looks the same.
- Provider dependency. Gmail, Outlook, iCloud and LinkedIn each control their own APIs and terms. A policy change upstream can break a server through no fault of the code.
- No usage telemetry. By design, these tools do not phone home. The cost is that I have download counts but almost no insight into which tools actually get called, which is a genuine product blind spot.
- Small n on adoption. Download counts on developer tooling are noisy and include CI and mirrors. I treat them as directional, not as demand evidence.
Lessons
The differentiator was a constraint, not a feature. Refusing to accept credentials in the config file shaped the entire architecture, and it is the thing people mention. Strong constraints produce better products than long feature lists — a pattern I keep re-learning.
Install friction is the whole funnel for developer tools. Everything upstream of the first successful call is where the users are lost. Choosing npm over the idiomatic package registry was the highest-leverage decision in the set.
Tool design is API design is product design. The granularity question — how many tools, at what level of abstraction — is the same question as coarse versus fine-grained API design, and it has the same answer: match the consumer’s mental model, not your internal structure.
Consistency is worth less than fit. Mixing Rust and TypeScript across a set of seven is aesthetically unsatisfying and was correct.
I built for one user and got lucky. Every server started as a workflow I lost time on. That is a good filter for whether something is worth building and a bad one for whether anyone else needs it. The honest read is that this set reflects my workflows, and any broader adoption is a bonus rather than a validated bet.
Sources
- Model Context Protocol specificationdocumentation
- Repositories and release historyrepository
- First-hand: I designed, built and maintain all seven serversfirst-hand