@pacerelle/mcp-server 0.1.0-alpha.2 → 0.1.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -51
- package/dist/bridge.d.ts +129 -15
- package/dist/bridge.js +199 -74
- package/dist/bridge.js.map +1 -1
- package/dist/cli.js +104 -31
- package/dist/cli.js.map +1 -1
- package/dist/server.d.ts +2 -1
- package/dist/server.js +43 -8
- package/dist/server.js.map +1 -1
- package/dist/storage.d.ts +28 -0
- package/dist/storage.js +183 -0
- package/dist/storage.js.map +1 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,41 +1,14 @@
|
|
|
1
1
|
# Pacerelle MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A local encrypted inbox for an assistant running in an MCP host. Pacerelle
|
|
4
|
+
transports messages; your host remains responsible for starting the assistant,
|
|
5
|
+
running tools, applying permissions, and deciding when its processing loop ends.
|
|
6
|
+
Connecting this server alone does not wake Claude Desktop, Cursor, Codex, or Zed.
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
as Claude Desktop, Cursor, Zed, or other MCP hosts.
|
|
8
|
+
## Connect and verify a real reply
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
npx -y @pacerelle/mcp-server
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Configuration
|
|
15
|
-
|
|
16
|
-
Create an agent in Pacerelle, copy its id and token, then expose them to the MCP
|
|
17
|
-
process:
|
|
18
|
-
|
|
19
|
-
```env
|
|
20
|
-
PACERELLE_AGENT_ID=agent_...
|
|
21
|
-
PACERELLE_AGENT_TOKEN=...
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
The published package connects to `https://api.pacerelle.com` by default. For
|
|
25
|
-
local development, set:
|
|
26
|
-
|
|
27
|
-
```env
|
|
28
|
-
PACERELLE_BASE_URL=http://localhost:8080
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Optional:
|
|
32
|
-
|
|
33
|
-
```env
|
|
34
|
-
PACERELLE_WS_URL=ws://localhost:8080
|
|
35
|
-
PACERELLE_STORE_ROOT=.pacerelle/mcp
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Claude Desktop Example
|
|
10
|
+
Create or resume an agent in Pacerelle. Copy both its ID and authentication token
|
|
11
|
+
into the host's local MCP configuration:
|
|
39
12
|
|
|
40
13
|
```json
|
|
41
14
|
{
|
|
@@ -52,31 +25,132 @@ PACERELLE_STORE_ROOT=.pacerelle/mcp
|
|
|
52
25
|
}
|
|
53
26
|
```
|
|
54
27
|
|
|
55
|
-
|
|
28
|
+
Use the configuration format supported by your host. Then start a conversation
|
|
29
|
+
with its assistant and select the `start_pacerelle` prompt, or ask:
|
|
56
30
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
31
|
+
> Start or resume my Pacerelle work. Read get_status and get_checkpoints, then
|
|
32
|
+
> wait_for_messages without a cursor. Handle each authorized request, reply
|
|
33
|
+
> using its sourceMessageId, and mark_processed after handling it. Save a
|
|
34
|
+
> checkpoint before an interruption. Keep listening while this host session can
|
|
35
|
+
> continue; tell me if listening stops or needs my intervention.
|
|
60
36
|
|
|
61
|
-
|
|
37
|
+
Send a test message from Pacerelle and verify a readable reply before leaving
|
|
38
|
+
your computer. Keep the computer, host, and assistant loop running. Hosts may
|
|
39
|
+
stop at a turn limit, require approval, or not support continuous waiting. For
|
|
40
|
+
unattended execution, use the SDK with your own continuously running agent.
|
|
41
|
+
MCP resource notifications or sampling are not a universal assistant wake-up API.
|
|
62
42
|
|
|
63
|
-
|
|
64
|
-
- `
|
|
43
|
+
These instructions describe the source implementation. To test an unreleased
|
|
44
|
+
checkout, build `sdk-js` and `mcp-server`, then configure the host with `node`
|
|
45
|
+
and the absolute path to `mcp-server/dist/cli.js` instead of `npx`.
|
|
65
46
|
|
|
66
|
-
##
|
|
47
|
+
## Tools
|
|
67
48
|
|
|
68
|
-
|
|
49
|
+
| Tool | Meaning |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| `get_status` | Relay state, number of pending requests, and uncertain outgoing sends. |
|
|
52
|
+
| `wait_for_messages` | Return pending requests, or wait 0–30 seconds for a matching request. |
|
|
53
|
+
| `send_message` | Submit an encrypted reply; use `sourceMessageId` from the incoming request. |
|
|
54
|
+
| `send_widget` | Submit a `widget.standard` v1 payload to the same source target. |
|
|
55
|
+
| `mark_processed` | Record that a received request has been handled. Reading does not do this. |
|
|
56
|
+
| `get_history` | Recent locally retained incoming requests and outgoing replies. |
|
|
57
|
+
| `save_checkpoint` | Save summary, next action, and optional host session label for a received request. |
|
|
58
|
+
| `get_checkpoints` | Retrieve active saved work; optionally include completed work. |
|
|
59
|
+
|
|
60
|
+
`wait_for_messages` accepts `conversationId`, `afterCursor`, `limit` (1–50), and
|
|
61
|
+
`timeoutMs` (0–30000, default 25000). Its result includes `messages`, `cursor`,
|
|
62
|
+
`has_more`, and `connection`. Canceling the MCP request cancels its wait. Reading,
|
|
63
|
+
waiting, and sending replies never mark an incoming request processed. Use a
|
|
64
|
+
single processing loop for each agent.
|
|
65
|
+
|
|
66
|
+
A cursor skips earlier requests, including unfinished ones. Omit it when
|
|
67
|
+
resuming a session; use it only to paginate a batch that your loop has tracked.
|
|
68
|
+
Pending requests are retained until explicitly processed. The local history
|
|
69
|
+
retains 50 recent processed messages, 50 outgoing replies, and source messages
|
|
70
|
+
needed by retained checkpoints. Duplicate IDs are ignored while retained.
|
|
71
|
+
|
|
72
|
+
Replies using `sourceMessageId` stay attached to the original conversation,
|
|
73
|
+
device, or group even if another message arrives meanwhile. Unknown sources and
|
|
74
|
+
conflicting targets are rejected. Without a source, the conversation target must
|
|
75
|
+
be unambiguous; explicit `conversationId` and `to` remain available for advanced
|
|
76
|
+
use. `conversation://current` is merely the latest observation, not ownership of
|
|
77
|
+
an assistant session. `widget://pending` is the latest unprocessed widget response.
|
|
78
|
+
|
|
79
|
+
## Resume in another assistant session
|
|
80
|
+
|
|
81
|
+
Before an interruption, the host can call:
|
|
69
82
|
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"sourceMessageId": "received-message-id",
|
|
86
|
+
"summary": "Draft complete. Figures checked. Waiting for the user's approval.",
|
|
87
|
+
"nextAction": "Read the approval response before publishing the report.",
|
|
88
|
+
"hostSessionLabel": "Report preparation",
|
|
89
|
+
"status": "active"
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The next session calls `get_checkpoints`, `wait_for_messages` without a cursor,
|
|
94
|
+
and `get_history` for the selected conversation. It receives the saved working
|
|
95
|
+
context and remaining requests without asking you to copy a transcript. Update
|
|
96
|
+
the checkpoint with `status: "completed"` when its work ends. Checkpoint updates
|
|
97
|
+
do not automatically mark messages processed, and processing a message does not
|
|
98
|
+
automatically complete a longer task.
|
|
99
|
+
|
|
100
|
+
This preserves context explicitly saved by the host. It does not access private
|
|
101
|
+
Codex, Claude, or Cursor transcripts, and cannot recover unsaved model reasoning.
|
|
102
|
+
The host's permissions and approval rules continue to apply after resumption.
|
|
103
|
+
|
|
104
|
+
## Local state and connection recovery
|
|
105
|
+
|
|
106
|
+
The default directory is `~/.pacerelle/mcp`; override it with
|
|
107
|
+
`PACERELLE_STORE_ROOT`. Use the same directory when moving between host sessions.
|
|
108
|
+
Each agent and gateway has its own AES-256-GCM encrypted file containing the
|
|
109
|
+
Signal snapshot, inbox, replies, and checkpoints. Writes use a temporary file,
|
|
110
|
+
flush, and atomic replacement. A separate random local key is needed to decrypt
|
|
111
|
+
it. Back up the key and encrypted file together. Do not copy live state between
|
|
112
|
+
two running processes.
|
|
113
|
+
|
|
114
|
+
Directory mode is `0700` and files use `0600` on POSIX. On Windows, filesystem
|
|
115
|
+
protection depends on the account's directory ACLs; use your private user profile
|
|
116
|
+
directory. Encryption with a locally stored key does not protect against another
|
|
117
|
+
process already running as your account. Tokens are not saved by this server.
|
|
118
|
+
|
|
119
|
+
Only one local MCP process can own an agent's state on a gateway. Close its
|
|
120
|
+
Pacerelle server in the old host before opening it in another. Normal process
|
|
121
|
+
crash locks are reclaimed when the recorded PID no longer exists. A crash during
|
|
122
|
+
the brief lock-opening operation can leave an `.opening` gate; the startup error
|
|
123
|
+
identifies it, and it must only be removed after checking no bridge is starting.
|
|
124
|
+
This local lock does not prevent an independent process on another computer
|
|
125
|
+
from using the same agent credentials.
|
|
126
|
+
|
|
127
|
+
The CLI reconnects with increasing delays (up to 30 seconds), creating a fresh
|
|
128
|
+
SDK client with the latest saved Signal state. The old production snapshot is
|
|
129
|
+
migrated into encrypted storage before its plaintext file is removed. A legacy
|
|
130
|
+
snapshot from an unknown gateway requires gateway verification instead of
|
|
131
|
+
silently creating a new identity. A lost key or corrupt state stops startup.
|
|
132
|
+
|
|
133
|
+
An outgoing `submitted` result means SDK submission, not verified execution or
|
|
134
|
+
recipient display. If a send or interruption leaves the outcome uncertain,
|
|
135
|
+
`get_status.uncertain_sends` and history expose it. Check the actual outcome
|
|
136
|
+
before repeating consequential work. The bridge does not guarantee exactly-once
|
|
137
|
+
external actions or atomicity between Signal decryption and inbox persistence.
|
|
138
|
+
|
|
139
|
+
## Local development
|
|
140
|
+
|
|
141
|
+
```env
|
|
142
|
+
PACERELLE_AGENT_ID=agent_...
|
|
143
|
+
PACERELLE_AGENT_TOKEN=...
|
|
144
|
+
PACERELLE_BASE_URL=http://localhost:8080
|
|
145
|
+
PACERELLE_STORE_ROOT=/absolute/private/directory
|
|
74
146
|
```
|
|
75
147
|
|
|
76
|
-
|
|
148
|
+
`PACERELLE_WS_URL` optionally overrides the websocket URL. Corresponding CLI
|
|
149
|
+
flags are `--agent-id`, `--token`, `--base-url`, `--ws-url`, and `--store-root`.
|
|
150
|
+
The default gateway is `https://api.pacerelle.com`. Keep E2EE enabled;
|
|
151
|
+
`--no-e2ee` exists for local transport debugging only.
|
|
77
152
|
|
|
78
153
|
```bash
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
--ws-url "ws://localhost:8080"
|
|
154
|
+
npm run build --workspace sdk-js
|
|
155
|
+
npm test --workspace mcp-server
|
|
82
156
|
```
|
package/dist/bridge.d.ts
CHANGED
|
@@ -3,36 +3,150 @@ export interface MessageSender {
|
|
|
3
3
|
sendMessage(options: {
|
|
4
4
|
conversationId: string;
|
|
5
5
|
to: string;
|
|
6
|
+
toDeviceId?: string;
|
|
6
7
|
text: string;
|
|
7
8
|
replyToMessageId?: string;
|
|
8
9
|
}): Promise<void>;
|
|
9
10
|
}
|
|
11
|
+
export interface InboxMessage {
|
|
12
|
+
id: string;
|
|
13
|
+
direction: "incoming";
|
|
14
|
+
sequence: number;
|
|
15
|
+
conversation_id: string;
|
|
16
|
+
from: string;
|
|
17
|
+
from_device_id?: string;
|
|
18
|
+
reply_target: string;
|
|
19
|
+
text: string;
|
|
20
|
+
encrypted: boolean;
|
|
21
|
+
reply_to_message_id: string | null;
|
|
22
|
+
attachments: AgentMessage["attachments"];
|
|
23
|
+
widget_response: AgentMessage["widgetResponse"] | null;
|
|
24
|
+
widget_update: AgentMessage["widgetUpdate"] | null;
|
|
25
|
+
recorded_at: string;
|
|
26
|
+
processed_at: string | null;
|
|
27
|
+
processing_note?: string;
|
|
28
|
+
}
|
|
29
|
+
interface ReplyRecord {
|
|
30
|
+
local_id: string;
|
|
31
|
+
direction: "outgoing";
|
|
32
|
+
conversation_id: string;
|
|
33
|
+
to: string;
|
|
34
|
+
text: string;
|
|
35
|
+
source_message_id?: string;
|
|
36
|
+
recorded_at: string;
|
|
37
|
+
delivery_status: "sending" | "submitted" | "unknown";
|
|
38
|
+
}
|
|
39
|
+
export interface BridgeState {
|
|
40
|
+
version: 1;
|
|
41
|
+
epoch: string;
|
|
42
|
+
nextSequence: number;
|
|
43
|
+
inbox: InboxMessage[];
|
|
44
|
+
replies: ReplyRecord[];
|
|
45
|
+
checkpoints: WorkCheckpoint[];
|
|
46
|
+
}
|
|
47
|
+
export interface WorkCheckpoint {
|
|
48
|
+
source_message_id: string;
|
|
49
|
+
conversation_id: string;
|
|
50
|
+
summary: string;
|
|
51
|
+
next_action: string;
|
|
52
|
+
host_session_label?: string;
|
|
53
|
+
status: "active" | "completed";
|
|
54
|
+
updated_at: string;
|
|
55
|
+
}
|
|
56
|
+
interface ReplyTarget {
|
|
57
|
+
sourceMessageId?: string;
|
|
58
|
+
conversationId?: string;
|
|
59
|
+
to?: string;
|
|
60
|
+
}
|
|
61
|
+
export interface WaitOptions {
|
|
62
|
+
conversationId?: string;
|
|
63
|
+
afterCursor?: string;
|
|
64
|
+
limit?: number;
|
|
65
|
+
timeoutMs?: number;
|
|
66
|
+
signal?: AbortSignal;
|
|
67
|
+
}
|
|
68
|
+
export type ConnectionState = "connecting" | "connected" | "reconnecting" | "disconnected" | "stopped";
|
|
69
|
+
/** Reading/waiting never acknowledges application work. Persistence precedes visibility. */
|
|
10
70
|
export declare class PacerelleMcpBridge {
|
|
11
71
|
private readonly client;
|
|
12
72
|
private readonly historyLimit;
|
|
13
|
-
private readonly
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
-
|
|
73
|
+
private readonly options;
|
|
74
|
+
private state;
|
|
75
|
+
private connection;
|
|
76
|
+
private readonly listeners;
|
|
77
|
+
constructor(client: MessageSender, historyLimit?: number, options?: {
|
|
78
|
+
state?: BridgeState;
|
|
79
|
+
onStateChange?: (state: BridgeState) => void;
|
|
80
|
+
});
|
|
17
81
|
recordMessage(message: AgentMessage): void;
|
|
18
|
-
|
|
19
|
-
|
|
82
|
+
setConnectionState(state: ConnectionState): void;
|
|
83
|
+
getStatus(): {
|
|
84
|
+
connection: ConnectionState;
|
|
85
|
+
pending_count: number;
|
|
86
|
+
cursor: string;
|
|
87
|
+
host_action: string;
|
|
88
|
+
uncertain_sends: ReplyRecord[];
|
|
89
|
+
};
|
|
90
|
+
waitForMessages(args?: WaitOptions): Promise<{
|
|
91
|
+
status: string;
|
|
92
|
+
messages: InboxMessage[];
|
|
93
|
+
cursor: string;
|
|
94
|
+
has_more: boolean;
|
|
95
|
+
connection: ConnectionState;
|
|
96
|
+
}>;
|
|
97
|
+
markProcessed(args: {
|
|
98
|
+
messageId: string;
|
|
99
|
+
note?: string;
|
|
100
|
+
}): {
|
|
101
|
+
ok: boolean;
|
|
102
|
+
message_id: string;
|
|
103
|
+
pending_count: number;
|
|
104
|
+
};
|
|
105
|
+
saveCheckpoint(args: {
|
|
106
|
+
sourceMessageId: string;
|
|
107
|
+
summary: string;
|
|
108
|
+
nextAction: string;
|
|
109
|
+
hostSessionLabel?: string;
|
|
110
|
+
status?: "active" | "completed";
|
|
111
|
+
}): WorkCheckpoint;
|
|
112
|
+
getCheckpoints(args?: {
|
|
20
113
|
conversationId?: string;
|
|
21
|
-
|
|
114
|
+
includeCompleted?: boolean;
|
|
115
|
+
}): WorkCheckpoint[];
|
|
116
|
+
sendMessage(args: ReplyTarget & {
|
|
117
|
+
text: string;
|
|
22
118
|
replyToMessageId?: string;
|
|
23
|
-
}): Promise<
|
|
24
|
-
|
|
119
|
+
}): Promise<{
|
|
120
|
+
ok: boolean;
|
|
121
|
+
conversation_id: string;
|
|
122
|
+
to: string;
|
|
123
|
+
delivery_status: string;
|
|
124
|
+
}>;
|
|
125
|
+
sendWidget(args: ReplyTarget & {
|
|
25
126
|
widgetJson: string | Record<string, unknown>;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
127
|
+
}): Promise<{
|
|
128
|
+
ok: boolean;
|
|
129
|
+
widget_id: unknown;
|
|
130
|
+
kind: unknown;
|
|
131
|
+
conversation_id: string;
|
|
132
|
+
to: string;
|
|
133
|
+
delivery_status: string;
|
|
134
|
+
}>;
|
|
29
135
|
getHistory(args: {
|
|
30
136
|
conversationId?: string;
|
|
31
137
|
limit?: number;
|
|
32
|
-
}):
|
|
33
|
-
currentConversation():
|
|
138
|
+
}): (InboxMessage | ReplyRecord)[];
|
|
139
|
+
currentConversation(): InboxMessage | {
|
|
140
|
+
status: string;
|
|
141
|
+
};
|
|
34
142
|
pendingWidgetResponse(): Record<string, unknown>;
|
|
35
143
|
attach(client: AgentGatewayClient): void;
|
|
36
|
-
private
|
|
144
|
+
private commit;
|
|
145
|
+
private notify;
|
|
146
|
+
private cursor;
|
|
147
|
+
private parseCursor;
|
|
148
|
+
private resolveTarget;
|
|
149
|
+
private submitReply;
|
|
37
150
|
private parseWidget;
|
|
38
151
|
}
|
|
152
|
+
export {};
|