SyncClient enables interacting with managed documents - MeshDocument and synchronizing local changes with all remote clients.


Overview

MeshDocuments enable your agents to share and synchronize information in real time across platforms. They resemble a web page’s structure but offer much greater flexibility. For example, if you want to generate website content with your agents, you can start with a simple HTML-compatible document format:

<html>
    <body>
        <p class="content"></p>
    </body>
</html>

A MeshDocument, like an HTML document, is composed of elements that can have child elements and attributes. However, MeshDocuments introduce additional features and capabilities that go beyond standard HTML or XML.

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 asyncio
from meshagent.api import RoomClient, websocket_protocol

async def main():
    room_name = "examples"
    participant_name = "example-participant"

    # connect to our room
    async with RoomClient(
        protocol=websocket_protocol(
            participant_name=participant_name,
            room_name=room_name, role="agent")) as room:

            # 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) 


if __name__ == '__main__':
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    asyncio.get_event_loop().run_until_complete(main())
    

API Methods

Below is an example of how you might use the SyncClient in your code:


# Create a MeshDocument on the server
await room.sync.create("my/path", json={"initial": "data"})

# Open a document (creates if not found)
doc = await room.sync.open("my/path", create=True)

# Close the document
await room.sync.close("my/path")

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?)

await room.sync.create(path="my/path", json={"key": "value"})
  • Parameters:
    • path – The path where the document will be created on the server.
    • schema – A MeshSchema describing the structure of the document.
    • 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 with a specified schema.

open(path, create = true)

doc = await sync_client.open("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("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("my/path", 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.