Picture this: you've built a solid REST API. FastAPI, Express, Go doesn't matter. It works. Then someone says "we need AI agents to use our API."
Now you're writing a separate MCP server. Maintaining tool definitions that mirror your routes. Keeping schemas in sync. Debugging network timeouts between agent→proxy→API.
What if your framework just... handled this?
MCP in 30 seconds
Model Context Protocol (MCP) is how AI agents call external tools. Claude Desktop uses it. Cursor uses it. VS Code agents use it.
An MCP tool has:
- A name and description
- An input schema (JSON Schema)
- A handler function
That's literally an API endpoint with an OpenAPI spec.
The RustAPI approach: automatic tool registration
use rustapi_rs::prelude::*;
use rustapi_rs::protocol::mcp::{McpConfig, InvocationMode};
#[tokio::main]
async fn main() {
let app = RustApi::new()
.route(get_weather)
.route(post_order);
// This single call:
// 1. Reads the OpenAPI spec RustAPI auto-generates
// 2. Creates MCP tool definitions for every route
// 3. Wires handlers that dispatch through your middleware stack
let mcp = McpServer::from_rustapi(
&app,
McpConfig::new()
.name("my-api".into())
.invocation_mode(InvocationMode::InProcess)
);
mcp.serve_stdio().await.unwrap();
}
No manual tool registration. No schema duplication. Your endpoints are the tools.
Architecture: what happens on a tool call
AI Agent
→ MCP Server (stdio/HTTP)
→ RequestInvoker (internal dispatch)
→ Router::dispatch()
→ LayerStack (auth, rate limit, logging, middleware)
→ Your handler function
← Response
← MCP tool result
← Agent sees result
The key: InProcess mode skips the network entirely. The RequestInvoker creates an internal request and dispatches it through the same Router that handles HTTP. Your middleware runs. Your interceptors fire. But no TCP socket. No serialization.
Benchmark reality check
// Test: 1000 sequential MCP tool calls
// RustAPI 0.1.507, Intel i7-12700H, Windows 11
InProcess mode: 27.9 µs/call (35,842 calls/sec)
Proxy mode: 1350.0 µs/call (741 calls/sec)
~48x faster
Why does this matter? An agent making 20 tool calls per user query with proxy mode = 27ms overhead. With InProcess = 0.5ms. That's the difference between "snappy" and "loading..."
The CLI: your existing API just got MCP powers
Not using RustAPI? The CLI bridges that gap:
# Install
cargo install cargo-rustapi --features mcp
# From any OpenAPI spec (FastAPI, Express, Go anything with OpenAPI)
rustapi mcp generate --spec ./openapi.json
# Results:
# ✓ Generated MCP tools: getWeather, postOrder, listProducts
# ✓ Server running on http://localhost:9090
# ✓ Connect Claude Desktop to http://localhost:9090/sse
Under the hood, it:
- Parses the OpenAPI spec (tolerant of partial/incomplete specs)
- Maps HTTP methods + paths → MCP tool names
- Maps parameter schemas → MCP input schemas
- Generates proxy handlers that call your real API
- Spins up a fully typed MCP server
This works with any API that has an OpenAPI spec. FastAPI, Express, Go, Python doesn't matter.
Why stdio transport matters
rustapi mcp generate --spec openapi.json --stdio
Stdio is the native transport for desktop AI clients. Claude Desktop reads MCP server configs and spawns processes. Your API becomes a first-class tool in the desktop agent's arsenal no HTTP server, no port conflicts, no CORS.
Putting it all together: a full example
use rustapi_rs::prelude::*;
use rustapi_rs::protocol::mcp::{McpServer, McpConfig, InvocationMode};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Weather {
city: String,
temp_c: f64,
humidity: u8,
}
/// Get current weather for a city
#[get("/weather/{city}")]
async fn get_weather(city: Path<String>) -> Result<Json<Weather>, Error> {
// Real app: call weather API, query DB, etc.
Ok(Json(Weather {
city: city.into_inner(),
temp_c: 22.5,
humidity: 65,
}))
}
/// Create a new order
#[post("/orders")]
async fn create_order(
body: Json<CreateOrderRequest>,
) -> Result<Json<Order>, Error> {
// ... order logic
}
#[tokio::main]
async fn main() {
let app = RustApi::new()
.route(get_weather)
.route(create_order);
// Auto-generate MCP tools from routes
// get_weather → MCP tool "getWeather" with {city: string} input
// create_order → MCP tool "createOrder" with full request schema
let mcp = McpServer::from_rustapi(
&app,
McpConfig::new()
.name("my-api".into())
.invocation_mode(InvocationMode::Auto) // InProcess if possible
);
// Serve via stdio for Claude Desktop integration
mcp.serve_stdio().await.unwrap();
}
The doc comment on get_weather becomes the MCP tool description. The Path<String> becomes the input schema. The return type determines the output. Zero additional code.
Real-world impact
Alexa (@alexabelonix, AI Distribution & Sales) put it best after trying the release:
"this just made AI agents way more useful"
Because right now, connecting agents to real APIs is the bottleneck. Every company building AI agents is building the same MCP glue. RustAPI removes that step entirely.
What's next
- MCP Gateway: One endpoint that exposes all deployed RustAPI apps as MCP tools a unified agent interface
-
RustAPI Cloud:
rustapi deploy→ your API is live + MCP-toolable atyou.rustapi.dev - Streaming tool results: Long-running tools with progress updates for agents
The bet
Within 2 years, "MCP-native" will be as expected as "REST API" is today. Frameworks that don't include it will feel broken. AI agents aren't a fad — they're a new consumer of APIs, just like browsers were in 1995 and mobile apps were in 2008.
RustAPI is built for that world.
Try it
cargo add rustapi-rs --features protocol-mcp
Or use the CLI on any existing API:
cargo install cargo-rustapi --features mcp
rustapi mcp generate --spec ./your-openapi.json
GitHub: Tuntii/RustAPI
Docs: tuntii.github.io/RustAPI

Top comments (0)