AI agents
MCP for AI coding agents
Uses the Ahena CLI (beta). Install it with npm install -g @ahena/cli, or prefix commands with npx @ahena/cli. See Installation. The dashboard covers projects, connections, Doctor, the Stack Graph, plans and approvals without it.
ahena mcp runs an MCP server on stdio, so AI coding agents can understand a project's
whole external stack through Ahena, and change it only with the developer's approval.
It runs on the developer's machine in the project directory and calls the same Core API as the CLI and dashboard, as the signed-in user: Ahena's RBAC applies, every call is audited, and the activity log labels these calls via mcp.
Agent sessions
ahena mcp doesn't use your CLI login for its requests. From it, the server creates an
agent session (a token starting ahena_agent_, labelled MCP: <client name>, valid 12
hours) and uses only that; it makes a new one when the old one expires or stops working. Ahena
knows from the session itself that a request came through MCP (no header decides it), and
whatever your role, it refuses an agent session:
- approving or declining changes, or approving a CLI sign-in;
- revealing secret values;
- managing members, billing or the organization, or deleting projects;
- creating CI tokens or further agent sessions.
Everything else (reading, Doctor, planning, asking for approval) works as you. See approvals.
Set up
ahena login
ahena mcp # read-only
ahena mcp --allow-write # also lets the agent generate code and propose provider changes
Once @ahena/cli is published to npm, you can also run it without installing anything globally, through npx (the same server, fetched on first use):
claude mcp add ahena -- npx -y @ahena/cli mcp # Claude Code
{ "mcpServers": { "ahena": { "command": "npx", "args": ["-y", "@ahena/cli", "mcp"] } } }
With the CLI installed (see installation; command ahena), most MCP clients take a stdio command. For example, a project-level .mcp.json:
{ "mcpServers": { "ahena": { "command": "ahena", "args": ["mcp"] } } }
VS Code (.vscode/mcp.json):
{ "servers": { "ahena": { "type": "stdio", "command": "ahena", "args": ["mcp"] } } }
TOML-configured clients:
[mcp_servers.ahena]
command = "ahena"
args = ["mcp"]
Tools
| Tool | Class | What it does |
|---|---|---|
inspect_project |
READ | Project, environments, connections and health, your role, ahena.config.ts. |
inspect_stack |
READ | The Stack Graph: every capability's provider, connection, resources (bucket, domain, webhook endpoints, project ids), health, issues and next actions. |
explain_stack |
READ | The Stack Graph as plain, factual language. |
list_integrations |
READ | The capability registry and where each provider is connected. |
list_recipes |
READ | Recommended stacks (recipes). |
inspect_environment |
READ | Connections, secret names only, latest Doctor run. |
inspect_provider |
READ | Provider resources (no secrets). |
run_doctor |
READ | Doctor checks (recorded as a run). Each non-passing check has an explanation: what, why, where, impact, canAhenaFix, next. |
get_required_actions |
READ | Providers to connect, fixes Ahena can apply, code to generate, developer-only steps. |
plan_changes |
READ | ahena diff across providers. |
validate_environment |
READ | Environment safety (same checks as validate_deployment): test credentials in production, localhost callbacks/CORS, missing providers, drift. |
validate_deployment |
READ | Ready to deploy? Doctor failures and drift from ahena.lock. |
connect_provider |
READ | Returns the ahena connect command for the developer. Credentials never pass through agents. |
generate_adapter |
SAFE_WRITE | Integration layer in src/ahena/ (dry run by default; keeps your edits). |
create_migration |
SAFE_WRITE | Adds a SQL file under supabase/migrations. Never applies it. |
plan_capability |
READ | What adding a capability means for this app (from its type): provider and why, settings, everything it needs, costs, related packs. |
add_capability |
SAFE_WRITE | Declares the capability in ahena.config.ts and creates a stored plan (or says which ahena connect the developer runs first). |
apply_plan |
BILLABLE | Applies a stored plan under the approval policy below; reports partial failure exactly. |
configure_provider |
BILLABLE | Makes a provider match ahena.config.ts, under the approval policy below. |
fix_issue |
BILLABLE | Applies Doctor's fix for one check, under the same policy. |
SAFE_WRITE tools change only local files (generated code, new migration files), never a provider. No tool has the SECRET_ACCESS class: secrets are never returned to an agent, and no tool accepts one.
Approval policy
Without --allow-write, WRITE and BILLABLE tools refuse. With it:
| Change | Development / staging | Production |
|---|---|---|
| SAFE | Applied | Applied |
| CONFIRMATION_REQUIRED | Developer approves in the client | Developer approves in the dashboard |
| BILLABLE / DESTRUCTIVE | Developer approves in the dashboard | Developer approves in the dashboard |
Every change that isn't SAFE needs the developer in every environment: a development connection can point at the same DNS zone or provider account as production, so the environment's kind doesn't lower the bar.
In the dashboard (changes that matter: production, billable, destructive): Ahena accepts approval only from the developer's signed-in browser, never from the agent's session or the CLI's token, so no prompt answered on the developer's machine can approve them. The tool stores the changes as a plan, asks for approval and returns, with nothing changed:
{
"status": "needs_human",
"approvalUrl": "https://app.ahena.io/approvals/apr_…",
"planId": "pln_…",
"proposed": ["~ Configure CORS on leo-production: https://example.com (production) [CONFIRMATION_REQUIRED]"],
"message": "… Ask the developer to approve it in the Ahena dashboard: https://app.ahena.io/approvals/apr_…, then call apply_plan { \"plan_id\": \"pln_…\" } again."
}
The dashboard shows the developer exactly what Ahena will apply (built from its own re-plan,
not from text the agent sent). Once approved (within 60 minutes), apply_plan applies that
plan. Declined or expired: calling apply_plan again asks again; nothing runs without it. The
client's elicitation isn't used for these, even when it could ask.
In the client (CONFIRMATION_REQUIRED outside production): MCP elicitation asks the person,
not the model, with the exact list of changes. If any change in a plan needs approval, the
whole plan waits for it (actions can depend on each other). If the client can't ask,
nothing is changed and the tool tells the agent to ask the developer to review the changes and
run ahena configure <provider> -e <environment> (or ahena apply <planId>) themselves; the
agent isn't told to run it, and is never handed --yes. What's approved is exactly what was
planned: Ahena applies with the plan's fingerprint and refuses if the provider changed in
between.
Agents express intent by editing ahena.config.ts (git-safe and reviewable), then calling
plan_changes / configure_provider. They can't send arbitrary provider payloads.
Intent example
Developer: Add payments to this marketplace.
inspect_stack→ it's a marketplace on Next.js; Stripe isn't declared.plan_capability { capability: "payments" }→ Stripe Connect (because it's a marketplace), webhook + signing secret, handler route, env vars, themarketplacepack.add_capability { capability: "payments", environment: "production", domain: "leo.app" }→ declared inahena.config.ts; a stored planpln_…(or "runahena connect stripe").apply_plan { plan_id }→needs_humanwith the dashboard link (production). The agent gives the developer the link; they approve;apply_planagain → Ahena creates the webhook and stores its secret.- The agent writes the application code against the generated
ahena.payments;run_doctorverifies.
Example
Developer: Set up Stripe subscriptions for this application.
inspect_stack→ Stripe is connected in development.- The agent adds
payments.productsandpayments.webhooktoahena.config.ts. plan_changes { environment: "development" }→+ Stripe webhook,+ product Pro,+ price pro_monthly.configure_provider→ the developer approves in the client (creating products and webhooks isn't SAFE); applied in development. The webhook signing secret is stored encrypted in Ahena; the agent sees only its name.generate_adapter { write: true }→ payments adapter, webhook route, env mappings.run_doctor→ verified. In production the same steps stop for the developer's approval in the dashboard instead (needs_humanwith the link, thenapply_plan).