Skip to content

Commit 1988fa3

Browse files
committed
Complete multi-instance implementation with configuration updates
- Update changeset config with linked packages for version alignment - Add test command to CI workflow - Update shared types and package configurations - Clean up linting and project setup files
1 parent a18349d commit 1988fa3

23 files changed

Lines changed: 178 additions & 83 deletions

.changeset/config.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
"changelog": "@changesets/cli/changelog",
44
"commit": false,
55
"fixed": [],
6-
"linked": [],
6+
"linked": [
7+
["@mcp-pointer/server", "@mcp-pointer/shared", "@mcp-pointer/chrome-extension"]
8+
],
79
"access": "restricted",
810
"baseBranch": "main",
911
"updateInternalDependencies": "patch",

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module.exports = {
1919
},
2020
rules: {
2121
'class-methods-use-this': 'off',
22+
'no-await-in-loop': 'off',
2223
},
2324
overrides: [
2425
{

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ jobs:
3232
- name: Type check
3333
run: pnpm typecheck
3434

35+
- name: Run Tests
36+
run: pnpm test
37+
3538
- name: Build
3639
run: pnpm build
3740

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ dist/
44
.DS_Store
55

66
# TypeScript
7-
*.tsbuildinfo
7+
*.tsbuildinfo
8+
9+
# Test files
10+
packages/server/src/__tests__/tmp/

CONTRIBUTING.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,101 @@ packages/
142142
└── package.json
143143
```
144144

145+
## 🏗️ System Architecture
146+
147+
MCP Pointer uses a distributed architecture with multiple server instances and leader election for high availability:
148+
149+
```mermaid
150+
graph TB
151+
subgraph "Browser Environment"
152+
WEB[Web Page]
153+
CS[Content Script<br/>element-pointer.ts]
154+
BG[Background Worker<br/>background.ts]
155+
ES[ElementSenderService<br/>ReconnectingWebSocket]
156+
WEB -->|Option+Click| CS
157+
CS -->|Chrome Runtime API| BG
158+
BG -->|sendElement| ES
159+
end
160+
161+
subgraph "MCP Server Instances"
162+
subgraph "Instance 1 - Leader"
163+
WS1[WebSocketService<br/>✅ Port 7007]
164+
MCP1[MCPService]
165+
SS1[SharedState<br/>Read/Write]
166+
end
167+
168+
subgraph "Instance 2 - Follower"
169+
WS2[WebSocketService<br/>⏸️ Retrying...]
170+
MCP2[MCPService]
171+
SS2[SharedState<br/>Read Only]
172+
end
173+
end
174+
175+
subgraph "Shared Resources"
176+
PORT[Port 7007<br/>First to bind wins]
177+
FS[/tmp/mcp-pointer-shared-state.json]
178+
end
179+
180+
subgraph "AI Client"
181+
CC[Claude Code]
182+
end
183+
184+
ES -->|WebSocket| WS1
185+
WS1 -->|Owns| PORT
186+
WS2 -.->|Retry every 5s| PORT
187+
SS1 -->|Write| FS
188+
SS2 -->|Read| FS
189+
CC <-->|MCP Protocol| MCP1
190+
CC <-->|MCP Protocol| MCP2
191+
192+
classDef leader fill:#90EE90
193+
classDef follower fill:#FFE4B5
194+
195+
class WS1,SS1 leader
196+
class WS2,SS2 follower
197+
```
198+
199+
### How It Works
200+
201+
#### Server-Side (Multiple Instances)
202+
203+
1. **Leader Election:**
204+
- Multiple MCP server instances can run simultaneously
205+
- First instance to bind port 7007 becomes the leader
206+
- Other instances become followers and retry every 5 seconds
207+
- Automatic failover when leader crashes (~5 second recovery)
208+
209+
2. **State Management:**
210+
- Leader instance saves element data to `/tmp/mcp-pointer-shared-state.json`
211+
- All instances (leader and followers) can read shared state
212+
- MCP requests work on any instance using shared state
213+
214+
3. **Service Architecture:**
215+
- **WebSocketService**: Handles port-based leader election and WebSocket connections
216+
- **MCPService**: Provides MCP protocol implementation for AI tools
217+
- **SharedStateService**: Manages persistent element state via filesystem
218+
219+
#### Client-Side (Browser Extension)
220+
221+
1. **Connection Management (ElementSenderService):**
222+
- Uses ReconnectingWebSocket library for robust WebSocket connections
223+
- Exponential backoff: 1s min delay, 10s max delay, 1.5x grow factor
224+
- Maximum 5 retry attempts per connection
225+
- 5-second connection timeout
226+
- Automatic idle disconnection after 2 minutes of inactivity
227+
228+
2. **Element Selection Flow:**
229+
- Content script captures element data on Option+Click
230+
- Data sent to background worker via Chrome Runtime API
231+
- Background worker uses ElementSenderService to send via WebSocket
232+
- Connection status callbacks provide user feedback (CONNECTING → CONNECTED → SENDING → SENT)
233+
234+
3. **Resilience Features:**
235+
- Automatic reconnection during server restarts or leader changes
236+
- Port change handling (disconnects old, connects to new)
237+
- Connection status monitoring with detailed logging
238+
- Graceful error handling with status reporting
239+
145240
## 🛠 Development Workflow
146241

147242
### Chrome Extension Development

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"name": "mcp-pointer-monorepo",
3-
"version": "0.2.0",
43
"description": "👆 MCP Pointer - Point at DOM elements for AI analysis",
54
"private": true,
65
"packageManager": "pnpm@10.15.0",
@@ -9,6 +8,7 @@
98
"build": "pnpm -r run build",
109
"lint": "eslint . --ext .ts,.tsx",
1110
"lint:fix": "eslint . --ext .ts,.tsx --fix",
11+
"test": "cd packages/server && pnpm test",
1212
"typecheck": "tsc --noEmit",
1313
"changeset": "changeset",
1414
"version": "changeset version"

packages/chrome-extension/CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
# @mcp-pointer/chrome-extension
22

3+
## 0.3.0
4+
5+
### Patch Changes
6+
7+
- feat(server): Add multi-instance support with port-based leader election
8+
9+
- Implement port-based leader election for WebSocket server management
10+
- Add shared state persistence to filesystem (/tmp/mcp-pointer-shared-state.json)
11+
- Support multiple MCP server instances without port conflicts
12+
- Add automatic failover when leader instance crashes (~5 second recovery)
13+
- Refactor services into dedicated service layer (WebSocketService, MCPService, SharedStateService)
14+
- Add comprehensive test suite using Node.js built-in test runner
15+
- Add architecture documentation with Mermaid diagram to CONTRIBUTING.md
16+
- Rename WebSocketMessageType to PointerMessageType for better domain clarity
17+
- Add proper process cleanup handling on Ctrl+C and other signals
18+
- MCP service now runs independently on all instances (leader and followers)
19+
20+
Breaking changes: None - fully backwards compatible with single instance deployments
21+
22+
- Updated dependencies
23+
- @mcp-pointer/shared@0.3.0
24+
325
## 0.2.0
426

527
### Minor Changes

packages/chrome-extension/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@mcp-pointer/chrome-extension",
3-
"version": "0.2.0",
3+
"version": "0.3.0",
44
"private": true,
55
"scripts": {
66
"clean": "rm -rf dist",

packages/chrome-extension/src/background.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ConnectionStatus } from '@mcp-pointer/shared';
1+
import { ConnectionStatus } from '@mcp-pointer/shared/types';
22
import logger from './logger';
33
import { ElementSenderService } from './services/element-sender-service';
44
import { ConfigStorage } from './storage';

packages/chrome-extension/src/services/element-sender-service.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import ReconnectingWebSocket from 'reconnecting-websocket';
22
import {
3-
TargetedElement, WebSocketMessage, WebSocketMessageType, ConnectionStatus,
4-
} from '@mcp-pointer/shared';
3+
TargetedElement, PointerMessage, PointerMessageType, ConnectionStatus,
4+
} from '@mcp-pointer/shared/types';
55
import logger from '../logger';
66

77
export type StatusCallback = (status: ConnectionStatus, error?: string) => void;
@@ -44,8 +44,8 @@ export class ElementSenderService {
4444
// Now sending the element
4545
statusCallback?.(ConnectionStatus.SENDING);
4646

47-
const message: WebSocketMessage = {
48-
type: WebSocketMessageType.ELEMENT_SELECTED,
47+
const message: PointerMessage = {
48+
type: PointerMessageType.ELEMENT_SELECTED,
4949
data: element,
5050
timestamp: Date.now(),
5151
};

0 commit comments

Comments
 (0)