# Stop Shipping MCP Servers That Need a Python Runtime

> Source: <https://sourcefeed.dev/a/stop-shipping-mcp-servers-that-need-a-python-runtime>
> Published: 2026-08-19 09:08:24+00:00

[Cloud & Infra](https://sourcefeed.dev/c/cloud)Article

# Stop Shipping MCP Servers That Need a Python Runtime

With rmcp 3.x stable and spec-current, compile-time schemas and single-binary distribution make Rust the grown-up choice for MCP servers.

[Emeka Okafor](https://sourcefeed.dev/u/emeka_okafor)

MCP servers have quietly crossed a line. A year ago they were weekend glue code — a decorator-wrapped Python function that let Claude poke at your Postgres. Now they're the control plane for agents that start GPU instances, run commands on production boxes, and health-check inference endpoints. A recent walkthrough of building exactly that kind of server in Rust — a DevOps agent managing AWS [EC2 G5g](https://aws.amazon.com/ec2/instance-types/g5g/) instances serving Gemma 4 under [vLLM](https://docs.vllm.ai/) — is a good excuse to say the quiet part out loud: once an MCP server is infrastructure, the scripting-language defaults stop making sense.

## The SDK finally caught up

The reason this argument is worth having *now* is [rmcp](https://github.com/modelcontextprotocol/rust-sdk), the official Rust SDK for the [Model Context Protocol](https://modelcontextprotocol.io). For a long time it was the ecosystem's awkward sibling — a community crate that got adopted upstream and churned through breaking changes. The 3.x line is a different story: it tracks the stable 2026-07-28 spec revision (with compatibility back through 2025-11-25), ships both stdio and Streamable HTTP transports, and covers the newer protocol surface — long-running tasks with cancellation, subscriptions, response caching — that most hand-rolled servers never bother with.

More importantly, the ergonomics stopped being a tax. Defining a tool in rmcp now looks a lot like FastMCP with types:

```
#[tool(description = "Start a stopped EC2 instance")]
async fn start_instance(
    &self,
    Parameters(req): Parameters<InstanceRequest>,
) -> Result<CallToolResult, McpError> { ... }
```

A `#[tool_router]`

macro on the impl block collects the tools, `#[tool_handler]`

wires up the `ServerHandler`

trait, and `schemars`

derives the JSON schema the agent sees directly from the `InstanceRequest`

struct — doc comments become field descriptions. That last detail is the load-bearing one, and it's worth dwelling on.

## Schema drift is the real bug class

In the Python SDK, your tool schema is generated from type hints, which is fine until it isn't: hints are advisory, `Optional`

sneaks in, someone edits the docstring but not the validator, and the agent ends up negotiating with a schema that no longer matches what the handler actually does. Nobody catches it because nothing *fails* — the model just starts getting weird tool errors, and you burn an afternoon blaming the prompt.

In rmcp, the schema the agent sees and the struct the handler destructures are the same compiled artifact. If they diverge, the build breaks. For a class of software whose entire job is to present a machine-readable contract to a non-deterministic caller, "the contract is checked by the compiler" isn't a nice-to-have — it's arguably the whole point. LLMs are already unreliable enough at the calling end; the receiving end should be boring.

## Distribution is the other half

The walkthrough's benchmarks — 2.5 ms cold start versus 462 ms for the equivalent Python server, 12 MB peak RSS versus 83 MB — come from one author's measurements of one server, so hold them loosely. But honestly, the headline numbers are the least persuasive part. A stdio MCP server is a long-running child process; nobody's cold start matters, and the author, to their credit, says as much.

The number that should sting is operational: a Python MCP server ships as an interpreter, a virtualenv, and a `uvx`

or `pip`

resolution step on every machine it lands on. Anyone who's watched a fleet of `uvx`

-launched servers drift out of sync — or debugged a server that broke because the *host's* Python moved — knows this failure mode. The Rust version is a ~20 MB stripped static binary. You `scp`

it, you point your client config at a path, and there is no second step. Multiply by sixteen developer laptops or a hundred CI runners, and the case makes itself. This is the same argument that made Go the default for infra CLIs a decade ago; MCP servers are just the newest thing in that category.

The honest counterweight: the Rust server pulled in 241 transitive crates against Python's 34 resolved packages (the AWS SDK crates do most of that damage). Static binaries move your supply-chain exposure to build time; they don't eliminate it. Run `cargo deny`

and vendor your lockfile, or you've traded one drift problem for another.

## Who should actually switch

Nobody should rewrite a working single-user MCP server in Rust. Python remains the right default for prototyping — the iteration loop is unbeatable, and if your server lives on one laptop, none of the above bites you.

The switch makes sense at two thresholds. First, **fleet distribution**: the moment an MCP server becomes something you ship to other people's machines — teammates, CI, customers — the static binary and the compiler-checked schema stop being aesthetics and start being support-ticket reduction. Second, **embedding**: teams building MCP capability into existing Rust or Go services were previously stuck sidecar-ing a Python process; rmcp's in-process and Streamable HTTP transports remove that.

The walkthrough's own use case is a nice proof that the ceiling is high enough. Driving EC2 lifecycle and SSM Run Command, health-checking a Gemma 4 E2B deployment under vLLM on Graviton2 boxes with T4G GPUs — including working around Turing's missing bf16 path — is real DevOps surface, nine tools deep, in about 560 lines. That's not a toy, and the SDK didn't get in the way.

Adoption is a single `cargo add rmcp --features server,macros,transport-io`

, and the mental model transfers almost one-to-one from FastMCP. The remaining gap is ecosystem breadth — Python still has more examples, more middleware, more Stack Overflow answers. But with the official SDK now stable, spec-current, and pleasant, that gap is closing from the direction that matters: the servers people actually depend on. MCP started as a protocol for wiring tools to models. It's ending up as a protocol for shipping software — and we already know what language ecosystem wins when the deliverable is a binary that has to run anywhere, forever, without a runtime.

## Sources & further reading

-
[Build an MCP server in Rust with rmcp: a walk-through](https://dev.to/gde/build-an-mcp-server-in-rust-with-rmcp-a-walk-through-4cif)— dev.to -
[The official Rust SDK for the Model Context Protocol](https://github.com/modelcontextprotocol/rust-sdk)— github.com -
[Announcing Gemma 4 on vLLM](https://vllm.ai/blog/2026-04-02-gemma4)— vllm.ai

[Emeka Okafor](https://sourcefeed.dev/u/emeka_okafor)· Security Editor

Emeka has spent over a decade tracking threat actors, vulnerability disclosures, and the evolving landscape of application security, bringing a sharp continent-spanning perspective to his reporting. He's known for translating dense CVE advisories into clear, actionable context that developers and security teams alike actually read.

## Discussion 0

No comments yet

Be the first to weigh in.
