A scope string partitions memory into named namespaces. It is the primary key used to organise, retrieve, and restrict access to lessons.
:: is the only valid separator. Any other separator (:, /, -) returns a 400 error.
| Scope type | Format | Example |
|---|---|---|
| Global | global |
global |
| Project (monorepo) | project::{name} |
project::agent-skills |
| Repository | repo::{owner}/{repo} |
repo::mthines/gw-tools |
| Branch | branch::{owner}/{repo}::{branch} |
branch::mthines/gw-tools::feat/add-memory |
All segments are normalised to lowercase by the server.
| Lesson type | Recommended scope |
|---|---|
| Universal principles (always apply) | global |
| Lessons about this specific repo's codebase | repo::{owner}/{repo} |
| Experimental learnings on a feature branch | branch::{owner}/{repo}::{branch} |
| Lessons shared across a monorepo | project::{name} |
Rule of thumb: use the narrowest scope that correctly describes where the lesson applies. Branch-scoped lessons don't pollute the repo's lesson set; repo-scoped lessons don't pollute global.
When an agent reads context before working on a task, it should query multiple scopes and merge the results:
# In order of specificity (narrow → broad)
memory.list { scope: "branch::mthines/gw-tools::feat/x" } # branch-specific
memory.list { scope: "repo::mthines/gw-tools" } # repo-level
memory.list { scope: "project::gw-tools" } # project-level (if monorepo)
memory.list { scope: "global" } # universalMore specific scopes take precedence when the same key exists at multiple levels.
The memory.search tool accepts owner-level wildcards in the scopes parameter:
{ "scopes": ["repo::mthines/*"] } // all repos under mthines
{ "scopes": ["global", "repo::mthines/gw-tools"] } // explicit multi-scopeWildcards only work in memory.search — not in memory.read, memory.list, or memory.delete.
::is the only separator. Single:→ 400.repo::format must include a/(owner/repo).repo::mthines→ 400.branch::format must have exactly two::separators.branch::mthines/gw-tools→ 400.- Unknown prefixes → 400. Only
global,project,repo,branchare valid. - Segments are trimmed and lowercased on ingest by the MCP tools (
memory.writevalidates through the normalisingvalidateScope). The REST write path (POST /memories) stores the scope EXACTLY as sent, somemories.scopecan hold mixed case — which is why a?scope=filter on the/memoriesroutes is validated but never lowercased and matches the stored string exactly. - No segment may itself contain
::.::is reserved as the separator, soproject::widget::xandrepo::owner/name::xare both invalid (andbranch::takes exactly two segments, per rule 3).
Rule 6 is what makes the CLI's <scope::key> shorthand decidable: lorekit show
/ write / link split a single argument at the LAST :: and only when the
left side is itself a complete valid scope, so repo::owner/name::my-key resolves
to scope repo::owner/name + key my-key while a bare repo::owner/name stays
whole. Keys are free-form and MAY contain ::; those are unrepresentable in one
token, so the CLI exposes --scope / --key for them.