Catalog and discovery
Browse the server catalog, search it by intent, and manage per-user provider connections from the terminal.
Eight commands cover the catalog and the per-user connections behind it: servers list, servers show, tools list, discover, connect list, connect start, connect revoke and wizard. None of them moves money, and only connect start and connect revoke change anything. Six go straight to a REST route. discover and wizard take a longer path: they open a session, run a meta-tool inside it, and close the session on the way out.
Every flag, message and exit code on this page was run against @codespar/cli 0.10.0 — the version on npm — and the transcripts are the captured bytes of those runs.
What every command here shares
Credentials and scope
Resolution order, first match wins: the root flag, then the environment variable, then ~/.codespar/config.json.
| Setting | Root flag | Environment | Config key |
|---|---|---|---|
| API key | --api-key <key> | CODESPAR_API_KEY | apiKey |
| API host | --base-url <url> | CODESPAR_BASE_URL | baseUrl |
| Project | --project <id> | CODESPAR_PROJECT | project |
With nothing set, the host is https://api.codespar.dev. On the six commands that call REST directly, a resolved project rides as the x-codespar-project header and an unresolved one leaves the header off. discover and wizard hand the project to the SDK constructor instead.
Without a key, every command on this page refuses before it opens a socket:
codespar servers list✗ Not logged in. Run `codespar login` or set CODESPAR_API_KEY.That line is identical on all eight, and discover and wizard give it too: the key is resolved before the SDK is constructed. A key the API rejects is a different story, and the per-command tables below carry it.
stdout, stderr and --json
Tables, key/value blocks and JSON go to stdout. Status lines go to stderr, prefixed ℹ, ✓ or ⚠, and an empty table prints (no results) there too. A pipe into jq therefore receives the data alone.
--json is declared on the root command, but Commander accepts it on either side of the subcommand: codespar --json servers list and codespar servers list --json both printed the same JSON with an empty stderr. Colour is dropped when stdout is not a TTY, when NO_COLOR is set, or when FORCE_COLOR=0.
Exit codes
| Code | When | First line of stderr |
|---|---|---|
0 | The command finished, including --help | nothing |
1 | A refusal the CLI raises itself, or an argument Commander rejects | ✗ <message>, or error: <message> for Commander's own |
2 | Any other exception. The stack trace follows | ✗ internal error: |
discover and wizard reach the API through the SDK, and the SDK throws a plain Error, not the CLI's own error type. Measured: a 401 or a 403 on either command comes out as ✗ internal error: plus a stack trace and exit 2, where the six REST commands give exit 1 and one line. A tool call that answers success: false does the same. On these two commands, exit 2 is not evidence of a bug in the CLI.
codespar servers list
Lists the server catalog your key can see, one row per server, filtered server-side by whatever you pass.
https://api.codespar.dev/v1/servers| Name | Type | Required | What it does |
|---|---|---|---|
-c, --category <name> | string | no | Sent as the category query parameter. The CLI does not validate the value |
-r, --region <code> | string | no | Sent as the region query parameter. The flag's help names BR and MX as examples; any string is passed through |
--json | boolean | no | Root flag. Prints the array inside the response envelope, not the envelope |
Measured wire: codespar servers list -c payments -r BR issued GET /v1/servers?category=payments®ion=BR. An absent filter is left off the query string, never sent empty.
codespar servers listID NAME CATEGORY REGION TOOLS STATUS
asaas Asaas payments BR 12 live
stark-bank Stark Bank banking BR 8 live
pomelo Pomelo issuing - - betaA column with no value prints -. With no rows at all, stdout is empty and stderr carries (no results).
Calls GET /v1/servers, documented at GET /v1/servers in the HTTP reference.
| When | What you get | Exit |
|---|---|---|
| No API key resolved | ✗ Not logged in. Run `codespar login` or set CODESPAR_API_KEY. | 1 |
| Key rejected or under-scoped | ✗ GET /v1/servers → 403: <the API's own message> | 1 |
| Host unreachable, or slower than 30s | ✗ Network error calling GET /v1/servers: <cause>, or ✗ Request to GET /v1/servers timed out after 30000ms. | 1 |
codespar servers show <id>
Reads one server's detail record and prints its metadata, its description and the tools it exposes.
https://api.codespar.dev/v1/servershttps://api.codespar.dev/v1/servers/{id}/toolshttps://api.codespar.dev/v1/servers/{id}/auth-schema| Name | Type | Required | What it does |
|---|---|---|---|
<id> | string | yes | Positional. Percent-encoded into the path |
--json | boolean | no | Root flag. Prints the response body as received |
codespar servers show asaasID asaas
Name Asaas
Category payments
Region BR
Status live
Auth api_key
Tools 12
Brazilian payment provider: Pix, boleto and card acquiring.
Tools:
• codespar_charge — Issue an inbound charge
• codespar_pay — Send an outbound paymentThe Tools count prefers the record's own count and falls back to the length of the returned tool list. The description paragraph and the Tools: block are printed only when the response carries them.
| When | What you get | Exit |
|---|---|---|
codespar servers show with no id | error: missing required argument 'id' | 1 |
| Unknown id | ✗ GET /v1/servers/<id> → 404: <the API's own message> | 1 |
| Key rejected or under-scoped | ✗ GET /v1/servers/<id> → 403: <the API's own message> | 1 |
| No API key resolved | ✗ Not logged in. Run `codespar login` or set CODESPAR_API_KEY. | 1 |
codespar tools list
Lists the tools of one server, with each description cut to fit a terminal column.
https://api.codespar.dev/v1/servers/{id}/tools| Name | Type | Required | What it does |
|---|---|---|---|
-s, --server <id> | string | yes | The server whose tools to list. It is the address of the listing, not a filter |
--json | boolean | no | Root flag. Prints the array with descriptions at full length |
Changed in 0.8.0. --server used to be an optional filter and is now required: the API lists tools per server, so the server is the address of the listing. Without it the command refuses without touching the network, and names the two commands that lead to an id.
codespar tools list --server asaasNAME DESCRIPTION
cancel_subscription Cancel an active subscription.
create_customer Create a customer. Required before creating any payment — Asaas requi…
create_payment Create a payment charge (Pix, boleto, or credit card) linked to a cus…
24 tool(s) on asaas.The DESCRIPTION column is truncated to 70 characters; --json carries the full text.
Calls GET /v1/servers/{id}/tools, which the HTTP reference documents.
| When | What you get | Exit |
|---|---|---|
No --server | The refusal above, naming codespar servers list and codespar tools meta. Nothing is sent | 1 |
| Unknown server id | ✗ GET /v1/servers/<id>/tools → 404: <the API's own message> | 1 |
| Key rejected | ✗ GET /v1/servers/<id>/tools → 401: <the API's own message> | 1 |
| No API key resolved | ✗ Not logged in. Run `codespar login` or set CODESPAR_API_KEY. | 1 |
codespar discover <query>
Searches the catalog by intent instead of by name: it opens a session attached to no server, runs the codespar_discover meta-tool inside it, prints the ranked matches and closes the session.
https://api.codespar.dev/v1/sessions/{id}/execute| Name | Type | Required | What it does |
|---|---|---|---|
<query> | string | yes | Positional. Sent as use_case |
--limit <n> | integer | no | Must parse as a positive integer or the command refuses. Sent as limit, and used again locally as the number of rows printed. With the flag absent the CLI prints at most 10 and sends no limit |
--category <name> | string | no | Sent as category when present |
--country <code> | string | no | Sent as country when present. The flag's help names BR and MX as examples |
-u, --user <id> | string | no | The session's user_id. Defaults to the literal cli-user. The default lives in the command body, not in Commander, so --help does not print it |
--json | boolean | no | Root flag. Prints the whole result object, not the table's five columns |
Measured wire, in order: POST /v1/sessions with {"servers":[],"user_id":"cli-user"}, then POST /v1/sessions/{id}/execute with {"tool":"codespar_discover","input":{"use_case":"…","category":"…","country":"…","limit":5}}, then DELETE /v1/sessions/{id}. The session is closed in a finally, so it is closed even when the call fails.
codespar discover "issue an NF-e for a service"ℹ Strategy: embedding · use_case: "issue an NF-e for a service"
# SCORE SERVER.TOOL CONN DESCRIPTION
1 0.887 codespar.codespar_invoice connected Issue an NF-e / NFS-e fiscal document after settlement.
2 0.609 codespar.codespar_charge not_required Issue an inbound charge so there is something to invoice.
ℹ Next steps:
- Connect a fiscal provider
- Call codespar_invoice with the charge idThree things the table does not say for itself:
SCOREis computed by the CLI, not returned by the API. It is1 - cosine_distancewhen a cosine distance is present, the trigram similarity when it is not, and-when neither is, always to three decimals. The stub'scosine_distance: 0.1132printed as0.887.- The
Strategyline and theNext steps:label go to stderr; the table and the step items go to stdout. The label and its list are split across the two channels. CONNis the match's connection status, one ofconnected,disconnectedornot_required, taken from the SDK's own type. It is what tells you whether the tool you just found is callable today or needs a connection first.
With no match at all, stderr carries (no matches) and stdout stays empty.
Calls the codespar_discover meta-tool over POST /v1/sessions/{id}/execute. The tool's own arguments and result are documented at codespar_discover.
| When | What you get | Exit |
|---|---|---|
codespar discover with no query | error: missing required argument 'query' | 1 |
--limit 0, --limit abc, any non-positive or unparseable value | ✗ --limit must be a positive integer. | 1 |
| No API key resolved | ✗ Not logged in. Run `codespar login` or set CODESPAR_API_KEY. | 1 |
| Key rejected or under-scoped | ✗ internal error: and a stack trace naming createSession failed: 403 … | 2 |
The tool call answers success: false | ✗ internal error: and a stack trace naming discover failed: <the tool's message> | 2 |
codespar connect list
Lists the per-user provider connections your project holds, with the dates cut to the day.
https://api.codespar.dev/v1/connections| Name | Type | Required | What it does |
|---|---|---|---|
-u, --user <id> | string | no | Sent as the user_id query parameter. Unset means every user |
--status <s> | string | no | Sent as status. The flag's help names connected, pending, revoked and expired; the CLI validates nothing and passes the string through |
--json | boolean | no | Root flag. Prints the array inside the envelope, with full timestamps |
This route answers a connections envelope where servers list and tools list get a data envelope. It matters only if you read the raw HTTP; --json unwraps it either way.
codespar connect list --user user_42 --status connectedID SERVER USER STATUS CONNECTED EXPIRES
ca_7Fq2xb asaas user_42 connected 2026-09-02 -
ca_9Kd0mn mercadopago user_42 connected 2026-09-09 2026-12-08CONNECTED and EXPIRES are the first ten characters of the ISO timestamp, so the time of day is visible only under --json. A null date prints -.
Calls GET /v1/connections, documented at GET /v1/connections in the HTTP reference.
| When | What you get | Exit |
|---|---|---|
| Key rejected or under-scoped | ✗ GET /v1/connections → 403: <the API's own message> | 1 |
| No API key resolved | ✗ Not logged in. Run `codespar login` or set CODESPAR_API_KEY. | 1 |
| No connection matches the filters | (no results) on stderr, empty stdout | 0 |
codespar connect start <server>
Opens a Connect Link flow for one end user and prints the authorize URL they have to visit.
https://api.codespar.dev/v1/connect/start| Name | Type | Required | What it does |
|---|---|---|---|
<server> | string | yes | Positional. Sent as server_id in the body |
-u, --user <id> | string | no | Sent as user_id. Defaults to the literal cli-user, a default that lives in the command body and not in --help |
-r, --redirect-uri <url> | string | no | Sent as redirect_uri. Defaults to http://localhost:3000/connect/success so the flow completes without a hosted page of your own |
--scopes <scopes> | string | no | Sent as scopes when present, omitted from the body when absent. Passed through as one string |
--open | boolean | no | Ask for the link to be opened in the system browser. See the note below: it is a request, not a guarantee |
--no-open | boolean | no | Never open the browser. Print the link only |
--json | boolean | no | Root flag. Prints the response body and nothing else |
Measured body: {"server_id":"asaas","user_id":"user_42","redirect_uri":"https://app.exemplo.com.br/connect/done","scopes":"payments:read"}.
codespar connect start asaas --user user_42ℹ Connect asaas for user user_42:
https://connect.codespar.dev/l/lnk_3Bq8
ℹ Link expires 13/09/2026, 15:00:00
ℹ Tip: pass --open on a future run to launch the link automatically.The authorize URL is the only thing on stdout: a blank line, the URL indented by two spaces, a blank line. The expiry is rendered in the machine's own locale and timezone, so it is not a stable string to parse; --json carries the expires_at instant.
--open opens the browser only when stdout is a TTY, despite its help text reading "Force-open the link". Measured with stdout in a pipe: --open opened nothing, and a bare run and --no-open printed byte-identical output. The one visible difference in a pipe is that --open suppresses the Tip: line. The TTY branch itself was not exercised, because exercising it opens a real browser.
Before the OS opener is called, the URL is parsed and checked to be http or https, then passed as a literal argument with no shell in between, so a link carrying shell metacharacters cannot run a command. Both refusals, the malformed URL and the non-http scheme, are swallowed by a catch in this command: nothing is printed and the exit code stays 0. The URL is on stdout either way, which is what the fallback assumes.
Calls POST /v1/connect/start, documented at POST /v1/connect/start in the HTTP reference. The concept behind it is Connect Links.
| When | What you get | Exit |
|---|---|---|
codespar connect start with no server | error: missing required argument 'server' | 1 |
| Key rejected or under-scoped | ✗ POST /v1/connect/start → 403: <the API's own message> | 1 |
| No API key resolved | ✗ Not logged in. Run `codespar login` or set CODESPAR_API_KEY. | 1 |
codespar connect revoke <connection-or-server>
Revokes one connection, taking either the connection id or a server id it resolves on your behalf.
https://api.codespar.dev/v1/connections/{id}/revoke| Name | Type | Required | What it does |
|---|---|---|---|
<connection-or-server> | string | yes | Positional. An argument starting with ca_ is treated as a connection id and revoked directly. Anything else is treated as a server id and resolved first |
-u, --user <id> | string | no | Only used on the resolve path. Defaults to the literal cli-user |
The resolve path costs one extra request: GET /v1/connections?user_id=<user>&server_id=<arg>&status=connected, and then the first row of that list is the one revoked. Measured: codespar connect revoke asaas -u user_42 issued that query and then POST /v1/connections/ca_7Fq2xb/revoke.
When a user holds more than one active connection to the same server, the first row of the list wins and the output names only the id it chose. The ordering the API applies to that list was not measured. Pass the ca_ id when you need to be sure which connection goes away.
codespar connect revoke ca_7Fq2xb✓ Revoked connection ca_7Fq2xb.This command has no JSON path. --json is accepted and ignored: measured, codespar connect revoke ca_7Fq2xb --json exits 0 with empty stdout and the same success line on stderr. A script that pipes it into a JSON parser gets an empty string.
Calls POST /v1/connections/{id}/revoke, documented at POST /v1/connections/{id}/revoke in the HTTP reference.
| When | What you get | Exit |
|---|---|---|
| A server id with no active connection for that user | ✗ No active connection for server "<arg>" and user "<user>". | 1 |
| No argument | error: missing required argument 'connection-or-server' | 1 |
| Key rejected or under-scoped | ✗ POST /v1/connections/<id>/revoke → 403: <the API's own message>, or the same shape on the lookup that precedes it | 1 |
| No API key resolved | ✗ Not logged in. Run `codespar login` or set CODESPAR_API_KEY. | 1 |
codespar wizard [server-id]
Answers what a provider needs before it can be connected: which are connectable, what one connection's state is, and which secrets and steps a new connection takes.
https://api.codespar.dev/v1/sessions/{id}/execute| Name | Type | Required | What it does |
|---|---|---|---|
[server-id] | string | no | Positional, and the only optional positional in the CLI. Sent as server_id when present |
--action <name> | string | no | list, status or initiate. Validated by the CLI before anything opens. When absent the CLI sends no action at all, so the default is the backend's; the flag's help says status with a server-id and list without |
--country <code> | string | no | Sent as country. The flag's help scopes it to the list action |
--environment <env> | string | no | live or test, validated by the CLI. The flag's help scopes it to initiate |
--return-to <path> | string | no | Sent as return_to. The flag's help says a dashboard path, validated /dashboard/* on the dashboard side |
-u, --user <id> | string | no | The session's user_id. Defaults to the literal cli-user |
--json | boolean | no | Root flag. Prints the whole result, including the fields the human rendering drops |
Measured wire for an initiate: POST /v1/sessions with {"servers":[],"user_id":"cli-user"}, then POST /v1/sessions/{id}/execute with {"tool":"codespar_manage_connections","input":{"server_id":"asaas","action":"initiate","country":"BR","environment":"test","return_to":"/dashboard/connections"}}, then DELETE /v1/sessions/{id}.
The human output has three shapes, and the shape is chosen by the action the result carries, not by the flag you passed.
SERVER AUTH STATUS DIFFICULTY CONNECTED
asaas api_key connected easy 2026-09-02
stark-bank cert disconnected hard -✓ Asaas (api_key) → disconnected
server_id asaas
difficulty easy
connect_url https://app.codespar.dev/connect/asaas
next_action open_connect_url
ℹ Required secrets:
- api_key (starts with $aact_)
ℹ Instructions:
1. Open the Asaas dashboard
2. Copy the API key
3. Paste it into the connect page
ℹ Known pitfalls:
- The sandbox key does not work in live modeThe third shape, status, prints a success line and a four-row block: display_name, auth_type, difficulty, connected_at. As with discover, the section headings go to stderr and their items to stdout, so a redirect of stdout alone gives you the secrets and the steps without the labels that introduce them. An empty list prints (no connections) on stderr.
Calls the codespar_manage_connections meta-tool over POST /v1/sessions/{id}/execute. The tool itself is documented at codespar_manage_connections.
| When | What you get | Exit |
|---|---|---|
--action outside the three names | ✗ --action must be one of: list, status, initiate. | 1 |
--environment outside live and test | ✗ --environment must be 'live' or 'test'. | 1 |
| No API key resolved | ✗ Not logged in. Run `codespar login` or set CODESPAR_API_KEY. | 1 |
| Key rejected or under-scoped | ✗ internal error: and a stack trace naming createSession failed: 403 … | 2 |
The tool call answers success: false | ✗ internal error: and a stack trace naming connectionWizard failed: <the tool's message> | 2 |
Both validations run before the session is opened, so a rejected flag costs no request.
Related
codespar_discoverandcodespar_manage_connections: the two meta-tools behinddiscoverandwizard- Connect Links: what
connect startis starting - Servers, Connections and Connect: the HTTP routes these commands call