Providers
4 operations under /v1/providers (GET POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Base URL: https://api.codespar.dev
Every operation below requires a Bearer token. See Authentication.
GET /v1/providers
https://api.codespar.dev/v1/providersList the MCP servers this project can attach to a session
The catalog POST /v1/sessions draws its servers ids from. total is everything visible to this project, filtered is what survived the query parameters, and servers is the filtered page; the route does not paginate, so filtered and servers.length agree.
Visibility is per project: another project's generated (gen_-prefixed) servers are absent, global catalog rows always present. Presence here is NOT permission to invoke — that is gated separately on a connected account, which is what POST /v1/connect/start establishes.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
category | string | no | — |
country | string | no | — |
q | string | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | Bad Request — the body or query did not match the schema. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
filtered | integer | yes | — |
servers | array of ServerCatalogRow | yes | — |
total | integer | yes | — |
curl -X GET https://api.codespar.dev/v1/providers \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/providers HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/providers",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/providers", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/providers");{
"total": 0,
"filtered": 0,
"servers": [
{
"id": "servercatalogrow_0000000000000000",
"name": "Example",
"pkg": "string",
"category": "string",
"country": "string",
"auth_type": "string",
"tools_count": 1,
"description": "string",
"status": "string",
"provider_homepage": "string",
"provider_logo_url": "https://example.com/hook",
"provider_logo_fallback_url": "https://example.com/hook",
"provider_docs_url": "https://example.com/hook",
"sandbox_available": true,
"sandbox_url": "https://example.com/hook",
"description_pt_br": "string",
"subaccount_provisionable": true,
"engine_provisionable": true,
"engine_status": "none",
"connect_fields": 0,
"connectable": true
}
]
}GET /v1/providers/{slug}
https://api.codespar.dev/v1/providers/{slug}Read one provider
The same enriched row the list returns, by slug — {slug} is the catalog primary key, the value GET /v1/providers/{slug}/auth-schema also takes. 404 for another project's generated (gen_-prefixed) provider: the list already hides it, so answering differently here would leak what the list conceals.
Responses
| Status | Body | Description |
|---|---|---|
200 | ServerCatalogRow | OK |
404 | object | Not Found |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
auth_type | string | yes | — |
category | string | yes | — |
connect_fields | integer | yes | How many fields this provider's connect form has: the length of fields on its auth-schema. 0 for oauth and none. |
connectable | boolean | yes | Whether the dashboard can connect this provider itself: a credential form with at least one field that POST /v1/connections accepts, or an oauth provider with an authorization config. False for none, which needs no connection, for a form type whose catalog row declares no refs, and for Coinbase CDP (cdp, jwt_ecdsa) while the deployment holds CDP self-serve connect. |
country | string | yes | — |
description | string,null | yes | — |
description_pt_br | string,null | yes | description in Brazilian Portuguese, a faithful translation kept in the catalog. Null where the catalog has none; read description then. |
engine_provisionable | boolean | yes | Has an engine provisioning descriptor AND is not quarantined. |
engine_status | "none" | "available" | "quarantined" | yes | Carries the reason engine_provisionable is false. |
id | string | yes | — |
name | string | yes | — |
pkg | string | yes | — |
provider_docs_url | string,null | no | — |
provider_homepage | string,null | no | — |
provider_logo_fallback_url | string,null | no | — |
provider_logo_url | string,null | no | — |
sandbox_available | boolean | no | — |
sandbox_url | string,null | no | — |
status | string | yes | — |
subaccount_provisionable | boolean | yes | — |
tools_count | integer | yes | — |
curl -X GET https://api.codespar.dev/v1/providers/{slug} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/providers/{slug} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/providers/{slug}",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/providers/{slug}", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/providers/{slug}");{
"id": "servercatalogrow_0000000000000000",
"name": "Example",
"pkg": "string",
"category": "string",
"country": "string",
"auth_type": "string",
"tools_count": 1,
"description": "string",
"status": "string",
"provider_homepage": "string",
"provider_logo_url": "https://example.com/hook",
"provider_logo_fallback_url": "https://example.com/hook",
"provider_docs_url": "https://example.com/hook",
"sandbox_available": true,
"sandbox_url": "https://example.com/hook",
"description_pt_br": "string",
"subaccount_provisionable": true,
"engine_provisionable": true,
"engine_status": "none",
"connect_fields": 0,
"connectable": true
}GET /v1/providers/{slug}/auth-schema
https://api.codespar.dev/v1/providers/{slug}/auth-schemaThe connect form for one provider
The form to render so an operator can connect this provider, and never a credential. The vault is write-only from this direction: no submitted value is readable back through any operation in this document.
fields is empty for oauth (the connect flow is a redirect, not a form), for none (there is nothing to hold) and for any provider whose auth mechanism carries no per-field input. An empty array therefore means "no form", not "not configured".
base_url and oauth_authorize_url are resolved for the environment the credential authenticates in, so what is previewed here is the host the call will actually reach.
Requires the servers:read scope.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
slug | string | yes | The provider slug, which is also the catalog primary key. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
404 | object | Not Found — no such provider for this caller. A generated provider belonging to another project answers here too, identically to one that does not exist: the listing already hides it, so answering differently would leak what the listing conceals. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
auth_type | "api_key" | "path_secret" | "oauth" | "cert" | "none" | "hmac_signed" | "jwt_ecdsa" | "two_header" | "cdp" | yes | The provider's declared auth mechanism, taken straight from the column. Nine values, which is the column constraint's set and NOT the six the shared types package publishes: three of these nine are wire values the published schema would reject, so validate against this list rather than against a generated type of that field. |
base_url | string | yes | The provider host for this environment, or an empty string when the catalog holds no endpoint row for it. |
environment | "live" | "test" | yes | The environment the credential authenticates in. Not a parameter of the request. |
fields | array of object | yes | In the order the form should render them. |
oauth_authorize_url | string,null | yes | Where an OAuth connect lands the operator. Null for every other mechanism. |
server_id | string | yes | Echo of the slug. The catalog primary key IS the slug — there is no second id. |
curl -X GET https://api.codespar.dev/v1/providers/{slug}/auth-schema \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/providers/{slug}/auth-schema HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/providers/{slug}/auth-schema",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/providers/{slug}/auth-schema", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/providers/{slug}/auth-schema", {
path: {
slug: "example"
}
});{
"server_id": "srv_0000000000000000",
"auth_type": "api_key",
"environment": "live",
"base_url": "https://example.com/hook",
"oauth_authorize_url": "https://example.com/hook",
"fields": [
{
"name": "Example",
"kind": "api_key",
"label": "Example",
"header_name": "Example"
}
]
}POST /v1/providers/{slug}/verify-connection
https://api.codespar.dev/v1/providers/{slug}/verify-connectionTest one provider connection's credential
Issue one known-safe read against the provider with the connection's own stored credential, so an operator learns that a credential does not authenticate here instead of learning it on an agent's first real call.
The check is a read on both of its lanes. A provider with a bespoke recipe gets that recipe's GET; every other provider falls back to re-running the read its own catalog entry declares as its probe, which is the same authenticated read that confirms a freshly provisioned sandbox. Which lane ran is visible in the body: only a recipe extracts an account.
READ THE OUTCOME FROM ok, NOT FROM THE STATUS. Every failure body carries ok: false and a stable error; the status is a mapping from that code and is the less precise of the two. One status in particular carries two different bodies — see the 404.
EXACTLY ONE of the seven failures is a verdict about the credential: provider_rejected. The other six are the absence of a verdict, and they are not interchangeable with a bad credential. redirect_not_followed is the subtle one: the configured host answered by pointing somewhere else, the probe deliberately does not follow it, and so no answer about the credential was obtained from anyone.
Requires the connections:write scope. It is the scope the connection family carries, and it is wider than this operation needs: nothing in this project is written by asking a provider to judge a credential.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
slug | string | yes | The provider slug, which is also the catalog primary key. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK. The credential authenticated at the provider. latency_ms is the probe's own round trip. |
401 | object | The PROVIDER rejected the stored credential — a verdict, and the only failure here that is one. detail carries the provider's own words. This 401 never describes the caller's own credential: one that does not authenticate is refused before the handler runs. |
404 | object | object | Not Found, and this status carries TWO different bodies. A generated provider belonging to another project is refused before the check runs, with the bare { error, server_id } shape and no ok key. A provider that has a verify recipe but no catalog row reaches the checker and comes back as an ordinary failure with ok: false and error: "server_unknown". Branch on ok, not on the status. |
409 | object | redirect_not_followed. The configured host answered with a redirect, which the probe does not follow, so the credential was never judged. Retrying cannot resolve it and reconfiguring can — which is why it is not a 502. |
424 | object | not_connected. There is no usable credential to test: no active connection for this project and provider, or the vault returned nothing for the reference it holds. This body carries the extra hint / alternative steer. Or connection_expired: the project has an authorization-code OAuth connection whose access token passed its expiry, and nothing refreshes it, so the fix is to reconnect. That body carries expires_at and no sandbox steer. Or shared_sandbox_live_project_refused: the project's connection for this provider is the platform's shared SANDBOX credential, which serves test projects only, and this is a live project (or a live-mode call). Nothing was sent to the provider; the fix is to connect the project's own credential. |
501 | object | verify_unsupported. Nothing ships that can check this provider — no recipe and no declared probe. The first real call will report authentication errors directly. |
502 | object | provider_unreachable, and it is WIDER than its name. It is returned for a transport failure or timeout, and also for EVERY provider response that is not 2xx, 3xx, 401 or 403 — a provider 400 or 404 arrives here too. Read status before retrying: absent means nothing was reached and a retry is reasonable; a 4xx there means the provider answered and will answer the same way again. |
503 | object | endpoint_missing. The provider is known but its catalog entry cannot be called as configured. An operator fixes this; a caller cannot. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
account | object | no | Absent when the check ran through the generic probe rather than a provider-specific recipe: only a recipe extracts an account summary. Its absence is not a weaker pass. |
latency_ms | integer | yes | — |
ok | true | yes | — |
provider | string | yes | — |
curl -X POST https://api.codespar.dev/v1/providers/{slug}/verify-connection \
-H "Authorization: Bearer $CODESPAR_API_KEY"POST /v1/providers/{slug}/verify-connection HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.post(
"https://api.codespar.dev/v1/providers/{slug}/verify-connection",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/providers/{slug}/verify-connection", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.post("/v1/providers/{slug}/verify-connection", {
path: {
slug: "example"
}
});{
"ok": true,
"provider": "string",
"account": {
"id": "obj_0000000000000000",
"name": "Example",
"extras": {}
},
"latency_ms": 0
}Servers
4 operations under /v1/servers (GET POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Generate
1 operation under /v1/generate/servers (GET): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.