| name | mcp2cli-runtime-api-tooling |
| description | Turn MCP, OpenAPI, or GraphQL servers into CLIs at runtime with zero codegen, saving 96-99% of tokens on tool schemas |
| triggers | ["convert an API to a CLI","create a CLI from an OpenAPI spec","connect to an MCP server","turn a GraphQL endpoint into commands","bake an API configuration","reduce tool schema token costs","generate a CLI from API documentation","call MCP tools from command line"] |
mcp2cli — Runtime API to CLI
Skill by ara.so — Devtools Skills collection.
mcp2cli dynamically converts MCP servers, OpenAPI specs, and GraphQL endpoints into CLIs at runtime, eliminating the need for code generation and reducing token waste by 96-99% when used with AI agents. It introspects APIs, generates subcommands, handles authentication (including OAuth), and caches schemas intelligently.
Installation
uvx mcp2cli --help
uv tool install mcp2cli
npx skills add knowsuchagency/mcp2cli --skill mcp2cli
Core Concepts
mcp2cli operates in four modes:
- MCP HTTP/SSE: Connect to MCP servers over HTTP with SSE or streamable HTTP transport
- MCP stdio: Launch and communicate with MCP servers via stdin/stdout
- OpenAPI: Convert REST APIs with OpenAPI specs into CLIs
- GraphQL: Introspect GraphQL endpoints and generate query/mutation commands
All modes support authentication, caching, and can be "baked" into named configurations for reuse.
MCP HTTP/SSE Mode
mcp2cli --mcp https://mcp.example.com/sse --list
mcp2cli --mcp https://mcp.example.com/sse search --query "rust packages"
mcp2cli --mcp https://mcp.example.com/sse \
--auth-header "x-api-key:env:MCP_API_KEY" \
query --sql "SELECT * FROM users LIMIT 10"
mcp2cli --mcp https://mcp.example.com/sse --transport sse --list
mcp2cli --mcp https://mcp.example.com/sse --search "database"
MCP stdio Mode
mcp2cli --mcp-stdio "npx @modelcontextprotocol/server-filesystem /tmp" --list
mcp2cli --mcp-stdio "npx @modelcontextprotocol/server-filesystem /tmp" \
read-file --path /tmp/data.json
mcp2cli --mcp-stdio "node mcp-server.js" \
--env "DATABASE_URL=env:DATABASE_URL" \
--env "LOG_LEVEL=debug" \
fetch-records --limit 50
OpenAPI Mode
mcp2cli --spec https://petstore3.swagger.io/api/v3/openapi.json --list
mcp2cli --spec https://api.example.com/openapi.json \
--base-url https://api.example.com \
list-pets --status available --limit 20
mcp2cli --spec ./openapi.yaml --base-url http://localhost:8000 --list
mcp2cli --spec ./api-spec.json \
--base-url https://api.example.com \
--auth-header "Authorization:Bearer env:API_TOKEN" \
create-item --name "Test Item" --price 99.99
echo '{"name": "Fido", "species": "dog", "age": 3}' | \
mcp2cli --spec ./spec.json create-pet --stdin
mcp2cli --spec ./spec.json --pretty list-users
GraphQL Mode
mcp2cli --graphql https://api.example.com/graphql --list
mcp2cli --graphql https://api.example.com/graphql \
users --limit 10
mcp2cli --graphql https://api.example.com/graphql \
create-user --name "Alice" --email "alice@example.com" --role "admin"
mcp2cli --graphql https://api.example.com/graphql \
users --fields "id name email createdAt"
mcp2cli --graphql https://api.example.com/graphql \
--auth-header "Authorization:Bearer env:GRAPHQL_TOKEN" \
list-repositories --owner "knowsuchagency"
OAuth Authentication
mcp2cli supports OAuth flows across all modes with automatic token caching and refresh.
mcp2cli --mcp https://mcp.example.com/sse --oauth --list
mcp2cli --spec https://api.example.com/openapi.json \
--oauth-client-id "env:OAUTH_CLIENT_ID" \
--oauth-client-secret "env:OAUTH_CLIENT_SECRET" \
list-resources
mcp2cli --graphql https://api.example.com/graphql \
--oauth --oauth-scope "read:users write:posts" \
create-post --title "Hello" --body "World"
mcp2cli --spec ./openapi.json \
--base-url https://api.example.com \
--oauth --list
Tokens are cached in ~/.cache/mcp2cli/oauth/ and automatically refreshed when expired.
Bake Mode — Named Configurations
Save connection settings and filters to avoid repeating flags.
mcp2cli bake create petstore \
--spec https://api.example.com/openapi.json \
--auth-header "Authorization:Bearer env:PETSTORE_TOKEN" \
--exclude "delete-*,update-*" \
--methods GET,POST \
--cache-ttl 7200
mcp2cli bake create github \
--mcp-stdio "npx @modelcontextprotocol/server-github" \
--env "GITHUB_TOKEN=env:GITHUB_TOKEN" \
--include "search-*,list-*" \
--exclude "delete-*"
mcp2cli bake create analytics \
--mcp https://analytics.example.com/mcp \
--oauth --oauth-client-id "env:ANALYTICS_CLIENT_ID" \
--oauth-client-secret "env:ANALYTICS_CLIENT_SECRET"
mcp2cli @petstore --list
mcp2cli @petstore list-pets --limit 10
mcp2cli @github search-repositories --query "rust mcp"
mcp2cli @analytics query-metrics --start-date "2026-01-01"
mcp2cli bake list
mcp2cli bake show petstore
mcp2cli bake update petstore --cache-ttl 3600
mcp2cli bake remove petstore
mcp2cli bake install petstore
mcp2cli bake install github --dir ./scripts/
Configurations are stored in ~/.config/mcp2cli/baked.json.
Filtering and Discovery
mcp2cli bake create myapi \
--spec ./spec.json \
--include "list-*,get-*,search-*"
mcp2cli bake create myapi \
--spec ./spec.json \
--exclude "delete-*,admin-*"
mcp2cli bake create readonly-api \
--spec ./spec.json \
--methods GET
mcp2cli @myapi --search "user"
mcp2cli --spec ./spec.json --search "database query"
Usage-Aware Tool Ranking
mcp2cli tracks tool usage locally and ranks output to reduce token costs.
mcp2cli @myapi --list
mcp2cli @myapi --list --top 10 --compact
mcp2cli @myapi --list --sort recent
mcp2cli @myapi --list --sort alpha
mcp2cli @myapi --list --verbose
Usage data is stored in ~/.cache/mcp2cli/usage.json.
Output Control
mcp2cli @myapi list-records --pretty
mcp2cli @myapi get-binary-data --raw > output.bin
mcp2cli @myapi list-users --head 5
mcp2cli @myapi list-tags --toon
mcp2cli @myapi list-users | jq '.[] | select(.active == true) | .email'
Caching
Specs and MCP tool lists are cached with a 1-hour TTL by default.
mcp2cli --spec https://api.example.com/openapi.json --refresh --list
mcp2cli --mcp https://mcp.example.com/sse --cache-ttl 86400 --list
mcp2cli --spec https://api.example.com/openapi.json \
--cache-key my-api-prod --list
MCP2CLI_CACHE_DIR=/tmp/my-cache mcp2cli --spec ./spec.json --list
Local file specs are never cached.
Secrets Management
Avoid passing secrets in CLI arguments (visible in process lists) using env: or file: prefixes.
mcp2cli --mcp https://mcp.example.com/sse \
--auth-header "Authorization:env:MCP_TOKEN" \
--list
mcp2cli --mcp https://mcp.example.com/sse \
--oauth-client-secret "file:/run/secrets/oauth_secret" \
--oauth-client-id "env:OAUTH_CLIENT_ID" \
--list
vault kv get -field=token secret/mcp | \
MCP_TOKEN=$(cat) mcp2cli --mcp https://mcp.example.com/sse \
--auth-header "Authorization:env:MCP_TOKEN" \
search --query "logs"
Common Patterns
Create a Project-Specific CLI Wrapper
"""Project-specific API wrapper using mcp2cli."""
import subprocess
import sys
import os
def main():
"""Run mcp2cli with project defaults."""
cmd = [
"mcp2cli",
"--spec", "https://api.myproject.com/openapi.json",
"--auth-header", f"Authorization:Bearer {os.environ['PROJECT_API_KEY']}",
"--cache-ttl", "3600",
*sys.argv[1:]
]
subprocess.run(cmd, check=True)
if __name__ == "__main__":
main()
Batch Operations with Shell
#!/bin/bash
while IFS=, read -r name email role; do
echo "Creating user: $name"
mcp2cli @myapi create-user \
--name "$name" \
--email "$email" \
--role "$role"
done < users.csv
Combine with jq for Processing
mcp2cli @analytics get-metrics --period "2026-05" | \
jq '[.data[] | {date: .timestamp, value: .metric_value}]' | \
mcp2cli @warehouse import-timeseries --stdin
Create a Skill from an API
If you're building AI agent skills for APIs, use mcp2cli to generate the skill:
mcp2cli --spec https://api.example.com/openapi.json --list > API_COMMANDS.txt
Configuration Locations
- Baked configs:
~/.config/mcp2cli/baked.json (override with MCP2CLI_CONFIG_DIR)
- Cache:
~/.cache/mcp2cli/ (override with MCP2CLI_CACHE_DIR)
- OAuth tokens:
~/.cache/mcp2cli/oauth/
- Usage tracking:
~/.cache/mcp2cli/usage.json
Troubleshooting
OAuth flow fails to open browser
echo $DISPLAY
mcp2cli --spec ./spec.json \
--oauth-client-id "env:CLIENT_ID" \
--oauth-client-secret "env:CLIENT_SECRET" \
--list
MCP transport auto-detection issues
mcp2cli --mcp https://mcp.example.com/sse --transport sse --list
mcp2cli --mcp https://mcp.example.com/sse --transport streamable --list
Cache not refreshing
mcp2cli @myapi --refresh --list
rm -rf ~/.cache/mcp2cli/
mcp2cli bake show myapi
Secrets not loading from environment
echo $MY_API_KEY
mcp2cli --mcp https://mcp.example.com/sse \
--auth-header "x-api-key:env:MY_API_KEY" \
--list
OpenAPI spec with relative servers
mcp2cli --spec ./openapi.json --base-url https://api.example.com --list
GraphQL field selection too shallow
mcp2cli --graphql https://api.example.com/graphql \
users --fields "id name email profile { avatar bio } posts { id title }"
Large response truncation
mcp2cli @myapi list-all-records --head 100 > sample.json
mcp2cli @myapi download-file --file-id 123 --raw > output.pdf
Token Efficiency Analysis
When using mcp2cli with AI agents:
- Traditional approach: Send full tool schemas every turn (~1,400 tokens for 96 tools)
- mcp2cli approach:
--list --compact (~20 tokens for tool names)
- Savings: 96-99% reduction in tool schema tokens
- Use
--top N to further reduce token costs by showing only frequently-used tools
- Use
--toon output format for 40-60% smaller responses on large uniform arrays
Development Integration
import subprocess
import json
def call_api(tool: str, **kwargs):
"""Call mcp2cli and parse JSON response."""
args = ["mcp2cli", "@myapi", tool]
for k, v in kwargs.items():
args.extend([f"--{k.replace('_', '-')}", str(v)])
result = subprocess.run(args, capture_output=True, text=True, check=True)
return json.loads(result.stdout)
users = call_api("list-users", status="active", limit=50)
for user in users:
print(f"{user['name']} <{user['email']}>")
Real-World Example: GitHub MCP Server
mcp2cli bake create github \
--mcp-stdio "npx @modelcontextprotocol/server-github" \
--env "GITHUB_TOKEN=env:GITHUB_TOKEN"
mcp2cli @github --list --compact
mcp2cli @github search-repositories --query "mcp server" --limit 20 --pretty
mcp2cli @github get-repository --owner "knowsuchagency" --repo "mcp2cli"
mcp2cli @github create-issue \
--owner "myorg" \
--repo "myrepo" \
--title "Feature request" \
--body "Add support for X"
mcp2cli bake install github
For complete documentation and token savings analysis, see the full writeup.