# auth.md

**Audience:** autonomous agents and MCP clients that want to call StalkChain Data on behalf of a person.

This file is self-contained: it names the registration endpoint, lists the supported methods, and explains
how the resulting credentials are used. The machine-readable source of truth is the Protected Resource
Metadata at https://data.stalkchain.com/.well-known/oauth-protected-resource and the Authorization Server Metadata at
https://data.stalkchain.com/.well-known/oauth-authorization-server, whose agent_auth block mirrors this document.

Access is metered in prepaid credits. Registering, connecting and listing tools cost nothing. A paid tool on
an empty balance returns an error pointing at https://data.stalkchain.com/#plans rather than failing opaquely.

## Registration at a glance

| Field | Value |
| --- | --- |
| Registration endpoint | https://data.stalkchain.com/oauth/register |
| Method | anonymous dynamic client registration, then user approval |
| Identity types | anonymous |
| Credential types | client_id, authorization_code, bearer, refresh_token, api_key |
| Claim (user approval) | https://data.stalkchain.com/oauth/authorize |
| Revocation | https://data.stalkchain.com/oauth/revoke |
| Credentials are used as | `Authorization: Bearer <token>` on https://data.stalkchain.com/mcp and https://data.stalkchain.com/api/v1 |

No identity assertion is required to register. You register anonymously, a human then claims the client by
approving it in a browser, and only after that can the client spend credits.

## Step 1 — Discover

**1a. Protected Resource Metadata.** Start here to learn which authorization server issues tokens for this
resource.

```http
GET /.well-known/oauth-protected-resource HTTP/1.1
Host: data.stalkchain.com
Accept: application/json
```

```json
{
  "resource": "https://data.stalkchain.com",
  "authorization_servers": ["https://data.stalkchain.com"],
  "scopes_supported": ["mcp"],
  "bearer_methods_supported": ["header"],
  "mcp_endpoint": "https://data.stalkchain.com/mcp",
  "api_base": "https://data.stalkchain.com/api/v1"
}
```

The MCP endpoint has its own document at https://data.stalkchain.com/.well-known/oauth-protected-resource/mcp whose resource is
https://data.stalkchain.com/mcp. That is the value to send as the resource parameter in step 4.

**1b. Authorization Server Metadata.**

```http
GET /.well-known/oauth-authorization-server HTTP/1.1
Host: data.stalkchain.com
Accept: application/json
```

The response carries the endpoints below plus an `agent_auth` block summarising this file.

| Purpose | URL |
| --- | --- |
| Register | https://data.stalkchain.com/oauth/register |
| Authorize | https://data.stalkchain.com/oauth/authorize |
| Token | https://data.stalkchain.com/oauth/token |
| Revoke | https://data.stalkchain.com/oauth/revoke |

## Step 2 — Pick a method

Two methods are supported. Choose before you register.

- **anonymous client registration** — you register yourself, a human approves the connection once in a browser, and you
  hold a rotating token. Use this when a person is present. This is the normal path for MCP clients.
- **api key** — a human creates a long-lived sc_ key in the dashboard and gives it to you. Use this
  for unattended jobs. Skip to step 6.

Every call is billed to an account, so an account must exist before a client can spend.

## Step 3 — Register

Dynamic Client Registration (RFC 7591). Public clients use `"none"` with PKCE. Registration is rate
limited, bodies are capped, and only `client_uri`, `software_id` and `software_version` are retained.

```http
POST /oauth/register HTTP/1.1
Host: data.stalkchain.com
Content-Type: application/json
```

```json
{
  "client_name": "My Agent",
  "redirect_uris": ["https://example.com/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"]
}
```

A `201` returns `client_id`. Public clients receive no `client_secret`.

## Step 4 — Claim: the user approves

Send the user to the authorization endpoint with PKCE. `code_challenge_method` must be `S256`; `plain`
is rejected. Include `resource` per RFC 8707.

```http
GET /oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT&code_challenge=CHALLENGE&code_challenge_method=S256&scope=mcp&state=STATE&resource=https%3A%2F%2Fdata.stalkchain.com%2Fmcp HTTP/1.1
Host: data.stalkchain.com
```

The user signs in and approves. You receive `code` and `state` on your redirect URI, plus `iss` for
issuer validation (RFC 9207). Validate `iss` before redeeming.

## Step 5 — Exchange the code

```http
POST /oauth/token HTTP/1.1
Host: data.stalkchain.com
Content-Type: application/x-www-form-urlencoded
```

```json
{
  "grant_type": "authorization_code",
  "code": "CODE",
  "code_verifier": "VERIFIER",
  "client_id": "CLIENT_ID",
  "redirect_uri": "REDIRECT"
}
```

Returns a bearer `access_token` valid one hour and a `refresh_token` valid thirty days. Refresh tokens
rotate on every use. **Presenting a rotated refresh token revokes the entire grant**, because reuse is
treated as a leak.

## Step 6 — Use the access token

```http
POST /mcp HTTP/1.1
Host: data.stalkchain.com
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
```

```json
{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }
```

The transport is Streamable HTTP and stateless: any request may land on any instance. Batched JSON-RPC is
refused with `-32600`; send one request per POST. The same token works on the REST API at
https://data.stalkchain.com/api/v1, and an `sc_` API key is used the same way.

Every response carries what the call cost and what is left. Read it, and tell the user when the balance is low.

## Errors

| Status | Where | What it means | What to do |
| --- | --- | --- | --- |
| 401 | `/mcp`, `/api/v1` | No or invalid token | Read `WWW-Authenticate`, which carries `resource_metadata`, then start at step 1 |
| 402 | `/api/v1` | Out of credits | Stop and relay https://data.stalkchain.com/#plans to the user; do not retry |
| 400 `invalid_grant` | `/oauth/token` | Code reused, expired, or PKCE failed | Start a new authorization; do not retry the same code |
| 400 `invalid_target` | `/oauth/authorize` | `resource` did not match | Use https://data.stalkchain.com/mcp exactly |
| 429 | any | Rate limited | Honour `Retry-After` |
| 5xx | any | Server fault | Retry with backoff |

A paid tool with no credits returns `200` with an error payload rather than a protocol error, so you can
relay it to the user in conversation.

## Revocation

Two independent layers.

- **Credentials** (RFC 7009): `POST https://data.stalkchain.com/oauth/revoke` with `token`. Revoking either token of a grant
  revokes its pair. API keys are revoked by their owner in the dashboard.
- **Registration**: a user disconnects your client from https://data.stalkchain.com/dashboard, which revokes every token in
  that grant. Treat a sudden 401 as a disconnection and re-register rather than retrying.

## More

- Setup sheet: https://data.stalkchain.com/llms.txt
- MCP server card: https://data.stalkchain.com/.well-known/mcp/server-card.json
- API catalog: https://data.stalkchain.com/.well-known/api-catalog
- Agent skills: https://data.stalkchain.com/.well-known/agent-skills/index.json
