@paigy/mcp 0.15.0 → 0.17.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/README.md +34 -2
- package/dist/chunk-3SVNPJPN.js +36 -0
- package/dist/{chunk-W2ZS7IBA.js → chunk-4LQAS5ZB.js} +295 -142
- package/dist/chunk-ZIOTFQPK.js +89 -0
- package/dist/index.js +40 -60
- package/dist/listen.js +678 -11
- package/dist/onboard.js +6 -5
- package/dist/statusline.js +6 -4
- package/package.json +13 -10
- package/dist/chunk-7S6W7NEZ.js +0 -224
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENT_NAME
|
|
3
|
+
} from "./chunk-4LQAS5ZB.js";
|
|
4
|
+
|
|
5
|
+
// src/clients.ts
|
|
6
|
+
import { execFile } from "child_process";
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
+
import { homedir, platform } from "os";
|
|
9
|
+
import { join, dirname } from "path";
|
|
10
|
+
function openBrowser(url) {
|
|
11
|
+
const cmd = platform() === "darwin" ? "open" : platform() === "win32" ? "cmd" : "xdg-open";
|
|
12
|
+
const args = platform() === "win32" ? ["/c", "start", url] : [url];
|
|
13
|
+
execFile(cmd, args, () => {
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function withPaigyMcpJson(existing, agent) {
|
|
17
|
+
let config = {};
|
|
18
|
+
if (existing) {
|
|
19
|
+
try {
|
|
20
|
+
config = JSON.parse(existing);
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
config.mcpServers = {
|
|
26
|
+
...config.mcpServers ?? {},
|
|
27
|
+
paigy: { command: "npx", args: ["-y", "@paigy/mcp@latest"], env: { PAIGY_AGENT: agent } }
|
|
28
|
+
};
|
|
29
|
+
return JSON.stringify(config, null, 2);
|
|
30
|
+
}
|
|
31
|
+
function withPaigyMcpToml(existing, agent) {
|
|
32
|
+
if (existing.includes("[mcp_servers.paigy]")) return null;
|
|
33
|
+
const entry = [
|
|
34
|
+
"",
|
|
35
|
+
"[mcp_servers.paigy]",
|
|
36
|
+
'command = "npx"',
|
|
37
|
+
'args = ["-y", "@paigy/mcp@latest"]',
|
|
38
|
+
`env = { PAIGY_AGENT = "${agent}" }`,
|
|
39
|
+
""
|
|
40
|
+
].join("\n");
|
|
41
|
+
return existing + entry;
|
|
42
|
+
}
|
|
43
|
+
function autoConfigureClients(skip = AGENT_NAME) {
|
|
44
|
+
const home = homedir();
|
|
45
|
+
const registered = [];
|
|
46
|
+
if (skip !== "antigravity") {
|
|
47
|
+
const antigravityConfigs = [
|
|
48
|
+
join(home, ".gemini", "config", "mcp_config.json"),
|
|
49
|
+
join(home, ".gemini", "antigravity-cli", "mcp_config.json")
|
|
50
|
+
];
|
|
51
|
+
for (const path of antigravityConfigs) {
|
|
52
|
+
try {
|
|
53
|
+
if (!existsSync(dirname(path))) continue;
|
|
54
|
+
const existing = existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
55
|
+
const updated = withPaigyMcpJson(existing, "antigravity");
|
|
56
|
+
if (updated === null) continue;
|
|
57
|
+
writeFileSync(path, updated);
|
|
58
|
+
registered.push(`Google Antigravity (${path})`);
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (skip !== "codex") {
|
|
64
|
+
const codexConfig = join(home, ".codex", "config.toml");
|
|
65
|
+
try {
|
|
66
|
+
if (existsSync(dirname(codexConfig))) {
|
|
67
|
+
const existing = existsSync(codexConfig) ? readFileSync(codexConfig, "utf8") : "";
|
|
68
|
+
const updated = withPaigyMcpToml(existing, "codex");
|
|
69
|
+
if (updated !== null) {
|
|
70
|
+
writeFileSync(codexConfig, updated);
|
|
71
|
+
registered.push(`OpenAI Codex (${codexConfig})`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return registered;
|
|
78
|
+
}
|
|
79
|
+
function claudeInstallHint(skip = AGENT_NAME) {
|
|
80
|
+
if (skip.startsWith("claude")) return null;
|
|
81
|
+
if (!existsSync(join(homedir(), ".claude"))) return null;
|
|
82
|
+
return "Claude Code detected \u2014 to add Paigy there, run /plugin marketplace add paigy-ai/claude then /plugin install paigy (pairing carries over; no need to pair again).";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
openBrowser,
|
|
87
|
+
autoConfigureClients,
|
|
88
|
+
claudeInstallHint
|
|
89
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
|
|
3
2
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
autoConfigureClients,
|
|
4
|
+
claudeInstallHint
|
|
5
|
+
} from "./chunk-ZIOTFQPK.js";
|
|
6
|
+
import {
|
|
7
|
+
clearSurface,
|
|
8
|
+
writeSurface
|
|
9
|
+
} from "./chunk-3SVNPJPN.js";
|
|
10
10
|
import {
|
|
11
11
|
NotifyRequestSchema,
|
|
12
12
|
ScheduleCallbackSchema,
|
|
13
13
|
SetTaskStateSchema,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
UnpairedError,
|
|
15
|
+
awaitReply,
|
|
16
|
+
checkReplies,
|
|
17
17
|
deleteKeyFile,
|
|
18
18
|
deleteToken,
|
|
19
19
|
fetchCredential,
|
|
20
20
|
finalizeE2ee,
|
|
21
|
+
getThread,
|
|
22
|
+
lintNotify,
|
|
21
23
|
pairStep,
|
|
22
24
|
readKeyFile,
|
|
23
25
|
readToken,
|
|
@@ -25,10 +27,12 @@ import {
|
|
|
25
27
|
revokeToken,
|
|
26
28
|
saveKeyFile,
|
|
27
29
|
saveToken,
|
|
30
|
+
scheduleCallback,
|
|
31
|
+
setTaskState,
|
|
28
32
|
sleep,
|
|
29
33
|
startE2ee,
|
|
30
|
-
|
|
31
|
-
} from "./chunk-
|
|
34
|
+
submitNotification
|
|
35
|
+
} from "./chunk-4LQAS5ZB.js";
|
|
32
36
|
|
|
33
37
|
// src/index.ts
|
|
34
38
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -38,52 +42,6 @@ import {
|
|
|
38
42
|
ListToolsRequestSchema
|
|
39
43
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
40
44
|
import { execSync } from "child_process";
|
|
41
|
-
|
|
42
|
-
// src/lint.ts
|
|
43
|
-
var TITLE_MAX = 90;
|
|
44
|
-
var CHUNKS_MAX = 8;
|
|
45
|
-
var CHUNK_MAX = 300;
|
|
46
|
-
var ASK_MAX = 600;
|
|
47
|
-
var OPTION_MAX = 80;
|
|
48
|
-
var NEEDS_MAX = 6;
|
|
49
|
-
var UNSPEAKABLE = /```|\n/;
|
|
50
|
-
function lintNotify(req) {
|
|
51
|
-
const problems = [];
|
|
52
|
-
if (req.ask !== void 0) {
|
|
53
|
-
if (req.ask.length > ASK_MAX)
|
|
54
|
-
problems.push(`ask is ${req.ask.length} chars \u2014 state the need and why it matters now in \u2264${ASK_MAX}; move detail into a smaller follow-up`);
|
|
55
|
-
if (UNSPEAKABLE.test(req.ask))
|
|
56
|
-
problems.push("ask contains code fences or newlines \u2014 write it as plain prose (it may be read aloud on a call)");
|
|
57
|
-
for (const n of req.needs ?? []) {
|
|
58
|
-
if (n.length > OPTION_MAX) problems.push(`need "${n.slice(0, 40)}\u2026" is too long \u2014 each need is a short phrase (\u2264${OPTION_MAX} chars)`);
|
|
59
|
-
}
|
|
60
|
-
if ((req.needs?.length ?? 0) > NEEDS_MAX)
|
|
61
|
-
problems.push(`${req.needs?.length} needs \u2014 cap at ${NEEDS_MAX}; a call can't cover more in one conversation, split the rest into a second ask`);
|
|
62
|
-
return problems;
|
|
63
|
-
}
|
|
64
|
-
const spoken = req.urgency === "call" || req.urgency === "banner";
|
|
65
|
-
if (req.context) {
|
|
66
|
-
if (req.context.title.length > TITLE_MAX)
|
|
67
|
-
problems.push(`title is ${req.context.title.length} chars \u2014 shorten to \u2264${TITLE_MAX} (it's what shows on the banner / gets spoken on a ring)`);
|
|
68
|
-
if (UNSPEAKABLE.test(req.context.title))
|
|
69
|
-
problems.push("title contains code fences or newlines \u2014 one plain-prose line");
|
|
70
|
-
if (req.context.description.length > CHUNKS_MAX)
|
|
71
|
-
problems.push(`${req.context.description.length} description chunks \u2014 cap at ${CHUNKS_MAX}; merge or drop the rest`);
|
|
72
|
-
for (const [i, chunk] of req.context.description.entries()) {
|
|
73
|
-
if (chunk.length > CHUNK_MAX)
|
|
74
|
-
problems.push(`description[${i}] is ${chunk.length} chars \u2014 split it into standalone points of \u2264${CHUNK_MAX}`);
|
|
75
|
-
}
|
|
76
|
-
if (spoken && UNSPEAKABLE.test(req.context.description.join(" ")))
|
|
77
|
-
problems.push("urgency is 'call'/'banner' but the description has code fences/newlines-in-chunk \u2014 rewrite in spoken register (it will be read aloud)");
|
|
78
|
-
}
|
|
79
|
-
for (const o of req.options ?? []) {
|
|
80
|
-
if (o.label.length > OPTION_MAX)
|
|
81
|
-
problems.push(`option label "${o.label.slice(0, 40)}\u2026" is ${o.label.length} chars \u2014 labels must read at a glance (\u2264${OPTION_MAX}); move detail into the description`);
|
|
82
|
-
}
|
|
83
|
-
return problems;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// src/index.ts
|
|
87
45
|
import { z } from "zod";
|
|
88
46
|
import qrcode from "qrcode-generator";
|
|
89
47
|
|
|
@@ -113,12 +71,16 @@ function json(s) {
|
|
|
113
71
|
}
|
|
114
72
|
|
|
115
73
|
// src/index.ts
|
|
74
|
+
var ONBOARD_MSG = "Not paired with Paigy yet \u2014 call the `pair` tool to connect this agent (it returns an approval link to show the user), then retry. Manual fallback: `npx -y -p @paigy/mcp paigy-mcp-onboard`.";
|
|
116
75
|
var AwaitReplySchema = z.object({
|
|
117
76
|
notificationId: z.string().describe("The notificationId returned by notify_user \u2014 waits for the user's reply to THIS notification only.")
|
|
118
77
|
});
|
|
119
78
|
var PairSchema = z.object({
|
|
120
79
|
device_code: z.string().optional().describe("Omit to start pairing (returns an approval link to show the user). Pass the device_code from that first call to finish, once the user has approved.")
|
|
121
80
|
});
|
|
81
|
+
var GetThreadSchema = z.object({
|
|
82
|
+
threadId: z.string().describe("The thread to read \u2014 from a reply, request, or past notification.")
|
|
83
|
+
});
|
|
122
84
|
var SetTaskStateToolSchema = z.object({
|
|
123
85
|
notificationId: z.string(),
|
|
124
86
|
state: SetTaskStateSchema.shape.state
|
|
@@ -163,6 +125,7 @@ function pairedResult(token, sas, note, e2ee) {
|
|
|
163
125
|
if (hint) base.claude_hint = hint;
|
|
164
126
|
} catch {
|
|
165
127
|
}
|
|
128
|
+
base.permissions_hint = "Offer the user ONCE: allowlist Paigy's tools so they run without prompting (in Claude Code, add mcp__paigy__notify_user, mcp__paigy__await_reply, mcp__paigy__check_replies, mcp__paigy__set_task_state, mcp__paigy__schedule_callback, mcp__paigy__get_thread to permissions.allow in ~/.claude/settings.json \u2014 or the project's .claude/settings.json if they prefer; other tools have their own allowlist). Merge into the existing file, never clobber it. Leave pair/unpair human-approved. If they decline, drop it.";
|
|
166
129
|
return { content: [{ type: "text", text: JSON.stringify(base) }] };
|
|
167
130
|
}
|
|
168
131
|
function awaitingConfirmResult(sas, device_code) {
|
|
@@ -190,7 +153,7 @@ var server = new Server(
|
|
|
190
153
|
{ name: "paigy", version: "0.0.0" },
|
|
191
154
|
{
|
|
192
155
|
capabilities: { tools: {} },
|
|
193
|
-
instructions: "On startup, call check_replies once to pick up any replies or pending work you missed while away. To wait for the answer to something you just asked, call await_reply with that notificationId \u2014 it's scoped to that one notification, so it never returns replies meant for other notifications. Use check_replies again only when re-booting or after waiting a long time on something else. Never end a turn that still needs the user without notify_user + await_reply. When you need a decision or input, MATCH the answer shape to the question \u2014 don't default everything to free text, and don't reflexively make everything yes/no. Pick the best tool for the job: yes/no \u2192 select:'confirm'; approve/deny an action \u2192 select:'confirm' + confirmStyle:'approve'; pick one of several \u2192 options + select:'one'; pick several / a subset \u2192 options + select:'many'; rank or prioritize \u2192 options + select:'rank'. Reserve select:'text' (free-form reply only) for plain updates and answers that genuinely can't be structured (the user can always add free text on top of any shape). On a { kind: 'clarify' } reply, see notify_user's own description for how to respond. When urgency is 'call', remember the title + description are spoken aloud \u2014 write them short and conversational, and name things instead of using IDs (e.g. 'the pull request about the agents page', not 'PR #235'). When the user asks you to follow up later \u2014 when you're done, if you're blocked, or at a set time \u2014 record it with schedule_callback so you don't drop it if you go idle. If you're about to start a genuinely long-running or blocking piece of work \u2014 one where the user would otherwise sit and wait \u2014 mention ONCE, in passing, that you can reach them when it's done or if you hit a blocker, instead of them needing to babysit the terminal. Don't offer this for quick tasks, and don't repeat the offer if they've already said yes or no earlier in the conversation. Escalate silence, don't just wait on it: if you notified at a lower urgency (inbox/push/banner) for something that's genuinely blocking real progress, call await_reply up to twice (~5 min each, ~10 min total) \u2014 if it's still idle after that AND the item is genuinely blocking, send a fresh notify_user on the SAME threadId at urgency:'call'. Skip this for anything that isn't truly blocking; a normal question can just sit in the inbox. NEVER go quietly idle while something might still be pending for you: whenever you end a turn with any Paigy notification unanswered (or any chance the user replied through the app while you worked), schedule your own ~2-minute wake-up (harness ScheduleWakeup or equivalent) and call check_replies when it fires; if still nothing, re-schedule and keep looping until resolved or the user says stop. For legibility, always use this exact wording \u2014 reason: 'Paigy idle check \u2014 waiting on <thing>', wake-up prompt: 'Paigy idle check: call check_replies and engage with anything unacknowledged; if idle, re-schedule (~2min).' \u2014 so the user can recognize every idle check at a glance. This self-polling in your own live session (full context intact) is the PRIMARY mechanism; the plugin's Stop hooks are only the dead-session safety net."
|
|
156
|
+
instructions: "On startup, call check_replies once to pick up any replies or pending work you missed while away. A check_replies request whose threadId you don't recognize, or one carrying a contextThreadId, means the user is resuming or seeding a past conversation \u2014 call get_thread on it FIRST and treat the transcript as prior conversation, not new input. To wait for the answer to something you just asked, call await_reply with that notificationId \u2014 it's scoped to that one notification, so it never returns replies meant for other notifications. Use check_replies again only when re-booting or after waiting a long time on something else. Never end a turn that still needs the user without notify_user + await_reply. When you need a decision or input, MATCH the answer shape to the question \u2014 don't default everything to free text, and don't reflexively make everything yes/no. Pick the best tool for the job: yes/no \u2192 select:'confirm'; approve/deny an action \u2192 select:'confirm' + confirmStyle:'approve'; pick one of several \u2192 options + select:'one'; pick several / a subset \u2192 options + select:'many'; rank or prioritize \u2192 options + select:'rank'. Reserve select:'text' (free-form reply only) for plain updates and answers that genuinely can't be structured (the user can always add free text on top of any shape). On a { kind: 'clarify' } reply, see notify_user's own description for how to respond. When urgency is 'call', remember the title + description are spoken aloud \u2014 write them short and conversational, and name things instead of using IDs (e.g. 'the pull request about the agents page', not 'PR #235'). When the user asks you to follow up later \u2014 when you're done, if you're blocked, or at a set time \u2014 record it with schedule_callback so you don't drop it if you go idle. If you're about to start a genuinely long-running or blocking piece of work \u2014 one where the user would otherwise sit and wait \u2014 mention ONCE, in passing, that you can reach them when it's done or if you hit a blocker, instead of them needing to babysit the terminal. Don't offer this for quick tasks, and don't repeat the offer if they've already said yes or no earlier in the conversation. Escalate silence, don't just wait on it: if you notified at a lower urgency (inbox/push/banner) for something that's genuinely blocking real progress, call await_reply up to twice (~5 min each, ~10 min total) \u2014 if it's still idle after that AND the item is genuinely blocking, send a fresh notify_user on the SAME threadId at urgency:'call'. Skip this for anything that isn't truly blocking; a normal question can just sit in the inbox. NEVER go quietly idle while something might still be pending for you: whenever you end a turn with any Paigy notification unanswered (or any chance the user replied through the app while you worked), schedule your own ~2-minute wake-up (harness ScheduleWakeup or equivalent) and call check_replies when it fires; if still nothing, re-schedule and keep looping until resolved or the user says stop. For legibility, always use this exact wording \u2014 reason: 'Paigy idle check \u2014 waiting on <thing>', wake-up prompt: 'Paigy idle check: call check_replies and engage with anything unacknowledged; if idle, re-schedule (~2min).' \u2014 so the user can recognize every idle check at a glance. This self-polling in your own live session (full context intact) is the PRIMARY mechanism; the plugin's Stop hooks are only the dead-session safety net."
|
|
194
157
|
}
|
|
195
158
|
);
|
|
196
159
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
@@ -220,6 +183,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
220
183
|
description: "The catch-up sweep for everything outstanding \u2014 a PURE read, takes no arguments, safe to call as often as you like: nothing here is consumed by reading it. Returns `replies` (answers to notifications you sent), your still-pending notifications, and `requests` \u2014 requests the user started toward you (each { notificationId, threadId, text }). Each keeps reappearing on every call until you actually engage with it: call set_task_state on its notificationId, which is what claims/acknowledges it \u2014 a human-initiated reply or request must never be silently dropped just because you read the list without acting. Use check_replies when booting up / starting a session, or when you've been waiting a long time on something else. To wait on an answer to a notify_user call you just made, use await_reply instead. Also returns owedCallbacks: callbacks now due that you promised \u2014 fulfill each with notify_user on its threadId. Also returns `stalled`: work (either direction) you reported in_progress via set_task_state a while ago and never reported completed \u2014 likely left half-done by this session or a prior one that crashed or went idle. For each, either continue the work and report a real state, or investigate why it stalled. Replies may carry `intents`/`transcript`/`covered` (call-mapped answers) \u2014 handle intents exactly as await_reply's description says (defer \u2192 schedule_callback now; delegate \u2192 decide and say so; channel \u2192 honor next contact), and treat a `covered` list missing one of your declared points as that part still unanswered.",
|
|
221
184
|
inputSchema: json(z.object({}))
|
|
222
185
|
},
|
|
186
|
+
{
|
|
187
|
+
name: "get_thread",
|
|
188
|
+
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 threadId you don't recognize means the user is continuing an old conversation with you, and one carrying a contextThreadId 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 } and { role:'user', text }, oldest first, capped at the most recent 30.",
|
|
189
|
+
inputSchema: json(GetThreadSchema)
|
|
190
|
+
},
|
|
223
191
|
{
|
|
224
192
|
name: "set_task_state",
|
|
225
193
|
description: "Report progress on the follow-up work behind ANY notification you own \u2014 a user-initiated request (from check_replies), or your OWN notify_user question once await_reply/check_replies returns its answer and you start acting on it. Pass that notificationId. THIS is what actually claims/acknowledges a reply or request \u2014 check_replies is a pure read that never consumes anything on its own, so call this as soon as you start engaging with something it returned; otherwise that same item just keeps reappearing forever. States: in_progress (you started working), completed (done), or needs_input (you need more from the user \u2014 usually paired with a notify_user carrying parentId = the same notificationId you're reporting on). Calling this reliably is also what lets a future session's check_replies surface `stalled` work you (or a crashed/idle prior session) left at in_progress without ever reporting completed.",
|
|
@@ -233,6 +201,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
233
201
|
]
|
|
234
202
|
}));
|
|
235
203
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
204
|
+
try {
|
|
205
|
+
return await handleTool(request);
|
|
206
|
+
} catch (e) {
|
|
207
|
+
if (e instanceof UnpairedError) throw new Error(ONBOARD_MSG);
|
|
208
|
+
throw e;
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
async function handleTool(request) {
|
|
236
212
|
switch (request.params.name) {
|
|
237
213
|
case "pair": {
|
|
238
214
|
const { device_code } = PairSchema.parse(request.params.arguments ?? {});
|
|
@@ -369,6 +345,10 @@ Enter it in the Paigy app (Inbox \u2192 Add a new agent).`,
|
|
|
369
345
|
const result = await checkReplies();
|
|
370
346
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
371
347
|
}
|
|
348
|
+
case "get_thread": {
|
|
349
|
+
const { threadId } = GetThreadSchema.parse(request.params.arguments);
|
|
350
|
+
return { content: [{ type: "text", text: JSON.stringify(await getThread(threadId)) }] };
|
|
351
|
+
}
|
|
372
352
|
case "set_task_state": {
|
|
373
353
|
const { notificationId, state } = SetTaskStateToolSchema.parse(request.params.arguments);
|
|
374
354
|
const result = await setTaskState(notificationId, state);
|
|
@@ -381,6 +361,6 @@ Enter it in the Paigy app (Inbox \u2192 Add a new agent).`,
|
|
|
381
361
|
default:
|
|
382
362
|
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
383
363
|
}
|
|
384
|
-
}
|
|
364
|
+
}
|
|
385
365
|
var transport = new StdioServerTransport();
|
|
386
366
|
await server.connect(transport);
|