feat(mcp): in-process invocation, OpenAPI→MCP CLI, stdio transport + v0.1.504#197
Merged
Conversation
- Add InvocationMode and RequestInvoker for zero-overhead in-process tool calls (~28µs vs ~1.3ms proxy). - Add 'rustapi mcp generate' CLI to turn any OpenAPI spec into MCP server. - Add --stdio support. - Live server benchmark (~48x speedup). - Cookbook recipes, README, CHANGELOG updates. - OpenAPI deserializer improvements for external specs. v0.1.504 release prep.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR expands RustAPI’s MCP capabilities by adding an in-process invocation path (bypassing HTTP proxy overhead), introducing an OpenAPI→MCP generator subcommand in cargo-rustapi (including stdio mode), and updating docs/changelog/versioning for the v0.1.504 release.
Changes:
- Added
InvocationMode+ in-process dispatch plumbing (RustApi::request_dispatcher, MCPRequestInvoker) and updated MCP server execution strategy. - Added
cargo rustapi mcp generateto spin up an MCP server from any OpenAPI 3.x spec (file/URL/api), including stdio transport. - Improved OpenAPI spec deserialization tolerance and updated cookbook/README/CHANGELOG + workspace version bump to
0.1.504.
Reviewed changes
Copilot reviewed 22 out of 23 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Updates MCP feature summary to reflect in-process, CLI generator, stdio. |
| docs/cookbook/src/SUMMARY.md | Adds new MCP recipe entries to cookbook navigation. |
| docs/cookbook/src/recipes/mcp_stdio.md | Documents stdio transport usage and limitations. |
| docs/cookbook/src/recipes/mcp_openapi_cli.md | Documents OpenAPI→MCP CLI generator usage and flags. |
| docs/cookbook/src/recipes/mcp_in_process.md | Documents in-process invocation, modes, and benchmark reference. |
| crates/rustapi-openapi/src/spec.rs | Adds serde defaults + Default derives to accept partial/real-world specs. |
| crates/rustapi-mcp/tests/mcp_e2e.rs | Adds a proxy-vs-in-process benchmark-style test. |
| crates/rustapi-mcp/src/server.rs | Adds optional in-process execution path and core request/response conversion. |
| crates/rustapi-mcp/src/lib.rs | Re-exports InvocationMode and wires up invocation module. |
| crates/rustapi-mcp/src/invocation.rs | Implements RequestInvoker for in-process dispatch via RequestDispatcher. |
| crates/rustapi-mcp/src/config.rs | Adds InvocationMode and config setter invocation_mode(...). |
| crates/rustapi-core/src/server.rs | Exposes routing helpers to support in-process dispatch. |
| crates/rustapi-core/src/router.rs | Derives Clone for Router to enable safe sharing. |
| crates/rustapi-core/src/lib.rs | Re-exports RequestDispatcher from the core crate. |
| crates/rustapi-core/src/app.rs | Adds RequestDispatcher and RustApi::request_dispatcher(); exposes router accessor. |
| crates/cargo-rustapi/src/commands/mod.rs | Registers MCP command module behind the mcp feature. |
| crates/cargo-rustapi/src/commands/mcp.rs | Implements rustapi mcp generate + stdio loop and spec loading. |
| crates/cargo-rustapi/src/cli.rs | Adds mcp subcommand wiring behind the mcp feature. |
| crates/cargo-rustapi/README.md | Documents new cargo rustapi mcp generate command in CLI table. |
| crates/cargo-rustapi/Cargo.toml | Adds MCP deps/features and enables mcp by default; updates Tokio features. |
| CHANGELOG.md | Adds v0.1.504 release notes describing MCP growth items and OpenAPI tolerance. |
| Cargo.toml | Bumps workspace version to 0.1.504. |
| Cargo.lock | Updates lockfile versions and adds new deps for MCP CLI support. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+330
to
+334
| let state = Arc::new(Extensions::new()); | ||
| let path_params = PathParams::new(); | ||
|
|
||
| let core_req = | ||
| CoreRequest::new(parts, BodyVariant::Buffered(body_bytes), state, path_params); |
Comment on lines
+110
to
+133
| let addr = format!("0.0.0.0:{}", args.port); | ||
|
|
||
| println!(" ✓ Spec loaded"); | ||
| println!(" → Proxying tool calls to: {}", target); | ||
| println!(" → MCP server listening on: http://{}", addr); | ||
| println!(); | ||
| println!("Useful test commands:"); | ||
| println!( | ||
| " curl -X POST http://127.0.0.1:{} -H 'content-type: application/json' \\", | ||
| args.port | ||
| ); | ||
| println!(" -d '{{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\"}}'"); | ||
| println!(); | ||
| println!( | ||
| " curl -X POST http://127.0.0.1:{} -H 'content-type: application/json' \\", | ||
| args.port | ||
| ); | ||
| println!(" -d '{{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/list\"}}'"); | ||
| println!(); | ||
| if args.stdio { | ||
| println!("🧠 MCP stdio transport active. Waiting for JSON-RPC on stdin..."); | ||
| run_stdio(mcp).await?; | ||
| return Ok(()); | ||
| } |
Comment on lines
+194
to
+209
| "tools/list" => match mcp.list_tools().await { | ||
| Ok(tools) => { | ||
| let tool_defs: Vec<_> = tools | ||
| .into_iter() | ||
| .map(|t| { | ||
| serde_json::json!({ | ||
| "name": t.name, | ||
| "description": t.description, | ||
| "inputSchema": t.input_schema | ||
| }) | ||
| }) | ||
| .collect(); | ||
| serde_json::json!({ "tools": tool_defs }) | ||
| } | ||
| Err(e) => serde_json::json!({ "error": e.to_string() }), | ||
| }, |
Comment on lines
+80
to
+83
| // Capture in-process dispatcher when the mode calls for it. | ||
| // We always capture it here; `call_tool` decides at runtime based on mode. | ||
| let dispatcher = app.request_dispatcher(); | ||
| server.invoker = Some(RequestInvoker::new(dispatcher, server.config.clone())); |
- Use router.state_ref() for in-process CoreRequest construction (preserves State<T>) - Conditional invoker capture in from_rustapi based on mode (no unnecessary work for Proxy) - Fix stdio: no misleading HTTP/curl output, default to 127.0.0.1 - Proper JSON-RPC error object for tools/list failures in stdio - Mark benchmark test #[ignore] to avoid flaky CI timing asserts
…cipes README - Escape State<T> in doc comment to fix rustdoc warning - Expose RequestDispatcher in rustapi-rs facade (core + prelude) - Sync recipes/README.md with new MCP recipes (in-process, CLI, stdio)
github-actions Bot
pushed a commit
that referenced
this pull request
Jun 19, 2026
…0.1.504-mcp-features feat(mcp): in-process invocation, OpenAPI→MCP CLI, stdio transport + v0.1.504 ab00b1c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR delivers the two main growth items for MCP:
In-Process Invocation
InvocationMode::InProcess/AutoRustApi::request_dispatcher()+RequestInvokerOpenAPI → MCP CLI
cargo rustapi mcp generate --spec|--url|--api ...--stdiofor desktop clientsOther
Benchmark (1000 calls):
In-process: 28ms total (28µs avg)
Proxy (live): 1.34s total (1.3ms avg)
Ready for release v0.1.504.