@paigy/mcp 0.40.0 → 0.40.2
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 +43 -30
- package/dist/{chunk-FYH6FW2D.js → chunk-647HS22P.js} +6 -91
- package/dist/{chunk-BOLKVO6W.js → chunk-BUTCP2ED.js} +1 -1
- package/dist/{chunk-DM6YPBQH.js → chunk-FFI3AIZE.js} +703 -644
- package/dist/{chunk-CWGZVTPZ.js → chunk-YJEJZT56.js} +832 -1108
- package/dist/enable.js +3 -3
- package/dist/index.js +11 -153
- package/dist/listen.js +34 -82
- package/dist/onboard.js +4 -4
- package/dist/slot.js +1 -1
- package/dist/statusline.js +4 -2
- package/package.json +12 -13
- package/dist/chunk-2SWUY5WK.js +0 -57
package/README.md
CHANGED
|
@@ -116,14 +116,24 @@ Then pair: `PAIGY_AGENT=gemini npx -p @paigy/mcp@latest paigy-mcp-onboard`.
|
|
|
116
116
|
|
|
117
117
|
## Tools
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
-
|
|
126
|
-
-
|
|
119
|
+
One catalog, on every transport (2026-09-11): the agent tools are `AGENT_TOOLS` in
|
|
120
|
+
`packages/schema/src/tools.ts` — contact, check_replies, get_thread, search_threads,
|
|
121
|
+
create_goal, claim_goal, get_goal, update_goal — and both this server and the hosted MCP
|
|
122
|
+
(`apps/api/src/mcp`) publish that list and dispatch it through the SDK's one `runTool`.
|
|
123
|
+
This server adds only onboard, pair and unpair, the tools that mint and delete the token
|
|
124
|
+
file this machine holds. See the [SDK contract](../../packages/sdk/README.md) for the
|
|
125
|
+
single-Goal start shape. Notification returns immediately. Call holds one cancellable
|
|
126
|
+
~45-second window here (the hosted transport returns after one read); continue with
|
|
127
|
+
contact({deliveryId}) without sending another ask. Durable Entries and accepted answers can
|
|
128
|
+
repeat on reads; check_replies lists every open Delivery addressed to you (a request the
|
|
129
|
+
user started toward you, an answer relayed to something you asked, a handoff) without
|
|
130
|
+
consuming any of it, and claim_goal is the catch-up for your own Goals. Bookkeeping ids
|
|
131
|
+
(`operationId`, `idempotencyKey`) are minted here, never asked of the model. Retired
|
|
132
|
+
reply-lease/ACK/work/callback tools and hidden notification aliases are rejected.
|
|
133
|
+
|
|
134
|
+
The standalone listener below is still a **legacy host and target-release blocker**.
|
|
135
|
+
Its old reply intake must migrate before enabling it against the target runtime; it is
|
|
136
|
+
not a compatibility fallback for target MCP tools.
|
|
127
137
|
|
|
128
138
|
## Configuration
|
|
129
139
|
|
|
@@ -135,42 +145,48 @@ Then pair: `PAIGY_AGENT=gemini npx -p @paigy/mcp@latest paigy-mcp-onboard`.
|
|
|
135
145
|
no extra setup. Two edges: with `HTTP_PROXY` set, an `http://localhost` backend
|
|
136
146
|
proxies too unless `NO_PROXY=localhost` (hostname entries work; CIDR ranges are
|
|
137
147
|
ignored), and `paigy-listen`'s realtime wake channel is a websocket that doesn't
|
|
138
|
-
proxy — its REST
|
|
148
|
+
proxy — its REST reads do.
|
|
139
149
|
|
|
140
150
|
## Wake any harness (`paigy-listen`)
|
|
141
151
|
|
|
142
152
|
`paigy-listen` is the self-hosted push daemon: it subscribes to this connection's
|
|
143
|
-
wake channel and
|
|
144
|
-
`paigy-listen --install` (launchd on macOS, systemd user unit on Linux;
|
|
145
|
-
removes it).
|
|
153
|
+
wake channel and reads what is waiting on every nudge. Keep it alive past the terminal
|
|
154
|
+
with `paigy-listen --install` (launchd on macOS, systemd user unit on Linux;
|
|
155
|
+
`--uninstall` removes it).
|
|
156
|
+
|
|
157
|
+
The read is `check_replies` — the open Deliveries addressed to this agent — and it
|
|
158
|
+
**consumes nothing**: no lease, no acknowledgement, and the same read twice returns the
|
|
159
|
+
same list. So the daemon hands the launcher what it found and stops there; claiming is
|
|
160
|
+
the agent's own first act (`claim_goal`), because the daemon shares this machine's
|
|
161
|
+
token with the agent it launches and a second claimer on one identity eats the first
|
|
162
|
+
one's claim.
|
|
146
163
|
|
|
147
164
|
To launch an agent — any agent, not just Claude — when work arrives, set
|
|
148
|
-
`PAIGY_ON_WAKE` to a command before `--install
|
|
149
|
-
work, so the launcher reads it from the environment rather than calling
|
|
150
|
-
`check_replies` again:
|
|
165
|
+
`PAIGY_ON_WAKE` to a command before `--install`:
|
|
151
166
|
|
|
152
167
|
| Variable | What it holds |
|
|
153
168
|
| --- | --- |
|
|
154
|
-
| `PAIGY_WORK` | the
|
|
155
|
-
| `PAIGY_EVENT` | the wake that caused this run — `boot`, `wake:reply`, `
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
159
|
-
| `
|
|
169
|
+
| `PAIGY_WORK` | everything the wake found, as JSON (`deliveries`, `goal`) |
|
|
170
|
+
| `PAIGY_EVENT` | the wake that caused this run — `boot`, `wake:reply`, `wake:request`… |
|
|
171
|
+
| `PAIGY_PARENT_ID` | the thread to continue on (`PAIGY_THREAD_ID` is the same value, for existing scripts) |
|
|
172
|
+
| `PAIGY_DELIVERY_ID` | the Delivery being acted on — `contact({deliveryId})` rereads it |
|
|
173
|
+
| `PAIGY_GOAL_ID` | the Goal it belongs to — `claim_goal` it **first** |
|
|
174
|
+
| `PAIGY_CONTEXT_THREAD_ID` | a past conversation this Goal names — `get_thread` it **first** |
|
|
175
|
+
| `PAIGY_TEXT` | what the person said, in prose |
|
|
160
176
|
|
|
161
177
|
Hand `$PAIGY_WORK` to a harness that can read JSON and decide for itself; use the
|
|
162
178
|
scalars for a plain shell launcher that shouldn't need `jq`. They describe **one**
|
|
163
|
-
item — the oldest
|
|
164
|
-
|
|
165
|
-
|
|
179
|
+
item — the oldest open Delivery — because the contract is one thread at a time. An
|
|
180
|
+
absent fact is unset rather than empty, so `${PAIGY_CONTEXT_THREAD_ID:-}`
|
|
181
|
+
distinguishes "no seed" from "seeded with nothing".
|
|
166
182
|
|
|
167
183
|
```sh
|
|
168
184
|
# Claude Code — hand it everything and let it plan:
|
|
169
|
-
PAIGY_ON_WAKE='claude -p "Handle the Paigy work in $PAIGY_WORK — rehydrate threads you do not recognize via get_thread
|
|
185
|
+
PAIGY_ON_WAKE='claude -p "Handle the Paigy work in $PAIGY_WORK — claim_goal first, and rehydrate threads you do not recognize via get_thread."' \
|
|
170
186
|
npx -y -p @paigy/mcp paigy-listen --install
|
|
171
187
|
|
|
172
188
|
# Codex (or any CLI harness) — the scalars are enough for a one-liner:
|
|
173
|
-
PAIGY_ON_WAKE='codex exec "
|
|
189
|
+
PAIGY_ON_WAKE='codex exec "Claim Paigy goal $PAIGY_GOAL_ID and continue thread $PAIGY_THREAD_ID. The user said: $PAIGY_TEXT. Reply with contact."' \
|
|
174
190
|
npx -y -p @paigy/mcp paigy-listen --install
|
|
175
191
|
```
|
|
176
192
|
|
|
@@ -184,10 +200,7 @@ script and branch on `$PAIGY_EVENT` there:
|
|
|
184
200
|
seed=""
|
|
185
201
|
[ -n "${PAIGY_CONTEXT_THREAD_ID:-}" ] && seed="It continues thread $PAIGY_CONTEXT_THREAD_ID — get_thread that first."
|
|
186
202
|
|
|
187
|
-
|
|
188
|
-
cron:callback*) codex exec "You owe the user a callback on thread $PAIGY_THREAD_ID: $PAIGY_TEXT. Deliver it with contact." ;;
|
|
189
|
-
*) codex exec "Paigy thread $PAIGY_THREAD_ID. The user said: $PAIGY_TEXT. $seed Reply with contact when done." ;;
|
|
190
|
-
esac
|
|
203
|
+
codex exec "Paigy goal $PAIGY_GOAL_ID on thread $PAIGY_THREAD_ID. claim_goal it first. The user said: $PAIGY_TEXT. $seed Reply with contact when done."
|
|
191
204
|
```
|
|
192
205
|
|
|
193
206
|
## Statusline (Claude Code)
|
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
CONTACT_DESCRIPTION,
|
|
4
|
-
CONTACT_SCHEMA,
|
|
5
|
-
CREATE_GOAL_DESCRIPTION,
|
|
6
|
-
CreateGoalSchema,
|
|
7
|
-
HandoffSchema,
|
|
8
|
-
SCHEDULE_CALLBACK_DESCRIPTION,
|
|
9
|
-
SEARCH_THREADS_DESCRIPTION,
|
|
10
|
-
SET_WORK_STATE_DESCRIPTION,
|
|
11
|
-
ScheduleCallbackSchema,
|
|
12
|
-
SetTaskStateSchema,
|
|
13
|
-
SetWorkStateSchema,
|
|
2
|
+
AGENT_TOOLS,
|
|
14
3
|
mcpInputSchema,
|
|
15
4
|
serverInstructions
|
|
16
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-FFI3AIZE.js";
|
|
17
6
|
import {
|
|
18
7
|
AGENT_NAME
|
|
19
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-YJEJZT56.js";
|
|
20
9
|
|
|
21
10
|
// src/toolset.ts
|
|
22
11
|
var ONBOARD_DESCRIPTION = "Get this agent talking to Paigy \u2014 call it FIRST, before contact, and any time you're unsure who you are. One call, and it does whatever the situation needs: NOT SET UP \u2192 hatches an identity instantly if this machine holds a device credential (the user ran the Paigy desktop app or harness), otherwise starts the code ceremony; ALREADY SET UP \u2192 returns your current identity and offers the two things left to decide, renaming it or unpairing; TOKEN NO LONGER VALID \u2192 says so, then re-pairs. Pass { name, voice } to choose who you are when hatching, or to RENAME yourself when already set up (voices: rachel, george, jessica, brian, lily). Safe to call any time: idempotent, and it never writes settings \u2014 the tool-allowlist state it reports is read-only. If it returns a `user_code`, print it to the user immediately and call onboard again with the `device_code`. If it returns `enable_command`, PRINT that command for the user to run \u2014 you cannot apply it yourself (it writes your own permission allowlist, which hosts block as privilege escalation), so print it, don't wait for it, and carry on.";
|
|
@@ -24,8 +13,6 @@ var SERVER_INSTRUCTIONS = serverInstructions({ waits: true });
|
|
|
24
13
|
|
|
25
14
|
// src/tools.ts
|
|
26
15
|
import { z } from "zod";
|
|
27
|
-
var UpdateGoalToolSchema = z.object({ goalId: z.string().uuid(), revision: z.number().int().positive(), changes: z.record(z.unknown()), reason: z.string().min(1) });
|
|
28
|
-
var ClaimGoalSchema = z.object({ goalId: z.string().uuid() });
|
|
29
16
|
var OnboardSchema = z.object({
|
|
30
17
|
name: z.string().max(60).optional(),
|
|
31
18
|
voice: z.string().max(40).optional(),
|
|
@@ -39,29 +26,7 @@ var PairSchema = z.object({
|
|
|
39
26
|
name: z.string().min(1).max(60).optional().describe("Hatch path only: the name you choose for this identity. Pick your own \u2014 ONE or TWO words, the way you'd introduce yourself on a call (it is spoken aloud and shown in lists). 'Piper', 'Blue Heron' \u2014 never a sentence or a task description."),
|
|
40
27
|
voice: z.string().optional().describe("Hatch path only: your voice on calls \u2014 one of rachel, george, jessica, brian, lily.")
|
|
41
28
|
});
|
|
42
|
-
var
|
|
43
|
-
parentId: z.string().describe("The thread to read \u2014 from a reply, request, or past notification.")
|
|
44
|
-
});
|
|
45
|
-
var SearchThreadsSchema = z.object({
|
|
46
|
-
q: z.string().describe("What to look for \u2014 plain words or a phrase (e.g. 'the livekit timeout', 'deploy to prod').")
|
|
47
|
-
});
|
|
48
|
-
var SetTaskStateToolSchema = z.object({
|
|
49
|
-
notificationId: z.string(),
|
|
50
|
-
state: SetTaskStateSchema.shape.state
|
|
51
|
-
});
|
|
52
|
-
var AnswerCallerQuestionSchema = z.object({
|
|
53
|
-
notificationId: z.string().describe("The notification whose call carried the caller's question \u2014 from the partial turn or the settled reply."),
|
|
54
|
-
answer: z.string().min(1).max(1500).describe("The answer, as one or two short SPOKEN sentences \u2014 it may be read aloud on the live call.")
|
|
55
|
-
});
|
|
56
|
-
var TOOLS = [
|
|
57
|
-
{
|
|
58
|
-
// #575: THE attention verb — the one model-facing surface, every agent.
|
|
59
|
-
// The retired names (notify/notify_user) stay callable as hidden aliases
|
|
60
|
-
// for stale prompts and cached servers; see toolset.ts for the design.
|
|
61
|
-
name: "contact",
|
|
62
|
-
description: CONTACT_DESCRIPTION,
|
|
63
|
-
inputSchema: CONTACT_SCHEMA
|
|
64
|
-
},
|
|
29
|
+
var IDENTITY_TOOLS = [
|
|
65
30
|
{
|
|
66
31
|
// ONE rail for starting a session (#875). `pair`, the `paigy-mcp-onboard` CLI and
|
|
67
32
|
// `enable_tools` were three doors into one flow, and "onboard" was the word people
|
|
@@ -79,55 +44,11 @@ var TOOLS = [
|
|
|
79
44
|
name: "unpair",
|
|
80
45
|
description: "Log out / unpair this agent from the user's Paigy account: revokes the token server-side (it stops working everywhere) and deletes the local ~/.paigy/token.json. Takes no arguments. After this, contact won't work until the user pairs again with the pair tool.",
|
|
81
46
|
inputSchema: mcpInputSchema(z.object({}))
|
|
82
|
-
},
|
|
83
|
-
// `await_reply` is GONE (owner, 2026-09-06, hard cutover): the wait is contact's — it holds
|
|
84
|
-
// the first window of a call it placed, and `contact({ wait: notificationId })` holds the
|
|
85
|
-
// next. One tool, one loop; the reply-reading guidance moved into CONTACT_DESCRIPTION.
|
|
86
|
-
{
|
|
87
|
-
name: "check_replies",
|
|
88
|
-
description: CHECK_REPLIES_DESCRIPTION,
|
|
89
|
-
inputSchema: mcpInputSchema(z.object({}))
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
name: "get_thread",
|
|
93
|
-
description: "The chronological transcript of one Paigy conversation thread \u2014 every past ask, answer, and user request on it. Call this to REHYDRATE when you're resuming or being seeded: a check_replies request whose parentId you don't recognize means the user is continuing an old conversation with you, and one carrying a contextParentId means they want a past conversation (possibly with a DIFFERENT agent) as your starting context \u2014 in both cases call get_thread FIRST and read the turns as prior conversation you were part of, not as new input. Turns: { role:'agent', title, description[], answer }, { role:'user', text }, and context turns { role:'handoff'|'recap', title, description[] } \u2014 a handoff is a predecessor's brief for you; a recap SUMMARIZES everything before it (the transcript starts at the latest recap, so treat it as the base and the turns after it as what happened since). Oldest first, capped at the most recent 30.",
|
|
94
|
-
inputSchema: mcpInputSchema(GetThreadSchema)
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
name: "search_threads",
|
|
98
|
-
description: SEARCH_THREADS_DESCRIPTION,
|
|
99
|
-
inputSchema: mcpInputSchema(SearchThreadsSchema)
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
name: "create_goal",
|
|
103
|
-
description: CREATE_GOAL_DESCRIPTION,
|
|
104
|
-
inputSchema: mcpInputSchema(CreateGoalSchema)
|
|
105
|
-
},
|
|
106
|
-
{ name: "claim_goal", description: "Claim a ready Goal you own and begin work. Returns its active revision.", inputSchema: mcpInputSchema(ClaimGoalSchema) },
|
|
107
|
-
{ name: "update_goal", description: "Update an owned Goal at an exact revision; stale revisions are rejected.", inputSchema: mcpInputSchema(UpdateGoalToolSchema) },
|
|
108
|
-
{
|
|
109
|
-
name: "set_work_state",
|
|
110
|
-
description: SET_WORK_STATE_DESCRIPTION,
|
|
111
|
-
inputSchema: mcpInputSchema(SetWorkStateSchema)
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
name: "answer_caller_question",
|
|
115
|
-
description: "Answer a question the user asked DURING a live call, while they're still on it. When a partial turn or a settled reply carries a `question` intent aimed at you, answer it here immediately: if their call is still live, your answer is spoken to them on that same call (returns live: true). If the call already ended (live: false), send the answer as a threaded contact instead \u2014 never drop it. Short spoken sentences only; this may be read aloud.",
|
|
116
|
-
inputSchema: mcpInputSchema(AnswerCallerQuestionSchema)
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
name: "schedule_callback",
|
|
120
|
-
description: SCHEDULE_CALLBACK_DESCRIPTION,
|
|
121
|
-
inputSchema: mcpInputSchema(ScheduleCallbackSchema)
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
name: "handoff",
|
|
125
|
-
description: "Deposit your working context for a SUCCESSOR agent \u2014 what you did, what's left, links, gotchas \u2014 as one note on a thread ({ title, notes[] }). This does NOT ring the user or enter their inbox: it's context, not a question. The successor reads it back with get_thread. Pass `target` (a sibling connection's token id or agent name, SAME account only) to hand off DIRECTLY to that agent \u2014 the note is dispatched to it as a request it picks up. Pass `workId` to move that existing outcome and its pending replies to the successor without reminting it; a target is required in that form. Omit `target` to leave the thread for the user to hand off to an agent themselves in the app. Pass `parentId` to land the handoff on an existing conversation; omit it to mint a fresh thread. Returns { parentId }. Pass recap:true when the note SUMMARIZES the thread so far (for a successor OR for your own later session): a recap resets the rehydration window \u2014 get_thread returns the latest recap + only the turns after it. Write one whenever a thread has grown long and you're pausing, handing off, or nearing your context limit.",
|
|
126
|
-
inputSchema: mcpInputSchema(HandoffSchema)
|
|
127
47
|
}
|
|
128
48
|
];
|
|
49
|
+
var TOOLS = [...AGENT_TOOLS, ...IDENTITY_TOOLS];
|
|
129
50
|
var TOOL_NAMES = TOOLS.map((t) => t.name);
|
|
130
|
-
var HIDDEN_ALIASES = [
|
|
51
|
+
var HIDDEN_ALIASES = [];
|
|
131
52
|
|
|
132
53
|
// src/clients.ts
|
|
133
54
|
import { execFile } from "child_process";
|
|
@@ -290,14 +211,8 @@ function enablePaigyTools(scope = "user", cwd = process.cwd()) {
|
|
|
290
211
|
|
|
291
212
|
export {
|
|
292
213
|
SERVER_INSTRUCTIONS,
|
|
293
|
-
UpdateGoalToolSchema,
|
|
294
|
-
ClaimGoalSchema,
|
|
295
214
|
OnboardSchema,
|
|
296
215
|
PairSchema,
|
|
297
|
-
GetThreadSchema,
|
|
298
|
-
SearchThreadsSchema,
|
|
299
|
-
SetTaskStateToolSchema,
|
|
300
|
-
AnswerCallerQuestionSchema,
|
|
301
216
|
TOOLS,
|
|
302
217
|
openBrowser,
|
|
303
218
|
autoConfigureClients,
|