# Anyapi Mcp

> One MCP server for all the APIs your model needs (OpenAPI, GraphQL, SOAP/WSDL, atproto). Sandboxed code execution keeps your context clean.

- **Type:** MCP server
- **Install:** `agentstack add mcp-gabrielbauman-anyapi-mcp`
- **Verified:** Pending review
- **Seller:** [gabrielbauman](https://agentstack.voostack.com/s/gabrielbauman)
- **Installs:** 0
- **Category:** [Integrations](https://agentstack.voostack.com/c/integrations)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [gabrielbauman](https://github.com/gabrielbauman)
- **Source:** https://github.com/gabrielbauman/anyapi-mcp
- **Website:** https://gabrielbauman.github.io/anyapi-mcp/

## Install

```sh
agentstack add mcp-gabrielbauman-anyapi-mcp
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# anyapi-mcp

**anyapi-mcp is one local MCP server that lets your model talk to almost any
HTTP API.** Add it once to Claude Code or Claude Desktop, then point it at any
API you find (an OpenAPI spec, a GraphQL endpoint, a SOAP/WSDL service, or an AT
Protocol/XRPC service like Bluesky) and your model can search and call it right
away. No separate server or custom integration per API: one server covers
everything you register, and it stays token-efficient however many calls a task
takes.

Under the hood it generates a typed client from the API's own description and
exposes a small set of tools: `search` to find operations, `execute` to run a
short TypeScript program against that client, and `add_api`/`list_apis`/
`remove_api` to manage what's registered. Instead of one MCP tool per endpoint
clogging the context window, the model writes code and runs it in a locked-down
sandbox; intermediate results stay in that subprocess rather than round-tripping
through the model, so a ten-step workflow costs one tool call and a handful of
tokens, not ten.

Because that client is fully typed, the model drives the API without guesswork:
a wrong argument is a type error, caught before any request leaves the machine.

Point an MCP client at `anyapi-mcp serve` with **nothing registered yet** and
the server explains what it is and how to register one, so an empty server
self-onboards instead of dead-ending. APIs added mid-session work immediately,
no restart.

```sh
anyapi-mcp add https://example.com/openapi.json --docs https://example.com/docs --token
anyapi-mcp list
anyapi-mcp serve            # stdio MCP server, for Claude Desktop / Claude Code
```

## Requirements

- [Deno](https://deno.com/) **2.x** (tested on 2.8).
- macOS or Linux.
  - macOS: tokens are stored in the login keychain via `security`.
  - Linux: tokens are stored via `secret-tool` - install `libsecret-tools` (e.g.
    `apt-get install libsecret-tools`).
- `execute` runs the model's code in a `deno` subprocess, so **`deno` must be on
  `PATH`** - including when you run the compiled binary (which is otherwise
  self-contained).

## Install

The quickest way - builds from source, so you'll need [Deno](https://deno.com/)
**2.x** (it's a runtime dependency too; see [Requirements](#requirements)):

```sh
curl -fsSL https://gabrielbauman.github.io/anyapi-mcp/install.sh | sh
```

It fetches the source, runs `deno task compile`, drops an `anyapi-mcp` binary in
`~/.local/bin` (override the location with `ANYAPI_MCP_BIN_DIR`), and registers
it with Claude Code, Claude Desktop, and OpenCode so it's ready to use (skip
that last step with `ANYAPI_MCP_NO_INSTALL=1`).

Or build it yourself:

```sh
deno task compile          # produces a self-contained ./anyapi-mcp binary
mv ./anyapi-mcp ~/.local/bin/   # or anywhere on your PATH
anyapi-mcp install              # register it with Claude Code, Claude Desktop, and OpenCode
```

Or run from source during development:

```sh
deno task dev list         # = deno run -A src/main.ts list
```

## Commands

### `add  [options]`

Inspects the source (an OpenAPI spec, a GraphQL endpoint, a WSDL, or an atproto
PDS/service URL), derives the base URL and host, builds a searchable operation
index, generates a typed client, and writes a registry entry.

| Option                                     | Description                                                                                                                                                |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--kind ` | Protocol (default: `openapi`). `graphql` introspects an endpoint; `soap` reads a WSDL URL; `atproto` takes a PDS/service URL (e.g. `https://bsky.social`). |
| `--id `                              | Id used on the CLI and in `execute` (default: the base URL in reverse-DNS form, e.g. `com.github.api`).                                                    |
| `--name `                            | Human-friendly name (default: spec `info.title`).                                                                                                          |
| `--base-url `                         | Override the base URL (otherwise derived from the spec's document-, path-, or operation-level `servers`).                                                  |
| `--docs `                             | Documentation URL to store and surface (not parsed).                                                                                                       |
| `--token`                                  | Store a bearer token. Read without echo from a TTY, or piped via stdin.                                                                                    |
| `--identifier `                    | atproto: the account handle/email to authenticate as (not a secret).                                                                                       |
| `--app-password`                           | atproto: store an app password now. Read without echo from a TTY, or piped via stdin.                                                                      |
| `--oauth`                                  | Treat the API as OAuth 2.0 even if the spec doesn't declare it.                                                                                            |
| `--auth-url` / `--token-url`               | OAuth authorize / token endpoints (override the spec's values).                                                                                            |
| `--scope `                           | Scope to request at login (repeatable; default: the spec's scopes).                                                                                        |
| `--scope-separator `                  | Scope separator in the authorize URL (default `" "`; Strava uses `","`).                                                                                   |
| `--no-auth`                                | Register without authentication.                                                                                                                           |
| `--force`                                  | Overwrite an existing API with the same id instead of failing (e.g. to fix a wrong base URL).                                                              |

If the base URL can't be derived and would fall back to a raw spec-hosting host
(e.g. `raw.githubusercontent.com`), `add` fails loudly instead of registering a
broken base — pass `--base-url` with the real API origin.

OpenAPI specs that declare an **OAuth 2.0 authorization-code flow are detected
automatically**: the API is registered as `oauth2`, and you run
[`login`](#login-id-options) once to authenticate (see [OAuth](#oauth-apis)).

```sh
# bearer auth, prompted without echo:
anyapi-mcp add https://api.github.com/openapi.json --token

# bearer auth, piped (CI):
echo "$GITHUB_TOKEN" | anyapi-mcp add  --token

# no auth - id defaults to the reverse-DNS base URL (here: io.swagger.petstore3.api.v3):
anyapi-mcp add https://petstore3.swagger.io/api/v3/openapi.json

# OAuth is auto-detected from the spec; just add, then `login`:
anyapi-mcp add https://developers.strava.com/swagger/swagger.json --id com.strava.api

# graphql - introspect an endpoint (id: com.trevorblades.countries):
anyapi-mcp add https://countries.trevorblades.com/ --kind graphql

# soap - read a public WSDL pointing at a live service:
anyapi-mcp add "http://www.dneonline.com/calculator.asmx?WSDL" --kind soap

# atproto - anonymous public reads (no login), via the public AppView:
anyapi-mcp add https://public.api.bsky.app --kind atproto --id bsky-public

# atproto - register your account to post / read private data (PDS + login):
anyapi-mcp add https://bsky.social --kind atproto --id bsky --identifier you.bsky.social
anyapi-mcp login bsky   # prompts for an app password (no echo)
```

### `list`

Lists registered APIs with id, name, base URL, operation count, and auth kind.
For OAuth APIs it also shows live login status and token expiry (e.g.
`oauth2 (logged in, expires in 5h)` or `oauth2 (not logged in)`).

### `regenerate [id ...]`

Rebuilds the generated client code (typed `.d.ts`/client module + operation
index) for registered APIs by re-fetching each one's saved source. With no id it
does all of them; pass ids to target specific APIs, or `--stale-only` to rebuild
just those whose code predates the current build.

This only refreshes generated code: **saved credentials and OAuth config are
left untouched** (bearer tokens, OAuth client credentials, and login state all
survive; baseUrl and the host allowlist are preserved too). Run it after
upgrading `anyapi-mcp` so cached code matches the new generators - though
`serve` also does this automatically (see below), so you rarely need to run it
by hand. Each API is independent: if one source can't be re-fetched, the rest
still regenerate and that API keeps its existing (working) code.

### `login  [options]`

Authenticates an OAuth 2.0 API in the browser (see [OAuth](#oauth-apis)). Stores
the OAuth app credentials in the keystore (the client secret is read without
echo, like `add --token`), opens the provider's consent page, captures the
redirect on a local one-shot callback server, and saves the resulting tokens
(which then refresh automatically). Re-running it re-authenticates.

| Option                       | Description                                                                 |
| ---------------------------- | --------------------------------------------------------------------------- |
| `--client-id `           | OAuth app client id (required on first login).                              |
| `--client-secret `        | Client secret (omit to be prompted without echo; or pipe via stdin).        |
| `--scope `             | Scope to request (repeatable; default: the API's configured scopes).        |
| `--scope-separator `    | Scope separator in the authorize URL (default `" "`; Strava uses `","`).    |
| `--redirect-uri `       | Local callback URL to listen on (default `http://localhost:9876/callback`). |
| `--port `                 | Shortcut to set the callback port (host `localhost`, path `/callback`).     |
| `--auth-url` / `--token-url` | Override the stored authorize / token endpoint.                             |
| `--no-browser`               | Print the authorize URL instead of opening a browser (headless/SSH).        |

### `logout  [--forget-client]`

Removes the stored OAuth tokens for an API (it stays registered). By default the
OAuth app credentials are kept so the next `login` needs no flags;
`--forget-client` removes those too.

### `remove `

Removes the registry entry, deletes stored secrets from the keystore (bearer
token, or OAuth client credentials + tokens), and cleans up cached files.

### `serve`

Runs the stdio MCP server. It re-reads the registry on each call (so newly
registered APIs are picked up without a restart). On startup, if the build's
codegen version has changed since an API's code was generated (i.e. you upgraded
`anyapi-mcp`), it regenerates that API's code first - the same work as
`regenerate`, limited to stale entries, with credentials preserved and any
re-fetch failure logged but non-fatal. It exposes:

- **`search`** - `{ query, api? }` → compact operation matches (`api`, `method`,
  `path`, `operationId`, `summary`, `params`, `requestBodyHint`). Each param
  also carries a `description` and its allowed `enum` values when the spec
  provides them, so the model can pick valid arguments without a failed call.
- **`execute`** - `{ api, code, check?, timeoutMs? }` → runs `code` against a
  typed `client` and returns `{ stdout, stderr, exitCode }` verbatim. For OAuth
  APIs the access token is refreshed automatically first; if the API isn't
  authenticated, the result explains how to fix it (call `authenticate`, or run
  `anyapi-mcp login`). `check:false` skips type-checking for one run (use when a
  stale spec `enum` rejects a value the live API still accepts); `timeoutMs`
  raises the 30s default (capped at 120s) for long or paginated runs.
- **`authenticate`** - `{ api }` → opens the user's browser to (re-)authenticate
  an OAuth API and stores the tokens. Lets the model recover from an expired or
  revoked session without leaving the chat. It never accepts secrets — the user
  must have run `anyapi-mcp login` once to set up the OAuth app credentials.
- **`configure_oauth`** - `{ api, scopes?, scopeSeparator?, extraAuthParams? }`
  → lets the model fix OAuth provider quirks it discovers (wrong scope set,
  comma-vs-space separator, an extra authorize param like
  `access_type=offline`). It only touches **safe** request params — never the
  authorize/token endpoints (those carry the client secret and stay CLI-only),
  and reserved params like `redirect_uri`/`client_id` are rejected. Call with
  just `{ api }` to read the current config.
- **`add_api`** - `{ specUrl, kind?, id?, name?, baseUrl?, docsUrl?, force? }` →
  registers a new API (`kind` `openapi`, `graphql`, or `soap`) so the model can
  self-serve public APIs. Secrets are **not** accepted here; for authenticated
  APIs use `anyapi-mcp add … --token` (bearer) or `anyapi-mcp login` (OAuth) so
  the secret goes to the OS keychain, not the conversation. `force:true`
  overwrites an existing id in place (e.g. to correct a wrong `baseUrl`) instead
  of failing; an OAuth API stays logged in across the overwrite.
- **`list_apis`** - `{}` → the registered APIs as JSON (id, name, kind, baseUrl,
  operation count, auth/login status, docsUrl). Lets the model see what's
  available and confirm an `add_api`/`remove_api` took effect mid-session.
- **`remove_api`** - `{ api }` → unregisters an API: removes its entry, deletes
  any stored secrets from the keychain, and cleans up its cached types and ops
  index. The model's own way to clean up a mistaken registration.

The server also sends MCP `instructions` at connect time describing the
workflow, and - when the registry is empty - exactly how to register an API.

### OAuth APIs

Many user-facing APIs (Strava, Google, GitHub, …) use **OAuth 2.0**. anyapi-mcp
supports the authorization-code flow end to end:

1. **Add the API.** OpenAPI specs that declare an authorization-code flow are
   detected automatically (`anyapi-mcp add ` registers it as `oauth2`).
   For sources that don't declare one, pass
   `--oauth --auth-url … --token-url …`.
2. **Create an OAuth app** with the provider and set its redirect/callback URL
   to the one `add`/`login` prints (default `http://localhost:9876/callback`).
   You get a **client id** and **client secret**.
3. **Log in once:**
   ```sh
   anyapi-mcp login com.strava.api --client-id  --client-secret  \
     --scope read --scope activity:read_all
   ```
   This opens your browser, you approve, and the tokens are stored in the OS
   keychain. The access token then **refreshes automatically** before each
   `execute` — you don't log in again until you revoke access.

From then on the model just calls `search`/`execute` as usual. If a session
can't be refreshed (e.g. you revoked the app), the model can call the
`authenticate` tool to re-open the browser login — no secrets pass through the
conversation, since the client credentials are already in the keychain.

A small built-in quirks table fixes well-known providers whose specs are wrong
or non-standard — e.g. Strava's spec lists `/api/v3/oauth/*` (the live endpoints
are `/oauth/*`) and Strava wants comma-separated scopes. For anything else, the
**registry entry is the override** (it's what every refresh reads):
`anyapi-mcp login --auth-url/--token-url/--scope-separator/--scope` writes the
corrected values onto it. The model can also fix the **safe** params it
discovers (scopes, scope separator, extra author

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [gabrielbauman](https://github.com/gabrielbauman)
- **Source:** [gabrielbauman/anyapi-mcp](https://github.com/gabrielbauman/anyapi-mcp)
- **License:** Apache-2.0
- **Homepage:** https://gabrielbauman.github.io/anyapi-mcp/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** yes
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: flagged — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-gabrielbauman-anyapi-mcp
- Seller: https://agentstack.voostack.com/s/gabrielbauman
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
