feat: superposition MCP server (smithy-generated)#1002
Draft
knutties wants to merge 20 commits into
Draft
Conversation
Captures the design for integrating juspay/smithy-mcp-generator into this repo: a generated superposition_mcp library crate plus a hand-written superposition_mcp_server binary crate, both regenerated when smithy models change. Covers crate layout, smithy model annotations, build/CI wiring, CLI surface, auth (static + HTTP passthrough), error mapping, testing strategy, and dependency sourcing (transitional bundled JAR for the codegen plugin, git-ref for smithy-mcp-runtime).
14 tasks covering: bundling the smithy-mcp-codegen JARs, wiring the smithy-build plugin, annotating ~85 operations with @mcptool, integrating the generated crate into the workspace, scaffolding the binary crate with config / auth / dispatch / transport modules, and a wiremock-backed integration test that asserts both tool count parity with the smithy models and Authorization-header passthrough. References the design spec at docs/superpowers/specs/2026-05-11-superposition-mcp-server-design.md.
Transitional local Maven repo for the MCP-server smithy build plugin and trait. See smithy/maven-local/README.md for the build recipe and migration note tracked in docs/superpowers/specs/2026-05-11-superposition-mcp-server-design.md §11.4.
Adds the mcp-rust plugin entry and points SMITHY_MAVEN_REPOS at the bundled local Maven repo. Smithy build is a no-op for MCP output until @mcptool annotations are added on operations (next task).
Every operation in the io.superposition#Superposition service is exposed as an MCP tool. Descriptions come from each operation's existing @documentation trait via the generator's fallback (see juspay/smithy-mcp-generator#5).
Patches generated Cargo.toml to inherit workspace fields, mirroring the superposition_sdk pattern.
Rebundles smithy-mcp-codegen + smithy-mcp-traits from the combined state of juspay/smithy-mcp-generator PR #6 (maven-publish gradle config) and PR #7 (smithy-rs fluent-builder interop via From-based bridging). Also bumps the smithy-mcp-runtime git ref in the patch to the same SHA so the generated tools.rs and conversions.rs use the runtime helpers (datetime_to_string, document_to_value, etc.) added in PR #7. Pins to juspay/smithy-mcp-generator HEAD 23080286 of branch feat/smithy-rs-fluent-builder-interop until PR #7 merges to main.
Generated crate from smithy-mcp-generator. Patch redirects smithy-mcp-runtime to a git ref (it is not yet on crates.io). README and CHANGELOG are hand-preserved via git-restore in the smithy-clients make target.
Rebundles smithy-mcp-codegen with juspay/smithy-mcp-generator PR #8 (commit 522aa24) on top of merged PR #7. PR #8 fixes four classes of codegen gaps that blocked compiling the generated MCP crate against the real smithy-rs SDK in this repo: 1. Walk union variant target structures into types.rs emission. 2. Harden StructureGenerator fallback (typed struct refs, bare String, user-namespaced Timestamp → String, no silent Value fallback for unrecognized shapes). 3. Detect fallible smithy-rs builder.build() and emit .expect(). 4. Inline map-value conversions to avoid nested-closure E0282. Also bumps chrono (0.4.34 → 0.4.44) and tokio-util in Cargo.lock to satisfy rmcp 1.6.0's transitive requirements (rmcp comes in via smithy-mcp-runtime). No source change in this repo required for that bump.
Generated by smithy-mcp-generator from smithy/models/*.smithy. Re-run make smithy-clients to regenerate.
Stub main; real wiring lands in subsequent commits.
Hand-rolled router builder wraps each generated tool handler with the workspace/org default injection. The ~85-line tool registration block is regenerated from crates/superposition_mcp/src/tools.rs by the script in the comment above register!.
Mounts rmcp's StreamableHttpService under /mcp on an Axum router. A tower middleware extracts the inbound Authorization header, parses it into AuthValue, and wraps the inner service call in SUPERPOSITION_AUTH.scope so the smithy-rs ResolveIdentity implementations can read it per-request. --allow-static-auth enables fallback to env-var creds when the header is absent.
…h passthrough Refactors build_client / build_router out of main.rs into a lib target so the integration test can call them directly without duplicating the ~85-tool registration list.
The smithy-rs SDK lists HTTP_BASIC_AUTH_SCHEME_ID first in every operation's auth-options vector, and the orchestrator does not fall back to subsequent schemes when the first resolver returns Err. So a client sending Authorization: Bearer <token> would hit BasicResolver first, get an error, and never reach the wire. Fix: install a TaskLocalAuthSchemePlugin that overrides the SDK's default StaticAuthSchemeOptionResolver with one that reads SUPERPOSITION_AUTH at request time and returns the single scheme that matches the credential variant. The fallback (used when the task-local is unset in stdio mode or HTTP --allow-static-auth) comes from the static-credential variant configured at startup. Adds a regression test (passthrough_bearer_auth_reaches_the_wire) so this can't silently break again. Also bumps the smithy-mcp-generator git ref to main's merge SHA 6ec7445 (smithy-mcp-generator PR #8 is now merged) and fixes two collapsible-if clippy lints in dispatch.rs by switching to Map::entry().or_insert_with().
The Axum middleware's SUPERPOSITION_AUTH.scope only wrapped the initialize request's future. rmcp's stateful StreamableHttpService spawns a long-running session task during initialize; subsequent tools/call requests are routed into that session task's queue, where they execute with the session task's task-local context (set from the initialize request's auth) — not the new request's. Fix: introduce an AuthRouter that wraps the inner smithy_mcp_runtime Router and implements ServerHandler::call_tool by extracting auth from the per-request RequestContext (rmcp stores http::request::Parts in context.extensions) and re-entering SUPERPOSITION_AUTH.scope around the inner dispatch. This runs in whatever task rmcp dispatches the handler in, so each request sees its own auth — regardless of session affinity. Smoke-tested end-to-end against a local Superposition: two concurrent HTTP sessions with different schemes (Bearer + Basic) both succeed; no-auth requests still get 401 at the Axum layer.
Contributor
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Summary
Adds a Model Context Protocol (MCP) server that exposes the Superposition API as MCP tools, generated from the existing Smithy models.
crates/superposition_mcp— generated MCP wrapper crate produced bysmithy-mcp-codegen. One MCP tool per@mcpTool-annotated Smithy operation (organisations, workspaces, dimensions, default-config, contexts, experiments, experiment groups, functions, type templates, variables, secrets, webhooks, audit logs, config).crates/superposition_mcp_server— thin binary (superposition-mcp) wiring the generated tools to a transport.SUPERPOSITION_BEARER_TOKENorSUPERPOSITION_BASIC_USER/SUPERPOSITION_BASIC_PASSenv vars.--http <addr>) with per-requestAuthorizationpassthrough; task-local auth scope means concurrent clients don't bleed credentials.--allow-static-authopts into env-var fallback when a request has no header.SUPERPOSITION_WORKSPACE_ID/SUPERPOSITION_ORG_IDso clients can omit them on every call.@mcpToolannotations on operations,smithy-mcp-codegenregistered insmithy-build.json, vendoredsmithy-mcp-codegen/smithy-mcp-traitsJARs undersmithy/maven-local/,makefiletarget for codegen, and a smallmcp-rust.patchfor the generated output..claude/plans/.Sized at ~12.6k LOC, of which the generated
superposition_mcpcrate (conversions.rs,tools.rs,types.rs,server.rs) is ~8.2k — review effort is concentrated insuperposition_mcp_server/(config, auth, dispatch, transport) and the Smithy model annotations.Test plan
cargo build -p superposition_mcp_servercargo test -p superposition_mcp_server(includes the wiremock integration test covering tool dispatch + auth passthrough)SUPERPOSITION_ENDPOINT=http://127.0.0.1:8080 superposition-mcp --http 127.0.0.1:8765 --allow-static-auth) and exerciseListOrganisation→ListWorkspace→ListExperimentvia an MCP clientSUPERPOSITION_BEARER_TOKENsetAuthorizationheader is forwarded (and not leaked across concurrent requests)make smithy-mcpand confirm the diff is empty (codegen is reproducible)🤖 Generated with Claude Code