## 1. Project Overview & Quickstart (openclaw/mcporter)

## File: README.md

# MCPorter 🧳 — Carry MCP tools into scripts, CLIs, and agents

[](https://github.com/openclaw/mcporter/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/mcporter)
[](https://nodejs.org)
[](LICENSE)
[](https://mcporter.sh)



MCPorter is a TypeScript runtime and command-line tool for discovering and calling Model Context Protocol servers. It is for developers and coding agents that need the same MCP tools from a terminal, a script, or a generated standalone CLI.

## Install

Try the CLI without installing it:

```sh
npx mcporter --version
```

Install it for repeated command-line use:

```sh
brew install steipete/tap/mcporter
# or
npm install mcporter
```

Node 24 or newer is required for npm installs. For the TypeScript runtime, add `mcporter` to your project with your package manager. Signed macOS binaries and the other supported install paths are covered in the [install guide](docs/install.md).

## Quick start

Inspect a public MCP server, then call one of its tools:

```sh
npx mcporter list https://mcp.context7.com/mcp --brief
npx mcporter call https://mcp.context7.com/mcp.resolve-library-id \
  query="React hooks docs" libraryName=react
```

The first command prints the server's TypeScript-style tool signatures. The second returns matching Context7 library IDs without requiring local configuration or credentials. See the [five-minute quickstart](docs/quickstart.md) for resources, generated CLIs, and typed clients.

## Core workflows

| Goal                                      | Command or API                       | Details                                    |
| ----------------------------------------- | ------------------------------------ | ------------------------------------------ |
| Discover servers and tools                | `mcporter list`                      | [CLI reference](docs/cli-reference.md)     |
| Call tools and read resources             | `mcporter call`, `mcporter resource` | [Call syntax](docs/call-syntax.md)         |
| Connect to a one-off URL or stdio command | `--http-url`, `--stdio`              | [Ad-hoc servers](docs/adhoc.md)            |
| Complete or seed OAuth                    | `mcporter auth`, `mcporter vault`    | [Configuration](docs/config.md)            |
| Generate a focused CLI                    | `mcporter generate-cli`              | [CLI generator](docs/cli-generator.md)     |
| Generate TypeScript types or clients      | `mcporter emit-ts`                   | [Typed clients](docs/emit-ts.md)           |
| Capture a reproducible MCP session        | `mcporter record`, `mcporter replay` | [Record and replay](docs/record-replay.md) |

Human-readable output goes to stdout by default. Use JSON output when another program or agent needs a stable result, and consult `mcporter <command> --help` for the flags supported by that command.

## Configuration and discovery

MCPorter reads project and user config, then imports MCP servers from Cursor, Claude Code and Desktop, Codex, Windsurf, OpenCode, and VS Code. A minimal `config/mcporter.json` looks like this:

```json
{
  "mcpServers": {
    "context7": {
      "url": "https://mcp.context7.com/mcp"
    }
  }
}
```

Config files accept JSONC, environment placeholders, HTTP and stdio definitions, OAuth settings, tool filters, and lifecycle policy. The [configuration guide](docs/config.md) defines precedence and the full schema; the [import reference](docs/import.md) lists every discovered client format.

Chrome DevTools definitions using `--autoConnect` can control Chrome through a paired [OpenClaw extension relay](docs/daemon.md#chrome-devtools-through-the-openclaw-extension-relay). MCPorter discovers OpenClaw's active extension relay endpoint, while preserving an explicit `MCPORTER_CHROME_DEVTOOLS_RELAY_URL` override and the legacy `127.0.0.1:18799` fallback. It uses Browser Relay Authentication v2 over one retained loopback socket, keeps the host key out of the network and child process, and gives `chrome-devtools-mcp` a credential-free loopback URL plus ephemeral authorization through an OS-protected preload handoff. Old relay authentication is never retried. Routing defaults to `prefer`, which may fall back only to Chrome's original auto-connect path; `require` fails closed and `off` keeps original auto-connect.

## TypeScript runtime

Use `createRuntime()` when a process needs explicit server definitions, connection reuse, or several calls:

```ts
import { createRuntime } from 'mcporter';

const runtime = await createRuntime({
  servers: [{ name: 'context7', command: { kind: 'http', url: new URL('https://mcp.context7.com/mcp') } }],
});
try {
  console.log((await runtime.listTools('context7')).map((tool) => tool.name));
} finally {
  await runtime.close();
}
```

`callOnce()` handles a single configured call and cleanup. `createServerProxy()` maps MCP tool names to callable camelCase properties and wraps results with text, Markdown, JSON, image, and raw-content helpers. See the [runtime overview](docs/mcp.md) and [tool-calling guide](docs/tool-calling.md).

## Protocols and long-lived servers

MCPorter connects to stdio, Streamable HTTP, and legacy SSE servers. It negotiates the current `2026-07-28` protocol or a legacy revision per server, while legacy connections advertise client elicitation capabilities. Interactive CLI calls can answer form and URL requests; headless and daemon-managed calls decline them with an actionable hint.

The repository's modern and legacy fixture servers cover both generations; CI exercises representative fixture paths end-to-end over stdio and Streamable HTTP. See [protocols and interactive requests](docs/protocols.md) for negotiation controls and [the keep-alive daemon](docs/daemon.md) for pooled servers, the MCP bridge, and Chrome DevTools integration.

## Development

```sh
pnpm install --frozen-lockfile
pnpm check
pnpm test
pnpm docs:site
```

The project uses the Node and pnpm versions declared in `package.json`. Live hosted-server tests are opt-in; see [live tests](docs/livetests.md) and [manual testing](docs/manual-testing.md).

## Related

- [CodexBar](https://codexbar.app) keeps Codex token windows visible in the macOS menu bar.
- [Trimmy](https://trimmy.app) flattens multi-line shell snippets for one-shot pasting.
- [Oracle](https://github.com/steipete/oracle) bundles prompts and files for multi-model runs.
- [Model Context Protocol specification](https://github.com/modelcontextprotocol/specification).

## License

MIT — see [LICENSE](LICENSE).

---

## File: docs/adhoc.md

---
summary: 'How to use mcporter’s ad-hoc HTTP/stdio flags to try MCP servers without editing config files.'
read_when:
  - 'Exploring or debugging unknown MCP endpoints'
---

# Ad-hoc MCP Servers

mcporter is gaining support for "just try it" workflows where you point the CLI at a raw MCP endpoint without first editing a config file. This doc tracks the behavior and heuristics we use to make that experience smooth while keeping the runtime predictable.

## Entry Points

Two new flag sets let you describe a server on the command line:

- `mcporter list --http-url https://mcp.linear.app/mcp [--name linear]`
- `mcporter call --stdio "bun run ./server.ts" --name local-tools`

You can also pass a bare URL as the selector (`mcporter list https://mcp.linear.app/mcp`) or embed the URL in a `call` expression (`mcporter call 'https://mcp.example.com/tools.generate({ topic: "release" })'`).

- Add `--json` to `mcporter list …` when you need a machine-readable summary of status counts and per-server failures, use `--output json`/`--output raw` with `mcporter call` to receive structured `{ server, tool, issue }` envelopes whenever a transport error occurs, and run `mcporter auth … --json` to capture the same envelope if OAuth or transport setup fails.

### Example: HTTP ad-hoc workflow

```bash
# Inspect the server directly via URL (no config entry needed)
mcporter list 'https://mcp.sentry.dev/mcp?agent=1'

# Call a tool by repeating the same URL + tool suffix
mcporter call 'https://mcp.sentry.dev/mcp?agent=1.use_sentry(request: "yo whats up")'
```

Notice that the second command repeats the URL. Ad-hoc definitions are ephemeral unless you pass `--persist`, so there is no stored server named `mcp-sentry-dev-mcp` yet. Reusing the printed slug only works after you persist the definition (see "Naming & Identity" below) or add the server to your config; otherwise keep passing the URL/stdio selector each time.

## Transport Detection

- **HTTP(S)**: Providing a URL defaults to the streamable HTTP transport. `https://` works out of the box; `http://` requires `--allow-http` (or the hidden alias `--insecure`) to acknowledge cleartext traffic. The `--sse` flag is a hidden alias for `--http-url` to match older examples. Use repeatable `--header KEY=value` flags for private servers that require headers such as `Authorization`, `X-API-Key`, or tenant selectors.
- **STDIO**: Supplying `--stdio` (with a command string) or `--stdio-bin` (binary + args) selects the stdio transport. Your current shell environment is inherited automatically; use `--env KEY=value` only when you need to inject/override specific variables (and `--cwd` to change directories).
- **Conflict guard**: Passing both URL and stdio flags errors out so we don’t guess.

## Naming & Identity

- `--name` wins when provided.
- Otherwise we derive a slug:
  - HTTP: `<host>` plus a sanitized path fragment (e.g. `mcp-linear-app-mcp`).
  - STDIO: executable basename + script (`node-singlestep`).
- Until you persist the definition (via `--persist`, `mcporter config add`, etc.), the slug is **only** used as the cache key for OAuth tokens/log prefs—you still need to supply the ad-hoc descriptor (`--http-url`, `--stdio`, or bare URL) every time you invoke `mcporter list|call|auth`.
- Once persisted, the slug becomes a normal server name so `mcporter call <slug>.tool` and `mcporter auth <slug>` work just like any other configured entry.
- If you don’t persist the definition, run `mcporter auth https://mcp.linear.app/mcp` (or supply `--name linear` so `mcporter auth linear` also works) to finish OAuth with the same settings.

This name becomes the cache key for OAuth tokens and log preferences, so repeated ad-hoc calls still benefit from credential reuse.

## OAuth Auto-Detection

Many hosted MCP servers (Supabase, Vercel, etc.) advertise OAuth capabilities but expect clients to discover this dynamically. When an ad-hoc HTTP server responds with `401/403` during the initial handshake, mcporter now:

1. **Promotes the definition to OAuth** and spins up the default browser flow—no need to edit config or supply `auth: "oauth"` manually. On headless hosts, pass `--no-browser` (or `--browser none`) to print the authorization URL instead of launching the platform browser.
2. **Persists the change** whenever you pass `--persist`, so future runs remember that the endpoint requires OAuth without repeating the detection step.

The CLI still avoids surprise prompts during `mcporter list`; the upgrade happens the first time you run `mcporter auth ` or any other command that allows OAuth (i.e., not in `--autoAuthorize=false` mode).

## Auth & Persistence

- OAuth flows are allowed; successful tokens store under the inferred name just like regular definitions.
- `mcporter auth` accepts the same `--http-url/--stdio` flags (and even bare URLs), so you can immediately re-run `mcporter auth https://…` after a 401 without touching a config file.
- Use `mcporter auth  --no-browser` for human-in-the-loop headless OAuth. Text mode writes only the authorization URL to stdout; `--json --no-browser` writes `authorizationUrl` plus `redirectUrl` as JSON. Keep that URL out of durable CI logs and support bundles. The `mcporter auth` process must keep running until the browser redirects to `redirectUrl`; process managers that capture stdout and then tear down the command can kill the local callback listener before tokens are saved. Use a persistent terminal, `tmux`, or a supervised background process such as `nohup` when completing OAuth outside an interactive shell.
- When opening the URL on a different machine, remember that loopback redirect URLs point at the browser machine unless an SSH tunnel forwards the callback port back to the mcporter process. Use `oauthRedirectUrl` when you need a predictable callback port.
- Nothing is written to disk unless you pass `--persist /path/to/config.json`. When set, we merge the generated definition into that file (creating it if necessary) so future runs can rely on the standard config pipeline. Ad-hoc HTTP headers are persisted with the entry, so placeholders such as `--header 'Authorization=$env:MY_TOKEN'` keep working through the normal config header resolver.

## Safety Nets

- Non-HTTPS endpoints require `--allow-http`.
- For stdio commands we print a confirmation snippet the first time we see a new command unless `--yes` is present.
- Missing transports or malformed combinations throw descriptive errors, pointing to `docs/adhoc.md` for guidance.

## Follow-ups

- Extend `mcporter config add` to leverage the same helper, making it the one-stop path from exploration to permanence.
- Consider caching inference results so repeated URL calls automatically rehydrate the previous settings (env/cwd).

---

## File: docs/agent-skills.md

---
summary: 'How to expose mcporter-backed MCP servers to agents through small per-server skills.'
read_when:
  - 'Writing agent skill docs or wiring mcporter into an agent runtime'
  - 'Triaging requests for a generic mcporter skill'
---

# Agent Skill Pattern

Prefer one small skill per MCP server or workflow instead of a single generic
`mcporter` skill. A focused skill keeps the agent prompt small, names the useful
tools directly, and avoids loading schemas for servers that are irrelevant to the
current task.

## Recommended Flow

1. Add or import the MCP server:

   ```bash
   npx mcporter config add docs https://mcp.context7.com/mcp --scope home
   ```

2. Inspect the tool surface:

   ```bash
   npx mcporter list docs --brief
   npx mcporter list docs --schema
   ```

3. Write a skill that calls only the relevant tools via `mcporter call`:

   ```markdown
   ---
   name: docs-mcp
   ---

   # Docs MCP

   Use `npx mcporter call docs.resolve-library-id query=<task> libraryName=<name>`
   to resolve a package, then call `npx mcporter call docs.query-docs ...` with
   the resolved ID and docs query.
   ```

4. For repeated or shareable workflows, generate a dedicated CLI instead of
   teaching the agent raw `mcporter call` syntax:

   ```bash
   npx mcporter generate-cli docs --bundle dist/docs-mcp.js
   ```

## Why Not One Generic Skill?

A generic skill has to teach the agent how to discover, choose, and call every
configured server. That recreates the large-schema context problem MCPorter is
trying to avoid. Per-server skills stay small and let the skill author describe
the safe, useful workflows for that server.

Use `allowedTools` or `blockedTools` in `mcporter.json` when a server exposes
tools that should not be shown to agents.

---

## File: docs/call-heuristic.md

---
summary: 'Explains mcporter call’s typo heuristics, auto-correction thresholds, and suggestion behavior.'
read_when:
  - 'Tool invocations fail due to misspelled names'
---

# Call Command Auto-Correction

`mcporter call` aims to help when a tool name is _almost_ correct without hiding real mistakes.

## Confident Matches → Auto-Correct

- We normalise tool names (strip punctuation, lowercase) and compute a Levenshtein distance.
- If the distance is ≤ `max(2, floor(length × 0.3))`, or the names only differ by case/punctuation, we retry automatically.
- A dim informational line explains the correction: `[mcporter] Auto-corrected tool call to linear.list_issues (input: linear.listIssues).`

## Low-Confidence Matches → Suggest

- When the best candidate falls outside the threshold we keep the original failure.
- We still print a hint so the user learns the canonical name: `[mcporter] Did you mean linear.list_issue_statuses?`
- No second call is attempted in this case.

## Edge Cases

- We only inspect the tool catalog if the server explicitly replied with “Tool … not found”. Other MCP errors surface untouched.
- If listing tools itself fails (auth, offline, etc.) we skip both auto-correct and hints.
- Behaviour is covered by `tests/cli-call.test.ts`.

## Server Selection Heuristic

- `mcporter list <server>` now applies the same edit-distance heuristic to server names. If you type `vercek`, the CLI auto-corrects to `vercel` (and logs `[mcporter] Auto-corrected server name to vercel (input: vercek).`).
- When the typo is too large, we keep the original failure but emit a hint: `[mcporter] Did you mean linear?` followed by the usual “Unknown MCP server …” line. This avoids giant stack traces while pointing to the right name.
- The heuristic considers every configured server (including ad-hoc ones registered via `--http-url/--stdio`). Tests covering this behaviour live in `tests/cli-list.test.ts`.
- `mcporter auth` shares the same routing logic, so `mcporter auth https://mcp.example.com/mcp` (or even `mcporter auth vercek`) will spin up the temporary definition, auto-correct close names, and launch OAuth without touching the config file.

---

## File: docs/call-syntax.md

---
summary: 'Reference for mcporter call argument styles, CLI signatures, and structured output modes.'
read_when:
  - 'Working on call CLI UX or documenting invocation examples'
---

# Call Syntax Reference

`mcporter call` now understands two complementary styles:

| Style                      | Example                                                                 | Notes                                                                                                                                     |
| -------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Flag-based (compatible)    | `mcporter call linear.create_comment --issue-id LNR-123 --body "Hi"`    | Use `--key value`, `--key=value`, `key=value`, `key:value`, or `key: value` pairs—ideal for shell scripts.                                |
| Function-call (expressive) | `mcporter call 'linear.create_comment(issueId: "LNR-123", body: "Hi")'` | Mirrors the pseudo-TypeScript signature shown by `mcporter list`; unlabeled values map to schema order.                                   |
| Structured output          | `mcporter call 'linear.create_comment(...)' --output json`              | Successful calls emit JSON bodies; failures emit `{ server, tool, issue }` envelopes so automation can react to auth/offline/http errors. |

Both forms share the same validation pipeline, so required parameters, enums, and formats behave identically.

## Reading the CLI Signatures

`mcporter list <server>` prints each tool as a compact TypeScript declaration:

```ts
/**
 * Create a comment on a specific Linear issue.
 * @param issueId The issue ID
 * @param body The content of the comment as Markdown
 * @param parentId? A parent comment ID to reply to
 */
function create_comment(issueId: string, body: string, parentId?: string): Comment;
// optional (3): notifySubscribers, labelIds, mentionIds, ...
```

Key details:

- Doc blocks use `@param` lines so every parameter description (even optional ones) stays in view.
- Required parameters appear without `?`; optional parameters use `?` and inherit enum literals (e.g. `"json" | "markdown"`).
- Known format hints are appended inline: `dueDate?: string /* ISO 8601 */` (we suppress the hint when the description already calls it out).
- When a tool exposes more than two optional parameters (or has ≥4 required parameters), the default output hides the extras and replaces them with an inline summary like `// optional (8): limit, before, after, orderBy, projectId, ...`.
- Run `mcporter list <server> --all-parameters` whenever you want the full signature; the footer repeats `Optional parameters hidden; run with --all-parameters to view all fields.` any time truncation occurs.
- Return types come from each tool’s output schema, so you’ll see concrete names when providers include `title` metadata (e.g. `DocumentConnection`). When no schema is advertised we omit the `: Type` suffix entirely instead of showing `unknown`.
- Each server concludes with a short `Examples:` block that mirrors the preferred function-call syntax.
- Run `mcporter list <server> --brief` (or `--signatures`) when you only want the compact signatures and optional summaries without doc blocks or examples.

## Function-Call Syntax Details

- **Named arguments preferred**: `issueId: "123"` keeps calls self-documenting. When labels are omitted, mcporter falls back to positional order defined by the tool schema.
- **Optional positional fallback**: omit labels when calling `mcporter 'context7.resolve-library-id("React hooks docs", "react")'`—arguments map to the schema order after any explicitly named parameters.
- **Literals supported**: strings, numbers, booleans, `null`, arrays, and nested objects. For strings containing spaces or commas, wrap the entire call in single quotes to keep the shell happy.
- **Error feedback**: invalid keys, unsupported expressions, or parser failures bubble up with actionable messages (`Unsupported argument expression: Identifier`, `Unable to parse call expression: …`).
- **Server selection**: You can embed the server in the expression (`linear.create_comment(...)`) or pass it separately (`--server linear create_comment(...)`).

## HTTP Selectors & Ad-hoc URLs

- You can skip `--server` entirely by pasting the MCP endpoint + tool name directly: `mcporter call 'https://www.shadcn.io/api/mcp.getComponent(component: "vortex")'`.
- Mcporter strips the `.tool` suffix to derive the base server URL, reuses any configured definition that already points at that endpoint, and only registers a new ad-hoc server when necessary (`--allow-http` still guards plain HTTP URLs).
- Function-call arguments continue to work in this form; if you omit parentheses you can still append `key=value` pairs (`https://.../mcp.getComponent component=vortex`).
- This is especially handy for copy/pasting tool listings or experimenting with anonymous HTTP MCP servers—you get the same parsing, auto-correction, and logging pipeline as the regular `server.tool` syntax.
- Protocols are optional; bare domains such as `shadcn.io/api/mcp.getComponents` automatically assume `https://`. If you really need plain HTTP, spell out `http://` and pass `--allow-http`.
- Hostname comparisons ignore a leading `www.` so `https://shadcn.io/api/mcp` and `https://www.shadcn.io/api/mcp` resolve to the same configured server when possible.

## Tips

- Use `--args '{ "issueId": "LNR-123" }'`, `--params '{ "issueId": "LNR-123" }'`, `--json '{ "issueId": "LNR-123" }'`, or `--json -` if you already have JSON payloads.
- The new syntax respects all existing features (timeouts, `--output`, auto-correction).
- Required fields show by default; pass `--all-parameters` when you want the full parameter list (or `--schema` for raw JSON schemas).
- When in doubt, run `mcporter list <server>` to see the current signature and sample invocation.

## Flag-Based Syntax Details

- `--key value`, `--key=value`, `key=value`, `key:value`, `key: value`, and `key:=value` all map to the same named-argument handling, so you can type whichever feels most natural for your shell. Long flag keys convert kebab-case to camelCase (`--save-to-drafts true` becomes `saveToDrafts: true`). The `:=` form is accepted as a compatibility alias for `=`.
- By default, arguments keep the same validation pipeline as the function-call syntax—enums, numbers, and booleans are coerced automatically, and missing required fields raise errors.
- `--args -`, `--params -`, and `--json -` read a JSON object from stdin.
- Named flag-style values can read exact UTF-8 text from a file with `key=@path` or `--key @path`. Paths resolve from the current working directory, file contents remain strings without coercion, and `key=@@literal` produces the literal value `@literal`. Function-call strings such as `body: "@literal"` remain literal.
- Bare string values supplied via long flags wrap into one-item arrays when the tool schema declares that field as an array.
- Numeric-looking `key=value` arguments are restored to their original string spelling when the tool schema declares that parameter as a string, which keeps timestamp-like IDs such as Slack `thread_ts=1234567890.123456` intact.
- `--raw-strings` disables numeric coercion for flag-style and positional values so IDs/codes stay literal strings (`code=12345` stays `"12345"`).
- `--no-coerce` disables all coercion for flag-style and positional values (`true`, `null`, and JSON-like values remain strings).
- Long tool flags are checked against the selected tool schema before dispatch; undeclared flags fail with guidance to use `key=value` or `--args`. Insert `--` before literal positional values that start with `--`.
- `--save-images <dir>` keeps stdout formatting untouched while writing image content blocks to disk when a tool response includes `type: "image"` entries.
- `tool=value`/`tool:value` and `server=value` still act as aliases for `--tool` / `--server` when you need to override the selector.

---

## File: docs/cli-generator.md

---
summary: 'Behavior and architecture of mcporter generate-cli, including outputs, runtimes, regeneration, and policy boundaries.'
read_when:
  - 'Changing generate-cli behavior or bundler integrations'
---

# CLI Generator

`mcporter generate-cli` produces a standalone CLI for one MCP server. Tool schemas become subcommands and schema fields become positional arguments or flags. By default, the command writes `<server>.ts` in the working directory. Bundling is opt-in via `--bundle`; Rolldown handles Node.js output, while Bun’s native bundler handles Bun output. Use `--bundler` to override that choice.

## Notes

- Generated CLI depends on the latest `commander` for argument parsing.
- Default timeout for tool calls is 30 seconds, overridable via `--timeout`.
- Runtime flag remains (`--runtime bun`) to tailor shebang/usage instructions, but Node.js is the default.
- Generated CLI embeds the resolved server definition and always targets that snapshot (no external `--config` or `--server` overrides at runtime).

## Usage Examples

```bash
# Minimal: infer the name from the command URL and emit TypeScript (optionally bundle)
npx mcporter generate-cli \
  --command https://mcp.context7.com/mcp \
  --minify

# Provide explicit name/description and compile a Bun binary (falls back to Node if Bun missing)
npx mcporter generate-cli \
  --name context7 \
  --command https://mcp.context7.com/mcp \
  --description "Context7 docs MCP" \
  --runtime bun \
  --compile

chmod +x context7
./context7
  # show the embedded help + tool list

# Shareable "one weird trick" for chrome-devtools (no config required)
npx mcporter generate-cli --command "npx -y chrome-devtools-mcp@latest"
```

- `--minify` shrinks the bundled output via the selected bundler (output defaults to `<server>.js`).
- `--compile [path]` implies bundling and invokes `bun build --compile` to create the native executable (Bun only). When you omit the path, the compiled binary inherits the server name.
- Use `--server '{...}'` when you need advanced configuration (headers, env vars, stdio commands, OAuth metadata).
- Omit `--name` to let mcporter infer it from the command URL (for example, `https://mcp.context7.com/mcp` becomes `context7`).
- When targeting an existing config entry, you can skip `--server` and pass the name as a positional argument:
  `npx mcporter generate-cli linear --bundle dist/linear.js`.
- When the MCP server is a stdio command, you can also skip `--command` by quoting the inline command as the first positional argument (e.g., `npx mcporter generate-cli "npx -y chrome-devtools-mcp@latest"`).
- Generated CLIs preserve `lifecycle: "keep-alive"` for embedded stdio servers. At runtime they create a stable generated config under `~/.mcporter/generated/` (or `$XDG_STATE_HOME/mcporter/generated/` when set), auto-start the daemon as needed, and keep the server process alive across separate generated-CLI invocations.
- Narrow the CLI to a specific subset of tools with `--include-tools`:
  `npx mcporter generate-cli linear --include-tools issues_list,issues_create`.
- Hide debug or admin tools with `--exclude-tools`:
  `npx mcporter generate-cli linear --exclude-tools debug_tool,admin_reset`.

## Artifact Metadata & Regeneration

- Every generated artifact embeds its metadata (generator version, resolved server definition, invocation flags). A hidden `__mcporter_inspect` subcommand prints the payload without contacting the MCP server, so binaries remain self-describing even after being copied to another machine.
- `mcporter inspect-cli <artifact>` shells out to that embedded command and prints a human summary (pass `--json` for raw output). The summary includes a ready-to-run `generate-cli` command you can reuse directly.
- `mcporter generate-cli --from <artifact>` replays the stored invocation against the latest mcporter build. `--server`, `--runtime`, `--timeout`, `--minify/--no-minify`, `--bundle`, `--compile`, `--output`, and `--dry-run` let you override specific pieces of the stored metadata when necessary.
- Because the metadata lives inside the artifact, any template, bundle, or compiled binary can be refreshed after a generator upgrade without juggling sidecar files.

## Policy boundary for generated CLIs

A generated CLI or typed client can be invoked independently of the MCP client
that originally configured the server. It therefore does not automatically
inherit that client's approval prompts, tool-call policies, or audit trail.

Use `--include-tools` to reduce the generated surface, but do not treat a static
tool list as dynamic authorization. Deployments that need per-call policy should
route the generated command through an external wrapper or policy gateway that:

1. normalizes the server, tool name, and arguments;
2. evaluates current policy and requests approval when required;
3. blocks the call or invokes the generated CLI; and
4. persists a redacted decision and result record.

Conceptual wrapper pseudocode (not a built-in mcporter API):

```ts
const operation = normalize({ server, tool, arguments });
let decision;
let outcome = 'evaluation_failed';
let result;
let failure;

try {
  decision = await gateway.evaluate(operation);

  if (decision.action === 'block') {
    outcome = 'blocked';
    failure = 'policy_block';
    throw new Error(decision.reason);
  }

  if (decision.action === 'approve' && !(await approvals.confirm(decision))) {
    outcome = 'approval_declined';
    failure = 'approval_declined';
    throw new Error('Approval declined');
  }

  try {
    result = await generatedCli.call(tool, arguments);
    outcome = 'succeeded';
    return result;
  } catch (error) {
    outcome = 'execution_failed';
    failure = classifyError(error);
    throw error;
  }
} catch (error) {
  failure ??= classifyError(error);
  throw error;
} finally {
  await audit.append(redact({ operation, decision, outcome, result, failure }));
}
```

The `finally` path records a redacted outcome for blocked requests, declined
approvals, policy-evaluation failures, execution failures, and successful calls.
The wrapper governs only calls routed through it. Direct execution of the
generated artifact bypasses that policy boundary.

---

## File: docs/cli-reference.md

---
summary: 'Quick reference for mcporter subcommands, their arguments, and shared global flags.'
read_when:
  - 'Need a refresher on available CLI commands'
---

# mcporter CLI Reference

A quick reference for the primary `mcporter` subcommands. Each command inherits
`--config <file>` and `--root <dir>` to override where servers are loaded from.

## `mcporter list [server]`

- Without arguments, lists every configured server (with live discovery + brief
  status).
- With a server name, prints TypeScript-style signatures for each tool, doc
  comments, optional summaries, and any server `instructions` returned during
  MCP initialization.
- With `server.tool`, prints just that tool; combine with `--schema` for a single
  tool schema.
- Add `--brief` or `--signatures` with a server or `server.tool` target to keep
  the server header/instructions and print compact signatures without doc
  comments, examples, or schemas.
- Add `--status` with a server target to print only the concise status row
  instead of full tool docs.
- Add `--exit-code` to make the command exit 1 when any checked server is
  unhealthy, or `--quiet` to suppress output and imply `--exit-code`.
- Hidden alias: `list-tools` (kept for muscle memory; not advertised in help output).
- Hidden ad-hoc flag aliases: `--sse` for `--http-url`, `--insecure` for `--allow-http` (for plain HTTP testing).
- Flags:
  - `--brief` – compact single-server output; cannot be combined with `--json`,
    `--schema`, `--verbose`, or `--all-parameters`.
  - `--signatures` – alias for `--brief`.
  - `--all-parameters` – include every optional parameter in the signature.
  - `--schema` – pretty-print the JSON schema for each tool.
  - `--status` – check server status only; cannot be combined with `--brief`,
    `--schema`, or `--all-parameters`.
  - `--exit-code` – exit 1 when any checked server is unhealthy.
  - `--quiet` – suppress output and exit 1 when any checked server is unhealthy.
  - `--timeout <ms>` – per-server timeout when enumerating all servers.
  - `--no-oauth` – never start an interactive OAuth flow; use cached
    tokens only while keeping eligible connections pooled.

## `mcporter call <server.tool>`

- Invokes a tool once and prints the response; supports positional arguments via
  pseudo-TS syntax and `--arg` flags.
- Useful flags:
  - `--server`, `--tool` – alternate way to target a tool.
  - `--args <json>`, `--params <json>` – provide a JSON object payload.
  - `--` – stop flag parsing so remaining tokens stay literal positional values.
  - `--timeout <ms>` – override the 60 s call timeout; `MCPORTER_CALL_TIMEOUT` provides the equivalent environment override.
  - `--output text|markdown|json|raw` – choose how to render the `CallResult`.
  - `--save-images <dir>` – persist image content blocks to files under the specified directory.
  - `--raw-strings` – disable numeric coercion for flag-style and positional values.
  - `--no-coerce` – disable all flag-style/positional value coercion.
  - `key=@path` / `--key @path` – read a named UTF-8 string argument from a file; prefix with `@@` for a literal leading `@`.
  - Generic long tool flags are validated against the selected tool schema before dispatch.
  - `--tail-log` – stream tail output when the tool returns log handles.
  - `--no-oauth` – never start an interactive OAuth flow; use cached
    tokens only while keeping eligible connections pooled.

## `mcporter resource <server> [uri]`

- Lists resources exposed by a server when no URI is provided.
- Reads an MCP resource when `uri` is provided and renders text, markdown, JSON,
  or raw output using the same output formatter as `mcporter call`.
- Hidden alias: `resources`.
- Useful flags:
  - `--output auto|text|markdown|json|raw` – choose how to render the response.
  - `--json` – shortcut for `--output json`.
  - `--raw` – shortcut for `--output raw`.
  - `--no-oauth` – never start an interactive OAuth flow; use cached
    tokens only while keeping eligible connections pooled.

## `mcporter serve [--servers a,b,c] [--stdio | --http ]`

- Exposes daemon-managed keep-alive servers as one MCP server for clients that
  consume MCP over stdio or Streamable HTTP.
- `tools/list` queries the daemon for each selected server and publishes tools
  as `server__tool`; `tools/call` strips the prefix and routes the call through
  the daemon.
- In HTTP mode, `/mcp` keeps the aggregate namespaced bridge, while
  `/mcp/<server>` exposes one selected keep-alive server with its original,
  unprefixed tool names.
- Only configured keep-alive servers participate. Add
  `"lifecycle": "keep-alive"` to a server definition when you want it managed
  by the daemon.
- Flags:
  - `--stdio` – serve MCP over stdio; this is the default and is the mode to
    register with Claude Code, Codex, and similar clients.
  - `--http ` – serve MCP Streamable HTTP on `/mcp` and
    `/mcp/<server>`, bound to `127.0.0.1` by default.
  - `--host <host>` – override the HTTP bind host when you intentionally need a
    non-local listener.
  - `--servers <csv>` – expose only the listed keep-alive server names.

## `mcporter generate-cli`

- Produces a standalone CLI for a single MCP server (optionally bundling or
  compiling with Bun).
- Key flags:
  - `--server <name>` (or inline JSON) – choose the server definition.
  - `--command ` – point at an ad-hoc HTTP endpoint (include `https://` or use `host/path.tool`) or a stdio command (anything with spaces, e.g., `"npx -y chrome-devtools-mcp@latest"`). If you omit `--command`, the first positional argument is inspected: whitespace → stdio, otherwise the parser probes for HTTP/HTTPS and falls back to config names.
  - `--output ` – where to write the TypeScript template.
  - `--bundle ` – emit a bundle (Node/Bun) ready for `bun x`.
  - `--bundler rolldown|bun` – pick the bundler implementation (defaults to Rolldown unless the runtime resolves to Bun, in which case Bun’s bundler is used automatically; still requires a local Bun install).
  - `--compile ` – compile with Bun (implies `--runtime bun`).
  - `--include-tools <a,b,c>` – only generate subcommands for the specified tools (exact MCP tool names). It is an error if any requested tool is missing.
  - `--exclude-tools <a,b,c>` – generate subcommands for every tool except the ones listed. Unknown tool names are ignored. If all tools are excluded, generation fails.
  - `--include-tools` and `--exclude-tools` are mutually exclusive.
  - `--timeout <ms>` / `--runtime node|bun` – shared via the generator flag
    parser so defaults stay consistent.
  - `--from <artifact>` – reuse metadata from an existing CLI artifact (legacy
    `regenerate-cli` behavior, must point at an existing CLI).
  - `--dry-run` – print the resolved `mcporter generate-cli ...` command without
    executing (requires `--from`).
  - Positional shorthand: `npx mcporter generate-cli linear` uses the configured
    `linear` definition; `npx mcporter generate-cli https://example.com/mcp`
    treats the URL as an ad-hoc server definition.

## `mcporter emit-ts <server>`

- Emits TypeScript definitions (and optionally a ready-to-use client) describing
  a server’s tools. This reuses the same formatter as `mcporter list` so doc
  comments, signatures, and examples stay in sync.
- Modes:
  - `--mode types --out <file.d.ts>` (default) – export an interface whose
    methods return `Promise<CallResult>`, with doc comments and optional
    summaries.
  - `--mode client --out <file.ts>` – emit both the interface (`<file>.d.ts`)
    and a factory that wraps `createServerProxy`, returning objects whose
    methods resolve to `CallResult`.
- Other flags:
  - `--include-optional` (alias `--all-parameters`) – show every optional field.
  - `--types-out <file>` – override where the `.d.ts` sits when using client
    mode.

For more detail about configuration and OAuth flows, see `docs/config.md` and
the command-specific docs under `docs/`.

---

## File: docs/config.md

---
summary: 'How mcporter discovers, merges, and mutates configuration files (project + home + imports), including OAuth and persistence.'
read_when:
  - 'Working on config resolution, imports, or mcporter config subcommands'
---

# Configuration Guide

## Overview

mcporter keeps three configuration buckets in sync: repository-scoped JSON (`config/mcporter.json`), imported editor configs (Cursor, Claude, Codex, Windsurf, OpenCode, VS Code), and ad-hoc definitions supplied on the CLI. This guide explains how those sources merge, how to mutate them with `mcporter config ...`, and the safety rails around OAuth, env interpolation, and persistence.

## Quick Start

1. Create `config/mcporter.json` at the repo root:
   ```jsonc
   {
     "$schema": "https://raw.githubusercontent.com/openclaw/mcporter/main/mcporter.schema.json",
     "mcpServers": {
       "linear": {
         "description": "Linear issues",
         "baseUrl": "https://mcp.linear.app/mcp",
         "headers": { "Authorization": "Bearer ${LINEAR_API_KEY}" },
       },
     },
     "imports": ["cursor", "claude-code", "claude-desktop", "codex", "windsurf", "opencode", "vscode"],
   }
   ```
   The `$schema` property enables IDE autocomplete and validation. Use the raw GitHub URL for the latest schema, or copy `mcporter.schema.json` locally.
2. Run `mcporter list linear` (or `mcporter config list linear`) to make sure the runtime can reach it.
3. Use `mcporter config add shadcn https://www.shadcn.io/api/mcp` to persist another server without editing JSON.
4. Authenticate any OAuth-backed server with either `mcporter auth <name>` or `mcporter config login <name>`; tokens land in the shared vault (`~/.mcporter/credentials.json`, or `$XDG_DATA_HOME/mcporter/credentials.json` when set) unless you override `tokenCacheDir`.

## Config Resolution Order

mcporter now merges home and project config files by default so global servers stay available inside repos. The order depends on how you invoke the CLI:

1. If you pass `--config <file>` (or set `--config` programmatically), only that file is used—no merging.
2. If `MCPORTER_CONFIG` is set, only that file is used—no merging.
3. Otherwise, mcporter loads both of these layers (when present):
   - `$XDG_CONFIG_HOME/mcporter/mcporter.json[c]` when `XDG_CONFIG_HOME` is set, falling back to `~/.mcporter/mcporter.json[c]` when no XDG mcporter config exists
   - `<root>/config/mcporter.json`
     Entries from the project file override entries with the same name from the home file. Each layer still pulls in its own imports before merging.

All `mcporter config …` mutations still write back to a single file: the explicit path when provided; otherwise the project config path (`<root>/config/mcporter.json`). To edit the home file explicitly, run commands like `mcporter config --config ~/.mcporter/mcporter.json add <name> …` or set `MCPORTER_CONFIG` in your shell profile.

mcporter honors XDG Base Directory env vars for its own paths when they are explicitly set to absolute paths:

| Kind   | Env var           | mcporter path                                   | Legacy fallback      |
| ------ | ----------------- | ----------------------------------------------- | -------------------- |
| config | `XDG_CONFIG_HOME` | `$XDG_CONFIG_HOME/mcporter/mcporter.json[c]`    | `~/.mcporter/...`    |
| data   | `XDG_DATA_HOME`   | `$XDG_DATA_HOME/mcporter/credentials.json`      | `~/.mcporter/...`    |
| cache  | `XDG_CACHE_HOME`  | `$XDG_CACHE_HOME/mcporter/<server>/schema.json` | `~/.mcporter/...`    |
| state  | `XDG_STATE_HOME`  | `$XDG_STATE_HOME/mcporter/daemon/...`           | `~/.mcporter/daemon` |

Unset, empty, or relative XDG vars fall back to `~/.mcporter` for backwards compatibility. For config files only, an absolute `XDG_CONFIG_HOME` is XDG-first but still probes `~/.mcporter/mcporter.json[c]` when no XDG mcporter config exists, so embedders that sandbox unrelated tools with `XDG_CONFIG_HOME` do not accidentally hide the user's registry. Explicit overrides still win: `--config`/`MCPORTER_CONFIG` for config files, `tokenCacheDir` for per-server OAuth/schema cache directories, and `MCPORTER_DAEMON_DIR` for daemon files.

## Discovery & Precedence

mcporter builds a merged view of all known servers before executing any command. The sources load in this order:

| Priority | Source                                                           | Notes                                                                                                                                                                                                                                                     |
| -------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1        | Explicit `--http-url`, `--stdio`, or bare URL passed to commands | Highest priority, never cached unless `--persist` is supplied. Requires `--allow-http` for plain HTTP URLs.                                                                                                                                               |
| 2        | `config/mcporter.json` (or the file passed via `--config`)       | Default path is `<root>/config/mcporter.json`; missing file returns an empty config so commands continue to work.                                                                                                                                         |
| 3        | Imports listed in `"imports"`                                    | When you omit `imports`, mcporter loads `['cursor','claude-code','claude-desktop','codex','windsurf','opencode','vscode']`. When you specify a non-empty array, mcporter appends any omitted defaults after your list so shared presets remain available. |

Rules:

- Later sources never override earlier ones. Local config always wins over imports; ad-hoc descriptors override both for the duration of a command.
- Each merged server tracks its origin (local path vs. import path), so `mcporter config get <name>` can show you the path before you edit or remove the local copy with `mcporter config remove <name>`.
- Imports remain read-only until you explicitly copy an entry via `mcporter config import <kind> --copy` or run `mcporter config add --copy-from claude-code:linear`.

## CLI Workflows

`mcporter config` is the entry point for reading and writing configuration files. Use the existing ad-hoc flags on `mcporter list|call|auth` when you want ephemeral definitions; once you’re ready to persist them, switch back to `mcporter config add`.

Use `--scope home|project` with `mcporter config add` to pick the write target explicitly. `project` is always the default (creating `config/mcporter.json` if needed); `home` writes to the XDG config path when `XDG_CONFIG_HOME` is set, otherwise `~/.mcporter/mcporter.json`, even when a project config is present. `--persist ` still takes precedence when you need a custom file.

### `mcporter config list [filter]`

- Shows **local** entries by default. Pass `--source import` to list imported editor configs, or `--json` for machine output.
- Always appends a summary of other config files (paths, counts, sample names) so you know where imported entries live.
- `filter` accepts a name, glob fragment, or `source:cursor` selector.
- Adds informational notes when we auto-correct names (same machinery as `mcporter list`).

### `mcporter config get <name>`

- Prints the resolved definition for a single server, including the on-disk path, inherited headers/env, and transport details.
- Near-miss names are auto-corrected with the same heuristics as `mcporter list`/`call`, and you’ll see suggestions whenever ambiguity remains.
- Supports ad-hoc descriptors so you can inspect a URL before persisting it.

### `mcporter config add <name> [target]`

- Persists a server into the writable config file. Accepts both positional shortcuts (`mcporter config add sentry https://mcp.sentry.dev/mcp`) and flag-driven definitions:
  - `--transport http|sse|stdio`
  - `--url` or `--command`/`--stdio`
  - `--env`, `--header`, `--token-cache-dir`, `--description`, `--client-name`, `--oauth-redirect-url`
  - `--oauth-client-id`, `--oauth-client-secret-env`, `--oauth-token-endpoint-auth-method` for pre-registered OAuth clients.
  - `--copy-from importKind:name` to clone settings from an imported entry before editing.
- `--dry-run` shows the JSON diff without writing, while `--persist ` overrides the destination file.

### `mcporter config remove <name>`

- Removes the local definition. Names sourced exclusively from imports remain untouched until you copy them locally.

### `mcporter config import <kind>`

- Displays (and optionally copies) entries from editor-specific configs:
  - `cursor`: `.cursor/mcp.json` in the repo, falling back to `~/.config/Cursor/User/mcp.json` (or `%APPDATA%/Cursor/User` on Windows).
  - `claude-code`: `<root>/.claude/settings.local.json`, `<root>/.claude/settings.json`, `<root>/.claude/mcp.json`, then `~/.claude/settings.json`, `~/.claude/mcp.json`, `~/.claude.json`. `settings.local.json` is meant for untracked per-developer overrides, while `settings.json` is the shared project config.
  - `claude-desktop`: platform-specific `Claude/claude_desktop_config.json` paths.
  - `codex`: `<root>/.codex/config.toml`, then `~/.codex/config.toml`.
  - `windsurf`: Codeium’s Windsurf config under `%APPDATA%/Codeium/windsurf/mcp_config.json` or `~/.codeium/windsurf/mcp_config.json`.
  - `opencode`: Honors `OPENCODE_CONFIG` when set, then `<root>/opencode.json(c)`, `OPENCODE_CONFIG_DIR/opencode.json(c)`, and finally `${XDG_CONFIG_HOME:-~/.config}/opencode/opencode.json(c)` (or `%APPDATA%/opencode/opencode.json(c)` on Windows). Both `.json` and `.jsonc` extensions are supported.
  - `vscode`: `Code/User/mcp.json` (stable + Insiders) inside the OS-appropriate config directory.
- `--copy` writes selected entries into your local config; `--filter <glob>` narrows the import list; `--path <file>` lets you point at bespoke locations.

### `mcporter config login <name|url>` / `logout`

- Mirrors `mcporter auth`. `login` completes OAuth (or token provisioning) for either a named server or an ad-hoc URL. When a hosted MCP returns 401/403, mcporter automatically promotes that target to OAuth and re-runs the flow, matching the behavior documented in `docs/adhoc.md`.
- `--no-browser` suppresses automatic browser launch and prints the authorization URL to stdout so it can be copied from a headless host. `--browser none` is accepted as a compatibility alias, and `MCPORTER_OAUTH_NO_BROWSER=1` / `true` / `yes` enables the same behavior by environment.
- In `--json --no-browser` mode, stdout contains a JSON object with `authorizationUrl` and `redirectUrl`; diagnostics stay off stdout so scripts can parse the result. Treat emitted authorization URLs as sensitive operational output.
- A configured server name is an OAuth trust boundary. Changing its URL clears cached tokens, dynamic client registration, PKCE verifier, and callback state from the shared vault and any `tokenCacheDir`; changing the URL back does not restore the old credentials, so re-authentication is required.
- `logout` wipes the shared vault entry, legacy `~/.mcporter/<name>/` caches, and the custom `tokenCacheDir` when present. Pass `--all` to clear everything.

### `mcporter config doctor`

- Early validator that checks for simple issues (e.g., OAuth entries missing cache paths). Future iterations will add fixes for Accept headers, duplicate imports, and more.

## Ad-hoc & Persistence

- `--http-url` and `--stdio` flags live on `mcporter list|call|auth`, keeping `mcporter config` focused on persistent config files. Ad-hoc HTTP targets also accept repeatable `--header KEY=value` flags for private endpoints.
- Names default to slugified hostnames or executable/script combos. Supply `--name` to improve reuse; mcporter uses that slug for OAuth caches even before persistence.
- `--allow-http` is mandatory for cleartext endpoints so we never downgrade transport silently.
- Add `--persist ` (defaulting to `config/mcporter.json` when omitted) to copy the ad-hoc definition into config. We reuse the same serializer as the import pipeline, so copying from Cursor → local config produces identical structure and preserves custom env/header fields.
- When an ad-hoc HTTP server returns an OAuth challenge during `list`, `call`, or `auth`, the persisted entry is rewritten with `auth: "oauth"` so later commands use the cached OAuth path instead of retrying unauthenticated HTTP.
- `--env KEY=VAL` entries merge with existing `env` dictionaries if you later persist the same server; nothing is lost when you alternate between CLI flags and JSON edits.
- `--header KEY=VAL` entries merge into the persisted HTTP `headers` object when used with `--persist`; values support the same `$env:VAR`, `${VAR}`, and `${VAR:-fallback}` placeholders as config-file headers.

## HTTP Compatibility

HTTP MCP servers normally use Node's built-in `fetch` for request/response traffic. MCPorter's optional long-lived SSE receive channel uses a separate HTTP/1.1 connection so an idle stream cannot occupy the request pool and stall later tool calls. Set `httpFetch: "default"` to opt out of that isolation and use the built-in fetch for every request.

If a provider rejects the built-in stack entirely but accepts plain Node `https.request` traffic, set `httpFetch: "node-http1"` on the server entry to force an HTTP/1.1 fetch implementation for all Streamable HTTP and SSE requests:

```jsonc
{
  "mcpServers": {
    "sunsama": {
      "baseUrl": "https://api.sunsama.com/mcp",
      "headers": { "Authorization": "Bearer ${SUNSAMA_TOKEN}" },
      "httpFetch": "node-http1",
    },
  },
}
```

The Sunsama endpoint is auto-detected and uses the all-request compatibility path by default.

## JSON Schema for IDE Support

mcporter provides a JSON Schema for config file validation and autocompletion. Add the `$schema` property to your config file:

```jsonc
{
  "$schema": "https://raw.githubusercontent.com/openclaw/mcporter/main/mcporter.schema.json",
  "mcpServers": { ... }
}
```

For local development, you can reference the schema from the repo root:

```jsonc
{
  "$schema": "../mcporter.schema.json",
  "mcpServers": { ... }
}
```

The schema is auto-generated from the Zod validation schemas using `pnpm generate:schema`.

## Schema Reference

Top-level structure:

| Key          | Type     | Description                                                                                                            |
| ------------ | -------- | ---------------------------------------------------------------------------------------------------------------------- |
| `mcpServers` | object   | Map of server names → definitions. Required even if empty.                                                             |
| `imports`    | string[] | Optional list of import kinds. Empty array disables imports entirely; omitting the key falls back to the default list. |

Server definition fields (subset of what `RawEntrySchema` accepts):

| Field                                           | Description                                                                                                                                                                                                                                                                                           |
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `description`                                   | Free-form summary printed by `mcporter list`/`config list`.                                                                                                                                                                                                                                           |
| `baseUrl` / `url` / `serverUrl`                 | HTTPS or HTTP endpoint. `http://` requires `--allow-http` in ad-hoc mode but works in config if you explicitly set it.                                                                                                                                                                                |
| `command` / `args`                              | Stdio executable definition (string or array). Arrays are preferred because they avoid shell quoting issues.                                                                                                                                                                                          |
| `cwd`                                           | Working directory for stdio servers. A leading `~` is expanded to `$HOME`; relative paths resolve against the config file directory. Defaults to the config file directory when omitted.                                                                                                              |
| `env`                                           | Key/value pairs applied when launching stdio commands. Supports `${VAR}` interpolation and `${VAR:-fallback}` defaults. Existing process env values win over fallbacks.                                                                                                                               |
| `headers`                                       | Request headers for HTTP/SSE transports. Values can reference `$env:VAR` or `${VAR}` placeholders, which must be set at runtime or mcporter aborts with a helpful error.                                                                                                                              |
| `auth`                                          | Recognizes `oauth` and `refreshable_bearer`. `oauth` runs the browser/client OAuth flow for HTTP transports; `refreshable_bearer` refreshes cached bearer tokens non-interactively before connecting.                                                                                                 |
| `tokenCacheDir`                                 | Directory for OAuth tokens and schema caches; still honored, but mcporter now keeps a centralized vault in `~/.mcporter/credentials.json` or `$XDG_DATA_HOME/mcporter/credentials.json` when set (legacy per-server caches are auto-migrated). Supports `~` expansion.                                |
| `clientName`                                    | Optional identifier some servers use for telemetry/audience segmentation.                                                                                                                                                                                                                             |
| `protocolVersion` / `protocol_version`          | Protocol negotiation override: `auto` (the default), `legacy` for byte-compatible 2025-era connections, or `2026-07-28` to require that modern revision.                                                                                                                                              |
| `chromeDevtoolsRelay` / `chrome_devtools_relay` | OpenClaw Chrome extension relay policy for eligible `chrome-devtools-mcp --autoConnect` stdio definitions: `prefer` (default), `require`, or `off`. `MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY` overrides config. See the [daemon guide](daemon.md#chrome-devtools-through-the-openclaw-extension-relay). |
| `oauthClientId`                                 | Pre-registered OAuth client id for providers that do not support dynamic client registration.                                                                                                                                                                                                         |
| `oauthClientSecretEnv`                          | Environment variable containing the OAuth client secret. Prefer this over committing `oauthClientSecret` directly.                                                                                                                                                                                    |
| `oauthTokenEndpointAuthMethod`                  | Optional token endpoint auth method override, for example `client_secret_post` when the provider requires client credentials in the token request body.                                                                                                                                               |
| `oauthRedirectUrl`                              | Override the default localhost callback. Required for many pre-registered OAuth apps because the provider must allowlist the exact redirect URI. Also useful when tunneling OAuth through Codespaces or remote dev boxes.                                                                             |
| `oauthClientMetadataUrl`                        | Optional HTTPS Client ID Metadata Document URL. When the authorization server supports URL-based client identity, the SDK uses this URL as the client id; otherwise it falls back to dynamic client registration.                                                                                     |
| `oauthScope`                                    | Optional explicit OAuth scope string. If omitted, mcporter lets the MCP SDK derive scope from server/auth metadata. Use this as an escape hatch for providers that require explicit scopes but don’t publish `scopes_supported`.                                                                      |
| `oauthCommand.args`                             | For STDIO servers that ship a custom auth subcommand (e.g., Gmail MCP). mcporter will spawn the stdio command with these args when you run `mcporter auth <name>`, so you don’t need to call `npx ... auth` manually.                                                                                 |
| `refresh`                                       | Explicit token refresh settings for `auth: "refreshable_bearer"`. Supports `tokenEndpoint`, `clientIdEnv`, `clientSecretEnv`, `clientAuthMethod`, `refreshSkewSeconds`, and `accessTokenEnv` (plus snake_case aliases).                                                                               |
| `allowedTools` / `allowed_tools`                | Optional exact-name allowlist. Only listed tools appear in `mcporter list` and can be called. An empty array blocks all tools. Cannot be combined with `blockedTools`.                                                                                                                                |
| `blockedTools` / `blocked_tools`                | Optional exact-name blocklist. Listed tools are hidden from `mcporter list` and rejected by `mcporter call`. Cannot be combined with `allowedTools`.                                                                                                                                                  |

mcporter normalizes headers to include `Accept: application/json, text/event-stream` automatically, matching the runtime’s streaming expectations.
String-valued config fields support `${VAR}` and `${VAR:-fallback}` placeholders. Secret-bearing `headers`, `env`, and bearer-token placeholders are preserved in `config get`/`config list` output and resolved only when the transport runs; `*Env` fields name environment variables and are not expanded.

Top-level `daemonIdleTimeoutMs` (or `daemon_idle_timeout_ms`) shuts down the keep-alive daemon after that many milliseconds without daemon requests. Per-server `lifecycle.idleTimeoutMs` still controls when individual keep-alive transports are closed.

### Refreshable Bearer Tokens

Use `auth: "refreshable_bearer"` when you already seeded OAuth tokens with `mcporter vault set <server>` or `tokenCacheDir`, and the server should receive only a fresh bearer token at runtime. HTTP servers get `Authorization: Bearer <token>` when no authorization header is already configured. STDIO servers require `refresh.accessTokenEnv`; mcporter refreshes before spawning the process and injects that env var with the raw access token.

```json
{
  "mcpServers": {
    "example": {
      "command": "uvx",
      "args": ["example-mcp-server"],
      "auth": "refreshable_bearer",
      "refresh": {
        "tokenEndpoint": "https://api.example.com/oauth/token",
        "clientIdEnv": "EXAMPLE_CLIENT_ID",
        "clientSecretEnv": "EXAMPLE_CLIENT_SECRET",
        "clientAuthMethod": "client_secret_basic",
        "refreshSkewSeconds": 300,
        "accessTokenEnv": "EXAMPLE_ACCESS_TOKEN"
      }
    }
  }
}
```

For keep-alive stdio servers, refresh happens before process start. If that process cannot read updated credentials after startup, use `lifecycle: "ephemeral"` or restart the daemon before the injected token expires.

## Imports & Conflict Resolution

- `pathsForImport(kind, rootDir)` determines every candidate path. mcporter searches the repo first, then user-level directories, and stops at the first file that parses.
- Entries pulled from imports are treated as read-only snapshots. The merge process keeps the first definition for each name; later sources with the same name are skipped until you override locally.
- To copy an imported entry, either run `mcporter config import <kind> --copy --filter name` or use `mcporter config add name --copy-from kind:name`. The copy operation writes through the same JSON normalization stack, so the resulting file matches our schema even if the source format was TOML (Codex) or legacy JSON shapes (`servers` vs `mcpServers`).

## Project vs. Machine Layers

- Keep `config/mcporter.json` under version control. Encourage contributors to add sensitive data via env vars (`${LINEAR_API_KEY}`) rather than inline secrets.
- For pre-registered OAuth apps, store the public `oauthClientId` in config and point `oauthClientSecretEnv` at a local environment variable. `oauthClientSecret` is supported for private machine-local configs but should not be committed.
- For headless deployments that already have OAuth credentials, run `mcporter vault set <server> --tokens-file ` or `mcporter vault set <server> --stdin` with a JSON payload shaped like `{ "tokens": { ... }, "clientInfo": { ... } }`. This lets mcporter compute the vault key from the resolved server definition instead of duplicating that internal format in scripts.
- Machine-specific additions can live in `~/.mcporter/local.json` or `$XDG_CONFIG_HOME/mcporter/local.json`; point `mcporter config --config ~/.mcporter/local.json add ...` there when you prefer not to touch the repo. Since the runtime only watches one config at a time, CI jobs should always pass `--config config/mcporter.json` (or run from the repo root) for deterministic behavior.
- OAuth tokens, cached server metadata, and generated CLIs should remain outside the repo (`~/.mcporter/...` or the matching `XDG_*_HOME/mcporter/...`, plus `dist/`).

## Validation & Troubleshooting

- `mcporter list --http-url ...` refuses to auto-run OAuth to keep listing commands quick; use `mcporter config login ...` or `mcporter auth ...` to finish credential setup.
- When env placeholders are missing, commands fail fast with the exact variable name. Add the variable or wrap it in `${VAR:-fallback}` to provide defaults.
- Use `mcporter config get <name>` to confirm where a server came from. If a teammate’s Cursor config keeps overriding your local entry, reorder the `imports` array to move Cursor later or set it to `[]` to disable imports entirely.
- `docs/adhoc.md` covers deeper debugging, including tmux workflows and OAuth promotion logs.

---

## File: docs/daemon.md

---
summary: 'How mcporter keeps stateful MCP servers warm and exposes them through its bridge.'
read_when:
  - 'Configuring keep-alive servers, the daemon, serve mode, or Chrome DevTools'
---

# Keep-alive daemon

MCPorter can keep stateful stdio servers alive across separate CLI invocations. A per-login daemon owns those transports, while the ordinary `mcporter list` and `mcporter call` commands route eligible servers through its local socket.

## Lifecycle selection

Chrome DevTools, Mobile MCP, Playwright MCP, and CloudBase MCP definitions default to keep-alive when MCPorter recognizes their configured name or stdio command. Other servers remain ephemeral unless their config opts in:

```jsonc
{
  "mcpServers": {
    "browser": {
      "command": "npx",
      "args": ["-y", "chrome-devtools-mcp@latest", "--autoConnect"],
      "lifecycle": "keep-alive",
    },
    "interactive": {
      "url": "https://example.com/mcp",
      "lifecycle": "ephemeral",
    },
  },
}
```

`MCPORTER_KEEPALIVE=name` opts named servers in. `MCPORTER_DISABLE_KEEPALIVE=name` opts them out; both variables accept comma-separated names or `*`. Explicit config remains the clearest choice for project-owned definitions.

Ad-hoc targets passed with `--stdio`, `--http-url`, or a URL selector are per-process. Persist the definition into config before expecting it to participate in the shared daemon.

## Daemon commands

```sh
mcporter daemon status
mcporter daemon start
mcporter daemon restart
mcporter daemon stop
```

Eligible servers start the daemon automatically when a list or call needs them. Use explicit commands to inspect, pre-warm, restart after configuration changes, or stop every managed transport.

Daemon calls are non-interactive. If a server requests elicitation, the call is declined with a hint to use an ephemeral terminal call instead. See [protocols and interactive requests](protocols.md).

Set `MCPORTER_OAUTH_NO_BROWSER=1` (also accepts `true` or `yes`) on a serve/daemon process to suppress automatic OAuth browser launches. Affected calls fail once with the server name and an `mcporter auth <name> --no-browser` hint instead of restarting or replaying. Changing the normalized setting replaces an already-running daemon on the next operation.

The local socket timeout is an idle liveness budget rather than a cap on the whole operation. A current daemon emits progress frames while work is in flight, allowing interactive OAuth to run to its own deadline without causing a daemon restart or a duplicate browser prompt. Mixed versions remain compatible: a current client falls back to the previous flat deadline with an older daemon, while a current daemon sends the single-response format expected by older clients unless the caller explicitly opts into progress frames.

## Expose servers to another MCP client

`mcporter serve` turns the daemon's keep-alive servers back into an MCP server:

```sh
mcporter serve --stdio
mcporter serve --http 3000
mcporter serve --http 3000 --servers chrome-devtools,playwright
```

The stdio form is suitable for clients such as Claude Code and Codex. HTTP mode binds to `127.0.0.1` by default and serves an aggregate endpoint at `/mcp`, with namespaced tools such as `chrome-devtools__list_pages`. It also serves `/mcp/<server>` with that server's original tool names.

## Chrome DevTools through the OpenClaw extension relay

`chrome-devtools-mcp --autoConnect` uses Chrome's remote-debugging handshake, which can display an “Allow remote debugging?” dialog for each session. When the [OpenClaw Chrome extension relay](https://docs.openclaw.ai/tools/chrome-extension/) is paired on the same host, MCPorter can use that extension-backed Chrome control path instead. Automatic rewriting supports direct `chrome-devtools-mcp` commands and the standard `npx`/`bunx` launch forms used by MCPorter definitions. Other shell and package-manager wrappers remain unchanged under `prefer`; `require` rejects recognizable Chrome auto-connect commands behind those unsupported wrappers instead of silently launching the legacy path.

MCPorter requires an OpenClaw relay that implements **Browser Relay Authentication v2**, including the connection-bound challenge, completion, authenticated `/json/version`, and same-connection `/cdp` upgrade sequence. An older OpenClaw relay is unsupported: `require` fails with `unsupported-auth`, while `prefer` may launch the original Chrome `--autoConnect` path. MCPorter never retries an old Bearer, Basic, or raw-token relay handshake, even when the v2 endpoint returns `404`, `401`, or `426`, a proof fails, or the handshake times out. During OpenClaw's dual-stack migration window, legacy acceptance exists only for older clients; current MCPorter always uses v2.

The mode-`0600`, current-user relay key never enters an HTTP header, URL, WebSocket subprotocol, application frame, child command line, or child environment. MCPorter derives its non-secret `keyId`, connects once to a numeric loopback address, verifies the connected peer, and keeps that exact raw TCP socket from the HMAC challenge through completion, `/json/version`, and the `/cdp` WebSocket upgrade. It does not follow redirects, reconnect, re-resolve, or hand the key to the proxy. Only after both server proofs verify does MCPorter start a short-lived downstream proxy bound strictly to `127.0.0.1`; that proxy wraps the already-authenticated and already-upgraded socket rather than opening another upstream connection.

The same raw client supports the protocol's separate `json-list` flow: it authenticates one `/json/list` request on the retained socket, reads a bounded response, and closes instead of upgrading.

Each downstream proxy retains the protections introduced for the child handoff: it gets a fresh 256-bit authorization bearer, MCPorter writes that ephemeral value to an exclusively created mode-`0600` file inside a mode-`0700` temporary directory, and only the protected file path reaches a Node preload through the child environment. The preload validates and consumes the file, removes the handoff variable, and appends `--wsHeaders` only to JavaScript's `process.argv`; the OS command line contains only the credential-free loopback WebSocket endpoint. The proxy accepts exactly one authorized downstream WebSocket, synthesizes that child's `101` response, and then bridges frames to the retained upstream socket. Losing either side retires the proxy and its authenticated connection.

The preload composes with existing `NODE_OPTIONS`, including MCPorter's separate Chrome compatibility preload. The proxy, handoff file, preload, and temporary directory are closed or removed on normal shutdown, setup failure, abort, and negotiation retry. On POSIX systems, ownership and permissions are checked before the handoff is consumed. On Windows, MCPorter creates the temporary directory atomically with a verified current-user-only ACL before its path exists; setup fails closed if that security descriptor cannot be established. Plaintext relay URLs remain limited to loopback, including custom URLs set with `MCPORTER_CHROME_DEVTOOLS_RELAY_URL`; remote HTTP relay targets and URLs containing credentials are rejected.

Choose routing with `chromeDevtoolsRelay` (or `chrome_devtools_relay`) on the server definition, or override it for the process with `MCPORTER_CHROME_DEVTOOLS_RELAY_POLICY`:

- `prefer` is the default. It uses the v2 extension relay when authentication and the protected child handoff succeed; otherwise it may keep only the original Chrome `--autoConnect` behavior. It never falls back to legacy relay authentication.
- `require` fails before any legacy auto-connect process is launched if the endpoint, credential, probe, authentication, extension connection, local proxy, or protected handoff is unavailable. Retries remain fail-closed.
- `off` disables relay probing and rewriting. The older `MCPORTER_DISABLE_CHROME_DEVTOOLS_RELAY=1` switch remains an alias for `off`.

```jsonc
{
  "mcpServers": {
    "chrome-devtools": {
      "command": "npx",
      "args": ["-y", "chrome-devtools-mcp@latest", "--autoConnect"],
      "lifecycle": "keep-alive",
      "chromeDevtoolsRelay": "require",
    },
  },
}
```

Before probing, MCPorter runs the non-secret `openclaw browser extension cdp --json` metadata command without the legacy-bearer option. POSIX launches `openclaw` directly. Windows resolves an existing `openclaw.cmd` only from safe absolute `PATH` entries, rejects the current directory and command-expanding path bytes, then passes the quoted absolute shim plus constant arguments to a validated system `cmd.exe`. Process spawning always keeps `shell: false`; time and combined output are bounded, and timeout or overflow terminates the full Windows process tree through the validated system `taskkill.exe`. Fallback diagnostics never include stdout or stderr. MCPorter accepts only the exact loopback Browser Relay Authentication v2 CDP contract whose `keyId` matches the local relay credential. An explicit nonempty `MCPORTER_CHROME_DEVTOOLS_RELAY_URL` remains authoritative and skips metadata discovery; unavailable, timed-out, malformed, unsafe, or incompatible metadata falls back to `http://127.0.0.1:18799`. Relay control and OpenClaw discovery variables in a server definition's `env` map use the same placeholder resolution as its runtime transport and override process-level values for both routing and daemon identity.

Metadata discovery and the authenticated `/json/version` probe each use the relay timeout, which is 5 seconds by default. `MCPORTER_CHROME_DEVTOOLS_RELAY_TIMEOUT_MS` accepts milliseconds, clamped to 100–30000; unset, zero, non-integer, and otherwise invalid values use 5000. Credential discovery follows OpenClaw: `OPENCLAW_OAUTH_DIR` wins, then `OPENCLAW_STATE_DIR`; otherwise credentials live under the effective home in `.openclaw/credentials`, or `.openclaw-/credentials` for a validated non-default `OPENCLAW_PROFILE`. Explicit `OPENCLAW_HOME` takes precedence over `HOME` and `USERPROFILE`.

Keep-alive daemons include the effective policy, discovered or explicit URL, timeout, state directory, credential directory, v2 protocol marker, and derived `keyId` in their runtime identity. Both daemon metadata creation and freshness checks await the same bounded asynchronous resolver; freshness never launches OpenClaw through a synchronous child process. Changing the selected OpenClaw profile or relay port, or rotating the key at the same path, therefore replaces stale daemon state on the next operation without exposing key material; no manual restart is required. `mcporter daemon status` shows the current or last redacted decision with route, policy, reason, safe logical upstream endpoint, and probe status/duration. Set `MCPORTER_LOG_LEVEL=info` to see the same structured decision during ordinary connection setup. Diagnostics distinguish unsupported v2 authentication, bad server proofs, unauthenticated server failures, replay, protocol, freshness, and sequence failures, disconnected extensions (an authenticated `503`), timeouts, network errors, protected handoff failures, and success. They never print keys, proofs, nonces, stable or ephemeral bearers, handoff paths, headers, pairing strings, or child arguments.

MCPorter also applies its Chrome DevTools auto-connect compatibility patch when relevant. Set `MCPORTER_DISABLE_CHROME_DEVTOOLS_COMPAT=1` to disable that separate behavior.

## Logging and shutdown

Use `mcporter daemon start --log` or `mcporter daemon restart --log` to keep a daemon log. `--log-file ` chooses the destination, and `--log-servers <csv>` limits per-call traces. The [logging guide](logging.md) covers environment and per-server controls.

The daemon closes managed transports on `stop`. A top-level `daemonIdleTimeoutMs` can shut down an inactive daemon, while a keep-alive lifecycle object can set an idle timeout for one server. See [configuration](config.md) for both forms.

---

## File: docs/emit-ts.md

---
summary: 'How to generate `.d.ts` files or typed client helpers with mcporter emit-ts.'
read_when:
  - 'Adding new emit-ts behavior or troubleshooting generated clients'
---

# `mcporter emit-ts`

`mcporter emit-ts` turns a configured MCP server into TypeScript artifacts so
agents, tests, and tooling can consume the server through strongly typed APIs.
It reuses the same `buildToolDoc()` data that powers `mcporter list`, so doc
comments, parameter hints, and signatures stay perfectly in sync. For a broader
overview of every CLI command, see `docs/cli-reference.md`.

```
mcporter emit-ts <server> --out linear-client.ts \
  [--mode types|client] \
  [--include-optional]
```

- `--mode types` (default) emits a `.d.ts` interface (`LinearTools`) with
  docblocks + promisified signatures. Missing output schemas fall back to
  `CallResult`.
- `--mode client` emits both the interface (auto-derived `.d.ts`) **and** an
  executable `.ts` helper that wraps `createServerProxy`. Each method returns a
  `CallResult`, and the factory exposes a `close()` helper for runtimes the client
  creates.
- `--include-optional` mirrors `mcporter list --all-parameters`, ensuring every
  parameter is shown even when optional.

Outputs overwrite existing files automatically so you can regenerate artifacts
whenever the server schema changes.

## Examples

### 1. Types-only header

```
mcporter emit-ts linear --out types/linear-tools.d.ts
```

Produces:

```ts
import type { CallResult } from 'mcporter';

export interface LinearTools {
  /**
   * List comments for a specific Linear issue.
   *
   * @param issueId The issue ID
   */
  list_comments(params: { issueId: string }): Promise<CallResult>;
}
```

Include the file in your agent/project and you can type-check code like
`const result = await proxy.list_comments({ issueId: '...' });`.

### 2. Client wrappers

```
mcporter emit-ts linear --mode client --out clients/linear.ts
```

Generates two files:

- `clients/linear.d.ts` – same interface as the types-only mode.
- `clients/linear.ts` – imports `createRuntime`, `createServerProxy`, and
  `createCallResult`, then exposes a `createLinearClient()` factory:

```ts
const client = await createLinearClient({ configPath: './mcporter.json' });
const comments = await client.list_comments({ issueId: 'LIN-1234' });
console.log(comments.text());
await client.close();
```

If you pass an existing runtime (`{ runtime }`), the factory reuses it; the
returned object’s `close()` becomes a no-op.

## Flags

| Flag | Description |
| -------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------- |
| `--out ` | Required. `.d.ts` target for `types`, `.ts` target for `client`. |
| `--mode types        | client` | Output kind (defaults to `types`). |
| `--types-out ` | Optional override for the `.d.ts` file when `--mode client`. Default: derive from `--out`. |
| `--include-optional` | Include every parameter (not just the minimum 5 + required). |
| `--json` | Emit a JSON summary describing the emitted file(s) instead of plain-text logs. |

## Testing

`tests/emit-ts.test.ts` covers:

- Template rendering (doc comments, `Promise<…>` return types, proxy wrappers).
- End-to-end CLI invocation with a stub runtime, ensuring both `.ts` and `.d.ts`
  files are written successfully.

## 2. Official Technical Reference & Guides (openclaw/docs)

## File: README.md

# openclaw-docs




Mirror repo for the published OpenClaw docs site.

Source of truth lives in [`openclaw/openclaw`](https://github.com/openclaw/openclaw), under `docs/`.

## How it works

1. English docs are authored in `openclaw/openclaw`.
2. `openclaw/openclaw/.github/workflows/docs-sync-publish.yml` mirrors the docs tree into this repo.
3. This repo stores the published docs tree plus generated locale output.
4. `openclaw/docs/.github/workflows/translate-incremental.yml` debounces normal docs changes, while `translate-all.yml` handles full reconciliation for glossary changes, weekly schedule, release dispatch, or manual dispatch.
5. `.github/workflows/r2-pages.yml` builds the full unpruned static site and uploads changed objects to Cloudflare R2.
6. `.github/workflows/pages.yml` deploys the small Cloudflare Worker router that preserves clean URLs and markdown negotiation while reading docs from R2.

## Translation behavior

- Locale pages under `docs/<locale>/**` are generated output.
- Each translated page stores `x-i18n.source_hash`.
- The translate workflow computes a pending file list before calling the model.
- If no English source hashes changed, the workflow skips the expensive translation step entirely.
- If files changed, only the pending files are translated.
- The workflow retries transient model-format failures.
- Locale outputs are uploaded as artifacts first, then committed together by the finalizer.
- Incremental and full translation use separate concurrency lanes, so small docs edits do not cancel weekly or glossary-triggered full reconciliation.
- The weekly scheduled run uses full reconciliation mode to repair missed or flaky locale updates.

## Editing rules

- Do not treat this repo as the primary place for English doc edits.
- Make English doc changes in `openclaw/openclaw`, then let sync copy them here.
- Locale pages under `docs/<locale>/**` are generated output.
- `.openclaw-sync/source.json` records which `openclaw/openclaw` commit this mirror was synced from.

## Static site build

- `npm run docs:build` renders the mirrored Mintlify-flavored docs into `dist/docs-site`.
- `npm run docs:build:cloudflare` is the legacy Worker Static Assets fallback build.
- `npm run docs:build:r2` renders the full unpruned site and prepares `dist/docs-r2-manifest.json` for R2 upload.
- `npm run docs:r2:upload` uploads only changed R2 objects, reports cache hits/misses, and refuses to turn a broken remote manifest read into a full-tree reupload.
- Manual R2 refreshes audit objects before upload; unchanged objects remain cache hits, and transient HEAD failures fall back to the signed manifest. `R2_UPLOAD_PUT_ALL=1` is the emergency escape hatch for intentionally rewriting every object.
- `npm run docs:smoke` checks representative English and locale pages plus the Pagefind search bundle.
- `npm run docs:check` runs both steps.
- The generated site includes the language picker and static full-text search via Pagefind.
- Cloudflare deploys `workers/docs-router.ts`, which serves slashless page URLs, English markdown responses for `.md` paths or `Accept: text/markdown`, and `/api/search` through the `DOCS_BUCKET` R2 binding.
- Cloudflare hosting details and limitations are documented in `CLOUDFLARE.md`.

## Secrets

- `OPENCLAW_DOCS_SYNC_TOKEN` lives in `openclaw/openclaw` and lets the source repo push into this repo.
- `OPENCLAW_DOCS_I18N_OPENAI_API_KEY` lives in this repo and powers locale translation refreshes.
- `CLOUDFLARE_API_TOKEN` lives in this repo and deploys the `docs.openclaw.ai` router.
- R2 uploads verify `CLOUDFLARE_API_TOKEN`, try temporary R2 credentials, and normally fall back to the token-derived direct S3 credential form. `OPENCLAW_R2_ACCESS_KEY_ID` / `OPENCLAW_R2_SECRET_ACCESS_KEY` are only fallback upload credentials when the Cloudflare token cannot be verified.

---

## File: docs/index.md

---
summary: "OpenClaw is a multi-channel gateway for AI agents that runs on any OS."
read_when:
  - Introducing OpenClaw to newcomers
title: "OpenClaw"
---

# OpenClaw 🦞



> _"EXFOLIATE! EXFOLIATE!"_ — A space lobster, probably



<Columns>
  <Card title="Get Started" href="/start/getting-started" icon="rocket">
    Install OpenClaw and bring up the Gateway in minutes.
  </Card>
  <Card title="Run Onboarding" href="/start/wizard" icon="list-checks">
    Guided setup with `openclaw onboard` and pairing flows.
  </Card>
  <Card title="Connect a Channel" href="/channels" icon="message-circle">
    Link Discord, Signal, Telegram, WhatsApp, and more to chat from anywhere.
  </Card>
  <Card title="Open the Control UI" href="/web/control-ui" icon="layout-dashboard">
    Launch the browser dashboard for chat, config, and sessions.
  </Card>
</Columns>

## Browse docs

Mobile browsers may show the section menu without the full desktop tab bar. Use
these hub links to reach the same top-level docs areas from the page body.

<Columns>
  <Card title="Get started" href="/" icon="rocket">
    Overview, showcase, first steps, and setup guides.
  </Card>
  <Card title="Install" href="/install" icon="download">
    Install paths, updates, containers, hosting, and advanced setup.
  </Card>
  <Card title="Channels" href="/channels" icon="messages-square">
    Messaging channels, pairing, routing, access groups, and channel QA.
  </Card>
  <Card title="Agents" href="/concepts/architecture" icon="bot">
    Architecture, sessions, context, memory, and multi-agent routing.
  </Card>
  <Card title="Capabilities" href="/tools" icon="wand-sparkles">
    Tools, skills, cron, webhooks, and automation capabilities.
  </Card>
  <Card title="ClawHub" href="/clawhub" icon="store">
    Plugin marketplace, publishing, curation, and trust guidance.
  </Card>
  <Card title="Models" href="/providers" icon="brain">
    Providers, model configuration, failover, and local model services.
  </Card>
  <Card title="Platforms" href="/platforms" icon="monitor-smartphone">
    macOS, Windows, iOS, Android, nodes, and web surfaces.
  </Card>
  <Card title="Gateway & Ops" href="/gateway" icon="server">
    Gateway configuration, security, diagnostics, and operations.
  </Card>
  <Card title="Reference" href="/cli" icon="terminal">
    CLI reference, schemas, RPC, release notes, and templates.
  </Card>
  <Card title="Help" href="/help" icon="life-buoy">
    Troubleshooting, FAQs, testing, diagnostics, and environment checks.
  </Card>
</Columns>

## What is OpenClaw?

OpenClaw is a **self-hosted gateway** that connects your favorite chat apps — Discord, Google Chat, iMessage, Matrix, Microsoft Teams, Signal, Slack, Telegram, WhatsApp, Zalo, and more via channel plugins — to AI coding agents. You run a single Gateway process on your own machine (or a server), and it becomes the bridge between your messaging apps and an always-available AI assistant.

**Who is it for?** Developers and power users who want a personal AI assistant they can message from anywhere — without giving up control of their data or relying on a hosted service.

**What makes it different?**

- **Self-hosted**: runs on your hardware, your rules
- **Multi-channel**: one Gateway serves every configured channel plugin simultaneously
- **Agent-native**: built for coding agents with tool use, sessions, memory, and multi-agent routing
- **Open source**: MIT licensed, community-driven

**What do you need?** Node 26 (recommended), or another supported release: Node 22.22.3+, Node 24.15+, or Node 25.9+. You also need an API key from your chosen provider and 5 minutes. For best quality and security, use the strongest latest-generation model available.

## How it works

```mermaid
flowchart LR
  A["Chat apps + plugins"] --> B["Gateway"]
  B --> C["OpenClaw agent"]
  B --> D["CLI"]
  B --> E["Web Control UI"]
  B --> F["macOS app"]
  B --> G["iOS and Android nodes"]
```

The Gateway is the single source of truth for sessions, routing, and channel connections.

## Key capabilities

<Columns>
  <Card title="Multi-channel gateway" icon="network" href="/channels">
    Discord, iMessage, Signal, Slack, Telegram, WhatsApp, WebChat, and more with a single Gateway process.
  </Card>
  <Card title="Plugin channels" icon="plug" href="/tools/plugin">
    Channel plugins add Matrix, Nostr, Twitch, Zalo, and more; official plugins install on demand.
  </Card>
  <Card title="Multi-agent routing" icon="route" href="/concepts/multi-agent">
    Isolated sessions per agent, workspace, or sender.
  </Card>
  <Card title="Media support" icon="image" href="/nodes/images">
    Send and receive images, audio, and documents.
  </Card>
  <Card title="Web Control UI" icon="monitor" href="/web/control-ui">
    Browser dashboard for chat, config, sessions, and nodes.
  </Card>
  <Card title="Mobile nodes" icon="smartphone" href="/nodes">
    Pair iOS and Android nodes for camera, screen, and voice-enabled workflows.
  </Card>
</Columns>

## Quick start

<Steps>
  <Step title="Install OpenClaw">
    On npm 12 or npm 11.16+:

    ```bash
    npm install openclaw@latest --allow-scripts=openclaw
    ```

    On npm 11.15 and earlier, omit `--allow-scripts=openclaw`.

  </Step>
  <Step title="Onboard and install the service">
    ```bash
    openclaw onboard --install-daemon
    ```
  </Step>
  <Step title="Chat">
    Open the Control UI in your browser and send a message:

    ```bash
    openclaw dashboard
    ```

    Or connect a channel ([Telegram](/channels/telegram) is fastest) and chat from your phone.

  </Step>
</Steps>

Need the full install and dev setup? See [Getting Started](/start/getting-started).

## Dashboard

Open the browser Control UI after the Gateway starts.

- Local default: [http://127.0.0.1:18789/](http://127.0.0.1:18789/)
- Remote access: [Web surfaces](/web) and [Tailscale](/gateway/tailscale)



## Configuration (optional)

Config lives at `~/.openclaw/openclaw.json`.

- If you **do nothing**, OpenClaw uses the bundled OpenClaw agent runtime; DMs share the agent's main session, and each group chat gets its own session.
- If you want to lock it down, start with `channels.whatsapp.allowFrom` and (for groups) mention rules.

Example:

```json5
{
  channels: {
    whatsapp: {
      allowFrom: ["+15555550123"],
      groups: { "*": { requireMention: true } },
    },
  },
  messages: { groupChat: { mentionPatterns: ["@openclaw"] } },
}
```

## Start here

<Columns>
  <Card title="Docs hubs" href="/start/hubs" icon="book-open">
    All docs and guides, organized by use case.
  </Card>
  <Card title="Configuration" href="/gateway/configuration" icon="settings">
    Core Gateway settings, tokens, and provider config.
  </Card>
  <Card title="Remote access" href="/gateway/remote" icon="globe">
    SSH and tailnet access patterns.
  </Card>
  <Card title="Channels" href="/channels/telegram" icon="message-square">
    Channel-specific setup for Discord, Feishu, Microsoft Teams, Telegram, WhatsApp, and more.
  </Card>
  <Card title="Nodes" href="/nodes" icon="smartphone">
    iOS and Android nodes with pairing, camera, screen, and device actions.
  </Card>
  <Card title="Help" href="/help" icon="life-buoy">
    Common fixes and troubleshooting entry point.
  </Card>
</Columns>

## Learn more

<Columns>
  <Card title="Full feature list" href="/concepts/features" icon="list">
    Complete channel, routing, and media capabilities.
  </Card>
  <Card title="Multi-agent routing" href="/concepts/multi-agent" icon="route">
    Workspace isolation and per-agent sessions.
  </Card>
  <Card title="Security" href="/gateway/security" icon="shield">
    Tokens, allowlists, and safety controls.
  </Card>
  <Card title="Troubleshooting" href="/gateway/troubleshooting" icon="wrench">
    Gateway diagnostics and common errors.
  </Card>
  <Card title="About and credits" href="/reference/credits" icon="info">
    Project origins, contributors, and license.
  </Card>
</Columns>