@n24q02m/mcp-core 1.9.0 → 1.11.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.
Files changed (38) hide show
  1. package/build/auth/credential-form.d.ts +48 -0
  2. package/build/auth/credential-form.d.ts.map +1 -1
  3. package/build/auth/credential-form.js +31 -0
  4. package/build/auth/credential-form.js.map +1 -1
  5. package/build/auth/local-oauth-app.d.ts +24 -1
  6. package/build/auth/local-oauth-app.d.ts.map +1 -1
  7. package/build/auth/local-oauth-app.js +133 -1
  8. package/build/auth/local-oauth-app.js.map +1 -1
  9. package/build/lifecycle/lock.d.ts +124 -0
  10. package/build/lifecycle/lock.d.ts.map +1 -0
  11. package/build/lifecycle/lock.js +318 -0
  12. package/build/lifecycle/lock.js.map +1 -0
  13. package/build/relay/session.d.ts +12 -0
  14. package/build/relay/session.d.ts.map +1 -0
  15. package/build/relay/session.js +55 -0
  16. package/build/relay/session.js.map +1 -0
  17. package/build/relay/tool-helpers.d.ts +56 -0
  18. package/build/relay/tool-helpers.d.ts.map +1 -0
  19. package/build/relay/tool-helpers.js +60 -0
  20. package/build/relay/tool-helpers.js.map +1 -0
  21. package/build/storage/config-file.d.ts +17 -0
  22. package/build/storage/config-file.d.ts.map +1 -1
  23. package/build/storage/config-file.js +22 -1
  24. package/build/storage/config-file.js.map +1 -1
  25. package/build/storage/session-lock.js +1 -1
  26. package/build/storage/session-lock.js.map +1 -1
  27. package/build/transport/cache.d.ts +19 -0
  28. package/build/transport/cache.d.ts.map +1 -0
  29. package/build/transport/cache.js +67 -0
  30. package/build/transport/cache.js.map +1 -0
  31. package/build/transport/local-server.d.ts.map +1 -1
  32. package/build/transport/local-server.js +47 -2
  33. package/build/transport/local-server.js.map +1 -1
  34. package/build/transport/smart-stdio.d.ts +64 -0
  35. package/build/transport/smart-stdio.d.ts.map +1 -1
  36. package/build/transport/smart-stdio.js +170 -0
  37. package/build/transport/smart-stdio.js.map +1 -1
  38. package/package.json +1 -1
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Lock-file lifecycle helpers (D9 — hybrid TTL extension).
3
+ *
4
+ * The lock file written by `runLocalServer` may contain 4 lines (legacy
5
+ * v1) or 6 lines (D9 modern):
6
+ *
7
+ * ```
8
+ * {pid}
9
+ * {port}
10
+ * {token}
11
+ * {spawnedAt_iso8601_utc}
12
+ * {credState} # "configured" | "unconfigured" (D9)
13
+ * {lastActivityAt_iso8601_utc} # bumped by daemon refresh loop (D9)
14
+ * ```
15
+ *
16
+ * Stale locks are detected when *any* of:
17
+ * - file has legacy 3-line format (no timestamp) → migrate by deletion
18
+ * - timestamp older than the applicable TTL
19
+ * - configured credState → 24h
20
+ * - unconfigured credState → 30 min
21
+ * - writing PID does not exist on this host
22
+ *
23
+ * Dead-PID locks are reaped immediately. Past-TTL idle setup daemons
24
+ * (unconfigured) are terminated; configured daemons past TTL get their
25
+ * lock unlinked but the process is left alone (parity with pre-D9 sweep
26
+ * behavior so a long-running daemon never gets SIGKILL'd by a sweep
27
+ * race).
28
+ *
29
+ * TypeScript port of core-py's `mcp_core.lifecycle.lock`. Behaviour and
30
+ * TTL constants kept identical for cross-language parity.
31
+ */
32
+ import { existsSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
33
+ import { homedir } from 'node:os';
34
+ import { join } from 'node:path';
35
+ // Self-import so `sweepStaleLocks` can resolve `lockDir`/`isAlive`/
36
+ // `terminateDaemon` through the module namespace at call time. This
37
+ // makes `vi.spyOn(lockModule, '<name>').mockReturnValue(...)` actually
38
+ // intercept the helper from inside the sweep loop — without this
39
+ // indirection, the closure-captured local binding ignores spies and
40
+ // test mocks become silent no-ops.
41
+ import * as self from './lock.js';
42
+ export const DEFAULT_LOCK_TTL_HOURS = 24;
43
+ /** D9 hybrid TTL constants — selected by `credState` in `sweepStaleLocks`. */
44
+ export const LIFECYCLE_TTL_CONFIGURED_MS = 24 * 3600 * 1000;
45
+ export const LIFECYCLE_TTL_UNCONFIGURED_MS = 30 * 60 * 1000;
46
+ export function locksDir(root) {
47
+ return root ?? join(homedir(), '.config', 'mcp', 'locks');
48
+ }
49
+ /**
50
+ * No-arg lock-dir resolver — D9 sweep tests `vi.spyOn(lockModule, 'lockDir')`
51
+ * to redirect into a temp directory without threading a `root` arg.
52
+ */
53
+ export function lockDir() {
54
+ return locksDir();
55
+ }
56
+ /**
57
+ * Serialize `LockMetadata` to the 6-line on-disk format. Trailing
58
+ * newline included so writes always end in `\n` and the parser's
59
+ * `lines.length` checks behave deterministically.
60
+ */
61
+ export function serializeLock(meta) {
62
+ return [
63
+ String(meta.pid),
64
+ String(meta.port),
65
+ meta.token,
66
+ meta.spawnedAt.toISOString(),
67
+ meta.credState,
68
+ meta.lastActivityAt.toISOString(),
69
+ ''
70
+ ].join('\n');
71
+ }
72
+ /**
73
+ * Parse the textual lock payload into `LockMetadata`. Accepts 4-line
74
+ * legacy, 5-line transitional, and 6-line D9 modern formats. Throws on
75
+ * fewer than 4 lines or unparseable timestamp / pid / port.
76
+ */
77
+ export function parseLock(raw) {
78
+ const lines = raw.replace(/\n+$/, '').split('\n');
79
+ if (lines.length < 4)
80
+ throw new Error(`lock file too few lines: ${lines.length}`);
81
+ const pid = Number(lines[0]);
82
+ const port = Number(lines[1]);
83
+ if (!Number.isInteger(pid) || !Number.isInteger(port)) {
84
+ throw new Error(`lock file has non-integer pid/port`);
85
+ }
86
+ const token = lines[2];
87
+ const spawnedAt = new Date(lines[3]);
88
+ if (Number.isNaN(spawnedAt.getTime())) {
89
+ throw new Error(`lock file has unparseable spawnedAt`);
90
+ }
91
+ let credState = 'configured';
92
+ let lastActivityAt = spawnedAt;
93
+ if (lines.length >= 6) {
94
+ credState = lines[4];
95
+ lastActivityAt = new Date(lines[5]);
96
+ if (Number.isNaN(lastActivityAt.getTime()))
97
+ lastActivityAt = spawnedAt;
98
+ }
99
+ else if (lines.length === 5) {
100
+ credState = lines[4];
101
+ }
102
+ return { pid, port, token, spawnedAt, credState, lastActivityAt, createdAt: spawnedAt };
103
+ }
104
+ export function parseLockMetadata(path) {
105
+ if (!existsSync(path))
106
+ return null;
107
+ let content;
108
+ try {
109
+ content = readFileSync(path, { encoding: 'utf-8' });
110
+ }
111
+ catch {
112
+ return null;
113
+ }
114
+ const lines = content.trim().split('\n');
115
+ // Pre-D9 callers expect null for legacy 3-line format. The new
116
+ // `parseLock` instead throws — adapter logic here keeps both
117
+ // contracts: legacy / corrupt -> null; valid 4/5/6-line -> populated.
118
+ if (lines.length < 4)
119
+ return null;
120
+ try {
121
+ return parseLock(content);
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ }
127
+ export function isLockExpired(path, ttlHours = DEFAULT_LOCK_TTL_HOURS) {
128
+ const md = parseLockMetadata(path);
129
+ if (md === null)
130
+ return true;
131
+ const ageMs = Date.now() - md.spawnedAt.getTime();
132
+ return ageMs > ttlHours * 3600 * 1000;
133
+ }
134
+ /**
135
+ * Cross-platform PID liveness check via `process.kill(pid, 0)`. Best-effort.
136
+ * Returns false on EPERM (different-user processes) so sweep does not steal
137
+ * locks owned by other users. Pre-D9 entry point — kept for backward compat.
138
+ */
139
+ export function isPidAlive(pid) {
140
+ if (pid <= 0)
141
+ return false;
142
+ try {
143
+ process.kill(pid, 0);
144
+ return true;
145
+ }
146
+ catch (err) {
147
+ const code = err?.code;
148
+ if (code === 'EPERM')
149
+ return true; // exists but we lack permission
150
+ return false;
151
+ }
152
+ }
153
+ /**
154
+ * Liveness check on a parsed `LockMetadata`. D9 sweep tests
155
+ * `vi.spyOn(lockModule, 'isAlive')` to control the alive/dead branch
156
+ * without needing a real PID. Wraps `isPidAlive` so the underlying
157
+ * cross-platform logic stays in one place.
158
+ */
159
+ export function isAlive(meta) {
160
+ return isPidAlive(meta.pid);
161
+ }
162
+ /**
163
+ * Kill the lock owner. D9 sweep tests `vi.spyOn(lockModule, 'terminateDaemon')`
164
+ * to assert the unconfigured-TTL branch invokes termination, without
165
+ * killing real PIDs. Used by `sweepStaleLocks` when an unconfigured
166
+ * daemon's idle TTL elapses.
167
+ */
168
+ export function terminateDaemon(pid) {
169
+ if (pid <= 0)
170
+ return;
171
+ try {
172
+ process.kill(pid, 'SIGKILL');
173
+ }
174
+ catch {
175
+ /* ignore */
176
+ }
177
+ }
178
+ /**
179
+ * Refresh `spawnedAt` of an existing lock file. Silent no-op on legacy /
180
+ * malformed files so callers don't need try/except.
181
+ *
182
+ * Padded to 512 bytes so on-disk size stays stable while a Windows
183
+ * byte-range lock is held past the metadata region (parity with core-py).
184
+ * Preserves the on-disk line count: a 4-line legacy lock stays 4-line
185
+ * after refresh; a 6-line modern lock stays 6-line (with both `spawnedAt`
186
+ * and `lastActivityAt` bumped).
187
+ */
188
+ export function refreshLockTimestamp(path) {
189
+ const md = parseLockMetadata(path);
190
+ if (md === null)
191
+ return;
192
+ let raw;
193
+ try {
194
+ raw = readFileSync(path, { encoding: 'utf-8' });
195
+ }
196
+ catch {
197
+ return;
198
+ }
199
+ const lineCount = raw.trim().split('\n').length;
200
+ const now = new Date().toISOString();
201
+ let payload;
202
+ if (lineCount >= 6) {
203
+ payload = `${md.pid}\n${md.port}\n${md.token}\n${now}\n${md.credState}\n${now}\n`;
204
+ }
205
+ else {
206
+ payload = `${md.pid}\n${md.port}\n${md.token}\n${now}\n`;
207
+ }
208
+ try {
209
+ writeFileSync(path, payload.padEnd(512, ' '), { encoding: 'utf-8' });
210
+ }
211
+ catch {
212
+ // Best-effort
213
+ }
214
+ }
215
+ /**
216
+ * Remove stale lock files for `serverName` (D9 hybrid TTL). Returns count removed.
217
+ *
218
+ * Called by `runLocalServer` at daemon startup before writing its own lock,
219
+ * preventing pile-up of dozens of `<server>-<port>.lock` files when daemons
220
+ * exit abnormally (Windows OOM, taskkill, signal).
221
+ *
222
+ * Backward-compat: the legacy signature `(serverName, ttlHours, root)` is
223
+ * preserved for pre-D9 callers. New D9 callers pass just `serverName` and
224
+ * rely on the hybrid TTL plus the `lockDir()` lookup (mockable in tests).
225
+ */
226
+ export function sweepStaleLocks(serverName, ttlHours, root) {
227
+ const dir = root !== undefined ? root : self.lockDir();
228
+ if (!existsSync(dir))
229
+ return 0;
230
+ let removed = 0;
231
+ let entries;
232
+ try {
233
+ entries = readdirSync(dir);
234
+ }
235
+ catch {
236
+ return 0;
237
+ }
238
+ const prefix = `${serverName}-`;
239
+ const suffix = '.lock';
240
+ const now = Date.now();
241
+ const configuredTtlMs = (ttlHours ?? DEFAULT_LOCK_TTL_HOURS) * 3600 * 1000;
242
+ for (const entry of entries) {
243
+ if (!entry.startsWith(prefix) || !entry.endsWith(suffix))
244
+ continue;
245
+ const full = join(dir, entry);
246
+ let raw;
247
+ try {
248
+ raw = readFileSync(full, { encoding: 'utf-8' });
249
+ }
250
+ catch {
251
+ try {
252
+ unlinkSync(full);
253
+ removed += 1;
254
+ }
255
+ catch {
256
+ /* ignore */
257
+ }
258
+ continue;
259
+ }
260
+ let meta;
261
+ try {
262
+ meta = parseLock(raw);
263
+ }
264
+ catch {
265
+ try {
266
+ unlinkSync(full);
267
+ removed += 1;
268
+ }
269
+ catch {
270
+ /* ignore */
271
+ }
272
+ continue;
273
+ }
274
+ if (!self.isAlive(meta)) {
275
+ try {
276
+ unlinkSync(full);
277
+ removed += 1;
278
+ }
279
+ catch {
280
+ /* ignore */
281
+ }
282
+ continue;
283
+ }
284
+ const ttlMs = meta.credState === 'configured' ? configuredTtlMs : LIFECYCLE_TTL_UNCONFIGURED_MS;
285
+ const lastActivityMs = meta.lastActivityAt.getTime();
286
+ if (now - lastActivityMs > ttlMs) {
287
+ // Only terminate idle setup daemons aggressively; preserve pre-D9
288
+ // behavior of unlink-only for configured daemons past TTL.
289
+ if (meta.credState === 'unconfigured') {
290
+ self.terminateDaemon(meta.pid);
291
+ }
292
+ try {
293
+ unlinkSync(full);
294
+ removed += 1;
295
+ }
296
+ catch {
297
+ /* ignore */
298
+ }
299
+ }
300
+ }
301
+ return removed;
302
+ }
303
+ /**
304
+ * Write the 4-line lock payload (pid, port, token, ISO timestamp) padded
305
+ * to 512 bytes. Used by `runLocalServer` after the OS file lock is
306
+ * acquired. Continues to write 4-line for backward compat — D9 sweep
307
+ * treats 4-line as `credState='configured'` so behavior is equivalent.
308
+ *
309
+ * Returns the absolute path to the lock file.
310
+ */
311
+ export function writeLockFile(serverName, port, token, root) {
312
+ const dir = locksDir(root);
313
+ const path = join(dir, `${serverName}-${port}.lock`);
314
+ const payload = `${process.pid}\n${port}\n${token}\n${new Date().toISOString()}\n`;
315
+ writeFileSync(path, payload.padEnd(512, ' '), { encoding: 'utf-8', mode: 0o600 });
316
+ return path;
317
+ }
318
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/lifecycle/lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export interface ActiveFormSession {
2
+ token: string;
3
+ clientId: string;
4
+ startedAt: Date;
5
+ expiresAt: Date;
6
+ }
7
+ export declare function claimSession(clientId: string): ActiveFormSession;
8
+ export declare function releaseSession(): void;
9
+ export declare function isSessionActive(): boolean;
10
+ export declare function validateSessionToken(token: string): boolean;
11
+ export declare function getActiveSession(): ActiveFormSession | null;
12
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/relay/session.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,IAAI,CAAA;IACf,SAAS,EAAE,IAAI,CAAA;CAChB;AAQD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAYhE;AAED,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAgB,eAAe,IAAI,OAAO,CAOzC;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAU3D;AAED,wBAAgB,gBAAgB,IAAI,iBAAiB,GAAG,IAAI,CAO3D"}
@@ -0,0 +1,55 @@
1
+ import { randomBytes, timingSafeEqual } from 'node:crypto';
2
+ const SESSION_TTL_MS = 30 * 60 * 1000;
3
+ const TOKEN_BYTES = 32;
4
+ let state = null;
5
+ function now() {
6
+ return new Date();
7
+ }
8
+ export function claimSession(clientId) {
9
+ if (state !== null && state.expiresAt > now()) {
10
+ return state;
11
+ }
12
+ const startedAt = now();
13
+ state = {
14
+ token: randomBytes(TOKEN_BYTES).toString('base64url'),
15
+ clientId,
16
+ startedAt,
17
+ expiresAt: new Date(startedAt.getTime() + SESSION_TTL_MS)
18
+ };
19
+ return state;
20
+ }
21
+ export function releaseSession() {
22
+ state = null;
23
+ }
24
+ export function isSessionActive() {
25
+ if (state === null)
26
+ return false;
27
+ if (state.expiresAt <= now()) {
28
+ state = null;
29
+ return false;
30
+ }
31
+ return true;
32
+ }
33
+ export function validateSessionToken(token) {
34
+ if (state === null)
35
+ return false;
36
+ if (state.expiresAt <= now()) {
37
+ state = null;
38
+ return false;
39
+ }
40
+ const a = Buffer.from(state.token);
41
+ const b = Buffer.from(token);
42
+ if (a.length !== b.length)
43
+ return false;
44
+ return timingSafeEqual(a, b);
45
+ }
46
+ export function getActiveSession() {
47
+ if (state === null)
48
+ return null;
49
+ if (state.expiresAt <= now()) {
50
+ state = null;
51
+ return null;
52
+ }
53
+ return state;
54
+ }
55
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/relay/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE1D,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AACrC,MAAM,WAAW,GAAG,EAAE,CAAA;AAStB,IAAI,KAAK,GAA6B,IAAI,CAAA;AAE1C,SAAS,GAAG;IACV,OAAO,IAAI,IAAI,EAAE,CAAA;AACnB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,SAAS,GAAG,GAAG,EAAE,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAA;IACd,CAAC;IACD,MAAM,SAAS,GAAG,GAAG,EAAE,CAAA;IACvB,KAAK,GAAG;QACN,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;QACrD,QAAQ;QACR,SAAS;QACT,SAAS,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC;KAC1D,CAAA;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,KAAK,GAAG,IAAI,CAAA;AACd,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAChC,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;QAC7B,KAAK,GAAG,IAAI,CAAA;QACZ,OAAO,KAAK,CAAA;IACd,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAChC,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;QAC7B,KAAK,GAAG,IAAI,CAAA;QACZ,OAAO,KAAK,CAAA;IACd,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAClC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC5B,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IACvC,OAAO,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAC9B,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC/B,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;QAC7B,KAAK,GAAG,IAAI,CAAA;QACZ,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Helper to register `config__open_relay` MCP tool — D6 (Task 1.8).
3
+ *
4
+ * Each consumer server calls `registerOpenRelayTool(mcp, SERVER_NAME, SCHEMA)`
5
+ * to get the standard tool registered into its MCP server instance. The tool,
6
+ * when invoked by the LLM, returns the relay form URL and attempts to open
7
+ * the user's browser. If the daemon is dead it auto-respawns; if a relay
8
+ * session is already active in another Claude Code window, the handler
9
+ * returns `session_active` status without auto-opening a second browser tab.
10
+ */
11
+ export interface OpenRelayResult {
12
+ url: string;
13
+ browserOpened: boolean;
14
+ status: 'configured' | 'unconfigured' | 'expired' | 'session_active';
15
+ }
16
+ export interface OpenRelaySchema {
17
+ server: string;
18
+ fields: Array<{
19
+ name: string;
20
+ label?: string;
21
+ required?: boolean;
22
+ secret?: boolean;
23
+ [key: string]: unknown;
24
+ }>;
25
+ [key: string]: unknown;
26
+ }
27
+ /**
28
+ * Build the `open_relay` handler closure. Separated out for testability —
29
+ * tests can call `buildOpenRelayHandler('demo', SCHEMA)` directly without
30
+ * needing a real MCP server instance.
31
+ */
32
+ export declare function buildOpenRelayHandler(serverName: string, _schema: OpenRelaySchema | unknown): () => Promise<OpenRelayResult>;
33
+ /**
34
+ * Minimal shape we require from the MCP server instance to register the
35
+ * tool. Both `@modelcontextprotocol/sdk`'s `McpServer.tool` and the
36
+ * FastMCP-style helpers used in consumer servers conform to this surface.
37
+ */
38
+ export interface ToolRegistrar {
39
+ tool: (config: {
40
+ name: string;
41
+ description?: string;
42
+ }, handler: () => Promise<OpenRelayResult>) => void;
43
+ }
44
+ /**
45
+ * Register `config__open_relay` on the given MCP server instance.
46
+ *
47
+ * Consumer servers add this single line in their tool registry after the
48
+ * other `config__*` registrations:
49
+ *
50
+ * ```ts
51
+ * import { registerOpenRelayTool } from '@n24q02m/mcp-core'
52
+ * registerOpenRelayTool(mcp, SERVER_NAME, RELAY_SCHEMA)
53
+ * ```
54
+ */
55
+ export declare function registerOpenRelayTool(mcp: ToolRegistrar, serverName: string, schema: OpenRelaySchema | unknown): void;
56
+ //# sourceMappingURL=tool-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-helpers.d.ts","sourceRoot":"","sources":["../../src/relay/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,OAAO,CAAA;IACtB,MAAM,EAAE,YAAY,GAAG,cAAc,GAAG,SAAS,GAAG,gBAAgB,CAAA;CACrE;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,OAAO,CAAA;QAClB,MAAM,CAAC,EAAE,OAAO,CAAA;QAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KACvB,CAAC,CAAA;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAeD;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,eAAe,GAAG,OAAO,GACjC,MAAM,OAAO,CAAC,eAAe,CAAC,CAYhC;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,KAAK,IAAI,CAAA;CACxG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,GAAG,IAAI,CASrH"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Helper to register `config__open_relay` MCP tool — D6 (Task 1.8).
3
+ *
4
+ * Each consumer server calls `registerOpenRelayTool(mcp, SERVER_NAME, SCHEMA)`
5
+ * to get the standard tool registered into its MCP server instance. The tool,
6
+ * when invoked by the LLM, returns the relay form URL and attempts to open
7
+ * the user's browser. If the daemon is dead it auto-respawns; if a relay
8
+ * session is already active in another Claude Code window, the handler
9
+ * returns `session_active` status without auto-opening a second browser tab.
10
+ */
11
+ import { daemonCredState, daemonIsAlive, daemonRelayUrl, daemonRespawn } from '../transport/smart-stdio.js';
12
+ import { tryOpenBrowser } from './browser.js';
13
+ import { isSessionActive } from './session.js';
14
+ /**
15
+ * Per-server session probe.
16
+ *
17
+ * The current session module tracks one global active form session at a
18
+ * time (Task 1.5), so the server name is currently ignored. Kept as a
19
+ * one-arg helper to give vi.spyOn hooks a single, server-aware seam that
20
+ * future per-server session tracking can implement without refactoring
21
+ * callers.
22
+ */
23
+ function isSessionActiveForServer(_serverName) {
24
+ return isSessionActive();
25
+ }
26
+ /**
27
+ * Build the `open_relay` handler closure. Separated out for testability —
28
+ * tests can call `buildOpenRelayHandler('demo', SCHEMA)` directly without
29
+ * needing a real MCP server instance.
30
+ */
31
+ export function buildOpenRelayHandler(serverName, _schema) {
32
+ return async function openRelay() {
33
+ const url = daemonIsAlive(serverName) ? daemonRelayUrl(serverName) : daemonRespawn(serverName);
34
+ const credState = daemonCredState(serverName);
35
+ if (isSessionActiveForServer(serverName)) {
36
+ return { url, browserOpened: false, status: 'session_active' };
37
+ }
38
+ const opened = await tryOpenBrowser(url);
39
+ return { url, browserOpened: !!opened, status: credState };
40
+ };
41
+ }
42
+ /**
43
+ * Register `config__open_relay` on the given MCP server instance.
44
+ *
45
+ * Consumer servers add this single line in their tool registry after the
46
+ * other `config__*` registrations:
47
+ *
48
+ * ```ts
49
+ * import { registerOpenRelayTool } from '@n24q02m/mcp-core'
50
+ * registerOpenRelayTool(mcp, SERVER_NAME, RELAY_SCHEMA)
51
+ * ```
52
+ */
53
+ export function registerOpenRelayTool(mcp, serverName, schema) {
54
+ const handler = buildOpenRelayHandler(serverName, schema);
55
+ mcp.tool({
56
+ name: 'config__open_relay',
57
+ description: `Open the relay configuration form for ${serverName} in the user's browser.`
58
+ }, handler);
59
+ }
60
+ //# sourceMappingURL=tool-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-helpers.js","sourceRoot":"","sources":["../../src/relay/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3G,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAoB9C;;;;;;;;GAQG;AACH,SAAS,wBAAwB,CAAC,WAAmB;IACnD,OAAO,eAAe,EAAE,CAAA;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAAkB,EAClB,OAAkC;IAElC,OAAO,KAAK,UAAU,SAAS;QAC7B,MAAM,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,CAAA;QAC9F,MAAM,SAAS,GAAG,eAAe,CAAC,UAAU,CAA8B,CAAA;QAE1E,IAAI,wBAAwB,CAAC,UAAU,CAAC,EAAE,CAAC;YACzC,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAA;QAChE,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAA;QACxC,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;IAC5D,CAAC,CAAA;AACH,CAAC;AAWD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAkB,EAAE,UAAkB,EAAE,MAAiC;IAC7G,MAAM,OAAO,GAAG,qBAAqB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;IACzD,GAAG,CAAC,IAAI,CACN;QACE,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EAAE,yCAAyC,UAAU,yBAAyB;KAC1F,EACD,OAAO,CACR,CAAA;AACH,CAAC"}
@@ -12,6 +12,23 @@ export declare function scheduleReloadExit(): void;
12
12
  export declare function readConfig(serverName: string): Promise<Record<string, string> | null>;
13
13
  export declare function writeConfig(serverName: string, config: Record<string, string>): Promise<void>;
14
14
  export declare function deleteConfig(serverName: string): Promise<void>;
15
+ /**
16
+ * Metadata flag set by `markSetupComplete` after a successful relay-form
17
+ * submission (POST /authorize). `runLocalServer`/`isSchemaComplete` read this
18
+ * to distinguish "user has submitted the form" from "config.enc has values
19
+ * written by a peer-share or partial bootstrap path". Lives alongside the
20
+ * user's normal credential keys in the same per-server config dict.
21
+ */
22
+ export declare const SETUP_COMPLETE_KEY = "_setup_complete";
23
+ /**
24
+ * Set the `_setup_complete` flag in `serverName`'s config. Idempotent.
25
+ * Creates a new entry with just the flag if no prior config exists (useful
26
+ * for all-optional schemas where the user may submit an empty form).
27
+ *
28
+ * Strict equality semantics in `isSchemaComplete` rely on this writing the
29
+ * literal string `"true"`.
30
+ */
31
+ export declare function markSetupComplete(serverName: string): Promise<void>;
15
32
  export declare function listConfigs(): Promise<string[]>;
16
33
  export declare function exportConfig(passphrase: string): Promise<Buffer>;
17
34
  export declare function importConfig(passphrase: string, data: Buffer): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"config-file.d.ts","sourceRoot":"","sources":["../../src/storage/config-file.ts"],"names":[],"mappings":"AAkDA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEvD;AA0BD,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAsDD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAIzC;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAG3F;AAED,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAInG;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYpE;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAGrD;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAItE;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBlF"}
1
+ {"version":3,"file":"config-file.d.ts","sourceRoot":"","sources":["../../src/storage/config-file.ts"],"names":[],"mappings":"AAkDA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEvD;AA0BD,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAsDD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAIzC;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAG3F;AAED,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAInG;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYpE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,oBAAoB,CAAA;AAEnD;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzE;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAGrD;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAItE;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBlF"}
@@ -108,7 +108,7 @@ async function saveStore(store) {
108
108
  }
109
109
  const key = await getKey();
110
110
  const encrypted = await encryptData(key, JSON.stringify(store));
111
- await withRetry(() => writeFile(configPath, encrypted));
111
+ await withRetry(() => writeFile(configPath, encrypted, { mode: 0o600 }));
112
112
  }
113
113
  /**
114
114
  * Schedule a process exit so an MCP client can respawn the server and pick up
@@ -145,6 +145,27 @@ export async function deleteConfig(serverName) {
145
145
  await saveStore(store);
146
146
  }
147
147
  }
148
+ /**
149
+ * Metadata flag set by `markSetupComplete` after a successful relay-form
150
+ * submission (POST /authorize). `runLocalServer`/`isSchemaComplete` read this
151
+ * to distinguish "user has submitted the form" from "config.enc has values
152
+ * written by a peer-share or partial bootstrap path". Lives alongside the
153
+ * user's normal credential keys in the same per-server config dict.
154
+ */
155
+ export const SETUP_COMPLETE_KEY = '_setup_complete';
156
+ /**
157
+ * Set the `_setup_complete` flag in `serverName`'s config. Idempotent.
158
+ * Creates a new entry with just the flag if no prior config exists (useful
159
+ * for all-optional schemas where the user may submit an empty form).
160
+ *
161
+ * Strict equality semantics in `isSchemaComplete` rely on this writing the
162
+ * literal string `"true"`.
163
+ */
164
+ export async function markSetupComplete(serverName) {
165
+ const existing = (await readConfig(serverName)) ?? {};
166
+ existing[SETUP_COMPLETE_KEY] = 'true';
167
+ await writeConfig(serverName, existing);
168
+ }
148
169
  export async function listConfigs() {
149
170
  const store = await loadStore();
150
171
  return Object.keys(store.servers);
@@ -1 +1 @@
1
- {"version":3,"file":"config-file.js","sourceRoot":"","sources":["../../src/storage/config-file.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config-file.js","sourceRoot":"","sources":["../../src/storage/config-file.ts"],"names":[],"mappings":"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"}
@@ -77,7 +77,7 @@ export async function writeSessionLock(serverName, info) {
77
77
  };
78
78
  // Write atomically via temp file + rename
79
79
  const tmpPath = `${path}.tmp`;
80
- await writeFile(tmpPath, JSON.stringify(data), 'utf-8');
80
+ await writeFile(tmpPath, JSON.stringify(data), { encoding: 'utf-8', mode: 0o600 });
81
81
  await rename(tmpPath, path);
82
82
  }
83
83
  export async function releaseSessionLock(serverName) {