instar 1.3.683 → 1.3.684
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/core/DynamicMcpManager.d.ts +105 -0
- package/dist/core/DynamicMcpManager.d.ts.map +1 -0
- package/dist/core/DynamicMcpManager.js +119 -0
- package/dist/core/DynamicMcpManager.js.map +1 -0
- package/dist/core/DynamicMcpService.d.ts +60 -0
- package/dist/core/DynamicMcpService.d.ts.map +1 -0
- package/dist/core/DynamicMcpService.js +76 -0
- package/dist/core/DynamicMcpService.js.map +1 -0
- package/dist/core/McpApprovalNonceStore.d.ts +46 -0
- package/dist/core/McpApprovalNonceStore.d.ts.map +1 -0
- package/dist/core/McpApprovalNonceStore.js +83 -0
- package/dist/core/McpApprovalNonceStore.js.map +1 -0
- package/dist/core/McpLoadedSetStore.d.ts +39 -0
- package/dist/core/McpLoadedSetStore.d.ts.map +1 -0
- package/dist/core/McpLoadedSetStore.js +82 -0
- package/dist/core/McpLoadedSetStore.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +19 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts +20 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +89 -0
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/dynamicMcpConfig.d.ts +141 -0
- package/dist/core/dynamicMcpConfig.d.ts.map +1 -0
- package/dist/core/dynamicMcpConfig.js +125 -0
- package/dist/core/dynamicMcpConfig.js.map +1 -0
- package/dist/core/frameworkSessionLaunch.d.ts +11 -0
- package/dist/core/frameworkSessionLaunch.d.ts.map +1 -1
- package/dist/core/frameworkSessionLaunch.js +7 -0
- package/dist/core/frameworkSessionLaunch.js.map +1 -1
- package/dist/core/mcpPidCapture.d.ts +43 -0
- package/dist/core/mcpPidCapture.d.ts.map +1 -0
- package/dist/core/mcpPidCapture.js +44 -0
- package/dist/core/mcpPidCapture.js.map +1 -0
- package/dist/core/types.d.ts +10 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/McpIdleOffloadSweep.d.ts +61 -0
- package/dist/monitoring/McpIdleOffloadSweep.d.ts.map +1 -0
- package/dist/monitoring/McpIdleOffloadSweep.js +89 -0
- package/dist/monitoring/McpIdleOffloadSweep.js.map +1 -0
- package/dist/monitoring/mcpIdleLiveOffload.d.ts +68 -0
- package/dist/monitoring/mcpIdleLiveOffload.d.ts.map +1 -0
- package/dist/monitoring/mcpIdleLiveOffload.js +59 -0
- package/dist/monitoring/mcpIdleLiveOffload.js.map +1 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +2 -1
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +2 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +165 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +1 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +4 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +51 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +65 -65
- package/src/scaffold/templates.ts +2 -1
- package/upgrades/1.3.684.md +62 -0
- package/upgrades/side-effects/dynamic-mcp-awareness.md +36 -0
- package/upgrades/side-effects/dynamic-mcp-baseline-at-spawn.md +74 -0
- package/upgrades/side-effects/dynamic-mcp-capability-classification.md +36 -0
- package/upgrades/side-effects/dynamic-mcp-driver.md +53 -0
- package/upgrades/side-effects/dynamic-mcp-idle-sweep.md +40 -0
- package/upgrades/side-effects/dynamic-mcp-launch-resolver.md +44 -0
- package/upgrades/side-effects/dynamic-mcp-loaded-set-store.md +37 -0
- package/upgrades/side-effects/dynamic-mcp-nonce-store.md +38 -0
- package/upgrades/side-effects/dynamic-mcp-pure-cores.md +53 -0
- package/upgrades/side-effects/dynamic-mcp-real-offload.md +57 -0
- package/upgrades/side-effects/dynamic-mcp-routes-wiring.md +59 -0
- package/upgrades/side-effects/dynamic-mcp-service.md +41 -0
- package/upgrades/side-effects/dynamic-mcp-sweep-wiring.md +46 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DynamicMcpManager — the load-on-demand / idle-offload DRIVER for the dynamic
|
|
3
|
+
* MCP lifecycle (DYNAMIC-MCP-LIFECYCLE-SPEC). Given a request to load or offload
|
|
4
|
+
* a server for a topic's session, it mutates the committed loaded-set + restarts
|
|
5
|
+
* the session (`--resume`) so the new set takes effect.
|
|
6
|
+
*
|
|
7
|
+
* All IO is INJECTED (state read/write, restart, pid capture/reap, authorization,
|
|
8
|
+
* mid-tool-use probe) so the orchestration — the part that carries real authority
|
|
9
|
+
* (it restarts a live session) — is fully unit-testable in isolation. The class
|
|
10
|
+
* itself touches no fs/process/network directly.
|
|
11
|
+
*
|
|
12
|
+
* The orchestration folds the convergence findings:
|
|
13
|
+
* - per-topic serialization lock so concurrent requests can't lost-update the
|
|
14
|
+
* state file (M2);
|
|
15
|
+
* - TWO-PHASE commit: write the new set un-committed, restart, commit ONLY on a
|
|
16
|
+
* confirmed restart, roll back on any non-ok restart (M1/M3) — the spawn
|
|
17
|
+
* builder ignores an un-committed file, so a failed restart leaves the live
|
|
18
|
+
* session on its OLD set, never a phantom unapproved change;
|
|
19
|
+
* - offload CAPTURES the heavy child pids BEFORE the kill and reaps them AFTER
|
|
20
|
+
* the restart confirms (C1 — the children reparent to launchd and do NOT die
|
|
21
|
+
* with the session, so a naive offload would leak);
|
|
22
|
+
* - mid-tool-use is RE-CHECKED at kill time for an offload (M3) — abort if the
|
|
23
|
+
* session is, or might be, using its tools;
|
|
24
|
+
* - authorization is VERIFIED, never trusted from the caller: a preapproval is
|
|
25
|
+
* re-checked live, otherwise a single-use server-minted nonce bound to
|
|
26
|
+
* (topicId, kind, server) is required (C4). An agent cannot self-authorize.
|
|
27
|
+
*/
|
|
28
|
+
export type McpChangeKind = 'load' | 'offload';
|
|
29
|
+
/** How a request is being authorized. The manager VERIFIES, never trusts. */
|
|
30
|
+
export type RequestActor =
|
|
31
|
+
/** The initial agent-initiated request (or the idle sweep). Authorized only
|
|
32
|
+
* if the topic is live-preapproved; otherwise it returns needs-approval. */
|
|
33
|
+
{
|
|
34
|
+
kind: 'agent';
|
|
35
|
+
}
|
|
36
|
+
/** The operator's approval coming back with the server-minted nonce. */
|
|
37
|
+
| {
|
|
38
|
+
kind: 'operator-approved';
|
|
39
|
+
nonce: string;
|
|
40
|
+
};
|
|
41
|
+
export interface RequestChangeInput {
|
|
42
|
+
topicId: number;
|
|
43
|
+
op: McpChangeKind;
|
|
44
|
+
server: string;
|
|
45
|
+
actor: RequestActor;
|
|
46
|
+
}
|
|
47
|
+
export type RequestChangeResult = {
|
|
48
|
+
status: 'no-op';
|
|
49
|
+
reason: 'already-loaded' | 'not-loaded' | 'unknown-server';
|
|
50
|
+
} | {
|
|
51
|
+
status: 'needs-approval';
|
|
52
|
+
nonce: string;
|
|
53
|
+
prompt: string;
|
|
54
|
+
} | {
|
|
55
|
+
status: 'aborted';
|
|
56
|
+
reason: 'mid-tool-use';
|
|
57
|
+
} | {
|
|
58
|
+
status: 'restart-failed';
|
|
59
|
+
code: string;
|
|
60
|
+
} | {
|
|
61
|
+
status: 'unsupported-unbound';
|
|
62
|
+
} | {
|
|
63
|
+
status: 'applied';
|
|
64
|
+
servers: string[];
|
|
65
|
+
};
|
|
66
|
+
export interface DynamicMcpDeps {
|
|
67
|
+
/** The set the session is CURRENTLY running with (committed state servers, or
|
|
68
|
+
* the baseline / full set when none) — the basis for the mutation. */
|
|
69
|
+
currentServers: (topicId: number) => string[];
|
|
70
|
+
/** Server names defined in `.mcp.json` (for validating a load). */
|
|
71
|
+
allServerNames: () => string[];
|
|
72
|
+
/** Persist the new loaded set. `committed:false` = in-flight (the spawn builder
|
|
73
|
+
* ignores it); `committed:true` = authoritative. Atomic in the impl. */
|
|
74
|
+
writeLoadedSet: (topicId: number, servers: string[], committed: boolean, reason: string) => void;
|
|
75
|
+
/** Live preapproval check (real autonomous-session registry / standing grant),
|
|
76
|
+
* fail-CLOSED. Re-checked at kill time, not trusted from the caller. */
|
|
77
|
+
isPreapproved: (topicId: number) => boolean;
|
|
78
|
+
/** Mint a single-use nonce bound to (topicId, kind, server). */
|
|
79
|
+
mintNonce: (topicId: number, kind: McpChangeKind, server: string) => string;
|
|
80
|
+
/** Consume (verify + invalidate) an operator-supplied nonce. */
|
|
81
|
+
consumeNonce: (topicId: number, kind: McpChangeKind, server: string, nonce: string) => boolean;
|
|
82
|
+
/** Capture the heavy MCP child pids for this session BEFORE an offload kill. */
|
|
83
|
+
captureHeavyPids: (topicId: number, server: string) => number[];
|
|
84
|
+
/** Reap captured orphan pids AFTER the restart confirms (C1). */
|
|
85
|
+
reapPids: (pids: number[]) => void;
|
|
86
|
+
/** Is the session mid-tool-use right now? true / false / null(unknown). */
|
|
87
|
+
isMidToolUse: (topicId: number) => boolean | null;
|
|
88
|
+
/** Restart the session `--resume`. ok:false carries a failure code. */
|
|
89
|
+
restartSession: (topicId: number) => Promise<{
|
|
90
|
+
ok: boolean;
|
|
91
|
+
code?: string;
|
|
92
|
+
}>;
|
|
93
|
+
/** Optional structured audit sink. */
|
|
94
|
+
audit?: (entry: Record<string, unknown>) => void;
|
|
95
|
+
}
|
|
96
|
+
export declare class DynamicMcpManager {
|
|
97
|
+
private readonly deps;
|
|
98
|
+
private readonly locks;
|
|
99
|
+
constructor(deps: DynamicMcpDeps);
|
|
100
|
+
/** Serialize per-topic so concurrent load/offload can't lost-update (M2). */
|
|
101
|
+
requestChange(input: RequestChangeInput): Promise<RequestChangeResult>;
|
|
102
|
+
private requestChangeLocked;
|
|
103
|
+
private audit;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=DynamicMcpManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DynamicMcpManager.d.ts","sourceRoot":"","sources":["../../src/core/DynamicMcpManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DynamicMcpManager — the load-on-demand / idle-offload DRIVER for the dynamic
|
|
3
|
+
* MCP lifecycle (DYNAMIC-MCP-LIFECYCLE-SPEC). Given a request to load or offload
|
|
4
|
+
* a server for a topic's session, it mutates the committed loaded-set + restarts
|
|
5
|
+
* the session (`--resume`) so the new set takes effect.
|
|
6
|
+
*
|
|
7
|
+
* All IO is INJECTED (state read/write, restart, pid capture/reap, authorization,
|
|
8
|
+
* mid-tool-use probe) so the orchestration — the part that carries real authority
|
|
9
|
+
* (it restarts a live session) — is fully unit-testable in isolation. The class
|
|
10
|
+
* itself touches no fs/process/network directly.
|
|
11
|
+
*
|
|
12
|
+
* The orchestration folds the convergence findings:
|
|
13
|
+
* - per-topic serialization lock so concurrent requests can't lost-update the
|
|
14
|
+
* state file (M2);
|
|
15
|
+
* - TWO-PHASE commit: write the new set un-committed, restart, commit ONLY on a
|
|
16
|
+
* confirmed restart, roll back on any non-ok restart (M1/M3) — the spawn
|
|
17
|
+
* builder ignores an un-committed file, so a failed restart leaves the live
|
|
18
|
+
* session on its OLD set, never a phantom unapproved change;
|
|
19
|
+
* - offload CAPTURES the heavy child pids BEFORE the kill and reaps them AFTER
|
|
20
|
+
* the restart confirms (C1 — the children reparent to launchd and do NOT die
|
|
21
|
+
* with the session, so a naive offload would leak);
|
|
22
|
+
* - mid-tool-use is RE-CHECKED at kill time for an offload (M3) — abort if the
|
|
23
|
+
* session is, or might be, using its tools;
|
|
24
|
+
* - authorization is VERIFIED, never trusted from the caller: a preapproval is
|
|
25
|
+
* re-checked live, otherwise a single-use server-minted nonce bound to
|
|
26
|
+
* (topicId, kind, server) is required (C4). An agent cannot self-authorize.
|
|
27
|
+
*/
|
|
28
|
+
import { mutateLoadedServers } from './dynamicMcpConfig.js';
|
|
29
|
+
/** A short, server-authored approval prompt (never agent free-text). */
|
|
30
|
+
function approvalPrompt(op, server) {
|
|
31
|
+
return op === 'load'
|
|
32
|
+
? `I need the "${server}" tool for this — ready for a quick restart? Your conversation is preserved.`
|
|
33
|
+
: `The "${server}" tool has been idle — OK to drop it (a quick restart; it reloads on next use)?`;
|
|
34
|
+
}
|
|
35
|
+
export class DynamicMcpManager {
|
|
36
|
+
deps;
|
|
37
|
+
locks = new Map();
|
|
38
|
+
constructor(deps) {
|
|
39
|
+
this.deps = deps;
|
|
40
|
+
}
|
|
41
|
+
/** Serialize per-topic so concurrent load/offload can't lost-update (M2). */
|
|
42
|
+
async requestChange(input) {
|
|
43
|
+
const prior = this.locks.get(input.topicId) ?? Promise.resolve();
|
|
44
|
+
const run = prior.then(() => this.requestChangeLocked(input), () => this.requestChangeLocked(input));
|
|
45
|
+
// Keep the chain alive but don't leak rejections into the next waiter.
|
|
46
|
+
this.locks.set(input.topicId, run.catch(() => undefined));
|
|
47
|
+
try {
|
|
48
|
+
return await run;
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
// Drop the lock entry once we're the tail (avoid unbounded growth).
|
|
52
|
+
if (this.locks.get(input.topicId) === run || this.locks.get(input.topicId)) {
|
|
53
|
+
// best-effort cleanup; a newer waiter may have replaced it already
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async requestChangeLocked(input) {
|
|
58
|
+
const { topicId, op, server, actor } = input;
|
|
59
|
+
const current = this.deps.currentServers(topicId);
|
|
60
|
+
const names = this.deps.allServerNames();
|
|
61
|
+
const mutateOp = { kind: op, server };
|
|
62
|
+
const result = mutateLoadedServers(current, names, mutateOp);
|
|
63
|
+
if (!result.changed) {
|
|
64
|
+
// Map the mechanical no-op reasons through verbatim.
|
|
65
|
+
const reason = result.reason === 'unknown-server' ? 'unknown-server'
|
|
66
|
+
: result.reason === 'already-loaded' ? 'already-loaded'
|
|
67
|
+
: 'not-loaded';
|
|
68
|
+
this.audit({ topicId, op, server, outcome: 'no-op', reason });
|
|
69
|
+
return { status: 'no-op', reason };
|
|
70
|
+
}
|
|
71
|
+
// ── Authorization (VERIFIED, never trusted from the caller) ──
|
|
72
|
+
const preapproved = this.deps.isPreapproved(topicId);
|
|
73
|
+
let authorized = preapproved;
|
|
74
|
+
if (!authorized && actor.kind === 'operator-approved') {
|
|
75
|
+
authorized = this.deps.consumeNonce(topicId, op, server, actor.nonce);
|
|
76
|
+
}
|
|
77
|
+
if (!authorized) {
|
|
78
|
+
const nonce = this.deps.mintNonce(topicId, op, server);
|
|
79
|
+
this.audit({ topicId, op, server, outcome: 'needs-approval' });
|
|
80
|
+
return { status: 'needs-approval', nonce, prompt: approvalPrompt(op, server) };
|
|
81
|
+
}
|
|
82
|
+
// ── Offload-only: re-check mid-tool-use at kill time (M3) ──
|
|
83
|
+
if (op === 'offload') {
|
|
84
|
+
const mid = this.deps.isMidToolUse(topicId);
|
|
85
|
+
if (mid !== false) {
|
|
86
|
+
this.audit({ topicId, op, server, outcome: 'aborted', reason: 'mid-tool-use', mid });
|
|
87
|
+
return { status: 'aborted', reason: 'mid-tool-use' };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// ── Offload-only: capture heavy child pids BEFORE the kill (C1) ──
|
|
91
|
+
const capturedPids = op === 'offload' ? this.deps.captureHeavyPids(topicId, server) : [];
|
|
92
|
+
// ── Two-phase: write un-committed, restart, commit only on success (M1/M3) ──
|
|
93
|
+
this.deps.writeLoadedSet(topicId, result.servers, false, op);
|
|
94
|
+
const restart = await this.deps.restartSession(topicId);
|
|
95
|
+
if (!restart.ok) {
|
|
96
|
+
// Roll back to the prior committed set; the spawn builder already ignores
|
|
97
|
+
// the un-committed write, but re-asserting the committed truth is cleaner.
|
|
98
|
+
this.deps.writeLoadedSet(topicId, current, true, `${op}-rollback`);
|
|
99
|
+
const code = restart.code ?? 'unknown';
|
|
100
|
+
this.audit({ topicId, op, server, outcome: 'restart-failed', code });
|
|
101
|
+
if (code === 'not_telegram_bound')
|
|
102
|
+
return { status: 'unsupported-unbound' };
|
|
103
|
+
return { status: 'restart-failed', code };
|
|
104
|
+
}
|
|
105
|
+
this.deps.writeLoadedSet(topicId, result.servers, true, op);
|
|
106
|
+
if (op === 'offload' && capturedPids.length > 0) {
|
|
107
|
+
this.deps.reapPids(capturedPids);
|
|
108
|
+
}
|
|
109
|
+
this.audit({ topicId, op, server, outcome: 'applied', servers: result.servers, reapedPids: capturedPids.length });
|
|
110
|
+
return { status: 'applied', servers: result.servers };
|
|
111
|
+
}
|
|
112
|
+
audit(entry) {
|
|
113
|
+
try {
|
|
114
|
+
this.deps.audit?.(entry);
|
|
115
|
+
}
|
|
116
|
+
catch { /* audit is best-effort */ }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=DynamicMcpManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DynamicMcpManager.js","sourceRoot":"","sources":["../../src/core/DynamicMcpManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DynamicMcpService — the composition root for the dynamic MCP lifecycle
|
|
3
|
+
* (DYNAMIC-MCP-LIFECYCLE-SPEC). It assembles the DynamicMcpManager, the loaded-set
|
|
4
|
+
* state store, and the approval-nonce store, wiring them to the host's real
|
|
5
|
+
* primitives (restart, preapproval, pid capture/reap, mid-tool-use probe) which
|
|
6
|
+
* are INJECTED so the whole thing is unit-testable without a live server.
|
|
7
|
+
*
|
|
8
|
+
* The three route handlers (`GET /mcp/session/:topicId`, `POST /mcp/load`,
|
|
9
|
+
* `POST /mcp/offload`) are thin wrappers over `getSessionState` / `requestLoad` /
|
|
10
|
+
* `requestOffload`. AgentServer builds this with the real primitives and gates the
|
|
11
|
+
* routes on `enabled()`.
|
|
12
|
+
*/
|
|
13
|
+
import { type RequestActor, type RequestChangeResult } from './DynamicMcpManager.js';
|
|
14
|
+
import { type DynamicMcpConfig } from './dynamicMcpConfig.js';
|
|
15
|
+
export interface DynamicMcpServicePrimitives {
|
|
16
|
+
/** `<projectDir>` (for `.mcp.json`) and where `.instar/state/mcp-loaded` lives. */
|
|
17
|
+
projectDir: string;
|
|
18
|
+
/** Live feature switch (dev-gate or explicit flag), evaluated per call. */
|
|
19
|
+
enabled: () => boolean;
|
|
20
|
+
/** The dynamicMcp config block (for the baseline `keepWarm`). */
|
|
21
|
+
config: () => DynamicMcpConfig | undefined;
|
|
22
|
+
/** Restart the topic's session `--resume`; ok:false carries a failure code. */
|
|
23
|
+
restart: (topicId: number) => Promise<{
|
|
24
|
+
ok: boolean;
|
|
25
|
+
code?: string;
|
|
26
|
+
}>;
|
|
27
|
+
/** Live preapproval (active autonomous run / standing grant), fail-CLOSED. */
|
|
28
|
+
isPreapproved: (topicId: number) => boolean;
|
|
29
|
+
/** Capture heavy MCP child pids for an offload (before the kill). */
|
|
30
|
+
captureHeavyPids: (topicId: number, server: string) => number[];
|
|
31
|
+
/** Reap captured orphan pids after a confirmed restart. */
|
|
32
|
+
reapPids: (pids: number[]) => void;
|
|
33
|
+
/** Mid-tool-use probe: true / false / null(unknown ⇒ offload aborts). */
|
|
34
|
+
isMidToolUse: (topicId: number) => boolean | null;
|
|
35
|
+
/** Optional audit sink. */
|
|
36
|
+
audit?: (entry: Record<string, unknown>) => void;
|
|
37
|
+
}
|
|
38
|
+
export interface SessionMcpState {
|
|
39
|
+
topicId: number;
|
|
40
|
+
servers: string[];
|
|
41
|
+
preapproved: boolean;
|
|
42
|
+
source: 'committed' | 'baseline' | 'full';
|
|
43
|
+
}
|
|
44
|
+
export declare class DynamicMcpService {
|
|
45
|
+
private readonly p;
|
|
46
|
+
private readonly loadedSet;
|
|
47
|
+
private readonly nonces;
|
|
48
|
+
private readonly manager;
|
|
49
|
+
constructor(p: DynamicMcpServicePrimitives);
|
|
50
|
+
enabled(): boolean;
|
|
51
|
+
/** Read `.mcp.json` server names ([] on absent/unreadable). */
|
|
52
|
+
private mcpServerNames;
|
|
53
|
+
/** The set the session is currently running with + its provenance. */
|
|
54
|
+
private currentServers;
|
|
55
|
+
/** Registry-first read for `GET /mcp/session/:topicId`. */
|
|
56
|
+
getSessionState(topicId: number): SessionMcpState;
|
|
57
|
+
requestLoad(topicId: number, server: string, actor: RequestActor): Promise<RequestChangeResult>;
|
|
58
|
+
requestOffload(topicId: number, server: string, actor: RequestActor): Promise<RequestChangeResult>;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=DynamicMcpService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DynamicMcpService.d.ts","sourceRoot":"","sources":["../../src/core/DynamicMcpService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAA0C,KAAK,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAG7H,OAAO,EAA0B,KAAK,gBAAgB,EAAgB,MAAM,uBAAuB,CAAC;AAEpG,MAAM,WAAW,2BAA2B;IAC1C,mFAAmF;IACnF,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,OAAO,CAAC;IACvB,iEAAiE;IACjE,MAAM,EAAE,MAAM,gBAAgB,GAAG,SAAS,CAAC;IAC3C,+EAA+E;IAC/E,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,8EAA8E;IAC9E,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C,qEAAqE;IACrE,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IAChE,2DAA2D;IAC3D,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACnC,yEAAyE;IACzE,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC;IAClD,2BAA2B;IAC3B,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CAClD;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;CAC3C;AAED,qBAAa,iBAAiB;IAKhB,OAAO,CAAC,QAAQ,CAAC,CAAC;IAJ9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;gBAEf,CAAC,EAAE,2BAA2B;IAkB3D,OAAO,IAAI,OAAO;IAElB,+DAA+D;IAC/D,OAAO,CAAC,cAAc;IAOtB,sEAAsE;IACtE,OAAO,CAAC,cAAc;IAStB,2DAA2D;IAC3D,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe;IAKjD,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAI/F,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,mBAAmB,CAAC;CAGnG"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DynamicMcpService — the composition root for the dynamic MCP lifecycle
|
|
3
|
+
* (DYNAMIC-MCP-LIFECYCLE-SPEC). It assembles the DynamicMcpManager, the loaded-set
|
|
4
|
+
* state store, and the approval-nonce store, wiring them to the host's real
|
|
5
|
+
* primitives (restart, preapproval, pid capture/reap, mid-tool-use probe) which
|
|
6
|
+
* are INJECTED so the whole thing is unit-testable without a live server.
|
|
7
|
+
*
|
|
8
|
+
* The three route handlers (`GET /mcp/session/:topicId`, `POST /mcp/load`,
|
|
9
|
+
* `POST /mcp/offload`) are thin wrappers over `getSessionState` / `requestLoad` /
|
|
10
|
+
* `requestOffload`. AgentServer builds this with the real primitives and gates the
|
|
11
|
+
* routes on `enabled()`.
|
|
12
|
+
*/
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import fs from 'node:fs';
|
|
15
|
+
import { DynamicMcpManager } from './DynamicMcpManager.js';
|
|
16
|
+
import { McpLoadedSetStore } from './McpLoadedSetStore.js';
|
|
17
|
+
import { McpApprovalNonceStore } from './McpApprovalNonceStore.js';
|
|
18
|
+
import { resolveBaselineServers } from './dynamicMcpConfig.js';
|
|
19
|
+
export class DynamicMcpService {
|
|
20
|
+
p;
|
|
21
|
+
loadedSet;
|
|
22
|
+
nonces = new McpApprovalNonceStore();
|
|
23
|
+
manager;
|
|
24
|
+
constructor(p) {
|
|
25
|
+
this.p = p;
|
|
26
|
+
this.loadedSet = new McpLoadedSetStore(path.join(p.projectDir, '.instar', 'state', 'mcp-loaded'));
|
|
27
|
+
const deps = {
|
|
28
|
+
currentServers: (t) => this.currentServers(t).servers,
|
|
29
|
+
allServerNames: () => this.mcpServerNames(),
|
|
30
|
+
writeLoadedSet: (t, servers, committed, reason) => this.loadedSet.write(t, servers, committed, reason),
|
|
31
|
+
isPreapproved: (t) => p.isPreapproved(t),
|
|
32
|
+
mintNonce: (t, kind, server) => this.nonces.mint(t, kind, server),
|
|
33
|
+
consumeNonce: (t, kind, server, nonce) => this.nonces.consume(t, kind, server, nonce),
|
|
34
|
+
captureHeavyPids: (t, server) => p.captureHeavyPids(t, server),
|
|
35
|
+
reapPids: (pids) => p.reapPids(pids),
|
|
36
|
+
isMidToolUse: (t) => p.isMidToolUse(t),
|
|
37
|
+
restartSession: (t) => p.restart(t),
|
|
38
|
+
audit: p.audit,
|
|
39
|
+
};
|
|
40
|
+
this.manager = new DynamicMcpManager(deps);
|
|
41
|
+
}
|
|
42
|
+
enabled() { return this.p.enabled(); }
|
|
43
|
+
/** Read `.mcp.json` server names ([] on absent/unreadable). */
|
|
44
|
+
mcpServerNames() {
|
|
45
|
+
try {
|
|
46
|
+
const full = JSON.parse(fs.readFileSync(path.join(this.p.projectDir, '.mcp.json'), 'utf-8'));
|
|
47
|
+
return Object.keys((full && typeof full === 'object' ? full.mcpServers : undefined) ?? {});
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** The set the session is currently running with + its provenance. */
|
|
54
|
+
currentServers(topicId) {
|
|
55
|
+
const committed = this.loadedSet.readCommitted(topicId);
|
|
56
|
+
if (committed !== null)
|
|
57
|
+
return { servers: committed, source: 'committed' };
|
|
58
|
+
const names = this.mcpServerNames();
|
|
59
|
+
const baseline = resolveBaselineServers(names, this.p.config());
|
|
60
|
+
if (baseline !== null)
|
|
61
|
+
return { servers: baseline, source: 'baseline' };
|
|
62
|
+
return { servers: names, source: 'full' };
|
|
63
|
+
}
|
|
64
|
+
/** Registry-first read for `GET /mcp/session/:topicId`. */
|
|
65
|
+
getSessionState(topicId) {
|
|
66
|
+
const cur = this.currentServers(topicId);
|
|
67
|
+
return { topicId, servers: cur.servers, preapproved: this.p.isPreapproved(topicId), source: cur.source };
|
|
68
|
+
}
|
|
69
|
+
requestLoad(topicId, server, actor) {
|
|
70
|
+
return this.manager.requestChange({ topicId, op: 'load', server, actor });
|
|
71
|
+
}
|
|
72
|
+
requestOffload(topicId, server, actor) {
|
|
73
|
+
return this.manager.requestChange({ topicId, op: 'offload', server, actor });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=DynamicMcpService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DynamicMcpService.js","sourceRoot":"","sources":["../../src/core/DynamicMcpService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* McpApprovalNonceStore — the single-use approval nonce substrate for the dynamic
|
|
3
|
+
* MCP lifecycle's authorization gate (DYNAMIC-MCP-LIFECYCLE-SPEC, fold C4).
|
|
4
|
+
*
|
|
5
|
+
* When a non-preapproved session asks to load/offload a server, the SERVER mints a
|
|
6
|
+
* nonce bound to the exact (topicId, kind, server) and hands it back in the
|
|
7
|
+
* `needs-approval` payload. The operator's approval — arriving on an
|
|
8
|
+
* operator-authenticated channel (dashboard PIN / a MessageSentinel-bound "yes") —
|
|
9
|
+
* carries that nonce; `consume` verifies it and invalidates it (single-use). An
|
|
10
|
+
* agent forwarding an `approved:true` flag over the shared Bearer can NEVER satisfy
|
|
11
|
+
* this, because there is no nonce it could have legitimately obtained for someone
|
|
12
|
+
* else's approval: the nonce is the proof that an operator-authenticated approval
|
|
13
|
+
* actually happened.
|
|
14
|
+
*
|
|
15
|
+
* In-memory + TTL-bounded (an approval is a short-lived flow). A nonce is bound to
|
|
16
|
+
* the exact change so a captured nonce cannot be replayed against a different
|
|
17
|
+
* server, a different kind, or a different topic.
|
|
18
|
+
*/
|
|
19
|
+
export type McpChangeKind = 'load' | 'offload';
|
|
20
|
+
export declare class McpApprovalNonceStore {
|
|
21
|
+
private readonly ttlMs;
|
|
22
|
+
/** Injectable clock for tests; defaults to real time. */
|
|
23
|
+
private readonly now;
|
|
24
|
+
private readonly byKey;
|
|
25
|
+
constructor(ttlMs?: number,
|
|
26
|
+
/** Injectable clock for tests; defaults to real time. */
|
|
27
|
+
now?: () => number);
|
|
28
|
+
/**
|
|
29
|
+
* Mint a fresh single-use nonce for (topicId, kind, server). A new mint for the
|
|
30
|
+
* SAME change replaces any prior outstanding nonce for it (only the latest
|
|
31
|
+
* prompt is valid), so a stale nonce from an earlier prompt cannot be replayed.
|
|
32
|
+
*/
|
|
33
|
+
mint(topicId: number, kind: McpChangeKind, server: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Verify + invalidate. Returns true ONLY when an unexpired nonce for this exact
|
|
36
|
+
* (topicId, kind, server) matches `nonce`. Always single-use: a successful
|
|
37
|
+
* consume removes it; a mismatched/expired attempt removes nothing (so the real
|
|
38
|
+
* operator can still retry with the right one until it expires).
|
|
39
|
+
*/
|
|
40
|
+
consume(topicId: number, kind: McpChangeKind, server: string, nonce: string): boolean;
|
|
41
|
+
/** Best-effort sweep of expired entries (callable on a cadence; not required). */
|
|
42
|
+
pruneExpired(): number;
|
|
43
|
+
/** Outstanding (un-consumed, unexpired) nonce count — for status/tests. */
|
|
44
|
+
size(): number;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=McpApprovalNonceStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpApprovalNonceStore.d.ts","sourceRoot":"","sources":["../../src/core/McpApprovalNonceStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,CAAC;AAc/C,qBAAa,qBAAqB;IAI9B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,yDAAyD;IACzD,OAAO,CAAC,QAAQ,CAAC,GAAG;IALtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;gBAGpC,KAAK,GAAE,MAAuB;IAC/C,yDAAyD;IACxC,GAAG,GAAE,MAAM,MAAyB;IAGvD;;;;OAIG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAOlE;;;;;OAKG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAarF,kFAAkF;IAClF,YAAY,IAAI,MAAM;IAStB,2EAA2E;IAC3E,IAAI,IAAI,MAAM;CAIf"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* McpApprovalNonceStore — the single-use approval nonce substrate for the dynamic
|
|
3
|
+
* MCP lifecycle's authorization gate (DYNAMIC-MCP-LIFECYCLE-SPEC, fold C4).
|
|
4
|
+
*
|
|
5
|
+
* When a non-preapproved session asks to load/offload a server, the SERVER mints a
|
|
6
|
+
* nonce bound to the exact (topicId, kind, server) and hands it back in the
|
|
7
|
+
* `needs-approval` payload. The operator's approval — arriving on an
|
|
8
|
+
* operator-authenticated channel (dashboard PIN / a MessageSentinel-bound "yes") —
|
|
9
|
+
* carries that nonce; `consume` verifies it and invalidates it (single-use). An
|
|
10
|
+
* agent forwarding an `approved:true` flag over the shared Bearer can NEVER satisfy
|
|
11
|
+
* this, because there is no nonce it could have legitimately obtained for someone
|
|
12
|
+
* else's approval: the nonce is the proof that an operator-authenticated approval
|
|
13
|
+
* actually happened.
|
|
14
|
+
*
|
|
15
|
+
* In-memory + TTL-bounded (an approval is a short-lived flow). A nonce is bound to
|
|
16
|
+
* the exact change so a captured nonce cannot be replayed against a different
|
|
17
|
+
* server, a different kind, or a different topic.
|
|
18
|
+
*/
|
|
19
|
+
import crypto from 'node:crypto';
|
|
20
|
+
const DEFAULT_TTL_MS = 10 * 60 * 1000; // 10 min — long enough for a human yes
|
|
21
|
+
function bindKey(topicId, kind, server) {
|
|
22
|
+
return `${topicId}|${kind}|${server}`;
|
|
23
|
+
}
|
|
24
|
+
export class McpApprovalNonceStore {
|
|
25
|
+
ttlMs;
|
|
26
|
+
now;
|
|
27
|
+
byKey = new Map();
|
|
28
|
+
constructor(ttlMs = DEFAULT_TTL_MS,
|
|
29
|
+
/** Injectable clock for tests; defaults to real time. */
|
|
30
|
+
now = () => Date.now()) {
|
|
31
|
+
this.ttlMs = ttlMs;
|
|
32
|
+
this.now = now;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Mint a fresh single-use nonce for (topicId, kind, server). A new mint for the
|
|
36
|
+
* SAME change replaces any prior outstanding nonce for it (only the latest
|
|
37
|
+
* prompt is valid), so a stale nonce from an earlier prompt cannot be replayed.
|
|
38
|
+
*/
|
|
39
|
+
mint(topicId, kind, server) {
|
|
40
|
+
const key = bindKey(topicId, kind, server);
|
|
41
|
+
const nonce = crypto.randomBytes(18).toString('base64url');
|
|
42
|
+
this.byKey.set(key, { nonce, key, expiresAt: this.now() + this.ttlMs });
|
|
43
|
+
return nonce;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Verify + invalidate. Returns true ONLY when an unexpired nonce for this exact
|
|
47
|
+
* (topicId, kind, server) matches `nonce`. Always single-use: a successful
|
|
48
|
+
* consume removes it; a mismatched/expired attempt removes nothing (so the real
|
|
49
|
+
* operator can still retry with the right one until it expires).
|
|
50
|
+
*/
|
|
51
|
+
consume(topicId, kind, server, nonce) {
|
|
52
|
+
const key = bindKey(topicId, kind, server);
|
|
53
|
+
const entry = this.byKey.get(key);
|
|
54
|
+
if (!entry)
|
|
55
|
+
return false;
|
|
56
|
+
if (this.now() > entry.expiresAt) {
|
|
57
|
+
this.byKey.delete(key);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (entry.nonce !== nonce)
|
|
61
|
+
return false;
|
|
62
|
+
this.byKey.delete(key); // single-use
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
/** Best-effort sweep of expired entries (callable on a cadence; not required). */
|
|
66
|
+
pruneExpired() {
|
|
67
|
+
const t = this.now();
|
|
68
|
+
let removed = 0;
|
|
69
|
+
for (const [key, entry] of this.byKey) {
|
|
70
|
+
if (t > entry.expiresAt) {
|
|
71
|
+
this.byKey.delete(key);
|
|
72
|
+
removed++;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return removed;
|
|
76
|
+
}
|
|
77
|
+
/** Outstanding (un-consumed, unexpired) nonce count — for status/tests. */
|
|
78
|
+
size() {
|
|
79
|
+
this.pruneExpired();
|
|
80
|
+
return this.byKey.size;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=McpApprovalNonceStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpApprovalNonceStore.js","sourceRoot":"","sources":["../../src/core/McpApprovalNonceStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,MAAM,MAAM,aAAa,CAAC;AAUjC,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,uCAAuC;AAE9E,SAAS,OAAO,CAAC,OAAe,EAAE,IAAmB,EAAE,MAAc;IACnE,OAAO,GAAG,OAAO,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,OAAO,qBAAqB;IAIb;IAEA;IALF,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IAEvD,YACmB,QAAgB,cAAc;IAC/C,yDAAyD;IACxC,MAAoB,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;QAFpC,UAAK,GAAL,KAAK,CAAyB;QAE9B,QAAG,GAAH,GAAG,CAAiC;IACpD,CAAC;IAEJ;;;;OAIG;IACH,IAAI,CAAC,OAAe,EAAE,IAAmB,EAAE,MAAc;QACvD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC3D,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACxE,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;OAKG;IACH,OAAO,CAAC,OAAe,EAAE,IAAmB,EAAE,MAAc,EAAE,KAAa;QACzE,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QACzB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK;YAAE,OAAO,KAAK,CAAC;QACxC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kFAAkF;IAClF,YAAY;QACV,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACrB,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,IAAI,CAAC,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;gBAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAAC,OAAO,EAAE,CAAC;YAAC,CAAC;QACjE,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,2EAA2E;IAC3E,IAAI;QACF,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* McpLoadedSetStore — durable per-topic "which MCP servers is this session running
|
|
3
|
+
* with" state for the dynamic MCP lifecycle (DYNAMIC-MCP-LIFECYCLE-SPEC). This is
|
|
4
|
+
* the single source of truth the spawn builder reads (to launch with the current
|
|
5
|
+
* set) and the driver writes (load/offload), so a `--resume` restart re-launches
|
|
6
|
+
* with the right set.
|
|
7
|
+
*
|
|
8
|
+
* Two-phase commit (fold M1/M3): a `committed:false` write is in-flight and is
|
|
9
|
+
* IGNORED by the reader (`readCommitted` returns null) — so a load/offload that
|
|
10
|
+
* writes the new set then fails to restart leaves the live session on its OLD set,
|
|
11
|
+
* never a phantom unapproved change. Only a `committed:true` write is authoritative.
|
|
12
|
+
*
|
|
13
|
+
* Writes are atomic (temp + rename on the same dir) so a crash mid-write can never
|
|
14
|
+
* leave a torn file the reader would choke on.
|
|
15
|
+
*/
|
|
16
|
+
export interface LoadedSetRecord {
|
|
17
|
+
servers: string[];
|
|
18
|
+
committed: boolean;
|
|
19
|
+
updatedAt: string;
|
|
20
|
+
reason: string;
|
|
21
|
+
}
|
|
22
|
+
export declare class McpLoadedSetStore {
|
|
23
|
+
private readonly dir;
|
|
24
|
+
/** @param dir e.g. `<projectDir>/.instar/state/mcp-loaded` */
|
|
25
|
+
constructor(dir: string);
|
|
26
|
+
private pathFor;
|
|
27
|
+
/** Raw record (committed or not), or null if absent/unreadable. */
|
|
28
|
+
read(topicId: number): LoadedSetRecord | null;
|
|
29
|
+
/** True iff a state file EXISTS on disk (regardless of readability). */
|
|
30
|
+
exists(topicId: number): boolean;
|
|
31
|
+
/** The COMMITTED server set, or null when there is no committed record (absent,
|
|
32
|
+
* un-committed/in-flight, or unreadable). The reader's two-phase contract. */
|
|
33
|
+
readCommitted(topicId: number): string[] | null;
|
|
34
|
+
/** Atomically write the loaded set. `committed:false` = in-flight (ignored by
|
|
35
|
+
* readCommitted); `committed:true` = authoritative. Throws on a write failure
|
|
36
|
+
* so the caller (driver) can react — the caller is responsible for fail-safety. */
|
|
37
|
+
write(topicId: number, servers: string[], committed: boolean, reason: string, nowIso?: string): void;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=McpLoadedSetStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpLoadedSetStore.d.ts","sourceRoot":"","sources":["../../src/core/McpLoadedSetStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,iBAAiB;IAEhB,OAAO,CAAC,QAAQ,CAAC,GAAG;IADhC,8DAA8D;gBACjC,GAAG,EAAE,MAAM;IAExC,OAAO,CAAC,OAAO;IAIf,mEAAmE;IACnE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAmB7C,wEAAwE;IACxE,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIhC;mFAC+E;IAC/E,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAM/C;;wFAEoF;IACpF,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;CAarG"}
|