Skip to content
OrchestriAI
Back to blog
11 min read

What building seven MCP servers taught me about useful tool design

Seven public MCP codebases exposed the same engineering lesson: protocol compliance is the beginning. Clear tools, narrow permissions, honest errors, and operable deployments determine whether a server is useful.

An MCP server can be technically valid and still be frustrating to use. It may connect, answer tools/list, and pass schema validation, yet give an AI application a confusing catalog, ambiguous errors, or more authority than the task needs.

That gap became clear while building seven public MCP projects across search, code retrieval, advertising, sales intelligence, cloud AI, and data tooling. The source is inspectable: Vertex AI MCP Server, Google AI Search MCP, CodeVault, Google Ads MCP, xAI MCP Server, Apollo.io MCP Server, and Search, Scrape, Supabase and RAGDocs MCP.

They are not seven copies of one template. Some run as local processes over standard input and output. One has local and HTTP deployment paths. Some wrap one provider; others combine several domains. CodeVault adds MCP access to a larger indexing and retrieval application. Those differences are the useful part of the evidence.

1. Start with the job, not the upstream API

The easiest way to design a poor MCP server is to mirror every API endpoint. Provider APIs are organized around the provider's product model. A useful tool catalog should be organized around what a person or agent is trying to accomplish.

A raw advertising API might expose resources for campaigns, ad groups, criteria, reports, and mutations. The user thinks in tasks: find keyword opportunities, inspect campaign performance, or add an ad group. A code search system has storage, parsers, embeddings, ranking, and indexes underneath; the useful interface is search this codebase or answer this question with retrieved code.

Before writing a handler, I now write the intended request in ordinary language. If several requests map cleanly to one operation with one permission level and one failure model, they probably belong together. If a tool tries to search, decide, mutate, and publish in one call, it is probably hiding too much.

This also prevents tool catalogs from becoming a token-heavy menu of nearly identical operations. MCP's server concepts documentation defines tools as schema-described operations that models can call. The protocol makes discovery possible; it does not decide which boundaries make sense for your product.

2. Tool names and descriptions are part of runtime behavior

An SDK type-checker cannot tell you whether a model will choose search_code over ask_codebase, or whether “update record” makes the consequences clear. Names and descriptions influence selection, so they deserve the same review as function signatures.

Good descriptions answer four practical questions: what the tool does, when to use it, what it can change, and what important constraint applies. They should not contain sales copy or try to override the host's instructions. A description is guidance to the model, not an authorization policy.

The current MCP schema also supports tool annotations such as readOnlyHint, destructiveHint, idempotentHint, and openWorldHint. The schema reference is explicit that these are hints and may be untrustworthy. Set them accurately for capable clients, but enforce the real rule in server code.

3. Schemas should reject ambiguity before a provider sees it

Across these projects, runtime schemas do more than satisfy the protocol. They convert loosely formed model arguments into a small, testable contract before an external API, filesystem, or database receives them.

Useful constraints include enums for closed choices, bounds for page sizes and timeouts, required identifiers for mutations, and formats for dates or account IDs. Cross-field rules matter too. “End date must follow start date” cannot be expressed by marking both strings required. Neither can “exactly one of URL or raw content.”

Validation should also happen at the response boundary. A provider can return a partial object, a new enum value, or an HTML error page where JSON was expected. Treating upstream data as trusted merely moves the validation gap from one side of the server to the other.

The protocol uses JSON Schema for tool inputs and can describe structured outputs. That gives clients a common vocabulary, as documented in the MCP specification overview. Application code still has to parse, normalize, and constrain the actual values.

4. Read and write operations need different shapes

Search and retrieval can often be retried safely. Creating a campaign, applying a database migration, editing a file, or starting a sequence cannot be treated the same way.

For consequential writes, a stronger design separates preparation from execution. One tool can return a proposed change or preview; another can apply an explicitly identified change. The execution path can require a narrow permission, an idempotency key where the downstream service supports one, or a fresh confirmation value tied to the exact payload.

This is an implementation control, not something MCP supplies automatically. The protocol can carry a call and expose descriptive hints. The server decides whether the caller is allowed to perform that exact operation, and the host decides whether to ask the person for approval.

5. Local transport changes the operations, not the need for boundaries

Several of the seven servers use stdio, which is a good fit for a single-user tool launched by an AI desktop application or coding client. One practical lesson is mundane but important: protocol messages use standard output, so diagnostics belong on standard error. A stray debug line on standard output can corrupt the message stream.

Local execution also means the server inherits a real process environment. Its working directory, environment variables, filesystem permissions, executable path, and installed runtime all become part of the product. A path restriction must be enforced after canonicalization, not by checking whether an untrusted string happens to start with an allowed prefix.

Local does not mean isolated. The MCP security guidance notes that a local server runs with the client's privileges unless an operating-system sandbox or other restriction says otherwise. The local versus remote MCP guide covers that deployment decision in detail.

6. Remote deployment creates a second product surface

Putting a server behind HTTP is not a transport toggle. It adds identity, authorization, sessions, origins, TLS, rate limits, request sizing, deployment topology, and multi-user isolation.

The current protocol defines Streamable HTTP alongside stdio. Its transport specification requires Origin validation for HTTP implementations, recommends binding local HTTP servers to loopback, and describes protocol-version and session handling. The authorization specification applies to HTTP deployments; stdio deployments are expected to obtain credentials from their environment instead.

An HTTP route that accepts valid JSON-RPC but cannot separate two users' data is not production-ready. A session ID is routing state, not proof of identity. A reverse proxy that supplies authentication still needs an explicit, verified identity contract with the application behind it.

7. Errors are part of the tool contract

“Tool failed” is not enough. The caller needs to know whether the input was invalid, credentials were absent, permission was denied, a provider rate limit was reached, or an upstream dependency timed out.

The message returned to the model should be concise and actionable. The operator log can contain a correlation ID, duration, provider status, and stack trace. Neither should contain access tokens, raw credentials, or sensitive payloads by default.

Retries belong only around failures that may be transient, with a limit, backoff, and awareness of whether the operation is safe to repeat. Retrying a read after a timeout is different from retrying a create call when the provider may have completed the first request but lost the response.

8. Broad servers need modular registration and capability checks

The combined search, scraping, database, and document project made a common scaling problem visible: one server may have tools that depend on different credentials and services. A clean registration layer keeps each domain's schema and handler together, while capability checks explain which portions are configured.

Starting the process even when an optional provider is missing can be reasonable. Silently listing tools that can never work is less helpful. Depending on the client and SDK generation, the server can omit unavailable tools, expose clear configuration status, or return a precise missing-capability error. The choice should be intentional and documented.

9. Compatibility has to be tested at the protocol boundary

These repositories span different generations of the TypeScript SDK. That is normal for public software, and it is why “the project builds” is not a complete MCP test.

The useful checks happen through a client or inspector: initialize, negotiate a protocol version, list capabilities, call representative tools, send invalid arguments, cancel or time out work, and disconnect. For HTTP, add malformed headers, unauthorized callers, expired sessions, concurrent users, and deployment restarts. The MCP Inspector helps exercise the protocol surface, but provider fakes and end-to-end tests are still needed for application behavior.

10. The smallest useful server is easier to trust

The strongest recurring lesson is restraint. Every tool adds schema, permissions, error cases, provider cost, context exposure, and maintenance. Combining services can be valuable when a workflow genuinely crosses them. It can also create one process holding credentials for unrelated systems.

I now treat tool inclusion as an authority decision: does this operation need to exist, who needs it, what data can it see, what can it change, and how will an operator know what happened? That review usually removes tools or splits a broad operation into safer pieces.

If you are planning a server, begin with a narrow workflow and two or three representative calls. Prove the schemas and permission boundaries through a real client before expanding the catalog. Our open-source projects show several implementation shapes, and the custom MCP server development service explains how that work is scoped for a business system.

Shariq Riaz

Shariq Riaz

AI Automation Engineer · CPHIMS · PMP · CBAP

11 years in enterprise IT at Fortune 500 companies. Now I build custom AI automations for healthcare, real estate, financial services, and freight forwarding teams.

Explore related solutions

Questions about this? Book a free call and ask directly.

Book a call