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

> Explore ChatMCP network sync for real-time LAN data synchronization. Learn how HTTP servers and REST endpoints enable peer data exchange on your local network.

- Repository: [刀刀/chatmcp](https://github.com/daodao97/chatmcp)
- Tags: internals
- Published: 2026-02-28

---

**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:

```dart
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:

```dart
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:

```dart
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:

```dart
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:

```dart
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.