mcp2cli
Turn any MCP server, OpenAPI spec, or GraphQL endpoint into a CLI at runtime. No codegen.
Install
# Run directly (no install needed)
uvx mcp2cli --help
# Or install
pip install mcp2cliCore Workflow
- Connect to a source (MCP server, OpenAPI spec, or GraphQL endpoint)
- Discover available commands with
--list(or filter with--search) - Inspect a specific command with
<command> --help - Execute the command with flags
# MCP over HTTP
mcp2cli --mcp https://mcp.example.com/sse --list
mcp2cli --mcp https://mcp.example.com/sse create-task --help
mcp2cli --mcp https://mcp.example.com/sse create-task --title "Fix bug"
# MCP over stdio
mcp2cli --mcp-stdio "npx @modelcontextprotocol/server-filesystem /tmp" --list
mcp2cli --mcp-stdio "npx @modelcontextprotocol/server-filesystem /tmp" read-file --path /tmp/hello.txt
# OpenAPI spec (remote or local, JSON or YAML)
mcp2cli --spec https://petstore3.swagger.io/api/v3/openapi.json --list
mcp2cli --spec ./openapi.json --base-url https://api.example.com list-pets --status available
# GraphQL endpoint
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"CLI Reference
mcp2cli [global options] <subcommand> [command options]
Source (mutually exclusive, one required):
--spec URL|FILE OpenAPI spec (JSON or YAML, local or remote)
--mcp URL MCP server URL (HTTP/SSE)
--mcp-stdio CMD MCP server command (stdio transport)
--graphql URL GraphQL endpoint URL
Options:
--auth-header K:V HTTP header (repeatable, value supports env:/file: prefixes)
--base-url URL Override base URL from spec
--transport TYPE MCP HTTP transport: auto|sse|streamable (default: auto)
--env KEY=VALUE Env var for stdio server process (repeatable)
--oauth Enable OAuth (authorization code + PKCE flow)
--oauth-client-id ID OAuth client ID (supports env:/file: prefixes)
--oauth-client-secret S OAuth client secret (supports env:/file: prefixes)
--oauth-scope SCOPE OAuth scope(s) to request
--cache-key KEY Custom cache key
--cache-ttl SECONDS Cache TTL (default: 3600)
--refresh Bypass cache
--list List available subcommands
--search PATTERN Search tools by name or description (implies --list)
--fields FIELDS Override GraphQL selection set (e.g. "id name email")
--pretty Pretty-print JSON output
--raw Print raw response body
--toon Encode output as TOON (token-efficient for LLMs)
--head N Limit output to first N records (arrays)
--version Show version
Bake mode:
bake create NAME [opts] Save connection settings as a named tool
bake list List all baked tools
bake show NAME Show config (secrets masked)
bake update NAME [opts] Update a baked tool
bake remove NAME Delete a baked tool
bake install NAME Create ~/.local/bin wrapper script
@NAME [args] Run a baked tool (e.g. mcp2cli @petstore --list)Subcommands and flags are generated dynamically from the source.
Patterns
Authentication
Always use env: or file: prefixes for secrets — never pass credentials as literal values in CLI flags. Literal values are visible in process listings and shell history.
# Secret from environment variable (recommended — avoids exposing in process list)
mcp2cli --spec ./spec.json --auth-header "Authorization:env:API_TOKEN" list-items
# Secret from file
mcp2cli --mcp https://mcp.example.com/sse \
--auth-header "x-api-key:file:/run/secrets/api_key" \
search --query "test"OAuth authentication (MCP HTTP only)
# Authorization code + PKCE (opens browser)
mcp2cli --mcp https://mcp.example.com/sse --oauth --list
# Client credentials (machine-to-machine)
mcp2cli --mcp https://mcp.example.com/sse \
--oauth-client-id env:OAUTH_CLIENT_ID --oauth-client-secret env:OAUTH_CLIENT_SECRET \
search --query "test"
# With scopes
mcp2cli --mcp https://mcp.example.com/sse --oauth --oauth-scope "read write" --listTokens are cached in ~/.cache/mcp2cli/oauth/ and refreshed automatically.
Transport selection (MCP HTTP only)
# Default: tries streamable HTTP, falls back to SSE
mcp2cli --mcp https://mcp.example.com/sse --list
# Force SSE transport (skip streamable HTTP attempt)
mcp2cli --mcp https://mcp.example.com/sse --transport sse --list
# Force streamable HTTP (no SSE fallback)
mcp2cli --mcp https://mcp.example.com/sse --transport streamable --listGraphQL
# Discover queries and mutations
mcp2cli --graphql https://api.example.com/graphql --list
# Run a query
mcp2cli --graphql https://api.example.com/graphql users --limit 10
# Run a mutation
mcp2cli --graphql https://api.example.com/graphql create-user --name "Alice" --email "alice@example.com"
# Override auto-generated selection set
mcp2cli --graphql https://api.example.com/graphql users --fields "id name email"
# With auth
mcp2cli --graphql https://api.example.com/graphql --auth-header "Authorization:env:API_TOKEN" usersTool search
# Filter tools by name or description (case-insensitive)
mcp2cli --mcp https://mcp.example.com/sse --search "task"
mcp2cli --spec ./openapi.json --search "create"
mcp2cli --graphql https://api.example.com/graphql --search "user"--search implies --list — shows only matching tools.
POST with JSON body from stdin
echo '{"name": "Fido", "tag": "dog"}' | mcp2cli --spec ./spec.json create-pet --stdinMultipart file uploads
When an OpenAPI spec declares multipart/form-data with format: binary fields, mcp2cli exposes them as file-path CLI arguments:
# Upload a file — binary fields accept local file paths
mcp2cli --spec ./spec.json upload-image --file /path/to/photo.png --caption "My photo"
# Non-binary fields in the same multipart schema become regular flags
mcp2cli --spec ./spec.json upload-image --file ./image.jpg --title "Cover" --alt-text "A sunset"File parameters show (file path) in --help output. MIME types are auto-detected from the file extension.
Env vars for stdio servers
mcp2cli --mcp-stdio "node server.js" --env API_KEY=env:API_SECRET_KEY --env DEBUG=1 search --query "test"Bake mode — saved configurations
Save connection settings as named configurations to avoid repeating flags:
# Create a baked tool
mcp2cli bake create petstore --spec https://api.example.com/spec.json \
--exclude "delete-*,update-*" --methods GET,POST --cache-ttl 7200
mcp2cli bake create mygit --mcp-stdio "npx @mcp/github" \
--include "search-*,list-*" --exclude "delete-*"
# Use with @ prefix
mcp2cli @petstore --list
mcp2cli @petstore list-pets --limit 10
# Manage
mcp2cli bake list
mcp2cli bake show petstore
mcp2cli bake update petstore --cache-ttl 3600
mcp2cli bake remove petstore
mcp2cli bake install petstore # creates ~/.local/bin/petstore wrapperFilter options: --include (glob whitelist), --exclude (glob blacklist), --methods (HTTP methods, OpenAPI only).
Configs stored in ~/.config/mcp2cli/baked.json (override with MCP2CLI_CONFIG_DIR).
Caching
Specs and MCP tool lists are cached in ~/.cache/mcp2cli/ (1h TTL). Local files are never cached.
mcp2cli --spec https://api.example.com/spec.json --refresh --list # Force refresh
mcp2cli --spec https://api.example.com/spec.json --cache-ttl 86400 --list # 24h TTLTOON output (token-efficient for LLMs)
mcp2cli --mcp https://mcp.example.com/sse --toon list-tagsBest for large uniform arrays — 40-60% fewer tokens than JSON.
Truncating large responses with --head
# Preview first 3 records from a potentially huge dataset
mcp2cli --spec ./spec.json list-records --head 3 --pretty--head N slices JSON arrays to the first N elements. Useful for datasets with oversized fields (e.g. geo_shape polygons at ~200KB per record).
Security
- Credentials: Always use
env:orfile:prefixes for secrets — never embed literal tokens or keys in commands. Theenv:prefix reads from environment variables;file:reads from a file path. - Trust boundary: mcp2cli connects to remote APIs and MCP servers specified by the user. Treat responses from external sources as untrusted — validate data before acting on it.
- Baked configs:
bake showmasks secrets in output. Baked configs are stored locally in~/.config/mcp2cli/baked.json— protect this file accordingly.
Generating a Skill from an API
When the user asks to create a skill from an MCP server, OpenAPI spec, or GraphQL endpoint, follow this workflow:
- Discover all available commands:
uvx mcp2cli --mcp https://target.example.com/sse --list - Inspect each command to understand parameters:
uvx mcp2cli --mcp https://target.example.com/sse <command> --help - Test key commands and probe for edge cases:
uvx mcp2cli --mcp https://target.example.com/sse <command> --param valueSpecifically test for:
- Large responses: use --head 3 to preview — do any fields produce oversized output (e.g. geo_shape, embedded blobs)? - Date/time fields: what format does the API expect? (ISO 8601, Unix timestamps, custom syntax like date'2022'?) - Pagination: does the API return all results or require --offset/--limit? - Error messages: what happens with invalid parameters? Are errors informative? - Binary vs text responses: do any endpoints return non-JSON (xlsx, parquet, images)? - Scope confusion: does the data contain more than expected (e.g. national data when you expect regional)?
- Bake the connection settings so the skill doesn't need to repeat flags:
uvx mcp2cli bake create myapi \ --mcp https://target.example.com/sse \ --auth-header "Authorization:Bearer env:MYAPI_TOKEN" \ --exclude "delete-*" --methods GET,POST - Install the wrapper into the skill's scripts directory:
uvx mcp2cli bake install myapi --dir.claude/skills/myapi/scripts/ - Create a SKILL.md in
.claude/skills/myapi/that teaches another AI agent how to use this API. The SKILL.md must go beyond--helpoutput — focus on knowledge that can only be learned through testing and reading documentation. Frontmatter:--- name: myapi description: Interact with the MyAPI service allowed-tools: Bash(bash *) ---Core Workflow (discovery + execution):# List available commands ${CLAUDE_SKILL_DIR}/scripts/myapi --list # Get help for a command ${CLAUDE_SKILL_DIR}/scripts/myapi <command> --help # Run a command ${CLAUDE_SKILL_DIR}/scripts/myapi <command> --param value --prettyBefore Querying checklist — include a decision framework: Anti-Patterns & Gotchas — document every surprise found during testing: Output Processing — use--prettyfor readable JSON,--headto limit results, or pipe tojqfor filtering:# Pretty-print results ${CLAUDE_SKILL_DIR}/scripts/myapi list-records --pretty # Limit large datasets ${CLAUDE_SKILL_DIR}/scripts/myapi list-records --head 5 # Filter with jq (pipe) ${CLAUDE_SKILL_DIR}/scripts/myapi list-records | jq '.[].name'Export Formats (if the API supports multiple output types): Knowledge Delta Principle: Do not duplicate parameter listings from--help. Instead, document which parameters actually matter for common tasks, default behaviors that are surprising, combinations that don't work, and rate limits or response size limits.
- What dataset/resource am I targeting? - Do I need pagination (--offset, --limit)? - Are there fields that produce large output I should exclude or truncate (--head)? - What date/filter format does this endpoint expect? - Date syntax quirks (e.g. date'2022' vs "2022") - Fields that produce oversized output (e.g. geo_shape → use --head to limit) - Parameter name inconsistencies across endpoints - Scope/filtering confusion (e.g. dataset contains national data, not just regional) - Binary export corruption risks (e.g. don't pipe binary formats through text encoding) - List supported formats (JSON, CSV, xlsx, parquet, etc.) - Note which are text-safe vs binary - For binary formats: ${CLAUDE_SKILL_DIR}/scripts/myapi export --format xlsx --raw > output.xlsx
The generated skill uses mcp2cli as its execution layer — the baked wrapper script handles all connection details so the SKILL.md stays clean and portable.