# Mithril Graph & Ontology database: agent quickstart

> The database behind https://graph.kotoba.cloud/ (formerly the kotobase.net
> graph plane): tenant-isolated graphs of datoms over immutable IPLD snapshots,
> every entity carrying an ontology type tag, queried in Datalog (the kotobase
> dialect, Datomic-shaped EDN), Cypher, SPARQL, Gremlin bytecode, or bounded
> read-only SQL.

This is an executable acceptance path, not a feature list. It ends with one
datom written and read back through https://api.mithril.fund/v1/database.
Do not report success from a health check, a tool schema, or a sign-in alone.

## Start in one command (MCP)

`https://graph.kotoba.cloud/mcp` is an MCP server (streamable HTTP,
stateless). With the two values from step 1 in the environment, an MCP
client needs one line and no other setup:

```sh
# Claude Code
claude mcp add --transport http kotoba-graph https://graph.kotoba.cloud/mcp \
  --header "Authorization: Bearer $KOTOBA_DATABASE_TOKEN" --header "X-Kotobase-Tenant: $KOTOBA_TENANT_ID"
# Codex CLI
codex mcp add kotoba-graph --url https://graph.kotoba.cloud/mcp --bearer-token-env-var KOTOBA_DATABASE_TOKEN
```

```json
{"mcpServers":{"kotoba-graph":{"type":"http","url":"https://graph.kotoba.cloud/mcp",
  "headers":{"Authorization":"Bearer ${KOTOBA_DATABASE_TOKEN}","X-Kotobase-Tenant":"${KOTOBA_TENANT_ID}"}}}}
```

Tools: `graph_list_databases`, `graph_create_database`, `graph_db_stats`,
`graph_transact`, `graph_query` (datalog · cypher · sparql · sql),
`graph_usage`, `graph_docs` (no credential). Each tool result is the
corresponding HTTP route's own answer, so a 401 in a tool result means the
connection carries no live credential. Descriptor:
https://graph.kotoba.cloud/.well-known/mcp.json. Steps 2–4 below are the same
calls as plain HTTP, for an agent without an MCP client.

## 0. Who does what

Without credentials an agent can read:

```sh
curl -fsS https://graph.kotoba.cloud/health
curl -fsS https://graph.kotoba.cloud/llms.txt
curl -fsS https://api.mithril.fund/v1/graph/tools
```

`/v1/graph/tools` is the OpenAI-format tool schema of the read-only query lane
(`POST /v1/graph/query`, five languages). Discovery grants no data access.

A person does three things, once, in a browser: signs in, creates a workspace,
and mints the agent's token (step 1). An agent cannot sign in and must never be
handed a passkey, a wallet, or a card. Everything after step 1 is the agent's.

## 1. The account holder (browser, once)

1. Open https://graph.kotoba.cloud/ and sign in at
   https://auth.mithril.fund/sign-in — passkey, own key (self-minted CACAO),
   Ethereum wallet (SIWE) / Base Account, or recovery phrase.
2. **Create workspace.** The workspace id has the form `t_…` (a tenant).
3. **Create agent token.** A tenant service account is created and its token
   (`kb_sa_…`) is shown once. Put these two values in the agent's secret store:

```text
KOTOBA_TENANT_ID=t_…
KOTOBA_DATABASE_TOKEN=kb_sa_…
```

Never place the token in a prompt, a commit, a pull request, or a committed
`.env`. Revocation and rotation are Authn calls from the account holder's
browser session today — `DELETE https://auth.mithril.fund/v1/tenants/<tenantId>/service-accounts/<sa_…>`
and `POST …/service-accounts/<sa_…>/rotate` — there is no button for them yet.

## 2. The credential on every call (and the optional Biscuit)

The service token is the agent's credential: every database call carries
`authorization: Bearer $KOTOBA_DATABASE_TOKEN`,
`x-kotobase-tenant: $KOTOBA_TENANT_ID` and `content-type: application/json`.
The edge verifies the token with Authn on each call; an `editor` service
account holds `data:read` and `data:write`.

Optional attenuation — a 15-minute credential bound to ONE database, to hand
to a less-trusted process or a read-only helper:

```sh
curl -sS -X POST https://auth.mithril.fund/v1/biscuit/token \
  -H "authorization: Bearer $KOTOBA_DATABASE_TOKEN" \
  -H 'content-type: application/json' \
  -d "{\"tenantId\":\"$KOTOBA_TENANT_ID\",\"dbName\":\"hello\",\"permissions\":[\"data:read\"]}"
```

HTTP 201 returns `tokenType` `Biscuit`, a `token` and `expiresIn` 900; send it
as `authorization: Biscuit <token>` in place of the Bearer line. A Biscuit
cannot be refreshed, only re-minted. HTTP 401 means the service token is not
live; HTTP 403 means the tenant or permission is out of the account's scope.
Do not retry with a broader identity.

## 3. Create a database and write one datom

Every database call carries three headers:
`authorization: Bearer $KOTOBA_DATABASE_TOKEN`,
`x-kotobase-tenant: $KOTOBA_TENANT_ID`, `content-type: application/json`.
The XRPC method is the path suffix.

```sh
curl -sS -X POST https://api.mithril.fund/v1/database/xrpc/ai.gftd.apps.kotobase.datomic.createDatabase \
  -H "authorization: Bearer $KOTOBA_DATABASE_TOKEN" -H "x-kotobase-tenant: $KOTOBA_TENANT_ID" \
  -H 'content-type: application/json' \
  -d '{"db_name":"hello"}'

curl -sS -X POST https://api.mithril.fund/v1/database/xrpc/ai.gftd.apps.kotobase.datomic.transact \
  -H "authorization: Bearer $KOTOBA_DATABASE_TOKEN" -H "x-kotobase-tenant: $KOTOBA_TENANT_ID" \
  -H 'content-type: application/json' \
  -d '{"db_name":"hello","tx_edn":"[{:greeting/id \"first\" :greeting/text \"hello, graph\" :ontology/type \"greeting\"}]"}'
```

Accept only HTTP 200 without `"ok":false`. `tx_edn` is an EDN transaction
vector (Datomic shape); `:ontology/type` is the type tag the ontology
inspector and the Cypher node label read. A write is one signed immutable
commit behind a conditional head compare-and-swap: HTTP 409 with
`retryable: true` means another writer won — read again, then retry.

## 4. Read it back

```sh
curl -sS -X POST https://api.mithril.fund/v1/database/xrpc/ai.gftd.apps.kotobase.datomic.q \
  -H "authorization: Bearer $KOTOBA_DATABASE_TOKEN" -H "x-kotobase-tenant: $KOTOBA_TENANT_ID" \
  -H 'content-type: application/json' \
  -d '{"db_name":"hello","query_edn":"{:find [?e ?text] :where [[?e :greeting/text ?text]]}"}'
```

Accept only a response whose `rows` (or `result`) contains `"hello, graph"`.
The same data through the other languages (`kg.query`, same headers):

```sh
curl -sS -X POST https://api.mithril.fund/v1/database/xrpc/ai.gftd.apps.kotobase.kg.query \
  -H "authorization: Bearer $KOTOBA_DATABASE_TOKEN" -H "x-kotobase-tenant: $KOTOBA_TENANT_ID" \
  -H 'content-type: application/json' \
  -d '{"db_name":"hello","lang":"sql","query":"SELECT e, v FROM datoms WHERE a = '"'"':greeting/text'"'"' LIMIT 10"}'
```

`lang` is `sql`, `cypher`, or `sparql`. SQL is bounded read-only `SELECT` over
`datoms(e, a, v, v_edn, added)`; Cypher reads the type tag as the node label
(`MATCH (g:greeting) RETURN g`). Write statements in these languages are
refused except an authenticated standalone Cypher `CREATE`; use `transact`.

Other methods on the same prefix, same headers, same `db_name` field:
`datomic.listDatabases` (`{}`), `datomic.dbStats`, `datomic.asOf`,
`datomic.pull`, `datomic.datoms`, `datomic.history`, `datomic.txRange`,
`datomic.deleteDatabase`, `usageGet`, `savedQuery.list` / `save` / `delete`.
The Datomic Client API shape (EDN or transit) is at
`POST /v1/database/api/{q,pull,datoms,transact,list-databases,create-database,…}`
with the same credential.

## 5. Read-only query lane for tool-calling models

`POST https://api.mithril.fund/v1/graph/query` with body
`{"lang":"datalog|cypher|sparql|gremlin|sql","query":"…","db_name":"hello","limit":100}`
and the same `authorization` header runs a READ-ONLY query; write
verbs are refused before any hop. Attach the tools array from
`GET /v1/graph/tools` to a `/v1/chat/completions` call, execute `graph_query`
tool calls client-side against this route, and feed the rows back. An
inference token (`kc_pat_…`) is refused here by name: inference keys do not
grant database rights.

## Evidence boundary

A `health` 200, a tool schema, an MCP `initialize`, a minted Biscuit, and a
created database are not evidence that data was written or read. Keep the `transact` response and
the `q` rows with your result. The workbench (https://graph.kotoba.cloud/)
shows the same databases, datoms, and observed ontology types to the account
holder; use it to confirm what the agent wrote.

## The rest of the stack

- MCP descriptor (transport, credential, tools, client one-liners): https://graph.kotoba.cloud/.well-known/mcp.json
- Machine documentation index for this database: https://graph.kotoba.cloud/llms.txt
- Kotoba language / CLI / first executable program: https://kotoba-lang.org/agent-quickstart.md
- Mithril identity, inference, signed packages: https://mithril.fund/agent-quickstart.md
- Human docs: https://docs.mithril.fund/graph/ · errors: https://docs.mithril.fund/reference/errors/

Public operator: Kotoba Labs Inc. Contact: support@kotoba.cloud.
