@paigy/mcp 0.5.0 → 0.6.0
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/dist/{chunk-BSUXW66R.js → chunk-XXBSXCOC.js} +26 -1
- package/dist/index.js +35 -4
- package/dist/onboard.js +1 -1
- package/package.json +11 -10
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/device.ts
|
|
2
2
|
import { execFile } from "child_process";
|
|
3
|
-
import { mkdirSync, writeFileSync } from "fs";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
4
4
|
import { homedir, platform } from "os";
|
|
5
5
|
import { join } from "path";
|
|
6
6
|
var BACKEND_URL = process.env.PAIGY_BACKEND_URL ?? "https://paigy.ai";
|
|
@@ -17,6 +17,28 @@ function saveToken(token) {
|
|
|
17
17
|
writeFileSync(TOKEN_PATH, JSON.stringify(token, null, 2) + "\n", { mode: 384 });
|
|
18
18
|
}
|
|
19
19
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
20
|
+
function readToken() {
|
|
21
|
+
if (process.env.PAIGY_TOKEN) return process.env.PAIGY_TOKEN;
|
|
22
|
+
if (existsSync(TOKEN_PATH)) {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(readFileSync(TOKEN_PATH, "utf8")).access_token ?? "";
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
function deleteToken() {
|
|
31
|
+
if (!existsSync(TOKEN_PATH)) return false;
|
|
32
|
+
rmSync(TOKEN_PATH);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
async function revokeToken(token) {
|
|
36
|
+
const res = await fetch(`${BACKEND_URL}/api/device/revoke`, {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: { authorization: `Bearer ${token}` }
|
|
39
|
+
});
|
|
40
|
+
return res.ok;
|
|
41
|
+
}
|
|
20
42
|
async function requestCode(agent = AGENT_NAME) {
|
|
21
43
|
const res = await fetch(`${BACKEND_URL}/api/device/code`, {
|
|
22
44
|
method: "POST",
|
|
@@ -47,6 +69,9 @@ export {
|
|
|
47
69
|
openBrowser,
|
|
48
70
|
saveToken,
|
|
49
71
|
sleep,
|
|
72
|
+
readToken,
|
|
73
|
+
deleteToken,
|
|
74
|
+
revokeToken,
|
|
50
75
|
requestCode,
|
|
51
76
|
pollToken
|
|
52
77
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
deleteToken,
|
|
3
4
|
openBrowser,
|
|
4
5
|
pollToken,
|
|
6
|
+
readToken,
|
|
5
7
|
requestCode,
|
|
8
|
+
revokeToken,
|
|
6
9
|
saveToken,
|
|
7
10
|
sleep
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-XXBSXCOC.js";
|
|
9
12
|
|
|
10
13
|
// src/index.ts
|
|
11
14
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -295,6 +298,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
295
298
|
description: "Pair this agent with the user's Paigy account (one-time) \u2014 required before notify_user/await_reply work. Two steps: (1) call with NO args to start; it opens the user's browser and returns { verification_uri_complete, user_code, device_code } \u2014 show the user the URL + user_code and ask them to approve. (2) call again passing that device_code to finish; it waits for approval and saves the token. If it returns { status:'pending' }, the user hasn't approved yet \u2014 call again with the same device_code to keep waiting.",
|
|
296
299
|
inputSchema: zodToJsonSchema(PairSchema, { target: "openApi3" })
|
|
297
300
|
},
|
|
301
|
+
{
|
|
302
|
+
name: "unpair",
|
|
303
|
+
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, notify_user/await_reply won't work until the user pairs again with the pair tool.",
|
|
304
|
+
inputSchema: zodToJsonSchema(z2.object({}), { target: "openApi3" })
|
|
305
|
+
},
|
|
298
306
|
{
|
|
299
307
|
name: "notify_user",
|
|
300
308
|
description: "Notify the user via Paigy and get a request id to poll for their answer. Provide context.title (a specific, non-empty one-line headline \u2014 this is what the user sees first, and what shows on the ring for a call) and context.description (an array of standalone, non-empty detail chunks the user can selectively ask you to expand). Set `urgency`: 'inbox' (default) drops it silently in their inbox; 'call' rings their phone now as a voice call \u2014 use 'call' only when you genuinely need them in the moment (blocked/waiting, time-sensitive), not for routine FYIs. Plus optional options (answerable choices; each may carry a sandboxed `html` or an `image` preview for a visual 'pick one' \u2014 e.g. show layout/UI alternatives) and visuals. Use `select` to control how the user answers options: 'one' (default) = pick a single option; 'many' = multi-select, user checks any subset \u2192 answer arrives as {kind:'multi', optionIds:[...]}; 'rank' = select and order, user taps options in preferred order \u2192 answer arrives as {kind:'ranked', optionIds:[...]} (ordered by choice). If a reply comes back as {kind:'clarify', chunks:[...]}, the user wants more detail on those chunks \u2014 respond via notify_user with the SAME threadId and an expanded description. Pass `threadId` from a prior notify_user result or an await_reply reply to continue that conversation thread; omit it to start a new one.",
|
|
@@ -344,7 +352,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
344
352
|
};
|
|
345
353
|
}
|
|
346
354
|
const start = Date.now();
|
|
347
|
-
const capMs =
|
|
355
|
+
const capMs = 3e4;
|
|
348
356
|
while (Date.now() - start < capMs) {
|
|
349
357
|
const token = await pollToken(device_code);
|
|
350
358
|
if (token) {
|
|
@@ -356,7 +364,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
356
364
|
}]
|
|
357
365
|
};
|
|
358
366
|
}
|
|
359
|
-
await sleep(
|
|
367
|
+
await sleep(2e3);
|
|
360
368
|
}
|
|
361
369
|
return {
|
|
362
370
|
content: [{
|
|
@@ -364,7 +372,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
364
372
|
text: JSON.stringify({
|
|
365
373
|
status: "pending",
|
|
366
374
|
device_code,
|
|
367
|
-
message: "Still awaiting approval. Call pair again with this device_code to keep waiting."
|
|
375
|
+
message: "Still awaiting approval after ~30s. Call pair again with this device_code to keep waiting."
|
|
376
|
+
})
|
|
377
|
+
}]
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
case "unpair": {
|
|
381
|
+
const token = readToken();
|
|
382
|
+
let revoked = false;
|
|
383
|
+
if (token) {
|
|
384
|
+
try {
|
|
385
|
+
revoked = await revokeToken(token);
|
|
386
|
+
} catch {
|
|
387
|
+
revoked = false;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
const removed = deleteToken();
|
|
391
|
+
return {
|
|
392
|
+
content: [{
|
|
393
|
+
type: "text",
|
|
394
|
+
text: JSON.stringify({
|
|
395
|
+
ok: true,
|
|
396
|
+
revoked,
|
|
397
|
+
removed,
|
|
398
|
+
message: token ? `Unpaired. Server token ${revoked ? "revoked" : "revoke not confirmed"}; local token ${removed ? "deleted" : "was already absent"}.` : "No token found \u2014 already unpaired."
|
|
368
399
|
})
|
|
369
400
|
}]
|
|
370
401
|
};
|
package/dist/onboard.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paigy/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Paigy MCP server — a voice inbox for your AI agents. Lets an agent notify a user and await their reply.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -20,22 +20,23 @@
|
|
|
20
20
|
"url": "git+https://github.com/mauurda/paigy.git",
|
|
21
21
|
"directory": "apps/mcp"
|
|
22
22
|
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup src/index.ts src/onboard.ts --format esm --clean",
|
|
25
|
+
"dev": "tsup src/index.ts src/onboard.ts --format esm --watch",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"prepublishOnly": "pnpm --filter @paigy/schema build && pnpm build"
|
|
29
|
+
},
|
|
23
30
|
"dependencies": {
|
|
24
31
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
25
32
|
"zod": "^3.24.1",
|
|
26
33
|
"zod-to-json-schema": "^3.24.1"
|
|
27
34
|
},
|
|
28
35
|
"devDependencies": {
|
|
36
|
+
"@paigy/schema": "workspace:*",
|
|
29
37
|
"@types/node": "^22.0.0",
|
|
30
38
|
"tsup": "^8.3.5",
|
|
31
39
|
"typescript": "^5.7.2",
|
|
32
|
-
"vitest": "^2.1.8"
|
|
33
|
-
"@paigy/schema": "0.0.0"
|
|
34
|
-
},
|
|
35
|
-
"scripts": {
|
|
36
|
-
"build": "tsup src/index.ts src/onboard.ts --format esm --clean",
|
|
37
|
-
"dev": "tsup src/index.ts src/onboard.ts --format esm --watch",
|
|
38
|
-
"typecheck": "tsc --noEmit",
|
|
39
|
-
"test": "vitest run"
|
|
40
|
+
"vitest": "^2.1.8"
|
|
40
41
|
}
|
|
41
|
-
}
|
|
42
|
+
}
|