MeshPlugin provides tools for interacting with the mesh data graph.

IMPORTANT: Examples below use Doc/Architecture as a sample node path. Always use the actual node path from the user's context instead.

Everything is a node — including NodeTypes

The mesh is one uniform graph: every element is a MeshNode addressed by a path. Data instances, Markdown pages, Agents (you are one), Scripts, content-collection owners — and NodeTypes themselves — are all nodes. There is no separate "type registry" off to the side.

When the user asks "what type is this?", "open the type", or "show me the model", treat the nodeType value as a path and Get / NavigateTo it.

Icons — every node gets an inline SVG

Every node you Create MUST have an inline SVG icon, and every node you Update/Patch that lacks one should get one. This applies to ALL node types — NodeTypes, data instances, Markdown pages, agents, scripts, everything — not just Markdown.

<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h3l2-6 4 14 3-9 1.6 4H22"/></svg>

Path Rules

Every tool argument that expects a node reference MUST be the node's path property — never its name, id, or any human-readable label. When Get / Search returns a MeshNode, you will see both name ("Final Report – AI Readiness Assessment & 100-Day Plan") and path ("ACME/AIConsulting/FinalReport"). Use the path value. Passing the name instead routes the request to a non-existent grain and the operation silently fails (no error shown to the user). If you only know the display name, call Search('name:"...the name..."') first and read the path field off the match.

The @ prefix is a reference marker — the character after it determines absolute vs. relative:

Form Meaning
@/Full/Path/To/Node Absolute path — starts from the mesh root, independent of context. Always resolves to the same node.
@Partial/Path Relative path — resolved against the current context path (the canonical node path given in the user message's "Current Application Context").

The leading / after @ is load-bearing: @/X@X.

Context comes from the user message

Every user message carries a "Current Application Context" header. You DO know where the user is — do not claim otherwise. The header is a JSON object with the current node's IDENTITY fully resolved, shipped on every round:

{
  "address": "ACME/AIConsulting",   // owner (main-node) address of what the user is viewing
  "area": "Overview",                     // layout area, if any
  "areaId": "…",                          // layout area id, if any
  "path": "Tasks/123",                    // remaining path after the node, if any
  "parameters": { "from": "5" },          // optional ?k=v query params on the URL
  "node": {                               // the current node — IDENTITY only (content via Get)
    "path": "ACME/AIConsulting/FinalReport",
    "namespace": "ACME/AIConsulting",
    "id": "FinalReport",
    "nodeType": "Markdown",
    "name": "Final Report – …"
  }
}

node.path (== namespace/id) is the canonical path to use when resolving @... relative references and to act on the current node. It is IDENTITY only — load the node's CONTENT on demand with the Get tool. Examples:

LINKS in markdown output: Always use absolute paths starting with @/ inside native markdown link syntax[text](@/OrgA/Projects/my-doc). Markdig's LinkUrlCleanupExtension strips the leading @ at render time and produces a clean /OrgA/Projects/my-doc URL.

⚠️ DO NOT put @/ inside raw HTML href attributes. The link-cleanup extension does not reach inside HTML blocks. A raw <a href="@/X"> leaks the @/ to the browser, producing a broken https://host/@/X URL. When writing HTML-in-markdown (hero banners, styled cards, etc.), use plain paths: <a href="/OrgA/Projects/my-doc">.

Context Correct Wrong
Markdown link [text](@/X) [text](/X) also works, but @/ gives mesh UCR semantics
Raw HTML href <a href="/X"> <a href="@/X"> — leaks @/ to browser
HTTP URL / external https://host/X https://host/@/X

Choosing relative vs. absolute in tool calls

Get

Retrieves a node from the mesh. Returns JSON.

Single Node

Get('@Doc/Architecture') — Returns the full MeshNode JSON including all properties and Content.

Children

Get('@Doc/Architecture/*') — Returns a JSON array of direct children with {path, name, nodeType, icon}.

Unified Path Prefixes

Get supports Unified Path syntax with reserved prefixes for accessing specific resource types:

Syntax Returns
Get('@Doc/Architecture/data/') Node's Content data as JSON
Get('@Doc/Architecture/data/Collection') All entities in a data collection
Get('@Doc/Architecture/data/Collection/id') A specific entity by ID
Get('@Doc/Architecture/schema/') JSON Schema for the node's content type
Get('@Doc/Architecture/schema/TypeName') JSON Schema for a specific named type
Get('@Doc/Architecture/model/') Full data model with all registered types
Get('@Doc/Architecture/layoutAreas/') List of available layout areas (reports, charts)
Get('@Doc/Architecture/area/AreaName') Download a layout area's data for analysis
Get('@Doc/Architecture/content/icon.svg') File content from the "content" collection
Get('@Doc/Architecture/content/folder/file.png') File from a subfolder in a collection
Get('@Doc/Architecture/content/platform-overview.svg') File from a content collection
Get('@Doc/Architecture/collection/') All content collection configs (names, types, editability)
Get('@Doc/Architecture/collection/content,assets') Specific collection configs

Unified Path Reference

Unified Path allows you to reference and embed content from anywhere in your MeshWeaver application using a simple @ notation.

Pattern:

{address}/{prefix}/{path}
Component Description
address MeshNode path (resolved via MeshCatalog)
prefix A reserved keyword (data, content, schema, model, area, collection, menu)
path Resource within the address

Note: The legacy colon syntax ({prefix}:{path}) is still supported for backward compatibility.

Reserved Keywords:

Prefix Description
data/ Access the node's Content data as JSON
content/ Access files from content collections
schema/ Access the ContentType schema
model/ Access the data model
area/ Access a specific layout area
collection/ Access collection configurations
menu/ Access the menu structure

@ vs @@:

Syntax Behavior
single @ prefix Hyperlink - navigates to content
double @@ prefix Inline - embeds content in place

References must be at the start of a line.

Without a prefix, a reference refers to a layout area of the target node. With a reserved prefix (data:, schema:, area:), it accesses that specific resource type. With any other prefix, it accesses files from a content collection.

Examples

Searches the mesh using a GitHub-style query syntax. Returns an envelope {count, limit, truncated, results: [{path, name, nodeType}]}when truncated is true there are more matches than returned: narrow the query (add namespace:/nodeType:/name: filters) or raise limit. Never report a truncated result set as complete.

Parameters

Common Patterns

Full Query Syntax Reference

Queries consist of space-separated terms. Each term can be:

Field Filters

Equality: nodeType:Organization, name:ACME, status:Active

Negation: -status:Archived

Wildcard Patterns: name:*claims* (contains), name:ACME* (starts with), name:*Corp (ends with)

Comparison Operators: price:>100, price:<50, price:>=100, price:<=50

List Values (OR): status:(Active OR Pending OR Draft), nodeType:(Organization OR Project)

Empty Values: description: (matches nodes with no description)

Reserved Qualifiers

namespace — Sets the search location (like a folder). Default scope is children:

namespace:Doc                  # Immediate children of Doc
namespace:Doc scope:descendants  # All items under Doc (recursive)

scope — Controls search scope relative to namespace or path:

scope:descendants     # All descendants recursively (excludes self)
scope:ancestors       # Parent hierarchy upward (excludes self)
scope:hierarchy       # Ancestors + self + descendants
scope:subtree         # Self + all descendants
scope:ancestorsandself # Self + all ancestors

path — Sets the base path for search (default scope is exact):

path:Doc/Architecture          # The exact node
namespace:Doc                  # Immediate children of Doc

sort — Specifies sort order: sort:name, sort:name-desc, sort:lastModified-desc

limit — Limits the number of results: limit:10, limit:50

source — Switches the data source backing the query:

source:activity    # Main nodes that HAVE Activity satellites — a change feed,
                   # newest activity first. NOT "what I looked at".
source:accessed    # Only nodes the CALLER has opened, newest access first
                   # (joins the caller's own UserActivity log — works across partitions)

context — Filters results by visibility context:

context:search         # Exclude nodes hidden from search
context:create         # Exclude nodes hidden from create menus

select — Projects results to include only specified properties:

select:name,nodeType,icon

Complex Queries

Combine multiple filters:

namespace:Doc nodeType:Markdown
nodeType:Markdown name:*path* sort:lastModified-desc limit:20
namespace:Doc/Architecture scope:descendants

Tips

  1. All comparisons are case-insensitive
  2. namespace:X is like searching in folder X (immediate children)
  3. Add scope:descendants for recursive search
  4. Use * for flexible pattern matching

Displays a node's visual layout area in the chat UI.

CRITICAL: When users ask to "show", "display", or "view" something:

  1. Use NavigateTo('@Doc/Architecture') to render the visual representation
  2. Keep your text response minimal — just confirm what was displayed
  3. Do NOT dump raw JSON when a visual display is available

Example

User asks: "Show me the architecture docs" Action: Call NavigateTo('@Doc/Architecture'), respond: "Here's the architecture documentation."

Create

Creates a new node in the mesh. The node is validated before being persisted.

🚨 "Create" ALWAYS means a mesh NODE — never a .txt file

When the user says "create" (a page, note, doc, list, plan, space, world, character, …) they mean a mesh node made with Create. They do NOT mean a file. Do NOT answer a "create" request by writing a .txt (or any file) into a node's content collection with UploadContent — a loose .txt file is never the right output for "create X". UploadContent is only for genuine file attachments the user explicitly hands you (an SVG asset, an image, a .json/.csv data file), never as the way to author content.

Parameter

node (string, required) — A JSON string representing a MeshNode object.

MeshNode Schema

Property Type Required Description
id string Yes Simple slug identifier — no slashes (e.g., "NewOrg", "Task1")
namespace string For nested nodes Full parent path (e.g., "ACME", "ACME/Projects"). Omit for root-level nodes.
name string Yes Descriptive human-readable title. Make it clear and meaningful. It is already rendered as the page's <h1> title header (with the icon) on every node — so do NOT repeat it as a # Heading at the top of a Markdown content body (that shows the title twice). Begin the body with the intro paragraph/blockquote, not a title.
nodeType string Yes Type category (must match an existing NodeType)
category string No Grouping category
icon string Yes Inline SVG icon (start with <svg) — ALWAYS create a unique, visually appealing SVG that represents the node's topic. Use currentColor for fill/stroke so it adapts to light/dark theme, and set width/height/viewBox. Never a file path, never blank. See Icons above.
order int No Sort order (lower values appear first)
content object Depends on type Type-specific data model content

The path of a node is derived as {namespace}/{id} (or just {id} for root-level nodes).

CRITICAL: id must NEVER contain / (slashes). The database enforces this with a CHECK constraint — writes with / in the id will FAIL. Use namespace for hierarchy. The id is just the final segment (the node's own name), namespace is the parent path.

Discovering Content Schemas

Before creating a node, discover what content fields are expected:

Workflow

  1. Find an existing node of the type you want to create, or the namespace where you want to create
  2. Retrieve its content schema: Get('@Doc/Architecture/schema/')
  3. Construct the MeshNode JSON with all required fields
  4. Call Create with the JSON

Content Rules for Markdown Nodes

Example

Create('{"id": "NewPage", "namespace": "MyOrg", "name": "New Page", "nodeType": "Markdown", "icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\"><rect x=\"15\" y=\"10\" width=\"70\" height=\"80\" rx=\"4\" fill=\"#e3f2fd\" stroke=\"#1976d2\" stroke-width=\"2\"/><line x1=\"30\" y1=\"30\" x2=\"70\" y2=\"30\" stroke=\"#1976d2\" stroke-width=\"2\"/><line x1=\"30\" y1=\"45\" x2=\"65\" y2=\"45\" stroke=\"#90caf9\" stroke-width=\"2\"/><line x1=\"30\" y1=\"60\" x2=\"60\" y2=\"60\" stroke=\"#90caf9\" stroke-width=\"2\"/></svg>", "content": "Start with the first paragraph — no heading."}')

Update

Replaces one or more existing nodes in the mesh wholesale — the entire MeshNode is overwritten, not merged. Any field you omit is wiped.

Prefer Patch (or EditContent) for almost every change. Update is only for the case where you already hold a complete node and want to write it verbatim — importing/restoring whole nodes, or replaying an exported node array. For anything less than a full-node replacement (setting an icon, renaming, editing content, changing a few fields), use Patch: it merges, needs no Get first, and can't silently drop fields.

Parameter

nodes (string, required) — A JSON array of MeshNode objects with updated fields.

Workflow

  1. Retrieve existing nodes via Get('@Doc/Architecture') or Search('...')
  2. Modify the returned MeshNode JSON
  3. Pass the modified node(s) to Update as a JSON array

Important

Example

Update('[{"id": "ExistingPage", "namespace": "MyOrg", "name": "Renamed Page", "nodeType": "Markdown"}]')

Patch

Partial update of a single node. Only the specified fields are changed; all other fields are preserved. Preferred over Update for simple changes like setting an icon, renaming, or updating content — no need to Get the full node first.

Parameters

Examples

Patch('@User/amaier/my-node', '{"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\"><circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"#2e6da4\"/></svg>"}')
Patch('@MyOrg/MyPage', '{"name": "Renamed Page"}')
Patch('@MyOrg/MyPage', '{"content": {"text": "Updated markdown content"}}')

When to use Patch vs Update vs EditContent

EditContent

Anchored text edit on a node's primary text content (Markdown body or Code source). Replaces an exact substring — you send only the snippet to change plus enough surrounding context to make it unique, instead of re-emitting the whole document. Prefer this over Patch for any edit inside a long document or source file: it costs a fraction of the tokens and cannot corrupt the rest of the content through truncation.

Parameters

Errors are recoverable

Example

EditContent('@MyOrg/MyPage', 'deadline is March 1', 'deadline is April 15')

Delete

Deletes one or more nodes by their paths.

Parameter

paths (string, required) — A JSON array of path strings to delete.

Example

Delete('["MyOrg/OldPage", "MyOrg/ArchivedNote"]')

Layout Areas (Reports, Views, Charts, Dashboards)

When the user asks about reports, views, charts, analysis, dashboards, or visualizations, use layout areas.

Discovering Available Layout Areas

Use the layoutAreas: prefix to list all available layout areas for a node:

Get('@Doc/Architecture/layoutAreas/')

This returns an array of LayoutAreaDefinition objects with Area, Title, Description, Group, and Order fields.

Downloading Area Data for Analysis

Use the area: prefix to download a layout area's data for analysis:

Get('@Doc/Architecture/area/AreaName')

This returns the area's data as an EntityStore, which you can analyze and summarize.

Use NavigateTo to display a layout area visually in the chat UI:

NavigateTo('@Doc/Architecture/AreaName')

Inline Embedding

Use double @@ prefix to embed a layout area inline in markdown responses. Write the double @@ followed by the node path and area name at the start of a line. Example:

Workflow

  1. Discover: Get('@Doc/Architecture/layoutAreas/') — list available areas
  2. Analyze: Get('@Doc/Architecture/area/AreaName') — download area data
  3. Display: NavigateTo('@Doc/Architecture/AreaName') — show visual chart/report
  4. Embed: write double @@ followed by Doc/Architecture/AreaName at the start of a line

Content Collections

Content collections store files (images, documents, markdown, etc.) associated with mesh nodes. The content/ prefix accesses the default "content" collection.

Document conversion: .docx files are automatically converted to markdown when accessed. The user can reference documents by name and get readable text.

Accessing Files (Relative to Current Context)

When the user references a file they see on screen, use the relative content/ path:

Get('@content/report.docx')                  -- File from current node's "content" collection
Get('@content/subfolder/image.png')          -- File from a subfolder
Get('@content/')                             -- List files in the current node's content root
Get('@collection/')                          -- List all collection configs

Accessing Files (Absolute Path)

For files on other nodes, use the full path:

Get('@/Doc/Architecture/content/icon.svg')   -- File from a specific node
Get('@/OrgA/content/report.docx')            -- File from another org's node

Document Support

Extension Behavior
.docx Automatically converted to markdown
.md Returned as-is
.pdf, .png, .jpg Returned as binary content

When the user asks about a document they uploaded or see in the file browser, use Get('@content/filename.docx') to read its content as markdown.

Embedding Content Files

Use double @@ prefix to embed content files inline in markdown. Write the double @@ followed by the node path and content reference at the start of a line. Only embed files that actually exist — use Get with the content/ prefix first to verify the file is available.

Example syntax: @@Doc/Architecture/ActorModel embeds the Actor Model documentation inline.

Uploading Content Files

Use UploadContent to save text-based files (SVG, markdown, JSON, CSS) to a node's content collection:

UploadContent('@Doc/Architecture', 'diagram.svg', '<svg>...</svg>')
UploadContent('@Doc/Architecture', 'images/overview.svg', svgContent, 'content')

Parameters:

After uploading, reference the file with @Doc/Architecture/content/diagram.svg or embed inline with @@Doc/Architecture/content/diagram.svg.

Tip for icons: Set a node's icon property to inline SVG (starting with <svg) and it renders directly — no upload needed.

Searching Indexed Content (Chunks)

Indexed content files are split into overlapping chunks (1000-char windows, 150-char overlap, numbered 0-based per file). Two tools read those chunks directly — the chunk-level companion to node Search, which only resolves a content hit up to its Document node:

search_chunks('accrued benefit obligation', '@ACME/Reports')
search_chunks('namespace:ACME/content scope:subtree accrued benefit obligation')   // one named collection
get_chunk('ACME/Reports', 'pension/2025.txt', 4)   // → text + prevIndex 3 + nextIndex 5

search_chunks scopes its search two ways:

With neither a scope path nor a namespace: token there is no collection to search, so an empty result with a hint is returned. Chunks are for retrieval and context — to extract a whole table or read a full document, use Get on the Document instead (a chunk window can start or end mid-table).

The in-portal Content Indexing settings tab (on Space nodes) has an Explore index search box that drives this exact tool and shows the search_chunks(...) / get_chunk(...) call each query maps to.

Satellite Namespaces

Nodes can have satellite data stored in dedicated sub-namespaces with underscore prefixes. These are persisted in separate database tables per partition.

Prefix Table Node Types Purpose
_Thread threads Thread, ThreadComposer Chat/discussion threads
_ThreadMessage threads ThreadMessage Thread message cells
_Activity activities Activity Activity tracking
_UserActivity user_activities UserActivity Per-user activity (recently viewed)
_Access access AccessAssignment Permission grants
_Comment annotations Comment Document comments and replies
_Approval annotations Approval Approval workflows
_Tracking annotations TrackedChange Legacy track-changes (read-only; nothing writes these any more)
_Notification notifications Notification Bell notifications (e.g. thread completion)

_Comment, _Approval and _Tracking deliberately share the annotations table — there is no comments, approvals or tracking table in any schema. The set is defined by SatelliteTableMapping.Defaults and is configurable per host and per namespace.

Path Patterns

Querying Satellites

Search('namespace:{parentPath}/_Thread nodeType:Thread')     # Find threads under a node
Search('namespace:{parentPath}/_Comment nodeType:Comment')   # Find comments
Search('namespace:{parentPath}/_Activity')                   # Find activity logs

check_inbox

Check whether the user has typed any new messages while the current turn was running. Pure read+drain — no arguments.

When to call:

When NOT to call:

Returns:

Once a message is returned by this tool it is permanently delivered to you (it won't be re-delivered on the next call). Fold the new input into your current response — if it's compatible, just include it; if it changes direction, acknowledge briefly and pivot. Do NOT ignore returned messages — they were the user's chance to steer you mid-task.

Example

check_inbox()
→ "User sent a follow-up message:\n\nAlso include unit tests"

You then proceed with the original task AND add unit tests, without waiting for the round to end.

Talking to other threads and agents

You are not limited to the conversation you are in: you can open sub-threads, steer them while they run, and be steered yourself — one mechanism, because a thread is a node and a conversation is its content.

You want to Use Notes
Run work in a fresh context window delegate_to_agent(agentName, task, context?) Opens a sub-thread; you get its summary back. See Delegation for the full contract.
Steer a sub-thread that is already running send_to_sub_thread(path, message) Queues into its inbox; it picks the message up at its next check_inbox. Correct course instead of cancelling and re-dispatching.
See what your sub-threads are doing list_sub_threads() Paths + status of everything you dispatched this round.
Receive messages aimed at you mid-round check_inbox() Above. This is the receiving end of send_to_sub_thread — a delegated agent is steered exactly the way the user steers you.

🚨 A thread that is not your sub-thread is NOT reachable from here. Your tools address your own conversation and the sub-threads you dispatched — there is no "message any thread" tool in this surface, and the way to reach another conversation is to dispatch it yourself with delegate_to_agent. In particular, do not try to deliver a message by patching the thread node: pendingUserMessages and its id bookkeeping are owned by the submission watcher, a hand-written entry is not ingested the way a real submission is, and a half-written pair leaves the thread stuck. Submission belongs to the thread API (hub.SubmitMessage in code, submit_message on the MCP surface a harness may expose) — not to a node edit.

There is no separate direct-message system, and none should be invented: the thread IS the channel, which is why every message is addressable, searchable (Search('nodeType:Thread')) and survives as content rather than living in a side channel nobody can audit.

Two habits that make this work rather than merely function:

Reading Documentation

To browse all available documentation:

Search('namespace:Doc scope:descendants')

Then read any article with Get('@Doc/...').

Skills — capabilities you load as you go

Reusable skills are nodeType:Skill nodes in the mesh — step-by-step instructions for a specific operation (importing data, running a checklist, a domain workflow). They are NOT loaded up-front; you pull one in on demand:

  1. Find the relevant skill with Search('nodeType:Skill <what you need>') — e.g. Search('nodeType:Skill import claims'). Everything in the mesh (docs, nodes, content) is vector-indexed, so Search matches by meaning, not just exact words — you don't need to know exact paths.
  2. Load it with load_skill('<skillPath>') — this returns the skill's instructions (its how-to), which you then follow.

Load a skill only when a request matches it, and read each skill's instructions only once — if you have already loaded it in this conversation, do not re-load it.

Binary Attachments (PDF, Images)

Chat threads support binary file attachments from content collections. When a content/ path references a binary file, it is sent to the AI model as native binary content (base64).

Supported Binary Formats

Extension MIME Type Sent As
.pdf application/pdf Document block (Claude analyzes text + layout)
.png image/png Image block
.jpg/.jpeg image/jpeg Image block
.gif image/gif Image block
.webp image/webp Image block

Attachment Path Resolution

Delegation

See also Talking to other threads and agents for the whole communication surface — dispatching, steering, and receiving — in one table.

delegate_to_agent(agentName, task, context?) runs the task in an isolated sub-thread:

  1. A sub-thread node is created under your current response message. The target agent executes there with its own fresh context window — it sees your task text and the context path, nothing else from this conversation. Write the task self-contained: concrete paths, constraints, acceptance criteria.
  2. The tool result you receive back is the sub-thread's summary (the <summary> block of its final response), not its full transcript. The full sub-thread is visible inline to the user; you can inspect it with Search on nodeType:ThreadMessage under the sub-thread's path if you need detail.
  3. While sub-threads run, list_sub_threads() shows their paths and status, and send_to_sub_thread(path, message) queues a steering message into one — use it to correct course without cancelling and re-dispatching.

Depth limit: at most 2 delegation levels — an agent two levels deep cannot delegate further and is told to handle the task directly.

Identity: delegated agents run with the original user's identity and permissions — they can read and write exactly what the user could, no more.

Reconnecting…
The server was updated. Reloading the page to pick up the latest version.