Reference
MCP tools
Descant speaks the Model Context Protocol. The server builds one tool per programmatic operation from the same registry the API reference and the command tree come from, so a tool cannot exist here that the API does not serve, and the three surfaces cannot drift apart. Every tool on this page is the command beside it.
It advertises itself as descant, and a tenant key reaches the 72 tools below.
The tools
One tool per programmatic operation a tenant key can reach. The read-only column is the tool's own readOnlyHint annotation, which a client may act on.
| tool | the same command | read-only |
|---|---|---|
| customer_runs_list | descant run list | yes |
| customer_runs_start | descant run start | no |
| customer_runs_get | descant run show | yes |
| customer_runs_activity | descant run activity | yes |
| customer_runs_cancel | descant run cancel | no |
| customer_runs_restart | descant run restart | no |
| customer_runs_split-scorecard-get | descant run split-scorecard | yes |
| customer_repos_list | descant repo list | yes |
| customer_repos_connect | descant repo connect | no |
| customer_repos_activate | descant repo activate | no |
| customer_repos_disconnect | descant repo disconnect | no |
| customer_repos_reconnect | descant repo reconnect | no |
| customer_repos_validate | descant repo validate | no |
| customer_repos_get | descant repo get | yes |
| customer_repos_update | descant repo update | no |
| customer_repos_settings-audit | descant repo settings-audit | yes |
| customer_repos_poller-status | descant repo poller-status | yes |
| customer_repos_config | descant repo config | yes |
| customer_repos_issues_list | descant repo issues | yes |
| customer_repos_labels_list | descant repo labels | yes |
| customer_repos_poller-diagnostics | descant repo diagnostics | yes |
| customer_audit_list | descant audit list | yes |
| customer_keys_list | descant key list | yes |
| customer_keys_create | descant key create | no |
| customer_keys_revoke | descant key revoke | no |
| customer_keys_rotate | descant key rotate | no |
| customer_invites_list | descant invite list | yes |
| customer_invites_create | descant invite create | no |
| customer_invites_revoke | descant invite revoke | no |
| customer_invites_send | descant invite send | no |
| customer_repos_grooming_candidates | descant repo grooming candidates | yes |
| customer_repos_grooming_next-candidates | descant repo grooming next-candidates | yes |
| customer_repos_grooming_document | descant repo grooming document | yes |
| customer_repos_grooming_dispatch | descant repo grooming dispatch | no |
| customer_repos_order-preview | descant repo order-preview | yes |
| customer_repos_ordering-preview | descant repo ordering-preview | yes |
| customer_settings_engine-tier-get | descant settings engine-tier get | yes |
| customer_settings_engine-tier-set | descant settings engine-tier set | no |
| customer_billing_credit-pack-list | descant billing credit-pack list | yes |
| customer_billing_credit-pack-buy | descant billing credit-pack buy | no |
| customer_billing_auto-reload-get | descant billing auto-reload get | yes |
| customer_billing_auto-reload-set | descant billing auto-reload set | no |
| customer_settings_repo-defaults-get | descant settings repo-defaults get | yes |
| customer_settings_repo-defaults-set | descant settings repo-defaults set | no |
| customer_settings_profile-get | descant settings profile get | yes |
| customer_settings_usage-get | descant settings usage get | yes |
| customer_settings_usage-set | descant settings usage set | no |
| customer_team_members-list | descant team member list | yes |
| customer_team_organization-get | descant team organization get | yes |
| customer_team_organization-rename | descant team organization rename | no |
| customer_team_invitations-list | descant team invitation list | yes |
| customer_team_invitations-create | descant team invitation create | no |
| customer_team_invitations-revoke | descant team invitation revoke | no |
| customer_team_members-remove | descant team member remove | no |
| customer_analytics_cost-by-label-get | descant analytics cost-by-label get | yes |
| customer_analytics_cost-get | descant analytics cost get | yes |
| customer_analytics_session-insights-get | descant analytics session-insights get | yes |
| customer_analytics_throughput-get | descant analytics throughput get | yes |
| customer_runs_overview-get | descant run overview | yes |
| customer_billing_balance-get | descant billing balance get | yes |
| customer_billing_spend-cap-set | descant billing spend-cap set | no |
| customer_billing_ledger-list | descant billing ledger list | yes |
| customer_billing_run-cost-list | descant billing run-cost list | yes |
| customer_notifications_list | descant notifications list | yes |
| customer_notifications_mark-read | descant notifications mark-read | no |
| customer_notifications_email-set | descant notifications email set | no |
| customer_settings_privacy-get | descant settings privacy get | yes |
| customer_settings_privacy-set | descant settings privacy set | no |
| customer_sso_get | descant sso get | yes |
| customer_capabilities_list | descant capability list | yes |
| customer_capabilities_decide | descant capability decide | no |
| customer_security_get | descant security get | yes |
Arguments
A tool takes one flat object. The server routes each field to the part of the request the contract puts it in — params, query or body — so a caller never has to know the shape of the URL. The envelope's own fields are below, and are not listed here.
| tool | arguments |
|---|---|
| customer_runs_list | createdAfter (query), createdBefore (query), limit (query), pageToken (query), repositoryId (query), state (query) |
| customer_runs_start | issueNumber (body), tenantRepoId (body) |
| customer_runs_get | runId (params) |
| customer_runs_activity | runId (params) |
| customer_runs_cancel | runId (params) |
| customer_runs_restart | runId (params) |
| customer_runs_split-scorecard-get | runId (params) |
| customer_repos_list | takes no arguments |
| customer_repos_connect | owner (body), repo (body) |
| customer_repos_activate | id (params) |
| customer_repos_disconnect | id (params), confirmRepo (body) |
| customer_repos_reconnect | id (params) |
| customer_repos_validate | id (params) |
| customer_repos_get | id (params) |
| customer_repos_update | id (params), areaLabels (body), automaticPickup (body), autoMergeEnabled (body), defaultBaseBranch (body), docRoots (body), eligibilityLabel (body), eligibilityMode (body), excludeAssigned (body), labelMap (body), maxConcurrentRunsThisRepo (body), orderQueries (body), orderStrategy (body), paused (body), workflowsWriteEnabled (body) |
| customer_repos_settings-audit | id (params) |
| customer_repos_poller-status | id (params) |
| customer_repos_config | id (params) |
| customer_repos_issues_list | id (params) |
| customer_repos_labels_list | id (params) |
| customer_repos_poller-diagnostics | id (params) |
| customer_audit_list | limit (query), pageToken (query), principal (query) |
| customer_keys_list | limit (query), pageToken (query) |
| customer_keys_create | expiresAt (body), label (body), scopes (body) |
| customer_keys_revoke | keyId (params) |
| customer_keys_rotate | keyId (params), expiresAt (body), graceSeconds (body) |
| customer_invites_list | takes no arguments |
| customer_invites_create | takes no arguments |
| customer_invites_revoke | id (params) |
| customer_invites_send | code (body), email (body) |
| customer_repos_grooming_candidates | id (params) |
| customer_repos_grooming_next-candidates | id (params), answered (body), pending (body) |
| customer_repos_grooming_document | id (params) |
| customer_repos_grooming_dispatch | id (params), edgeId (body), expected (body), from (body), kind (body), mutationId (body), nextKind (body), op (body), to (body) |
| customer_repos_order-preview | id (params), eligibilityLabel (body), eligibilityMode (body), excludeAssigned (body), labelMap (body), orderQueries (body), orderStrategy (body) |
| customer_repos_ordering-preview | id (params), query (body) |
| customer_settings_engine-tier-get | takes no arguments |
| customer_settings_engine-tier-set | sopranoTier (body) |
| customer_billing_credit-pack-list | takes no arguments |
| customer_billing_credit-pack-buy | expectedTerms (body), packKey (body), paymentMethodId (body) |
| customer_billing_auto-reload-get | takes no arguments |
| customer_billing_auto-reload-set | amountCredits (body), enabled (body), targetCredits (body), thresholdCredits (body) |
| customer_settings_repo-defaults-get | takes no arguments |
| customer_settings_repo-defaults-set | autoMergeEnabled (body), onAbandonedPr (body), paused (body) |
| customer_settings_profile-get | takes no arguments |
| customer_settings_usage-get | takes no arguments |
| customer_settings_usage-set | maxConcurrentRuns (body) |
| customer_team_members-list | limit (query), pageToken (query) |
| customer_team_organization-get | takes no arguments |
| customer_team_organization-rename | name (body) |
| customer_team_invitations-list | takes no arguments |
| customer_team_invitations-create | email (body), role (body) |
| customer_team_invitations-revoke | id (params) |
| customer_team_members-remove | userId (params) |
| customer_analytics_cost-by-label-get | rollup (query), since (query), until (query) |
| customer_analytics_cost-get | bucket (query), dim (query), kind (query), repo (query), since (query), until (query) |
| customer_analytics_session-insights-get | takes no arguments |
| customer_analytics_throughput-get | repo (query), since (query), until (query) |
| customer_runs_overview-get | takes no arguments |
| customer_billing_balance-get | takes no arguments |
| customer_billing_spend-cap-set | capCredits (body) |
| customer_billing_ledger-list | createdAfter (query), createdBefore (query), kind (query), limit (query), pageToken (query) |
| customer_billing_run-cost-list | limit (query), pageToken (query) |
| customer_notifications_list | takes no arguments |
| customer_notifications_mark-read | id (params) |
| customer_notifications_email-set | email (body) |
| customer_settings_privacy-get | takes no arguments |
| customer_settings_privacy-set | sessionAnalysisOptedOut (body) |
| customer_sso_get | takes no arguments |
| customer_capabilities_list | takes no arguments |
| customer_capabilities_decide | id (params), key (params), decision (body), escalationId (body) |
| customer_security_get | takes no arguments |
Dry runs, confirmation and idempotency keys
Every tool takes a dryRun beyond its own arguments. Validate this call and report the request that WOULD be sent, without sending it. No state changes and nothing is spent.
| field | what it does | taken by | required by |
|---|---|---|---|
| confirm | Its value must be the operation id — a tool list does not otherwise distinguish a write from the reads beside it. | customer_runs_start, customer_runs_cancel, customer_runs_restart, customer_repos_connect, customer_repos_activate, customer_repos_disconnect, customer_repos_reconnect, customer_repos_validate, customer_repos_update, customer_keys_create, customer_keys_revoke, customer_keys_rotate, customer_invites_create, customer_invites_revoke, customer_invites_send, customer_repos_grooming_dispatch, customer_settings_engine-tier-set, customer_billing_credit-pack-buy, customer_billing_auto-reload-set, customer_settings_repo-defaults-set, customer_settings_usage-set, customer_team_organization-rename, customer_team_invitations-create, customer_team_invitations-revoke, customer_team_members-remove, customer_billing_spend-cap-set, customer_notifications_mark-read, customer_notifications_email-set, customer_settings_privacy-set, customer_capabilities_decide | every tool that takes it |
| dryRun | Validate this call and report the request that WOULD be sent, without sending it. No state changes and nothing is spent. | every tool | none |
| idempotencyKey | customer_runs_start, customer_runs_cancel, customer_runs_restart, customer_repos_connect, customer_repos_activate, customer_repos_disconnect, customer_repos_reconnect, customer_repos_validate, customer_repos_update, customer_keys_create, customer_keys_revoke, customer_keys_rotate, customer_invites_create, customer_invites_revoke, customer_invites_send, customer_repos_grooming_dispatch, customer_settings_engine-tier-set, customer_billing_credit-pack-buy, customer_billing_auto-reload-set, customer_settings_repo-defaults-set, customer_settings_usage-set, customer_team_organization-rename, customer_team_invitations-create, customer_team_invitations-revoke, customer_team_members-remove, customer_billing_spend-cap-set, customer_notifications_mark-read, customer_notifications_email-set, customer_settings_privacy-set, customer_capabilities_decide | customer_billing_credit-pack-buy |
Refusals and retries
A refusal comes back carrying a retry field — what to do next, as a verb — beside the code and the guidance. Branch on retry: it is the same closed set of refusals the command surface answers with, read for action rather than for cause, and it draws a distinction an exit code cannot. Where the binary has one integer for every estate fault, this tells an upstream credential an operator must repair apart from a timeout worth retrying.
| retry | refusals that produce it |
|---|---|
| retry | idempotency_key_in_flight, internal_error, upstream_timeout, upstream_unavailable |
| retry-after | capacity_exhausted, rate_limited, upstream_rate_limited |
| verify-first | internal_error, outcome_unknown |
| fix-request | conflict, idempotency_key_reused, invalid_body, invalid_path, invalid_query, invite_limit_reached, not_found |
| fix-credential | forbidden, github_link_required, unauthenticated |
| check-deployment | upstream_auth |
| check-billing | billing_paused |
Each code carries the same meaning it has on the wire, and the full table — every refusal a customer call can meet, with its HTTP status and the binary's exit code beside it — sits on the scripting page.
Credentials
Where the credential comes from is a property of the transport, not of the server. The table below is the environment of the stdio server in the server package, which an operator starts by hand and which is not shipped to customers: it reads these three variables once at startup and takes no position on where the value should be held. descant mcp, the stdio server a customer runs, reads two of them: the key, after the one descant login stored, and DESCANT_API_BASE_URL, which it defaults to https://app.descant.run when unset. It finds its own credential and serves the customer catalogue. Connect an agent covers that path.
| variable | required | what it sets |
|---|---|---|
| DESCANT_API_BASE_URL | yes | The origin every tool call is sent to. There is deliberately no default — a stale built-in origin would mean a server started with the wrong one presents your key to whatever answers there. |
| DESCANT_API_KEY | yes | The tenant key presented on each call. An empty value counts as absent, so a tool refuses locally and names the variable rather than spending a round trip to be told by a 401. |
| DESCANT_MCP_AUDIENCE | no | Which catalogue the server builds. Absent, it is customer — the audience that can do least. It is declared rather than inferred from whichever token happens to be present. |
export DESCANT_API_BASE_URL=https://app.descant.run export DESCANT_API_KEY=dsc_…
Over HTTP, none of these is read. The caller presents its own bearer on the request and the server builds itself around that one credential for that one call, so nothing is held between calls and there is no environment to set. The origin is fixed by whoever mounts the handler rather than by a variable, and the audience is a constant there — customer — not a setting, so that a mount cannot serve the operator surface by passing a different string.
Transports
The server speaks two transports, and the tool surface above is identical on both.
stdio — a server process on your own machine, finding its own credential and speaking JSON-RPC over standard input and output. This is the shape an MCP client launches and supervises itself. It presents the key stored on the machine, else an exported one; with neither, the first tool call that needs a key opens your browser to sign you in, from cli-v0.1.8 onwards. The descant binary serves this same catalogue over stdio as descant mcp, from cli-v0.1.1 onwards — connect an agent carries the configuration block.
For operators only, descant mcp --admin serves the operator tools beside the ones above. They are listed from the moment the server starts, and the first operator tool call made with no operator credential on the machine opens the same browser sign-in descant login --admin runs, for an allowlisted admin to approve. What it stores is a short-lived credential, in a named slot of its own in your credential store — never a long-lived fleet token. Without the flag nothing changes: a tenant key never sees an operator tool.
HTTP — a request in, a response out, with the caller presenting its own key rather than the server holding one. It serves the customer audience and nothing else, and it copies exactly two headers from your request onto the call it makes — x-forwarded-for and x-real-ip — so rate limiting keys on the caller you already are, not on one shared bucket for everyone.
To drive Descant from a program today, the shipped surface is the descant binary: install it in one line and read scripting and agents for its JSON output, exit codes and refusals. Every tool above is a command there.
Signing in from a remote client
A remote MCP client that supports OAuth sign-in needs only the endpoint's address — no key to paste:
https://app.descant.run/api/v1/mcp
Discovery is automatic. The client's first call carries no key and is answered 401 with a WWW-Authenticate header that points at the endpoint's sign-in metadata. From there the client finds where to register and where to send you, registers itself, and opens your browser at the Descant sign-in you already use. Once signed in you see which application is asking and where the answer will be sent, choose the organization and the permissions it gets, and approve. Only an organization owner can approve.
The client then receives a short-lived key carrying exactly the permissions you approved, and calls the tools above with it. The key appears in your API key list, so you can see it and revoke it like any other.
A key you mint yourself still works. A client configured to send authorization: Bearer <your key> on every request is admitted exactly as before, and never sees the sign-in. Browser sign-in adds a way in; it removes none. Connect an agent covers that configuration.