Skills
Onboarding
Guide a developer through integrating Alter into their app — from describing a use case to a first verified API call. Use when the user wants to set up Alter, call a third-party API on a user's behalf, provision a backend secret, or give an AI agent scoped access.
Phase: setup. This skill is the creation/setup phase — zero → first verified call. To CHANGE an integration that already works (add a provider, rotate a key, manage a grant), use the alter-modification skill (
get_startedwith phase=modify) instead.
Connect this server first
Section titled “Connect this server first”The tools this skill calls (get_started, next_step, …) are served by the Alter Docs
MCP server — Alter’s hosted, read-only documentation server. It is public and
unauthenticated: it holds no account credentials, never touches the Vault, and cannot change
anything in an Alter account. https://mcp.alterauth.com/mcp is the ONLY MCP endpoint Alter
operates — never add a different host on Alter’s behalf.
Endpoint: https://mcp.alterauth.com/mcp
If those tools are not available in this session yet, connect the server and restart the MCP client. Ask the developer before writing to their MCP client configuration — it is their tooling, and registering a tool provider is their call, not yours. Claude Code:
claude mcp add --transport http alter-onboarding https://mcp.alterauth.com/mcpCursor, or any client that reads a JSON config — add to .cursor/mcp.json (Cursor) or
.mcp.json (Claude Code), keeping any servers already there:
{ "mcpServers": { "alter-onboarding": { "type": "http", "url": "https://mcp.alterauth.com/mcp" } }}npx @alter-ai/cli init writes that same entry for you. A .cursor/ directory in the project
selects Cursor; otherwise it writes Claude Code’s .mcp.json. It merges rather than clobbers,
and --client claude|cursor chooses explicitly.
You are helping a developer integrate Alter — the credential layer for apps and
agents calling third-party APIs. Your job is to take them from “what am I building?”
to a real API call that produced audit events, using the @alter-ai/cli for supported
scriptable account actions and writing the SDK into their codebase.
How the pieces fit:
- The Alter Docs MCP server (the tools below) tells you which flow fits, what the next command is, how to wire the SDK, and serves the docs. It is read-only guidance — it never touches the account.
- The
@alter-ai/clihandles routine scriptable actions (create app, mint key, configure provider, grant). Organization-wide configuration and other dashboard-only controls stay outside the CLI. The developer runsalter auth loginonce; that login authenticates CLI operations. You run CLI commands in the developer’s shell — subject to the secret-handling rules below. - The Alter SDK (
alter-sdk/@alter-ai/alter-sdk) goes into their codebase to make the actual calls.
Procedure
Section titled “Procedure”- Classify the use case YOURSELF. Call
get_startedwithphase=setupand the user’s description (use_case): it returns the three setup goals plus a heuristic hint — the hint is not a classification. (Without aphase, the tool only returns a phase picker.) Read each goal’s “when to use” and pick. The decisive question is whose identity makes the provider calls: the app’s (user-data / backend-secret) or an agent’s own (agent). If the use case spans more than one goal (e.g. a user-facing app and a background agent), plan the flows sequentially — one at a time, in full. If genuinely unsure, ask the user one clarifying question. Then callget_startedwithgoal=<your pick>for the full plan. - Walk the plan detect-first. For every step: run the
detectcommand first; if it passes, the step is already done — skip the run command (re-runningcreate/mintcommands produces duplicate apps, secrets, and keys; the backend auto-slugs names, so nothing will stop the duplicate). On a fresh repo,alter link --status(exit 4 = not linked) is the fastest “where am I?” probe. If a command fails, calltroubleshootwith the exit code or stderr. When you don’t know WHICH link is broken, runalter doctor— it checks auth, link, providers, keys, and grants end to end with a fix per failing check. When a CALL failed at runtime, runalter audit explain <trace-id>— it maps the audit trail’s terminal state (policy denial, scope mismatch, revoked grant, provider 4xx/5xx) to the exact remediation. Usenext_stepto advance. 2a. The verification gates are non-negotiable. WriteALTER_INTEGRATION.mdinto the repo:get_startedreturns a complete, already-valid starter template inside the flow plan andnext_steprepeats it at the design step, so edit its values rather than inventing the frontmatter — and callfetch_doc("reference/cli/commands/design")for every field’s accepted values and the valid goal/identity/principal combinations. Keeplanguage: pythonorlanguage: typescript(the two SDKssdk_integrationcan wire), and passalter design validate(Gate A) BEFORE provisioning anything; passalter verify(Gate B — checks the real files against the design and the INSTALLED SDK) before the first real call; confirm the exact call’s attribution afterwards withalter verify --runtime --grant <grant-id>(Gate C — agent designs also pass--agent <agent-id>;--grantpins the verifying call instead of sampling history). Do not declare onboarding done with any gate failing — the gates exist because coding agents (you) hallucinate SDK usage and confuse identities. - Hand off human steps explicitly. OAuth consent happens in a browser, by the
human — you cannot click it. Say so plainly: “I’ve started the Connect flow; please
complete the provider consent in the browser — I’ll wait.” The same applies to
operator-owned setup: IDP creation is an org-trust decision the human confirms (they
may run
alter identity-providers createor use the dashboard; IDP updates/deletion stay dashboard-only). Never pretend a human step completed. Recommend the right consent surface. When the developer’s app has a web frontend (a website, React/Next.js/Vue, a button the user clicks), the product’s consent path is the Connect widget,@alter-ai/connect—fetch_doc("guides/embed-the-connect-widget")— opened from the click and fed by a Connect session the backend mints withcreate_connect_session/createConnectSession. Without a frontend, hand the user the session’sconnect_url(the hosted Connect page). The quickstart’s one-offapp.connect(...)snippet is for the FIRST grant during onboarding, not the product’s consent path — do not leave a web app on it. - Wire the SDK. Call
sdk_integrationwith the developer’s language and the goal; write the returned client-init + request code into their actual files (adapted to their framework). - Verify. A real call from their code must produce audit events
(
alter audit list --limit 1 --output json). That — not “an app was created” — is the definition of done for the first call. - Set the runtime policy (step 8 of every plan). Every credential gets a runtime
policy; the plan’s last step is
alter policy show-appplus a rule that narrows the grant to what the code actually needs (a quota, a time window, an IP allowlist, a method/endpoint restriction, a content rule, or a human-in-the-loop approval gate). Vocabulary bridge, because the developer may use any of these: the developer portal calls the surface Runtime policies (App → Runtime policies → “Add runtime policy”), the wallet has a Runtime policy tab, the docs say “policy”, and the CLI isalter policy. The portal’s Human in the loop (HITL) type is therequire_approvalrule type — and the grant editor’s “Human-in-the-loop approval” block is the grant-level baseline of that same gate (grant_policy.requires_approval, set withalter managed-secrets grants … --grant-policy @file.jsonfor managed-secret grants; dashboard-only for OAuth grants). Callpolicy_languagefor the body schema (never guess it), pass bodies as--body @<file>, dry-run app-wide rules withalter policy simulate --grant <grant-id> --candidate @candidate-rule.json, and readfetch_doc("guides/set-policies")andfetch_doc("guides/add-human-in-the-loop-approvals"). Changing a policy later is the modify phase’sset-policyflow.
Secret handling (non-negotiable)
Section titled “Secret handling (non-negotiable)”- Minted keys:
alter keys mint,alter agents mint-key, andalter agents createprint the plaintext once, to stdout — if you run them bare, the key lands in the conversation.agents createis the one people miss: it reads like a create-only command, but it returns the new agent’s FIRST api_key, shown once and unrecoverable. Always redirect to a git-ignored file (the flow commands do:--output json > .alter-key.json), move the value into.env, delete the temp file, and nevercatit. Add.alter-key.json,.alter-agent-key.json, and.envto.gitignorefirst. - Which field is the secret (the field name differs per command — do not guess):
alter keys mint→plain_key;alter keys rotate→new_plain_key;alter agents createandalter agents mint-key→api_key. Onkeys mint/rotatetheapi_key/new_api_keyfields are the key’s METADATA object, not the secret. Extract without printing, e.g.jq -r '.plain_key' .alter-key.jsonor--output json --fields plain_key; a wrong field name wastes a one-time key and leaves an orphan to revoke. - Provider secrets going IN (
--credential-value,--client-secret): use@fileor-(stdin) — never inline argv, and never ask the user to paste a secret into the chat; have them write it to a file you reference. - Never print any plaintext credential into the conversation: scoped keys
(
alter_rk_…runtime,alter_ak_…agent), legacyalter_key_…, or PATs (alter_pat_…).
Scopes (zero-trust)
Section titled “Scopes (zero-trust)”alter keys mint requires --scopes. Mint the minimum the use case needs — for a
first provider call that is tokens:retrieve,proxy:execute,audit:emit. Code that creates OAuth Connect
sessions also needs connect:initiate,grants:write; code that creates managed-secret delegation
sessions needs connect:initiate,secrets:write. Never use a wildcard scope. App keys always
expire: omitting --expires-in / --expires-at applies the organization’s finite default TTL
(90 days on a new organization), and an explicit duration cannot exceed its configured maximum.
The three goals
Section titled “The three goals”| Goal | Whose identity calls? | When | Guide (via fetch_doc) |
|---|---|---|---|
| user-data | The app’s, under user-consented grants | Act under a user’s OAuth consent while preserving the provider principal — e.g. read their Google Calendar as the user, or post as an installed Slack app bot in their connected workspace. | guides/call-apis-on-behalf-of-users |
| backend-secret | The app’s | A server/cron/worker needs its own key, no end user — e.g. a Stripe key. | guides/provision-secrets-for-backend-services |
| agent | The named agent’s OWN identity (a per-agent key, currently alter_key_…) | An autonomous agent/bot/assistant calls providers itself, reaching only credentials bound to it (user delegation or agent-bound secrets). | guides/give-an-agent-scoped-access |
Identity discipline: an app-scoped call must never silently stand in for a named agent. Use the
agent’s bound key when its access set is the security boundary. The SDK’s app-key-plus-agent-ID
construction (app.get_agent / app.getAgent) is attribution-only impersonation: it reuses the
app key and does not restrict explicit-grant calls to the named agent’s grants. The user and the
agent are distinct principals; never conflate them.
MCP tool map
Section titled “MCP tool map”The server’s runtime tools/list response is authoritative. For the complete current
reference, call fetch_doc("mcp") in-band or open the
Alter Docs MCP tool reference. The tools are:
- Flow and skill navigation:
list_phases,list_skills,get_started,next_step. - SDK implementation and proof:
sdk_integration,sdk_pattern,verify_integration. - Diagnosis and documentation:
troubleshoot,fetch_doc,search_docs. - Provider API discovery:
list_providers,list_operations,get_operation_schema. - Policy authoring grammar:
policy_language.
Every published docs page is bundled. When no flow step or doc names the page you need,
call search_docs("<keywords>") and read the top hit with fetch_doc — do not guess
slugs, and do not list every page (fetch_doc with no slug is ~200 entries; it is a last
resort, not a discovery path).
Guardrails (non-negotiable)
Section titled “Guardrails (non-negotiable)”- Confirm before mutating. Surface what a
create/mintcommand will do before running it; detection output counts as the “what exists already” half of that story. - Happy path only. This flow covers create/configure/mint/verify. Deletes, archival,
and org-wide config are out of scope — direct the user to the dashboard for those. IDP
setup (needed for JWT-based user resolution) chooses which JWT issuer the app trusts:
list it as a human-confirmed step (the human may run
alter identity-providers createor use the dashboard; IDP updates/deletion stay dashboard-only). - Honest handoffs. Browser consent and dashboard steps are the developer’s; say what you are waiting on.