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.

toolthe same commandread-only
customer_runs_listdescant run listyes
customer_runs_startdescant run startno
customer_runs_getdescant run showyes
customer_runs_activitydescant run activityyes
customer_runs_canceldescant run cancelno
customer_runs_restartdescant run restartno
customer_runs_split-scorecard-getdescant run split-scorecardyes
customer_repos_listdescant repo listyes
customer_repos_connectdescant repo connectno
customer_repos_activatedescant repo activateno
customer_repos_disconnectdescant repo disconnectno
customer_repos_reconnectdescant repo reconnectno
customer_repos_validatedescant repo validateno
customer_repos_getdescant repo getyes
customer_repos_updatedescant repo updateno
customer_repos_settings-auditdescant repo settings-audityes
customer_repos_poller-statusdescant repo poller-statusyes
customer_repos_configdescant repo configyes
customer_repos_issues_listdescant repo issuesyes
customer_repos_labels_listdescant repo labelsyes
customer_repos_poller-diagnosticsdescant repo diagnosticsyes
customer_audit_listdescant audit listyes
customer_keys_listdescant key listyes
customer_keys_createdescant key createno
customer_keys_revokedescant key revokeno
customer_keys_rotatedescant key rotateno
customer_invites_listdescant invite listyes
customer_invites_createdescant invite createno
customer_invites_revokedescant invite revokeno
customer_invites_senddescant invite sendno
customer_repos_grooming_candidatesdescant repo grooming candidatesyes
customer_repos_grooming_next-candidatesdescant repo grooming next-candidatesyes
customer_repos_grooming_documentdescant repo grooming documentyes
customer_repos_grooming_dispatchdescant repo grooming dispatchno
customer_repos_order-previewdescant repo order-previewyes
customer_repos_ordering-previewdescant repo ordering-previewyes
customer_settings_engine-tier-getdescant settings engine-tier getyes
customer_settings_engine-tier-setdescant settings engine-tier setno
customer_billing_credit-pack-listdescant billing credit-pack listyes
customer_billing_credit-pack-buydescant billing credit-pack buyno
customer_billing_auto-reload-getdescant billing auto-reload getyes
customer_billing_auto-reload-setdescant billing auto-reload setno
customer_settings_repo-defaults-getdescant settings repo-defaults getyes
customer_settings_repo-defaults-setdescant settings repo-defaults setno
customer_settings_profile-getdescant settings profile getyes
customer_settings_usage-getdescant settings usage getyes
customer_settings_usage-setdescant settings usage setno
customer_team_members-listdescant team member listyes
customer_team_organization-getdescant team organization getyes
customer_team_organization-renamedescant team organization renameno
customer_team_invitations-listdescant team invitation listyes
customer_team_invitations-createdescant team invitation createno
customer_team_invitations-revokedescant team invitation revokeno
customer_team_members-removedescant team member removeno
customer_analytics_cost-by-label-getdescant analytics cost-by-label getyes
customer_analytics_cost-getdescant analytics cost getyes
customer_analytics_session-insights-getdescant analytics session-insights getyes
customer_analytics_throughput-getdescant analytics throughput getyes
customer_runs_overview-getdescant run overviewyes
customer_billing_balance-getdescant billing balance getyes
customer_billing_spend-cap-setdescant billing spend-cap setno
customer_billing_ledger-listdescant billing ledger listyes
customer_billing_run-cost-listdescant billing run-cost listyes
customer_notifications_listdescant notifications listyes
customer_notifications_mark-readdescant notifications mark-readno
customer_notifications_email-setdescant notifications email setno
customer_settings_privacy-getdescant settings privacy getyes
customer_settings_privacy-setdescant settings privacy setno
customer_sso_getdescant sso getyes
customer_capabilities_listdescant capability listyes
customer_capabilities_decidedescant capability decideno
customer_security_getdescant security getyes

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.

toolarguments
customer_runs_listcreatedAfter (query), createdBefore (query), limit (query), pageToken (query), repositoryId (query), state (query)
customer_runs_startissueNumber (body), tenantRepoId (body)
customer_runs_getrunId (params)
customer_runs_activityrunId (params)
customer_runs_cancelrunId (params)
customer_runs_restartrunId (params)
customer_runs_split-scorecard-getrunId (params)
customer_repos_listtakes no arguments
customer_repos_connectowner (body), repo (body)
customer_repos_activateid (params)
customer_repos_disconnectid (params), confirmRepo (body)
customer_repos_reconnectid (params)
customer_repos_validateid (params)
customer_repos_getid (params)
customer_repos_updateid (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-auditid (params)
customer_repos_poller-statusid (params)
customer_repos_configid (params)
customer_repos_issues_listid (params)
customer_repos_labels_listid (params)
customer_repos_poller-diagnosticsid (params)
customer_audit_listlimit (query), pageToken (query), principal (query)
customer_keys_listlimit (query), pageToken (query)
customer_keys_createexpiresAt (body), label (body), scopes (body)
customer_keys_revokekeyId (params)
customer_keys_rotatekeyId (params), expiresAt (body), graceSeconds (body)
customer_invites_listtakes no arguments
customer_invites_createtakes no arguments
customer_invites_revokeid (params)
customer_invites_sendcode (body), email (body)
customer_repos_grooming_candidatesid (params)
customer_repos_grooming_next-candidatesid (params), answered (body), pending (body)
customer_repos_grooming_documentid (params)
customer_repos_grooming_dispatchid (params), edgeId (body), expected (body), from (body), kind (body), mutationId (body), nextKind (body), op (body), to (body)
customer_repos_order-previewid (params), eligibilityLabel (body), eligibilityMode (body), excludeAssigned (body), labelMap (body), orderQueries (body), orderStrategy (body)
customer_repos_ordering-previewid (params), query (body)
customer_settings_engine-tier-gettakes no arguments
customer_settings_engine-tier-setsopranoTier (body)
customer_billing_credit-pack-listtakes no arguments
customer_billing_credit-pack-buyexpectedTerms (body), packKey (body), paymentMethodId (body)
customer_billing_auto-reload-gettakes no arguments
customer_billing_auto-reload-setamountCredits (body), enabled (body), targetCredits (body), thresholdCredits (body)
customer_settings_repo-defaults-gettakes no arguments
customer_settings_repo-defaults-setautoMergeEnabled (body), onAbandonedPr (body), paused (body)
customer_settings_profile-gettakes no arguments
customer_settings_usage-gettakes no arguments
customer_settings_usage-setmaxConcurrentRuns (body)
customer_team_members-listlimit (query), pageToken (query)
customer_team_organization-gettakes no arguments
customer_team_organization-renamename (body)
customer_team_invitations-listtakes no arguments
customer_team_invitations-createemail (body), role (body)
customer_team_invitations-revokeid (params)
customer_team_members-removeuserId (params)
customer_analytics_cost-by-label-getrollup (query), since (query), until (query)
customer_analytics_cost-getbucket (query), dim (query), kind (query), repo (query), since (query), until (query)
customer_analytics_session-insights-gettakes no arguments
customer_analytics_throughput-getrepo (query), since (query), until (query)
customer_runs_overview-gettakes no arguments
customer_billing_balance-gettakes no arguments
customer_billing_spend-cap-setcapCredits (body)
customer_billing_ledger-listcreatedAfter (query), createdBefore (query), kind (query), limit (query), pageToken (query)
customer_billing_run-cost-listlimit (query), pageToken (query)
customer_notifications_listtakes no arguments
customer_notifications_mark-readid (params)
customer_notifications_email-setemail (body)
customer_settings_privacy-gettakes no arguments
customer_settings_privacy-setsessionAnalysisOptedOut (body)
customer_sso_gettakes no arguments
customer_capabilities_listtakes no arguments
customer_capabilities_decideid (params), key (params), decision (body), escalationId (body)
customer_security_gettakes 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.

fieldwhat it doestaken byrequired by
confirmIts 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_decideevery tool that takes it
dryRunValidate this call and report the request that WOULD be sent, without sending it. No state changes and nothing is spent.every toolnone
idempotencyKeycustomer_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_decidecustomer_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.

retryrefusals that produce it
retryidempotency_key_in_flight, internal_error, upstream_timeout, upstream_unavailable
retry-aftercapacity_exhausted, rate_limited, upstream_rate_limited
verify-firstinternal_error, outcome_unknown
fix-requestconflict, idempotency_key_reused, invalid_body, invalid_path, invalid_query, invite_limit_reached, not_found
fix-credentialforbidden, github_link_required, unauthenticated
check-deploymentupstream_auth
check-billingbilling_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.

variablerequiredwhat it sets
DESCANT_API_BASE_URLyesThe 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_KEYyesThe 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_AUDIENCEnoWhich 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.