# Getting and using a key for mcp.nodo.lt

This service authenticates with a bearer token. There is no signup form and no
self-service registration endpoint: keys are minted by the operator and handed over
out of band. That is a deliberate limit of the current build, not an omission - a
registration endpoint on a metered service before billing exists is a way to give the
service away.

## Ask for a key

Keys are issued by the operator of nodo.lt on request; say what you are building and
roughly how many answers a month you expect. There is no signup endpoint to point you
at, so the request goes through whatever contact route https://nodo.lt publishes.

What you get back looks like `ltl_...`. It is shown once and stored nowhere on the
server - only a SHA-256 digest of it is kept - so if you lose it, it cannot be
recovered and a new one has to be minted.

## Use it

```
claude mcp add --transport http ltlaw https://mcp.nodo.lt \
  --header "Authorization: Bearer $LTLAW_KEY"
```

Put the key in an environment variable first. An inline key lands in your shell
history, and `claude mcp get` prints the header back unredacted.

Raw, the same thing:

```http
POST https://mcp.nodo.lt
Authorization: Bearer ltl_...
Content-Type: application/json
Accept: application/json, text/event-stream
```

## Which clients can connect today

| Client | Works | Why |
|---|---|---|
| Claude Code | Yes | It sends a bearer token, which is what this server speaks. |
| Any MCP client with a Streamable HTTP transport and a custom header | Yes | Same. |
| A custom connector doing OAuth | Yes | This server is now its own authorization server: RFC 9728 protected-resource metadata, RFC 8414 server metadata, RFC 7591 dynamic client registration, PKCE S256, RFC 8707 audience binding. A client that discovers and registers by itself needs nothing from us in advance. |

**This section used to say claude.ai could not connect, and that is no longer true.**
It was written before the authorization server existed, when the only two routes in
were a bearer token in a header and the `static_headers` connector type that is in
closed beta rather than generally available. Both `/.well-known/oauth-protected-resource`
and `/.well-known/oauth-authorization-server` are now served, and the whole flow -
register, authorize with PKCE, exchange, refresh - was walked end to end against
production.

One limit stated precisely, because it is the sort that costs an afternoon: consent
alone grants the **free** tier. Pasting an API key on the same consent page raises the
session to whatever that key is entitled to, and the subject is derived from the key's
own digest - so a browser session and a terminal session on one key share one meter
rather than getting one each.

## What a call costs

| Tier | Allowance | Free calls | What it is for |
|---|---|---|---|
| `free` | 300 per month | 1,000 per day | 300 billable answers per calendar month. No card required. |
| `starter` | 5,000 per month | 5,000 per day | 5,000 billable answers per calendar month. |
| `professional` | 30,000 per month | 30,000 per day | 30,000 billable answers per calendar month, shared across the team. |
| `institution` | 100,000 per month | 100,000 per day | 100,000 billable answers per calendar month, under a written agreement. Raised by contract where an organisation genuinely needs more. |
| `internal` | unmetered | unlimited | Unmetered. For this service's own pipeline and monitoring only. |

One unit is one answer that contains statute text. Metadata calls - finding an act,
listing its editions, checking what is scheduled - are free, because an assistant makes
several of them before it can ask the question that matters. A refusal costs nothing,
whatever the tool: charging for our own gap would put a thumb on the scale in the one
place this product cannot afford one.

When a monthly allowance is exhausted the server answers **402 Payment Required**, not
429. It is a billing condition rather than a rate limit, and a client that retries with
backoff will still be out of allowance in ten seconds. The free tools keep working.

Free is not the same as unlimited. Those metadata calls spend no allowance and never
will, but they carry a separate ceiling per key per day - the third column above. That
one answers **429 Too Many Requests** with a `Retry-After`, and it leaves the monthly
allowance untouched. The two are deliberately different statuses: 402 means buy more,
429 means wait. The ceiling is set where bulk extraction of the catalogue begins rather
than where ordinary work ends - three to six metadata lookups per billable answer is
the normal pattern, and every plan allows about thirty times that.

Both budgets ride on every response, so nothing has to be discovered by hitting it:
`X-Allowance-Limit`, `X-Allowance-Remaining`, `X-Allowance-Reset` for the month, and
`X-Free-Calls-Limit`, `X-Free-Calls-Remaining`, `X-Free-Calls-Reset` for the day.

## Checking what a key has spent

`GET /usage` with the key returns its own standing and history - what has been spent
this month, how many free calls today, and up to a year of past months. It costs
neither a unit nor a free call, because a support conversation about a limit should
not consume the thing being discussed. It reports only the key that asked: there is no
parameter for naming another one.

```bash
curl -s -H "Authorization: Bearer $LTLAW_KEY" https://mcp.nodo.lt/usage
```

## If it does not connect

- **401 on every call.** The key does not match a stored digest. Keys are peppered with
  the server's salt; a key minted against a different salt will never match, and nothing
  is logged when a lookup misses because that would be a free oracle for anyone probing.
- **405 on a `GET` of the endpoint.** Expected. Clients open a session with `POST`, then try `GET`
  for a server-initiated stream; this server offers none and says so. The handshake
  carries on normally afterwards.
- **Nothing reaches the server.** Check that the proxy in front of it forwards
  `Authorization`. Health without a key: `https://mcp.nodo.lt/health`.

Generated by scripts/generate_agent_artefacts.py - edit the generator, not this file.
