Model or dataset
knowsuchagency/mcp2cli avatar
knowsuchagency/mcp2cli

mcp2cli: Turn an MCP, OpenAPI or GraphQL Server into a CLI at Runtime

Turn any MCP, OpenAPI, or GraphQL server into a CLI — at runtime, with zero codegen

2,408 stars179 forksPythonMIT

At a glance

What is it?
mcp2cli wraps MCP servers, OpenAPI specs and GraphQL endpoints behind one command line interface without generating code. It is a thin runtime adapter, and the trade-offs show up in OAuth, sessions and schema fidelity.
Who is it for?
Adopt mcp2cli if you want to call an MCP server, an OpenAPI spec or a GraphQL endpoint from a shell or an agent without writing a client, and if your endpoint is reachable over HTTP or can be started with --mcp-stdio. Do not adopt it if you need generated typed clients, offline operation against a spec with no live server, or full coverage of every OpenAPI and GraphQL construct.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 18 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem mcp2cli solves for agents and shell users

Tool schemas cost tokens. The README states mcp2cli can "Save 96-99% of the tokens wasted on tool schemas every turn." That is the pitch: instead of loading every tool definition into a model's context, you give the model a shell and let it call one CLI that discovers operations at runtime. mcp2cli is aimed at two audiences. The first is engineers who want to poke at an MCP server, an OpenAPI spec or a GraphQL endpoint from a terminal without writing a client. The second is AI coding agents: the repository ships an installable skill for Claude Code, Cursor and Codex, so an agent can discover and call an API through the same binary. Three transports are covered by one entry point. MCP servers are reached over HTTP or SSE with --mcp, or started as a subprocess with --mcp-stdio. OpenAPI documents are read from a URL or a local JSON or YAML file with --spec. GraphQL endpoints are introspected with --graphql. The common thread is that nothing is compiled ahead of time; the command surface is built from the remote description each time you run it.

How mcp2cli discovers commands at runtime

The mechanism is introspection plus argument mapping. In MCP mode, mcp2cli connects to the server and asks for its tool list; in OpenAPI mode it fetches the spec and derives one subcommand per operation; in GraphQL mode it introspects the endpoint and "auto-generates selection sets" for queries and mutations. The README notes that --search implies --list and works across all four modes (--mcp, --spec, --graphql, --mcp-stdio), matching tool names and descriptions with a case-insensitive substring match. Mapping is name-based: a tool called search becomes the subcommand search, and --query "test" becomes an argument. OpenAPI bodies can come from stdin, as in echo '{"name": "Fido"}' | mcp2cli --spec ./spec.json create-pet --stdin. GraphQL selection sets are generated automatically but can be overridden with --fields "id name email", which matters when the generated set is too wide. For MCP there is more than listing: --root exposes filesystem roots as file:// URIs, and --complete sends prompt-argument or resource-template completion requests using a REF:ARG=PREFIX form. Both of those depend on a persistent session, because "roots are retained by the session daemon and completion requests can be sent through --session". That daemon is the piece most users will not notice until they need it.

Installing mcp2cli and making a first call

The README gives two install paths. uvx runs the tool without installing it, and uv tool install puts the mcp2cli command on your path. The package is published on PyPI, and pyproject.toml sets requires-python to >=3.10, so an older interpreter will not work.

bash
uvx mcp2cli --help
uv tool install mcp2cli

With the binary available, the fastest real use is listing the tools of a public MCP server. The README uses a placeholder host, so substitute your own endpoint. --list prints the discovered tools and exits.

bash
mcp2cli --mcp https://mcp.example.com/sse --list

Once you have a tool name, call it by passing arguments as flags. The README shows a search tool taking a --query flag, and a --search flag that filters the tool list by name or description.

bash
mcp2cli --mcp https://mcp.example.com/sse search --query "test"
mcp2cli --mcp https://mcp.example.com/sse --search "task"

The same pattern applies to a local OpenAPI file. Here a YAML spec is loaded from disk, --base-url supplies the host the spec omits, and --list shows the derived commands.

bash
mcp2cli --spec ./api.yaml --base-url http://localhost:8000 --list

If the server needs a key, pass it as a header. The README supports env: and file: prefixes on secret values so credentials do not land in the process listing.

bash
mcp2cli --mcp https://mcp.example.com/sse \
  --auth-header "Authorization:env:MY_API_TOKEN" \
  --list

For stdio servers, the server command is a single quoted string and environment variables are passed with repeated --env flags.

bash
mcp2cli --mcp-stdio "node server.js" --env API_KEY=sk-... --env DEBUG=1 \
  search --query "test"

What you should see in each case is a command list for --list, or the tool's raw result for a call. There is no config file to write and no generated client to commit.

OAuth, secrets and the headless callback problem

OAuth is handled in all three modes, and the README says mcp2cli "handles token acquisition, caching, and refresh automatically". Tokens are persisted under ~/.cache/mcp2cli/oauth/, so a second invocation reuses them. Two flows are documented. Authorization code with PKCE opens a browser, which is fine on a laptop. Client credentials is machine-to-machine and takes --oauth-client-id and --oauth-client-secret. Scopes go in --oauth-scope. The interesting constraint is the callback. The default flow starts a callback server on 127.0.0.1, which only works when the browser runs on the same machine as mcp2cli. On a VPS over SSH or inside a container that assumption breaks, and the README's answer is --oauth-manual-callback: mcp2cli prints the authorization URL instead of opening a browser and reads the redirect back from stdin. The documented workflow is slightly awkward on purpose. You open the printed URL elsewhere, authorize, and paste back the URL you land on. That page will fail to load because nothing is listening on the loopback port, and the README says this is expected; only the address matters, since it carries the code and state parameters. The README also warns to paste the URL unmodified, because PKCE and state verification still run. For a local spec file, OAuth discovery needs a host, which is what --base-url is for. Secret handling is separate from OAuth and applies to --auth-header, --oauth-client-id and --oauth-client-secret, all of which accept env: and file: prefixes. That is a deliberate response to the fact that CLI arguments show up in process listings.

Where mcp2cli is the wrong tool

The runtime approach has a cost, and it is worth being blunt about it. Nothing is generated, so there are no typed client classes, no IDE autocompletion for a specific API, and no compile-time check that your arguments match the schema. Every invocation re-fetches or re-reads the description, which is the point, but it also means mcp2cli needs the endpoint to be reachable when you run it. OpenAPI mode accepts a local file, yet the README's own examples pair a local spec with --base-url, because the requests still go to a live host. If your environment is air-gapped or your spec describes a service you cannot reach, this tool does not help. GraphQL users should also expect the generated selection set to be a starting point rather than a finished query; --fields exists precisely because the automatic set is not always what you want. And the MCP features that go beyond simple calls, roots and completions, are tied to the session daemon, so anything that needs those has a longer-lived process to manage. Finally, the dependency floor is Python 3.10 and mcp>=1.26,<3, which is a real constraint for teams pinned to older interpreters. None of this is hidden in the README, but none of it is framed as a limitation either.

Alternatives: mcporter and hand-written clients

The closest thing in the search data is mcporter, which people look up alongside mcp2cli (queries include "Mcporter skill" and "Npm mcporter"). The distinction that matters is scope. mcp2cli covers MCP, OpenAPI and GraphQL through one binary, and its README frames the value as token savings for agents that would otherwise carry tool schemas in context. A tool focused on MCP alone solves a narrower problem and will not read an OpenAPI document or introspect a GraphQL endpoint. The other alternative is the obvious one: write a small client with the official SDKs. That gives you typed arguments, retries you control, and no runtime introspection step. What it costs is the thing mcp2cli exists to remove, namely a code change every time the remote API changes. If your API surface is stable and small, a hand-written client is less machinery. If you are exploring many servers, or you want an agent to discover capabilities without a rebuild, the runtime approach is the one that scales.

Licence, maintenance and upgrade cost

mcp2cli is MIT licensed, and pyproject.toml declares license = "MIT" with the author listed as Stephan Fitzpatrick. MIT is permissive: you can use, modify and redistribute it, including in commercial work, provided the copyright notice and permission notice travel with it. That is a description of the licence text, not legal advice; check it against your own policy. The repository is not archived, and the last push was on 2026-09-09, which is recent. The version in pyproject.toml is 3.7.0. Upgrade cost is low by design: there is no generated code to regenerate, so a new release does not leave stale artifacts in your tree. The dependencies are httpx, pyyaml and mcp, and the mcp range is >=1.26,<3, so a major bump of the MCP SDK is the most likely source of breakage. Because the tool reads remote schemas at runtime, a change in a remote API shows up on your next invocation rather than at build time. That cuts both ways: no rebuild, but also no early warning. Pinning the version in CI is the cheap mitigation, and the test extra (pytest, pytest-asyncio, tiktoken) is available if you want to run the repository's own suite.

Editorial conclusion

Adopt mcp2cli if you want to call an MCP server, an OpenAPI spec or a GraphQL endpoint from a shell or an agent without writing a client, and if your endpoint is reachable over HTTP or can be started with --mcp-stdio. Do not adopt it if you need generated typed clients, offline operation against a spec with no live server, or full coverage of every OpenAPI and GraphQL construct. Before relying on it, verify three things against your own endpoint: that --list produces every operation you expect, that your auth path works (--auth-header, env: or file:, or --oauth with --oauth-manual-callback on a headless host), and that your Python is 3.10 or newer.

Frequently asked questions

How can I convert an MCP server to a CLI with mcp2cli?

Point mcp2cli at the server with --mcp for an HTTP or SSE endpoint, or --mcp-stdio with the server command for a subprocess, then run --list to see the discovered tools. Each tool becomes a subcommand you call with flags, and no code generation step is involved.

How is MCP different from an API in mcp2cli?

mcp2cli treats them as separate modes. MCP servers are contacted with --mcp or --mcp-stdio and expose tools discovered at connect time, while an API is described by an OpenAPI document passed with --spec or by a GraphQL endpoint passed with --graphql. The README presents all of them behind the same command surface.

What does MCP stand for in mcp2cli?

The repository does not spell the acronym out in the README or pyproject.toml. What it does document is that mcp2cli connects to MCP servers over HTTP or SSE with --mcp, or starts them as subprocesses with --mcp-stdio.

Is MCP just a JSON format in mcp2cli?

The README does not describe MCP as a JSON format. It documents MCP as a protocol reached over HTTP or SSE with --mcp or as a subprocess with --mcp-stdio, and JSON does appear in mcp2cli's OpenAPI mode, where a POST body can be piped in with --stdin.

Official sources

  1. Issues
  2. knowsuchagency/mcp2cli on GitHub
  3. License: MIT
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/knowsuchagency-mcp2cli.svg)](https://hysenlabs.com/projects/knowsuchagency-mcp2cli)