Imported from latentminds-ai/atmina-plugins (
plugins/atmina-cursor/skills/atmina-kb-mcp/SKILL.md). Install upstream withnpx skills add latentminds-ai/atmina-plugins --skill atmina-kb-mcp. Copyright stays with the author (Apache-2.0).
Atmina — your memory
Memory, Shared. Total Recall.
Atmina is where you keep the thoughts, decisions, and findings worth surviving
the next prompt. You forget between sessions and your context window thins
within one; Atmina is the durable store. This skill is the workflow guide — the
MCP server's own tool descriptors, its auto-injected server instructions, and
the atmina://docs/agent-quickstart resource are canonical. When this skill and
the live surface disagree, the live surface wins.
The server splices its operating model into your system prompt at
initializetime (the recall / capture / update / consolidate reflexes and the memory-trust rules). Readatmina://docs/agent-quickstartfor the full operator's manual — chains, error catalog, tool & resource index.
Orient once per session
health— server build + whether your team's AI Search instance is provisioned. A[provisioning]result means retry shortly / surface to the user.whoami— identity, current team, role.list_kbs(or readatmina://my-kbs) — the memory surfaces you can access. Copy theRefcolumn (the qualifiedteam-slug/kb-slug) as thekb_idargument on KB-scoped tools. Thekb_idUUID is the canonical form; a bare slug works only while it resolves to exactly one accessible KB.
On POST /mcp/kb/{kb_id} the URL pins the KB — omit KB selection from
arguments. On POST /mcp (bearer endpoint), KB-scoped tools take kb_id
unless you have exactly one accessible KB (auto-resolves).
The four reflexes
| Reflex | When | Chain |
|---|---|---|
| Recall | You'd otherwise answer from training, or the user implies a known fact ("what did we decide", "recall…", "our take on…") | search (default discovery path) → answer from snippets; read_file the top read_ref only for authoritative detail. A snippet carries no attestation — before acting on a claim found in one, read_file the page and honour its evidence states |
| Capture | Save a thought / decision / finding ("save this", "for the record", "decision:") — an explicit ask always produces a write: a durable page when the lesson is settled and team-relevant, a working-layer note when it isn't — or a session note
at a natural session end (offer first) → journal/YYYY-MM-DD-<slug>.md | write_file (single) / write_files (bulk, ≤50, inlined) with expected_bytes + content_sha256 → get_file_outline to verify |
| Update | Edit existing memory ("update the doc", "correct X") | get_file_outline (etag) → read_file → write_file(if_match) → on [conflict] re-read, re-draft against the new content, retry with a little jitter (never blind-retry the same bytes or drop if_match) |
| Consolidate | Compile / refresh a KB's memory into one reviewed current-state view ("consolidate this memory", "refresh the current state") | run_routine → get_routine_run (poll) → read_file staged report.md + proposed-current-state.md → promote_artifact (admin) |
Cite exact source spans
When authoring same-KB Markdown, use an ordinary link:
[Pale evidence](archive/original/gardens-of-the-moon/book-01-pale.md#L1666-L1702)
The canonical shape is [label](path.md#Lstart-Lend). start and end are
positive, 1-based, inclusive line numbers in the stored UTF-8 text body,
including YAML frontmatter. Use #L1666 for one line. The citation follows
normal same-KB path resolution and addresses the current file head only. Such a
link is a pointer, not a pin — it binds nothing once the file changes. A claim
is checkable only with an evidence entry carrying pinned_version +
span_sha256, which a head read_file reports as attested, drifted,
unresolved or external.
Session notes (journal/)
At a natural end of a working session — a decision reached, a task wrapped, the
user signing off — offer to capture a short session note; write only on a yes.
The offer is the reflex; the write is the user's call — never capture a
session note unprompted. It lands at journal/YYYY-MM-DD-<slug>.md (the
leading date becomes the memory's event_date). Keep it compact — decisions,
findings, open questions, refs; a screen, not a transcript. journal/ is the
personal or single-agent working layer; a team convention may keep its working
layer elsewhere (chronicle/ at organisation scope) — same layer, different
scope. journal/ is ordinary, searchable memory: the whole-KB current-state
routine consolidates it automatically — working layer included, so a compiled
view can carry candidates and the source files win — the journal is the raw
layer and the compiled view is its distillation.
Transport discipline (non-negotiable)
All bytes destined for Atmina arrive via MCP write tools — directly. Never
stage payloads in /tmp, never emit a sync script, never write intermediate
JSON for later upload. Pass content inline through write_file / write_files,
or PUT to the presigned URLs request_uploads mints (the one sanctioned
shell-HTTP path, closed by confirm_uploads). The shell is for inspecting
local source files only.
Typed filters: list_files is exhaustive, search is not
search and list_files share one filter grammar: AND-combined
{ field, op, value } clauses. field is either a built-in — event_date
(RFC 3339 datetime), created_at / updated_at (epoch-millisecond numbers), or
mime_type (exact string) — or a registered pointer, a non-root RFC 6901
JSON Pointer into the file's YAML frontmatter (/release/stage; escape ~ as
~0 and / as ~1). Each pointer declares one scalar type: string and
boolean take eq / ne; number and datetime add gt / gte / lt /
lte. Values are never coerced, and a missing value fails every predicate —
including ne.
Coverage is the part that changes what you do. list_files applies path
scope and every predicate to the KB's complete active catalog, so it is
exhaustive. search applies the same predicates authoritatively against
current-head rows, but only over that query's relevance candidates: it reports
coverage: "relevance_candidates" and exhaustive: false, and matches outside
that window are not returned. When you need every match, a count, or an
audit, reach for list_files, not search.
A filtered search resolves those candidates through at most 4 knowledge
bases. The bound is on where the candidates land rather than on the scope you
asked for, so a wide unscoped search keeps working until relevance actually
spreads past four KBs — at which point it fails closed with [conflict]
instead of answering from part of the set. Select at most four KBs with
kb_ids, or filter one KB at a time.
Before using a pointer filter, read the KB's descriptor on list_kbs or
atmina://kb/{kb_id}: queryable_frontmatter_fields plus
query_projection_version and query_projection_state. A pointer clause needs
that state to be ready; while it is rebuilding or error only built-ins
run. Across several KBs, a pointer must be registered with the same type on
every selected KB.
Registering pointers is update_kb with queryable_frontmatter_fields,
owning-team admin only (a share-grant admin cannot). The rebuild is
synchronous. If it fails, the state is error and the usual cause is an
existing file whose frontmatter holds the wrong declared type — fix that file
and resubmit the identical registry, which retries the same version rather
than starting a new one. Pointer names are your KB's own information
architecture; Atmina owns only the four built-ins.
Folder hub indexes for multi-phase extracts
Keep index maintenance separate from recall, capture, and consolidation. After one bulk write/upload-confirmation phase:
- Call
generate_hub_indexwith the defaultpreviewoperation; it writes nothing. - Inspect the exact returned
contentand all generated relative paths. - Call again with explicit
operation: "apply"; optionally pin the inspected current etag withif_match. - Explicitly regenerate after later add, delete, or move phases.
The tool selects paths from one live catalog snapshot and does not read candidate file bodies. Apply replaces only one inclusive managed block and preserves authored bytes outside it. The result is a snapshot at generation, not automatic, background, or permanently live navigation. Ambiguous or malformed markers fail closed; repair the markers manually, preview again, and only then apply.
sort defaults to { kind: "alpha" } (title, NFKC-collated). Pass
{ kind: "frontmatter", field, order? } to order entries by a registered
queryable-frontmatter pointer instead — field must already be registered on
the KB (see the pointer-filter guidance above), or the call fails closed with
[validation]: pick a registered pointer, or register it first via
update_kb. The KB's query projection must also be ready at its exact
current version, or the call fails closed with [conflict]. Entries whose
frontmatter is missing that field always sort last (alpha-asc among
themselves), in both asc and desc order. If the projection is error or
its version has drifted, the usual recovery is the same as for pointer
filters: fix the offending file's frontmatter and resubmit the identical
registry via update_kb, which retries the same version instead of starting
a new one. A single stored file whose projection value is corrupt or
version-drifted fails the entire generation — the index is never built
partially.
Generating a Menu from folders (generate_nav)
generate_nav builds wiki/nav.yaml Menu sections from the KB's folder
layout — one section per immediate child folder under path_prefix (default
wiki/), listing every eligible Markdown file recursively beneath it. Files
sitting directly under path_prefix (e.g. wiki/README.md) belong to no
child folder and are deliberately left alone for a hand-curated section such
as "Start here".
- Call
generate_navwith the defaultpreviewoperation; it writes nothing. Inspect the returnedcontentand thesections_added/sections_replaced/sections_removed/sections_keptcounts. - Call again with explicit
operation: "apply"; optionally pin the inspectedcurrent_etagwithif_match. - Re-sync explicitly after later add, delete, or move phases — the manifest is a snapshot at generation, not automatic, background, or live.
The preservation boundary. A sync only ever touches sections it owns (a
generated: marker naming a folder inside path_prefix); every hand-curated
section, its comments, and the overall section ordering survive untouched.
The one real loss: comments written inside a generator-owned section — on
the label line, above pages:, above or beside a page entry — are replaced
along with the section body on the next sync. Only the comment directly
above a generated section survives. Keep notes above a generated section,
not within it. If a sync's plan is empty for a given path_prefix (no
eligible folders left), it removes every section it previously owned there —
check sections_removed in preview before applying if that matters.
Labels come from the live catalog, not the manifest. Generated pages
entries are bare paths with no label key, so the client resolves each
page's display name from the catalog at render time — retitling a page needs
no re-sync. Section labels ARE generator-authored (folder name → title case).
Grouping. Pass group_by: { field } with a pointer already registered via
update_kb (see the pointer-filter guidance above) to sub-group each
section's pages by that frontmatter value. Requires the KB's query projection
ready at its exact current version, same as generate_hub_index's
frontmatter sort — otherwise the call fails closed with [conflict]. Pages
with no value for the pointer are bucketed last in an "Ungrouped" group.
Fail-closed repair. A manifest the generator cannot model — an unknown
schema, YAML anchors/aliases, unparseable YAML, or two generated sections
claiming the same folder — makes the sync refuse rather than guess. Repair the
manifest by hand, preview again, and only then apply.
Large KBs (100+ pages). Sync once after finishing a bulk extract phase,
not per file. Keep a hand-curated "Start here" section at the top of the
manifest — it is never touched by a sync. Reach for group_by once a single
folder's section would otherwise exceed roughly 30 pages. Re-sync after any
later add, delete, or move phase, since the manifest is a point-in-time
snapshot rather than a live view.
Live tool surface
Orientation: whoami, health
Recall & read: search (the default discovery path — file-grouped results
with snippets + a read_ref), read_file, list_files, get_file_outline,
related (a memory's linked memories — what it links to, what links to it,
and the references with no live file behind them; depth: 2 reaches one
memory further, type narrows to a registered frontmatter type),
get_context (KB orientation — state/pulse by default; opt-in actions,
source_snapshot, and link_health for a self-audit of broken references /
unconnected memories / hubs before you consolidate)
Capture & write: write_file, write_files (text, path-addressed);
request_uploads → confirm_uploads (presigned bulk / binary / >1 MiB);
upload_file, update_file (binary, base64, ≤1 MiB)
Index maintenance: generate_hub_index (default no-write preview, explicit
apply, regenerate after later catalog changes), generate_nav (same
preview/apply loop; generates wiki/nav.yaml Menu sections from folders,
preserving hand-curated sections)
Version history & rollback: every head-changing write returns its version_n
(last ~20 kept per file); file_history (viewer) lists a file's versions,
read_file(version: n) reads a prior version, restore_version (editor) rolls
the head back as a new attributed write (history is never rewritten)
Admin: create_kb, update_kb, delete_kb, delete_file (destructive —
confirm with the user first); audit_log, get_audit_event,
get_team_settings, update_team_settings
Memory routines (consolidation): list_routines (viewer), run_routine
(editor), list_routine_runs (viewer), get_routine_run (viewer),
promote_artifact (admin)
Chaining related for multi-hop exploration. One call walks at most two
hops from wherever it's anchored. To go further — say, character → faction →
event → archive — chain calls: anchor the next call at a neighbour from the
previous call's Linked memories / Linked from list, one hop at a time.
References with no live file behind them still surface in every call; they're
honest dangles, not something to chase.
Text vs binary: plain UTF-8 (markdown, .txt, .csv, .json, .yaml) →
always write_file / write_files. Binary (PDF, images, audio) →
upload_file / request_uploads. Never use upload_file to dodge path syntax.
Memory routines: the run → review → promote loop
A KB can keep a compiled current-state view of its own memory — one reviewed summary the team can recall in a single shot. It's produced by a memory routine: a background consolidation job that reads the KB's source files, asks the model to compile them, and stages a draft for a human to review.
The trust spine — internalise it before touching a routine:
- Compiled views (
_consolidated/) cite their sources. A promotedcurrent-state.mdis a convenience summary, not ground truth. - On conflict, the source files win. When a compiled view disagrees with a
source file,
read_filethe cited source and prefer it. - Staged outputs (
_staged/) are unreviewed machine drafts.read_filethem only when reviewing a run; never treat them as authoritative or cite them as memory. They're hard-excluded fromsearch.
The loop:
- Run.
run_routine(editor+). Async-always — returns{ run_id, status: "queued" }. Short-circuits to outcomeno_opwhen nothing changed; passforce: trueto override. - Poll.
get_routine_run(run_id)untilstatusiscompleted.status(lifecycle) andoutcome(verdict: succeeded/no_op/needs_review/partial/ failed) are recorded independently — acompletedrun can beneeds_review(model refused, malformed output, or out-of-snapshot citation). Don't promoteneeds_reviewblind. - Review.
read_filethe staged artifacts fromoutput_paths(under_staged/<date>/<run_id>/). Readreport.mdfirst — it has a## Sources usedsection and a## What changed vs current compiled statedelta — thenproposed-current-state.md, the draft itself. Review them like a PR, not like memory. - Promote (admin).
promote_artifact(run_id)— owning-team admin only, after readingreport.md. Copiesproposed-current-state.mdinto_consolidated/current-state.md, re-verifies the staged sha, writes with an etag guard. Re-promoting a current run is a no-op.
There is no edit-then-promote. To refine an almost-right draft, adjust the
routine's instructions (REST-only, owning-team admin) → run_routine again
(cents) → review → promote. search labels _consolidated/ chunks
compiled: true + stale; a stale label is the signal to re-run the loop.
Resources (read-only mirrors)
atmina://docs/agent-quickstart— the full operator's manual.atmina://my-kbs— accessible KBs (same columns aslist_kbs).atmina://my-team/atmina://my-team-status— team metadata + indexing.atmina://kb/{kb_id}/…/files/…/files/{file_id}/…/indexing-status/…/audit/…/write-guide— per-KB reads.atmina://status— anonymous build metadata.
Not on the MCP surface
- Sharing & membership (
share_kb,invite_to_kb,revoke_share) is deprecated on MCP and moving to Dashboard-only. Do not build agent flows around these; manage shares from the Dashboard. - Routine config CRUD (create/enable/edit a routine's
instructionsand schedule) is REST-only, owning-team admin (/api/v1/kbs/:kbId/routines). The MCP surface runs, reviews, and promotes routines; it does not configure them.