Memory
Asking a memory
cortex_ask, cortex_recall, and how to read what comes back.
cortex_ask
The default retrieval call. It fuses vector search, full-text search and graph expansion, then returns the fused result set with provenance attached. No model runs server-side: what comes back is evidence to reason over, not prose.
{
"question": "What did we decide about pricing last quarter?",
"limit": 10,
"since": "2026-04-01T00:00:00Z"
}| Argument | Type | Notes |
|---|---|---|
question | string | Required. Natural-language question or search query. |
limit | number | Max results. Defaults to 10. |
source_table | string | Restrict to one source table, e.g. gmail_messages. |
since | string | RFC 3339 lower bound on item time. |
until | string | RFC 3339 upper bound on item time. |
Follow-up questions are new questions
A result describes only the question it was fetched for. There is no conversational
state on the server. A follow-up needs its own cortex_ask call with its own
question, not a re-read of the previous result.
cortex_recall
Semantic-only vector search, returning source excerpts rather than a composed answer. Reach for it when you want nearest-neighbour matches on meaning and intend to do your own synthesis.
{ "query": "onboarding friction", "limit": 20 }It takes query and limit, nothing else. There are no time bounds on
cortex_recall; use cortex_ask when you need them.
Reading the response
cortex_ask returns evidence, not prose. There is no model on the server
side composing an answer: you get a ranked result set with provenance, and your
agent does the reasoning over it. An abridged response looks like this:
{
"query": "What did we decide about pricing last quarter?",
"results": [
{
"universal_id": "gmail_messages:18f2c9a...",
"score": 0.412,
"title": "Re: tier pricing",
"preview": "Agreed: Solo goes to $19.99/seat from July.",
"source_table": "gmail_messages",
"source_url": "https://mail.google.com/mail/u/0/#inbox/18f2c9a...",
"age_seconds": 3421,
"signals": { "vector_rank": 1, "text_rank": 3 }
}
],
"freshness": [
{ "source_id": "google-workspace", "last_success_at": "2026-08-26T18:02:00Z", "status": "ok" }
],
"signals": { "vector_candidates": 40, "text_candidates": 22, "graph_added": 6 },
"took_ms": 380,
"graph_url": "https://mitosislabs.ai/graph?office=4d38b034-...",
"cited_graph_url": "https://mitosislabs.ai/graph?office=4d38b034-...&cite=gmail_messages%3A18f2c9a...",
"memory": {
"office_id": "4d38b034-...",
"office_name": "Mitosis Labs",
"graph_url": "https://mitosislabs.ai/graph?office=4d38b034-..."
}
}Four fields carry most of the operational meaning:
resultsobject[]
The evidence. Each item has a universal_id (this is the citation: pass it to
cortex_remember as source_universal_ids), a fused score, a preview,
the source_table it came from, and a source_url when the original is
linkable. There is no top-level citations array: the ids live here.
freshnessobject[]
Per-source sync state. A confident-looking answer drawn from a source that last synced a week ago is a different claim from the same answer drawn from one that synced an hour ago, and only this field tells them apart.
cited_graph_urlstring
Deep link into the user's own graph with the cited nodes highlighted. Good to
surface directly to a human who asks "where did that come from?" Absent when
the answer cited nothing, because an empty &cite= highlights nothing.
graph_url is the same graph without the citations pre-selected, and is
always present.
memory.office_namestring
Which memory answered. Note that a connector's display name is text the user typed and identifies nothing. This field is the real one.
The same call from the SDK and the CLI
The MCP tools above are one surface over the retrieval engine. From TypeScript
the same call is cortex.answer(), whose request field is query rather than
question, and whose results carry per-item scores and signals instead of a
composed prose answer.
const cortex = client.cortex(officeId);
const answer = await cortex.answer(officeId, {
query: 'What did we decide about pricing last quarter?',
limit: 10,
since: '2026-04-01T00:00:00Z',
});
for (const r of answer.results) {
console.log(r.score.toFixed(3), r.title, r.universal_id);
}// Attributes writes, and carries provenance from ask() into remember().
const memory = client.cortex(officeId).forAgent(officeId, 'atlas');
const evidence = await memory.ask('What did we decide about pricing last quarter?');
evidence.top; // best-scored result
evidence.isEmpty; // nothing matched at all
evidence.isFresh(3600); // did every source sync within the last hour?
evidence.universalIds; // the ids remember() will link against
evidence.graphUrl(); // /graph deep link highlighting exactly these nodes
console.log(evidence.context()); // citation-formatted block for a promptmi cortex ask "What did we decide about pricing last quarter?" --office "$OFFICE"
mi cortex answer "..." --office "$OFFICE" --limit 10 \
| jq -r '.results[] | "\(.score)\t\(.title)\t\(.universal_id)"'| MCP field | SDK equivalent |
|---|---|
results[].universal_id | answer.results[].universal_id, or evidence.universalIds |
cited_graph_url | evidence.graphUrl(), or graph_url in mi cortex ask --json |
| per-source sync state | answer.freshness[], or evidence.isFresh() |
Full worked example, including ingest: ingest and query.
Handling a gap instead of guessing
The mistake to avoid is treating weak results as an answer. Check for the gap blocks described in how memory works before you compose a reply:
const res = await askMemory(question);
if (res.memory_state) {
// Nothing connected yet. Offer res.memory_state.cta.url, don't apologise
// for a search that never had anything to search.
} else if (res.source_gap) {
// The graph has data, just not this kind. Name the missing source and
// offer res.source_gap.cta.url.
} else if (res.possible_source_gap) {
// Answer, but hedge: these may be near-misses.
} else {
// Trustworthy: either a real answer, or a real absence.
}The cta.url in each block opens the user's own dashboard page for connecting
sources. Handing it over is safe: the link only shows them that page, and
connecting is an authorization they perform there.