@paigy/mcp 0.8.1 → 0.8.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
CHANGED
|
@@ -20,7 +20,7 @@ approve).
|
|
|
20
20
|
No clone needed. Add it to Claude Code (`-s user` = available in every project; drop it for just the current one):
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
claude mcp add paigy -s user -- npx -y @paigy/mcp
|
|
23
|
+
claude mcp add paigy -s user -- npx -y @paigy/mcp@latest
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
Or wire it into any MCP client config:
|
|
@@ -30,7 +30,7 @@ Or wire it into any MCP client config:
|
|
|
30
30
|
"mcpServers": {
|
|
31
31
|
"paigy": {
|
|
32
32
|
"command": "npx",
|
|
33
|
-
"args": ["-y", "@paigy/mcp"]
|
|
33
|
+
"args": ["-y", "@paigy/mcp@latest"]
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -39,7 +39,7 @@ Or wire it into any MCP client config:
|
|
|
39
39
|
First-time pairing (link the server to your Paigy account):
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
npx -p @paigy/mcp paigy-mcp-onboard
|
|
42
|
+
npx -p @paigy/mcp@latest paigy-mcp-onboard
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
It talks to the hosted backend by default — no config needed. Set
|
|
@@ -50,7 +50,7 @@ It talks to the hosted backend by default — no config needed. Set
|
|
|
50
50
|
Codex speaks MCP, so the same server drops in. Add it (writes `~/.codex/config.toml`):
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
-
codex mcp add paigy --env PAIGY_AGENT=codex -- npx -y @paigy/mcp
|
|
53
|
+
codex mcp add paigy --env PAIGY_AGENT=codex -- npx -y @paigy/mcp@latest
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
Or add the entry to `~/.codex/config.toml` by hand:
|
|
@@ -58,16 +58,16 @@ Or add the entry to `~/.codex/config.toml` by hand:
|
|
|
58
58
|
```toml
|
|
59
59
|
[mcp_servers.paigy]
|
|
60
60
|
command = "npx"
|
|
61
|
-
args = ["-y", "@paigy/mcp"]
|
|
61
|
+
args = ["-y", "@paigy/mcp@latest"]
|
|
62
62
|
env = { PAIGY_AGENT = "codex" }
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
Then pair: `npx -p @paigy/mcp paigy-mcp-onboard` (or have the agent call the `pair` tool).
|
|
65
|
+
Then pair: `npx -p @paigy/mcp@latest paigy-mcp-onboard` (or have the agent call the `pair` tool).
|
|
66
66
|
|
|
67
67
|
### Gemini CLI
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
|
-
gemini mcp add -s user -e PAIGY_AGENT=gemini paigy npx -y @paigy/mcp
|
|
70
|
+
gemini mcp add -s user -e PAIGY_AGENT=gemini paigy npx -y @paigy/mcp@latest
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
`-s user` makes it available across all projects — omit it for just the current one.
|
|
@@ -78,14 +78,14 @@ Or add the entry to `~/.gemini/settings.json`:
|
|
|
78
78
|
"mcpServers": {
|
|
79
79
|
"paigy": {
|
|
80
80
|
"command": "npx",
|
|
81
|
-
"args": ["-y", "@paigy/mcp"],
|
|
81
|
+
"args": ["-y", "@paigy/mcp@latest"],
|
|
82
82
|
"env": { "PAIGY_AGENT": "gemini" }
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
Then pair: `npx -p @paigy/mcp paigy-mcp-onboard`.
|
|
88
|
+
Then pair: `npx -p @paigy/mcp@latest paigy-mcp-onboard`.
|
|
89
89
|
|
|
90
90
|
> `PAIGY_AGENT` just names the agent on the device-approval screen (defaults to
|
|
91
91
|
> `mcp-agent`) — set it per client so you can tell your connected agents apart.
|
|
@@ -102,6 +102,17 @@ Then pair: `npx -p @paigy/mcp paigy-mcp-onboard`.
|
|
|
102
102
|
|
|
103
103
|
- `PAIGY_BACKEND_URL` — the Paigy API base (defaults to the hosted backend).
|
|
104
104
|
|
|
105
|
+
## Publishing (maintainers)
|
|
106
|
+
|
|
107
|
+
Tool input schemas are generated from zod in `src/schema.ts` as draft-2020-12 JSON
|
|
108
|
+
Schema, and `src/schema.test.ts` guards that every tool stays valid (strict clients
|
|
109
|
+
like the Anthropic API reject anything else). **A schema fix only reaches agents once
|
|
110
|
+
a new version is published to npm** — 0.8.0 once shipped *without* a committed fix and
|
|
111
|
+
400'd strict clients for weeks. So after any change under `src/schema*` or the tool
|
|
112
|
+
definitions: **bump the version and `pnpm publish`** (don't rely on the commit alone).
|
|
113
|
+
Install recipes pin `@paigy/mcp@latest` so a fresh `npx` picks up the new version; if a
|
|
114
|
+
stale one sticks, `rm -rf ~/.npm/_npx` and restart the client.
|
|
115
|
+
|
|
105
116
|
## License
|
|
106
117
|
|
|
107
118
|
MIT
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BACKEND_URL,
|
|
3
|
+
reach
|
|
4
|
+
} from "./chunk-GU7C5H6L.js";
|
|
5
|
+
|
|
1
6
|
// src/device.ts
|
|
2
7
|
import { execFile } from "child_process";
|
|
3
8
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
4
9
|
import { homedir, platform } from "os";
|
|
5
10
|
import { join } from "path";
|
|
6
|
-
var BACKEND_URL = process.env.PAIGY_BACKEND_URL ?? "https://paigy.ai";
|
|
7
11
|
var AGENT_NAME = process.env.PAIGY_AGENT ?? "mcp-agent";
|
|
8
12
|
var TOKEN_PATH = join(homedir(), ".paigy", "token.json");
|
|
9
13
|
function openBrowser(url) {
|
|
@@ -33,14 +37,14 @@ function deleteToken() {
|
|
|
33
37
|
return true;
|
|
34
38
|
}
|
|
35
39
|
async function revokeToken(token) {
|
|
36
|
-
const res = await
|
|
40
|
+
const res = await reach(`${BACKEND_URL}/api/device/revoke`, {
|
|
37
41
|
method: "POST",
|
|
38
42
|
headers: { authorization: `Bearer ${token}` }
|
|
39
43
|
});
|
|
40
44
|
return res.ok;
|
|
41
45
|
}
|
|
42
46
|
async function requestCode(agent = AGENT_NAME) {
|
|
43
|
-
const res = await
|
|
47
|
+
const res = await reach(`${BACKEND_URL}/api/device/code`, {
|
|
44
48
|
method: "POST",
|
|
45
49
|
headers: { "content-type": "application/json" },
|
|
46
50
|
body: JSON.stringify({ agent })
|
|
@@ -49,7 +53,7 @@ async function requestCode(agent = AGENT_NAME) {
|
|
|
49
53
|
return await res.json();
|
|
50
54
|
}
|
|
51
55
|
async function pollToken(deviceCode) {
|
|
52
|
-
const res = await
|
|
56
|
+
const res = await reach(`${BACKEND_URL}/api/device/token`, {
|
|
53
57
|
method: "POST",
|
|
54
58
|
headers: { "content-type": "application/json" },
|
|
55
59
|
body: JSON.stringify({ device_code: deviceCode })
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BACKEND_URL,
|
|
3
|
+
reach
|
|
4
|
+
} from "./chunk-GU7C5H6L.js";
|
|
5
|
+
|
|
1
6
|
// ../../packages/schema/dist/index.js
|
|
2
7
|
import { z } from "zod";
|
|
3
8
|
var ContextSchema = z.object({
|
|
@@ -275,7 +280,6 @@ var SupportRequestSchema = z.object({
|
|
|
275
280
|
import { existsSync, readFileSync } from "fs";
|
|
276
281
|
import { homedir } from "os";
|
|
277
282
|
import { join } from "path";
|
|
278
|
-
var BACKEND_URL = process.env.PAIGY_BACKEND_URL ?? "https://paigy.ai";
|
|
279
283
|
var TOKEN_PATH = join(homedir(), ".paigy", "token.json");
|
|
280
284
|
function loadToken() {
|
|
281
285
|
if (process.env.PAIGY_TOKEN) return process.env.PAIGY_TOKEN;
|
|
@@ -296,7 +300,7 @@ function ensureAuthed(res) {
|
|
|
296
300
|
}
|
|
297
301
|
async function submitNotification(req) {
|
|
298
302
|
const token = loadToken();
|
|
299
|
-
const res = ensureAuthed(await
|
|
303
|
+
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/notify`, {
|
|
300
304
|
method: "POST",
|
|
301
305
|
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
|
|
302
306
|
body: JSON.stringify(req)
|
|
@@ -313,7 +317,7 @@ async function awaitReply(notificationId, opts = {}) {
|
|
|
313
317
|
const token = loadToken();
|
|
314
318
|
const start = now();
|
|
315
319
|
while (true) {
|
|
316
|
-
const res = ensureAuthed(await
|
|
320
|
+
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/await?notificationId=${encodeURIComponent(notificationId)}`, {
|
|
317
321
|
headers: { authorization: `Bearer ${token}` }
|
|
318
322
|
}));
|
|
319
323
|
if (!res.ok) throw new Error(`await failed: ${res.status} ${await res.text()}`);
|
|
@@ -325,14 +329,14 @@ async function awaitReply(notificationId, opts = {}) {
|
|
|
325
329
|
}
|
|
326
330
|
async function checkReplies() {
|
|
327
331
|
const token = loadToken();
|
|
328
|
-
const res = ensureAuthed(await
|
|
332
|
+
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/pending`, {
|
|
329
333
|
headers: { authorization: `Bearer ${token}` }
|
|
330
334
|
}));
|
|
331
335
|
if (!res.ok) throw new Error(`check_replies failed: ${res.status} ${await res.text()}`);
|
|
332
336
|
return await res.json();
|
|
333
337
|
}
|
|
334
338
|
async function setTaskState(id, state) {
|
|
335
|
-
const res = ensureAuthed(await
|
|
339
|
+
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/notify/${id}/state`, {
|
|
336
340
|
method: "PATCH",
|
|
337
341
|
headers: { "content-type": "application/json", authorization: `Bearer ${loadToken()}` },
|
|
338
342
|
body: JSON.stringify({ state })
|
|
@@ -341,7 +345,7 @@ async function setTaskState(id, state) {
|
|
|
341
345
|
return await res.json();
|
|
342
346
|
}
|
|
343
347
|
async function registerDelivery(mode) {
|
|
344
|
-
const res = ensureAuthed(await
|
|
348
|
+
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/delivery`, {
|
|
345
349
|
method: "POST",
|
|
346
350
|
headers: { "content-type": "application/json", authorization: `Bearer ${loadToken()}` },
|
|
347
351
|
body: JSON.stringify({ mode })
|
|
@@ -351,7 +355,7 @@ async function registerDelivery(mode) {
|
|
|
351
355
|
}
|
|
352
356
|
async function scheduleCallback(req) {
|
|
353
357
|
const token = loadToken();
|
|
354
|
-
const res = ensureAuthed(await
|
|
358
|
+
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/callback`, {
|
|
355
359
|
method: "POST",
|
|
356
360
|
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
|
|
357
361
|
body: JSON.stringify(req)
|
|
@@ -361,7 +365,7 @@ async function scheduleCallback(req) {
|
|
|
361
365
|
}
|
|
362
366
|
async function pollAnswer(id) {
|
|
363
367
|
const token = loadToken();
|
|
364
|
-
const res = ensureAuthed(await
|
|
368
|
+
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/poll/${id}`, {
|
|
365
369
|
headers: { authorization: `Bearer ${token}` }
|
|
366
370
|
}));
|
|
367
371
|
if (!res.ok) throw new Error(`poll failed: ${res.status} ${await res.text()}`);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/http.ts
|
|
2
|
+
var BACKEND_URL = process.env.PAIGY_BACKEND_URL ?? "https://paigy.ai";
|
|
3
|
+
var NETWORK_MSG = `Can't reach ${BACKEND_URL} \u2014 the connection was blocked or dropped before an HTTP response. If this agent runs in a sandboxed environment with a network allowlist (e.g. Claude Code on the web, CI), ask the user to add the Paigy domain (paigy.ai) to the environment's allowed domains, then retry.`;
|
|
4
|
+
async function reach(url, init) {
|
|
5
|
+
try {
|
|
6
|
+
return await fetch(url, init);
|
|
7
|
+
} catch (e) {
|
|
8
|
+
throw new Error(`${NETWORK_MSG} (${e?.message ?? String(e)})`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
BACKEND_URL,
|
|
14
|
+
reach
|
|
15
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
scheduleCallback,
|
|
12
12
|
setTaskState,
|
|
13
13
|
submitNotification
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-BUELEUTV.js";
|
|
15
15
|
import {
|
|
16
16
|
deleteToken,
|
|
17
17
|
openBrowser,
|
|
@@ -21,7 +21,8 @@ import {
|
|
|
21
21
|
revokeToken,
|
|
22
22
|
saveToken,
|
|
23
23
|
sleep
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-AILC3H3U.js";
|
|
25
|
+
import "./chunk-GU7C5H6L.js";
|
|
25
26
|
|
|
26
27
|
// src/index.ts
|
|
27
28
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
package/dist/listen.js
CHANGED
package/dist/onboard.js
CHANGED