MCP Server Development

MCP server development, where the hard part is tool design rather than the protocol.

You’ve already decided to expose your product to AI agents over the Model Context Protocol. The protocol itself is a weekend of work. Servers fail on three other things. The tool surface gets generated from a route table, the transport was superseded in March 2025, and the authorisation model quietly hands every agent your service key.

If you’re not yet sure an agent belongs in the product, Our AI integration and consulting engagement covers the use-case and evaluation work this page assumes is done.

Six things that end up in your repository.

Not a server that demos well in Inspector and then falls over the first time an agent runs a forty-step loop against it.

A tool inventory, argued before a line is written

Twelve to twenty tools named for the task a person is finishing, each with a hand-written description that says when not to call it. Not one tool per REST route.

Streamable HTTP, with the session decision on paper

One endpoint taking POST and GET, answering with JSON or an event stream per call. Stateless across replicas, or sessioned with resumability, and you pick which rather than defaulting into one.

OAuth 2.1 with the server as a resource server

Authorisation code with PKCE, audience-bound tokens rejected when they weren’t minted for you, protected resource metadata advertised from the challenge on a 401.

Typed output and errors written to be read by a model

Output schemas, structured results, responses trimmed to the fields the task needs, cursors, hard caps, and failures returned as tool errors a model can recover from.

A read and write boundary the token actually enforces

Separate scopes, idempotency keys, arguments a model cannot fabricate from retrieved text, rate limits on the agent rather than the human, and an audit row per call.

A compatibility matrix and a handover runbook

Exercised through MCP Inspector and against the clients you intend to support, with deployment, secret rotation and the next specification revision written down.

Engineering position

Five positions the rest of this search won’t take.

Build Streamable HTTP. The two-endpoint SSE transport is legacy.

The 2024-11-05 revision defined a remote transport with two endpoints. You held a long-lived GET stream open and posted to a separate URL handed back in the first event. The 2025-03-26 revision replaced it. Streamable HTTP is one endpoint that accepts POST and answers with a JSON body or an event stream depending on the call, with GET reserved for server-initiated messages. Pages competing for this search still list both transports without saying which to build, and that’s how you inherit session affinity you never needed. Build the current transport, adapt to the legacy one only for a client you have to support, and honour the protocol version header sent after the handshake.

Most MCP servers are bad because a generator wrote them

Point a converter at an OpenAPI file and you get one tool per endpoint. Every name, description and JSON Schema is serialised into the model context on every request, before the user types a word, so a hundred generated tools spend thousands of tokens on preamble and then hand the model get_customer, list_customers, search_customers and customers_by_email to disambiguate between. Selection accuracy falls as the surface grows, and that’s the failure mode, not latency. Shape the tools around tasks instead. One takes an email or an id, one runs the three-call sequence a support agent runs by hand, and the arguments are enums rather than free text. Actions belong in tools because the model picks those, application-selected context belongs in resources, and user-invoked workflows belong in prompts.

Responses repeat the same mistake one layer down. A tool result never leaves the conversation, so a 200-field record gets re-billed on every later turn while its irrelevant fields compete for attention. Return the twelve fields the task needs, cap the count server-side rather than trusting a limit the model chose, and declare an output schema so the client receives typed structured content. Errors get even less care. A JSON-RPC protocol error ends the call and the model rarely learns why, whereas the same failure returned as a tool result flagged as an error, saying what to try instead, gets corrected on the next turn.

The server is a resource server, and must never be a confused deputy

From the 2025-06-18 revision the rules are explicit. Authorisation code with PKCE, no implicit grant, no password grant. The server publishes protected resource metadata, points at it from the challenge header on an unauthorised response, and checks the resource indicator so a token minted for your server cannot be replayed at somebody else’s. Two further rules decide whether an engagement is buildable at all. Reject any token whose audience is not you, and never forward the caller’s token upstream. Hold one broad upstream credential, act on whoever asks, and you’re the textbook confused deputy. The agent inherits what the server can do, not what the user may do. Scope per user and, separately, per agent, because a loop issues in ninety seconds what a person issues in a day.

Prompt injection is a write-path problem, and annotations are only hints

Everything a read tool returns lands in the model context. A support ticket, a scraped page, a field a customer typed. If a mutating tool is one turn away, that text becomes an instruction channel into your system. The annotations (read-only, destructive, idempotent, open-world) are advisory metadata for the client’s consent interface, and the specification says hints from an untrusted server must not be relied on. They document intent, they don’t enforce it. The token and the schema enforce it: read and write scopes issued separately, mutating tools requiring an identifier the model cannot invent from retrieved text, idempotency keys so a retried agent does not double-charge, and, where the blast radius is real, write tools in a second server the client mounts on purpose.

stdio is a development transport and a single-user deployment

A stdio server is a subprocess the client spawns. One process per session, credentials inherited from whoever launched it, and no authorisation story at all. That’s right for a developer tool on a laptop and wrong for a product with customers, because you can’t put a subprocess behind a load balancer, can’t share a connection pool, and pay a cold start on every session. The gotcha that costs an afternoon is that stdout is the transport, so one stray console log corrupts the message stream. Log to stderr. Going remote, decide statelessness first. A server that issues no session id runs on any number of replicas behind round-robin, while one that issues a session id needs sticky routing or a shared event store before stream resumption works. Serving HTTP locally, validate the origin header and bind to loopback, or a browser page reaches your server by DNS rebinding.

The arithmetic behind tool schemas and response size is the same arithmetic as in what an AI feature actually costs to run per user. The storage, policies and vector index underneath it all are Supabase and Postgres work.

When not to build one

Five projects that don’t need an MCP server.

The first two are usually an n8n workflow with a webhook trigger, and the third is answered by choosing retrieval over fine-tuning and building it directly.

One deterministic integration between two systems you own

If the sequence never varies, a model choosing tools adds cost, latency and a new failure mode. A webhook-triggered workflow costs less and won’t pick the wrong branch.

The consumer is a human interface, not an agent

MCP exists so a model can discover and call your capabilities. If people are clicking buttons, you want a REST or GraphQL API and a UI. A protocol layer buys you nothing.

The task is search over a corpus, not action on a system

Wrapping a document store in tools so a model calls it repeatedly is a slower, pricier retrieval pipeline. Build retrieval properly and put one tool over it, if any.

Your API has no per-user authorisation yet

If access is one shared service key, an MCP server hands every agent full-tenant reach. Authorisation comes first, because the protocol cannot invent a permission model you never had.

Nobody can name the five tasks the agent should perform

Without them you’ll generate a wrapper from your route table and ship the server this page argues against. Spend the sprint on the tool surface instead.

How it runs

Four commitments, and the variables behind the estimate.

Your repo and your cloud from commit one

Server, schemas, tests and Inspector fixtures are files in your repository from commit one, not a zip at the end.

A staging URL by the end of week one

A reachable endpoint you can point a real client at, so tool naming is argued against real model behaviour. A build and a short note every Friday.

Decisions in writing

Every tool that exists, every tool we rejected, the session and scope model, and what each mutating tool may touch, recorded with the reasoning.

Fixed scope, quoted before kickoff

Change requests are priced and confirmed in writing before work starts. The specification revises, and a revision is quoted rather than quietly absorbed.

We quote fixed scope against a published rate card rather than selling hours. A readiness sprint that settles the tool surface before anything is built starts at $6,000 and is credited against the build that follows. A pilot server against one system starts at $7,500, a proof of concept at $16,000, and a production workflow with authorisation, scoping and audit logging at $12,000. The minimum for a build is $7,500, and a retainer starts at $2,500 a month. Those published figures are estimates in US dollars, not quotations; the quote that follows scoping is fixed in writing before kickoff.

That’s the difference between this page and the rest of the search, where the published options are an hourly rate or a fixed floor around fifteen thousand dollars. What moves our number is how many systems sit behind the tools, whether authorisation already exists or has to be designed, and whether any tool is allowed to write. Send the five tasks you want an agent to finish and you’ll get the tool surface back before you get a price.

FAQ

The questions that decide it

Streamable HTTP. The two-endpoint HTTP+SSE transport from the 2024-11-05 revision was superseded in 2025-03-26, and it’s only a compatibility path for older clients now. Build one endpoint that accepts POST and answers with either JSON or an event stream, add a legacy adapter only if a client you have to support needs one, and honour the protocol version header sent after initialize.

Because one tool per REST endpoint is the wrong shape. Every tool name, description and JSON Schema is serialised into the model’s context on every request, so a hundred generated tools burn thousands of tokens before anyone types a word, and near-identical names make the model pick wrong. Twelve to twenty task-shaped tools with tight enums beat a generated wrapper of any size.

No, and the specification says so directly. A server holding one broad upstream credential and acting on any caller’s request is a confused deputy. The agent gets everything the server can do, rather than what the user is allowed to do. The server must reject tokens whose audience is not itself, must not forward a caller’s token upstream, and should scope per user and per agent.

Not the tool annotations. The read-only and destructive hints are advisory metadata for the client’s consent interface, and they don’t stop anything on their own. What holds is the token: separate read and write scopes, mutating tools requiring an identifier the model cannot invent from retrieved text, idempotency keys, and, where the blast radius is real, write tools split into a second server.

We quote fixed scope against a published rate card once we’ve agreed the tool surface. A pilot server against one system starts at $7,500, a proof of concept at $16,000, and a production workflow with authorisation and audit logging at $12,000. A readiness sprint that settles the tool surface first starts at $6,000 and is credited against the build that follows. The minimum for a build is $7,500. Those figures are estimates in US dollars, not quotations; the quote that follows scoping is fixed in writing before kickoff.

Send the five tasks. Get the tool surface back first.

Tell us what an agent should be able to finish and who may let it, and we’ll map that to tools and scopes before we quote. Fixed scope, fixed price, start date within two working days. Email edgebrainstudios@gmail.com or call the number below.