AI Blog

FastMCP vs the TypeScript SDK vs mcp-go vs rmcp: who negotiates the revision for you

These four are benchmarked on throughput, which is a 1.9 ms spread inside a 50–500 ms upstream call — and ranked on it while the axis with a date attached goes unmeasured. Three of the four implement the 2026-07-28 stateless revision and the most-used Go library does not, but the sharper question is which of them absorbs your dual-revision window instead of turning it into your topology.

By Agentic AI Wiki 15 min read

Pick an MCP server library on the numbers everyone publishes — throughput, memory, p50 — and you will optimise a difference of two milliseconds inside a response that takes two hundred, because the work these servers do is calling somebody else's API. The axis that will actually cost you a quarter is which specification revision the library speaks, and it splits the field in an awkward direction: the two options a team is most likely to already be running are not the two most current ones. Worse, "supports the new revision" is the wrong question, because your clients upgrade on their own schedule and you need one deployment that answers both.

At a glance

Four libraries, four languages, and one shared deadline that arrived on 28 July 2026.

LibraryLanguageCurrent revisionGovernance
FastMCP Python 2026-07-28, from FastMCP 4 Community framework above the official SDK; FastMCP 1.0 was folded into that SDK in 2024
Official TypeScript SDK TypeScript 2026-07-28, in the v2 packages Tier 1, maintained alongside the specification
mcp-go Go 2025-11-25, with compatibility back to 2024-11-05 Community; the separate official Go SDK holds the Tier 1 slot
rmcp Rust 2026-07-28, compatible with 2025-11-25 Official Rust SDK, now on its 3.x line
Four implementations against four axes of specification currency A matrix with four rows and four columns. FastMCP for Python is strong on implementing the current revision, strong on serving handshake-era clients from the same deployment, medium on being tracked by the specification maintainers, and strong on being the default choice in its language. The official TypeScript SDK is strong on the current revision, medium on serving older clients, strong on being tracked, and strong on being the default. mcp-go is weak on the current revision, medium on serving older clients, weak on being tracked because a separate official Go SDK holds that role, and strong on being the default. rmcp for Rust is strong on the current revision, strong on serving older clients, strong on being tracked, and weak on being the default choice. The matrix shows that the two most widely used options are not the two most current ones. Specification currency, not throughput Implements the current revision Serves handshake-era clients too Tracked by the spec maintainers Default choice in its language FastMCP Python · 27.5k stars Strong Strong Medium Strong Official TypeScript SDK TypeScript · 13.3k stars Strong Medium Strong Strong mcp-go Go · 9.1k stars Weak Medium Weak Strong rmcp Rust · 3.9k stars Strong Strong Strong Weak Strong Medium Weak Star counts are a proxy for share within each language, not across them. The two rows a team is most likely to already be on — FastMCP and mcp-go — differ on the first column, and that column is the one with a date attached to it.
The column with a date attached to it is the first one, and it is where the two most-used rows disagree.

The 2026-07-28 revision is not a routine bump. It retired the initialize/initialized exchange and the Mcp-Session-Id header outright, added Mcp-Method, Mcp-Name and MCP-Protocol-Version so gateways can route without parsing bodies, deprecated the HTTP+SSE transport, moved Tasks out of the core into an extension, and started a minimum twelve-month clock on Roots, Sampling and Logging. Things were removed, which is why "we will get to it" is a position with a cost attached.

The benchmark axis is the one that does not matter

Implementation latency against the upstream call it wraps Two panels. The upper panel, drawn to a zero to five hundred millisecond scale, shows the typical upstream API round trip occupying fifty to five hundred milliseconds while the entire spread of five protocol implementations, from nought point three eight to two point two five milliseconds, is a sliver too narrow to read. The lower panel rescales to zero to two point five milliseconds and separates the five: Rust with rmcp at nought point three eight, Go with mcp-go at nought point five nought, C sharp at nought point six nought, TypeScript at nought point seven six, and Python with FastMCP at two point two five. The chart shows that the axis these libraries are benchmarked on is invisible inside the response time of the work they do. p50 request handling, against the upstream call it wraps Drawn to scale — 0 to 500 ms Upstream API round trip 50–500 ms — the work the server actually does All five implementations 0.38–2.25 ms 0 125 250 375 500 ms Rescaled 200× — 0 to 2.5 ms, the axis the benchmarks report Rust · rmcp 0.38 Go · mcp-go 0.50 C# · official SDK 0.60 TypeScript · official SDK 0.76 Python · FastMCP 2.25 0 1.25 2.5 ms Figures are p50 echo-proxy latency from an independent five-language benchmark; the upstream band is the range its author reports as typical.
The lower panel is the comparison everyone publishes. The upper panel is the same data in the context it runs in.

An independent five-language benchmark puts p50 echo-proxy handling at 0.38 ms for Rust with rmcp, 0.50 ms for Go with mcp-go, 0.60 ms for C#, 0.76 ms for TypeScript and 2.25 ms for Python with FastMCP; idle resident memory runs from 7 MB for Rust through 21 MB for Go and 97 MB for Python to 162 MB for TypeScript and 221 MB for C#. Its author is refreshingly direct about what this means: for a proxying MCP server, language does not matter, because all five handle a request in under 3 ms and the real bottleneck is the 50–500 ms network round trip to the upstream API.

Those two sentences are the whole argument for ignoring the benchmark. A 1.9 ms spread inside a 200 ms response is a 1% effect on latency you cannot feel, and the memory numbers only start mattering at a replica count most tool servers never reach. If your server does something genuinely compute-bound — parsing large documents, running embeddings inline — the ranking becomes real, but then you are benchmarking your workload, not the protocol library.

The reason the wrong axis wins is that it is the only one with numbers. Revision currency has no leaderboard, so it loses to a chart. That is a bad trade, because latency here is a 1% difference and being a revision behind is a binary that eventually stops your clients connecting.

FastMCP — the ecosystem tax and the ecosystem benefit

The default that is genuinely the default

FastMCP sits at around 27,500 stars, is downloaded roughly a million times a day, and claims that some version of it powers about 70% of MCP servers across all languages. Even discounting a project's own framing, nothing else in this comparison is close on adoption. Its lineage is unusual and worth knowing: FastMCP 1.0 was absorbed into the official Python SDK in 2024, and the actively maintained standalone project continued alongside it — so "FastMCP" and "the official Python SDK" are related but not interchangeable, and a Python team has to know which one it is on.

It is the only one that advertises the thing you actually need

FastMCP 4 is the interesting release here, because it is aimed squarely at the problem this article is about: making stateful applications work on the sessionless 2026-07-28 protocol while one deployment continues to serve handshake-era clients, negotiating the best revision per connection. Tools can ask follow-up questions across requests and hold authenticated user state without sticky sessions, so any replica behind an ordinary load balancer can answer any request. Most FastMCP 3 servers are said to upgrade untouched.

That per-connection negotiation is a product decision the others have not made as loudly, and it is worth more than 1.9 ms. It is also, notably, a framework feature rather than a protocol feature — which means it is exactly the kind of thing you lose in a language migration.

The official TypeScript SDK — currency by construction

Tier 1 means the revision ships with the spec

The TypeScript SDK implements 2026-07-28 in its v2 packages and sits at roughly 13,300 stars. Its structural advantage is boring and large: it is one of the Tier 1 SDKs, so a revision landing in the specification and a revision landing in the library are close to the same event. Across the Tier 1 set the maintainers report close to half a billion downloads a month, with TypeScript and Python both past a billion total — this is the well-trodden path, and the cost of being on it is that you get the protocol's raw primitives and wire the ergonomics yourself.

Where the ergonomics gap actually bites

The comparison people make is decorators versus explicit registration, which is taste. The comparison that costs money is auth: with a raw SDK you assemble OAuth discovery, token validation and PKCE rather than inheriting them. That is a real amount of work, and it is work you must not get wrong. Weigh it against the certainty that the next revision arrives on day zero rather than in some later minor release; see MCP auth and OAuth 2.1 for what you are assembling.

mcp-go — the anomaly worth checking today

Popular and a revision behind, and those are separate facts

mcp-go is the library most Go MCP servers were built on: roughly 9,100 stars and a long head start. Its README states that it implements 2025-11-25, with backward compatibility to 2025-06-18, 2025-03-26 and 2024-11-05. That is the handshake era. Meanwhile the official Go SDK — a different project, around 5,100 stars — implements 2026-07-28 from v1.7.0 onward while retaining compatibility with the three previous revisions.

So Go is the language where the popular choice and the tracked choice are two different codebases at two different revisions, and moving between them is a port rather than an upgrade. If you run a Go MCP server, this is the paragraph to act on: check which import path you are on before you read anything else here.

What "a revision behind" costs, concretely

Not much, until it does. Clients built on current SDKs generally negotiate down, so an older server keeps working — right up to a client that drops the deprecated transport, or a gateway that routes on the new headers, or an infrastructure team that wants to scale a stateless workload and finds session affinity in the way. Each of those is somebody else's roadmap item, which means the date is not yours to pick. That is the operational shape covered in protocol revisions and deprecation windows.

rmcp — current, official, and the smallest ecosystem

The trajectory is the story

The Rust SDK reached 1.0 in March 2026 and moved fast; it is now on a 3.x line, implements the stable 2026-07-28 revision, and remains compatible with 2025-11-25 and earlier. At roughly 3,900 stars it is the smallest community here by some distance, which shows up as fewer worked examples and fewer people who have already hit your problem — but on the axis this article cares about it is the strongest row in the matrix.

Where it earns its place

Not on the 0.38 ms, which is a rounding error against the upstream call. It earns its place on the 7 MB idle footprint when you are running many small tool servers per host, on procedural macros that generate tool implementations from typed structs — which makes the schema and the code one artefact rather than two that drift — and on being an official SDK rather than a community one, which is what makes the revision cadence predictable.

The window, not the cutover

Two ways to survive a dual-revision window Two architectures side by side. On the left, forked deployments: handshake-era clients reach a legacy endpoint and current clients reach a new endpoint, each carrying its own copy of the tool implementations, authorization checks and rate limits, so every change must be made twice for as long as the window lasts. On the right, one deployment: both classes of client reach the same address, the revision is resolved at the edge into a single internal request shape, and one copy of the tools, authorization and rate limits serves both. A note below states that the compatibility layer on the right is a module with a removal date, so retiring a revision is a deletion rather than a migration. Forked deployments the obvious answer, and the expensive one Handshake-era clients Current-revision clients legacy.example.com mcp.example.com Tool implementations Authorization checks Rate limits Tracing copy 1 of 2 Tool implementations Authorization checks Rate limits Tracing copy 2 of 2 Every fix lands twice, for as long as the window lasts. One deployment, negotiated per connection the revision is an edge concern, not a topology Handshake-era clients Current-revision clients Edge: negotiate the revision, normalise inward one internal request shape — the only place a revision appears Tool implementations Authorization checks Rate limits Tracing one copy, no revision-specific branches Any replica answers any client — each request carries its own context. Both diagrams describe the same window. The difference is where the revision lives: in your topology, or in one module at the edge. Keep the compatibility layer in a single module with its removal date written in the file, and retiring a revision becomes deleting a directory rather than an archaeology exercise — which is the difference between a window that closes and one that never does.
The revision either lives in your topology or in one module at the edge. Only one of those has a removal date.

The framing error underneath most of these library comparisons is the word "migration". There is no cutover, because you own one end of the connection and your clients own the other. What you actually run is a window in which both revisions are live, and the only question is where in your system that fact is represented.

Fork the deployment and you fork your tool implementations, authorisation checks, rate limits and tracing along with it, and every fix lands twice for as long as the window lasts — which is longer than anyone plans, because the last 3% of callers are unattended jobs with no owner reading release notes. Negotiate at the edge and normalise inward, and the revision appears in exactly one module, with a removal date you can write in the file. Retiring a revision becomes deleting a directory.

This reframes the library choice. The question is not "does it support 2026-07-28" — three of these four do. It is "how much of my dual-revision window does this library absorb, and how much becomes my topology". That is why FastMCP's per-connection negotiation is the most consequential single feature in the comparison, and why a Go team on mcp-go has the largest decision in front of it.

When to pick which

SituationPickBecause
Greenfield, Python shop, want the shortest path to a working serverFastMCPLargest ecosystem, batteries-included auth, and per-connection revision negotiation you would otherwise build
Greenfield, TypeScript shop, want protocol primitives and no framework opinionOfficial TypeScript SDKTier 1 currency by construction; budget real time for the OAuth wiring
Existing Go server on mcp-goAudit first, then plan the portYour library and the Tier 1 Go implementation are different projects at different revisions
Many small tool servers per host, or a compute-bound toolrmcp7 MB idle is a real multiplier at replica count; typed macros keep schema and code as one artefact
You cannot control when your clients upgradeWhichever negotiates per connectionThe window is the deliverable, not the revision

And a check that takes ten minutes for any of them: for each of the last three revisions, record the specification's publication date, the library's first release implementing it, and its first stable release. Three historical lags predict the fourth better than any roadmap. That number, not the p50, is the one that should decide this.

FAQ

Does language really not matter for MCP server performance?

For a server whose tools call an upstream API, no — the benchmark's own author says so, and a 1.9 ms spread inside a 50–500 ms round trip is unmeasurable in practice. It starts mattering when your tools do the compute themselves, or when idle memory multiplied by replica count becomes a bill.

Is FastMCP the same thing as the official Python SDK?

No. FastMCP 1.0 was folded into the official Python SDK in 2024, and the standalone project kept developing separately. They share ancestry, not a release train, and a Python team should know which one its server imports.

My mcp-go server works fine against current clients. Do I have to move?

Not today — current clients generally negotiate down. The exposure is that your deadline is set by other people: a client dropping the deprecated transport, a gateway routing on the new headers, or your own need to scale a session-free workload. Check which import path you are on, then decide with a date rather than under one.

What is the actual risk of staying on the handshake-era revision?

Two things. Session affinity keeps a stateless workload from scaling like ordinary HTTP, and the twelve-month deprecation clocks on Roots, Sampling and Logging expire whether or not you have moved. Neither is an outage today; both are work you do not get to schedule.

Should I run two deployments during the transition?

Prefer not to. Forking the deployment forks your tool code, authorisation and rate limits with it, and the window outlasts the plan. Negotiate at the edge, normalise to one internal request shape, and keep the compatibility layer in a single module with its removal date written in the file.

Further reading

On this wiki:

Project sources: