@scp-dev/mcp 0.1.4 → 0.1.6

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _scp_dev_core from '@scp-dev/core';
2
- import { Claim } from '@scp-dev/core';
2
+ import { Claim, Wake } from '@scp-dev/core';
3
3
  import { RelayClient } from '@scp-dev/broker';
4
4
 
5
5
  declare const MCP_PROTOCOLS: readonly ["2025-11-25", "2025-06-18", "2025-03-26", "2024-11-05"];
@@ -25,6 +25,8 @@ interface RelayMcpOptions {
25
25
  channels?: boolean;
26
26
  /** the client was started with channels loaded (Claude Code does not declare the capability itself) */
27
27
  launchedWithChannels?: boolean;
28
+ /** the wake the endpoint advertises while this shim pushes by channel, over `pi` for a pi client and `channels` for any other */
29
+ wake?: Wake;
28
30
  version?: string;
29
31
  /** 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 */
30
32
  spawn?: (request: SpawnRequest) => Promise<{
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  deliveryText,
8
8
  LIMITS,
9
9
  PREAMBLE,
10
+ presenceText,
10
11
  reachLines,
11
12
  receiptLines,
12
13
  RelayError,
@@ -278,7 +279,11 @@ function heldText(held) {
278
279
  return `held for review by your human: hold ${held.hold_id}; nothing was delivered`;
279
280
  }
280
281
  function sentContent(result) {
281
- return { id: result.id, thread: result.thread, recipients: result.deliveries.map((d) => ({ address: d.address, ...d.state ? { state: d.state } : {} })) };
282
+ return {
283
+ id: result.id,
284
+ thread: result.thread,
285
+ recipients: result.deliveries.map((d) => ({ address: d.address, ...d.state ? { state: d.state } : {}, ...d.wake ? { wake: d.wake } : {} }))
286
+ };
282
287
  }
283
288
  function redactedText(redacted) {
284
289
  return redacted ? `; redacted ${redacted.n} span${redacted.n === 1 ? "" : "s"} (${redacted.kinds.join(", ")}) before delivery` : "";
@@ -404,6 +409,8 @@ async function runRelayMcp(options) {
404
409
  let stopped = false;
405
410
  const channelPushed = /* @__PURE__ */ new Set();
406
411
  let channelLoop;
412
+ let wake;
413
+ let advertised;
407
414
  const write = (message) => {
408
415
  if (!stopped) options.output(`${JSON.stringify(message)}
409
416
  `);
@@ -446,8 +453,17 @@ async function runRelayMcp(options) {
446
453
  throw new Error("the relay MCP shim is stopping");
447
454
  }
448
455
  client = next;
456
+ advertise();
449
457
  return next;
450
458
  }
459
+ function advertise() {
460
+ if (!client || wake === void 0) return;
461
+ const via = wake ?? "none";
462
+ if (advertised?.client === client && advertised.via === via || via === "none" && !advertised) return;
463
+ advertised = { client, via };
464
+ client.request("wake", { via }).catch(() => {
465
+ });
466
+ }
451
467
  function ensure() {
452
468
  if (client) return Promise.resolve(client);
453
469
  connecting ??= open().finally(() => connecting = void 0);
@@ -473,7 +489,7 @@ async function runRelayMcp(options) {
473
489
  return [
474
490
  `${e.address}${me && e.id === me.id ? " (you)" : ""}`,
475
491
  e.harness ?? e.kind,
476
- e.presence,
492
+ presenceText(e),
477
493
  e.title ? `title ${quote(e.title)}` : "",
478
494
  status,
479
495
  cwd ? quote(cwd) : "",
@@ -815,6 +831,9 @@ ${wrapMessage(message)}`,
815
831
  const capabilities = params.capabilities;
816
832
  const channel = capabilities?.experimental?.["claude/channel"];
817
833
  channelClient = typeof channel === "object" && channel !== null || options.launchedWithChannels === true;
834
+ const clientInfo = params.clientInfo;
835
+ wake = options.channels && channelClient ? options.wake ?? (clientInfo?.name === "pi" ? "pi" : "channels") : null;
836
+ advertise();
818
837
  return respond({
819
838
  protocolVersion,
820
839
  capabilities: { tools: {}, ...options.channels ? { experimental: { "claude/channel": {} } } : {} },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scp-dev/mcp",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
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.4",
33
- "@scp-dev/core": "0.1.3"
32
+ "@scp-dev/broker": "0.1.6",
33
+ "@scp-dev/core": "0.1.4"
34
34
  },
35
35
  "devDependencies": {
36
36
  "tsup": "^8.5.1"
package/src/server.ts CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  type SendResult,
19
19
  type PeerView,
20
20
  PREAMBLE,
21
+ presenceText,
21
22
  reachLines,
22
23
  receiptLines,
23
24
  RelayError,
@@ -26,6 +27,7 @@ import {
26
27
  shortId,
27
28
  STATUS_STATES,
28
29
  type ThreadMessage,
30
+ type Wake,
29
31
  type WhyResult,
30
32
  widenText,
31
33
  wrapMessage,
@@ -65,6 +67,8 @@ export interface RelayMcpOptions {
65
67
  channels?: boolean
66
68
  /** the client was started with channels loaded (Claude Code does not declare the capability itself) */
67
69
  launchedWithChannels?: boolean
70
+ /** the wake the endpoint advertises while this shim pushes by channel, over `pi` for a pi client and `channels` for any other */
71
+ wake?: Wake
68
72
  version?: string
69
73
  /** 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 */
70
74
  spawn?: (request: SpawnRequest) => Promise<{ name: string; pid: number }>
@@ -366,7 +370,11 @@ function heldText(held: JudgeHeld): string {
366
370
 
367
371
  /** a send's id, thread and recipients with their states, for a result's structuredContent */
368
372
  function sentContent(result: SendResult): Record<string, unknown> {
369
- return { id: result.id, thread: result.thread, recipients: result.deliveries.map((d) => ({ address: d.address, ...(d.state ? { state: d.state } : {}) })) }
373
+ return {
374
+ id: result.id,
375
+ thread: result.thread,
376
+ recipients: result.deliveries.map((d) => ({ address: d.address, ...(d.state ? { state: d.state } : {}), ...(d.wake ? { wake: d.wake } : {}) })),
377
+ }
370
378
  }
371
379
 
372
380
  function redactedText(redacted: JudgeRedacted | undefined): string {
@@ -530,6 +538,9 @@ export async function runRelayMcp(options: RelayMcpOptions): Promise<void> {
530
538
  /** seqs this shim pushed, so a push whose confirm was lost is not pushed again */
531
539
  const channelPushed = new Set<number>()
532
540
  let channelLoop: Promise<void> | undefined
541
+ /** what the endpoint advertises once initialize said whether the client takes channel pushes; null advertises nothing */
542
+ let wake: Wake | null | undefined
543
+ let advertised: { client: RelayClient; via: Wake } | undefined
533
544
 
534
545
  const write = (message: Json) => {
535
546
  if (!stopped) options.output(`${JSON.stringify(message)}\n`)
@@ -576,9 +587,19 @@ export async function runRelayMcp(options: RelayMcpOptions): Promise<void> {
576
587
  throw new Error("the relay MCP shim is stopping")
577
588
  }
578
589
  client = next
590
+ advertise()
579
591
  return next
580
592
  }
581
593
 
594
+ /** tells the broker how a message wakes this session, once per connection and value, when both it and the client's capabilities are known */
595
+ function advertise() {
596
+ if (!client || wake === undefined) return
597
+ const via = wake ?? "none"
598
+ if ((advertised?.client === client && advertised.via === via) || (via === "none" && !advertised)) return
599
+ advertised = { client, via }
600
+ client.request("wake", { via }).catch(() => {})
601
+ }
602
+
582
603
  function ensure(): Promise<RelayClient> {
583
604
  if (client) return Promise.resolve(client)
584
605
  connecting ??= open().finally(() => (connecting = undefined))
@@ -608,7 +629,7 @@ export async function runRelayMcp(options: RelayMcpOptions): Promise<void> {
608
629
  return [
609
630
  `${e.address}${me && e.id === me.id ? " (you)" : ""}`,
610
631
  e.harness ?? e.kind,
611
- e.presence,
632
+ presenceText(e),
612
633
  e.title ? `title ${quote(e.title)}` : "",
613
634
  status,
614
635
  cwd ? quote(cwd) : "",
@@ -960,6 +981,9 @@ export async function runRelayMcp(options: RelayMcpOptions): Promise<void> {
960
981
  const capabilities = params.capabilities as { experimental?: Record<string, unknown> } | undefined
961
982
  const channel = capabilities?.experimental?.["claude/channel"]
962
983
  channelClient = (typeof channel === "object" && channel !== null) || options.launchedWithChannels === true
984
+ const clientInfo = params.clientInfo as { name?: unknown } | undefined
985
+ wake = options.channels && channelClient ? (options.wake ?? (clientInfo?.name === "pi" ? "pi" : "channels")) : null
986
+ advertise()
963
987
  return respond({
964
988
  protocolVersion,
965
989
  capabilities: { tools: {}, ...(options.channels ? { experimental: { "claude/channel": {} } } : {}) },