A pipeline for generating ToolHive-ready MCP servers from OpenAPI specs.
Wrapping an OpenAPI spec as an MCP server one-for-one doesn't work: enterprise
APIs expose hundreds of endpoints, auto-generated names like
drives_files_list_v2 tell an LLM nothing, and parameter docs are written for
developers instead of models. mcp-builder curates the spec into a short YAML
contract (mcp-scope.yaml), then deterministically generates a complete
ToolHive-ready server from that contract. AI assists at the edges (scoping and
validation); the code generator itself is plain Python with no model in the
loop. See the RFC for the full design.
Requires Python 3.13+ and uv.
git clone https://github.com/stacklok/mcp-builder.git
cd mcp-builder
uv syncRun uv run mcp-builder --help to see the full list of CLI commands.
Phases 1 and 4 (and the optional deploy step) are driven by AI skills that
live in skills/. The skills spawn sub-agents from agents/ (for spec
analysis, tool scoping, validation, and polish). Both need to be visible to
your AI coding tool. Symlinking from the repo keeps them in sync with
git pull.
Claude Code — symlink the skills into .claude/skills/ and the agents
into .claude/agents/ (project-level). Swap .claude/ for ~/.claude/
to install at the user level (available everywhere) instead.
mkdir -p "$(pwd)/.claude/skills" "$(pwd)/.claude/agents"
# Skills
ln -s "$(pwd)/skills/ai-scoping" "$(pwd)/.claude/skills/ai-scoping"
ln -s "$(pwd)/skills/ai-validation" "$(pwd)/.claude/skills/ai-validation"
ln -s "$(pwd)/skills/deploy-assist" "$(pwd)/.claude/skills/deploy-assist"
# Agents
ln -s "$(pwd)/agents/spec-analyzer.md" "$(pwd)/.claude/agents/spec-analyzer.md"
ln -s "$(pwd)/agents/endpoint-scoper.md" "$(pwd)/.claude/agents/endpoint-scoper.md"
ln -s "$(pwd)/agents/code-validator.md" "$(pwd)/.claude/agents/code-validator.md"
ln -s "$(pwd)/agents/polish-suggester.md" "$(pwd)/.claude/agents/polish-suggester.md"Once installed, the skills appear as slash commands: /ai-scoping,
/ai-validation, /deploy-assist.
Gemini CLI — Gemini CLI supports skills at .gemini/skills/ and
sub-agents at .gemini/agents/ (swap for user-level ~/.gemini/... if
you prefer). The install is symmetric with Claude Code:
mkdir -p "$(pwd)/.gemini/skills" "$(pwd)/.gemini/agents"
# Skills
ln -s "$(pwd)/skills/ai-scoping" "$(pwd)/.gemini/skills/ai-scoping"
ln -s "$(pwd)/skills/ai-validation" "$(pwd)/.gemini/skills/ai-validation"
ln -s "$(pwd)/skills/deploy-assist" "$(pwd)/.gemini/skills/deploy-assist"
# Agents
ln -s "$(pwd)/agents/spec-analyzer.md" "$(pwd)/.gemini/agents/spec-analyzer.md"
ln -s "$(pwd)/agents/endpoint-scoper.md" "$(pwd)/.gemini/agents/endpoint-scoper.md"
ln -s "$(pwd)/agents/code-validator.md" "$(pwd)/.gemini/agents/code-validator.md"
ln -s "$(pwd)/agents/polish-suggester.md" "$(pwd)/.gemini/agents/polish-suggester.md"Gemini CLI also ships gemini skills link <path>, which auto-discovers and
symlinks SKILL.md files if you'd rather not do it by hand.
The pipeline has four phases plus an optional deploy step. Phases 1 and 4 (and deploy) run inside your AI coding tool via the skills above. Phases 2 and 3 are CLI steps you run at the terminal.
You are responsible for runtime testing. The pipeline validates structure — tool coverage, HTTP methods, manifest shape, auth wiring. It does not exercise the server against the real API. Auth especially must be verified by you: specs misrepresent auth more often than anything else, and a server that compiles and passes structural checks can still fail the first real token exchange. Plan to run the built image against the live API with real credentials before declaring any server done.
Example specs live in e2e/fixtures/ (Google Drive, GitHub, Jira,
BambooHR, Stripe, Slack, and more) if you want something to try against.
-
Launch your AI coding tool (Claude Code or Gemini CLI).
-
Run the scoping skill:
/ai-scoping <path-to-openapi-spec> -
Work through the skill's review gates: spec analysis, endpoint grouping, tool naming, description writing, and auth detection. The skill pauses at each gate for your approval.
-
Verify the detected auth at the auth gate. The skill maps OpenAPI security schemes to ToolHive auth types, but it is inferring — not observing. Before confirming, check:
- Auth type is correct (
oauth2,oidc,api_key, etc.). - Issuer / token URL is reachable and has no template placeholders
(e.g.
{companyDomain}). - Scopes match what your workflows actually need.
- Header name for API keys matches what the API expects.
If the skill flagged a discovery-doc warning, read it. Upstream IdPs that publish non-compliant docs need
type: oauth2with explicit endpoints — nottype: oidc. - Auth type is correct (
Outputs:
mcp-scope.yaml— the curated contract the generator consumes.scoping-summary.md— explanation of the AI's choices.
-
Open
mcp-scope.yamlalongsidescoping-summary.md. -
Review tool groups, names, descriptions, and hints. Edit freely.
-
Re-verify the
auth:block. This is your last chance before code is generated around it. Confirmauth.type, endpoints, scopes, and any header names one more time. -
(Optional) If you made substantial edits, re-run validation:
uv run mcp-builder validate path/to/mcp-scope.yaml path/to/openapi.yaml
The scoping skill already validated once before handoff, so a clean YAML does not strictly need this step.
-
One-time setup: clone
mcp-template-pysomewhere on disk.git clone https://github.com/stacklok/mcp-template-py.git ../mcp-template-py
-
Generate the server:
uv run mcp-builder generate \ path/to/mcp-scope.yaml \ path/to/openapi.yaml \ ../mcp-template-py \ --output-dir ./out -
Create a lock file for the generated project:
cd ./out/{server-name}-mcp && uv sync
The generator intentionally doesn't copy
uv.lockfrom the template — the template's lock pins its own package name, which the generator rewrites.uv syncproduces a fresh lock that matches the generatedpyproject.toml.
This step is fully deterministic — no AI in the loop. Output is a complete
MCP server project plus ToolHive deployment manifests in ./out.
-
Launch your AI coding tool and run:
/ai-validation <generated-project-dir> <scope-yaml> <spec-path> -
The skill performs structural and behavioral checks (every scoped tool is present, HTTP methods match the spec, auth is wired correctly), builds the Docker image, and suggests improvements driven by scope hints (response shaping, pagination, API quirks).
-
Runtime-test the server yourself. The skill does not make a live API call — structural checks do not cover runtime behavior.
- Run the generated server locally (
task runfrom the generated project; see its README for the exact command, port, and env vars). - Connect with the MCP Inspector to list tools and invoke them interactively against the real API.
- Prove auth works by successfully invoking at least one tool with real credentials. A live tool call is the only signal that token exchange, headers, and scopes are all correct.
- Probe a few error paths (bad token, missing parameter). Error messages from the real API often need response shaping the generator cannot anticipate.
- Run the generated server locally (
-
Tag and push the image built in Phase 4 to a registry your cluster can reach.
ttl.shworks without auth for quick iteration. -
Launch your AI coding tool and run:
/deploy-assist <generated-project-dir> <cluster-repo-path> -
The skill drops the generated manifests into your cluster repo and fills placeholders by inferring values from existing cluster configuration. It will list manual steps that remain.
-
Complete the manual steps yourself. At minimum you will need to:
- Create the Kubernetes
Secretwith real credentials. - Confirm the OAuth client registration in your IdP matches the redirect URI the manifest uses.
- Re-run the runtime auth tests from Phase 4 against the deployed server — the cluster is not special; auth can still break there (redirect URIs, egress rules, clock skew, certificate trust).
- Create the Kubernetes
Scope of the current version:
- OpenAPI 3.0 / 3.1 only. No Swagger 2.0, GraphQL, gRPC, or WSDL.
- 1:1 endpoint-to-tool mapping. Merging related endpoints into a single tool is deferred.
- Single spec per server. No multi-spec composition or vMCP groups.
- SDK-only APIs are not supported (e.g., Google services with no usable spec).
- ToolHive-supported auth only: OAuth2 authorization code, HTTP bearer, and header-injected API keys. Query-parameter API keys and HTTP basic are flagged but not generated.
- Python output only. The generator targets
mcp-template-py; other language templates are future work. - No spec-diffing. Detecting upstream API changes and re-running the pipeline is a manual step.
flowchart LR
subgraph Inputs
spec["OpenAPI 3.x spec"]
workflows["Workflow descriptions"]
end
subgraph "Phase 1: AI scoping"
skill1["ai-scoping skill"]
scope["mcp-scope.yaml"]
end
subgraph "Phase 2: Human review"
editor["Edit YAML"]
end
subgraph "Phase 3: Code generation"
gen["mcp-builder generate"]
server["MCP server project"]
end
subgraph "Phase 4: AI validation"
skill2["ai-validation skill"]
report["Validation report"]
end
spec --> skill1
workflows --> skill1
skill1 --> scope
scope --> editor
editor --> gen
gen --> server
server --> skill2
skill2 --> report
Phase 3 is intentionally deterministic: the same mcp-scope.yaml and spec
always produce the same server, which makes the output reviewable and
reproducible in CI. Phase 4 is where AI re-enters — to catch generator bugs
and suggest improvements the generator can't anticipate (pagination, response
shaping, API quirks). A user who prefers no AI can hand-write the YAML and
enter at Phase 3. Full rationale, alternatives, and the mcp-scope.yaml
schema live in the RFC.
Task runners are defined in Taskfile.yml:
task check # lint + typecheck + test + security
task test # unit tests
task test-e2e # end-to-end tests (generates servers from fixtures)
task format # ruff format + fixThe repo uses uv for dependency management, ruff for lint/format, ty for
typechecking, and pytest for tests. See CLAUDE.md for
project-specific implementation guidelines.
- RFC: Custom MCP Server Builder — full design and alternatives considered
mcp-template-py— Python MCP server template the generator builds from- ToolHive — runtime for MCP servers