@scp-dev/mcp 0.1.1 → 0.1.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/dist/index.d.ts +34 -1
- package/dist/index.js +55 -7
- package/package.json +3 -3
- package/src/server.ts +64 -8
package/dist/index.d.ts
CHANGED
|
@@ -13,10 +13,18 @@ interface RelayMcpOptions {
|
|
|
13
13
|
input: AsyncIterable<Uint8Array | string> | ReadableStream<Uint8Array>;
|
|
14
14
|
/** one JSON-RPC line, newline included */
|
|
15
15
|
output: (line: string) => void;
|
|
16
|
+
/** whether the session is on the relay now: `dark` (why not) keeps the shim from claiming, `handle` is the name it joined under */
|
|
17
|
+
membership?: () => {
|
|
18
|
+
dark: string;
|
|
19
|
+
} | {
|
|
20
|
+
handle?: string;
|
|
21
|
+
};
|
|
16
22
|
/** HOME shortens cwds in relay_list */
|
|
17
23
|
env?: Record<string, string | undefined>;
|
|
18
|
-
/** push deliveries as `notifications/claude/channel` */
|
|
24
|
+
/** push deliveries as `notifications/claude/channel` to a client that declares `claude/channel` or was launched with channels */
|
|
19
25
|
channels?: boolean;
|
|
26
|
+
/** the client was started with channels loaded (Claude Code does not declare the capability itself) */
|
|
27
|
+
launchedWithChannels?: boolean;
|
|
20
28
|
version?: string;
|
|
21
29
|
/** starts a worker for relay_spawn, resolving with its name and its runner's pid once its harness started; without it there is no relay_spawn */
|
|
22
30
|
spawn?: (request: SpawnRequest) => Promise<{
|
|
@@ -96,6 +104,10 @@ declare const TOOLS: readonly [{
|
|
|
96
104
|
readonly type: "boolean";
|
|
97
105
|
readonly description: "only for recipients allowed private data";
|
|
98
106
|
};
|
|
107
|
+
readonly live: {
|
|
108
|
+
readonly type: "boolean";
|
|
109
|
+
readonly description: "refuse (not_live) instead of queueing when a recipient has no live session";
|
|
110
|
+
};
|
|
99
111
|
};
|
|
100
112
|
readonly required: readonly ["to", "body"];
|
|
101
113
|
readonly additionalProperties: false;
|
|
@@ -275,6 +287,27 @@ declare const TOOLS: readonly [{
|
|
|
275
287
|
readonly required: readonly ["now"];
|
|
276
288
|
readonly additionalProperties: false;
|
|
277
289
|
};
|
|
290
|
+
}, {
|
|
291
|
+
readonly name: "relay_join";
|
|
292
|
+
readonly description: "Put this session on the relay, so peers can message it and new messages reach you after your tool calls; name sets your handle once (renaming you in place when you already joined).";
|
|
293
|
+
readonly inputSchema: {
|
|
294
|
+
readonly type: "object";
|
|
295
|
+
readonly properties: {
|
|
296
|
+
readonly name: {
|
|
297
|
+
readonly type: "string";
|
|
298
|
+
readonly maxLength: 63;
|
|
299
|
+
};
|
|
300
|
+
};
|
|
301
|
+
readonly additionalProperties: false;
|
|
302
|
+
};
|
|
303
|
+
}, {
|
|
304
|
+
readonly name: "relay_leave";
|
|
305
|
+
readonly description: "Take this session off the relay: it goes offline and gets no new messages pushed; its mail is kept until relay_join.";
|
|
306
|
+
readonly inputSchema: {
|
|
307
|
+
readonly type: "object";
|
|
308
|
+
readonly properties: {};
|
|
309
|
+
readonly additionalProperties: false;
|
|
310
|
+
};
|
|
278
311
|
}, {
|
|
279
312
|
readonly name: "relay_set_title";
|
|
280
313
|
readonly description: "Set your title, a short line on what you work on that every agent and your user see in relay_list and swarm list; an empty title clears it.";
|
package/dist/index.js
CHANGED
|
@@ -70,7 +70,8 @@ var TOOLS = [
|
|
|
70
70
|
to: { oneOf: [{ type: "string" }, { type: "array", items: { type: "string" }, minItems: 1, maxItems: LIMITS.recipients }] },
|
|
71
71
|
body: { type: "string" },
|
|
72
72
|
thread: { type: "string", description: "continue this thread: its id, or the id or short id of a message in it" },
|
|
73
|
-
private: { type: "boolean", description: "only for recipients allowed private data" }
|
|
73
|
+
private: { type: "boolean", description: "only for recipients allowed private data" },
|
|
74
|
+
live: { type: "boolean", description: "refuse (not_live) instead of queueing when a recipient has no live session" }
|
|
74
75
|
},
|
|
75
76
|
required: ["to", "body"],
|
|
76
77
|
additionalProperties: false
|
|
@@ -157,6 +158,16 @@ var TOOLS = [
|
|
|
157
158
|
description: "Set your now, a one-line note on what you are doing right now, shown with your title in relay_list and relay_peers; your status state stays (working when unset).",
|
|
158
159
|
inputSchema: { type: "object", properties: { now: { type: "string", maxLength: LIMITS.noteChars } }, required: ["now"], additionalProperties: false }
|
|
159
160
|
},
|
|
161
|
+
{
|
|
162
|
+
name: "relay_join",
|
|
163
|
+
description: "Put this session on the relay, so peers can message it and new messages reach you after your tool calls; name sets your handle once (renaming you in place when you already joined).",
|
|
164
|
+
inputSchema: { type: "object", properties: { name: { type: "string", maxLength: 63 } }, additionalProperties: false }
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: "relay_leave",
|
|
168
|
+
description: "Take this session off the relay: it goes offline and gets no new messages pushed; its mail is kept until relay_join.",
|
|
169
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
170
|
+
},
|
|
160
171
|
{
|
|
161
172
|
name: "relay_set_title",
|
|
162
173
|
description: `Set your title, a short line on what you work on that every agent and your user see in relay_list and ${CLI_NAME} list; an empty title clears it.`,
|
|
@@ -336,6 +347,7 @@ function isDirectory(path) {
|
|
|
336
347
|
return false;
|
|
337
348
|
}
|
|
338
349
|
}
|
|
350
|
+
var capped = (wait) => typeof wait === "number" && wait > LIMITS.waitMaxS;
|
|
339
351
|
function seconds(args, fallback) {
|
|
340
352
|
const value = args.wait_s;
|
|
341
353
|
if (value === void 0) return fallback;
|
|
@@ -397,7 +409,13 @@ async function runRelayMcp(options) {
|
|
|
397
409
|
sent.set(message.id, { from: message.from.address, thread: message.thread });
|
|
398
410
|
while (sent.size > DELIVERED_KEEP) sent.delete(sent.keys().next().value);
|
|
399
411
|
};
|
|
412
|
+
function sessionClaim() {
|
|
413
|
+
const joined = options.membership?.() ?? {};
|
|
414
|
+
if ("dark" in joined) throw new RelayError("unavailable", joined.dark);
|
|
415
|
+
return joined.handle ? { ...options.claim, handle: joined.handle } : options.claim;
|
|
416
|
+
}
|
|
400
417
|
async function open() {
|
|
418
|
+
const claim = sessionClaim();
|
|
401
419
|
const next = await options.connect();
|
|
402
420
|
try {
|
|
403
421
|
let hello;
|
|
@@ -408,7 +426,7 @@ async function runRelayMcp(options) {
|
|
|
408
426
|
if (!(error instanceof RelayError)) throw error;
|
|
409
427
|
}
|
|
410
428
|
}
|
|
411
|
-
hello ??= await next.hello({ claim
|
|
429
|
+
hello ??= await next.hello({ claim, ...claim.cwd === void 0 ? {} : { cwd: claim.cwd } });
|
|
412
430
|
if (hello.token) token = hello.token;
|
|
413
431
|
me = hello.endpoint;
|
|
414
432
|
if (!announced) {
|
|
@@ -515,10 +533,11 @@ async function runRelayMcp(options) {
|
|
|
515
533
|
body: str(args, "body", true),
|
|
516
534
|
...args.thread !== void 0 ? { thread: str(args, "thread") } : {},
|
|
517
535
|
...bool(args, "private") ? { data_class: "private" } : {},
|
|
536
|
+
...bool(args, "live") ? { live: true } : {},
|
|
518
537
|
client_id: clientId()
|
|
519
538
|
});
|
|
520
539
|
if (result.held) return text(heldText(result.held));
|
|
521
|
-
const lines = reachLines(result.deliveries);
|
|
540
|
+
const lines = [...reachLines(result.deliveries), ...result.warnings ?? []];
|
|
522
541
|
const to = result.deliveries.map((d) => lines.length ? d.address : deliveryText(d)).join(", ");
|
|
523
542
|
const receipt = result.deliveries.some((d) => d.state === "running" || d.state === "starting") ? [`relay_status with id ${shortId(result.id)} shows when it is read`] : [];
|
|
524
543
|
return {
|
|
@@ -581,7 +600,7 @@ async function runRelayMcp(options) {
|
|
|
581
600
|
case "relay_inbox": {
|
|
582
601
|
const ack = bool(args, "ack") ?? true;
|
|
583
602
|
const { messages } = await call("inbox", { wait_s: seconds(args, 0) });
|
|
584
|
-
if (!messages.length) return text(
|
|
603
|
+
if (!messages.length) return text(`no unread messages${capped(args.wait_s) ? `; waited ${LIMITS.waitMaxS} s (cap)` : ""}`);
|
|
585
604
|
for (const message of messages) remember(message);
|
|
586
605
|
if (ack) await call("ack", { seqs: messages.map((m) => m.seq) });
|
|
587
606
|
return text(delivered(messages));
|
|
@@ -613,6 +632,27 @@ async function runRelayMcp(options) {
|
|
|
613
632
|
const view = await call("status", { note: args.now });
|
|
614
633
|
return text(`${view.address}: ${view.status?.state}${view.status?.note ? `: ${quote(view.status.note)}` : ""}`);
|
|
615
634
|
}
|
|
635
|
+
case "relay_join": {
|
|
636
|
+
const name2 = str(args, "name");
|
|
637
|
+
const joined = options.membership?.() ?? {};
|
|
638
|
+
const claim = "handle" in joined && joined.handle ? { ...options.claim, handle: joined.handle } : options.claim;
|
|
639
|
+
const next = await options.connect();
|
|
640
|
+
try {
|
|
641
|
+
const hello = await next.hello({ transient: true, claim, join: name2 ? { handle: name2 } : {} });
|
|
642
|
+
me = hello.endpoint;
|
|
643
|
+
} finally {
|
|
644
|
+
next.close();
|
|
645
|
+
}
|
|
646
|
+
await ensure();
|
|
647
|
+
return text(`joined the relay as ${me?.address}: peers can message you, and new messages reach you after your tool calls`);
|
|
648
|
+
}
|
|
649
|
+
case "relay_leave": {
|
|
650
|
+
const view = await call("leave");
|
|
651
|
+
client?.close();
|
|
652
|
+
client = void 0;
|
|
653
|
+
token = void 0;
|
|
654
|
+
return text(`${view.address} left the relay: it shows offline and gets no new messages pushed; its mail is kept until relay_join`);
|
|
655
|
+
}
|
|
616
656
|
case "relay_set_title": {
|
|
617
657
|
if (typeof args.title !== "string") throw new ToolInputError("title must be a string");
|
|
618
658
|
const view = await call("title", { text: args.title });
|
|
@@ -766,9 +806,8 @@ ${wrapMessage(message)}`,
|
|
|
766
806
|
const protocolVersion = typeof asked === "string" && MCP_PROTOCOLS.includes(asked) ? asked : MCP_PROTOCOLS[0];
|
|
767
807
|
initialized = true;
|
|
768
808
|
const capabilities = params.capabilities;
|
|
769
|
-
const client2 = params.clientInfo;
|
|
770
809
|
const channel = capabilities?.experimental?.["claude/channel"];
|
|
771
|
-
channelClient = typeof channel === "object" && channel !== null ||
|
|
810
|
+
channelClient = typeof channel === "object" && channel !== null || options.launchedWithChannels === true;
|
|
772
811
|
return respond({
|
|
773
812
|
protocolVersion,
|
|
774
813
|
capabilities: { tools: {}, ...options.channels ? { experimental: { "claude/channel": {} } } : {} },
|
|
@@ -807,7 +846,16 @@ ${wrapMessage(message)}`,
|
|
|
807
846
|
inFlight.add(running);
|
|
808
847
|
void running.finally(() => inFlight.delete(running));
|
|
809
848
|
}
|
|
810
|
-
|
|
849
|
+
const dark = (() => {
|
|
850
|
+
try {
|
|
851
|
+
sessionClaim();
|
|
852
|
+
return false;
|
|
853
|
+
} catch {
|
|
854
|
+
return true;
|
|
855
|
+
}
|
|
856
|
+
})();
|
|
857
|
+
if (!dark)
|
|
858
|
+
ensure().catch((error) => log(`${CLI_NAME} mcp: could not reach the relay broker yet: ${error instanceof Error ? error.message : String(error)}`));
|
|
811
859
|
const heartbeat = setInterval(() => {
|
|
812
860
|
call("whoami").catch(() => {
|
|
813
861
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scp-dev/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "The SCP stdio MCP server: gives an agent session the relay's tools.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://scp.dev",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"typecheck": "tsc -p ."
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@scp-dev/broker": "0.1.
|
|
33
|
-
"@scp-dev/core": "0.1.
|
|
32
|
+
"@scp-dev/broker": "0.1.3",
|
|
33
|
+
"@scp-dev/core": "0.1.2"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"tsup": "^8.5.1"
|
package/src/server.ts
CHANGED
|
@@ -57,10 +57,14 @@ export interface RelayMcpOptions {
|
|
|
57
57
|
input: AsyncIterable<Uint8Array | string> | ReadableStream<Uint8Array>
|
|
58
58
|
/** one JSON-RPC line, newline included */
|
|
59
59
|
output: (line: string) => void
|
|
60
|
+
/** whether the session is on the relay now: `dark` (why not) keeps the shim from claiming, `handle` is the name it joined under */
|
|
61
|
+
membership?: () => { dark: string } | { handle?: string }
|
|
60
62
|
/** HOME shortens cwds in relay_list */
|
|
61
63
|
env?: Record<string, string | undefined>
|
|
62
|
-
/** push deliveries as `notifications/claude/channel` */
|
|
64
|
+
/** push deliveries as `notifications/claude/channel` to a client that declares `claude/channel` or was launched with channels */
|
|
63
65
|
channels?: boolean
|
|
66
|
+
/** the client was started with channels loaded (Claude Code does not declare the capability itself) */
|
|
67
|
+
launchedWithChannels?: boolean
|
|
64
68
|
version?: string
|
|
65
69
|
/** starts a worker for relay_spawn, resolving with its name and its runner's pid once its harness started; without it there is no relay_spawn */
|
|
66
70
|
spawn?: (request: SpawnRequest) => Promise<{ name: string; pid: number }>
|
|
@@ -135,6 +139,7 @@ export const TOOLS = [
|
|
|
135
139
|
body: { type: "string" },
|
|
136
140
|
thread: { type: "string", description: "continue this thread: its id, or the id or short id of a message in it" },
|
|
137
141
|
private: { type: "boolean", description: "only for recipients allowed private data" },
|
|
142
|
+
live: { type: "boolean", description: "refuse (not_live) instead of queueing when a recipient has no live session" },
|
|
138
143
|
},
|
|
139
144
|
required: ["to", "body"],
|
|
140
145
|
additionalProperties: false,
|
|
@@ -224,6 +229,17 @@ export const TOOLS = [
|
|
|
224
229
|
"Set your now, a one-line note on what you are doing right now, shown with your title in relay_list and relay_peers; your status state stays (working when unset).",
|
|
225
230
|
inputSchema: { type: "object", properties: { now: { type: "string", maxLength: LIMITS.noteChars } }, required: ["now"], additionalProperties: false },
|
|
226
231
|
},
|
|
232
|
+
{
|
|
233
|
+
name: "relay_join",
|
|
234
|
+
description:
|
|
235
|
+
"Put this session on the relay, so peers can message it and new messages reach you after your tool calls; name sets your handle once (renaming you in place when you already joined).",
|
|
236
|
+
inputSchema: { type: "object", properties: { name: { type: "string", maxLength: 63 } }, additionalProperties: false },
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: "relay_leave",
|
|
240
|
+
description: "Take this session off the relay: it goes offline and gets no new messages pushed; its mail is kept until relay_join.",
|
|
241
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
242
|
+
},
|
|
227
243
|
{
|
|
228
244
|
name: "relay_set_title",
|
|
229
245
|
description: `Set your title, a short line on what you work on that every agent and your user see in relay_list and ${CLI_NAME} list; an empty title clears it.`,
|
|
@@ -443,6 +459,8 @@ function isDirectory(path: string): boolean {
|
|
|
443
459
|
}
|
|
444
460
|
}
|
|
445
461
|
|
|
462
|
+
const capped = (wait: unknown): boolean => typeof wait === "number" && wait > LIMITS.waitMaxS
|
|
463
|
+
|
|
446
464
|
function seconds(args: Json, fallback: number): number {
|
|
447
465
|
const value = args.wait_s
|
|
448
466
|
if (value === undefined) return fallback
|
|
@@ -502,7 +520,7 @@ export async function runRelayMcp(options: RelayMcpOptions): Promise<void> {
|
|
|
502
520
|
let me: EndpointView | null = null
|
|
503
521
|
let announced = false
|
|
504
522
|
let initialized = false
|
|
505
|
-
/** whether the client takes channel notifications: it says so, or it
|
|
523
|
+
/** whether the client takes channel notifications: it says so, or it was launched with channels (Claude Code does not say so itself) */
|
|
506
524
|
let channelClient = false
|
|
507
525
|
let stopped = false
|
|
508
526
|
/** seqs this shim pushed, so a push whose confirm was lost is not pushed again */
|
|
@@ -519,7 +537,15 @@ export async function runRelayMcp(options: RelayMcpOptions): Promise<void> {
|
|
|
519
537
|
while (sent.size > DELIVERED_KEEP) sent.delete(sent.keys().next().value!)
|
|
520
538
|
}
|
|
521
539
|
|
|
540
|
+
/** the claim for the session as it stands; a session off the relay is refused `unavailable` */
|
|
541
|
+
function sessionClaim(): Claim {
|
|
542
|
+
const joined = options.membership?.() ?? {}
|
|
543
|
+
if ("dark" in joined) throw new RelayError("unavailable", joined.dark)
|
|
544
|
+
return joined.handle ? { ...options.claim, handle: joined.handle } : options.claim
|
|
545
|
+
}
|
|
546
|
+
|
|
522
547
|
async function open(): Promise<RelayClient> {
|
|
548
|
+
const claim = sessionClaim()
|
|
523
549
|
const next = await options.connect()
|
|
524
550
|
try {
|
|
525
551
|
let hello: HelloResult | undefined
|
|
@@ -530,7 +556,7 @@ export async function runRelayMcp(options: RelayMcpOptions): Promise<void> {
|
|
|
530
556
|
if (!(error instanceof RelayError)) throw error
|
|
531
557
|
}
|
|
532
558
|
}
|
|
533
|
-
hello ??= await next.hello({ claim
|
|
559
|
+
hello ??= await next.hello({ claim, ...(claim.cwd === undefined ? {} : { cwd: claim.cwd }) })
|
|
534
560
|
if (hello.token) token = hello.token
|
|
535
561
|
me = hello.endpoint
|
|
536
562
|
if (!announced) {
|
|
@@ -649,10 +675,11 @@ export async function runRelayMcp(options: RelayMcpOptions): Promise<void> {
|
|
|
649
675
|
body: str(args, "body", true),
|
|
650
676
|
...(args.thread !== undefined ? { thread: str(args, "thread") } : {}),
|
|
651
677
|
...(bool(args, "private") ? { data_class: "private" } : {}),
|
|
678
|
+
...(bool(args, "live") ? { live: true } : {}),
|
|
652
679
|
client_id: clientId(),
|
|
653
680
|
})
|
|
654
681
|
if (result.held) return text(heldText(result.held))
|
|
655
|
-
const lines = reachLines(result.deliveries)
|
|
682
|
+
const lines = [...reachLines(result.deliveries), ...(result.warnings ?? [])]
|
|
656
683
|
const to = result.deliveries.map((d) => (lines.length ? d.address : deliveryText(d))).join(", ")
|
|
657
684
|
const receipt = result.deliveries.some((d) => d.state === "running" || d.state === "starting")
|
|
658
685
|
? [`relay_status with id ${shortId(result.id)} shows when it is read`]
|
|
@@ -717,7 +744,7 @@ export async function runRelayMcp(options: RelayMcpOptions): Promise<void> {
|
|
|
717
744
|
case "relay_inbox": {
|
|
718
745
|
const ack = bool(args, "ack") ?? true
|
|
719
746
|
const { messages } = await call<{ messages: Envelope[] }>("inbox", { wait_s: seconds(args, 0) })
|
|
720
|
-
if (!messages.length) return text(
|
|
747
|
+
if (!messages.length) return text(`no unread messages${capped(args.wait_s) ? `; waited ${LIMITS.waitMaxS} s (cap)` : ""}`)
|
|
721
748
|
for (const message of messages) remember(message)
|
|
722
749
|
if (ack) await call("ack", { seqs: messages.map((m) => m.seq) })
|
|
723
750
|
return text(delivered(messages))
|
|
@@ -748,6 +775,27 @@ export async function runRelayMcp(options: RelayMcpOptions): Promise<void> {
|
|
|
748
775
|
const view = await call<EndpointView>("status", { note: args.now })
|
|
749
776
|
return text(`${view.address}: ${view.status?.state}${view.status?.note ? `: ${quote(view.status.note)}` : ""}`)
|
|
750
777
|
}
|
|
778
|
+
case "relay_join": {
|
|
779
|
+
const name = str(args, "name")
|
|
780
|
+
const joined = options.membership?.() ?? {}
|
|
781
|
+
const claim = "handle" in joined && joined.handle ? { ...options.claim, handle: joined.handle } : options.claim
|
|
782
|
+
const next = await options.connect()
|
|
783
|
+
try {
|
|
784
|
+
const hello = await next.hello({ transient: true, claim, join: name ? { handle: name } : {} })
|
|
785
|
+
me = hello.endpoint
|
|
786
|
+
} finally {
|
|
787
|
+
next.close()
|
|
788
|
+
}
|
|
789
|
+
await ensure()
|
|
790
|
+
return text(`joined the relay as ${me?.address}: peers can message you, and new messages reach you after your tool calls`)
|
|
791
|
+
}
|
|
792
|
+
case "relay_leave": {
|
|
793
|
+
const view = await call<EndpointView>("leave")
|
|
794
|
+
client?.close()
|
|
795
|
+
client = undefined
|
|
796
|
+
token = undefined
|
|
797
|
+
return text(`${view.address} left the relay: it shows offline and gets no new messages pushed; its mail is kept until relay_join`)
|
|
798
|
+
}
|
|
751
799
|
case "relay_set_title": {
|
|
752
800
|
if (typeof args.title !== "string") throw new ToolInputError("title must be a string")
|
|
753
801
|
const view = await call<EndpointView>("title", { text: args.title })
|
|
@@ -903,9 +951,8 @@ export async function runRelayMcp(options: RelayMcpOptions): Promise<void> {
|
|
|
903
951
|
const protocolVersion = typeof asked === "string" && (MCP_PROTOCOLS as readonly string[]).includes(asked) ? asked : MCP_PROTOCOLS[0]
|
|
904
952
|
initialized = true
|
|
905
953
|
const capabilities = params.capabilities as { experimental?: Record<string, unknown> } | undefined
|
|
906
|
-
const client = params.clientInfo as { name?: unknown } | undefined
|
|
907
954
|
const channel = capabilities?.experimental?.["claude/channel"]
|
|
908
|
-
channelClient = (typeof channel === "object" && channel !== null) ||
|
|
955
|
+
channelClient = (typeof channel === "object" && channel !== null) || options.launchedWithChannels === true
|
|
909
956
|
return respond({
|
|
910
957
|
protocolVersion,
|
|
911
958
|
capabilities: { tools: {}, ...(options.channels ? { experimental: { "claude/channel": {} } } : {}) },
|
|
@@ -946,7 +993,16 @@ export async function runRelayMcp(options: RelayMcpOptions): Promise<void> {
|
|
|
946
993
|
void running.finally(() => inFlight.delete(running))
|
|
947
994
|
}
|
|
948
995
|
|
|
949
|
-
|
|
996
|
+
const dark = (() => {
|
|
997
|
+
try {
|
|
998
|
+
sessionClaim()
|
|
999
|
+
return false
|
|
1000
|
+
} catch {
|
|
1001
|
+
return true
|
|
1002
|
+
}
|
|
1003
|
+
})()
|
|
1004
|
+
if (!dark)
|
|
1005
|
+
ensure().catch((error: unknown) => log(`${CLI_NAME} mcp: could not reach the relay broker yet: ${error instanceof Error ? error.message : String(error)}`))
|
|
950
1006
|
const heartbeat = setInterval(() => {
|
|
951
1007
|
call("whoami").catch(() => {})
|
|
952
1008
|
}, options.heartbeatMs ?? 30_000)
|