# Mazarbul (mcpb · mazarbul_v0.2.3_linux_amd64.mcpb)

Local-first memory for AI agents: entities, facts, relations and documents with semantic recall.

- Trust score: 42/100 (low)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-11-05

## Components

- mcpb · `mazarbul_v0.2.3_darwin_amd64.mcpb`: 10/100, [markdown](https://verifymcp.io/servers/mo-cmyk-mazarbul/mazarbul-darwin-amd64.md), [page](https://verifymcp.io/servers/mo-cmyk-mazarbul/mazarbul-darwin-amd64)
- mcpb · `mazarbul_v0.2.3_darwin_arm64.mcpb`: 10/100, [markdown](https://verifymcp.io/servers/mo-cmyk-mazarbul/mazarbul-darwin-arm64.md), [page](https://verifymcp.io/servers/mo-cmyk-mazarbul/mazarbul-darwin-arm64)
- mcpb · `mazarbul_v0.2.3_linux_amd64.mcpb`: 42/100 (this document), [markdown](https://verifymcp.io/servers/mo-cmyk-mazarbul/mazarbul-linux-amd64.md), [page](https://verifymcp.io/servers/mo-cmyk-mazarbul/mazarbul-linux-amd64)
- mcpb · `mazarbul_v0.2.3_linux_arm64.mcpb`: 10/100, [markdown](https://verifymcp.io/servers/mo-cmyk-mazarbul/mazarbul-linux-arm64.md), [page](https://verifymcp.io/servers/mo-cmyk-mazarbul/mazarbul-linux-arm64)

## Channel facts

- Registry: `mcpb`
- Package: `https://github.com/mo-cmyk/mazarbul/releases/download/v0.2.3/mazarbul_v0.2.3_linux_amd64.mcpb`
- Transport: `stdio`
- SHA-256: `45c720f7a2179be5914ab90ad4cc64416b8fbe5da47e6a5f7080fca3ed68be67`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-11-05.

- **Supply Chain Security**: 0/100
  - No malware scan is available for this kind of package: the supply-chain vendors we use do not cover it. This is a permanent gap in our coverage, not a finding about the package.
  - Known CVEs could not be checked: we found no dependency list to check in this bundle: either it runs a compiled binary, whose bundled manifests we do not read, or none of the package.json, requirements.txt, pyproject.toml or uv.lock files it ships for its runtime declares a dependency we can read (a requirements.txt that only points at a setup.py project, a local path, or a nested requirements file we cannot follow counts as none).
  - Install-script risk not yet assessed.
  - Dependency health could not be checked: we found no dependency list to check in this bundle: either it runs a compiled binary, whose bundled manifests we do not read, or none of the package.json, requirements.txt, pyproject.toml or uv.lock files it ships for its runtime declares a dependency we can read (a requirements.txt that only points at a setup.py project, a local path, or a nested requirements file we cannot follow counts as none).
- **Provenance & Transparency**: 48/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - Clear OSI-approved license (Apache-2.0).
  - Actively maintained (last published 4 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 77/100
  - 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 17073 tokens (~254/item across 67 items; 64 tools + 3 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 17/100
  - Stability observed for 5 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Tool Safety**: 75/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - 0 of 4 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "deleteEntity" implies "delete" and declares no destructiveHint at all, which the MCP spec reads as destructive by default.
  - An AI judge read all 66 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

**Unverified: 1 category.** A category scored 0 because we could not verify it: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

## Install

### How do I install the Mazarbul MCP server?

Mazarbul ships as an MCPB bundle, a single file you download and open in an MCP host that supports MCPB bundles, such as Claude Desktop. The host reads the launch command from the bundle's own manifest, so there is no command to copy. Before opening it, check the file's SHA-256 matches the one the MCP registry declares for it, 45c720f7a2179be5914ab90ad4cc64416b8fbe5da47e6a5f7080fca3ed68be67; the commands to do that are on this page. If it does not match, do not open it.

- Download bundle: `https://github.com/mo-cmyk/mazarbul/releases/download/v0.2.3/mazarbul_v0.2.3_linux_amd64.mcpb`
- SHA-256: `45c720f7a2179be5914ab90ad4cc64416b8fbe5da47e6a5f7080fca3ed68be67`

### Verify on macOS / Linux

```bash
echo "45c720f7a2179be5914ab90ad4cc64416b8fbe5da47e6a5f7080fca3ed68be67  mazarbul_v0.2.3_linux_amd64.mcpb" | shasum -a 256 -c
```

### Verify on Windows (PowerShell)

```powershell
(Get-FileHash -Algorithm SHA256 .\mazarbul_v0.2.3_linux_amd64.mcpb).Hash -eq "45c720f7a2179be5914ab90ad4cc64416b8fbe5da47e6a5f7080fca3ed68be67"
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-11-04 (score 42, +1)

No change was recorded against any check on this day. Stability & Change Management went from 10 to 13. That category is still filling its 30-day observation window: 3 days of observed history at the previous scan, 4 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-11-02 (score 41, +1)

No change was recorded against any check on this day. Stability & Change Management went from 3 to 7. That category is still filling its 30-day observation window: 1 days of observed history at the previous scan, 2 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-10-31 (score 40)

First indexed and scored.

## MCP tools (64)

### `addIdentifier` (~268 tokens)

Attach an identifier (an alias by default) to an EXISTING entity, appending an entityIdentifierAdded event so the resolver and vault wikilinks match the entity by that surface form. Use this after renaming an entity's displayName via updateEntity to retain the prior name: addIdentifier{entityUid, identifier:"Old Name", kind:"alias"}. createEntity only seeds identifiers at mint time; this adds them later. kind defaults to "alias"; pass email/username/phone/id or a snake_case domain type for other identifiers. Returns E_NOT_FOUND when the uid does not resolve and E_VALIDATION on an empty identifier or malformed kind.

Input parameters:

- `entityUid` (string, required): ULID of the existing entity to attach the identifier to. Required.
- `identifier` (string, required): The surface form / external identifier to attach, e.g. a prior display name "Erebor Global", an email, a username, or a domain id. Required.
- `kind` (string): Identifier kind in snake_case. Canonical: alias, email, username, phone, id; domain types (hrb, lei, tax_id, …) keep their own lookup lane. Defaults to "alias" when omitted — the kind to use for reta…

Output parameters:

- `name` (string): AIP resource name of the entity the identifier was attached to.
- `status` (string): Always "added" on success.

### `agentFeedback` (~318 tokens)

Record durable feedback about using Mazarbul: a friction point (confusing|missing|wrong_result|slow|tool_error|suggestion) or a lesson you learned (a one-line rule). Give a category, severity, and one-line summary; optionally the surface (tool/CLI), detail, and a distilled rule. Recurring feedback auto-promotes into your per-session guidance (the user can pin/edit/veto). File DURABLE friction or rules, not one-off disagreements.

Input parameters:

- `activePreset` (string): Active steering preset, if any. Optional; auto-context.
- `category` (string, required): Kind of feedback. confusing|missing|wrong_result|slow|tool_error|suggestion are friction; lesson is a durable rule you learned. Required.
- `detail` (string): Optional longer context: reproduction or expected-vs-actual.
- `keywords` (array): Optional tags aiding clustering + search.
- `rule` (string): For a lesson: the distilled one-line durable rule, phrased as guidance. This is what gets promoted into the session guidance. Optional.
- `scope` (string): Scope to record into. Defaults to personal (device-local diagnostic).
- `severity` (string): Impact. high promotes into guidance at a lower recurrence threshold. Required.
- `summary` (string, required): One-line description of the friction or rule. Required.
- `surface` (string): Tool / CLI / instruction the feedback is about, e.g. recall, "maz load". Optional but encouraged (part of the recurrence cluster).

Output parameters:

- `clusterKey` (string): Recurrence cluster this feedback was filed under. Reportable via maz feedback.
- `status` (string): Always "recorded" on success.

### `cancelJob` (~45 tokens)

Cancel a tracked background job by its AIP resource name ("jobs/{uid}").

Input parameters:

- `name` (string, required): Resource name of the job to cancel: "jobs/{uid}".

Output parameters:

- `name` (string)
- `state` (string)

### `captureExternal` (~736 tokens)

Index an external item you fetched from a live connector (Notion, Linear, Granola, Drive, GitHub) WITHOUT copying its body into maz. One call stores a pointer + YOUR summary + a freshness anchor; maz never fetches or holds the content. recall later returns the summary with an inline freshness verdict so you reuse it when fresh and re-fetch live when stale. The capture is UNTRUSTED (lineage connector:<name>): facts land proposed, never superseding your own. Idempotent by contentHash within the connector's TTL. Pass status=deleted|access-revoked to tombstone an item you can no longer reach. Refuses project scope and ephemeral connectors (gmail/gcal/slack; read those live). recall FIRST before capturing, to avoid duplicates.

Input parameters:

- `connector` (string, required): The connector the item came from: notion, linear, granola, drive, github. Drives the freshness budget and the untrusted-lineage trust tag. Ephemeral connectors (gmail, gcal, slack) are refused: read…
- `contentHash` (string): SHA-256 hex of the item's canonical content subset (the freshness ETag). Recapturing with the same hash within the connector's TTL is a no-op. OMIT it if you cannot compute a real SHA-256 over the fe…
- `dedupKey` (string): Idempotency key. Defaults to <connector>:<externalId>:<scope>. Recapturing the same item must reuse the same dedupKey (omit it and pass a stable externalId, or pass it explicitly).
- `externalId` (string): The source-system stable id (Notion page id, Linear issue key). Used to mint the dedupKey when you omit it; hashed in any plaintext render.
- `externalUrl` (string): Dereferenceable location to re-fetch when stale. Defaults to uri.
- `fetchTime` (string): When you fetched the item (RFC 3339 UTC). Defaults to now.
- `label` (string): Human-readable display name for the external_ref. Defaults to the externalId or connector, NEVER the raw URI (which would leak into a shared render).
- `scope` (string): Scope to capture into. Defaults to personal. Project is refused: captured external content is sensitive and project renders to shared git.
- `sourceType` (string, required): Open-taxonomy kind of the item, e.g. notion_page, linear_issue, meeting_notes, drive_doc.
- `status` (string): active (default) records a live snapshot. deleted (a 404 on re-fetch) or access-revoked (a 403) tombstones the prior capture so recall flags it access-unverified instead of dropping it.
- `stream` (string): Sub-channel within the connector, e.g. a Notion database id or a Linear team key. Optional.
- `summaries` (array): Chunked summary: one episode per element, all sharing one external_ref. Use for long items; otherwise pass summary.
- `summary` (string): Your faithful summary of the fetched content. Put the stable external id and key facts in the prose so recall can find it. Required unless you pass summaries[] or a tombstone status.
- `uri` (string, required): Absolute URI of the external item (must carry a scheme), e.g. a Notion page URL, a Linear issue URL, a Drive doc link. The canonical identity of the external_ref pointer.

Output parameters:

- `action` (string): captured = first snapshot; skipped = unchanged within TTL (no write); revision = content changed (new episode, prior superseded); expired = unchanged but past TTL (fetch-time refreshed); tombstoned =…
- `episodeNames` (array): Resource names of the summary episode(s) this capture is grounded in (one per chunk).
- `externalRefName` (string): AIP resource name of the external_ref pointer entity.
- `freshness` (string): The resulting freshness verdict for this item, the same value recall will report.
- `sourceRefNames` (array): Resource names of the SourceRef provenance rows.

### `connectSource` (~326 tokens)

Connect a source and run a bounded first backfill into the graph. Give a connector (local-git, claude-chats, or granola) and, for the file-based ones, repoPath (granola uses a stored API key instead); optionally scope (personal|team, default personal), dryRun (preview + count without writing), maxItems, and maxExtractionCalls. Ingested content is UNTRUSTED: facts land proposed and capped, never superseding your own. Returns a run report with itemsApplied/Skipped and a jobUid. Refuses project scope.

Input parameters:

- `connector` (string, required): Source connector name. Supported: local-git, claude-chats, granola.
- `dryRun` (boolean): Preview only: validate the source and count would-be items WITHOUT writing anything (the consent gate).
- `maxExtractionCalls` (integer): Cap on episodes appended this run, each one downstream extraction. Defaults to 500.
- `maxItems` (integer): Cap on items considered this run (backfill bound). Defaults to 500.
- `repoPath` (string): Source locator for file-based connectors: a local git repository path (local-git), or an export file path (claude-chats: a .zip bundle or a conversations.json). Omit for granola, which authenticates…
- `scope` (string): Scope to ingest into. Defaults to personal. Project scope is refused: ingested external content is sensitive and project is plaintext.

Output parameters:

- `connector` (string)
- `dryRun` (boolean)
- `episodesAppended` (integer)
- `itemsApplied` (integer): Items turned into episodes (or, in a dry run, that WOULD be).
- `itemsSkipped` (integer): Items skipped because their content hash was unchanged (idempotent re-ingest).
- `jobUid` (string): Job UID; pass to listJobs / cancelJob to track or cancel a long backfill.
- `streams` (array)

### `contextCorrect` (~205 tokens)

Correct a wrong item the cold-start brief surfaced. Pass the item's id (a pin's contextPins/{uid}, or an entities/{uid} the brief showed) and the corrected text; the brief renders your text in its place. The correction is a NEW event, not an in-place edit — history is preserved and the latest correction wins. Returns the overlay's contextPins/{uid}.

Input parameters:

- `scope` (string): Scope to write into; match the scope the corrected pin lives in. Defaults to personal ("project" = this repo; "team" requires admitted membership, else E_PERMISSION).
- `targetId` (string, required): Id of the item to correct: a contextPins/{uid} (or bare ULID) of a pin shown in the brief, or an entities/{uid} surfaced in it. The brief renders this corrected text in the item's place.
- `text` (string, required): The corrected text to show in place of the surfaced item. Required, non-empty.

Output parameters:

- `name` (string): AIP resource name of the correction overlay; retire it with contextUnpin.
- `status` (string): Always "corrected" on success.

### `contextPin` (~233 tokens)

Pin a free-text steering note to the top of the user's cold-start context brief. Use it when the user states a durable preference or constraint they want surfaced first every session (e.g. "I'm mid-migration off Postgres — don't suggest new Postgres features"). The note wins a budget slot before auto-surfaced documents and entities. Returns the pin's contextPins/{uid}; retire it with contextUnpin or override it with contextCorrect.

Input parameters:

- `scope` (string): Scope to write into — a pin steers whoever reads that scope. Defaults to personal (steers only the user). Use "project" to steer anyone working in this repo. Use "team" to steer the whole team (requi…
- `targetRef` (string): Optional resource the note is about, e.g. an entities/{uid}. Omit for a free-floating steering note.
- `text` (string, required): The steering note to surface at the top of the cold-start brief, e.g. "Mid-migration off Postgres — don't suggest new Postgres features". Required, non-empty.

Output parameters:

- `name` (string): AIP resource name of the new pin; pass to contextUnpin or contextCorrect.
- `status` (string): Always "pinned" on success.

### `contextUnpin` (~134 tokens)

Retire a steering pin or correction so it stops surfacing in the cold-start brief. Pass its contextPins/{uid} (or bare ULID). The row is tombstoned, not deleted — history is preserved and the retire converges across devices. Use when a pinned note or correction is no longer relevant.

Input parameters:

- `pin` (string, required): The pin or correction to retire: contextPins/{uid} or a bare ULID, as returned by contextPin/contextCorrect or shown in the brief.
- `scope` (string): Scope the tombstone is written into; match the pin's scope. Defaults to personal.

Output parameters:

- `name` (string): AIP resource name of the retired pin or correction.
- `status` (string): Always "unpinned" on success: the row is retired (a convergent tombstone), not deleted.

### `count` (~316 tokens)

Count entities matching an AIP-160 filter — the deterministic answer to "how many …". With groupBy, returns a per-value breakdown (e.g. people per status) instead of a single total. Same filter grammar as the entities tool. Scope: defaults to project+personal; pass scopes:["team"] to include team entities.

Input parameters:

- `filter` (string): AIP-160 filter expression selecting which entities to count. Same grammar as the entities tool: reference entity columns (kind, displayName, confidence, resolutionStatus, createTime), fact slots by s…
- `groupBy` (string): When set, return a per-value breakdown counted by this field instead of a single total. The field is an entity column (kind, resolutionStatus, …) or a fact slot state_key (status, …); entities with n…
- `kind` (string): Convenience prefilter to one entity kind, AND-combined with filter.
- `limit` (integer): Max groups to return when groupBy is set, most-populous first. Defaults to 50. Ignored without groupBy.
- `scopes` (array): Scope filter: which data partitions to count. Allowed values: project, personal, team. Defaults to [project, personal] when omitted — team entities are hidden unless you pass scopes:["team"].

Output parameters:

- `count` (integer): Total matching entities. Present when groupBy is not set.
- `groups` (array): Per-value breakdown, most-populous first. Present when groupBy is set.

### `createEntity` (~569 tokens)

Mint a structured entity of a registered kind directly, bypassing LLM extraction — the write-side complement to relate. Use it when you already hold an entity as (kind, displayName) and need its entities/{uid} to assert a relation, without a remember() round-trip. Inputs: kind (a core kind or a registered extension kind — register first with defineEntityKind, check with introspect), displayName, optional confidence (default 1.0), identifiers (anchor the entity so the resolver attaches future mentions instead of duplicating), alternateNames, and properties (typed attributes validated against the kind's propertySchema). Deduplicates before minting: a matching identifier returns the existing entity (status reused); an existing same-name or near-duplicate of the same kind is NOT created (status duplicate_candidates) — inspect candidates, then reuse a uid, merge with mergeEntities, or retry with force=true to record a genuinely distinct entity that happens to share a name. On a plain create (status created) any advisory near-matches are still listed in candidates. Returns E_VALIDATION on a malformed kind, empty displayName, out-of-range confidence, or non-object properties.

Input parameters:

- `alternateNames` (array): Other surface forms for this entity. Optional.
- `confidence` (number): Writer confidence in the entity's identity. Defaults to 1.0 (user-confirmed).
- `displayName` (string, required): Human-readable label, e.g. "Mithril Corp" or "Eowyn".
- `force` (boolean): Skip the duplicate check and mint a new entity even when one with the same name already exists. Use to record a genuinely distinct entity that shares a name with an existing one (e.g. a second real p…
- `identifiers` (array): Strong/weak external identifiers to anchor the entity so the resolver attaches future mentions instead of minting a duplicate. Optional.
- `kind` (string, required): Entity kind. A core kind (person, organization, place, project, event, document) or a registered extension kind (e.g. user:vehicle). Register a new kind with defineEntityKind first; introspect lists…
- `properties` (object): Typed attribute values matching the kind's propertySchema (P3), e.g. {"vin":"…","make":"…"}. Validated on write when strict_attributes is enabled. Optional.
- `scope` (string): Scope to write into — pick by who should see it. Defaults to personal (private to the user, syncs to their devices). Use "project" for records tied to the current repo/codebase (shared with anyone wh…

Output parameters:

- `candidates` (array): Existing entities that match by name or embedding. On duplicate_candidates the create was refused; on created these are advisory near-matches you may want to merge.
- `kind` (string): The entity's kind (set on status created/reused).
- `name` (string): AIP resource name of the entity (set on status created/reused). Pass to relate or get(entities/{uid}).
- `status` (string): created = a new entity was minted; reused = an existing entity matched by identifier and is returned in uid/name; duplicate_candidates = NOT created because same-name or near-duplicate entities exist…
- `uid` (string): Bare ULID of the entity (set on status created/reused).

### `defineEntityKind` (~582 tokens)

Register a new entity kind in the dynamic-ontology registry — but first check whether an existing kind already covers the concept (introspect → entityKinds, or describeType). The built-in dedup check runs automatically: a near-identical kind returns status="reused" with the existing name so you avoid minting a synonym; a medium-similarity near-duplicate mints but records a SKOS closeMatch alias (response field closeMatch + similarity, internal URN maz:kind:<name> added to sameAs). Because that dedup runs a semantic embedding pass over existing kinds, this call routinely takes a few seconds to return — that latency is expected, not a hang; do not abort or retry on a short timeout. Inputs: name (bare snake_case, no namespace), displayName, description (required for dedup), optional extends (parent kind — must be a core kind or an already-registered active kind; bare names resolve to their canonical registered form), sameAs (external vocabulary IRIs), propertySchema (JSON Schema for typed attributes). Appends an entityKindRegistered event; the dispatcher materialises it into entity_kinds and the render pipeline writes it to the git-synced ontology/ tree. Re-registering an existing user kind with force=true overwrites it. Attempting to modify a core kind, or extending an unknown/ambiguous parent, returns E_VALIDATION.

Input parameters:

- `description` (string, required): One-sentence explanation of the kind. Used by the dedup check and agents.
- `displayName` (string, required): Human-readable label, e.g. Vehicle.
- `extends` (string): Parent kind name for single-inheritance subtype hierarchy (RDFS subClassOf). Must be a core kind or an already-registered active kind; a bare name (vehicle) resolves to its canonical registered form…
- `force` (boolean): Bypass the reuse-before-mint dedup check and register unconditionally. Default false.
- `name` (string, required): Bare snake_case kind identifier, e.g. vehicle. No namespace prefix — user: is added automatically. Must not start with core:.
- `preview` (boolean): When true, compute and return a plan of what this call would do (collapse, supersede, or mint) WITHOUT appending any event. The event log is unchanged. Use this to inspect impact before committing. D…
- `propertySchema` (object): Optional JSON Schema (Draft 2020-12) for typed attributes. Compiled before registration; E_VALIDATION on compile failure.
- `sameAs` (array): Alias/sameAs IRIs to canonical external vocabularies, e.g. ["schema.org/Vehicle", "wikidata:Q42889"]. Optional.

Output parameters:

- `closeMatch` (string): The name of a closely related existing kind (skos:closeMatch semantics). Present when a medium-similarity near-duplicate was found during minting.
- `name` (string): The registered kind name, e.g. user:vehicle.
- `plan` (object): Present only when preview=true. Describes what the call would do without writing any event.
- `reusedKind` (string): The existing kind name when status=reused. Absent otherwise.
- `similarity` (number): Dedup similarity score (0..1). Present when status=reused or when closeMatch is set.
- `status` (string): registered = kind written as active; reused = existing near-identical kind returned instead of minting; proposed = kind queued for review in governed mode; preview = plan computed without writing any…
- `warnings` (array): Non-fatal advisory warnings. MAZ-KIND-001 = close match found; MAZ-KIND-002 = semantic dedup skipped; MAZ-SCHEMA-001 = supersede; MAZ-SCHEMA-002 = orphaned fields; MAZ-SCHEMA-003 = collapse.

### `definePredicate` (~443 tokens)

Register a new predicate in the shared predicates vocabulary — but first call introspect to check existing predicates (works_at, member_of, etc.) so you don't mint a synonym. Inputs: predicate (snake_case), kind (functional for at-most-one-live-value, non-functional for multiple concurrent values), description, and optional typing: subjectKinds/objectKinds (domain/range — the entity kinds allowed on each end), sameAs (external-vocabulary IRIs), and propertySchema (a JSON Schema for the relation's attributes). Domain/range and attribute constraints are advisory by default and enforced only when [ontology] strict_domain_range is enabled. The predicate is immediately queryable via introspect and usable in relate calls. Re-registering an existing predicate updates its kind, description, domain/range, sameAs, and propertySchema and returns status=updated. Returns E_VALIDATION if the predicate name or propertySchema is malformed.

Input parameters:

- `description` (string, required): One-sentence human-readable explanation of what the predicate means.
- `kind` (string, required): functional = at most one live value per subject (e.g. works_at). non-functional = multiple concurrent values allowed (e.g. member_of).
- `objectKinds` (array): Optional range constraint: entity kinds allowed as the object of this relation, e.g. ["organization"] for works_at. Omit to accept any kind.
- `predicate` (string, required): Snake_case predicate identifier, e.g. works_at or member_of. Must be unique in the predicates vocabulary.
- `propertySchema` (object): Optional JSON Schema (Draft 2020-12) constraining the relation's attributes map, e.g. require a string role. Validated against relation instances only when [ontology] strict_domain_range is enabled.
- `sameAs` (array): Optional alias IRIs mapping this predicate to external vocabularies, e.g. ["https://schema.org/worksFor", "wikidata:P108"].
- `subjectKinds` (array): Optional domain constraint: entity kinds allowed as the subject of this relation, e.g. ["person"] for works_at. Omit to accept any kind (open-world default).

Output parameters:

- `predicate` (string): The registered predicate identifier.
- `status` (string): registered = new predicate written to the vocabulary; updated = existing predicate's kind, description, domain/range, sameAs, and propertySchema overwritten.

### `defineSchema` (~300 tokens)

Register a JSON Schema document in the dynamic-ontology schema registry. Use this to attach validation schemas for custom entity-kind properties (schemaKind=entityKind:vehicle) or to define schemas for new event types. Inputs: schemaKind (bare camelCase or colon-namespaced, e.g. entityKind:deal), version (semver vX.Y.Z), definition (JSON Schema object). Core schema kinds (entity, episode, etc.) are protected and return E_VALIDATION. The schema is immediately usable for property validation via ValidateEntityProperties. Returns E_VALIDATION if the definition is not a JSON object or if schemaKind is a protected core kind.

Input parameters:

- `definition` (object, required): JSON Schema document (Draft 2020-12) to register. Must be a JSON object.
- `preview` (boolean): When true, compute and return a plan (supersede or register) WITHOUT appending any event. Use this to inspect impact before committing. Default false.
- `schemaKind` (string, required): Lookup key for the schema, e.g. entityKind:vehicle or myCustomEvent. Bare camelCase or colon-namespaced. No spaces. Core kinds (entityKindRegistered, schemaRegistered, predicateRegistered, entity, ep…
- `version` (string, required): Semver version, e.g. v0.1.0. Higher versions replace lower ones; same version with a different definition is a conflict.

Output parameters:

- `plan` (object): Present only when preview=true. Describes what the call would do.
- `schemaKind` (string): The registered schema kind identifier.
- `status` (string): registered = schema written to the event log; preview = plan computed without writing any event (preview=true was set).
- `version` (string): The registered schema version.
- `warnings` (array): Non-fatal advisory warnings. MAZ-SCHEMA-001 = supersede; MAZ-SCHEMA-002 = orphaned fields.

### `deleteEntity` (~275 tokens)

Hard-delete an entity by ULID: removes the entity and all its derived data (relations, facts, mentions, identifiers, citations) and detaches its graph node, appending an entityDeleted tombstone to the audit log. This is for unwanted or junk entities — to consolidate DUPLICATES without losing data use mergeEntities instead. Safe default: refuses with E_VALIDATION when the entity still has active relations (so a connected entity is never dropped by accident); pass cascade=true to tombstone those relations first and proceed. Returns E_NOT_FOUND when the uid is unknown.

Input parameters:

- `cascade` (boolean): Delete the entity even when it still has active relations. When true, each active relation is first tombstoned (relationForgotten) for the audit trail, then the entity and all its remaining derived d…
- `entityUid` (string, required): ULID of the entity to delete. Required.
- `reason` (string): Optional free-text reason for the deletion. Surfaced in audit/log views and stamped on the cascaded relationForgotten tombstones.

Output parameters:

- `forgottenRelations` (integer): Number of active relations tombstoned by the cascade before the entity was removed (0 unless cascade was needed).
- `name` (string): AIP resource name of the deleted entity.
- `status` (string): Always "deleted" on success.

### `describeType` (~171 tokens)

Describe a single entity kind in full: displayName, description, parent (extends), child kinds, external-vocabulary aliases (sameAs), provenance lane, and how many entities currently carry it. The deep counterpart to introspect's cheap kind index — call it to confirm a kind's shape and reuse before minting a near-duplicate with defineEntityKind, or before createEntity. Accepts a canonical name (person, user:vehicle) or a bare local name (vehicle) that resolves to the user: kind when unambiguous. Returns E_NOT_FOUND with near-name suggestions when the kind is not registered.

Input parameters:

- `name` (string, required): Entity kind to describe, e.g. person or user:vehicle. A bare local name (vehicle) also resolves to the user: kind when unambiguous.

Output parameters:

- `addedBy` (string): Who registered the kind: seed, llm, user, or extension.
- `children` (array): Registered kinds that extend this one.
- `description` (string): One-sentence explanation of the kind.
- `displayName` (string): Human-readable label.
- `name` (string): Canonical kind name, e.g. user:vehicle.
- `parent` (string): Parent kind in the subtype hierarchy (extends). Absent for root kinds.
- `propertySchema` (object): JSON Schema for the kind's typed properties (attr_schema). Present only when the kind declares typed properties; use this to validate or populate the properties field in createEntity.
- `provenance` (string): Namespace lane: core, user, or extension:<name>.
- `sameAs` (array): Alias IRIs to external vocabularies (schema.org, Wikidata, FOAF, …).
- `usageCount` (integer): Number of entities currently carrying this kind.

### `dismissInsight` (~193 tokens)

Dismiss a synthesized insight the user judges wrong or unhelpful. Appends a durable insightDismissed event (the source of truth) that flips the insight to a terminal 'dismissed' state - it drops out of hot recall and is never re-promoted by access. This is NOT a decay/demotion (which the system does on its own); it records an explicit user decision and survives a rebuild. Identify the insight by insights/{uid} or a bare uid. Returns the dismissed insight's resource name.

Input parameters:

- `name` (string, required): The insight to dismiss: an insights/{uid} resource name (paste a listInsights / recall hit's name) or a bare uid.
- `reason` (string): Optional free-text justification for the dismissal, retained on the event for audit.
- `scopes` (array): Scopes to resolve the insight in: project, personal, team. Defaults to project+personal.

Output parameters:

- `name` (string): Resource name insights/{uid} of the dismissed insight.
- `status` (string): Always "dismissed" on success.

### `editDocument` (~223 tokens)

Edit a document by exact string replacement (Claude-Code Edit semantics): `oldString` must match the current body exactly and be unique unless `replaceAll`. The result is ingested as a new revision (history kept; wikilink edges re-resolved; removed links forgotten) and the vault file refreshes. Use writeDocument to replace a whole body. Result counts (`edges`, `unresolved`) are whole-document snapshots after the edit.

Input parameters:

- `newString` (string, required): Replacement text. Must differ from oldString.
- `oldString` (string, required): Exact text to replace. Must match the current body exactly (including whitespace) and, unless replaceAll, be unique in it.
- `path` (string, required): Vault-relative path of the document to edit.
- `replaceAll` (boolean): Replace every occurrence instead of requiring oldString to be unique.
- `scope` (string): Disambiguates when the same path exists in more than one scope. Omit when the path is unambiguous. An edit never changes a document's scope; it is preserved.

Output parameters:

- `changed` (boolean): False when the content matched the latest revision (no-op).
- `documentUid` (string)
- `edges` (integer): Total resolved wikilink and frontmatter edges the whole document holds after this write (snapshot, not a delta — counts all [[links]] in the current body that resolved, regardless of how many were al…
- `file` (string): Absolute path of the materialized vault file.
- `name` (string): entities/{uid} resource name of the document-as-entity.
- `path` (string)
- `pendingEdit` (boolean): True when the vault file holds an un-ingested manual edit and was left untouched.
- `reconciled` (integer): Count of wikilinks in OTHER vault documents that this write newly made resolvable (cross-document reconcile pass; separate from the document's own edge snapshot above).
- `revisionUid` (string)
- `unresolved` (integer): Count of [[wikilinks]] left dangling in the whole document after this write (snapshot, not a delta).
- `unresolvedTargets` (array): The dangling wikilink targets by name (text inside [[…]] that matched nothing) — fix a typo or create the target. Omitted when none dangle.
- `warnings` (array): Non-fatal advisory messages — e.g. a title-collision warning when the document's display name shadows a non-document entity. Does not affect the written content; the document body is stored byte-for-…

### `entities` (~440 tokens)

List and filter entities by kind, name substring, and confidence, with pagination. LLM-free discovery surface: find an anchor here, then pivot into neighbors/relations. sortBy=recallCount returns the most-recalled entities (replaces listTopEntities). Spot a duplicate of the same real-world thing? Consolidate the pair with mergeEntities (lossless); remove a junk entity with deleteEntity.

Input parameters:

- `filter` (string): AIP-160 filter expression, AND-combined with the other params. Reference entity columns (kind, displayName, confidence, resolutionStatus, createTime), the short alias status for resolutionStatus (val…
- `kind` (string): Filter to one entity kind, e.g. person, place, organization. See introspect for the kinds present.
- `limit` (integer): Maximum entities to return. Defaults to 50.
- `minConfidence` (number): Drop entities below this resolution confidence.
- `nameExact` (string): Case-insensitive exact match on display name (unbounded; takes precedence over nameLike). Use this to resolve a known name to its entity.
- `nameLike` (string): Case-insensitive substring match on display name.
- `offset` (integer): Pagination offset (recent sort only). Defaults to 0.
- `scopes` (array): Scopes to read from: project, personal, team. Defaults to project+personal. Unknown values are rejected.
- `sortBy` (string): Ordering. "recent" (default) = newest first. "recallCount" = most-recalled first (requires usage stats).
- `window` (string): Aggregation window for sortBy=recallCount. Defaults to last7Days.

Output parameters:

- `entities` (array)

### `fact` (~393 tokens)

Record a scalar fact — an attribute, state, or preference — about an entity as a (subject, stateKey, value) slot, bypassing LLM extraction. The fact-shaped counterpart to relate: relate records edges between entities, fact records a single entity's slots (e.g. state.task.status=pending). A later write to the same stateKey supersedes the prior value (kept as history). Read them back via get(entities/{uid}) for the current slots, or get(facts/{uid}) for one.

Input parameters:

- `eventStart` (string): When the fact became true in the world (RFC 3339 UTC). Optional; defaults to now.
- `scope` (string): Scope to write into — pick by who should see it. Defaults to personal (private to the user, syncs to their devices). Use "project" for facts tied to the current repo/codebase (shared with anyone who…
- `source` (string): Provenance label for the appended event, e.g. "cli:fact". Optional; defaults to "mcp:fact".
- `sourcePriority` (number): Recall-lane priority, 0..1. Optional; defaults to 1.0.
- `stateKey` (string, required): Hierarchical slot address: lowercase, dot-separated, e.g. state.task.status, preference.coffee.milk. The first segment is the category (event|state|relationship|preference|plan|absence|timeless).
- `subject` (string, required): Subject entity the fact is about: entities/{uid}, a bare ULID, the literal "self", or a human display name (resolved case-insensitively, like maz get).
- `value` (string, required): The scalar value the slot holds, e.g. "pending", "Steven".

Output parameters:

- `name` (string): AIP resource name of the asserted Fact; pass to get(facts/{uid}).
- `status` (string): Always "asserted" on success.

### `forget` (~189 tokens)

Forget an Episode, single Relation, or single Fact by ULID: appends a tombstone that removes derived state across the storage layers while preserving the JSONL audit trail. factUid hard-deletes a fact (state/preference) slot value — use retractFact to close a fact while keeping its history.

Input parameters:

- `episodeUid` (string): ULID of the Episode to forget. Mutually exclusive with relationUid/factUid.
- `factUid` (string): ULID of a single Fact (a state/preference slot value) to hard-delete. Mutually exclusive with episodeUid/relationUid. To close a fact while keeping its history, use retractFact instead.
- `reason` (string): Optional free-text reason for the forget; surfaced in audit/log views.
- `relationUid` (string): ULID of a single Relation to forget. Mutually exclusive with episodeUid/factUid.

Output parameters:

- `name` (string): AIP resource name of the forgotten Episode, Relation, or Fact.
- `status` (string): Always "forgotten" on success.

### `get` (~682 tokens)

Fetch one record by AIP resource name — entities/{uid}, episodes/{uid}, relations/{uid}, facts/{uid}, insights/{uid}, or consolidatedMemories/{uid} — plus its provenance lineage. For an entity you may also pass a bare uid or a human display name (resolved case-insensitively, the same forms relations/neighbors accept). When a name is shared across kinds, a non-document entity deterministically wins over a same-named document — the passed-over document(s) are surfaced in `shadowedDocuments` as entities/{uid} you can fetch directly; pass `kind` to target a specific kind, or a vault path / entities/{uid} to address a document. A name that is ambiguous among non-document entities (or unknown) returns a pointed error naming the candidates. The structured, LLM-free way to follow any recall hit's name field. Returns a discriminated union tagged by `type`. An entity record returns the canonical node (uid, kind, displayName, confidence, resolutionStatus) AND its typed `properties`, external `identifiers`, and currently-open scalar `facts`. A relation record carries hydrated endpoints + validity. An episode carries its source text. An insight (type=insight) returns the synthesized statement, insightType, currentConfidence, state, synthesizedAt, and its sourceUids as entities/{uid} citations. A consolidated memory (type=consolidatedMemory) returns the compressed summary, memoryType, confidence, consolidatedAt, and its sourceUids as episodes/{uid} citations. Demoted insights (state='demoted') are still fetchable — get is not the hot-recall index.

Input parameters:

- `asOf` (string): Transaction-time anchor (RFC 3339 UTC) for an entity record's fact rows: hide facts recorded after this instant. Ignored for non-entity records.
- `at` (string): Valid-time anchor (RFC 3339 UTC) for an entity record's fact rows: return each slot's value live at this instant. Defaults to now. Does not move the entity identity row; ignored for non-entity record…
- `includeInactive` (boolean): Include superseded / expired facts (the full slot history) on an entity record. Off by default. Ignored for non-entity records.
- `kind` (string): Optional entity kind hint (e.g. 'document', 'person', 'product'). When supplied alongside a display name, narrows the resolution to entities of that kind only. Useful when a name is shared between a…
- `name` (string, required): What to fetch: an AIP resource name whose collection prefix routes the lookup (entities/{uid}, episodes/{uid}, relations/{uid}, facts/{uid}, insights/{uid}, or consolidatedMemories/{uid}) — feed one…
- `scopes` (array): Scopes to read from: project, personal, team. Defaults to project+personal. Unknown values are rejected.

Output parameters:

- `consolidatedMemory` (object): Present when type=consolidatedMemory. A compressed episode summary: summary, memoryType, confidence, consolidatedAt, and sourceUids typed as episodes/{uid} citations.
- `entity` (object): Present when type=entity. The canonical node: uid, kind, displayName, confidence, resolutionStatus, and (when createEntity stored them) a 'properties' object of typed attributes validated against the…
- `episode` (object): Present when type=episode.
- `fact` (object): Present when type=fact. A scalar state slot: subjectUid, stateKey, value, validity window.
- `facts` (array): Present when type=entity: the entity's currently-open scalar facts (its attributes), newest slot value per state_key. Empty when the entity has none.
- `identifiers` (array): Present when type=entity and the entity has external identifiers: the strong/weak identifiers (type + value, e.g. {type:"hrb",value:"HRB 12345 B"}) that anchor it for the resolver. Omitted when none.
- `insight` (object): Present when type=insight. A synthesized reflection: statement, insightType, currentConfidence, state (hot|demoted), synthesizedAt, and sourceUids typed as entities/{uid} citations.
- `provenance` (array): Source/confidence/decided-at lineage walked back to the record's origin episode(s): direct for an episode, via source_event_uid for a relation, aggregated across citing episodes for an entity. Empty…
- `relation` (object): Present when type=relation. Carries subject/object resource names for onward traversal, plus a 'status' trust posture: 'asserted' (authoritative) or 'proposed' (extracted from an untrusted connector…
- `shadowedDocuments` (array): Present when the name argument was resolved by display name AND one or more document entities share that name but were passed over in favour of a higher-priority non-document entity. Each entry gives…
- `type` (string): Which record kind this response carries.

### `getBacklinks` (~109 tokens)

List a document's wikilink edges without its body: `backlinks` (notes/entities that reference it) and `links` (what it references). The fast way to map a note's place in the graph. Identify the document by `path` or `documentUid`.

Input parameters:

- `documentUid` (string): Stable document UID. Provide this OR path (exactly one).
- `path` (string): Vault-relative path of the document. Provide this OR documentUid (exactly one).

Output parameters:

- `backlinks` (array): Incoming edges — documents/entities that reference this one. Always present; [] when none.
- `links` (array): Outgoing edges — what this document references. Always present; [] when none.

### `getCapacity` (~37 tokens)

Return per-storage-layer on-disk byte totals: event log, sqlite+fts, graph, vector index, embedding cache, plus free disk.

Output parameters:

- `onDisk` (object)

### `getContext` (~206 tokens)

Assemble a token-budgeted markdown brief of the user's working set — pinned documents, recently-updated documents, key entities, and (when you pass a `query`) the most relevant hits — for loading at the start of a session. Deterministic and fast (no LLM). The brief never exceeds `budgetTokens`; lower-priority content is dropped behind a visible marker. Prefer this over many small reads when you need to warm up context about the user.

Input parameters:

- `budgetTokens` (integer): Token budget for the brief (default 4000). The brief never exceeds it; the lowest-priority tail is dropped behind a marker.
- `query` (string): Optional focus. When set, the brief adds a section of the most query-relevant hits (documents + episodes), co-ranked by the same recall used by recall/ask.
- `scopes` (array): Scopes to read from: project, personal, team. Defaults to project+personal. Unknown values are rejected.

Output parameters:

- `budget` (integer): The token budget that was applied.
- `dropped` (integer): How many lower-priority items were omitted to fit the budget.
- `markdown` (string): The assembled context brief as markdown: pinned documents, recently-updated documents, key entities, and (when a query is given) relevant hits. Load this verbatim at session start.
- `sections` (array): Per-section counts of what made it into the brief.
- `tokens` (integer): Estimated token count of the brief (always <= budget).

### `getEntityContext` (~454 tokens)

Assemble a single token-budgeted markdown dossier for ONE entity, collapsing what otherwise takes five calls (get + relations + neighbors + recall + facts) into one card. The dossier carries a header (name, kind, entities/{uid}, resolution status), Connections (the entity's typed relations as direction-aware predicate fragments), What I know (its scalar facts), Mentioned in (recall hits naming it), and a counts footer. Pass `entity` as an entities/{uid} resource name, a bare uid, a display name (resolved case-insensitively), or `self`. Use `at` (RFC 3339) to time-slice the relations + facts to a past instant; the header is unchanged. The card never exceeds `tokenBudget`; lower-priority bullets are dropped behind a marker. An unknown entity returns E_NOT_FOUND; an ambiguous display name returns E_VALIDATION naming the candidates. Prefer this over many small reads when you need the full picture of one entity.

Input parameters:

- `at` (string): Valid-time anchor (RFC 3339 UTC): return the Connections and What-I-know rows as they were live at this instant. Defaults to now. Per ADR-0001 the anchor binds the relation + fact sub-queries only; t…
- `entity` (string, required): Which entity to profile: an entities/{uid} resource name (feed one straight from a recall hit's entities[] / name), a bare uid, a human display name (resolved case-insensitively), or 'self'. An ambig…
- `include` (array): Optional section filter. Omit for all three sections; pass a subset (e.g. ["facts"]) to narrow the card. The entity header and footer always render.
- `scopes` (array): Scopes to read from: project, personal, team. Defaults to project+personal. Unknown values are rejected.
- `tokenBudget` (integer): Token budget for the dossier (default 4000). The card never exceeds it; the lowest-priority bullets are dropped behind a marker, then a counts footer is appended.

Output parameters:

- `budget` (integer): The token budget that was applied.
- `dropped` (integer): How many lower-priority bullets were omitted to fit the budget.
- `markdown` (string): The entity dossier as markdown: a header (name · kind · entities/{uid} · resolution), Connections (typed relations as direction-aware predicate fragments), What I know (the entity's scalar facts), Me…
- `sections` (array): Per-section counts of what made it into the dossier.
- `tokens` (integer): Estimated token count of the dossier (always <= budget).

### `getSourceRef` (~167 tokens)

Fetch one external-source provenance record (SourceRef) by AIP resource name sourceRefs/{uid}. A SourceRef records what a memory was derived from — the connector, stream, external id/url, content hash, and fetch time (W3C PROV wasDerivedFrom). Its provenance is SELF-ASSERTED and UNVERIFIED (trustState is always "unverified"): there is no cryptographic attestation of the external claim yet. Use recordSourceRef to create one.

Input parameters:

- `name` (string, required): AIP resource name to fetch: sourceRefs/{uid}. The uid is the SourceRef ULID returned by recordSourceRef.
- `scopes` (array): Scopes to read from: project, personal, team. Defaults to project+personal. Unknown values are rejected.

Output parameters:

- `connector` (string): Connector that produced the reference, e.g. gmail, slack. Omitted when absent.
- `contentHash` (string): Appender-supplied SHA-256 hex of the fetched content. Omitted when absent.
- `createTime` (string): When the SourceRef event was recorded (RFC 3339 UTC).
- `dedupKey` (string): Idempotency key for the source_refs projection.
- `externalId` (string): Source-system opaque identifier for the item. Omitted when absent.
- `externalUrl` (string): Dereferenceable location of the source item. Omitted when absent.
- `fetchTime` (string): When the source content was fetched (RFC 3339 UTC).
- `name` (string): AIP resource name of this SourceRef.
- `scope` (string): Scope this SourceRef lives in: personal, project, or team.
- `sourceEventUid` (string): UID of the sourceRefRecorded event that carried this record.
- `sourceType` (string): Kind of external source (open taxonomy), e.g. email, slack_message, web_page.
- `stream` (string): Sub-channel within the connector. Omitted when absent.
- `subjectUid` (string): ULID of the record this source was derived from (PROV wasDerivedFrom).
- `trustState` (string): Provenance is self-asserted by the appending device and is NOT cryptographically verified. No principal/signature trust exists for a SourceRef yet (#207).
- `uid` (string): SourceRef ULID identity.

### `getUsageSnapshot` (~49 tokens)

Return the current usage snapshot: events written, recalls performed, embed-cache hit rate, recall latency percentiles.

Input parameters:

- `window` (string): Aggregation window. Defaults to "today" when omitted.

Output parameters:

- `bytesIngested` (integer)
- `embedCacheHitRate` (number)
- `embedCacheHits` (integer)
- `embedCacheMisses` (integer)
- `events` (integer)
- `recallLatencyP50Ms` (number)
- `recallLatencyP95Ms` (number)
- `recallLatencyP99Ms` (number)
- `recalls` (integer)
- `window` (string)

### `idleStatus` (~53 tokens)

Return current background-work counters: extraction queue depth, in-flight enrichment, in-flight embeddings. Use to poll whether the kernel has caught up after a burst of remember() calls, before calling recall() for a clean retrieval measurement.

Output parameters:

- `enrichmentInflight` (integer): Total background enrichment work in flight (queued + running). When 0 the extraction/resolution side is quiescent.
- `enrichmentQueueDepth` (integer): Jobs sitting in the bounded extraction worker pool, not yet picked up by a worker.
- `isIdle` (boolean): True when every background lane reports 0 in-flight work.
- `vecInflight` (integer): Background embedding+vec0-upsert goroutines currently running. When 0 the vector side is quiescent.

### `ingestSource` (~309 tokens)

Ingest the delta from a previously connected source, resuming from its saved watermark (cheap incremental pull). Give a connector (local-git, claude-chats, or granola) and, for the file-based ones, repoPath (granola uses a stored API key instead); optionally scope (personal|team), dryRun, maxItems, and maxExtractionCalls. Idempotent: unchanged items are skipped by content hash. Returns a run report with a jobUid. Refuses project scope.

Input parameters:

- `connector` (string, required): Source connector name. Supported: local-git, claude-chats, granola.
- `dryRun` (boolean): Preview only: validate the source and count would-be items WITHOUT writing anything (the consent gate).
- `maxExtractionCalls` (integer): Cap on episodes appended this run, each one downstream extraction. Defaults to 500.
- `maxItems` (integer): Cap on items considered this run (backfill bound). Defaults to 500.
- `repoPath` (string): Source locator for file-based connectors: a local git repository path (local-git), or an export file path (claude-chats: a .zip bundle or a conversations.json). Omit for granola, which authenticates…
- `scope` (string): Scope to ingest into. Defaults to personal. Project scope is refused: ingested external content is sensitive and project is plaintext.

Output parameters:

- `connector` (string)
- `dryRun` (boolean)
- `episodesAppended` (integer)
- `itemsApplied` (integer): Items turned into episodes (or, in a dry run, that WOULD be).
- `itemsSkipped` (integer): Items skipped because their content hash was unchanged (idempotent re-ingest).
- `jobUid` (string): Job UID; pass to listJobs / cancelJob to track or cancel a long backfill.
- `streams` (array)

### `introspect` (~189 tokens)

Return the ontology you navigate by: the registered relation predicates (edge vocabulary, with functional/non-functional semantics), the entity kinds present (node vocabulary with live counts, plus taxonomy metadata — displayName, description, provenance namespace, extends/parent hierarchy, sameAs external URIs — for kinds in the registry), a namespaces summary (core/user/extension:<name> buckets), and the event types recorded. Custom kinds added at runtime appear under the user or extension:<name> provenance namespace. Call once to learn the vocabulary before filtering neighbors/relations/entities. NOTE: this lists what has been registered or extracted, so it can include rarely- or never-populated predicates — e.g. a role/title is usually an attributes.role string on a works_at/founded edge rather than a populated has_role edge — so confirm a predicate is actually in use (via relations) before relying on it. LLM-free.

Output parameters:

- `entityKinds` (array): Entity kinds present in the store or registered in the taxonomy, with live counts and optional taxonomy metadata. Includes registered-but-zero-count kinds (e.g. core kinds not yet ingested). Custom k…
- `eventTypes` (array): Registered event-schema kinds the store records. Empty when no ontology registry is wired.
- `namespaces` (array): Provenance namespaces aggregated from the registered entity kinds: core (built-in), user (manually added), extension:<name> (plugin-contributed). Predicate provenance joins in a later phase.
- `predicates` (array): Registered relation predicates — the edge vocabulary for neighbors/relations filtering.
- `views` (array): Curated read-only views for the query tool — the stable SQL schema. Query these names, not the physical tables.

### `lintDocuments` (~130 tokens)

Run a read-only health check over the markdown vault: `orphans` (no graph edges), `deadLinks` (unresolved [[wikilinks]]), `contradictions` (contradicts edges), `danglingEdges` (live link to a deleted doc), `nearDuplicates` (pairs of docs whose chunk embeddings exceed cosine 0.92, non-blocking lint signal only), and `nameCollisions` (documents whose title shadows a non-document entity of the same display name — wikilink resolution silently favours the entity). Takes no arguments; scans the whole vault.

Output parameters:

- `contradictions` (array): Document pairs joined by an active contradicts edge.
- `danglingEdges` (array): Active links from a live document to a DELETED document (a backlink left behind when deleteDocument tombstoned its target). Each entry carries the live 'subjectPath' and the deleted 'targetPath'/'tar…
- `deadLinks` (array): Documents that still carry an unresolved [[wikilink]]; each entry's 'unresolved' array names the exact target strings that resolve to nothing.
- `nameCollisions` (array): Documents whose title (display name) shadows a non-document entity with the exact same name. Wikilink resolution silently favours the non-document entity in this case; use the vault path or a kind qu…
- `nearDuplicates` (array): Pairs of live documents whose chunk embeddings are suspiciously similar (cosine >= 0.92). Non-blocking lint signal only — does NOT prevent writes or imports. Each pair is reported once.
- `orphans` (array): Documents with no inbound or outbound graph edges (isolated notes).

### `listDocuments` (~123 tokens)

List vault documents, most-recently-updated first, filtered by `pathPrefix`, `tag`, and/or `pinned`. Bodies are omitted to stay lightweight; call readDocument for one document's full body and edges.

Input parameters:

- `limit` (integer): Max documents to return (default 50).
- `pathPrefix` (string): Restrict to documents whose path starts with this prefix, e.g. notes/.
- `pinned` (boolean): When true, only pinned documents.
- `tag` (string): Restrict to documents whose frontmatter tags include this value.

Output parameters:

- `documents` (array): Matching documents, most-recently-updated first. Bodies are omitted; call readDocument for the full body.

### `listDupCandidates` (~71 tokens)

List entity pairs the post-consume dedup sweep flagged as likely cross-device duplicates. Auto-merged strong-identifier pairs (same email / username) are not included — only advisory candidates that need human review. Call mergeEntities on a pair to consolidate; use maz review dups for the interactive CLI view.

Output parameters:

- `candidates` (array): Entity pairs flagged by the post-consume dedup sweep after cross-device sync. Auto-merged strong-identifier pairs (email, username) do not appear here — they are already consolidated.
- `total` (integer): Total candidate count (may exceed the 200-row page).

### `listInsights` (~213 tokens)

List synthesized insights by lifecycle state - the Insight Inbox read surface. Defaults to the hot (live, recall-visible) insights, most-confident first, each carrying its statement, confidence, lifecycle state, and the entity source records it cites (as entities/{uid} citations plus a count). Pass state=demoted, state=dismissed, or state=all to review the rest. Pair with get(insights/{uid}) to inspect one, dismissInsight to dismiss one, and triggerReflection to synthesize more.

Input parameters:

- `limit` (integer): Maximum insights to return. Defaults to 50.
- `scopes` (array): Scopes to read from: project, personal, team. Defaults to project+personal. Unknown values are rejected.
- `state` (string): Which insights to list. "hot" (default) = live, recall-visible insights. "demoted" = decayed out of hot recall. "dismissed" = explicitly dismissed by the user (terminal). "all" = every state.

Output parameters:

- `insights` (array): Insights in the requested state, most-confident first.

### `listJobs` (~41 tokens)

Return the currently-tracked background jobs (running and recent history), optionally filtered by state.

Input parameters:

- `stateFilter` (string): Optional filter; absent returns every job.

Output parameters:

- `jobs` (array)

### `listPresets` (~53 tokens)

List the presets currently active (steering this agent's operating instructions). Returns name, version, and description for each — read-only. Presets are installed via the `maz preset install` CLI, not by agents.

Output parameters:

- `presets` (array): Active presets, ordered by name.

### `listSources` (~51 tokens)

List the external source connectors available to ingest from (e.g. local-git) and the resume watermark of each source connected so far (connector, stream, items ingested, last advance time). Read-only.

Output parameters:

- `connectors` (array): Registered connector names available to connectSource/ingestSource.
- `watermarks` (array): Per-(connector, stream) resume state for sources connected so far, with a derived freshness status.

### `mergeEntities` (~117 tokens)

Merge source entity into target: repoints all relations, facts, and mentions from source to target, then removes the source entity row. Append-only: an entityMerged event is written to the audit log. Returns E_VALIDATION when source and target are the same uid, E_NOT_FOUND when either uid is unknown.

Input parameters:

- `sourceEntityUid` (string, required): ULID of the entity to absorb (will be deleted). Required.
- `targetEntityUid` (string, required): ULID of the surviving entity. Must differ from sourceEntityUid. Required.

Output parameters:

- `name` (string): AIP resource name of the surviving (target) entity.
- `status` (string): Always "merged" on success.

### `neighbors` (~358 tokens)

Expand an entity's neighborhood up to 6 hops and return the hydrated neighbor entities plus the relations connecting them, each hop tagged direction=outgoing|incoming relative to the seed. LLM-free graph walk: pass a recall anchor as the seed and traverse the structure yourself. The DEFAULT view is point-in-time NOW: only edges valid now and not superseded; pass includeInactive=true for full history, or time-travel with at/asOf. Filter edges by predicate. The `limit` (default 50) caps total neighbors across all hops, so deep walks stay bounded.

Input parameters:

- `asOf` (string): Transaction-time anchor (RFC 3339 UTC). Ignore edges recorded after this instant.
- `at` (string): Valid-time anchor (RFC 3339 UTC). Walk edges live at this instant. Defaults to now.
- `depth` (integer): Hops to expand. 1 = direct neighbors, 2 = neighbors-of-neighbors, up to 6. Defaults to 1.
- `includeInactive` (boolean): Follow superseded / expired edges too. Off by default.
- `limit` (integer): Maximum neighbors to return across all hops. Defaults to 50.
- `predicates` (array): Only follow edges with these predicates. Omit to follow any.
- `scopes` (array): Scopes to read from: project, personal, team. Defaults to project+personal. Unknown values are rejected.
- `seed` (string, required): Entity to expand from: an entities/{uid} ref (as returned in a recall hit's entities[]), a bare uid, or a display name (resolved case-insensitively; an unknown or ambiguous name returns an error nami…

Output parameters:

- `neighbors` (array)

### `promoteRelation` (~167 tokens)

Promote a proposed relation to asserted: verify an untrusted connector-ingested fact and lift its trust quarantine. A fact extracted from an external source (Granola transcript, Claude-chats export) lands status=proposed — it surfaces in recall but is excluded from supersession until confirmed. Promoting it asserts it, so it then behaves like any user-authored edge. Idempotent: promoting an already-asserted edge is a no-op.

Input parameters:

- `name` (string, required): Resource name of the Relation to promote: relations/{uid}, as returned by recall/get/relations/neighbors.
- `scope` (string): Scope the promotion event is written into. Defaults to personal. Use the scope the relation lives in; promoting a team-scope edge requires admitted team membership, else E_PERMISSION.

Output parameters:

- `name` (string): Resource name of the promoted Relation.
- `promoted` (boolean): true when this call flipped a proposed edge to asserted; false when the edge was already asserted (idempotent no-op, no event appended).
- `status` (string): Always "asserted" on success — the relation now carries the authoritative trust posture.

### `query` (~276 tokens)

Run a read-only SQL SELECT over the curated views (v_entities, v_relations, v_facts) — the analytical escape hatch for questions the structured entities/count filters can't express. Single statement, SELECT/WITH only; writes are rejected and impossible. Run introspect for the view columns. Scope: results default to project+personal; pass scopes:["team"] to include team rows. The views are pre-filtered to the requested scopes before your SQL runs — no WHERE scope = … clause needed.

Input parameters:

- `limit` (integer): Maximum rows to return. Defaults to 200, hard-capped at 1000. Rows beyond the cap set truncated=true.
- `scopes` (array): Scope filter: which data partitions to include. Allowed values: project, personal, team. Defaults to [project, personal] when omitted — team rows are hidden unless you pass scopes:["team"]. The curat…
- `sql` (string, required): A single read-only SELECT (or WITH … SELECT) statement. Query the curated views: v_entities, v_relations, v_facts (run introspect for their columns). Writes, multiple statements, PRAGMA/ATTACH and DD…

Output parameters:

- `columns` (array): Result column names in order.
- `rowCount` (integer): Number of rows returned (after the limit cap).
- `rows` (array): Result rows as column→value objects.
- `truncated` (boolean): True when more rows matched than the limit returned.

### `readDocument` (~208 tokens)

Read one vault document by `path` or `documentUid`: its title, verbatim body, frontmatter, tags, its wikilink edges — `links` (what it references: mentions of entities, links_to other docs) and `backlinks` (what references it) — and `observations` (the `- [category] text` lines parsed from the body). Each observation is projected to a durable fact ON THIS DOCUMENT ENTITY, so query them with get(entities/{documentUid}) or `maz fact get <documentUid>`. A document is also an entity, so the returned `document.name` (entities/{uid}) pivots straight into neighbors/relations to walk the unified graph.

Input parameters:

- `documentUid` (string): Stable document UID (= its entity UID). Provide this OR path (exactly one).
- `path` (string): Vault-relative path of the document, e.g. notes/project-alpha.md. Provide this OR documentUid (exactly one).

Output parameters:

- `backlinks` (array): Incoming edges (what references this note). Always present; [] when none.
- `document` (object): The document: path, title, verbatim body, frontmatter, tags, pinned. The name field is the entities/{uid} resource name for pivoting to neighbors/relations.
- `links` (array): Outgoing wikilink edges (what this note references): mentions → entities, links_to → other documents. Always present; [] when none.
- `observations` (array): Structured observations parsed from the body ('- [category] text #tag (context)' lines). EACH is projected to a durable fact on THIS document entity — query them with get(entities/{documentUid}) or '…
- `unresolvedLinks` (array): Target names of [[wikilinks]] in the body that resolve to nothing in the graph (dead links). These have NO edge in 'links' precisely because they did not resolve; surface them to flag a broken refere…

### `recall` (~1236 tokens)

Search long-term memory for facts matching a query — about the user OR any person, place, organization, or event in the graph. CALL THIS before answering anything that depends on stored context (names, roles, projects, history, people, places, decisions); Mazarbul persists across sessions and clients, your conversation context does not. It is a SEED-FINDER: ground answers on each hit's `text` and `entities[]` — feed an entity name into get/neighbors/relations for the authoritative structured facts — not on the raw rank. `fusedScore` is a blended relevance score where rank ORDER is the signal, not absolute magnitude (normalized to [0,1] under the default weighted fusion, or a small reciprocal-rank value under rrf), so read each hit's `text` rather than blindly taking hit[0]. A miss is cheap; a confident hallucination is not. Recall ALWAYS returns its best-ranked candidates even when nothing strongly matches, so low, flat scores across unrelated hits usually mean the subject is NOT in memory — confirm a name with `entities`/`get` before treating a hit as fact, and use `relations`/`neighbors` (not recall) for the AUTHORITATIVE list of what mentions or links a known entity. Pass `compact: true`, `maxTokens`, or both to shrink the returned payload when you only need seeds to traverse. Returns ranked hits with citable ULIDs.

Input parameters:

- `anchor` (string): Entity ULID. When set, restricts candidates to episodes that cite entities within anchorDepth hops of this anchor (default depth 1, max 6). Useful when you already hold an entity anchor and want topi…
- `anchorDepth` (integer): Hop radius around the anchor entity (1..6). Defaults to 1 (immediate neighbourhood). Ignored when anchor is absent.
- `asOf` (string): RFC 3339 UTC instant. Hides any event whose transaction time is later than this anchor, answering 'what did we know on X?'. Bi-temporal transaction-time anchor. Optional.
- `asOfEventUID` (string): Event ULID. Resolves to that event's createTime and uses it as the asOf anchor. Mutually exclusive with 'asOf' and 'at'; supply at most one of the three.
- `at` (string): RFC 3339 UTC instant to scope the query. Bi-temporal valid-time anchor. Optional.
- `compact` (boolean): Return a reduced per-hit envelope — {name, kind, snippet, fusedScore, entities} plus source for document hits — dropping the full text, the per-lane score components, confidence, citations, and creat…
- `currentDate` (string): Caller's "now" anchor (RFC 3339 UTC or YYYY-MM-DD) the rewriter uses to resolve relative phrases like "two months ago" into an absolute timestamp. Folded into 'at' only when 'at' is unset. Requires r…
- `embedDeadlineMs` (integer): Per-call deadline in milliseconds for the query-embedding step. When the embedder does not respond within this window the call degrades to FTS-only and surfaces MAZ-RECALL-001. Absent means no per-ca…
- `explain` (boolean): When true, attach per-lane candidate counts and total wall-clock time under the optional '_meta' key in the response. Off by default.
- `hasPrimarySource` (boolean): Keep only hits grounded in a primary source: the hit (or one of its entity anchors) is the subject of a recorded source_ref or of an active has_primary_source edge (#207/#208). Filtered AFTER ranking…
- `kinds` (array): Restrict hits to these record kinds (set semantics). Omit for the default mix of episodes + vault documents. Pass ["documentRevision"] for document-only recall, ["episode"] to exclude documents.
- `limit` (integer): Maximum number of hits to return. Defaults to 10.
- `maxTokens` (integer): Soft cap on the total estimated tokens across all returned hits (estimated at ~4 bytes/token). Hits are dropped lowest-fusedScore-first until the payload fits; the single top-ranked hit is always ret…
- `minConfidence` (number): Filter out hits whose writer confidence is below this value. Defaults to 0.
- `near` (object): Restrict results to episodes citing entities within a geographic radius of a named place. Both fields required when present.
- `noGraph` (boolean): Skip the graph traversal lane when true (vector + FTS only). Off by default.
- `query` (string, required): Natural-language question or keyword phrase to search the event log for.
- `rewriteQuery` (boolean): Expand the query via the query-rewrite-v1 LLM prompt before retrieval. Off by default.
- `scopes` (array): Scopes to read from: project, personal, team. Defaults to project+personal. Unknown values are rejected.
- `source` (array): Constrain hits to these origin sources (set semantics, OR across values). Matches a hit's eventSource — the originating event's source tag, e.g. "remember", "extractor/v1", or a connector tag like "c…

Output parameters:

- `degraded` (boolean): True when recall ran BELOW full hybrid because the embedder was unavailable: FTS(+graph)-only, no dense-vector lane. Semantic matching did not run, so treat low, flat scores cautiously and lean on th…
- `hits` (array): Ranked hits. In compact mode (compact=true) each hit carries only {name, kind, snippet, fusedScore, entities} plus source for document hits; the verbose-only fields (hitType, text, confidence, compon…
- `lanes` (array): The retrieval lanes that actually contributed to this result: ["vector","fts","graph","nameEmb"] for full hybrid, or a subset like ["fts"] when degraded. Always present.

### `recordSourceRef` (~363 tokens)

Record where a memory came from: append an external-source provenance reference (SourceRef, W3C PROV wasDerivedFrom) for a subject record. Give the subject's uid, a sourceType (open taxonomy), and a dedupKey for idempotency; optionally a connector, stream, externalId, externalUrl, and contentHash. Returns the new SourceRef's resource name. Provenance is self-asserted and UNVERIFIED. Does not fetch the source.

Input parameters:

- `connector` (string): Connector that produced the reference, e.g. gmail, slack. Optional.
- `contentHash` (string): SHA-256 hex of the fetched content. Optional; supplied by the caller (fetch is out of scope).
- `dedupKey` (string, required): Idempotency key. Recording the same external item twice (same dedupKey) collapses to one SourceRef. Required.
- `externalId` (string): Source-system opaque identifier for the item, e.g. a message id. Optional.
- `externalUrl` (string): Dereferenceable location of the source item. Optional.
- `fetchTime` (string): When the source content was fetched (RFC 3339 UTC). Defaults to now.
- `scope` (string): Scope to record into. Defaults to personal.
- `sourceType` (string, required): Kind of external source, drawn from an open taxonomy: email, slack_message, notion_page, web_page, etc. Required.
- `stream` (string): Sub-channel within the connector, e.g. a mailbox label or channel id. Optional.
- `subjectUid` (string, required): ULID of the record this source was derived from (PROV wasDerivedFrom): an entity, episode, or fact uid. Required.

Output parameters:

- `name` (string): AIP resource name of the recorded SourceRef; pass to getSourceRef.
- `status` (string): Always "recorded" on success.

### `reference` (~296 tokens)

Mint-or-return an external_ref entity for a URI: a first-class local pointer to information held in another system (a GitHub issue, a Drive doc, a Granola transcript, a repo file). Returns the external_ref's resource name so you can relate(localEntity, has_primary_source, name) to record that a local record is grounded in it. Idempotent: the same URI in the same scope returns the same entity. Defaults to personal scope (local, not rendered to the shared project git tree); pass scope=project to share. Mazarbul does NOT fetch the URI — to make the referenced content searchable, fetch it with your own tools, remember() it, then reference() the URI and relate the two. Returns E_VALIDATION on a URI with no scheme or an unknown scope.

Input parameters:

- `label` (string): Optional human-readable display name. Defaults to the URI. Keep third-party identity metadata out of project (git-rendered) scope.
- `scope` (string): Scope to write into. Defaults to personal (local, not rendered to the shared project git tree). Pass "project" to share. Dedup is WITHIN scope: the same URI in two scopes yields two entities.
- `uri` (string, required): Absolute URI of the external thing, e.g. https://github.com/org/repo/issues/42, mailto:a@b.com, or a connector URL. Must carry a scheme.

Output parameters:

- `created` (boolean): true if a new external_ref was minted, false if an existing one was returned (mint-or-return).
- `name` (string): AIP resource name of the external_ref; pass to relate(localEntity, has_primary_source, name) or get(name).
- `scope` (string): The resolved scope the external_ref lives in.
- `uid` (string): Bare ULID of the external_ref, derived deterministically from (scope, uri).
- `uri` (string): The canonical URI, echoed back.

### `relate` (~415 tokens)

Assert a structured (subject, predicate, object) relation directly. You are the extractor: when you see a relationship, record it here rather than relying on server-side extraction (off unless a chat model is configured). Pass source for provenance. Returns the new relation's resource name.

Input parameters:

- `attributes` (object): Qualifying key/value pairs, e.g. {"role":"CEO"}. Optional.
- `confidence` (number): Writer confidence in the edge. Defaults to 1.0.
- `object` (string, required): Object entity: entities/{uid}, a bare ULID, the literal "self", or a human display name (resolved case-insensitively, like maz get).
- `predicate` (string, required): Relationship type, e.g. works_at, reports_to, knows. See introspect for registered predicates.
- `provenance` (string): Free-text rationale for the edge. Optional.
- `scope` (string): Scope to write into — pick by who should see it. Defaults to personal (private to the user, syncs to their devices). Use "project" for edges tied to the current repo/codebase (shared with anyone who…
- `source` (string): Provenance label for the appended event, e.g. "cli:relate". Optional; defaults to "mcp:relate".
- `subject` (string, required): Subject entity: entities/{uid}, a bare ULID, the literal "self", or a human display name (resolved case-insensitively, like maz get).
- `validFrom` (string): Inclusive lower bound of the relation's valid-time (RFC 3339 UTC). Optional.
- `validUntil` (string): Exclusive upper bound of the relation's valid-time (RFC 3339 UTC). Optional.

Output parameters:

- `name` (string): AIP resource name of the asserted Relation; pass to get(relations/{uid}).
- `status` (string): Always "asserted" on success.

### `relateBatch` (~122 tokens)

Assert multiple (subject, predicate, object) relations in ONE atomic event-log append. All edges are validated before any write: a single invalid endpoint aborts the entire batch. Use this to seed a graph in one call instead of N sequential relate() calls. Returns the new relation resource names in input order.

Input parameters:

- `relations` (array, required): Array of relation assertions to append atomically.
- `source` (string): Provenance label for all appended events, e.g. "mcp:relateBatch". Optional; defaults to "mcp:relateBatch".

Output parameters:

- `count` (integer): Number of relations appended.
- `names` (array): AIP resource names of the asserted Relations, in input order.
- `status` (string): Always "asserted" on success.

### `relations` (~353 tokens)

List an entity's typed relations (incoming + outgoing), each row tagged direction=outgoing|incoming relative to the queried entity so directional predicates (reports_to, manages) read unambiguously. Endpoint names are hydrated. LLM-free. The DEFAULT view is point-in-time NOW: only edges valid now and not superseded; pass includeInactive=true for expired/superseded history, or time-travel with at/asOf. Filter by predicate. Role/title is often carried as an attributes.role string on a works_at/founded edge rather than a separate has_role edge; each row's sourceEventUid links back to the originating episode. Pair with neighbors to walk further.

Input parameters:

- `asOf` (string): Transaction-time anchor (RFC 3339 UTC). Hides relations recorded after this instant.
- `at` (string): Valid-time anchor (RFC 3339 UTC). Returns relations live at this instant. Defaults to now.
- `entity` (string, required): Entity whose relations to list: an entities/{uid} ref (as returned in a recall hit's entities[]), a bare uid, or a display name (resolved case-insensitively; an unknown or ambiguous name returns an e…
- `includeInactive` (boolean): Return superseded / expired relations too (full history). Off by default.
- `limit` (integer): Maximum relations to return. Defaults to 50.
- `predicates` (array): Restrict to these predicates, e.g. ["works_at","reports_to"]. Omit to return all.
- `scopes` (array): Scopes to read from: project, personal, team. Defaults to project+personal. Unknown values are rejected.

Output parameters:

- `relations` (array)

### `remember` (~452 tokens)

Persist a fact to the user's long-term memory. CALL THIS whenever the user tells you something about themselves, their work, their preferences, decisions, plans, or corrects a prior fact, even if you think you'll remember it within the current conversation. Mazarbul is the durable store; your own context is not. Pass the original sentence verbatim. remember stores and indexes the text for recall; entities and relations are extracted server-side only when a chat model is configured (usually not), so write the structure you see yourself with createEntity, relate, and fact.

Input parameters:

- `actor` (string): Who said it. "user" (the human), "assistant" (the AI agent), or "system". Defaults to "user" when omitted. Assistant-actor episodes are routed through a different extractor branch so AI-generated fac…
- `confidence` (number): Writer's confidence in the Episode. Range 0..1 inclusive. Defaults to 1.0 when omitted.
- `coordinates` (object): Optional geographic coordinates (WGS84) associated with this Episode. When present, the resolver forwards them onto any matched or created place Entity.
- `coordinatesProvenance` (string): Origin of the coordinates, e.g. user_provided or geocoded:nominatim. Required when coordinates is set; rejected when coordinates is absent.
- `scope` (string): Sync / encryption boundary for the Episode. "personal" (default) is local-only; "project" is in-repo; "team" is shared under the team key; "draft" is private staging — invisible to recall until promo…
- `source` (string): Optional. Where the Episode originated (free-form, e.g. "cli:remember", "providers/anthropic"). Defaults to "mcp:remember" when omitted — just call remember({text}) for an ordinary fact.
- `text` (string, required): Natural-language record to remember. Stored verbatim as the Episode text.

Output parameters:

- `name` (string): AIP resource name of the created Episode: "episodes/{uid}".

### `retractFact` (~291 tokens)

Close the current value of a fact slot — the scalar counterpart to unrelate. Records that a fact STOPPED being true (a status that no longer holds, a preference that changed with no replacement) by retracting it: it drops out of current reads but its history is PRESERVED (unlike forget, which deletes the row). The slot then reads as EMPTY — retraction does NOT fall back to an earlier value. Identify the value by its facts/{uid} or by the (subject, stateKey) pair (which closes the slot's single open value). To set a different value instead, just write it with fact() — a later write supersedes the prior.

Input parameters:

- `fact` (string): The fact to retract: facts/{uid} or a bare ULID (as returned by fact() or get). Use THIS or a (subject, stateKey) pair, not both.
- `scopes` (array): Scopes to search when identifying the slot by (subject, stateKey): project, personal, team. Defaults to project+personal.
- `stateKey` (string): Slot address to close, e.g. state.task.status, preference.coffee.milk. Provide with subject.
- `subject` (string): Subject entity whose slot to close: entities/{uid}, a bare ULID, "self", or a display name. Provide with stateKey as an alternative to fact.

Output parameters:

- `name` (string): Resource name of the retracted fact.
- `retractedAt` (string): The instant the fact was retracted (RFC 3339 UTC).
- `status` (string): Always "retracted" on success: the value is closed but its history is preserved.

### `shareToTeam` (~287 tokens)

Re-publish a personal-scope record (entity, fact, or relation) to the team scope so all admitted team members can read it. The personal original is unchanged — both copies coexist as separate scope lenses. Requires admitted team membership (else E_PERMISSION). Entities are deduplicated against the team scope by exact name before minting; an existing match returns status=reused. Facts and relations remap their endpoint UIDs to the team-scope counterparts: for a bare fact/relation share, all referenced entities must already be in team scope (else E_VALIDATION); for a subtree share (withFacts/withRelations), relations whose object has no team counterpart are skipped (status=skipped, reason set) rather than appended with dangling references. Use withFacts/withRelations to share an entity subtree in one call.

Input parameters:

- `ref` (string, required): The record to share. Accepts an AIP resource name (entities/{uid}, facts/{uid}, relations/{uid}) or a bare ULID (treated as entity). Entity names are resolved case-insensitively when not a uid.
- `withFacts` (boolean): Also share the entity's currently-open facts. Only meaningful when ref resolves to an entity. Defaults to false.
- `withRelations` (boolean): Also share the entity's active relations. Only meaningful when ref resolves to an entity. Defaults to false.

Output parameters:

- `shared` (array): Per-item share outcomes.
- `status` (string): Always "shared" on success.

### `team_create` (~86 tokens)

Create the team scope (genesis): mints a fresh per-team data-encryption key, admits this device's principal as the genesis owner, and switches the team scope to encrypted envelope mode. Fails if a team already exists.

Input parameters:

- `remote` (string): Optional git remote URL to pin for the team scope (e.g. git@github.com:org/team-brain.git).

Output parameters:

- `dekEpoch` (integer): Epoch of the freshly-minted data-encryption key (always 0 at genesis).
- `genesisPrincipalUid` (string): Principal UID admitted as the team's genesis owner (this device's bound principal).
- `status` (string): Always "created" on success.

### `team_device_list` (~51 tokens)

List all devices enrolled under the local principal's identity, including revocation status. Each device entry shows its signing-key fingerprint (sigKeyId), optional label, create time, and whether it has been revoked.

Output parameters:

- `devices` (array)

### `team_device_revoke` (~117 tokens)

Revoke an enrolled device by its ULID. Appends a deviceRevoked event to the team scope. After revocation, any event authored by this device with a UID greater than the revocation event's UID is dropped on all peers (convergent fold). The revoker must be the device's own principal, or a team admin/owner.

Input parameters:

- `deviceId` (string, required): ULID of the device to revoke. Required.
- `reason` (string): Optional free-form reason for the revocation (audit trail).

Output parameters:

- `deviceId` (string): ULID of the revoked device.
- `eventUid` (string): UID of the appended deviceRevoked event.
- `principalUid` (string): ULID of the device's owning principal.
- `status` (string): Always "revoked" on success.

### `team_grant` (~168 tokens)

Admit a principal to the team by wrapping the current team data-encryption key to their pasted x25519 public key. The grantee unwraps it on their next sync pull. Requires the caller to hold at least admin role. The optional role parameter sets the grantee's access level (default: member).

Input parameters:

- `joinKey` (string, required): The grantee's join-key blob (the opaque token from team_key / 'maz team key'). It carries their principal UID, wrap public key, and signing identity — this is the trust input; obtain it out of band.
- `label` (string): Optional human label for local logging; never serialized into the synced recipients.jsonl.
- `role` (string): Access role to assign the grantee. Defaults to "member" when absent.

Output parameters:

- `dekEpoch` (integer): Highest (current) DEK epoch wrapped to the grantee.
- `grantedEpochs` (array): Every DEK epoch wrapped to the grantee (ascending). A full-history grant covers 0..dekEpoch; it is a subset only when this device lacks a prior epoch's DEK.
- `granteePrincipalUid` (string): Principal UID that was admitted.
- `role` (string): Effective access role assigned to the grantee.
- `status` (string): Always "granted" on success.

### `team_join` (~103 tokens)

Point this device at an existing team's encrypted remote so it can sync the shared brain, WITHOUT creating a new team. Use this (not team_create) to join a team someone else created. After joining, run team_key and send the blob to an admitted member to be granted, then sync pull to bootstrap the team key.

Input parameters:

- `remote` (string, required): The team's shared git remote URL to sync through (e.g. git@github.com:org/team-brain.git).

Output parameters:

- `nextSteps` (array): What to do next to finish joining.
- `remoteUrl` (string): The team remote this device is now configured to sync with.
- `status` (string): Always "joined" on success.

### `team_key` (~67 tokens)

Show this device's team join request: its bound principal UID and base64 x25519 wrap public key. Send both to an admitted team member so they can grant you (maz team grant --principal <uid> --wrap-pubkey <key>). Read-only; neither value is secret.

Output parameters:

- `joinKey` (string): Opaque join-key blob to hand an admitted member (maz team grant --join-key <blob>). Carries the principal UID, wrap public key, and signing identity; nothing in it is secret.
- `principalUid` (string): This device's bound principal UID.
- `wrapPublicKey` (string): This device's base64 x25519 wrap public key.

### `team_members` (~50 tokens)

List the admitted team members (the derived ACL): opaque principal UID, DEK epoch, wrap public key, and whether each is the genesis owner. Read-only and contains no display names or other PII.

Output parameters:

- `members` (array)

### `team_revoke` (~105 tokens)

Revoke a team member and forward re-key the team: appends the revocation, mints a fresh data-encryption key at the next epoch, and re-wraps it to every remaining member. Returns a checklist — you must separately remove the principal's git-host access, which Mazarbul cannot do.

Input parameters:

- `principalUid` (string, required): ULID of the principal to revoke.
- `reason` (string): Optional human-readable revocation reason recorded in the audit trail.

Output parameters:

- `checklist` (array): Post-revoke operator checklist: what Mazarbul did and what you must still do.
- `newDekEpoch` (integer): Epoch of the fresh DEK the remaining members were re-keyed to; the revoked principal has no grant here.
- `revokedPrincipalUid` (string): Principal UID that was revoked.
- `rewrappedTo` (array): Principal UIDs the new DEK was re-wrapped to (the remaining members).

### `team_role_set` (~113 tokens)

Change an admitted team member's access role (owner | admin | member | reader) without re-wrapping key material. Requires the caller to hold at least admin role; assigning admin or owner requires owner. The change is recorded as a teamRoleChanged event and converges identically on every peer.

Input parameters:

- `principalUid` (string, required): ULID of the admitted team member whose role is being changed.
- `role` (string, required): New access role. Assigning admin or owner requires the caller to hold owner role.

Output parameters:

- `eventUid` (string): UID of the appended teamRoleChanged event.
- `role` (string): New effective role.
- `status` (string): Always "role_set" on success.
- `subjectPrincipalUid` (string): Principal whose role was changed.

### `unrelate` (~353 tokens)

End a relation as of a date — the temporal counterpart to relate. Records that a relationship STOPPED being true (someone left a job, a project ended) by closing the edge with a valid_until: it drops out of current reads but its history is PRESERVED (unlike forget, which deletes the edge). Identify the edge by its relations/{uid} or by the (subject, predicate, object) triple (which closes the single open match). Pass endedAt for when it ended (defaults to now); pass supersededBy when a newer relation replaces it.

Input parameters:

- `endedAt` (string): When the relationship ended (RFC 3339 UTC); becomes the edge's valid_until. Defaults to now.
- `object` (string): Object of the relation to close: entities/{uid}, a bare ULID, "self", or a display name.
- `predicate` (string): Predicate of the relation to close, e.g. works_at.
- `relation` (string): The relation to close: relations/{uid} or a bare ULID (as returned by relations() or recall). Use THIS or a (subject, predicate, object) triple, not both.
- `scopes` (array): Scopes to search when identifying the edge by triple: project, personal, team. Defaults to project+personal.
- `subject` (string): Subject of the relation to close: entities/{uid}, a bare ULID, "self", or a display name. Provide with predicate+object as an alternative to relation.
- `supersededBy` (string): Optional relations/{uid} of a newer relation that replaces this one (a correction). Omit for a pure close — the relationship simply ended.

Output parameters:

- `name` (string): Resource name of the closed relation.
- `status` (string): Always "retracted" on success: the edge is closed (valid_until set) but its history is preserved.
- `validUntil` (string): The valid_until the relation was closed at (RFC 3339 UTC).

### `updateEntity` (~126 tokens)

Relabel an entity's kind and/or displayName by appending an entityUpdated event. Supply at least one of kind or displayName; omitting both is E_VALIDATION. Returns E_NOT_FOUND when the uid does not resolve. Only the supplied fields change; confidence, properties, identifiers, and coordinates are untouched.

Input parameters:

- `displayName` (string): New human-readable label. Omit to leave unchanged.
- `entityUid` (string, required): ULID of the entity to update. Required.
- `kind` (string): New entity kind in snake_case. Omit to leave unchanged.

Output parameters:

- `name` (string): AIP resource name of the updated entity.
- `status` (string): Always "updated" on success.

### `waitForIdle` (~96 tokens)

Block until the kernel finishes all background extraction, resolution, and embedding work (or the timeout expires). Use between a burst of remember() calls and a benchmark recall() so the recall measures retrieval latency only, not the tail of ingestion. Cheap when the kernel is already idle.

Input parameters:

- `timeoutSeconds` (integer): Maximum seconds to wait. Defaults to 300. The tool returns timedOut:true rather than erroring when the budget expires.

Output parameters:

- `elapsedMs` (integer): Wall-clock spent waiting, in milliseconds.
- `enrichmentInflight` (integer): Enrichment depth observed at return time (0 unless timedOut).
- `isIdle` (boolean): True when both enrichment and vec drained within the budget; false if timedOut.
- `timedOut` (boolean): True when the timeoutSeconds budget expired before the kernel went idle.
- `vecInflight` (integer): Vec depth observed at return time (0 unless timedOut).

### `writeDocument` (~500 tokens)

Create or replace a markdown document at `path` with `body` (stored verbatim, event-sourced — full history is kept). [[Wikilinks]] in the body resolve to canonical entities and other documents as graph edges; dangling links reconcile automatically when their target appears. Optional `title`/`pinned`/`tags`/`frontmatter` synthesize a YAML frontmatter block when the body has none. Frontmatter `about: <name|entities/{uid}>` records the document's primary subject as a high-confidence `describes` edge (distinct from body [[wikilinks]], which are `mentions`); resolve it by uid where you can, since a renamed subject keeps the link. Frontmatter `canonical: true` lets this document win name resolution over a same-named non-document entity, so a later-created entity cannot silently steal the name. The document materializes as an editable file in the vault. Idempotent: rewriting identical content is a no-op. `scope` picks who sees the document (personal by default; team requires membership, else E_PERMISSION) and is fixed at creation — the same path in a different scope is a separate document. Result counts (`edges`, `unresolved`, `reconciled`) are whole-document snapshots after the write, not deltas. A non-fatal `warnings` field appears when the title shadows a same-named non-document entity.

Input parameters:

- `body` (string, required): The full markdown body, stored verbatim. May include its own YAML frontmatter block.
- `frontmatter` (object): Additional frontmatter keys (JSON object), synthesized into a YAML block. Only when body has no frontmatter of its own.
- `path` (string, required): Vault-relative path, e.g. notes/project-alpha.md (.md appended when no extension).
- `pinned` (boolean): Pin the document (synthesized into frontmatter). Pinned documents surface first in context packs.
- `scope` (string): Storage scope, fixed at creation. personal (default) = only you; project = a shared project; team = every team member (requires membership, else E_PERMISSION); draft = private staging. The same path…
- `tags` (array): Tags, synthesized into frontmatter.
- `title` (string): Optional title, synthesized into frontmatter. Only when body has no frontmatter of its own.

Output parameters:

- `changed` (boolean): False when the content matched the latest revision (no-op).
- `documentUid` (string)
- `edges` (integer): Total resolved wikilink and frontmatter edges the whole document holds after this write (snapshot, not a delta — counts all [[links]] in the current body that resolved, regardless of how many were al…
- `file` (string): Absolute path of the materialized vault file.
- `name` (string): entities/{uid} resource name of the document-as-entity.
- `path` (string)
- `pendingEdit` (boolean): True when the vault file holds an un-ingested manual edit and was left untouched.
- `reconciled` (integer): Count of wikilinks in OTHER vault documents that this write newly made resolvable (cross-document reconcile pass; separate from the document's own edge snapshot above).
- `revisionUid` (string)
- `unresolved` (integer): Count of [[wikilinks]] left dangling in the whole document after this write (snapshot, not a delta).
- `unresolvedTargets` (array): The dangling wikilink targets by name (text inside [[…]] that matched nothing) — fix a typo or create the target. Omitted when none dangle.
- `warnings` (array): Non-fatal advisory messages — e.g. a title-collision warning when the document's display name shadows a non-document entity. Does not affect the written content; the document body is stored byte-for-…

## Diagnostics

Captured diagnostic sections: Provenance. The full working is on the page: https://verifymcp.io/servers/mo-cmyk-mazarbul/mazarbul-linux-amd64#diagnostics

## Score history

- 2026-11-05: 42
- 2026-11-04: 42
- 2026-11-03: 41
- 2026-11-02: 41
- 2026-11-01: 40
- 2026-10-31: 40

## Common questions

### What is the Mazarbul MCP server?

Mazarbul is an MCP server listed in the public MCP registry as io.github.mo-cmyk/mazarbul. Local-first memory for AI agents: entities, facts, relations and documents with semantic recall. This page covers its MCPB bundle (https://github.com/mo-cmyk/mazarbul/releases/download/v0.2.3/mazarbul_v0.2.3_linux_amd64.mcpb).

### Is the Mazarbul MCP server safe to use?

Mazarbul scores 42 out of 100 on VerifyMCP. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the Mazarbul MCP server expose?

Mazarbul exposes 64 tools: addIdentifier, agentFeedback, cancelJob, captureExternal, connectSource, and 59 more. Their descriptions and schemas cost roughly 16,172 tokens of context every time the server is loaded.

### What licence is the Mazarbul MCP server under?

Mazarbul declares the Apache-2.0 licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- Repository: https://github.com/mo-cmyk/mazarbul
- Changelog RSS feed: https://verifymcp.io/servers/mo-cmyk-mazarbul/mazarbul-linux-amd64.xml
- Changelog JSON feed: https://verifymcp.io/servers/mo-cmyk-mazarbul/mazarbul-linux-amd64.json
- HTML version of this page: https://verifymcp.io/servers/mo-cmyk-mazarbul/mazarbul-linux-amd64
