Remote storage
Sync lore to the hosted LoreKit MCP server so your memories are available on every machine, from every agent, without any extra setup on each device.
You can use the hosted instance at lorekit.io or self-host your own. This tutorial uses the hosted instance. For self-hosting see the Installation Guide.
Generate an API token
Go to Settings → API keys and click Generate new token. Enter a name (e.g. claude-local, ci-prod) and choose a permission tier:
lk_rw_…— read + write (agents that learn)lk_ro_…— read only (CI context injection)lk_wo_…— write only (one-way memory feeders)
The token is shown once only — copy it before closing the modal.
Optionally open Scoping to narrow what the token can reach — an allowlist of scopes, and whether it may touch your organisations at all:
- Scopes — pick specific scopes, or an owner wildcard like
repo::mthines/*that keeps covering new repos under that owner as they appear. Leave it empty for any scope. - Organisations — All my orgs (the default), Personal only, or a specific list. Your own memories stay reachable under all three.
A scoped token that names a scope outside its allowlist is refused outright rather than answering with an empty list, and it cannot enumerate scope names it is not allowed to read. Scoping is optional: a token you never scope behaves exactly as before.
Point your agent at the MCP server
The fastest path is npx @lorekit/cli install — it scaffolds the skills, wires the MCP server, and installs the lifecycle hooks in one command. Or add the endpoint manually to your agent's MCP config:
// .mcp.json (Claude Code / Cursor / opencode — project-local)
{
"mcpServers": {
"lorekit": {
"command": "npx",
"args": ["-y", "mcp-remote",
"https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp",
"--header", "Authorization:Bearer lk_rw_<your-token>"]
}
}
}Keep .mcp.json out of version control — it contains your token. Commit a .lorekit.json instead to set the team default mode (e.g. { "mode": "remote" }).
Verify the connection
npx @lorekit/cli doctorThe doctor command checks connectivity, token permissions, and scope health. Look for the Remote section in the output — it should show a green ok status.
Two of those checks answer different questions, and both must be green:
connectivityprobes the public/healthfunction. It proves the network path only — it stays green for a token that no longer works.authenticationmakes one authenticated request and reports what the server said about the token. A revoked or deleted token fails here with HTTP 401, and doctor exits non-zero. A write-onlylk_wo_*token passes with a note that it has no read permission.
Seeing authentication — token REJECTED? The token was revoked or deleted. Generate a new one at lorekit.io, then run npx @lorekit/cli install --force — it asks whether to keep, replace, or remove the token already in your config.
(Coming from offline?) Bring your local lessons up
If you started with offline storage, everything your agent learned so far lives in a local store. One command copies it into the hosted one:
npx @lorekit/cli migrate --from .lorekit --to remote # preview the plan
npx @lorekit/cli migrate --from .lorekit --to remote --yes # push itPoint --from at whichever store holds your lore. ~/.lorekit is the default home tier and the right answer unless you opted a repo in; <repo>/.lorekit is the project tier, which only exists if you created it.
It is a dry run by default: it prints how many memories it would add, update, or leave unchanged in each scope, and writes nothing until you pass --yes. It is also idempotent — re-running it after a push reports everything as unchanged, so it is safe to run again after learning more offline.
The command checks the connection and the token before it writes anything, so a misconfigured run fails in one line instead of halfway through. Migrating writes, so a read-only lk_ro_… token is rejected outright — use lk_rw_… (a write-only lk_wo_… also works, though it cannot read the hosted store, so every memory is reported as new). A "deny": ["remote"] setting in your config is refused here too, by design.
What carries over. The scope, key, value, source agent and trigger of every memory transfer as-is, and a memory the hosted store has never seen keeps its original creation date, so recency-based ranking still works after the move (one it already holds keeps the date it has). A few things do not carry over, and the command names every affected memory rather than leaving you to find out: the last-updated timestamp is set to the moment of the push, the times-seen count starts at 1 for a new memory and otherwise counts up from the hosted one's own tally, labels replace whatever the hosted copy had, and an expiry further out than a year is shortened to the hosted maximum. Archived and expired memories are skipped rather than pushed, so nothing you retired comes back to life.
Migrating a large store? The command paces itself under the hosted rate limit and waits out the ones it hits — it says when pacing starts, so a slow run is never a mystery — and it retries a passing server error rather than dropping the memory. A few retries is the budget, though: a memory that keeps failing is reported and the run exits non-zero, and five failures in a row stop it early with a count of what made it. Reaching the memory limit on your plan stops it the same way. In every case the migration is safe to re-run — it picks up where it left off.
Write and read a memory
Write a test memory via the MCP endpoint:
memory.write {
scope: "global",
key: "hello-remote",
value: "Remote lore is working."
}Read it back from any machine:
memory.read {
scope: "global",
key: "hello-remote"
}
// → { "value": "Remote lore is working.", "updated_at": "…", "scope": "global" }scope is optional. Leave it out and the key is resolved across every scope you
can see, preferring the most specific type (project → branch → repo →
global) — useful when you were handed a key without one. An omitted scope does
not mean global:
memory.read { key: "hello-remote" }
// → { "value": "…", "updated_at": "…", "scope": "global" }The scope field always names the scope that answered. If the key exists in
more than one, the response also carries other_scopes — pass an explicit
scope next time if it picked the wrong one. memory.list takes the same
optional scope, listing across everything when you omit it.
Fetching several lessons at once? Pass refs (a list of scope::key strings)
instead of scope/key — one round trip for the whole batch:
memory.read {
refs: ["global::hello-remote", "repo::acme/widget::build-flags"]
}
// → { "entries": [ … ], "missing": [ "repo::acme/widget::build-flags" ] }Every reference resolves independently — an unresolved one lands in missing
rather than failing the call. The CLI's lorekit show supports the same
pattern: pass two or more scope::key positionals and it batches them the
same way.
Use in CI / GitHub Actions
Store a read-only token as LOREKIT_TOKEN in your repo secrets, then inject global memories before any AI step:
- name: Inject LoreKit context
run: |
curl -s -X POST "$LOREKIT_MCP_URL" \
-H "Authorization: Bearer $LOREKIT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "memory.list",
"arguments": { "scope": "global", "limit": 20 }
}
}'
env:
LOREKIT_MCP_URL: https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp
LOREKIT_TOKEN: ${{ secrets.LOREKIT_TOKEN }}(Optional) Install the GitHub App
To have LoreKit automatically learn from PR review comments, go to Settings → Integrations and install the LoreKit GitHub App. Every repository you grant it is covered instantly — there is no per-repo secret to copy and no webhook to configure on GitHub:
- Covers: every repo you grant the App, as you add or remove them on GitHub
- Learns from: resolved pull request review comments and reviews
- Tags each memory:
source::pr-webhook
If you run a review agent that posts its findings as PR review comments, the App can also record whether each finding turned out to be worth posting — a thread that was fixed counts for that finding class, one declined in a reply or thumbed down by the PR author counts against it — so the agent's next review can suppress the noise and keep the signal.
That part is opt-in per installation and has no settings page yet: it needs one configuration row telling LoreKit how your agent marks its own comments. Ask your LoreKit maintainer to set it up, or follow the GitHub App integration guide.
Next steps: share memories with your whole team by setting up an organization. See the Team sharing tutorial.