How the Network Sync Feature Works in ChatMCP for LAN Data Synchronization

The network sync feature in ChatMCP enables real-time LAN data synchronization by running an HTTP server on each device that exports chat history, settings, and MCP configurations via REST endpoints, allowing peers to discover and exchange data across the local network using a singleton NetworkSyncService.

ChatMCP, an open-source MCP (Model Context Protocol) client, includes a built-in network sync capability that transforms any device on the same Wi-Fi network into a sync server. This allows users to seamlessly migrate chat data, application settings, and custom MCP server configurations between devices without cloud dependencies.

Core Architecture of the Network Sync Service

The NetworkSyncService Singleton

The entire synchronization system is orchestrated by NetworkSyncService, implemented as a singleton in lib/services/network_sync_service.dart. This service manages both server-side HTTP operations and client-side sync logic, maintaining state through callbacks like onServerStateChanged to update the UI when the server starts or stops.

HTTP Server Implementation with Shelf

The sync server leverages the shelf package ecosystem (shelf, shelf_router, shelf_cors_headers) to handle HTTP requests. When startServer() is invoked, the service binds to 0.0.0.0:<port> (default 8080) using io.serve(handler, InternetAddress.anyIPv4, _port), making the device discoverable on all network interfaces.

The server exposes four critical endpoints:

  • /health – Returns server status for discovery validation
  • /info – Supplies device metadata (hostname, OS, version) via _handleDeviceInfo()
  • /export – Serializes all local data via _handleExportData()
  • /import – Accepts and applies remote data via _handleImportData()

Data Export and Import Endpoints

The /export endpoint returns a comprehensive JSON payload containing:

  • SQLite chat tables (chat, chat_message) from the local database
  • SharedPreferences settings (user preferences and configuration)
  • MCP server definitions and connection parameters
  • Device metadata for identification

When /import receives this payload, _handleImportData() writes the data to the local SQLite store and SharedPreferences, then triggers _reinitializeProviders() to refresh the application state.

LAN Discovery Mechanism

ChatMCP discovers peers using a subnet scanning approach. The discoverServers() method scans the local network range (e.g., 192.168.1.xxx) targeting IPs 100–110, probing each address with a /health request to identify active sync servers.

Once discovered, servers are returned as complete URLs (http://<ip>:<port>) and presented in the UI for user selection.

Synchronization Workflows

Pulling Data with syncFromRemote()

The syncFromRemote(serverUrl) method implements the client-side download logic. It performs a health check on the target server, downloads the JSON export via the /export endpoint, and executes _importAllData() to merge remote data into the local database. Following a successful import, the service calls ProviderManager through _reinitializeProviders() to reload settings, chat histories, and MCP server definitions instantly.

Pushing Data with pushToRemote()

Conversely, pushToRemote(serverUrl) exports the local database and settings into a JSON blob, then POSTs this data to the remote device's /import endpoint. This enables one-way synchronization from the current device to a peer, useful for backup or migration scenarios.

Provider Re-initialization After Import

After any data import operation, the service must reconcile the UI with the new underlying data. The _reinitializeProviders() function signals ProviderManager (defined in lib/provider/provider_manager.dart) to reload all stateful components, ensuring that chat lists, settings panels, and MCP server configurations reflect the synchronized data without requiring an application restart.

Code Examples for Implementing LAN Sync

Starting the Sync Server

To activate the server mode and begin accepting connections:

await NetworkSyncService().startServer(port: 8080);

This binds to the device's Wi-Fi address (retrieved via _getLocalIPAddress()) and announces the server state through onServerStateChanged callbacks.

Discovering Peers on the Network

Scan the local subnet for available sync servers:

final servers = await NetworkSyncService().discoverServers();
print('Found servers: $servers');

This returns a list of reachable URLs like http://192.168.1.101:8080 that can be presented to the user for selection.

Downloading Data from a Remote Device

Pull all chat history and settings from a discovered server:

await NetworkSyncService().syncFromRemote('http://192.168.1.101:8080');

This executes the full download-and-import pipeline, including database writes and provider re-initialization.

Uploading Data to a Remote Device

Push local configuration and chat data to another device:

await NetworkSyncService().pushToRemote('http://192.168.1.102:8080');

This serializes the local state and transmits it to the target server's /import handler.

Stopping the Server

To gracefully terminate the HTTP listener:

await NetworkSyncService().stopServer();

This closes the socket and broadcasts onServerStateChanged(false) to update the UI.

Summary

  • NetworkSyncService in lib/services/network_sync_service.dart acts as both server and client for LAN synchronization
  • The shelf HTTP server exposes REST endpoints for health checks, device info, data export, and data import
  • Subnet scanning (IPs 100-110) discovers peers automatically without requiring manual IP entry
  • syncFromRemote() downloads and imports remote data, while pushToRemote() uploads local state
  • _reinitializeProviders() ensures the UI reflects synchronized data immediately by reloading ProviderManager state
  • The system synchronizes SQLite chat tables, SharedPreferences settings, and MCP server configurations across devices

Frequently Asked Questions

How does ChatMCP discover other devices on the same network?

ChatMCP scans the local subnet (typically 192.168.1.xxx) by iterating through IP addresses 100–110 and sending /health requests to each. Active servers respond with a 200 status, allowing the discoverServers() method to compile a list of available sync endpoints for the user to select.

What data is included when exporting chat data via the network sync feature?

The export includes three primary data categories: SQLite database tables (chat and chat_message storing conversation history), SharedPreferences settings (user preferences and app configuration), and MCP server definitions (connection parameters and custom server setups). This comprehensive export ensures complete state migration between devices.

Can I sync data from an older version of ChatMCP to a newer version?

While the NetworkSyncService handles data import/export agnostically, compatibility depends on database schema changes in lib/dao/init_db.dart. If the SQLite schema differs between versions, the _importAllData() method may need to handle migration logic. Always ensure both devices run compatible versions for seamless LAN data synchronization.

Is the network sync feature secure for sensitive chat data?

The current implementation uses unencrypted HTTP over the local network. According to the source code in lib/services/network_sync_service.dart, there is no TLS encryption or authentication layer on the endpoints. Users should only enable the sync server on trusted private networks, as the /export and /import endpoints expose full application state without access controls.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →