Stepgate · Writing a stepfile · Connecting a client · How it works

# Connecting a client Stepgate is an MCP server, so any MCP client whose model can call tools can run stepfiles: Claude Code, Claude Desktop, Cursor, VS Code, or an agent you wrote yourself. The client's own model does each step; Stepgate needs no model key. You need Node.js 22.18 or later where the client runs.
Contents
  1. What the server command looks like
  2. Claude Code
  3. Claude Desktop
  4. Cursor
  5. VS Code
  6. Clients that connect by URL
  7. Your own agent
  8. What a run looks like
  9. When something goes wrong
  10. Your own stepfiles
  11. MCP servers that use MCP authorization
  12. Writing stepfiles with an agent
## What the server command looks like Every client launches the same command: ```sh npx -y stepgate market-research drug-shortage-watch ``` Each argument is the name of a stepfile in the [catalog](../awesome-stepfiles/) or a path to your own `.stepfile.yaml`. `npx -y stepgate --list` shows the catalog. Options you are likely to want: - **`--ledger-dir `** writes one ledger file per run. Without it, ledger records go to standard error, which most clients hide. - **`--contact `** adds your email to the User-Agent. SEC EDGAR and USAJOBS require it. - **`--tool-result-chars `** raises the 20,000-character cap on each result passed to the model. [how-it-works.md](how-it-works.md#limits) lists every option. Credentials and settings come from the server's environment, and the model never sees them. A stepfile's README lists what it needs: - credential `tavily` is read from `TAVILY_API_KEY`; - setting `jira-site` is read from `JIRA_SITE`. Set them in the client's server configuration, as the sections below show.

(back to top)

## Claude Code Add the server from your project folder: ```sh claude mcp add stepgate -e TAVILY_API_KEY=tvly-... -- npx -y stepgate market-research ``` Choose where the configuration lives: - **`-s project`** writes it to `.mcp.json` in the project root, so your team shares it. - **`-s user`** makes it available in every project. A shared `.mcp.json` should reference keys rather than contain them. Claude Code expands `${VAR}` from your environment: ```json { "mcpServers": { "stepgate": { "command": "npx", "args": ["-y", "stepgate", "--ledger-dir", ".stepgate/ledgers", "market-research"], "env": { "TAVILY_API_KEY": "${TAVILY_API_KEY}" } } } } ``` Start `claude` in that folder. It asks once whether to trust servers from `.mcp.json`. Then ask for a run in plain words: > Run the market-research stepfile for brand Oatly in the UK plant-based milk market. Claude calls `market-research` and works through each step with `stepgate_call` and `stepgate_submit`. It fixes whatever the gates reject, then reports the outputs. - **Permissions.** Claude Code asks before each tool call until you allow them. To allow every Stepgate tool, add `"mcp__stepgate__*"` to `permissions.allow` in `.claude/settings.json`. - **Connection.** `/mcp` shows whether the server connected. To run without the interactive session, pass the configuration explicitly: ```sh claude -p --mcp-config .mcp.json --allowedTools "mcp__stepgate__*" \ "Run the market-research stepfile for brand Oatly in the UK plant-based milk market." ``` ### Approvals A stepfile with an `approve` gate asks you through a form the client shows. As of 2026-09-28, only Claude Code in a terminal (`claude`) shows it. These tell Stepgate they can show the form, then decline every request without asking you ([#79174](https://github.com/anthropics/claude-code/issues/79174), [#89858](https://github.com/anthropics/claude-code/issues/89858)): - the VS Code extension; - the desktop app; - `claude -p`; - the Agent SDK without an `onElicitation` handler. The run then fails that gate, whatever you would have answered. Run a stepfile that writes from the terminal.

(back to top)

## Claude Desktop Open **Settings > Developer > Edit Config**, which opens `claude_desktop_config.json` (`~/Library/Application Support/Claude/` on macOS, `%APPDATA%\Claude\` on Windows), and add: ```json { "mcpServers": { "stepgate": { "command": "npx", "args": ["-y", "stepgate", "market-research"], "env": { "TAVILY_API_KEY": "tvly-..." } } } } ``` Quit and reopen Claude Desktop. If the server fails to start, Desktop may not have found `npx` on its `PATH`. Replace `"npx"` with the output of `which npx`. Server logs are in `~/Library/Logs/Claude/` on macOS.

(back to top)

## Cursor Add the same `mcpServers` block to `.cursor/mcp.json` in the project, or `~/.cursor/mcp.json` for every project. Cursor reads a variable from your shell with `"TAVILY_API_KEY": "${env:TAVILY_API_KEY}"`.

(back to top)

## VS Code VS Code uses a `servers` key and a `type` field, in `.vscode/mcp.json`. An `inputs` entry asks for the key once and stores it: ```json { "inputs": [{ "type": "promptString", "id": "tavily-key", "description": "Tavily API key", "password": true }], "servers": { "stepgate": { "type": "stdio", "command": "npx", "args": ["-y", "stepgate", "market-research"], "env": { "TAVILY_API_KEY": "${input:tavily-key}" } } } } ```

(back to top)

## Clients that connect by URL Run the server yourself and point the client at it: ```sh TAVILY_API_KEY=tvly-... npx -y stepgate --http 3100 market-research ``` The endpoint is `http://127.0.0.1:3100/mcp`, using Streamable HTTP. It listens on the loopback address only. claude.ai connectors need a public HTTPS URL, which Stepgate does not provide.

(back to top)

## Your own agent Your agent needs an MCP client and a model that calls tools. Then: 1. List Stepgate's tools. 2. Pass them to your model, with the server's instructions (`getInstructions()` in the TypeScript SDK) as part of the system prompt. 3. Forward each tool call to Stepgate until a response's `structuredContent.state` is `finished` or `failed`. [server/examples/demo-client.ts](../server/examples/demo-client.ts) does this in about a hundred lines with any OpenAI-compatible API.

(back to top)

## What a run looks like 1. **The agent starts the run.** It calls the stepfile's tool with its inputs. Stepgate checks every credential and API first, then returns a run id and step 1: instructions, the operations it may call, and the output schema. 2. **The agent calls operations.** It calls them through `stepgate_call`. Stepgate makes each request, adds credentials, and returns the result. 3. **The agent submits.** It sends the step's output to `stepgate_submit`. - If a gate rejects it, the response says which gate and why, and how many attempts are left. - When every gate passes, the response is the next step. 4. **The run finishes.** After the last step, the response holds every step's output. Every response carries its content both as text and as `structuredContent`, because some clients, Claude Code among them, show the model only the structured part.

(back to top)

## When something goes wrong | What you see | What it means | |---|---| | `PreflightFailed: preflight failed for credential tavily` | `TAVILY_API_KEY` is not set in the server's environment. Set it in the client's configuration and restart the server. | | `PreflightFailed: ... setting jira-site` | A setting is missing, or its value does not match the setting's pattern. | | `GateFailed: step ... failed gates: ...` | The step used every attempt without passing; the gates named are the ones that failed last. | | `RunNotActive` | The run finished, failed, or sat idle past `--run-idle-ms` (30 minutes by default). Start it again. | | `CallLimitReached` | One step made more than `--calls-per-step` calls (100 by default), which usually means the agent was looping. | | The agent stops partway through | Ask it to continue the run; the run waits for its next call until it goes idle. | | A result ends in `[truncated: ...]` | Raise `--tool-result-chars`. Claude Code also caps each tool result at 25,000 tokens unless `MAX_MCP_OUTPUT_TOKENS` is set higher. |

(back to top)

## Your own stepfiles A stepfile does not need to be in the catalog to run. Pass its path, alone or next to catalog names: ```sh npx -y stepgate /Users/me/flows/invoice-check.stepfile.yaml market-research ``` ### Paths and ids - **Use an absolute path.** A relative path is resolved from the folder the client starts the server in. Claude Code starts it in the project folder; other clients do not say. - **Each file becomes one tool named after its `id`,** so every file you serve needs its own `id`. ### Mistakes and edits - **Mistakes show when the server starts.** Every file is validated at startup, and a broken one stops the server with the reason, such as `StepfileInvalid: stepfile invalid: /stepgate must be equal to constant`. - **The client shows this as a server that failed to start.** Claude Code's `/mcp` and Claude Desktop's logs carry the message. - **Without `--watch`, restart after editing.** Files are read once, at startup. In Claude Code, reconnect the server from `/mcp`. - **With `--watch`, Stepgate reloads a file when it changes** and tells the client its tools changed. A run in progress finishes on the version it started with. - **A file that breaks under `--watch` stays listed under its old name.** That covers a file that no longer loads, has been removed, or takes an id another served file has. Calling it reports why, rather than running the old version. ### Credentials and hosts - **Credentials and settings work as for catalog entries.** Credential `my-api` is read from `MY_API_API_KEY`, and setting `erp-host` from `ERP_HOST`. - **An `oauth2` credential with a `token_url` can take a refresh token instead:** `MY_API_REFRESH_TOKEN` and `MY_API_CLIENT_ID`. Stepgate refreshes the access token itself ([stepfile.md](stepfile.md#credentials)). - **Your own files may call `http://localhost`,** which helps while testing against a local API. Catalog entries must use public `https` URLs. ### Editor checking To have your editor check the file as you type, start it with this line. Editors using `yaml-language-server`, such as VS Code with the YAML extension, then validate every field: ```yaml # yaml-language-server: $schema=https://raw.githubusercontent.com/Chaarangan/stepgate/main/server/schema/stepfile.schema.json ```

(back to top)

## MCP servers that use MCP authorization Some remote MCP servers, such as Linear's and Notion's, issue tokens through their own OAuth authorization server rather than taking an API key. A stepfile declares such a credential as `oauth2` with the advertised `token_url`. You authorize it once: ```sh npx -y stepgate --auth ``` 1. Stepgate finds the server's authorization server, registers itself as a client, and prints a URL to open. 2. You approve access in the browser. 3. Stepgate prints the variables to set in the server's configuration: `_REFRESH_TOKEN`, `_CLIENT_ID` and `_RESOURCE`. When the server issues no refresh token, it prints `_API_KEY` instead. It writes nothing to disk. Where the authorization server offers no client registration, pass `--client-id` with a client you registered for `http://127.0.0.1` redirects.

(back to top)

## Writing stepfiles with an agent Every Stepgate server also offers tools for writing new stepfiles, so you can ask your agent for one in plain words: > Write a stepfile that converts an amount between currencies at the latest ECB rate, using the Frankfurter API. Try it with 250 USD to EUR. | Tool | What the agent gets | |---|---| | `stepgate_guide` | The authoring workflow, the full format reference and the JSON Schema | | `stepgate_examples` | The catalog as a list, or one entry's stepfile and README to copy a pattern from | | `stepgate_inspect_api` | What it needs to declare an API or MCP server ([details](#inspecting-an-api)) | | `stepgate_outline` | A skeleton stepfile from a SKILL.md or markdown SOP ([details](#outlining-a-procedure)) | | `stepgate_validate` | Every `TODO(...)` marker still to write, every issue in a draft with its path, or the draft's identity and whether it can be tried | | `stepgate_try` | A run of the draft from its text, driven with `stepgate_call` and `stepgate_submit` like any run | If you already have the procedure written down, as a skill or a runbook, start from it: > Turn my release-notes SKILL.md into a stepfile with stepgate_outline, then fill in the gates and try it. `npx -y stepgate` with no stepfiles starts a server that offers only these tools. ### Inspecting an API `stepgate_inspect_api` returns, for an OpenAPI document: - its sha256, to pin; - its servers and security schemes; - its operationIds, with each operation's arguments and success response; - operations without an operationId, with their definitions to copy inline. For an MCP server, it returns the server's tools and their `schema_sha256`. ### Outlining a procedure `stepgate_outline` turns a SKILL.md or markdown SOP into a skeleton stepfile: - **Steps.** One step per top-level numbered item where there are two or more; otherwise one per second-level heading. Code fences are ignored. - **Gates.** Each rule the document states (MUST, SHALL, never) becomes a gate to write. - **The rest.** Everything else is left as `TODO(...)` markers. ### What drafts may do Drafts are written by a model, so `stepgate_try` holds them to stricter rules than files you load yourself. - **Public URLs only.** A draft's tools must use public `https` URLs. - **Granted credentials and settings only.** A draft may declare only the credentials and settings you grant to drafts. Otherwise Stepgate would read their values from your environment and send them wherever the draft's author chose. ``` npx -y stepgate --draft-credential jira=acme.atlassian.net --draft-setting jira-site ``` - **Granted credentials go only to the hosts you named.** A granted credential is refused if the draft lists any other host, or a `token_url`. - **Inspection carries no credentials.** `stepgate_inspect_api` sends none, and reaches only public `https` URLs. - **Anything else is tried from a file.** A stepfile that needs a key you have not granted is tried by saving it and adding its path to the server's configuration, as in [your own stepfiles](#your-own-stepfiles). "Public" is decided from the URL's host name. Loopback, private and link-local addresses, and names without a dot, are refused. A public name that resolves to a private address is not caught, so run Stepgate where reaching any public host is acceptable.

(back to top)