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. Usebudget checkfollowed bybudget confirmonly around a specific un-instrumented provider call you make yourself. Fail-open is explicit: when a check returnsallowed=truewithreservation_id=null, skip confirm. Do not leave repeated unconfirmed checks outstanding — each one temporarily holds budget and can cause self-denial. Treatprice_hintsas advisory relative prices — the cheapest lane is1, a local zero-cost lane is0— never as currency or permission to spend. - Run identity and termination. Set
SOLWYN_AGENT_RUN_IDonce and reuse it for the whole job so enforcement and reporting stay grouped. A denied check withreason=run_terminatedis an operator stop: end the job and do not retry under a new run id.solwyn runs stopstops future checks and lease renewals while in-flight requests finish. - Tags and attribution. Pass tags with repeated
--tag key=valueflags orSOLWYN_TAGS=key=value,key2=value2; flags override matching environment keys. Usesolwyn recordonly for a completed call that had no reservation, with one canonical lowercase UUID per logical call, reused on retry.budget confirmrecords attribution itself, so never alsorecordthat call. Denied checks record a savings receipt by default;--no-recordis an explicit opt-out. - Machine contract and free-tier backoff. Use
--jsonfor schema-bearing output; success schema names are additive-only and JSON errors usesolwyn.error.v1on stderr. Exit codes are frozen —0success,1generic/API,2usage,3auth,4budget denial,5not found. On a rate limit, honorRetry-Afteronce 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:
| Profile | Tools | Purpose |
|---|---|---|
read | 33 | Observe budgets, costs, runs, alerts, plans, team access, tags, and accounts. Nothing reserves or attributes spend. |
guardrails | 37 | The 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.
| Tool | What it does |
|---|---|
budget_status | Read budget utilization — a pure poll that reserves nothing |
budget_check | Reserve budget immediately before an un-instrumented call |
budget_confirm | Settle a non-null reservation after that call and record attribution |
budget_check_batch | Reserve for 1 to 32 calls at once, with a recoverable operation id |
record_call | Attribute a completed call that had no reservation |
get_project_status | Read a project's budget and provider health |
get_forecast | Read the month-to-date spend forecast |
get_costs, get_cost_total | Read grouped costs, or the uncapped total, with the full filter set |
list_budget_rules | Read rules, their routing, and their paused and snoozed states |
list_tags, list_tag_values | Browse attribution tags (Team and above) |
list_agent_runs, get_agent_run | Read runs and their lifetime cost |
list_alert_events | Read alert history with delivery receipts |
list_projects, get_overview | Account-level reads; login required |
list_cost_events, get_cost_baseline, get_unit_economics, get_cost_drift | Event pages and cost analytics |
list_priced_models, simulate_costs | Current priced targets and read-only historical repricing |
list_provider_failovers, list_untracked_surfaces | Failovers and advisory unmetered observations |
plan_status, plan_catalog, plan_preview | Lifecycle, prices and read-only estimates; preview requires JWT at any role |
get_whoami, get_account_phone | Selected identity, email verification, phone and SMS state |
list_alert_activity, list_open_alert_deliveries, get_account_alert | Account-wide activity, pending deliveries and account alert configuration |
budget_uncounted | Advisory, potentially overcounted unpriced calls/tokens |
list_audit_events | Account mutation history; JWT |
list_team_members, list_team_invites | Full 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:
export SOLWYN_AGENT_RUN_ID="job-$(uuidgen | tr A-F a-f)"
export SOLWYN_TAGS="env=prod,team=research" # optionalReserve. 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.
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.
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
ficonfirm 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
| Result | Meaning | Action |
|---|---|---|
exit 0, reservation_id set | Reserved | Spend, then confirm exactly once. |
exit 0, reservation_id: null | Fail-open; stderr says fail-open, unreserved — skip confirm | Spend, then skip confirm. Confirming a null reservation is a usage error. |
exit 4, denied_by_period a period | A hard_deny budget cap | Do 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 cap | Do not spend under that tag. |
exit 4, reason: "run_terminated" | An operator stopped the run | End the job. Do not retry under a new run id. |
exit 1 | API error, invalid response, or a batch item error | Treat the reservation as unknown; do not spend on it. Do not replay completed batch checks. |
exit 3 | No credential, a login where a key is required, or a read-only key | Fix 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:
$ 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": {
"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:
claude mcp add solwyn -- /absolute/path/to/solwyn --project proj_0123456789abcdef01234567 --api-url https://api.solwyn.ai mcp --profile readCredentials 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
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:
| Row | If degraded |
|---|---|
Credential empty or invalid | SOLWYN_API_KEY is set but blank or malformed. Unset it or fix it. |
Credential missing | Nothing in the keychain. solwyn login --with-key. |
| Supplied key project ≠ selected read project | The key writes to a different project than you read from. Reads follow --project; writes follow the key. |
Server / version mismatch | A different Solwyn installation is being launched. Fix --executable or PATH shadowing. |
Profile / inventory mismatch | The 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.
Related
mcp/mcp-config— the full tool catalogue, result envelope, and work boundsbudget— every option forcheck,confirm, andstatus, including batch checks and denial receiptsrecordandruns— 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