Generating MCP tools
An app can expose a set of MCP tools — named actions an agent calls, like
purchase_order.create, instead of driving your interface. WAID drafts those tools
from your source. Nothing becomes callable until you say so.
waid new → waid app generate-mcp → review → waid app syncThat is the whole pipeline. waid new scaffolds the agent runtime and
generate-mcp scans your source itself, so there is no init or scan command in
between.
The CLI reference has the flags. This page is about what the steps mean and where the safety boundary sits.
Generation never exposes a tool. Every newly generated tool is recorded as
denied, and only a person changes that. Nothing is sent anywhere until sync.
1. Generate#
waid app generate-mcp --in ./frontendwaid app generate-mcp --in ./frontend --agent claudeThere is no separate setup or scan step to run first. generate-mcp does the
whole thing in five stages:
| Stage | What happens |
|---|---|
| 1 | Prepares source evidence: scaffolds the agent runtime if missing, then scans your source for candidates. |
| 2 | Hands that evidence to a local coding agent — codex or claude, auto-selected if you don't choose — to author real MCP handlers. |
| 3 | Re-scans the source the agent wrote. |
| 4 | Validates the generated catalog against the snapshot contract. |
| 5 | Builds and tests the project. |
What you get is a worktree diff to review. No session is required, because nothing leaves your machine.
Two files matter afterwards:
| File | What it is |
|---|---|
waid/mcp/generated/tools.json | The verified inventory: every tool generation produced. |
waid/mcp/curation.json | The review ledger: your decision about each tool. |
The agent runtime under src/waid/agent/ is scaffolded by waid new, and
generate-mcp re-creates it if it is missing, so neither waid actions init nor
waid app scan is a step you need to run. waid app scan --check remains useful
on its own: it reports whether the candidate list still matches your source.
2. Review#
Every new tool arrives in curation.json as denied. Open it and promote the ones
you have actually read:
{ "tools": [ { "name": "purchase_order.create", "state": "exposed" }, { "name": "admin.reset_tenant", "state": "denied" } ]}Generation never changes a decision you have already made. Re-running
generate-mcp cannot quietly re-expose something you denied, and cannot withdraw
something you approved — it only appends tools that are new, denied.
This is the human boundary in the pipeline. A tool an agent can reach is one a person deliberately allowed.
3. Sync#
waid login --env local --tenant <tenant-key>waid app sync --in ./frontendSubmits an immutable candidate to ICE Develop. Only tools marked exposed are
projected; denied ones are not sent.
Sync needs a session, and the mode follows it: local → development, staging →
staging, prod → production. An incompatible explicit --mode is rejected. The
app's hosted URL comes from WAID_WEB_URL in that mode — see
Modes and env files.
Sync is not publication. Publishing a candidate, installing it for a tenant, and granting access are separate, deliberate steps in ICE. A successful sync does not make anything live.
Services#
A JVM service follows the same steps with the same guarantees, and the same
absorption — waid service generate-mcp scans the Java source itself, so
waid service scan is not a step you run first:
waid service generate-mcp --in ./my-service --agent codexwaid service sync --in ./my-servicewaid service sync --in ./my-service --release 2026.10.1The origin comes from splenta.service.public-base-url in the service's
application-<env>.properties; --url overrides it. Staging sync needs a
--release.