Skip to main content

Overview

The SyncClient is the Room API for live shared documents (MeshDocuments). Use it when multiple humans or agents need to read and edit the same structured document in real time.

Why use the Sync API?

  • Keep shared state in a live document instead of relying only on chat history.
  • Let multiple participants collaborate on the same structured content at once.
  • Define document schemas that help users and agents write valid data.

How it works

You create or open a document by path. Opening a document starts a live synchronization session so local changes propagate to other participants in the room. Use describe when you want the current JSON state without opening a sync session, and use sync only when you already have encoded changes to send.

Permissions and grants

If a deployed service needs to synchronize room documents, its participant token needs the appropriate Room API grants for sync and the relevant room resources. See API Scopes and Packaging and Deploying Services.

CLI and SDK availability

  • CLI: create and inspect documents with meshagent room sync ....
  • Python, JavaScript/TypeScript, Dart, and .NET: sync helpers are shown below.

Defining Your Document Structure

To define the structure of a MeshDocument, you start by creating a schema using the MeshSchema class. A schema:
  • Documents the structure of your MeshDocument for anyone in your organization.
  • Ensures agents and users don’t write invalid data to the document.
  • Allows agents to automatically generate, manipulate, and validate structured documents.
  • Automatically generates LLM-compatible schemas for structured outputs.
  • Synchronizes documents across all platforms that MeshAgent supports.
Similar to how a web page is structured, a MeshSchema begins with a root element that includes an allowed tag name and optional attributes. Afterward, you can define additional tags, their attributes, and the types of child nodes they can contain.

Example Schema

Suppose you have a basic web page structure and want to define a schema for it.
from meshagent.api.schema import MeshSchema, ElementType, ChildProperty, ValueProperty

schema = MeshSchema(
    root_tag_name="html",
    elements=[
        ElementType(
            tag_name="html",
            properties=[
                # a ChildProperty describes the type of children that
                # an element allows. There can be at most one child
                # property for each element type, but the child property
                # can allow multiple types of child elements.
                ChildProperty(name="children", child_tag_names=["body"])
            ],
        ),
        ElementType(
            tag_name="body",
            properties=[
                # Our body can only contain paragraph elements
                ChildProperty(name="children", child_tag_names=["p"])
            ],
        ),
        ElementType(
            tag_name="p",
            properties=[
                # A ValueProperty property describes an attribute that
                # contains a single value
                ValueProperty(name="class", type="string"),
            ],
        ),
    ],
)

Many LLMs (such as OpenAI) and agent frameworks (such as MeshAgent) support JSON Schemas for defining inputs and outputs. Generating an OpenAI-compatible JSON schema from your MeshSchema requires only one line of code:
json_schema = schema.to_json()

Creating a MeshDocument

To create a MeshDocument based on your schema and enable synchronization across different clients, use the MeshAgent runtime:
import os
import asyncio
from meshagent.api import (
    RoomClient,
    WebSocketClientProtocol,
    ParticipantToken,
    ApiScope,
    ParticipantGrant,
)
from meshagent.api.helpers import meshagent_base_url, websocket_room_url


def env(name: str) -> str:
    val = os.getenv(name)
    if not isinstance(val, str) or not val:
        raise RuntimeError(f"Missing required environment variable: {name}.")
    return val


async def main():
    # Define a unique room name
    room_name = "my-room"

    async with RoomClient(
        protocol=WebSocketClientProtocol(
            url=websocket_room_url(room_name=room_name),
            token=ParticipantToken(
                name="participant",
                project_id=env("MESHAGENT_PROJECT_ID"),
                api_key_id=env("MESHAGENT_KEY_ID"),
                grants=[
                    ParticipantGrant(name="room", scope=room_name),
                    ParticipantGrant(name="role", scope="agent"),
                    ParticipantGrant(name="api", scope=ApiScope.agent_default()),
                ],
            ).to_jwt(token=env("MESHAGENT_SECRET")),
        )
    ) as room:
        print(f"Connected to room: {room.room_name}")
        # open our document
        document = await room.sync.open(path="hello-world.document")

        # wait for the document to sync from the server
        await document.synchronized

        # our root element is automatically added to the document, let's construct the sample document by inserting
        # a body with a paragraph element
        document.root.append_child(tag_name="body", attributes={"text": "hello world!"})

        # wait before closing so the sync can finish
        await asyncio.sleep(3)


asyncio.run(main())

API Methods

Below is an example of how you might use the SyncClient in your code:
meshagent room sync create \
  --room myroom \
  my/path \
  --schema ./schema.json \
  --json '{"initial":"data"}'

SyncClient

The SyncClient class is responsible for coordinating document synchronization through a RoomClient. It automatically listens for local changes on MeshDocument instances and forwards them to the remote server, while also applying remote changes as they are received.
  • room: RoomClient
    An instance of RoomClient, which handles the underlying communication with the server.

create(path, json=None)

meshagent room sync create \
  --room myroom \
  my/path \
  --schema ./schema.json \
  --json '{"key":"value"}'
  • Parameters:
    • path – The path where the document will be created on the server.
    • json – (Optional) Initial data or metadata to send along with the creation request.
  • Description
    Sends a request to create a new MeshDocument at the given path. The server infers the schema from the document’s extension (for example, .html uses .schemas/html.json).

describe(path)

meshagent room sync show \
  --room myroom \
  my/path
  • Parameters:
    • path – The document path to describe.
  • Returns
    A JSON representation of the document root in a single response.
  • Description
    Fetches the current state of a MeshDocument without opening a sync session. If the document is already open, the in-memory state is returned; otherwise, it is read from storage (schema inferred from the document extension). This is what the storage read_file tool uses to read MeshDocument contents as JSON.
    • Availability: Supported by the room server; the Python SDK exposes room.sync.describe today, while other SDKs may add a convenience wrapper in upcoming releases.

open(path, create = true)

doc = await room.sync.open(path="my/path", create=True)
  • Parameters:
    • path – The path identifying the document to open.
    • create – (Optional) Whether to create the document if it doesn’t exist. Defaults to true.
  • Returns
    A MeshDocument instance tied to the specified path.
  • Description
    Sends a request to connect to (and possibly create) the document at the given path. If successful, returns a reference-counted MeshDocument that automatically sends local changes to the backend.

close(path)

await room.sync.close(path="my/path")
  • Parameters:
    • path – The path of the document to close.
  • Description
    Decrements the internal reference count for the document. If it reaches zero, the document is unregistered locally, and a request is sent to the server to disconnect. No further changes will be synced once closed.

sync(path, data)

await room.sync.sync(path="my/path", data=b"some bytes")
  • Parameters:
    • path – The path of the document to synchronize.
    • data – A Uint8Array (or equivalent) containing serialized changes.
  • Description
    Immediately sends sync data to the server. This method is useful if you have already encoded your changes and want to bypass the typical queued flow.

Additional Notes

  • Synchronized Changes
    Once a document is opened, any local changes you make in the associated MeshDocument are automatically queued for sending, thanks to sendChangesToBackend. You rarely need to call sync() manually unless you want to send raw data yourself.
  • Reference Counting
    The SyncClient internally tracks how many times a document is opened via the same path. Calling open multiple times for the same path returns the same underlying document. Only when all references are closed (via close()) will the client actually disconnect.
  • Error Handling
    If a request fails, the RoomClient may throw an error (like RoomServerException). Wrap calls in try/catch (or use .catch(...)) to handle them gracefully.
  • Concurrency and Performance
    • For high throughput, ensure that your RoomClient and server are configured for concurrent operations.
    • The SyncClient uses a StreamController internally to manage queued updates, sending them asynchronously to the server.
  • Extensibility
    You can extend the SyncClient for specialized document operations—such as partial updates, conflict resolution logic, or customized schema handling—by adding new methods or composing it with other classes that also use RoomClient.