Skip to main content

Projects

A project is a notebook somebody else can also read. Your assistant writes a fact on a page and names the people and things that fact is about; later, you — and every one of your Nirvai agents — open the same notebook and find the same page. It is not your assistant's private scratchpad. It is one brain, shared.

Everything on this page runs through nirvai_execute under the projects area. Read the MCP reference first if the alias grammar is new to you.

If you knew this as `memory.*`

Projects used to be called memories, and the actions were named memory.*. Those old names are deprecated: calling one now returns a clear error naming its projects.* replacement and asking you to re-download the projects skill package. Use projects.*.


What a project holds​

PieceWhat it is
ObservationOne atomic fact, with a stable slug that is its permanent address, plus a title, category, tags and the date it was observed.
EntityA person, company or thing an observation mentions. Every observation must name at least one, each with a description — that description is what recall matches on.
RelationshipA typed link between two entities ("Acme signed MSA-2025").
Appendix fileAn original document kept beside the notes, addressed by a logical path.
The embedding model is chosen once, forever

A project's embedding model — the thing that decides what "similar" means when you search it — is fixed when the project is created and can never change. Your assistant picks it from the listed models at creation and is refused if it guesses. Changing it later would mean rebuilding the project.


The verbs​

VerbScopeWhat it does
projects.recallreadBrowse or search a project. Three modes — see below.
projects.get_observationreadExpand ONE observation to its full text, by slug.
projects.list_filesreadList the appendix — path, filename, description, type, size. No file contents.
projects.read_filereadGet a short-lived download link for one appendix file.
projects.addwriteWrite one new observation directly, with its entities and relationships.
projects.store_filewriteFile an already-uploaded document into the appendix at a path.
projects.update_observationwriteEdit an observation in place; only the fields supplied change.
projects.consolidatewriteSuggests merges, de-duplications and missing links. Never changes the project by itself.
projects.apply_consolidationwriteAccept ONE suggestion from a consolidation, by id — this is the step that actually changes the graph.
projects.reject_consolidationwriteDiscard one suggestion; nothing is applied.
projects.edit_storewriteRename the project or change its description, icon or visibility. The embedding model stays fixed.
projects.create_storecreateMake a new project. embedding_model_id is required and permanent.
projects.proposecreateAuthor a reviewable package of projects and observations; you click Create.
projects.delete_observationdestructiveWithheld. Refused for every token — deletion is not possible over the connection.
projects.merge_entitiesdestructiveWithheld. Fusing two entities is irreversible, so it is refused for every token.

Anything a person should see before it lands goes through projects.propose — your assistant writes the package, you get a review page with the observations laid out as cards and a real Create button. The direct writes are for the cases where a review adds nothing, like fixing one wrong sentence.

Consolidation is a two-step, on purpose

projects.consolidate only proposes a clean-up set; it changes nothing. You (or your assistant, if you ask it to) then accept the good suggestions one at a time with projects.apply_consolidation and drop the rest with projects.reject_consolidation. Re-applying an already-applied suggestion is refused cleanly.

Where you finish this​

projects.propose creates nothing. It hands you a link to a page at /external/memories/… inside Nirvai, where you read the project and the first facts it will hold, and click Create.

  • You have to be signed in, and only you can open it. A proposal link is not shareable.
  • projects.add has no review page. Writing a new observation into a project that already exists lands directly — the review step is for creating the project itself.
  • Proposing into a project you already have adds facts to it — pass its id and the package is an add-only one; it never makes a second project.
  • It can't be clicked twice. Re-opening afterwards shows Created ✓ and refuses a second project; your assistant picks up the new id from the feed, not by proposing again.
  • Want it different? The page gives you a copy-paste block to hand back, and your assistant re-proposes — see the review page.

recall has three modes, and you must name one​

mode is required. The connection validates the (mode, query) pair and rejects an inconsistent one — it will never quietly switch modes on your assistant's behalf.

modequeryUse it toYou get back
recallmust be absentBrowse or enumerate — everything in a category, everything since a date, everything about an entityFull-text observations plus entities. Complete; no follow-up needed.
localrequiredAsk a natural-language questionRanked entities, their relationships, and 240-character observation snippets flagged truncated.
cascaderequiredThe same question, when local came back thinThe same shape as local.

mode="recall" with a query is rejected. mode="local" without one is rejected. Filters differ by mode — nirvai_describe on the project lists which ones that project accepts.

nirvai_execute(
alias = "projects.get_observation",
args = { store: "…", slug: "acme-renewed-msa" },
description = "Read the full note behind the Acme snippet"
)

Reach for get_observation to expand one truncated snippet. Don't loop it to rebuild a project — that is what the structural mode is for, and it already returns full text.


Worked example: reconciling on first connect​

The first time an assistant connects, the durable facts it already carries about you belong in Nirvai, where your agents can see them too. Read first, then write only what is missing.

nirvai_execute(
alias = "projects.recall",
args = { store: "…", mode: "recall", category: "clients", limit: 20 },
description = "See what Nirvai already knows about my clients"
)

nirvai_execute(
alias = "projects.add",
args = {
store: "…",
title: "Acme renewed their MSA",
content: "Acme renewed the master service agreement in March 2025 on a 24-month term.",
category: "contracts",
tags: ["acme", "renewal"],
entities: [{ name: "Acme Corp", type: "company",
description: "long-standing enterprise client; renewed MSA" }],
relationships: [{ source: "Acme Corp", target: "MSA-2025",
relationship_type: "signed" }]
},
description = "Record the Acme renewal I already knew about"
)

This is a one-time reconciliation per project, not a dump of every turn — durable facts about your world only, never transient chat state.


Limits & guarantees​

  • An observation with no described entity is rejected, up front and cleanly. Without one it would be unfindable, so the connection refuses to write a fact nobody can ever recall.
  • Consolidation only proposes. projects.consolidate returns suggestions; applying them is a separate, explicit projects.apply_consolidation. If an assistant says it cleaned up your graph without that step, it did not.
  • Deletion is impossible. projects.delete_observation and projects.merge_entities refuse for every token, always.
  • Editing a title may re-mint the slug; a content-only edit keeps it, and keeps the entities linked to it.
  • Results are bounded. Recall returns a preview plus a link, never the raw graph; large files come back as a download reference rather than inline text.
  • Every successful write shows up immediately in your projects view and in the Activity Feed.

What's next​