SOLWYN

Agent integration

How coding agents use Solwyn — the agent-context guardrails brief, the MCP server and its two profiles, the check/confirm/record loop, run identity, and how to wire and verify a client

Solwyn gives coding agents a wrapper-side budget guardrail. It ships three things for that: a plain-text guardrails brief you can paste into an agent's context (solwyn agent-context), a local server that exposes Solwyn's operations as tools (solwyn mcp), and a command that prints the launch descriptor a client needs (solwyn mcp-config). All of them enforce the same rule — an agent guards the spend it makes itself, and never the spend the Solwyn SDK already tracks.

The one rule

Never wrap an SDK-instrumented call with a budget check or confirm. The Solwyn SDK already reserves budget before that call and confirms it after; checking it again double-counts — the reservation is held twice, and a project can end up denying itself. Budget check and confirm are only for un-instrumented spend an agent makes directly.

This works because the CLI is a wrapper-side guardrail, not a proxy. Its tools move quantities — token estimates and counts, dollars, tags — never a prompt or a response. See Privacy for the full wire contract.

solwyn agent-context

solwyn agent-context prints the canonical "Solwyn budget guardrails" document as plain text, with no credential and no network. Pipe it into an agent's system context or save it alongside your other rules. It opens by stating that the contract follows Solwyn SDK 0.6 semantics, and has five sections:

  • Polling and reservations. Poll with solwyn --json budget status — a pure read that holds no budget. Use budget check followed by budget confirm only around a specific un-instrumented provider call you make yourself. Fail-open is explicit: when a check returns allowed=true with reservation_id=null, skip confirm. Do not leave repeated unconfirmed checks outstanding — each one temporarily holds budget and can cause self-denial. Treat price_hints as advisory relative prices — the cheapest lane is 1, a local zero-cost lane is 0 — never as currency or permission to spend.
  • Run identity and termination. Set SOLWYN_AGENT_RUN_ID once and reuse it for the whole job so enforcement and reporting stay grouped. A denied check with reason=run_terminated is an operator stop: end the job and do not retry under a new run id. solwyn runs stop stops future checks and lease renewals while in-flight requests finish.
  • Tags and attribution. Pass tags with repeated --tag key=value flags or SOLWYN_TAGS=key=value,key2=value2; flags override matching environment keys. Use solwyn record only for a completed call that had no reservation, with one canonical lowercase UUID per logical call, reused on retry. budget confirm records attribution itself, so never also record that call. Denied checks record a savings receipt by default; --no-record is an explicit opt-out.
  • Machine contract and free-tier backoff. Use --json for schema-bearing output; success schema names are additive-only and JSON errors use solwyn.error.v1 on stderr. Exit codes are frozen — 0 success, 1 generic/API, 2 usage, 3 auth, 4 budget denial, 5 not found. On a rate limit, honor Retry-After once for at most ten seconds; on the free tier, cache and back off rather than starting a retry storm.
  • Trust boundary. Keep Solwyn credentials in the OS keychain only — never echo them, never place them in config files or argv. Solwyn must never collect, store, or proxy provider keys, prompts, or responses. The CLI sends only your own spend events, under a random per-invocation SDK instance id, with no telemetry. Do not add telemetry or update checks.

solwyn init --agents writes these same guardrails into your repository's agent files as a one-time setup step. See solwyn init.

solwyn mcp — the local tool server

solwyn mcp runs a local MCP server over stdio. It advertises tools only — no prompts, no resources — and offers two fixed profiles:

ProfileToolsPurpose
read33Observe budgets, costs, runs, alerts, plans, team access, tags, and accounts. Nothing reserves or attributes spend.
guardrails37The read set plus budget_check, budget_confirm, budget_check_batch, and record_call, for agents that spend.

Bare solwyn mcp serves guardrails, for compatibility with 0.1.0; mcp-config and doctor --mcp default to read. Choose read unless the agent makes un-instrumented provider calls itself.

ToolWhat it does
budget_statusRead budget utilization — a pure poll that reserves nothing
budget_checkReserve budget immediately before an un-instrumented call
budget_confirmSettle a non-null reservation after that call and record attribution
budget_check_batchReserve for 1 to 32 calls at once, with a recoverable operation id
record_callAttribute a completed call that had no reservation
get_project_statusRead a project's budget and provider health
get_forecastRead the month-to-date spend forecast
get_costs, get_cost_totalRead grouped costs, or the uncapped total, with the full filter set
list_budget_rulesRead rules, their routing, and their paused and snoozed states
list_tags, list_tag_valuesBrowse attribution tags (Team and above)
list_agent_runs, get_agent_runRead runs and their lifetime cost
list_alert_eventsRead alert history with delivery receipts
list_projects, get_overviewAccount-level reads; login required
list_cost_events, get_cost_baseline, get_unit_economics, get_cost_driftEvent pages and cost analytics
list_priced_models, simulate_costsCurrent priced targets and read-only historical repricing
list_provider_failovers, list_untracked_surfacesFailovers and advisory unmetered observations
plan_status, plan_catalog, plan_previewLifecycle, prices and read-only estimates; preview requires JWT at any role
get_whoami, get_account_phoneSelected identity, email verification, phone and SMS state
list_alert_activity, list_open_alert_deliveries, get_account_alertAccount-wide activity, pending deliveries and account alert configuration
budget_uncountedAdvisory, potentially overcounted unpriced calls/tokens
list_audit_eventsAccount mutation history; JWT
list_team_members, list_team_invitesFull roster at any JWT role; invitations with owner/admin JWT

Billing and team writes, account rename/recovery and invitation credential workflows remain CLI-only. plan_preview is read-only despite using POST and works in both profiles. Opt-in with_last_fired on list_budget_rules requires JWT. get_costs(account=true) also requires JWT and excludes explicit project selection. See the exact tool and credential table.

Credentials are resolved on every tool call from SOLWYN_API_KEY or the OS keychain — never at startup, and never during tool discovery. A denied budget check is a successful result with allowed: false, not an error, so a model reads the decision rather than a failure. Every call runs inside a fixed 30-second deadline and byte caps, and an oversized result still returns the reservation and call ids it knows. mcp has the full catalogue, envelope, and limits.

The check → confirm loop

For un-instrumented spend — a provider call the SDK does not wrap — the guardrail is three steps, whether the agent calls the tools or the CLI. Set the run identity first:

Terminal
export SOLWYN_AGENT_RUN_ID="job-$(uuidgen | tr A-F a-f)"
export SOLWYN_TAGS="env=prod,team=research"   # optional

Reserve. budget_check (or solwyn budget check) reserves budget before the call. Mint your own call id now — a canonical lowercase UUID — so you can reuse it if anything needs retrying.

Terminal
CALL_ID=$(uuidgen | tr A-F a-f)
CHECK=$(solwyn --json --project "$PROJ" budget check \
  --provider openai --model gpt-4o --estimated-input-tokens 1200 --estimated-output-tokens 400)
RID=$(printf '%s' "$CHECK" | jq -r '.reservation_id // empty')

Spend. Make the provider call yourself — only if the check exited 0.

Settle. budget_confirm (or solwyn budget confirm) settles the reservation with the real usage. Skip it when the reservation was null.

Terminal
if [ -n "$RID" ]; then
  solwyn --json budget confirm --reservation-id "$RID" --call-id "$CALL_ID" \
    --provider openai --model gpt-4o --input-tokens 1180 --output-tokens 320
fi

confirm records attribution under the same call id, so do not also record that call. A call that ran with no reservation — one made before the guardrail, or a local model at $0 — is the fourth branch: report it afterwards with record. And never run this loop around an SDK-instrumented call; to tag SDK spend to a named run instead, see Agent runs.

What to do with each check result

ResultMeaningAction
exit 0, reservation_id setReservedSpend, then confirm exactly once.
exit 0, reservation_id: nullFail-open; stderr says fail-open, unreserved — skip confirmSpend, then skip confirm. Confirming a null reservation is a usage error.
exit 4, denied_by_period a periodA hard_deny budget capDo not spend. Back off or reduce the estimate. Quote the emitted call_id when reporting.
exit 4, denied_by_period: "tag"A scoped tag cap; stderr says denied by tag capDo not spend under that tag.
exit 4, reason: "run_terminated"An operator stopped the runEnd the job. Do not retry under a new run id.
exit 1API error, invalid response, or a batch item errorTreat the reservation as unknown; do not spend on it. Do not replay completed batch checks.
exit 3No credential, a login where a key is required, or a read-only keyFix credentials; the hint names the remedy. Agents need a full-scope key.

After a confirm, inspect settlement and recording separately. A known successful settlement is retained even if attribution failed. ingested or a call-ID deduplication acknowledges the event; a legacy_key duplicate remains unknown in MCP and failed in CLI. Keep the first exact recording_retry and use record-only after known settlement, preserving call/attempt identity. A lost confirmation reply leaves settlement unknown; media recovery explains the receipt window and the limit when the first event was never received.

Wiring a client

solwyn mcp-config prints the descriptor. Its command is this installation's absolute executable and its args carry the resolved project and API URL, so run it from the machine and shell the agent will use:

Terminal
$ solwyn --json --project proj_0123456789abcdef01234567 mcp-config
{"schema":"solwyn.mcp-config.v1","command":"/absolute/path/to/solwyn","args":["--project","proj_0123456789abcdef01234567","--api-url","https://api.solwyn.ai","mcp","--profile","read"]}

Claude Code, Claude Desktop, Cursor, and Windsurf all take the same mcpServers shape — paste it into .mcp.json, claude_desktop_config.json, or ~/.cursor/mcp.json:

mcpServers
{
  "mcpServers": {
    "solwyn": {
      "command": "/absolute/path/to/solwyn",
      "args": ["--project", "proj_0123456789abcdef01234567", "--api-url", "https://api.solwyn.ai", "mcp", "--profile", "read"]
    }
  }
}

For an agent that spends, change the last argument to "guardrails". For Claude Code from the terminal:

Terminal
claude mcp add solwyn -- /absolute/path/to/solwyn --project proj_0123456789abcdef01234567 --api-url https://api.solwyn.ai mcp --profile read

Credentials are never in this file. The descriptor never contains a key. Either store a project key in the OS keychain with solwyn login --with-key — the server reads it on every call — or add an env block with SOLWYN_API_KEY if your host supports one. An empty or malformed SOLWYN_API_KEY blocks keychain fallback rather than silently selecting another identity.

For account tools, use user login. Credential precedence is environment key, stored key, then stored JWT; a key masks a login rather than falling back on a role failure. Writes use the key's project, while read tools can use an explicit project selector. Profile visibility, project selection, key scope, account role and Core authorization are separate checks.

MCP is bundled with published CLI v0.3.0, with no extra. The September PyPI 0.2.0 artifact predates this 33/37 inventory; inspect your installed version and MCP discovery. The MCP Registry draft remains unpublished and its proposed namespace unverified.

Diagnosing the server

Terminal
solwyn --project proj_0123456789abcdef01234567 doctor --mcp [--profile read|guardrails] [--executable <path>]

doctor --mcp captures your credential once, then runs two independent lanes: a direct HTTP read with it, and a real MCP client that launches the installed server in an isolated subprocess, verifies the server's name and version, lists the tools for the profile, and — with a project key — makes one read-only budget_status call through the server. Exit 0 means discovery, key identity, and an authenticated read all passed. Read a failing report top-down:

RowIf degraded
Credential empty or invalidSOLWYN_API_KEY is set but blank or malformed. Unset it or fix it.
Credential missingNothing in the keychain. solwyn login --with-key.
Supplied key project ≠ selected read projectThe key writes to a different project than you read from. Reads follow --project; writes follow the key.
Server / version mismatchA different Solwyn installation is being launched. Fix --executable or PATH shadowing.
Profile / inventory mismatchThe launched server's tool set differs from this install's catalogue — same cause.
MCP read skipped with "Captured JWT…"Expected under a login: a JWT cannot be forwarded to the child. Store a project key for a fully green run.

The result lands under solwyn.doctor.mcp.v1. doctor covers the non-MCP checks.

  • mcp / mcp-config — the full tool catalogue, result envelope, and work bounds
  • budget — every option for check, confirm, and status, including batch checks and denial receipts
  • record and runs — attribute un-reserved spend, and inspect or stop a run
  • Scripting and CI — the JSON envelope and frozen exit codes the tools return
  • solwyn init — write the guardrails into your repo with --agents

On this page