Skip to main content
Glama
jonjoe

state-bridge-mcp

by jonjoe
README.md
# state-bridge-mcp

MCP server + client SDK for bridging [Zustand](https://github.com/pmndrs/zustand) stores over WebSocket. Lets any MCP-capable AI inspect and manipulate your app's live state.

## How it works

```
┌─────────────┐   WebSocket   ┌──────────────┐   stdio   ┌──────────┐
│  Your App   │──────────────▶│  MCP Server  │◀─────────▶│  Claude  │
│  (client)   │◀──────────────│  (server)    │           │          │
└─────────────┘               └──────────────┘           └──────────┘
```

The **server** runs as an MCP server (launched by Claude, Cursor, etc.) and opens a WebSocket port. Your **app** connects as a client and exposes its Zustand stores. The AI can then list stores, read state, write state, and call actions.

## Installation

```bash
npm install state-bridge-mcp
```

## Server Setup

Add to your `.mcp.json` (or equivalent MCP config):

```json
{
  "mcpServers": {
    "app-state": {
      "command": "npx",
      "args": ["state-bridge-mcp"]
    }
  }
}
```

### Custom port

```json
{
  "mcpServers": {
    "app-state": {
      "command": "npx",
      "args": ["state-bridge-mcp", "--port", "9000"]
    }
  }
}
```

Or via environment variable: `STATE_BRIDGE_PORT=9000`

Default port: `8098`

## Client Usage

```typescript
import { createStateBridge } from 'state-bridge-mcp/client';
import { useSessionStore, useAppStore } from './store';

const bridge = createStateBridge({
  stores: {
    session: useSessionStore,
    app: useAppStore,
  },
  url: 'ws://localhost:8098',
});

// Later, to tear down:
bridge.stop();
```

### React Native (Android emulator)

For Android emulator, use `10.0.2.2` to reach the host machine:

```typescript
const bridge = createStateBridge({
  stores: { session: useSessionStore, app: useAppStore },
  url: 'ws://10.0.2.2:8098',
});
```

## Client Config

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `stores` | `Record<string, StoreEntry>` | *required* | Named map of Zustand stores |
| `url` | `string` | `ws://localhost:8098` | WebSocket URL of the MCP server |
| `reconnectInterval` | `number` | `3000` | Auto-reconnect interval in ms. `0` to disable. |
| `onConnect` | `() => void` | — | Called when WebSocket opens |
| `onDisconnect` | `() => void` | — | Called when WebSocket closes |

## MCP Tools

Once connected, the AI has access to these tools:

| Tool | Description |
|------|-------------|
| `connection_status` | Check if a client app is connected |
| `list_stores` | List all stores and their top-level state keys |
| `get_state` | Read state from a store (supports dot-paths like `settings.theme`) |
| `set_state` | Write a value at a dot-path (supports arbitrary depth) |
| `call_action` | Invoke a store action function by name |

## Store Compatibility

Any object with `getState()` and `setState()` works. Zustand stores satisfy this out of the box:

```typescript
type StoreEntry = {
  getState: () => Record<string, unknown>;
  setState: (partial: Record<string, unknown>) => void;
};
```

## License

MIT

TDQS

A3.8/5.0

Scored across 5 tools

Disambiguation5/5

Each tool addresses a distinct concern: actions, connections, reading state, listing stores, and writing state. No overlap or ambiguity between tools.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case (e.g., get_state, list_stores), making them predictable and easy to understand.

Tool Count5/5

With 5 tools, the set is well-scoped for a state management bridge, covering all essential operations without unnecessary bloat.

Completeness4/5

The tools cover core state operations (list, get, set, actions) and a connection check. Missing subscription or store creation, but these are minor gaps for the intended purpose.

Maintenance

ActivityInactive
ResponsivenessNo issues