{
  "markdown": "<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/knowsuchagency/mcp2cli/main/assets/hero.png\" alt=\"mcp2cli — one CLI for every API\" width=\"700\">\n</p>\n\n<h1 align=\"center\">mcp2cli</h1>\n\n<p align=\"center\">\n  Turn any MCP server, OpenAPI spec, or GraphQL endpoint into a CLI — at runtime, with zero codegen.<br>\n  <strong>Save 96–99% of the tokens wasted on tool schemas every turn.</strong><br><br>\n  <a href=\"https://www.orangecountyai.com/blog/mcp2cli-one-cli-for-every-api-zero-wasted-tokens\"><strong>Read the full writeup →</strong></a>\n</p>\n\n## Install\n\n```bash\n# Run directly without installing\nuvx mcp2cli --help\n\n# Or install globally\nuv tool install mcp2cli\n```\n\n## AI Agent Skill\n\nmcp2cli ships with an installable [skill](https://skills.sh) that teaches AI coding agents (Claude Code, Cursor, Codex) how to use it. Once installed, your agent can discover and call any MCP server or OpenAPI endpoint — and even generate new skills from APIs.\n\n```bash\nnpx skills add knowsuchagency/mcp2cli --skill mcp2cli\n```\n\nAfter installing, try prompts like:\n- `mcp2cli --mcp https://mcp.example.com/sse` — interact with an MCP server\n- `mcp2cli create a skill for https://api.example.com/openapi.json` — generate a skill from an API\n\n## Usage\n\n### MCP HTTP/SSE mode\n\n```bash\n# Connect to an MCP server over HTTP\nmcp2cli --mcp https://mcp.example.com/sse --list\n\n# Call a tool\nmcp2cli --mcp https://mcp.example.com/sse search --query \"test\"\n\n# With auth header\nmcp2cli --mcp https://mcp.example.com/sse --auth-header \"x-api-key:sk-...\" \\\n  query --sql \"SELECT 1\"\n\n# Force a specific transport (skip streamable HTTP fallback dance)\nmcp2cli --mcp https://mcp.example.com/sse --transport sse --list\n\n# Search tools by name or description (case-insensitive substring match)\nmcp2cli --mcp https://mcp.example.com/sse --search \"task\"\n```\n\n`--search` implies `--list` and works across all modes (`--mcp`, `--spec`, `--graphql`, `--mcp-stdio`).\n\n### OAuth authentication\n\nAPIs that require OAuth are supported out of the box — across MCP, OpenAPI, and GraphQL modes.\nmcp2cli handles token acquisition, caching, and refresh automatically.\n\n```bash\n# Authorization code + PKCE flow (opens browser for login)\nmcp2cli --mcp https://mcp.example.com/sse --oauth --list\nmcp2cli --spec https://api.example.com/openapi.json --oauth --list\nmcp2cli --graphql https://api.example.com/graphql --oauth --list\n\n# Client credentials flow (machine-to-machine, no browser)\nmcp2cli --spec https://api.example.com/openapi.json \\\n  --oauth-client-id \"my-client-id\" \\\n  --oauth-client-secret \"my-secret\" \\\n  list-pets\n\n# With specific scopes\nmcp2cli --graphql https://api.example.com/graphql --oauth --oauth-scope \"read write\" users\n\n# Local spec file — use --base-url for OAuth discovery\nmcp2cli --spec ./openapi.json --base-url https://api.example.com --oauth --list\n```\n\nTokens are persisted in `~/.cache/mcp2cli/oauth/` so subsequent calls reuse existing tokens\nand refresh automatically when they expire.\n\n#### Headless hosts — no browser on the machine running mcp2cli\n\nThe default authorization-code flow starts a callback server on `127.0.0.1`, which only\nworks when the browser runs on the same machine. On a VPS over SSH or in a container,\nadd `--oauth-manual-callback`: mcp2cli prints the authorization URL instead of opening a\nbrowser, and reads the redirect back from stdin.\n\n```bash\nmcp2cli --mcp https://mcp.linear.app/mcp --oauth --oauth-manual-callback --list\n```\n\nOpen the printed URL in a browser on any machine, authorize, then paste the URL you land\non. That page will fail to load — nothing is listening on the loopback port — which is\nexpected; only its address matters, because it carries the `code` and `state` parameters.\nPKCE and state verification are unchanged, so paste the URL unmodified.\n\n### Secrets from environment or files\n\nSensitive values (`--auth-header` values, `--oauth-client-id`, `--oauth-client-secret`) support\n`env:` and `file:` prefixes to avoid passing secrets as CLI arguments (which are visible in\nprocess listings):\n\n```bash\n# Read from environment variable\nmcp2cli --mcp https://mcp.example.com/sse \\\n  --auth-header \"Authorization:env:MY_API_TOKEN\" \\\n  --list\n\n# Read from file\nmcp2cli --mcp https://mcp.example.com/sse \\\n  --oauth-client-secret \"file:/run/secrets/client_secret\" \\\n  --oauth-client-id \"my-client-id\" \\\n  --list\n\n# Works with secret managers that inject env vars\nfnox exec -- mcp2cli --mcp https://mcp.example.com/sse \\\n  --oauth-client-id \"env:OAUTH_CLIENT_ID\" \\\n  --oauth-client-secret \"env:OAUTH_CLIENT_SECRET\" \\\n  --list\n```\n\n### MCP stdio mode\n\n```bash\n# List tools from an MCP server\nmcp2cli --mcp-stdio \"npx @modelcontextprotocol/server-filesystem /tmp\" --list\n\n# Call a tool\nmcp2cli --mcp-stdio \"npx @modelcontextprotocol/server-filesystem /tmp\" \\\n  read-file --path /tmp/hello.txt\n\n# Pass environment variables to the server process\nmcp2cli --mcp-stdio \"node server.js\" --env API_KEY=sk-... --env DEBUG=1 \\\n  search --query \"test\"\n```\n\n### MCP roots and completion\n\nExpose one or more filesystem roots when a server scopes operations to a\nworkspace. Paths are converted to `file://` URIs; explicit roots must also use\nthe `file://` scheme.\n\n```bash\nmcp2cli --mcp-stdio \"npx @modelcontextprotocol/server-filesystem /tmp\" \\\n  --root \"$PWD\" --root file:///var/shared --list\n```\n\nRequest prompt-argument or resource-template completions with\n`REF:ARG=PREFIX`:\n\n```bash\nmcp2cli --mcp https://example.com/mcp \\\n  --complete \"greeting:name=San\"\nmcp2cli --mcp https://example.com/mcp \\\n  --complete \"file:///docs/{topic}:topic=api\"\n```\n\nBoth options work when starting a persistent session; roots are retained by\nthe session daemon and completion requests can be sent through `--session`.\n\n### OpenAPI mode\n\n```bash\n# List all commands from a remote spec\nmcp2cli --spec https://petstore3.swagger.io/api/v3/openapi.json --list\n\n# Call an endpoint\nmcp2cli --spec ./openapi.json --base-url https://api.example.com list-pets --status available\n\n# With auth\nmcp2cli --spec ./spec.json --auth-header \"Authorization:Bearer tok_...\" create-item --name \"Test\"\n\n# POST with JSON body from stdin\necho '{\"name\": \"Fido\", \"tag\": \"dog\"}' | mcp2cli --spec ./spec.json create-pet --stdin\n\n# Local YAML spec\nmcp2cli --spec ./api.yaml --base-url http://localhost:8000 --list\n```\n\n### GraphQL mode\n\n```bash\n# List all queries and mutations from a GraphQL endpoint\nmcp2cli --graphql https://api.example.com/graphql --list\n\n# Call a query\nmcp2cli --graphql https://api.example.com/graphql users --limit 10\n\n# Call a mutation\nmcp2cli --graphql https://api.example.com/graphql create-user --name \"Alice\" --email \"alice@example.com\"\n\n# Override auto-generated selection set fields\nmcp2cli --graphql https://api.example.com/graphql users --fields \"id name email\"\n\n# With auth\nmcp2cli --graphql https://api.example.com/graphql --auth-header \"Authorization:Bearer tok_...\" users\n```\n\nmcp2cli introspects the endpoint, discovers queries and mutations, auto-generates selection sets, and constructs parameterized queries with proper variable declarations. No SDL parsing, no code generation — just point and run.\n\n### Bake mode — save connection settings\n\nTired of repeating `--spec`/`--mcp`/`--mcp-stdio` plus auth flags on every invocation? Bake them into a named configuration:\n\n```bash\n# Create a baked tool from an OpenAPI spec\nmcp2cli bake create petstore --spec https://api.example.com/spec.json \\\n  --exclude \"delete-*,update-*\" --methods GET,POST --cache-ttl 7200\n\n# Create a baked tool from an MCP stdio server\nmcp2cli bake create mygit --mcp-stdio \"npx @mcp/github\" \\\n  --include \"search-*,list-*\" --exclude \"delete-*\"\n\n# Use a baked tool with @ prefix — no connection flags needed\nmcp2cli @petstore --list\nmcp2cli @petstore list-pets --limit 10\nmcp2cli @mygit search-repos --query \"rust\"\n\n# Manage baked tools\nmcp2cli bake list                         # show all baked tools\nmcp2cli bake show petstore                # show config (secrets masked)\nmcp2cli bake update petstore --cache-ttl 3600\nmcp2cli bake remove petstore\nmcp2cli bake install petstore             # creates ~/.local/bin/petstore wrapper\nmcp2cli bake install petstore --dir ./scripts/  # install wrapper to custom directory\n```\n\nFiltering options:\n- `--include` — comma-separated glob patterns to whitelist tools (e.g. `\"list-*,get-*\"`)\n- `--exclude` — comma-separated glob patterns to blacklist tools (e.g. `\"delete-*\"`)\n- `--methods` — comma-separated HTTP methods to allow (e.g. `\"GET,POST\"`, OpenAPI only)\n\nConfigs are stored in `~/.config/mcp2cli/baked.json`. Override with `MCP2CLI_CONFIG_DIR`.\n\n### Usage-aware tool ranking\n\nmcp2cli tracks tool invocations locally and uses that data to rank `--list` output, reducing token costs for LLM agents working with large servers.\n\n```bash\n# Default --list: ~1,400 tokens for 96 tools\nmcp2cli @myapi --list\n\n# Top 10 most-used tools, names only: ~20 tokens\nmcp2cli @myapi --list --top 10 --compact\n\n# Sort by most recently used\nmcp2cli @myapi --list --sort recent\n\n# Alphabetical sort\nmcp2cli @myapi --list --sort alpha\n```\n\nWhen usage data exists for a source, `--list` defaults to sorting by call frequency. Otherwise insertion order is preserved. Usage data is stored in `~/.cache/mcp2cli/usage.json`.\n\n### JSON output\n\n`--json` forces **valid JSON on stdout for every command**, in every mode. It is the\nmachine-readable counterpart to the human-formatted default output, designed for LLM\nagents and scripts that need to parse results reliably.\n\n```bash\n# --list emits a JSON array of command objects (name, description, parameters, ...)\nmcp2cli --mcp https://mcp.example.com/sse --list --json\nmcp2cli --spec ./openapi.json --list --json\nmcp2cli --graphql https://api.example.com/graphql --list --json\n\n# --list --json --compact emits a JSON array of names only\nmcp2cli --mcp https://mcp.example.com/sse --list --json --compact\n\n# MCP tool calls emit the FULL CallToolResult envelope — including\n# structuredContent and isError, not just the flattened text. This surfaces the\n# machine-readable result that modern MCP tools put in structuredContent.\nmcp2cli --mcp https://mcp.example.com/sse --json search --query \"test\"\n# { \"content\": [...], \"structuredContent\": {...}, \"isError\": false }\n\n# OpenAPI / GraphQL calls emit the response as JSON (non-JSON bodies become a JSON string)\nmcp2cli --spec ./openapi.json --json list-pets\nmcp2cli --graphql https://api.example.com/graphql --json users\n```\n\n`--json` takes precedence over `--raw` and `--toon` (both of which can produce\nnon-JSON), so it always wins — that is what makes it a reliable \"force JSON\" switch.\nIndentation follows the usual rule: pretty on a TTY or with `--pretty`, compact when piped.\n\n### Output control\n\n```bash\n# Pretty-print JSON (also auto-enabled for TTY)\nmcp2cli --spec ./spec.json --pretty list-pets\n\n# Raw response body (no JSON parsing)\nmcp2cli --spec ./spec.json --raw get-data\n\n# Truncate large responses to first N records\nmcp2cli --spec ./spec.json list-records --head 5\n\n# Pipe-friendly (compact JSON when not a TTY)\nmcp2cli --spec ./spec.json list-pets | jq '.[] | .name'\n\n# TOON output — token-efficient encoding for LLM consumption\n# Best for large uniform arrays (40-60% fewer tokens than JSON)\nmcp2cli --mcp https://mcp.example.com/sse --toon list-tags\n```\n\n### Caching\n\nSpecs and MCP tool lists are cached in `~/.cache/mcp2cli/` with a 1-hour TTL by default.\n\n```bash\n# Force refresh\nmcp2cli --spec https://api.example.com/spec.json --refresh --list\n\n# Custom TTL (seconds)\nmcp2cli --spec https://api.example.com/spec.json --cache-ttl 86400 --list\n\n# Custom cache key\nmcp2cli --spec https://api.example.com/spec.json --cache-key my-api --list\n\n# Override cache directory\nMCP2CLI_CACHE_DIR=/tmp/my-cache mcp2cli --spec ./spec.json --list\n```\n\nLocal file specs are never cached.\n\n## CLI reference\n\n```\nmcp2cli [global options] <subcommand> [command options]\n\nSource (mutually exclusive, one required):\n  --spec URL|FILE       OpenAPI spec (JSON or YAML, local or remote)\n  --mcp URL             MCP server URL (HTTP/SSE)\n  --mcp-stdio CMD       MCP server command (stdio transport)\n  --graphql URL         GraphQL endpoint URL\n\nOptions:\n  --auth-header K:V       HTTP header (repeatable, value supports env:/file: prefixes)\n  --base-url URL          Override base URL from spec\n  --transport TYPE        MCP HTTP transport: auto|sse|streamable (default: auto)\n  --env KEY=VALUE         Env var for MCP stdio server (repeatable)\n  --root PATH|FILE_URI    Expose a filesystem root to an MCP server (repeatable)\n  --complete SPEC         Complete an MCP prompt or resource-template argument\n  --oauth                 Enable OAuth (authorization code + PKCE flow)\n  --oauth-client-id ID    OAuth client ID (supports env:/file: prefixes)\n  --oauth-client-secret S OAuth client secret (supports env:/file: prefixes)\n  --oauth-scope SCOPE     OAuth scope(s) to request\n  --oauth-manual-callback Print the auth URL and read the redirect from stdin\n                          (for hosts with no reachable browser)\n  --cache-key KEY         Custom cache key\n  --cache-ttl SECONDS     Cache TTL (default: 3600)\n  --refresh               Bypass cache\n  --list                  List available subcommands\n  --search PATTERN        Search tools by name or description (implies --list)\n  --sort MODE             Sort --list output: usage|recent|alpha|default\n  --top N                 Show only the top N tools in --list output\n  --compact               Space-separated tool names only, no descriptions\n  --verbose               Show full tool descriptions (unwrapped)\n  --fields FIELDS         Override GraphQL selection set (e.g. \"id name email\")\n  --pretty                Pretty-print JSON output\n  --raw                   Print raw response body\n  --json                  Force valid JSON output for every command (--list and tool\n                          calls). MCP calls emit the full result envelope including\n                          structuredContent. Takes precedence over --raw and --toon.\n  --toon                  Encode output as TOON (token-efficient for LLMs)\n  --head N                Limit output to first N records (arrays)\n  --version               Show version\n\nBake mode:\n  bake create NAME [opts]   Save connection settings as a named tool\n  bake list                 List all baked tools\n  bake show NAME            Show config (secrets masked)\n  bake update NAME [opts]   Update a baked tool\n  bake remove NAME          Delete a baked tool\n  bake install NAME         Create ~/.local/bin wrapper script\n  @NAME [args]              Run a baked tool (e.g. mcp2cli @petstore --list)\n```\n\nSubcommands and their flags are generated dynamically from the spec or MCP server tool definitions. Run `<subcommand> --help` for details.\n\n> For token savings analysis, architecture details, and comparison to Anthropic's Tool Search, see the **[full writeup on the OCAI blog](https://www.orangecountyai.com/blog/mcp2cli-one-cli-for-every-api-zero-wasted-tokens)**.\n\n## Development\n\n```bash\n# Install with test + MCP deps\nuv sync --extra test\n\n# Run tests\nuv run pytest tests/ -v\n\n# Run just the token savings tests\nuv run pytest tests/test_token_savings.py -v -s\n```\n\n### MCP SDK compatibility\n\nmcp2cli works with **both major versions** of the MCP Python SDK (`mcp>=1.26,<3`),\nso it never forces a resolver conflict with other tools in the same environment.\nCI runs the suite against the declared floor, the latest 1.x, and the latest 2.x.\n\nThe two majors differ in ways that matter to a client — v2 renamed model fields\nto snake_case, replaced `streamablehttp_client`, moved to `httpx2`, and dropped\nthe session-id element from the transport tuple. Those differences are confined\nto a handful of helpers (`_mcp_attr`, `_mcp_dump`, `_streamable_streams`,\n`_list_tools_page`, `_resource_uri`, `_authorization_code_result`); the rest of\nthe codebase is version-agnostic. The test fixtures speak the JSON-RPC wire\nprotocol directly and import no SDK, so they hold across majors.\n\n---\n\n## License\n\n[MIT](LICENSE)\n",
  "bytes": 16047,
  "sha": "67dfbb819d6eb55204a381aef8103e4d59b5286e7a87add05767c8fae9670e61",
  "repo_slug": "knowsuchagency/mcp2cli",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/skl_knowsuchagency_mcp2cli_mcp2cli_c2c5fc53/readme"
}