@praxis-ai/praxis 0.1.3 → 0.1.5
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/agentCore/index.d.ts +11 -3
- package/dist/applicationLayer/applicationRuntime.js +7 -1
- package/dist/basetool/authoring.d.ts +2 -0
- package/dist/basetool/authoring.js +2 -0
- package/dist/basetool/catalog.d.ts +1 -1
- package/dist/basetool/catalog.js +86 -4
- package/dist/basetool/core/index.d.ts +4 -2
- package/dist/basetool/core/index.js +8 -0
- package/dist/basetool/core/mcpCompletions.d.ts +2 -0
- package/dist/basetool/core/mcpCompletions.js +70 -0
- package/dist/basetool/core/mcpPrompts.d.ts +2 -0
- package/dist/basetool/core/mcpPrompts.js +48 -0
- package/dist/basetool/core/mcpResources.js +41 -5
- package/dist/basetool/profiles.js +15 -1
- package/dist/basetool/registry.d.ts +1 -1
- package/dist/basetool/supportCatalog.js +23 -6
- package/dist/runtimeImplementation/praxisRuntimeKernel.js +1696 -1499
- package/dist/runtimeImplementation/runtime.execEngine/baseToolApprovalScope.js +11 -0
- package/dist/runtimeImplementation/runtime.execEngine/baseToolExecutorPortFactory.js +13 -1
- package/dist/runtimeImplementation/runtime.execEngine/baseToolPolicyAdjudicator.js +14 -0
- package/dist/runtimeImplementation/runtime.execEngine/mcpRuntimeAdapter.d.ts +27 -0
- package/dist/runtimeImplementation/runtime.execEngine/mcpRuntimeAdapter.js +648 -56
- package/dist/runtimeImplementation/runtime.execEngine/promptContextAssembly.d.ts +1 -0
- package/dist/runtimeImplementation/runtime.execEngine/promptContextAssembly.js +18 -0
- package/dist/runtimeImplementation/runtime.mcpPlane/index.d.ts +20 -7
- package/dist/runtimeImplementation/runtime.mcpPlane/index.js +105 -89
- package/dist/toolBase/catalog.d.ts +24 -0
- package/dist/toolBase/catalog.js +41 -3
- package/dist/toolBase/profiles.d.ts +3 -3
- package/dist/toolBase/profiles.js +2 -0
- package/dist/toolBase/types.d.ts +1 -1
- package/examples/raxode-mcp-plus-ten-server.config.json +229 -0
- package/examples/scripts/README.md +8 -2
- package/examples/scripts/mcp-plus-native-smoke.ts +1296 -0
- package/package.json +4 -2
- package/raxode-tui/dist/raxode-cli/backend/agents/codingAgent/prompts/tool-use.md +1 -1
- package/raxode-tui/dist/raxode-cli/backend/application/mcpConfig.d.ts +9 -0
- package/raxode-tui/dist/raxode-cli/backend/application/mcpConfig.js +65 -0
- package/raxode-tui/dist/raxode-cli/backend/application/mcpReadinessSummary.d.ts +28 -0
- package/raxode-tui/dist/raxode-cli/backend/application/mcpReadinessSummary.js +57 -0
- package/raxode-tui/dist/raxode-cli/backend/application/runtimeReadiness.d.ts +5 -1
- package/raxode-tui/dist/raxode-cli/backend/application/runtimeReadiness.js +40 -0
- package/raxode-tui/dist/raxode-cli/backend/application/stdioApplicationServer.js +6 -0
- package/raxode-tui/dist/raxode-cli/backend/directApplicationBackend.d.ts +4 -0
- package/raxode-tui/dist/raxode-cli/backend/directApplicationBackend.js +14 -0
- package/raxode-tui/dist/raxode-cli/backend/raxodeBackend.d.ts +1 -1
- package/raxode-tui/dist/raxode-cli/backend/raxodeBackend.js +16 -1
- package/raxode-tui/dist/raxode-cli/contracts.d.ts +1 -0
- package/raxode-tui/dist/raxode-cli/frontend/bridge/readiness.js +24 -0
- package/raxode-tui/dist/raxode-cli/frontend/tui/app/direct-tui.js +35 -0
- package/raxode-tui/dist/raxode-cli/frontend/tui/cli/raxode-cli.d.ts +2 -0
- package/raxode-tui/dist/raxode-cli/frontend/tui/cli/raxode-cli.js +8 -0
- package/raxode-tui/dist/raxode-cli/frontend/tui/config/raxode-config.d.ts +31 -0
- package/raxode-tui/dist/raxode-cli/frontend/tui/config/raxode-config.js +129 -0
- package/raxode-tui/dist/raxode-cli/index.d.ts +1 -0
- package/raxode-tui/package.json +1 -1
|
@@ -0,0 +1,1296 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import type { AddressInfo } from "node:net";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
import { createCredentialRef } from "../../src/modelAdapter/authProfileLayer/credentialRef.js";
|
|
8
|
+
import { createChatGPTCodexAuthEnvelope } from "../../src/modelAdapter/authProfileLayer/codexAuth.js";
|
|
9
|
+
import { compileAgent, harness, loop, model, policy, PraxisAgent, toolPolicies } from "../../src/runtimeImplementation/runtimeAgentManifest.js";
|
|
10
|
+
import { createPraxisRuntimeKernel, type AgentModelCacheDebugRecord, type AgentModelCallProgressEvent } from "../../src/runtimeImplementation/praxisRuntimeKernel.js";
|
|
11
|
+
import { createMcpRuntimeAdapter, type McpRuntimeServerProfile } from "../../src/runtimeImplementation/runtime.execEngine/mcpRuntimeAdapter.js";
|
|
12
|
+
import { runDevDoctor } from "../../src/devdoctor/index.js";
|
|
13
|
+
import type { ExecutionMonitorReport } from "../../src/runtimeImplementation/runtime.executionMonitor/index.js";
|
|
14
|
+
import {
|
|
15
|
+
createInMemoryMcpPlusSkillStore,
|
|
16
|
+
mcp,
|
|
17
|
+
type McpHarnessModuleSpec,
|
|
18
|
+
type McpHarnessServerSpec,
|
|
19
|
+
} from "../../src/runtimeImplementation/runtime.mcpPlane/index.js";
|
|
20
|
+
|
|
21
|
+
type DiscoveredServer = {
|
|
22
|
+
serverId: string;
|
|
23
|
+
title: string;
|
|
24
|
+
summary: string;
|
|
25
|
+
profile: Extract<McpRuntimeServerProfile, { transport: "stdio" }>;
|
|
26
|
+
tools: Array<{
|
|
27
|
+
name: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
inputSchema?: unknown;
|
|
30
|
+
}>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type LiveCallProbe = {
|
|
34
|
+
serverId: string;
|
|
35
|
+
toolName: string;
|
|
36
|
+
arguments: Record<string, unknown>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type LiveCallProbeResult = LiveCallProbe & {
|
|
40
|
+
ok: boolean;
|
|
41
|
+
outputPreview?: string;
|
|
42
|
+
error?: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type LivePromptProbeResult = {
|
|
46
|
+
serverId: string;
|
|
47
|
+
operation: "list" | "get";
|
|
48
|
+
name?: string;
|
|
49
|
+
ok: boolean;
|
|
50
|
+
outputPreview?: string;
|
|
51
|
+
error?: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
type LiveResourceProbeResult = {
|
|
55
|
+
serverId: string;
|
|
56
|
+
operation: "list" | "templates" | "read";
|
|
57
|
+
uri?: string;
|
|
58
|
+
ok: boolean;
|
|
59
|
+
outputPreview?: string;
|
|
60
|
+
error?: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type LiveCompletionProbeResult = {
|
|
64
|
+
serverId: string;
|
|
65
|
+
refType: "ref/resource" | "ref/prompt";
|
|
66
|
+
argumentName: string;
|
|
67
|
+
ok: boolean;
|
|
68
|
+
values?: readonly string[];
|
|
69
|
+
outputPreview?: string;
|
|
70
|
+
error?: string;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
type HostSemanticsProbeResult = {
|
|
74
|
+
serverId: string;
|
|
75
|
+
ok: boolean;
|
|
76
|
+
rootName?: string;
|
|
77
|
+
requestedMethods: readonly string[];
|
|
78
|
+
notificationMethods: readonly string[];
|
|
79
|
+
outputPreview?: string;
|
|
80
|
+
error?: string;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
type LiveTransportProbeResult = {
|
|
84
|
+
transport: "http";
|
|
85
|
+
serverId: string;
|
|
86
|
+
ok: boolean;
|
|
87
|
+
sessionIdObserved: boolean;
|
|
88
|
+
protocolVersionObserved: boolean;
|
|
89
|
+
eventStreamObserved?: boolean;
|
|
90
|
+
outputPreview?: string;
|
|
91
|
+
error?: string;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
type RuntimeToolFlowSummary = {
|
|
95
|
+
mode: "native" | "mcp-plus";
|
|
96
|
+
ok: boolean;
|
|
97
|
+
providerToolName?: string;
|
|
98
|
+
toolId?: string;
|
|
99
|
+
toolCallCount: number;
|
|
100
|
+
toolCallOkCount: number;
|
|
101
|
+
toolOutputPreview?: string;
|
|
102
|
+
outputPreview?: string;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
type RuntimeSkillFlowSummary = {
|
|
106
|
+
ok: boolean;
|
|
107
|
+
providerToolName?: string;
|
|
108
|
+
toolCallCount: number;
|
|
109
|
+
toolCallOkCount: number;
|
|
110
|
+
skillBodyMarker: string;
|
|
111
|
+
skillPitfallMarker: string;
|
|
112
|
+
firstStablePrefixContainsSkillBody: boolean;
|
|
113
|
+
firstProviderBodyContainsSkillBody: boolean;
|
|
114
|
+
secondStablePrefixContainsSkillBody: boolean;
|
|
115
|
+
secondDynamicInputContainsSkillBody: boolean;
|
|
116
|
+
secondDynamicInputContainsSkillPitfall: boolean;
|
|
117
|
+
secondObservationSegmentCachePolicy?: string;
|
|
118
|
+
secondProviderInstructionSegmentKinds: readonly string[];
|
|
119
|
+
secondProviderDynamicInputSegmentKinds: readonly string[];
|
|
120
|
+
outputPreview?: string;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
type DevdoctorCacheXraySummary = {
|
|
124
|
+
status: "ok" | "warning" | "error" | "no-model-calls";
|
|
125
|
+
cache?: {
|
|
126
|
+
weightedCacheHitRate?: number;
|
|
127
|
+
cacheTelemetryCoverage?: number;
|
|
128
|
+
providerCacheMissCalls?: number;
|
|
129
|
+
previousResponseReuseCalls?: number;
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
type PromptPackFlowSummary = {
|
|
134
|
+
mcpPlusPreludePresent: boolean;
|
|
135
|
+
mcpPlusPreludeSegmentKind?: string;
|
|
136
|
+
mcpPlusPreludeCachePolicy?: string;
|
|
137
|
+
mcpPlusPreludeInCacheablePrefix: boolean;
|
|
138
|
+
mcpPlusPreludeMaterialIndex: number;
|
|
139
|
+
builtInToolDeclarationsMaterialIndex: number;
|
|
140
|
+
mcpPlusPreludeAfterBuiltInToolDeclarations: boolean;
|
|
141
|
+
refsAroundMcpPlusPrelude: readonly string[];
|
|
142
|
+
providerInstructionSegmentKinds: readonly string[];
|
|
143
|
+
providerDynamicInputSegmentKinds: readonly string[];
|
|
144
|
+
cacheRiskWarnings: readonly string[];
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const repoRoot = path.resolve(import.meta.dirname, "../..");
|
|
148
|
+
const runRoot = process.env.PRAXIS_MCP_SMOKE_DIR
|
|
149
|
+
? path.resolve(process.env.PRAXIS_MCP_SMOKE_DIR)
|
|
150
|
+
: path.join(os.tmpdir(), `praxis-mcp-smoke-${Date.now()}`);
|
|
151
|
+
|
|
152
|
+
const SERVER_PROFILES: Array<{
|
|
153
|
+
serverId: string;
|
|
154
|
+
title: string;
|
|
155
|
+
summary: string;
|
|
156
|
+
profile: Extract<McpRuntimeServerProfile, { transport: "stdio" }>;
|
|
157
|
+
}> = [
|
|
158
|
+
{
|
|
159
|
+
serverId: "filesystem-praxis",
|
|
160
|
+
title: "Filesystem Praxis",
|
|
161
|
+
summary: "Filesystem MCP server scoped to the Praxis repository.",
|
|
162
|
+
profile: {
|
|
163
|
+
serverId: "filesystem-praxis",
|
|
164
|
+
transport: "stdio",
|
|
165
|
+
command: "npx",
|
|
166
|
+
args: ["-y", "@modelcontextprotocol/server-filesystem", repoRoot],
|
|
167
|
+
cwd: repoRoot,
|
|
168
|
+
timeoutMs: 20_000,
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
serverId: "filesystem-tmp",
|
|
173
|
+
title: "Filesystem Tmp",
|
|
174
|
+
summary: "Filesystem MCP server scoped to /tmp.",
|
|
175
|
+
profile: {
|
|
176
|
+
serverId: "filesystem-tmp",
|
|
177
|
+
transport: "stdio",
|
|
178
|
+
command: "npx",
|
|
179
|
+
args: ["-y", "@modelcontextprotocol/server-filesystem", os.tmpdir()],
|
|
180
|
+
cwd: repoRoot,
|
|
181
|
+
timeoutMs: 20_000,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
serverId: "memory",
|
|
186
|
+
title: "Memory",
|
|
187
|
+
summary: "Reference memory graph MCP server.",
|
|
188
|
+
profile: {
|
|
189
|
+
serverId: "memory",
|
|
190
|
+
transport: "stdio",
|
|
191
|
+
command: "npx",
|
|
192
|
+
args: ["-y", "@modelcontextprotocol/server-memory"],
|
|
193
|
+
cwd: repoRoot,
|
|
194
|
+
timeoutMs: 20_000,
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
serverId: "sequential-thinking",
|
|
199
|
+
title: "Sequential Thinking",
|
|
200
|
+
summary: "Structured reasoning MCP server.",
|
|
201
|
+
profile: {
|
|
202
|
+
serverId: "sequential-thinking",
|
|
203
|
+
transport: "stdio",
|
|
204
|
+
command: "npx",
|
|
205
|
+
args: ["-y", "@modelcontextprotocol/server-sequential-thinking"],
|
|
206
|
+
cwd: repoRoot,
|
|
207
|
+
timeoutMs: 20_000,
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
serverId: "everything",
|
|
212
|
+
title: "Everything",
|
|
213
|
+
summary: "Reference MCP server with broad protocol examples.",
|
|
214
|
+
profile: {
|
|
215
|
+
serverId: "everything",
|
|
216
|
+
transport: "stdio",
|
|
217
|
+
command: "npx",
|
|
218
|
+
args: ["-y", "@modelcontextprotocol/server-everything"],
|
|
219
|
+
cwd: repoRoot,
|
|
220
|
+
timeoutMs: 20_000,
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
serverId: "playwright",
|
|
225
|
+
title: "Playwright",
|
|
226
|
+
summary: "Browser automation through Playwright MCP.",
|
|
227
|
+
profile: {
|
|
228
|
+
serverId: "playwright",
|
|
229
|
+
transport: "stdio",
|
|
230
|
+
command: "npx",
|
|
231
|
+
args: ["-y", "@playwright/mcp@latest", "--headless"],
|
|
232
|
+
cwd: repoRoot,
|
|
233
|
+
timeoutMs: 30_000,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
serverId: "time",
|
|
238
|
+
title: "Time",
|
|
239
|
+
summary: "Time and timezone conversion MCP server.",
|
|
240
|
+
profile: {
|
|
241
|
+
serverId: "time",
|
|
242
|
+
transport: "stdio",
|
|
243
|
+
command: "uvx",
|
|
244
|
+
args: ["mcp-server-time", "--local-timezone", "UTC"],
|
|
245
|
+
cwd: repoRoot,
|
|
246
|
+
timeoutMs: 30_000,
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
serverId: "fetch",
|
|
251
|
+
title: "Fetch",
|
|
252
|
+
summary: "HTTP fetch MCP server.",
|
|
253
|
+
profile: {
|
|
254
|
+
serverId: "fetch",
|
|
255
|
+
transport: "stdio",
|
|
256
|
+
command: "uvx",
|
|
257
|
+
args: ["mcp-server-fetch"],
|
|
258
|
+
cwd: repoRoot,
|
|
259
|
+
timeoutMs: 30_000,
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
serverId: "git",
|
|
264
|
+
title: "Git",
|
|
265
|
+
summary: "Git repository MCP server scoped to Praxis.",
|
|
266
|
+
profile: {
|
|
267
|
+
serverId: "git",
|
|
268
|
+
transport: "stdio",
|
|
269
|
+
command: "uvx",
|
|
270
|
+
args: ["mcp-server-git", "--repository", repoRoot],
|
|
271
|
+
cwd: repoRoot,
|
|
272
|
+
timeoutMs: 30_000,
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
serverId: "sqlite",
|
|
277
|
+
title: "SQLite",
|
|
278
|
+
summary: "SQLite MCP server backed by a temporary database.",
|
|
279
|
+
profile: {
|
|
280
|
+
serverId: "sqlite",
|
|
281
|
+
transport: "stdio",
|
|
282
|
+
command: "uvx",
|
|
283
|
+
args: ["mcp-server-sqlite", "--db-path", path.join(os.tmpdir(), "praxis-mcp-smoke.sqlite")],
|
|
284
|
+
cwd: repoRoot,
|
|
285
|
+
timeoutMs: 30_000,
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
];
|
|
289
|
+
|
|
290
|
+
class McpSmokeAgent extends PraxisAgent {
|
|
291
|
+
identity = "agent.mcp-smoke";
|
|
292
|
+
model = model("gpt-5.4", { carrierId: "carrier.mcp-smoke" });
|
|
293
|
+
toolPolicy = toolPolicies.bapr();
|
|
294
|
+
harness = harness({
|
|
295
|
+
tools: mcp.recommendedTools(),
|
|
296
|
+
policy: policy({
|
|
297
|
+
allowProviderCall: true,
|
|
298
|
+
allowToolExecution: true,
|
|
299
|
+
scopes: ["agent.invoke", "tool.execute", "mcp:call", "mcp:resource:list", "mcp:prompt:list", "mcp:prompt:get"],
|
|
300
|
+
}),
|
|
301
|
+
loop: loop({ strategy: "tool-calling-v1", maxModelTurns: 1, maxToolCalls: 0 }),
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
class McpSmokeToolFlowAgent extends PraxisAgent {
|
|
306
|
+
identity = "agent.mcp-smoke-tool-flow";
|
|
307
|
+
model = model("gpt-5.4", { carrierId: "carrier.mcp-smoke-tool-flow" });
|
|
308
|
+
toolPolicy = toolPolicies.bapr();
|
|
309
|
+
harness = harness({
|
|
310
|
+
tools: mcp.recommendedTools(),
|
|
311
|
+
policy: policy({
|
|
312
|
+
allowProviderCall: true,
|
|
313
|
+
allowToolExecution: true,
|
|
314
|
+
scopes: ["agent.invoke", "tool.execute", "mcp:call", "mcp:prompt:list", "mcp:prompt:get"],
|
|
315
|
+
}),
|
|
316
|
+
loop: loop({ strategy: "tool-calling-v1", maxModelTurns: 2, maxToolCalls: 1 }),
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function authEnvelope() {
|
|
321
|
+
const ref = createCredentialRef({
|
|
322
|
+
id: "mcp-smoke",
|
|
323
|
+
provider: "openai",
|
|
324
|
+
credentialType: "chatgpt_codex_oauth",
|
|
325
|
+
source: { kind: "test", label: "mcp-smoke" },
|
|
326
|
+
});
|
|
327
|
+
if (!ref.ok) throw new Error("Failed to create MCP smoke credential ref.");
|
|
328
|
+
return createChatGPTCodexAuthEnvelope({
|
|
329
|
+
credentialRef: ref.credentialRef,
|
|
330
|
+
snapshot: {
|
|
331
|
+
sourceShape: "chatgpt-auth-tokens",
|
|
332
|
+
authMode: "chatgpt",
|
|
333
|
+
accessToken: "mcp-smoke-token",
|
|
334
|
+
refreshTokenPresent: false,
|
|
335
|
+
idTokenPresent: false,
|
|
336
|
+
accountId: "mcp-smoke-account",
|
|
337
|
+
accountIsFedramp: false,
|
|
338
|
+
publicSafe: false,
|
|
339
|
+
},
|
|
340
|
+
}).envelope;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function jsonl(value: unknown): string {
|
|
344
|
+
return `${JSON.stringify(value)}\n`;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function toolManifest(server: DiscoveredServer) {
|
|
348
|
+
const pinnedCount = server.tools.length <= 3 ? server.tools.length : 2;
|
|
349
|
+
const pinnedTools = server.tools.slice(0, pinnedCount).map((tool) => tool.name);
|
|
350
|
+
const indexedTools = server.tools.slice(pinnedCount).map((tool) => tool.name);
|
|
351
|
+
return {
|
|
352
|
+
server: {
|
|
353
|
+
id: server.serverId,
|
|
354
|
+
title: server.title,
|
|
355
|
+
summary: server.summary,
|
|
356
|
+
},
|
|
357
|
+
exposure: {
|
|
358
|
+
pinnedTools,
|
|
359
|
+
indexedTools,
|
|
360
|
+
toolCards: Object.fromEntries(server.tools.slice(pinnedCount).map((tool) => [tool.name, {
|
|
361
|
+
title: tool.name,
|
|
362
|
+
summary: tool.description ?? tool.name,
|
|
363
|
+
keywords: tool.name.split(/[^a-zA-Z0-9]+/u).filter(Boolean),
|
|
364
|
+
}])),
|
|
365
|
+
},
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function moduleFor(mode: "native" | "mcp-plus", discovered: readonly DiscoveredServer[]): McpHarnessModuleSpec {
|
|
370
|
+
const servers: McpHarnessServerSpec[] = discovered.map((server) => ({
|
|
371
|
+
...server.profile,
|
|
372
|
+
serverId: server.serverId,
|
|
373
|
+
title: server.title,
|
|
374
|
+
summary: server.summary,
|
|
375
|
+
mode,
|
|
376
|
+
...(mode === "mcp-plus" ? { manifest: toolManifest(server) } : {}),
|
|
377
|
+
}));
|
|
378
|
+
return mcp.module({ servers, metadata: { source: "examples.scripts.mcp-plus-native-smoke", mode } });
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function outputPreview(value: unknown): string {
|
|
382
|
+
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
383
|
+
return (text ?? "").slice(0, 500);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function liveCallProbesFor(discovered: readonly DiscoveredServer[]): LiveCallProbe[] {
|
|
387
|
+
const servers = new Map(discovered.map((server) => [server.serverId, new Set(server.tools.map((tool) => tool.name))]));
|
|
388
|
+
const probes: LiveCallProbe[] = [
|
|
389
|
+
{
|
|
390
|
+
serverId: "filesystem-praxis",
|
|
391
|
+
toolName: "list_allowed_directories",
|
|
392
|
+
arguments: {},
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
serverId: "filesystem-tmp",
|
|
396
|
+
toolName: "list_allowed_directories",
|
|
397
|
+
arguments: {},
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
serverId: "memory",
|
|
401
|
+
toolName: "read_graph",
|
|
402
|
+
arguments: {},
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
serverId: "sequential-thinking",
|
|
406
|
+
toolName: "sequentialthinking",
|
|
407
|
+
arguments: {
|
|
408
|
+
thought: "Praxis MCP smoke probe.",
|
|
409
|
+
nextThoughtNeeded: false,
|
|
410
|
+
thoughtNumber: 1,
|
|
411
|
+
totalThoughts: 1,
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
serverId: "everything",
|
|
416
|
+
toolName: "echo",
|
|
417
|
+
arguments: { message: "praxis mcp smoke" },
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
serverId: "playwright",
|
|
421
|
+
toolName: "browser_navigate",
|
|
422
|
+
arguments: { url: "data:text/html,<title>Praxis MCP Smoke</title><main>Praxis MCP smoke probe</main>" },
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
serverId: "playwright",
|
|
426
|
+
toolName: "browser_snapshot",
|
|
427
|
+
arguments: { depth: 2 },
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
serverId: "time",
|
|
431
|
+
toolName: "get_current_time",
|
|
432
|
+
arguments: { timezone: "UTC" },
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
serverId: "fetch",
|
|
436
|
+
toolName: "fetch",
|
|
437
|
+
arguments: { url: "https://example.com", max_length: 300 },
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
serverId: "git",
|
|
441
|
+
toolName: "git_status",
|
|
442
|
+
arguments: { repo_path: repoRoot },
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
serverId: "sqlite",
|
|
446
|
+
toolName: "read_query",
|
|
447
|
+
arguments: { query: "select 1 as praxis_mcp_smoke" },
|
|
448
|
+
},
|
|
449
|
+
];
|
|
450
|
+
return probes.filter((probe) => servers.get(probe.serverId)?.has(probe.toolName) === true);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async function runLiveCallProbes(discovered: readonly DiscoveredServer[]): Promise<LiveCallProbeResult[]> {
|
|
454
|
+
const adapter = createMcpRuntimeAdapter({ servers: SERVER_PROFILES.map((server) => server.profile) });
|
|
455
|
+
const results: LiveCallProbeResult[] = [];
|
|
456
|
+
try {
|
|
457
|
+
for (const probe of liveCallProbesFor(discovered)) {
|
|
458
|
+
const called = await adapter.callTool?.({
|
|
459
|
+
serverId: probe.serverId,
|
|
460
|
+
toolName: probe.toolName,
|
|
461
|
+
arguments: probe.arguments,
|
|
462
|
+
});
|
|
463
|
+
const result: LiveCallProbeResult = called?.ok === true
|
|
464
|
+
? {
|
|
465
|
+
...probe,
|
|
466
|
+
ok: true,
|
|
467
|
+
outputPreview: outputPreview(called.output),
|
|
468
|
+
}
|
|
469
|
+
: {
|
|
470
|
+
...probe,
|
|
471
|
+
ok: false,
|
|
472
|
+
error: called?.ok === false ? called.error.message : "missing callTool result",
|
|
473
|
+
};
|
|
474
|
+
results.push(result);
|
|
475
|
+
console.log(`[probe] ${probe.serverId}.${probe.toolName}: ${result.ok ? "ok" : `failed: ${result.error}`}`);
|
|
476
|
+
if (!result.ok) throw new Error(`Live MCP call probe failed for ${probe.serverId}.${probe.toolName}: ${result.error}`);
|
|
477
|
+
}
|
|
478
|
+
} finally {
|
|
479
|
+
await adapter.callTool?.({ serverId: "playwright", toolName: "browser_close", arguments: {} });
|
|
480
|
+
await adapter.shutdown?.({});
|
|
481
|
+
}
|
|
482
|
+
return results;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
async function runLivePromptProbes(): Promise<LivePromptProbeResult[]> {
|
|
486
|
+
const adapter = createMcpRuntimeAdapter({ servers: SERVER_PROFILES.map((server) => server.profile) });
|
|
487
|
+
const results: LivePromptProbeResult[] = [];
|
|
488
|
+
try {
|
|
489
|
+
const listed = await adapter.listPrompts?.({ serverId: "everything" });
|
|
490
|
+
const listResult: LivePromptProbeResult = listed?.ok === true
|
|
491
|
+
? {
|
|
492
|
+
serverId: "everything",
|
|
493
|
+
operation: "list",
|
|
494
|
+
ok: true,
|
|
495
|
+
outputPreview: outputPreview(listed.output),
|
|
496
|
+
}
|
|
497
|
+
: {
|
|
498
|
+
serverId: "everything",
|
|
499
|
+
operation: "list",
|
|
500
|
+
ok: false,
|
|
501
|
+
error: listed?.ok === false ? listed.error.message : "missing listPrompts result",
|
|
502
|
+
};
|
|
503
|
+
results.push(listResult);
|
|
504
|
+
console.log(`[prompt-probe] everything.prompts/list: ${listResult.ok ? "ok" : `failed: ${listResult.error}`}`);
|
|
505
|
+
if (!listResult.ok) throw new Error(`Live MCP prompt list probe failed: ${listResult.error}`);
|
|
506
|
+
const promptName = listed.output.prompts.find((prompt: { name?: string }) => prompt.name === "simple-prompt")?.name ?? listed.output.prompts[0]?.name;
|
|
507
|
+
if (promptName === undefined) throw new Error("Live MCP prompt list probe returned no prompt names.");
|
|
508
|
+
|
|
509
|
+
const prompt = await adapter.getPrompt?.({ serverId: "everything", name: promptName, arguments: {} });
|
|
510
|
+
const getResult: LivePromptProbeResult = prompt?.ok === true
|
|
511
|
+
? {
|
|
512
|
+
serverId: "everything",
|
|
513
|
+
operation: "get",
|
|
514
|
+
name: promptName,
|
|
515
|
+
ok: true,
|
|
516
|
+
outputPreview: outputPreview(prompt.output),
|
|
517
|
+
}
|
|
518
|
+
: {
|
|
519
|
+
serverId: "everything",
|
|
520
|
+
operation: "get",
|
|
521
|
+
name: promptName,
|
|
522
|
+
ok: false,
|
|
523
|
+
error: prompt?.ok === false ? prompt.error.message : "missing getPrompt result",
|
|
524
|
+
};
|
|
525
|
+
results.push(getResult);
|
|
526
|
+
console.log(`[prompt-probe] everything.prompts/get:${promptName}: ${getResult.ok ? "ok" : `failed: ${getResult.error}`}`);
|
|
527
|
+
if (!getResult.ok) throw new Error(`Live MCP prompt get probe failed for ${promptName}: ${getResult.error}`);
|
|
528
|
+
} finally {
|
|
529
|
+
await adapter.shutdown?.({});
|
|
530
|
+
}
|
|
531
|
+
return results;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
async function runLiveResourceProbes(): Promise<LiveResourceProbeResult[]> {
|
|
535
|
+
const adapter = createMcpRuntimeAdapter({ servers: SERVER_PROFILES.map((server) => server.profile) });
|
|
536
|
+
const results: LiveResourceProbeResult[] = [];
|
|
537
|
+
try {
|
|
538
|
+
const listed = await adapter.listResources?.({ serverId: "everything" });
|
|
539
|
+
const listResult: LiveResourceProbeResult = listed?.ok === true
|
|
540
|
+
? {
|
|
541
|
+
serverId: "everything",
|
|
542
|
+
operation: "list",
|
|
543
|
+
ok: true,
|
|
544
|
+
outputPreview: outputPreview(listed.output),
|
|
545
|
+
}
|
|
546
|
+
: {
|
|
547
|
+
serverId: "everything",
|
|
548
|
+
operation: "list",
|
|
549
|
+
ok: false,
|
|
550
|
+
error: listed?.ok === false ? listed.error.message : "missing listResources result",
|
|
551
|
+
};
|
|
552
|
+
results.push(listResult);
|
|
553
|
+
console.log(`[resource-probe] everything.resources/list: ${listResult.ok ? "ok" : `failed: ${listResult.error}`}`);
|
|
554
|
+
if (!listResult.ok) throw new Error(`Live MCP resource list probe failed: ${listResult.error}`);
|
|
555
|
+
|
|
556
|
+
const templates = await adapter.listResourceTemplates?.({ serverId: "everything" });
|
|
557
|
+
const templatesResult: LiveResourceProbeResult = templates?.ok === true
|
|
558
|
+
? {
|
|
559
|
+
serverId: "everything",
|
|
560
|
+
operation: "templates",
|
|
561
|
+
ok: true,
|
|
562
|
+
outputPreview: outputPreview(templates.output),
|
|
563
|
+
}
|
|
564
|
+
: {
|
|
565
|
+
serverId: "everything",
|
|
566
|
+
operation: "templates",
|
|
567
|
+
ok: false,
|
|
568
|
+
error: templates?.ok === false ? templates.error.message : "missing listResourceTemplates result",
|
|
569
|
+
};
|
|
570
|
+
results.push(templatesResult);
|
|
571
|
+
console.log(`[resource-probe] everything.resources/templates/list: ${templatesResult.ok ? "ok" : `failed: ${templatesResult.error}`}`);
|
|
572
|
+
if (!templatesResult.ok) throw new Error(`Live MCP resource template list probe failed: ${templatesResult.error}`);
|
|
573
|
+
|
|
574
|
+
const resourceUri = listed.output.resources.find((resource: { uri?: string; name?: string }) =>
|
|
575
|
+
resource.uri === "demo://resource/static/document/architecture.md" || resource.name === "architecture.md"
|
|
576
|
+
)?.uri ?? listed.output.resources[0]?.uri;
|
|
577
|
+
if (typeof resourceUri !== "string" || resourceUri.length === 0) {
|
|
578
|
+
throw new Error("Live MCP resource list probe returned no readable resource URI.");
|
|
579
|
+
}
|
|
580
|
+
const read = await adapter.readResource?.({ serverId: "everything", uri: resourceUri });
|
|
581
|
+
const readResult: LiveResourceProbeResult = read?.ok === true
|
|
582
|
+
? {
|
|
583
|
+
serverId: "everything",
|
|
584
|
+
operation: "read",
|
|
585
|
+
uri: resourceUri,
|
|
586
|
+
ok: true,
|
|
587
|
+
outputPreview: outputPreview(read.output),
|
|
588
|
+
}
|
|
589
|
+
: {
|
|
590
|
+
serverId: "everything",
|
|
591
|
+
operation: "read",
|
|
592
|
+
uri: resourceUri,
|
|
593
|
+
ok: false,
|
|
594
|
+
error: read?.ok === false ? read.error.message : "missing readResource result",
|
|
595
|
+
};
|
|
596
|
+
results.push(readResult);
|
|
597
|
+
console.log(`[resource-probe] everything.resources/read:${resourceUri}: ${readResult.ok ? "ok" : `failed: ${readResult.error}`}`);
|
|
598
|
+
if (!readResult.ok) throw new Error(`Live MCP resource read probe failed for ${resourceUri}: ${readResult.error}`);
|
|
599
|
+
} finally {
|
|
600
|
+
await adapter.shutdown?.({});
|
|
601
|
+
}
|
|
602
|
+
return results;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
async function runLiveCompletionProbes(): Promise<LiveCompletionProbeResult[]> {
|
|
606
|
+
const adapter = createMcpRuntimeAdapter({ servers: SERVER_PROFILES.map((server) => server.profile) });
|
|
607
|
+
const results: LiveCompletionProbeResult[] = [];
|
|
608
|
+
try {
|
|
609
|
+
const completed = await adapter.complete?.({
|
|
610
|
+
serverId: "everything",
|
|
611
|
+
ref: { type: "ref/resource", uri: "demo://resource/dynamic/text/{resourceId}" },
|
|
612
|
+
argument: { name: "resourceId", value: "1" },
|
|
613
|
+
});
|
|
614
|
+
const result: LiveCompletionProbeResult = completed?.ok === true
|
|
615
|
+
? {
|
|
616
|
+
serverId: "everything",
|
|
617
|
+
refType: "ref/resource",
|
|
618
|
+
argumentName: "resourceId",
|
|
619
|
+
ok: completed.output.values.includes("1"),
|
|
620
|
+
values: completed.output.values,
|
|
621
|
+
outputPreview: outputPreview(completed.output),
|
|
622
|
+
...(completed.output.values.includes("1") ? {} : { error: `completion values did not include expected resource id: ${completed.output.values.join(", ")}` }),
|
|
623
|
+
}
|
|
624
|
+
: {
|
|
625
|
+
serverId: "everything",
|
|
626
|
+
refType: "ref/resource",
|
|
627
|
+
argumentName: "resourceId",
|
|
628
|
+
ok: false,
|
|
629
|
+
error: completed?.ok === false ? completed.error.message : "missing completion result",
|
|
630
|
+
};
|
|
631
|
+
results.push(result);
|
|
632
|
+
console.log(`[completion-probe] everything.completion/complete: ${result.ok ? "ok" : `failed: ${result.error}`}`);
|
|
633
|
+
if (!result.ok) throw new Error(`Live MCP completion probe failed: ${result.error}`);
|
|
634
|
+
} finally {
|
|
635
|
+
await adapter.shutdown?.({});
|
|
636
|
+
}
|
|
637
|
+
return results;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
async function runRuntimeToolFlow(mode: "native" | "mcp-plus", discovered: readonly DiscoveredServer[]): Promise<RuntimeToolFlowSummary> {
|
|
641
|
+
const compiled = compileAgent(McpSmokeToolFlowAgent, {
|
|
642
|
+
compiledAt: "2026-06-05T00:00:00.000Z",
|
|
643
|
+
manifestId: `manifest.mcp-smoke.tool-flow.${mode}`,
|
|
644
|
+
});
|
|
645
|
+
if (!compiled.ok) throw new Error(`Failed to compile ${mode} MCP tool-flow smoke agent.`);
|
|
646
|
+
|
|
647
|
+
let calls = 0;
|
|
648
|
+
let providerToolName: string | undefined;
|
|
649
|
+
const result = await createPraxisRuntimeKernel({ runtimeId: `runtime-real-mcp-tool-flow-${mode}` }).runManifest(
|
|
650
|
+
compiled.manifest,
|
|
651
|
+
`Call the everything echo MCP tool through ${mode} runtime tool flow.`,
|
|
652
|
+
{
|
|
653
|
+
sessionId: `session-real-mcp-tool-flow-${mode}`,
|
|
654
|
+
dryRun: false,
|
|
655
|
+
allowProviderCall: true,
|
|
656
|
+
allowToolExecution: true,
|
|
657
|
+
auth: authEnvelope(),
|
|
658
|
+
mcpModule: moduleFor(mode, discovered),
|
|
659
|
+
mcpPlus: { projectId: "project.mcp-smoke" },
|
|
660
|
+
providerCaller: async (envelope) => {
|
|
661
|
+
calls += 1;
|
|
662
|
+
const body = envelope.body as { tools?: readonly { name?: string }[] };
|
|
663
|
+
providerToolName ??= body.tools
|
|
664
|
+
?.map((item) => item.name)
|
|
665
|
+
.find((name): name is string => typeof name === "string" && name.includes("mcp_everything_echo"));
|
|
666
|
+
if (calls === 1) {
|
|
667
|
+
if (providerToolName === undefined) {
|
|
668
|
+
throw new Error(`${mode} tool-flow did not expose everything.echo provider tool.`);
|
|
669
|
+
}
|
|
670
|
+
return {
|
|
671
|
+
output: [{
|
|
672
|
+
type: "function_call",
|
|
673
|
+
name: providerToolName,
|
|
674
|
+
call_id: `${mode}-everything-echo`,
|
|
675
|
+
arguments: JSON.stringify({ message: `praxis ${mode} runtime tool flow` }),
|
|
676
|
+
}],
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
return { output_text: `${mode} runtime MCP tool flow completed` };
|
|
680
|
+
},
|
|
681
|
+
now: () => "2026-06-05T00:00:00.000Z",
|
|
682
|
+
},
|
|
683
|
+
);
|
|
684
|
+
if (!result.ok) throw new Error(`${mode} runtime tool-flow failed: ${JSON.stringify(result.error)}`);
|
|
685
|
+
const toolCall = result.toolCalls[0];
|
|
686
|
+
const summary: RuntimeToolFlowSummary = {
|
|
687
|
+
mode,
|
|
688
|
+
ok: result.ok,
|
|
689
|
+
providerToolName,
|
|
690
|
+
toolId: toolCall?.toolId,
|
|
691
|
+
toolCallCount: result.toolCalls.length,
|
|
692
|
+
toolCallOkCount: result.toolCalls.filter((toolCall) => toolCall.ok).length,
|
|
693
|
+
toolOutputPreview: outputPreview(toolCall?.output),
|
|
694
|
+
outputPreview: outputPreview(result.finalOutput),
|
|
695
|
+
};
|
|
696
|
+
if (summary.toolCallCount !== 1 || summary.toolCallOkCount !== 1) {
|
|
697
|
+
throw new Error(`${mode} runtime tool-flow expected one successful MCP tool call, got ${JSON.stringify(summary)}`);
|
|
698
|
+
}
|
|
699
|
+
if (!summary.toolOutputPreview?.includes(`praxis ${mode} runtime tool flow`)) {
|
|
700
|
+
throw new Error(`${mode} runtime tool-flow did not capture the expected MCP echo output: ${summary.toolOutputPreview ?? "empty"}`);
|
|
701
|
+
}
|
|
702
|
+
console.log(`[tool-flow] ${mode}: ${JSON.stringify(summary)}`);
|
|
703
|
+
return summary;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
async function runRuntimeSkillFlow(discovered: readonly DiscoveredServer[]): Promise<RuntimeSkillFlowSummary> {
|
|
707
|
+
const compiled = compileAgent(McpSmokeToolFlowAgent, {
|
|
708
|
+
compiledAt: "2026-06-05T00:00:00.000Z",
|
|
709
|
+
manifestId: "manifest.mcp-smoke.skill-flow.mcp-plus",
|
|
710
|
+
});
|
|
711
|
+
if (!compiled.ok) throw new Error("Failed to compile MCP+ skill-flow smoke agent.");
|
|
712
|
+
|
|
713
|
+
const skillBodyMarker = "UNIQUE_MCP_PLUS_SKILL_BODY_DYNAMIC_ONLY";
|
|
714
|
+
const skillPitfallMarker = "UNIQUE_MCP_PLUS_SKILL_PITFALL_DYNAMIC_ONLY";
|
|
715
|
+
const skillStore = createInMemoryMcpPlusSkillStore([{
|
|
716
|
+
id: "skill.everything.echo",
|
|
717
|
+
serverId: "everything",
|
|
718
|
+
projectId: "project.mcp-smoke",
|
|
719
|
+
chapter: "everything-debug",
|
|
720
|
+
title: "Echo workflow skill card",
|
|
721
|
+
summary: "Compact card for the everything echo workflow.",
|
|
722
|
+
whenToUse: "When validating MCP+ skill body expansion.",
|
|
723
|
+
do: [skillBodyMarker],
|
|
724
|
+
pitfalls: [skillPitfallMarker],
|
|
725
|
+
createdAt: "2026-06-05T00:00:00.000Z",
|
|
726
|
+
updatedAt: "2026-06-05T00:00:00.000Z",
|
|
727
|
+
}]);
|
|
728
|
+
|
|
729
|
+
let calls = 0;
|
|
730
|
+
let providerToolName: string | undefined;
|
|
731
|
+
let firstProviderBodyText = "";
|
|
732
|
+
let firstStableInstructionsText = "";
|
|
733
|
+
let secondStableInstructionsText = "";
|
|
734
|
+
let secondDynamicInputText = "";
|
|
735
|
+
const cacheDebugs: AgentModelCacheDebugRecord[] = [];
|
|
736
|
+
const result = await createPraxisRuntimeKernel({ runtimeId: "runtime-real-mcp-skill-flow-mcp-plus" }).runManifest(
|
|
737
|
+
compiled.manifest,
|
|
738
|
+
"Read the full everything MCP+ skill body and keep it out of the stable prefix.",
|
|
739
|
+
{
|
|
740
|
+
sessionId: "session-real-mcp-skill-flow-mcp-plus",
|
|
741
|
+
dryRun: false,
|
|
742
|
+
allowProviderCall: true,
|
|
743
|
+
allowToolExecution: true,
|
|
744
|
+
auth: authEnvelope(),
|
|
745
|
+
mcpModule: moduleFor("mcp-plus", discovered),
|
|
746
|
+
mcpPlus: {
|
|
747
|
+
projectId: "project.mcp-smoke",
|
|
748
|
+
skillStore,
|
|
749
|
+
},
|
|
750
|
+
providerCaller: async (envelope) => {
|
|
751
|
+
calls += 1;
|
|
752
|
+
const body = envelope.body as { instructions?: unknown; input?: unknown; tools?: readonly { name?: string }[] };
|
|
753
|
+
if (calls === 1) {
|
|
754
|
+
firstProviderBodyText = JSON.stringify(envelope.body);
|
|
755
|
+
firstStableInstructionsText = JSON.stringify(body.instructions);
|
|
756
|
+
providerToolName = body.tools
|
|
757
|
+
?.map((item) => item.name)
|
|
758
|
+
.find((name): name is string => typeof name === "string" && name.includes("mcp_plus_skill_read"));
|
|
759
|
+
if (providerToolName === undefined) {
|
|
760
|
+
throw new Error("MCP+ skill-flow did not expose mcp_plus.skill_read provider tool.");
|
|
761
|
+
}
|
|
762
|
+
return {
|
|
763
|
+
output: [{
|
|
764
|
+
type: "function_call",
|
|
765
|
+
name: providerToolName,
|
|
766
|
+
call_id: "mcp-plus-skill-read",
|
|
767
|
+
arguments: JSON.stringify({ serverId: "everything", id: "skill.everything.echo" }),
|
|
768
|
+
}],
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
secondStableInstructionsText = JSON.stringify(body.instructions);
|
|
772
|
+
secondDynamicInputText = JSON.stringify(body.input);
|
|
773
|
+
return { output_text: "mcp-plus skill body expansion stayed dynamic" };
|
|
774
|
+
},
|
|
775
|
+
onModelCallProgress: async (progress) => {
|
|
776
|
+
if (progress.phase === "completed" && progress.cacheDebug !== undefined) {
|
|
777
|
+
cacheDebugs.push(progress.cacheDebug);
|
|
778
|
+
}
|
|
779
|
+
},
|
|
780
|
+
now: () => "2026-06-05T00:00:00.000Z",
|
|
781
|
+
},
|
|
782
|
+
);
|
|
783
|
+
if (!result.ok) throw new Error(`mcp-plus runtime skill-flow failed: ${JSON.stringify(result.error)}`);
|
|
784
|
+
const secondCache = cacheDebugs[1];
|
|
785
|
+
const observationSegment = secondCache?.promptPack.segments.find((segment) => segment.segmentKind === "observations");
|
|
786
|
+
const summary: RuntimeSkillFlowSummary = {
|
|
787
|
+
ok: result.ok,
|
|
788
|
+
providerToolName,
|
|
789
|
+
toolCallCount: result.toolCalls.length,
|
|
790
|
+
toolCallOkCount: result.toolCalls.filter((toolCall) => toolCall.ok).length,
|
|
791
|
+
skillBodyMarker,
|
|
792
|
+
skillPitfallMarker,
|
|
793
|
+
firstStablePrefixContainsSkillBody: firstStableInstructionsText.includes(skillBodyMarker) || firstStableInstructionsText.includes(skillPitfallMarker),
|
|
794
|
+
firstProviderBodyContainsSkillBody: firstProviderBodyText.includes(skillBodyMarker) || firstProviderBodyText.includes(skillPitfallMarker),
|
|
795
|
+
secondStablePrefixContainsSkillBody: secondStableInstructionsText.includes(skillBodyMarker) || secondStableInstructionsText.includes(skillPitfallMarker),
|
|
796
|
+
secondDynamicInputContainsSkillBody: secondDynamicInputText.includes(skillBodyMarker),
|
|
797
|
+
secondDynamicInputContainsSkillPitfall: secondDynamicInputText.includes(skillPitfallMarker),
|
|
798
|
+
secondObservationSegmentCachePolicy: observationSegment?.cachePolicy,
|
|
799
|
+
secondProviderInstructionSegmentKinds: secondCache?.promptPack.providerLowering?.instructionSegmentKinds ?? [],
|
|
800
|
+
secondProviderDynamicInputSegmentKinds: secondCache?.promptPack.providerLowering?.dynamicInputSegmentKinds ?? [],
|
|
801
|
+
outputPreview: outputPreview(result.finalOutput),
|
|
802
|
+
};
|
|
803
|
+
if (summary.toolCallCount !== 1 || summary.toolCallOkCount !== 1) {
|
|
804
|
+
throw new Error(`mcp-plus skill-flow expected one successful skill_read call, got ${JSON.stringify(summary)}`);
|
|
805
|
+
}
|
|
806
|
+
if (summary.firstProviderBodyContainsSkillBody || summary.secondStablePrefixContainsSkillBody) {
|
|
807
|
+
throw new Error(`MCP+ full skill body leaked into provider stable/prefix text: ${JSON.stringify(summary)}`);
|
|
808
|
+
}
|
|
809
|
+
if (!summary.secondDynamicInputContainsSkillBody || !summary.secondDynamicInputContainsSkillPitfall) {
|
|
810
|
+
throw new Error(`MCP+ full skill body was not delivered through dynamic tool-result input: ${JSON.stringify(summary)}`);
|
|
811
|
+
}
|
|
812
|
+
if (summary.secondObservationSegmentCachePolicy !== "dynamic-no-cache") {
|
|
813
|
+
throw new Error(`MCP+ skill_read observation is not dynamic-no-cache: ${JSON.stringify(summary)}`);
|
|
814
|
+
}
|
|
815
|
+
console.log(`[skill-flow] mcp-plus: ${JSON.stringify(summary)}`);
|
|
816
|
+
return summary;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
async function discover(): Promise<DiscoveredServer[]> {
|
|
820
|
+
const adapter = createMcpRuntimeAdapter({ servers: SERVER_PROFILES.map((server) => server.profile) });
|
|
821
|
+
const discovered: DiscoveredServer[] = [];
|
|
822
|
+
try {
|
|
823
|
+
for (const server of SERVER_PROFILES) {
|
|
824
|
+
const listed = await adapter.listTools?.({ serverId: server.serverId });
|
|
825
|
+
if (listed?.ok !== true) {
|
|
826
|
+
throw new Error(`Failed to list ${server.serverId}: ${listed?.ok === false ? listed.error.message : "missing listTools result"}`);
|
|
827
|
+
}
|
|
828
|
+
discovered.push({
|
|
829
|
+
...server,
|
|
830
|
+
tools: listed.output.tools.map((tool: { name: string; description?: string; inputSchema?: unknown }) => ({
|
|
831
|
+
name: tool.name,
|
|
832
|
+
description: tool.description,
|
|
833
|
+
inputSchema: tool.inputSchema,
|
|
834
|
+
})),
|
|
835
|
+
});
|
|
836
|
+
console.log(`[discover] ${server.serverId}: ${listed.output.tools.length} tools`);
|
|
837
|
+
}
|
|
838
|
+
return discovered;
|
|
839
|
+
} finally {
|
|
840
|
+
await adapter.shutdown?.({});
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
async function runLiveTransportProbes(): Promise<LiveTransportProbeResult[]> {
|
|
845
|
+
const sessionId = "smoke-http-session";
|
|
846
|
+
let sessionIdObserved = false;
|
|
847
|
+
let protocolVersionObserved = false;
|
|
848
|
+
let eventStreamObserved = false;
|
|
849
|
+
const sse = (message: unknown): string => `event: message\ndata: ${JSON.stringify(message)}\n\n`;
|
|
850
|
+
const server = createServer((request, response) => {
|
|
851
|
+
if (request.method !== "POST" || request.url !== "/rpc") {
|
|
852
|
+
response.writeHead(404).end();
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
let body = "";
|
|
856
|
+
request.setEncoding("utf8");
|
|
857
|
+
request.on("data", (chunk) => {
|
|
858
|
+
body += chunk;
|
|
859
|
+
});
|
|
860
|
+
request.on("end", () => {
|
|
861
|
+
const payload = JSON.parse(body) as { id?: string | number; method?: string };
|
|
862
|
+
const protocolVersion = request.headers["mcp-protocol-version"];
|
|
863
|
+
protocolVersionObserved ||= protocolVersion === "2025-06-18";
|
|
864
|
+
const currentSessionId = request.headers["mcp-session-id"];
|
|
865
|
+
if (payload.method !== "initialize") {
|
|
866
|
+
sessionIdObserved ||= currentSessionId === sessionId;
|
|
867
|
+
}
|
|
868
|
+
if (payload.method !== "initialize" && currentSessionId !== sessionId) {
|
|
869
|
+
response.writeHead(400, { "content-type": "application/json" });
|
|
870
|
+
response.end(JSON.stringify({ jsonrpc: "2.0", id: payload.id, error: { code: -32000, message: "Mcp-Session-Id header is required" } }));
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
if (payload.method === "tools/list") {
|
|
874
|
+
eventStreamObserved = true;
|
|
875
|
+
response.writeHead(200, { "content-type": "text/event-stream" });
|
|
876
|
+
response.end(sse({
|
|
877
|
+
jsonrpc: "2.0",
|
|
878
|
+
id: payload.id,
|
|
879
|
+
result: { tools: [{ name: "http_session_echo", description: "HTTP session echo", inputSchema: { type: "object" } }] },
|
|
880
|
+
}));
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
response.writeHead(200, {
|
|
884
|
+
"content-type": "application/json",
|
|
885
|
+
...(payload.method === "initialize" ? { "Mcp-Session-Id": sessionId } : {}),
|
|
886
|
+
});
|
|
887
|
+
response.end(JSON.stringify({
|
|
888
|
+
jsonrpc: "2.0",
|
|
889
|
+
id: payload.id,
|
|
890
|
+
result: { ok: true },
|
|
891
|
+
}));
|
|
892
|
+
});
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
896
|
+
try {
|
|
897
|
+
const address = server.address() as AddressInfo;
|
|
898
|
+
const adapter = createMcpRuntimeAdapter({
|
|
899
|
+
servers: [{
|
|
900
|
+
serverId: "strict-http-session",
|
|
901
|
+
transport: "http",
|
|
902
|
+
url: `http://127.0.0.1:${address.port}/rpc`,
|
|
903
|
+
timeoutMs: 3_000,
|
|
904
|
+
}],
|
|
905
|
+
});
|
|
906
|
+
try {
|
|
907
|
+
const listed = await adapter.listTools?.({ serverId: "strict-http-session" });
|
|
908
|
+
const ok = listed?.ok === true
|
|
909
|
+
&& listed.output.tools[0]?.name === "http_session_echo"
|
|
910
|
+
&& sessionIdObserved
|
|
911
|
+
&& protocolVersionObserved
|
|
912
|
+
&& eventStreamObserved;
|
|
913
|
+
const result: LiveTransportProbeResult = {
|
|
914
|
+
transport: "http",
|
|
915
|
+
serverId: "strict-http-session",
|
|
916
|
+
ok,
|
|
917
|
+
sessionIdObserved,
|
|
918
|
+
protocolVersionObserved,
|
|
919
|
+
eventStreamObserved,
|
|
920
|
+
outputPreview: listed?.ok === true ? JSON.stringify(listed.output.tools[0]) : undefined,
|
|
921
|
+
error: listed?.ok === false ? listed.error.message : ok ? undefined : "missing HTTP MCP session/protocol evidence",
|
|
922
|
+
};
|
|
923
|
+
console.log(`[transport-probe] ${result.serverId}.streamable-http-session: ${result.ok ? "ok" : "failed"}`);
|
|
924
|
+
return [result];
|
|
925
|
+
} finally {
|
|
926
|
+
await adapter.shutdown?.({});
|
|
927
|
+
}
|
|
928
|
+
} finally {
|
|
929
|
+
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
async function runHostSemanticsProbe(): Promise<HostSemanticsProbeResult> {
|
|
934
|
+
const serverId = "host-semantics";
|
|
935
|
+
const serverPath = path.join(runRoot, "host-semantics-mcp-server.mjs");
|
|
936
|
+
await writeFile(serverPath, `
|
|
937
|
+
let pendingToolsListId;
|
|
938
|
+
let rootsDone = false;
|
|
939
|
+
let samplingDone = false;
|
|
940
|
+
let elicitationDone = false;
|
|
941
|
+
let progressSent = false;
|
|
942
|
+
let hooksDeclared = false;
|
|
943
|
+
let rootName = "missing-root";
|
|
944
|
+
function send(payload) {
|
|
945
|
+
process.stdout.write(JSON.stringify(payload) + "\\n");
|
|
946
|
+
}
|
|
947
|
+
function maybeFinish() {
|
|
948
|
+
if (pendingToolsListId !== undefined && rootsDone && samplingDone && elicitationDone && progressSent) {
|
|
949
|
+
send({ jsonrpc: "2.0", id: pendingToolsListId, result: { tools: [{ name: "host_semantics_ok", description: rootName, inputSchema: { type: "object" } }] } });
|
|
950
|
+
pendingToolsListId = undefined;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
function handle(payload) {
|
|
954
|
+
if (payload.method === "initialize") {
|
|
955
|
+
hooksDeclared = payload.params?.capabilities?.roots?.listChanged === true &&
|
|
956
|
+
payload.params?.capabilities?.sampling !== undefined &&
|
|
957
|
+
payload.params?.capabilities?.elicitation?.form !== undefined &&
|
|
958
|
+
payload.params?.capabilities?.elicitation?.url !== undefined;
|
|
959
|
+
send({ jsonrpc: "2.0", id: payload.id, result: { protocolVersion: "2025-06-18", capabilities: { tools: {} }, serverInfo: { name: "host-semantics", version: "1" } } });
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
if (payload.method === "tools/list") {
|
|
963
|
+
if (!hooksDeclared) {
|
|
964
|
+
send({ jsonrpc: "2.0", id: payload.id, error: { code: -32002, message: "host capabilities were not declared" } });
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
pendingToolsListId = payload.id;
|
|
968
|
+
send({ jsonrpc: "2.0", id: "roots-1", method: "roots/list", params: {} });
|
|
969
|
+
send({ jsonrpc: "2.0", method: "notifications/progress", params: { progressToken: "host-semantics", progress: 1, total: 1, message: "checking host semantics" } });
|
|
970
|
+
progressSent = true;
|
|
971
|
+
send({ jsonrpc: "2.0", id: "sampling-1", method: "sampling/createMessage", params: { messages: [{ role: "user", content: { type: "text", text: "summarize host semantics" } }], maxTokens: 16 } });
|
|
972
|
+
send({ jsonrpc: "2.0", id: "elicit-1", method: "elicitation/create", params: { mode: "form", message: "Confirm host semantics", requestedSchema: { type: "object", properties: { ok: { type: "boolean" } } } } });
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
if (payload.id === "roots-1") {
|
|
976
|
+
const roots = payload.result?.roots ?? [];
|
|
977
|
+
rootName = roots[0]?.name ?? "missing-root";
|
|
978
|
+
rootsDone = rootName === "mcp-smoke-root";
|
|
979
|
+
maybeFinish();
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
if (payload.id === "sampling-1") {
|
|
983
|
+
samplingDone = payload.result?.model === "praxis-smoke-model";
|
|
984
|
+
maybeFinish();
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
if (payload.id === "elicit-1") {
|
|
988
|
+
elicitationDone = payload.result?.action === "decline";
|
|
989
|
+
maybeFinish();
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
process.stdin.setEncoding("utf8");
|
|
993
|
+
let buffer = "";
|
|
994
|
+
process.stdin.on("data", (chunk) => {
|
|
995
|
+
buffer += chunk;
|
|
996
|
+
const lines = buffer.split("\\n");
|
|
997
|
+
buffer = lines.pop() ?? "";
|
|
998
|
+
for (const line of lines) {
|
|
999
|
+
if (line.trim().length > 0) handle(JSON.parse(line));
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
`, "utf8");
|
|
1003
|
+
|
|
1004
|
+
const requestedMethods: string[] = [];
|
|
1005
|
+
const notificationMethods: string[] = [];
|
|
1006
|
+
const adapter = createMcpRuntimeAdapter({
|
|
1007
|
+
servers: [{
|
|
1008
|
+
serverId,
|
|
1009
|
+
transport: "stdio",
|
|
1010
|
+
command: process.execPath,
|
|
1011
|
+
args: [serverPath],
|
|
1012
|
+
timeoutMs: 3_000,
|
|
1013
|
+
}],
|
|
1014
|
+
host: {
|
|
1015
|
+
createSamplingMessage(request) {
|
|
1016
|
+
requestedMethods.push(request.method);
|
|
1017
|
+
return {
|
|
1018
|
+
role: "assistant",
|
|
1019
|
+
content: { type: "text", text: "sampled host semantics" },
|
|
1020
|
+
model: "praxis-smoke-model",
|
|
1021
|
+
stopReason: "endTurn",
|
|
1022
|
+
};
|
|
1023
|
+
},
|
|
1024
|
+
elicit(request) {
|
|
1025
|
+
requestedMethods.push(request.method);
|
|
1026
|
+
return { action: "decline" };
|
|
1027
|
+
},
|
|
1028
|
+
onNotification(notification) {
|
|
1029
|
+
notificationMethods.push(notification.method);
|
|
1030
|
+
},
|
|
1031
|
+
},
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
try {
|
|
1035
|
+
const registered = await adapter.setRoots?.({
|
|
1036
|
+
serverId,
|
|
1037
|
+
roots: [{ uri: `file://${repoRoot}`, name: "mcp-smoke-root" }],
|
|
1038
|
+
});
|
|
1039
|
+
if (registered?.ok !== true) {
|
|
1040
|
+
return {
|
|
1041
|
+
serverId,
|
|
1042
|
+
ok: false,
|
|
1043
|
+
requestedMethods,
|
|
1044
|
+
notificationMethods,
|
|
1045
|
+
error: registered?.ok === false ? registered.error.message : "missing setRoots result",
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
const listed = await adapter.listTools?.({ serverId });
|
|
1049
|
+
const tool = listed?.ok === true
|
|
1050
|
+
? listed.output.tools.find((item: { name?: string }) => item.name === "host_semantics_ok")
|
|
1051
|
+
: undefined;
|
|
1052
|
+
const result: HostSemanticsProbeResult = listed?.ok === true && tool !== undefined
|
|
1053
|
+
? {
|
|
1054
|
+
serverId,
|
|
1055
|
+
ok: requestedMethods.includes("sampling/createMessage") &&
|
|
1056
|
+
requestedMethods.includes("elicitation/create") &&
|
|
1057
|
+
notificationMethods.includes("notifications/progress") &&
|
|
1058
|
+
tool.description === "mcp-smoke-root",
|
|
1059
|
+
rootName: tool.description,
|
|
1060
|
+
requestedMethods,
|
|
1061
|
+
notificationMethods,
|
|
1062
|
+
outputPreview: outputPreview(listed.output),
|
|
1063
|
+
}
|
|
1064
|
+
: {
|
|
1065
|
+
serverId,
|
|
1066
|
+
ok: false,
|
|
1067
|
+
requestedMethods,
|
|
1068
|
+
notificationMethods,
|
|
1069
|
+
error: listed?.ok === false ? listed.error.message : "missing host semantics listTools result",
|
|
1070
|
+
};
|
|
1071
|
+
console.log(`[host-semantics-probe] ${serverId}: ${result.ok ? "ok" : `failed: ${result.error ?? "semantic assertion failed"}`}`);
|
|
1072
|
+
if (!result.ok) throw new Error(`Host semantics MCP probe failed: ${JSON.stringify(result)}`);
|
|
1073
|
+
return result;
|
|
1074
|
+
} finally {
|
|
1075
|
+
await adapter.disconnect?.({ serverId });
|
|
1076
|
+
await adapter.shutdown?.({});
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function eventFromProgress(mode: string, progress: AgentModelCallProgressEvent & { cacheDebug?: AgentModelCacheDebugRecord }) {
|
|
1081
|
+
const cacheDebug = progress.cacheDebug;
|
|
1082
|
+
const estimatedInput = cacheDebug?.providerBody.cacheShape.providerStablePrefixEstimatedTokens ?? 0;
|
|
1083
|
+
const estimatedDynamic = cacheDebug?.providerBody.cacheShape.providerDynamicInputEstimatedTokens ?? 0;
|
|
1084
|
+
return {
|
|
1085
|
+
eventId: `event.${mode}.model.${progress.phase}`,
|
|
1086
|
+
kind: "model",
|
|
1087
|
+
status: progress.phase === "completed" ? "completed" : progress.phase,
|
|
1088
|
+
message: `${mode} MCP smoke model call ${progress.phase}`,
|
|
1089
|
+
createdAt: "2026-06-05T00:00:00.000Z",
|
|
1090
|
+
sessionId: `session-real-mcp-${mode}`,
|
|
1091
|
+
runtimeId: `runtime-real-mcp-${mode}`,
|
|
1092
|
+
turnId: `turn-real-mcp-${mode}-1`,
|
|
1093
|
+
publicSafe: true,
|
|
1094
|
+
metadata: {
|
|
1095
|
+
modelPhase: progress.phase,
|
|
1096
|
+
invocationId: progress.invocationId,
|
|
1097
|
+
turnIndex: progress.turnIndex,
|
|
1098
|
+
provider: progress.provider,
|
|
1099
|
+
carrierId: progress.carrierId,
|
|
1100
|
+
model: progress.model,
|
|
1101
|
+
usage: {
|
|
1102
|
+
inputTokens: estimatedInput,
|
|
1103
|
+
cachedInputTokens: 0,
|
|
1104
|
+
outputTokens: 1,
|
|
1105
|
+
totalTokens: estimatedInput + estimatedDynamic + 1,
|
|
1106
|
+
estimated: true,
|
|
1107
|
+
},
|
|
1108
|
+
cacheDebug,
|
|
1109
|
+
},
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
async function runDevdoctorCacheDiagnostics(runDir: string): Promise<{
|
|
1114
|
+
report: ExecutionMonitorReport;
|
|
1115
|
+
cacheXray: DevdoctorCacheXraySummary;
|
|
1116
|
+
}> {
|
|
1117
|
+
const monitor = await runDevDoctor(["monitor", "--run", runDir, "--project", repoRoot, "--json"]);
|
|
1118
|
+
if (monitor.exitCode !== 0) {
|
|
1119
|
+
throw new Error(`devdoctor monitor failed for ${runDir}: ${monitor.output}`);
|
|
1120
|
+
}
|
|
1121
|
+
const report = JSON.parse(monitor.output) as ExecutionMonitorReport;
|
|
1122
|
+
const cacheXray = await runDevDoctor(["cache-xray", "--run", runDir, "--project", repoRoot, "--json"]);
|
|
1123
|
+
if (cacheXray.exitCode !== 0) {
|
|
1124
|
+
throw new Error(`devdoctor cache-xray failed for ${runDir}: ${cacheXray.output}`);
|
|
1125
|
+
}
|
|
1126
|
+
return { report, cacheXray: JSON.parse(cacheXray.output) as DevdoctorCacheXraySummary };
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
function summarizePromptPackFlow(cache: AgentModelCacheDebugRecord): PromptPackFlowSummary {
|
|
1130
|
+
const toolSegment = cache.promptPack.segments.find((segment) => segment.segmentKind === "toolDeclarations");
|
|
1131
|
+
const toolRefs = toolSegment?.materialRefs ?? [];
|
|
1132
|
+
const builtInToolDeclarationsMaterialIndex = toolRefs.indexOf("runtime:tool-declarations");
|
|
1133
|
+
const mcpPlusPreludeMaterialIndex = toolRefs.indexOf("runtime:mcp-plus-native-exposure");
|
|
1134
|
+
const providerLowering = cache.promptPack.providerLowering ?? {
|
|
1135
|
+
instructionSegmentKinds: [],
|
|
1136
|
+
dynamicInputSegmentKinds: [],
|
|
1137
|
+
};
|
|
1138
|
+
return {
|
|
1139
|
+
mcpPlusPreludePresent: mcpPlusPreludeMaterialIndex >= 0,
|
|
1140
|
+
mcpPlusPreludeSegmentKind: mcpPlusPreludeMaterialIndex >= 0 ? toolSegment?.segmentKind : undefined,
|
|
1141
|
+
mcpPlusPreludeCachePolicy: mcpPlusPreludeMaterialIndex >= 0 ? toolSegment?.cachePolicy : undefined,
|
|
1142
|
+
mcpPlusPreludeInCacheablePrefix: toolSegment?.cachePolicy === "cacheable-prefix" && mcpPlusPreludeMaterialIndex >= 0,
|
|
1143
|
+
mcpPlusPreludeMaterialIndex,
|
|
1144
|
+
builtInToolDeclarationsMaterialIndex,
|
|
1145
|
+
mcpPlusPreludeAfterBuiltInToolDeclarations:
|
|
1146
|
+
builtInToolDeclarationsMaterialIndex >= 0 &&
|
|
1147
|
+
mcpPlusPreludeMaterialIndex > builtInToolDeclarationsMaterialIndex,
|
|
1148
|
+
refsAroundMcpPlusPrelude: mcpPlusPreludeMaterialIndex < 0
|
|
1149
|
+
? []
|
|
1150
|
+
: toolRefs.slice(Math.max(0, mcpPlusPreludeMaterialIndex - 3), mcpPlusPreludeMaterialIndex + 4),
|
|
1151
|
+
providerInstructionSegmentKinds: providerLowering.instructionSegmentKinds,
|
|
1152
|
+
providerDynamicInputSegmentKinds: providerLowering.dynamicInputSegmentKinds,
|
|
1153
|
+
cacheRiskWarnings: cache.promptPack.cacheRiskWarnings,
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
async function runMode(mode: "native" | "mcp-plus", discovered: readonly DiscoveredServer[]) {
|
|
1158
|
+
const runDir = path.join(runRoot, mode);
|
|
1159
|
+
await mkdir(runDir, { recursive: true });
|
|
1160
|
+
const events: unknown[] = [];
|
|
1161
|
+
const cacheDebugs: AgentModelCacheDebugRecord[] = [];
|
|
1162
|
+
const compiled = compileAgent(McpSmokeAgent, {
|
|
1163
|
+
compiledAt: "2026-06-05T00:00:00.000Z",
|
|
1164
|
+
manifestId: `manifest.mcp-smoke.${mode}`,
|
|
1165
|
+
});
|
|
1166
|
+
if (!compiled.ok) throw new Error(`Failed to compile ${mode} MCP smoke agent.`);
|
|
1167
|
+
|
|
1168
|
+
const result = await createPraxisRuntimeKernel({ runtimeId: `runtime-real-mcp-${mode}` }).runManifest(
|
|
1169
|
+
compiled.manifest,
|
|
1170
|
+
`Run ${mode} MCP smoke without calling tools.`,
|
|
1171
|
+
{
|
|
1172
|
+
sessionId: `session-real-mcp-${mode}`,
|
|
1173
|
+
dryRun: false,
|
|
1174
|
+
allowProviderCall: true,
|
|
1175
|
+
allowToolExecution: false,
|
|
1176
|
+
auth: authEnvelope(),
|
|
1177
|
+
mcpModule: moduleFor(mode, discovered),
|
|
1178
|
+
mcpPlus: { projectId: "project.mcp-smoke" },
|
|
1179
|
+
providerCaller: async () => ({
|
|
1180
|
+
output_text: `${mode} smoke ok`,
|
|
1181
|
+
usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
|
|
1182
|
+
}),
|
|
1183
|
+
onModelCallProgress: async (progress) => {
|
|
1184
|
+
if (progress.phase === "completed" && progress.cacheDebug !== undefined) {
|
|
1185
|
+
cacheDebugs.push(progress.cacheDebug);
|
|
1186
|
+
events.push(eventFromProgress(mode, progress));
|
|
1187
|
+
}
|
|
1188
|
+
},
|
|
1189
|
+
now: () => "2026-06-05T00:00:00.000Z",
|
|
1190
|
+
},
|
|
1191
|
+
);
|
|
1192
|
+
if (!result.ok) throw new Error(`${mode} runManifest failed.`);
|
|
1193
|
+
if (cacheDebugs[0] === undefined) throw new Error(`${mode} did not emit cacheDebug`);
|
|
1194
|
+
|
|
1195
|
+
await writeFile(path.join(runDir, "events.jsonl"), events.map(jsonl).join(""), "utf8");
|
|
1196
|
+
await writeFile(path.join(runDir, "views.jsonl"), "", "utf8");
|
|
1197
|
+
await writeFile(path.join(runDir, "config.json"), `${JSON.stringify({ mode, serverIds: discovered.map((server) => server.serverId) }, null, 2)}\n`, "utf8");
|
|
1198
|
+
|
|
1199
|
+
const { report, cacheXray } = await runDevdoctorCacheDiagnostics(runDir);
|
|
1200
|
+
|
|
1201
|
+
const cache = cacheDebugs[0];
|
|
1202
|
+
const promptPackFlow = summarizePromptPackFlow(cache);
|
|
1203
|
+
if (mode === "native" && promptPackFlow.mcpPlusPreludePresent) {
|
|
1204
|
+
throw new Error("native MCP smoke unexpectedly included MCP+ native exposure prelude.");
|
|
1205
|
+
}
|
|
1206
|
+
if (mode === "mcp-plus" && !promptPackFlow.mcpPlusPreludeAfterBuiltInToolDeclarations) {
|
|
1207
|
+
throw new Error(`MCP+ native exposure prelude is not placed after built-in tool declarations: ${JSON.stringify(promptPackFlow)}`);
|
|
1208
|
+
}
|
|
1209
|
+
if (mode === "mcp-plus" && !promptPackFlow.mcpPlusPreludeInCacheablePrefix) {
|
|
1210
|
+
throw new Error(`MCP+ native exposure prelude is not in the cacheable tool declaration prefix: ${JSON.stringify(promptPackFlow)}`);
|
|
1211
|
+
}
|
|
1212
|
+
const toolRefs = cache.promptPack.segments.find((segment) => segment.segmentKind === "toolDeclarations")?.materialRefs ?? [];
|
|
1213
|
+
const summary = {
|
|
1214
|
+
mode,
|
|
1215
|
+
runDir,
|
|
1216
|
+
providerToolCount: cache.providerBody.toolCount,
|
|
1217
|
+
toolsEstimatedTokens: cache.providerBody.toolsEstimatedTokens,
|
|
1218
|
+
providerStablePrefixEstimatedTokens: cache.providerBody.cacheShape.providerStablePrefixEstimatedTokens,
|
|
1219
|
+
providerDynamicInputEstimatedTokens: cache.providerBody.cacheShape.providerDynamicInputEstimatedTokens,
|
|
1220
|
+
promptPackTotalEstimatedTokens: cache.promptPack.totalEstimatedTokens,
|
|
1221
|
+
cacheablePrefixEstimatedTokens: cache.promptPack.cacheablePrefixEstimatedTokens,
|
|
1222
|
+
sidecarPresent: toolRefs.includes("runtime:mcp-plus-native-exposure"),
|
|
1223
|
+
promptPackFlow,
|
|
1224
|
+
devdoctorCacheStatus: cacheXray.status,
|
|
1225
|
+
devdoctorWeightedCacheHitRate: cacheXray.cache?.weightedCacheHitRate,
|
|
1226
|
+
devdoctorCacheTelemetryCoverage: cacheXray.cache?.cacheTelemetryCoverage,
|
|
1227
|
+
devdoctorProviderCacheMissCalls: cacheXray.cache?.providerCacheMissCalls,
|
|
1228
|
+
mcpGroups: toolRefs
|
|
1229
|
+
.filter((ref) => ref.startsWith("baseTool:context:group:mcp:"))
|
|
1230
|
+
.map((ref) => ref.slice("baseTool:context:group:mcp:".length)),
|
|
1231
|
+
monitorFindingIds: report.findings.map((finding) => finding.id),
|
|
1232
|
+
monitorHealthGrade: report.project.health.grade,
|
|
1233
|
+
};
|
|
1234
|
+
await writeFile(path.join(runDir, "summary.json"), `${JSON.stringify(summary, null, 2)}\n`, "utf8");
|
|
1235
|
+
console.log(`[${mode}] ${JSON.stringify(summary)}`);
|
|
1236
|
+
return summary;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
await mkdir(runRoot, { recursive: true });
|
|
1240
|
+
const discovered = await discover();
|
|
1241
|
+
const liveCallProbes = await runLiveCallProbes(discovered);
|
|
1242
|
+
const livePromptProbes = await runLivePromptProbes();
|
|
1243
|
+
const liveResourceProbes = await runLiveResourceProbes();
|
|
1244
|
+
const liveCompletionProbes = await runLiveCompletionProbes();
|
|
1245
|
+
const liveTransportProbes = await runLiveTransportProbes();
|
|
1246
|
+
const hostSemanticsProbe = await runHostSemanticsProbe();
|
|
1247
|
+
const nativeToolFlow = await runRuntimeToolFlow("native", discovered);
|
|
1248
|
+
const mcpPlusToolFlow = await runRuntimeToolFlow("mcp-plus", discovered);
|
|
1249
|
+
const mcpPlusSkillFlow = await runRuntimeSkillFlow(discovered);
|
|
1250
|
+
await writeFile(path.join(runRoot, "discovery.json"), `${JSON.stringify(discovered.map((server) => ({
|
|
1251
|
+
serverId: server.serverId,
|
|
1252
|
+
toolCount: server.tools.length,
|
|
1253
|
+
toolNames: server.tools.map((tool) => tool.name),
|
|
1254
|
+
})), null, 2)}\n`, "utf8");
|
|
1255
|
+
await writeFile(path.join(runRoot, "live-call-probes.json"), `${JSON.stringify(liveCallProbes, null, 2)}\n`, "utf8");
|
|
1256
|
+
await writeFile(path.join(runRoot, "live-prompt-probes.json"), `${JSON.stringify(livePromptProbes, null, 2)}\n`, "utf8");
|
|
1257
|
+
await writeFile(path.join(runRoot, "live-resource-probes.json"), `${JSON.stringify(liveResourceProbes, null, 2)}\n`, "utf8");
|
|
1258
|
+
await writeFile(path.join(runRoot, "live-completion-probes.json"), `${JSON.stringify(liveCompletionProbes, null, 2)}\n`, "utf8");
|
|
1259
|
+
await writeFile(path.join(runRoot, "live-transport-probes.json"), `${JSON.stringify(liveTransportProbes, null, 2)}\n`, "utf8");
|
|
1260
|
+
await writeFile(path.join(runRoot, "host-semantics-probe.json"), `${JSON.stringify(hostSemanticsProbe, null, 2)}\n`, "utf8");
|
|
1261
|
+
await writeFile(path.join(runRoot, "runtime-tool-flow.json"), `${JSON.stringify([nativeToolFlow, mcpPlusToolFlow], null, 2)}\n`, "utf8");
|
|
1262
|
+
await writeFile(path.join(runRoot, "runtime-skill-flow.json"), `${JSON.stringify(mcpPlusSkillFlow, null, 2)}\n`, "utf8");
|
|
1263
|
+
const native = await runMode("native", discovered);
|
|
1264
|
+
const mcpPlus = await runMode("mcp-plus", discovered);
|
|
1265
|
+
const comparison = {
|
|
1266
|
+
runRoot,
|
|
1267
|
+
serverCount: discovered.length,
|
|
1268
|
+
totalNativeToolsDiscovered: discovered.reduce((sum, server) => sum + server.tools.length, 0),
|
|
1269
|
+
liveCallProbeCount: liveCallProbes.length,
|
|
1270
|
+
liveCallProbeServers: [...new Set(liveCallProbes.map((probe) => probe.serverId))],
|
|
1271
|
+
livePromptProbeCount: livePromptProbes.length,
|
|
1272
|
+
livePromptProbeServers: [...new Set(livePromptProbes.map((probe) => probe.serverId))],
|
|
1273
|
+
liveResourceProbeCount: liveResourceProbes.length,
|
|
1274
|
+
liveResourceProbeServers: [...new Set(liveResourceProbes.map((probe) => probe.serverId))],
|
|
1275
|
+
liveResourceProbeOperations: [...new Set(liveResourceProbes.map((probe) => probe.operation))],
|
|
1276
|
+
liveCompletionProbeCount: liveCompletionProbes.length,
|
|
1277
|
+
liveCompletionProbeServers: [...new Set(liveCompletionProbes.map((probe) => probe.serverId))],
|
|
1278
|
+
liveTransportProbeCount: liveTransportProbes.length,
|
|
1279
|
+
liveTransportProbeTransports: [...new Set(liveTransportProbes.map((probe) => probe.transport))],
|
|
1280
|
+
hostSemanticsProbe,
|
|
1281
|
+
liveResourceProbeResults: liveResourceProbes,
|
|
1282
|
+
liveCompletionProbeResults: liveCompletionProbes,
|
|
1283
|
+
liveTransportProbeResults: liveTransportProbes,
|
|
1284
|
+
runtimeToolFlows: [nativeToolFlow, mcpPlusToolFlow],
|
|
1285
|
+
runtimeSkillFlow: mcpPlusSkillFlow,
|
|
1286
|
+
native,
|
|
1287
|
+
mcpPlus,
|
|
1288
|
+
deltas: {
|
|
1289
|
+
providerToolCount: mcpPlus.providerToolCount - native.providerToolCount,
|
|
1290
|
+
toolsEstimatedTokens: mcpPlus.toolsEstimatedTokens - native.toolsEstimatedTokens,
|
|
1291
|
+
providerStablePrefixEstimatedTokens: mcpPlus.providerStablePrefixEstimatedTokens - native.providerStablePrefixEstimatedTokens,
|
|
1292
|
+
promptPackTotalEstimatedTokens: mcpPlus.promptPackTotalEstimatedTokens - native.promptPackTotalEstimatedTokens,
|
|
1293
|
+
},
|
|
1294
|
+
};
|
|
1295
|
+
await writeFile(path.join(runRoot, "comparison.json"), `${JSON.stringify(comparison, null, 2)}\n`, "utf8");
|
|
1296
|
+
console.log(`[comparison] ${JSON.stringify(comparison, null, 2)}`);
|