Loading...
Loading...
Use when generating an MCP server from an OpenAPI spec with Speakeasy. Triggers on "generate MCP server", "MCP server", "Model Context Protocol", "AI assistant tools", "Claude tools", "speakeasy MCP", "mcp-typescript"
npx skill4agent add speakeasy-api/skills generate-mcp-server| Input | Required | Description |
|---|---|---|
| OpenAPI spec | Yes | Path or URL to the OpenAPI specification |
| Package name | Yes | npm package name for the MCP server (e.g., |
| Auth method | Yes | How the API authenticates (bearer token, API key, etc.) |
| Env var prefix | No | Prefix for environment variables (e.g., |
| Scope strategy | No | How to map operations to scopes (default: read/write by HTTP method) |
| Output | Description |
|---|---|
| MCP server | TypeScript MCP server with one tool per API operation |
| CLI entry point | Command-line interface with stdio and SSE transports |
| Scope definitions | Scope-based access control for filtering tools |
| Docker support | Dockerfile and compose config for containerized deployment |
| Workflow config | |
speakeasy auth login
# Or for CI/AI agents:
export SPEAKEASY_API_KEY="<your-api-key>"speakeasy lint openapi --non-interactive -s ./openapi.yamlspeakeasy auth loginSPEAKEASY_API_KEYspeakeasy run# After all config files are in place:
speakeasy runmcp-scopes-overlay.yaml# mcp-scopes-overlay.yaml
openapi: 3.1.0
overlay: 1.0.0
info:
title: Add MCP scopes
version: 0.0.0
actions:
# Enable read operations
- target: $.paths.*["get","head"]
update:
x-speakeasy-mcp:
scopes: [read]
disabled: false
# Enable write operations
- target: $.paths.*["post","put","delete","patch"]
update:
x-speakeasy-mcp:
scopes: [write]
disabled: false
# Disable specific sensitive endpoints (customize as needed)
# - target: $.paths["/admin/danger-zone"]["delete"]
# update:
# x-speakeasy-mcp:
# disabled: true.speakeasy/workflow.yaml# .speakeasy/workflow.yaml
workflowVersion: 1.0.0
speakeasyVersion: latest
sources:
My-API:
inputs:
- location: ./openapi.yaml
overlays:
- location: mcp-scopes-overlay.yaml
output: openapi.yaml
targets:
mcp-server:
target: mcp-typescript
source: My-API./openapi.yamlImportant: Use the standalonetarget, notmcp-typescriptwithtypescript. The embedded approach (enableMCPServer: trueflag) is deprecated.enableMCPServer
.speakeasy/gen.yaml# .speakeasy/gen.yaml
configVersion: 2.0.0
generation:
sdkClassName: MyApiMcp
maintainOpenAPIOrder: true
devContainers:
enabled: true
schemaPath: ./openapi.yaml
typescript:
version: 1.0.0
packageName: my-api-mcp
envVarPrefix: MYAPItarget: mcp-typescriptworkflow.yamlpackageNamenpxenvVarPrefixspeakeasy runspeakeasy run --output console 2>&1 | tail -50# Start with stdio transport (default, for local AI assistants)
npx my-api-mcp mcp start --bearer-auth "YOUR_TOKEN"
# Start with SSE transport (for networked deployment)
npx my-api-mcp mcp start --transport sse --port 3000 --bearer-auth "YOUR_TOKEN"
# Filter by scope (only expose read operations)
npx my-api-mcp mcp start --scope read --bearer-auth "YOUR_TOKEN"
# Mount specific tools only
npx my-api-mcp mcp start --tool users-get-users --tool users-create-user --bearer-auth "YOUR_TOKEN"| Flag | Description | Default |
|---|---|---|
| Transport type: | |
| Port for SSE transport | |
| API authentication token | Required |
| Override API base URL | From spec |
| Filter by scope (repeatable) | All scopes |
| Mount specific tools (repeatable) | All tools |
| Logging level | |
claude_desktop_config.json{
"mcpServers": {
"my-api": {
"command": "npx",
"args": [
"-y", "--package", "my-api-mcp",
"--",
"mcp", "start",
"--bearer-auth", "<API_TOKEN>"
]
}
}
}.claude/settings.jsonclaude mcp add{
"mcpServers": {
"my-api": {
"command": "npx",
"args": [
"-y", "--package", "my-api-mcp",
"--",
"mcp", "start",
"--bearer-auth", "<API_TOKEN>"
]
}
}
}# Build and run
docker-compose up -d
# Configure MCP client to use SSE endpoint
# "url": "http://localhost:32000/sse"# 1. Validate the spec
speakeasy lint openapi --non-interactive -s ./petstore.yaml
# 2. Create scopes overlay
cat > mcp-scopes-overlay.yaml << 'EOF'
openapi: 3.1.0
overlay: 1.0.0
info:
title: Add MCP scopes
version: 0.0.0
actions:
- target: $.paths.*["get","head"]
update:
x-speakeasy-mcp:
scopes: [read]
disabled: false
- target: $.paths.*["post","put","delete","patch"]
update:
x-speakeasy-mcp:
scopes: [write]
disabled: false
EOF
# 3. Create workflow (assumes .speakeasy/ dir exists)
mkdir -p .speakeasy
cat > .speakeasy/workflow.yaml << 'EOF'
workflowVersion: 1.0.0
speakeasyVersion: latest
sources:
petstore:
inputs:
- location: ./petstore.yaml
overlays:
- location: mcp-scopes-overlay.yaml
output: openapi.yaml
targets:
mcp-server:
target: mcp-typescript
source: petstore
EOF
# 4. Create gen.yaml
cat > .speakeasy/gen.yaml << 'EOF'
configVersion: 2.0.0
generation:
sdkClassName: PetStoreMcp
maintainOpenAPIOrder: true
typescript:
version: 1.0.0
packageName: petstore-mcp
envVarPrefix: PETSTORE
EOF
# 5. Generate
speakeasy run
# 6. Test locally
npx petstore-mcp mcp start --bearer-auth "test-token"Workflow completed successfully.
Generated TypeScript MCP server in ./src/mcp-server/server.tssrc/mcp-server/tools/src/mcp-server/mcp-server.tssrc/mcp-server/scopes.ts--scope--toolx-speakeasy-mcpenableMCPServer: truemcp-typescriptnpx my-api-mcp mcp start# Ensure auth flag matches your API's auth scheme
npx my-api-mcp mcp start --bearer-auth "YOUR_TOKEN"
# Check --help for available auth flags
npx my-api-mcp mcp start --helpx-speakeasy-mcpworkflow.yamloverlays:disabled: falsespeakeasy runtarget: mcp-typescriptenableMCPServermcp-typescript# Validate spec first
speakeasy lint openapi --non-interactive -s ./openapi.yaml
# Ensure workflow.yaml uses target: mcp-typescript (NOT target: typescript with enableMCPServer)
cat .speakeasy/workflow.yaml
# Remove enableMCPServer from gen.yaml if present -- it is deprecatedmcp-scopes-overlay.yaml