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.
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.
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.
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.