@praxis-ai/praxis 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agentCore/index.d.ts +3 -3
- package/dist/agentCore/index.js +2 -2
- package/dist/applicationLayer/applicationRuntime.d.ts +8 -1
- package/dist/applicationLayer/applicationRuntime.js +2 -0
- package/dist/applicationLayer/index.d.ts +2 -1
- package/dist/applicationLayer/index.js +1 -0
- package/dist/runtimeImplementation/praxisRuntimeKernel.d.ts +8 -1
- package/dist/runtimeImplementation/praxisRuntimeKernel.js +392 -8
- package/dist/runtimeImplementation/runtime.mcpPlane/index.d.ts +113 -2
- package/dist/runtimeImplementation/runtime.mcpPlane/index.js +392 -10
- package/package.json +1 -1
|
@@ -25,10 +25,10 @@ import { assemblePromptPack } from "../executionEngine/promptPack/promptAssemble
|
|
|
25
25
|
import { assemblePromptContextMaterials, promptPackMaterialsForManifest } from "../runtimeImplementation/runtime.execEngine/promptContextAssembly.js";
|
|
26
26
|
import { createObservationMaterial } from "../executionEngine/coreLogic/observationIntegrator.js";
|
|
27
27
|
import { createMemoryPlane, memoryPlane } from "../memory_managementPool/index.js";
|
|
28
|
-
import { buildMcpServerProfilesFromManifest, createMcpApplicationStateView, mcp, planMcpHarnessExposure } from "../runtimeImplementation/runtime.mcpPlane/index.js";
|
|
28
|
+
import { buildMcpServerProfilesFromManifest, createMcpApplicationStateView, createFileMcpPlusProfileStore, createFileMcpPlusSkillStore, createInMemoryMcpPlusOverlayStore, createInMemoryMcpPlusProfileStore, createInMemoryMcpPlusSkillStore, mcp, planMcpHarnessExposure } from "../runtimeImplementation/runtime.mcpPlane/index.js";
|
|
29
29
|
export { PromptPack, PraxisAgent, PraxisAgentArchetype, STATE_PLANE_STANDARD_CONTROLS, append, compileAgent, endpoint, harness, inspectAgentManifest, loop, mainLoop, markdown, markdownFile, model, modelFleet, overwrite, policy, prepend, replaceLastLines, sandbox, session, statePlane, storage, tool, toolPolicies, tools, validateAgentManifest, type AgentCompileErrorCode, type AgentCompileResult, type AgentIdentity, type AgentManifest, type AgentManifestInspection, type AgentManifestValidationResult, type BaseToolPolicyDecision, type BaseToolPolicyMatrixSpec, type BaseToolPolicyProfile, type BaseToolPolicyRisk, type BaseToolPolicyRule, type HarnessSpec, type FrameworkCoreContractSpec, type LoopSpec, type MainLoopSpec, type ModelEndpointSpec, type ModelFleetSpec, type ModelSpec, type PolicySpec, type PraxisAgentClass, type PraxisAgentInput, type PromptMaterialSource, type PromptPackSpec, type PromptPatchSpec, type SandboxSpec, type SandboxIsolationLevel, type SandboxMountPolicy, type SandboxNetworkRuntimePolicy, type SandboxPlatformSupport, type SandboxPlatformSupportStatus, type SandboxProcessPolicy, type SandboxProviderFamily, type SessionSpec, type StatePlaneSpec, type StorageSpec, type ToolSpec, type ToolPolicyCustomInput, } from "../runtimeImplementation/runtimeAgentManifest.js";
|
|
30
|
-
export { buildMcpServerProfilesFromManifest, createMcpApplicationStateView, mcp, planMcpHarnessExposure, };
|
|
31
|
-
export type { McpApplicationServerView, McpApplicationServerInput, McpApplicationStateView, McpHarnessExposurePlan, McpHarnessModuleSpec, McpHarnessServerMode, McpHarnessServerSpec, McpPlusApplicationServerInput, McpTransportSpec, } from "../runtimeImplementation/runtime.mcpPlane/index.js";
|
|
30
|
+
export { buildMcpServerProfilesFromManifest, createMcpApplicationStateView, createFileMcpPlusProfileStore, createFileMcpPlusSkillStore, createInMemoryMcpPlusOverlayStore, createInMemoryMcpPlusProfileStore, createInMemoryMcpPlusSkillStore, mcp, planMcpHarnessExposure, };
|
|
31
|
+
export type { McpApplicationServerView, McpApplicationServerInput, McpApplicationStateView, McpHarnessExposurePlan, McpHarnessModuleSpec, McpHarnessServerMode, McpHarnessServerSpec, McpPlusLearnedProfile, McpPlusApplicationServerInput, McpPlusOverlayStore, McpPlusOverlayStoreKey, McpPlusProfileProposal, McpPlusProfileStore, McpPlusProfileStoreKey, McpPlusRuntimeOverlay, McpPlusSkillNote, McpPlusSkillStore, McpTransportSpec, } from "../runtimeImplementation/runtime.mcpPlane/index.js";
|
|
32
32
|
export { createMemoryPlane, memoryPlane, };
|
|
33
33
|
export type { MemoryArtifactRef, MemoryIndexStatus, MemoryLayout, MemoryPlane, MemoryPlaneOptions, MemoryPolicyRisk, MemoryProfile, MemoryPromptGuide, MemoryReindexResult, MemoryRiskMetadata, MemoryScope, MemorySearchGuide, MemorySearchRequest, MemorySourceType, } from "../memory_managementPool/index.js";
|
|
34
34
|
export { capabilities, capability, createProvisionPlan, dependencies, dependencyAuthoring, provisionRuntimeDescriptor, type CapabilityFallbackSpec, type CapabilityInput, type CapabilityKind, type CapabilityPolicySpec, type CapabilityReadiness, type CapabilitySpec, type CodeIntelligenceCapabilityInput, type ProvisionPlan, type SandboxCapabilityInput, } from "../runtimeImplementation/runtime.provisionPlane/index.js";
|
package/dist/agentCore/index.js
CHANGED
|
@@ -31,9 +31,9 @@ import { assemblePromptContextMaterials, promptPackMaterialsForManifest, } from
|
|
|
31
31
|
import { createObservationMaterial, } from "../executionEngine/coreLogic/observationIntegrator.js";
|
|
32
32
|
import { runtimeAuth, } from "../runtimeImplementation/runtime.authPlane/index.js";
|
|
33
33
|
import { createMemoryPlane, memoryPlane, } from "../memory_managementPool/index.js";
|
|
34
|
-
import { buildMcpServerProfilesFromManifest, createMcpApplicationStateView, mcp, planMcpHarnessExposure, } from "../runtimeImplementation/runtime.mcpPlane/index.js";
|
|
34
|
+
import { buildMcpServerProfilesFromManifest, createMcpApplicationStateView, createFileMcpPlusProfileStore, createFileMcpPlusSkillStore, createInMemoryMcpPlusOverlayStore, createInMemoryMcpPlusProfileStore, createInMemoryMcpPlusSkillStore, mcp, planMcpHarnessExposure, } from "../runtimeImplementation/runtime.mcpPlane/index.js";
|
|
35
35
|
export { PromptPack, PraxisAgent, PraxisAgentArchetype, STATE_PLANE_STANDARD_CONTROLS, append, compileAgent, endpoint, harness, inspectAgentManifest, loop, mainLoop, markdown, markdownFile, model, modelFleet, overwrite, policy, prepend, replaceLastLines, sandbox, session, statePlane, storage, tool, toolPolicies, tools, validateAgentManifest, } from "../runtimeImplementation/runtimeAgentManifest.js";
|
|
36
|
-
export { buildMcpServerProfilesFromManifest, createMcpApplicationStateView, mcp, planMcpHarnessExposure, };
|
|
36
|
+
export { buildMcpServerProfilesFromManifest, createMcpApplicationStateView, createFileMcpPlusProfileStore, createFileMcpPlusSkillStore, createInMemoryMcpPlusOverlayStore, createInMemoryMcpPlusProfileStore, createInMemoryMcpPlusSkillStore, mcp, planMcpHarnessExposure, };
|
|
37
37
|
export { createMemoryPlane, memoryPlane, };
|
|
38
38
|
export { capabilities, capability, createProvisionPlan, dependencies, dependencyAuthoring, provisionRuntimeDescriptor, } from "../runtimeImplementation/runtime.provisionPlane/index.js";
|
|
39
39
|
export { component, createRuntimeComponentRegistry, lookupRuntimeComponent, officialRuntimeComponents, } from "../runtimeImplementation/runtime.componentPlane/index.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AgentManifest, type BaseToolExecutorPort, type AgentToolCallProgressEvent, type RuntimeApprovalResolver, type RuntimeAgentReviewResolver, type RuntimeAuthResolver, type RuntimeAuthResolverRequest, type SandboxExecutionProviderPort, type CompactExecutor, type PreCompactGovernanceExecutor, type PraxisProjectRuntime } from "../agentCore/index.js";
|
|
2
|
-
import { type McpApplicationServerInput, type McpHarnessModuleSpec, type McpPlusApplicationServerInput } from "../runtimeImplementation/runtime.mcpPlane/index.js";
|
|
2
|
+
import { type McpApplicationServerInput, type McpHarnessModuleSpec, type McpPlusApplicationServerInput, type McpPlusOverlayStore, type McpPlusProfileStore, type McpPlusSkillStore } from "../runtimeImplementation/runtime.mcpPlane/index.js";
|
|
3
3
|
import { type OpenAIV1ResponsesProviderCaller, type OpenAIV1ResponsesResult } from "../modelAdapter/actualInvocationLayer/openai/v1_responses.js";
|
|
4
4
|
import type { OpenAiV1ChatCompletionsProviderCaller } from "../modelAdapter/actualInvocationLayer/openai/v1_chat_completions.js";
|
|
5
5
|
import type { AnthropicV1MessagesProviderCaller } from "../modelAdapter/actualInvocationLayer/anthropic/v1_messages.js";
|
|
@@ -75,6 +75,13 @@ export type PraxisApplicationRuntimeOptions = {
|
|
|
75
75
|
mcpServers?: readonly McpApplicationServerInput[];
|
|
76
76
|
mcpPlusServers?: readonly McpPlusApplicationServerInput[];
|
|
77
77
|
mcpModule?: McpHarnessModuleSpec;
|
|
78
|
+
mcpPlus?: {
|
|
79
|
+
projectId?: string;
|
|
80
|
+
profileStore?: McpPlusProfileStore;
|
|
81
|
+
overlayStore?: McpPlusOverlayStore;
|
|
82
|
+
skillStore?: McpPlusSkillStore;
|
|
83
|
+
reprofileConsecutiveIndexedCalls?: number;
|
|
84
|
+
};
|
|
78
85
|
onApplicationToolEvent?: (event: PraxisApplicationEvent) => void | Promise<void>;
|
|
79
86
|
initialConversations?: readonly PraxisApplicationInitialConversation[];
|
|
80
87
|
foundationProject?: PraxisProjectRuntime;
|
|
@@ -2839,6 +2839,7 @@ export function createPraxisApplicationRuntime(options) {
|
|
|
2839
2839
|
agentReviewResolver: options.agentReviewResolver,
|
|
2840
2840
|
mcpServers: applicationMcpServerProfiles(applicationMcpModule),
|
|
2841
2841
|
mcpModule: applicationMcpModule,
|
|
2842
|
+
mcpPlus: options.mcpPlus,
|
|
2842
2843
|
storage: {
|
|
2843
2844
|
cwd: childSession.workingDirectory,
|
|
2844
2845
|
workspaceRoot: path.join(project.projectRoot, ".raxode"),
|
|
@@ -3466,6 +3467,7 @@ export function createPraxisApplicationRuntime(options) {
|
|
|
3466
3467
|
agentReviewResolver: options.agentReviewResolver,
|
|
3467
3468
|
mcpServers: applicationMcpServerProfiles(applicationMcpModule),
|
|
3468
3469
|
mcpModule: applicationMcpModule,
|
|
3470
|
+
mcpPlus: options.mcpPlus,
|
|
3469
3471
|
storage: {
|
|
3470
3472
|
cwd: state.cwd,
|
|
3471
3473
|
workspaceRoot: path.join(project.projectRoot, ".raxode"),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type { PraxisApplicationAttachment, PraxisApplicationAgentEntryView, PraxisApplicationAuxiliaryTaskInput, PraxisApplicationAuthProfileView, PraxisApplicationAuthState, PraxisApplicationCommand, PraxisApplicationCommandResult, PraxisApplicationContextTelemetry, PraxisApplicationEvent, PraxisApplicationEventKind, PraxisApplicationInputEnvelope, PraxisApplicationManifestView, PraxisApplicationModelState, PraxisApplicationPermissionProfile, PraxisApplicationReasoningEffort, PraxisApplicationRuntime, PraxisApplicationRuntimeMode, PraxisApplicationStatus, PraxisApplicationToolCatalogState, PraxisApplicationToolProfile, PraxisApplicationUsageTelemetry, PraxisApplicationViewModel, } from "./applicationContract.js";
|
|
2
2
|
export { loadApplicationProject, type PraxisApplicationProject, type PraxisApplicationProjectDescriptor, type PraxisApplicationProjectResult, } from "./applicationProject.js";
|
|
3
3
|
export { createApplicationProjectRuntime, createPraxisApplicationRuntime, type PraxisApplicationBaseToolIntegrationOptions, type CreateApplicationProjectRuntimeOptions, type PraxisApplicationInitialConversation, type PraxisApplicationInitialConversationMessage, type PraxisApplicationLiveProvider, type PraxisApplicationRuntimeOptions, } from "./applicationRuntime.js";
|
|
4
|
-
export type { McpApplicationServerInput, McpPlusApplicationServerInput, } from "../runtimeImplementation/runtime.mcpPlane/index.js";
|
|
4
|
+
export type { McpApplicationServerInput, McpPlusLearnedProfile, McpPlusApplicationServerInput, McpPlusOverlayStore, McpPlusProfileProposal, McpPlusProfileStore, McpPlusRuntimeOverlay, McpPlusSkillNote, McpPlusSkillStore, } from "../runtimeImplementation/runtime.mcpPlane/index.js";
|
|
5
|
+
export { createFileMcpPlusProfileStore, createFileMcpPlusSkillStore, createInMemoryMcpPlusOverlayStore, createInMemoryMcpPlusProfileStore, createInMemoryMcpPlusSkillStore, } from "../runtimeImplementation/runtime.mcpPlane/index.js";
|
|
5
6
|
export { createApplicationRestServer, createApplicationWebSocketServer, createLocalApplicationTransport, describeApplicationRestTransport, describeApplicationWebSocketTransport, type PraxisApplicationRestServer, type PraxisApplicationWebSocketServer, type PraxisApplicationProtocolMessage, type PraxisApplicationRestRoute, type PraxisApplicationTransportClient, type PraxisApplicationTransportDescriptor, type PraxisApplicationTransportKind, type PraxisApplicationWebSocketMessage, } from "./applicationTransport.js";
|
|
@@ -4,4 +4,5 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { loadApplicationProject, } from "./applicationProject.js";
|
|
6
6
|
export { createApplicationProjectRuntime, createPraxisApplicationRuntime, } from "./applicationRuntime.js";
|
|
7
|
+
export { createFileMcpPlusProfileStore, createFileMcpPlusSkillStore, createInMemoryMcpPlusOverlayStore, createInMemoryMcpPlusProfileStore, createInMemoryMcpPlusSkillStore, } from "../runtimeImplementation/runtime.mcpPlane/index.js";
|
|
7
8
|
export { createApplicationRestServer, createApplicationWebSocketServer, createLocalApplicationTransport, describeApplicationRestTransport, describeApplicationWebSocketTransport, } from "./applicationTransport.js";
|
|
@@ -22,7 +22,7 @@ import { type RuntimeApprovalRecord, type RuntimeSessionSnapshot, type RuntimeSe
|
|
|
22
22
|
import { type RaxStorageInitMode } from "./runtime.storagePlane/storagePlaneRuntime.js";
|
|
23
23
|
import type { SandboxRemoteWorkerAdapter } from "./runtime.sandboxPlane/sandboxCommandRunner.js";
|
|
24
24
|
import type { SandboxExecutionProviderPort } from "./runtime.sandboxPlane/sandboxPolicyMiddleware.js";
|
|
25
|
-
import { type McpHarnessModuleSpec } from "./runtime.mcpPlane/index.js";
|
|
25
|
+
import { type McpPlusOverlayStore, type McpPlusProfileStore, type McpPlusSkillStore, type McpHarnessModuleSpec } from "./runtime.mcpPlane/index.js";
|
|
26
26
|
import type { McpRuntimeServerProfile } from "./runtime.execEngine/mcpRuntimeAdapter.js";
|
|
27
27
|
export type PraxisRuntimeKernelErrorCode = "MANIFEST_COMPILE_FAILED" | "TEXT_INPUT_REJECTED" | "PROMPT_PACK_FAILED" | "MAIN_LOOP_INTERRUPTED" | "MODEL_INVOCATION_FAILED" | "MODEL_DECISION_FAILED" | "TOOL_INVOCATION_FAILED" | "PROCEDURE_INVOCATION_FAILED" | "APPROVAL_REQUIRED" | "SANDBOX_UNAVAILABLE" | "STORAGE_RESOLUTION_FAILED" | "TEXT_OUTPUT_REJECTED";
|
|
28
28
|
export type PraxisRuntimeKernelError = {
|
|
@@ -52,6 +52,13 @@ export type PraxisRuntimeKernelOptions = {
|
|
|
52
52
|
baseToolAdapters?: Partial<BaseToolExecutorPort>;
|
|
53
53
|
mcpServers?: readonly McpRuntimeServerProfile[];
|
|
54
54
|
mcpModule?: McpHarnessModuleSpec;
|
|
55
|
+
mcpPlus?: {
|
|
56
|
+
projectId?: string;
|
|
57
|
+
profileStore?: McpPlusProfileStore;
|
|
58
|
+
overlayStore?: McpPlusOverlayStore;
|
|
59
|
+
skillStore?: McpPlusSkillStore;
|
|
60
|
+
reprofileConsecutiveIndexedCalls?: number;
|
|
61
|
+
};
|
|
55
62
|
baseToolPolicy?: RuntimeBaseToolExecutorPolicy;
|
|
56
63
|
baseToolResourceLimits?: RuntimeBaseToolExecutorResourceLimits;
|
|
57
64
|
store?: RuntimeSessionStateEventStore;
|
|
@@ -42,7 +42,7 @@ import { approvalInterfaceEnvelope, } from "../interfaceAdapter/interfaceEnvelop
|
|
|
42
42
|
import { createInMemorySessionStateEventStore, createSqliteSessionStateEventStore, } from "./runtimeSessionStateEventStore.js";
|
|
43
43
|
import { applyRaxStorageInitPlan, createStoragePlaneRuntime, } from "./runtime.storagePlane/storagePlaneRuntime.js";
|
|
44
44
|
import { prepareSandboxRuntime, } from "./runtime.sandboxPlane/sandboxRuntimeProvider.js";
|
|
45
|
-
import { buildMcpServerProfilesFromManifest, mcp, planMcpHarnessExposure, } from "./runtime.mcpPlane/index.js";
|
|
45
|
+
import { buildMcpServerProfilesFromManifest, createInMemoryMcpPlusOverlayStore, createFileMcpPlusProfileStore, createFileMcpPlusSkillStore, learnedProfileFromProposal, mcp, mcpHarnessModuleFrom, planMcpHarnessExposure, } from "./runtime.mcpPlane/index.js";
|
|
46
46
|
async function inferFilesystemActionForTool(input) {
|
|
47
47
|
if (input.toolId !== "patch.apply")
|
|
48
48
|
return undefined;
|
|
@@ -290,9 +290,7 @@ function defaultMcpServerId(toolId, args) {
|
|
|
290
290
|
return undefined;
|
|
291
291
|
return readString(args.serverId) ?? "local-mcp";
|
|
292
292
|
}
|
|
293
|
-
function
|
|
294
|
-
if (dynamicTools.length === 0)
|
|
295
|
-
return manifest;
|
|
293
|
+
function withRuntimeHarnessToolLayer(manifest, dynamicTools, reason) {
|
|
296
294
|
const byId = new Map();
|
|
297
295
|
for (const tool of [...manifest.harness.tools, ...dynamicTools]) {
|
|
298
296
|
byId.set(tool.toolId, tool);
|
|
@@ -305,6 +303,7 @@ function withRuntimeHarnessTools(manifest, dynamicTools) {
|
|
|
305
303
|
metadata: {
|
|
306
304
|
...manifest.harness.metadata,
|
|
307
305
|
runtimeMcpDynamicToolCount: dynamicTools.length,
|
|
306
|
+
runtimeMcpDynamicToolRefreshReason: reason,
|
|
308
307
|
},
|
|
309
308
|
},
|
|
310
309
|
};
|
|
@@ -345,7 +344,246 @@ function normalizeMcpNativeToolDeclaration(tool) {
|
|
|
345
344
|
const schema = isRecord(tool.inputSchema) ? tool.inputSchema : isRecord(tool.input_schema) ? tool.input_schema : {};
|
|
346
345
|
return { name, description, inputSchema: schema };
|
|
347
346
|
}
|
|
348
|
-
|
|
347
|
+
function runtimeMcpPlusProjectId(input) {
|
|
348
|
+
if (typeof input.explicit === "string" && input.explicit.trim().length > 0)
|
|
349
|
+
return input.explicit.trim();
|
|
350
|
+
return `project.${createHash("sha256").update(path.resolve(input.workspaceRoot)).digest("hex").slice(0, 16)}`;
|
|
351
|
+
}
|
|
352
|
+
function mcpPlusOverlayToExposureState(overlay) {
|
|
353
|
+
if (overlay === undefined)
|
|
354
|
+
return {};
|
|
355
|
+
return {
|
|
356
|
+
mode: overlay.mode,
|
|
357
|
+
activeTools: [
|
|
358
|
+
...overlay.activeTools,
|
|
359
|
+
...(overlay.pendingReprofile ? ["mcp_plus.reprofile"] : []),
|
|
360
|
+
],
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function readStringList(value) {
|
|
364
|
+
if (!Array.isArray(value))
|
|
365
|
+
return [];
|
|
366
|
+
return value.filter((item) => typeof item === "string" && item.trim().length > 0).map((item) => item.trim());
|
|
367
|
+
}
|
|
368
|
+
function proposalFromArgs(args, fallbackServerId) {
|
|
369
|
+
const rawToolCards = isRecord(args.toolCards) ? args.toolCards : undefined;
|
|
370
|
+
const toolCards = rawToolCards === undefined ? undefined : Object.fromEntries(Object.entries(rawToolCards).flatMap(([toolName, card]) => {
|
|
371
|
+
if (!isRecord(card))
|
|
372
|
+
return [];
|
|
373
|
+
return [[toolName, {
|
|
374
|
+
title: readString(card.title),
|
|
375
|
+
summary: readString(card.summary),
|
|
376
|
+
keywords: readStringList(card.keywords),
|
|
377
|
+
}]];
|
|
378
|
+
}));
|
|
379
|
+
const rawSkillChapters = Array.isArray(args.skillChapters) ? args.skillChapters : [];
|
|
380
|
+
const proposal = {
|
|
381
|
+
serverId: readString(args.serverId) ?? fallbackServerId,
|
|
382
|
+
pinnedTools: readStringList(args.pinnedTools),
|
|
383
|
+
warmTools: readStringList(args.warmTools),
|
|
384
|
+
indexedTools: readStringList(args.indexedTools),
|
|
385
|
+
alwaysIndexTools: readStringList(args.alwaysIndexTools),
|
|
386
|
+
toolCards,
|
|
387
|
+
skillChapters: rawSkillChapters.flatMap((chapter) => {
|
|
388
|
+
if (!isRecord(chapter))
|
|
389
|
+
return [];
|
|
390
|
+
const id = readString(chapter.id);
|
|
391
|
+
const title = readString(chapter.title);
|
|
392
|
+
const summary = readString(chapter.summary);
|
|
393
|
+
return id === undefined || title === undefined || summary === undefined ? [] : [{ id, title, summary }];
|
|
394
|
+
}),
|
|
395
|
+
rationale: readString(args.rationale),
|
|
396
|
+
};
|
|
397
|
+
if (Object.hasOwn(args, "modeHint")) {
|
|
398
|
+
return {
|
|
399
|
+
...proposal,
|
|
400
|
+
modeHint: args.modeHint,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
return proposal;
|
|
404
|
+
}
|
|
405
|
+
function mcpPlusServerSpec(manifest, serverId) {
|
|
406
|
+
return mcpHarnessModuleFrom(manifest.harness)?.servers.find((server) => server.serverId === serverId);
|
|
407
|
+
}
|
|
408
|
+
function createRuntimeMcpPlusController(input) {
|
|
409
|
+
let nativeInventory = {};
|
|
410
|
+
async function loadOverlay(serverId, now) {
|
|
411
|
+
return await input.overlayStore.load({ sessionId: input.sessionId, serverId }) ?? {
|
|
412
|
+
serverId,
|
|
413
|
+
sessionId: input.sessionId,
|
|
414
|
+
mode: "expanded",
|
|
415
|
+
activeTools: [],
|
|
416
|
+
counters: { consecutiveIndexedToolCalls: {} },
|
|
417
|
+
updatedAt: now,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
async setNativeInventory(inventory) {
|
|
422
|
+
nativeInventory = inventory;
|
|
423
|
+
},
|
|
424
|
+
async learnedProfilesByServerId(manifest) {
|
|
425
|
+
const module = mcpHarnessModuleFrom(manifest.harness);
|
|
426
|
+
const profiles = {};
|
|
427
|
+
for (const server of module?.servers ?? []) {
|
|
428
|
+
if (server.mode !== "mcp-plus" || server.manifest !== undefined)
|
|
429
|
+
continue;
|
|
430
|
+
profiles[server.serverId] = await input.profileStore.load({ projectId: input.projectId, serverId: server.serverId });
|
|
431
|
+
}
|
|
432
|
+
return profiles;
|
|
433
|
+
},
|
|
434
|
+
async exposureStateByServerId(manifest) {
|
|
435
|
+
const module = mcpHarnessModuleFrom(manifest.harness);
|
|
436
|
+
const states = {};
|
|
437
|
+
for (const server of module?.servers ?? []) {
|
|
438
|
+
if (server.mode !== "mcp-plus")
|
|
439
|
+
continue;
|
|
440
|
+
states[server.serverId] = mcpPlusOverlayToExposureState(await input.overlayStore.load({ sessionId: input.sessionId, serverId: server.serverId }));
|
|
441
|
+
}
|
|
442
|
+
return states;
|
|
443
|
+
},
|
|
444
|
+
async callControlTool(control) {
|
|
445
|
+
if (control.controlName === "mcp_plus.init" || control.controlName === "mcp_plus.reprofile") {
|
|
446
|
+
const proposal = proposalFromArgs(control.args, control.serverId);
|
|
447
|
+
const nativeTools = nativeInventory[proposal.serverId] ?? [];
|
|
448
|
+
const existing = await input.profileStore.load({ projectId: input.projectId, serverId: proposal.serverId });
|
|
449
|
+
const learned = learnedProfileFromProposal({
|
|
450
|
+
proposal,
|
|
451
|
+
nativeTools,
|
|
452
|
+
projectId: input.projectId,
|
|
453
|
+
now: control.now,
|
|
454
|
+
existing,
|
|
455
|
+
});
|
|
456
|
+
if (!learned.ok)
|
|
457
|
+
return { ok: false, error: learned.error };
|
|
458
|
+
await input.profileStore.save({ projectId: input.projectId, serverId: proposal.serverId }, learned.profile);
|
|
459
|
+
const overlay = await loadOverlay(proposal.serverId, control.now);
|
|
460
|
+
await input.overlayStore.save({ sessionId: input.sessionId, serverId: proposal.serverId }, {
|
|
461
|
+
...overlay,
|
|
462
|
+
mode: "expanded",
|
|
463
|
+
activeTools: [],
|
|
464
|
+
pendingReprofile: false,
|
|
465
|
+
counters: { consecutiveIndexedToolCalls: {} },
|
|
466
|
+
updatedAt: control.now,
|
|
467
|
+
metadata: {
|
|
468
|
+
...(overlay.metadata ?? {}),
|
|
469
|
+
lastProfileControl: control.controlName,
|
|
470
|
+
},
|
|
471
|
+
});
|
|
472
|
+
return {
|
|
473
|
+
ok: true,
|
|
474
|
+
output: {
|
|
475
|
+
accepted: true,
|
|
476
|
+
serverId: proposal.serverId,
|
|
477
|
+
projectId: input.projectId,
|
|
478
|
+
schemaVersion: learned.profile.schemaVersion,
|
|
479
|
+
controlName: control.controlName,
|
|
480
|
+
},
|
|
481
|
+
metadata: { serverId: proposal.serverId, projectId: input.projectId, controlName: control.controlName },
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
if (control.controlName === "mcp_plus.skill_read") {
|
|
485
|
+
const serverId = readString(control.args.serverId) ?? control.serverId;
|
|
486
|
+
const notes = await input.skillStore.read({ serverId, projectId: input.projectId }, {
|
|
487
|
+
id: readString(control.args.id),
|
|
488
|
+
chapter: readString(control.args.chapter),
|
|
489
|
+
});
|
|
490
|
+
return { ok: true, output: { serverId, projectId: input.projectId, notes } };
|
|
491
|
+
}
|
|
492
|
+
if (control.controlName === "mcp_plus.skill_write" || control.controlName === "mcp_plus.finish") {
|
|
493
|
+
const rawSkill = control.controlName === "mcp_plus.finish" && isRecord(control.args.skill)
|
|
494
|
+
? control.args.skill
|
|
495
|
+
: control.args;
|
|
496
|
+
const serverId = readString(rawSkill.serverId) ?? readString(control.args.serverId) ?? control.serverId;
|
|
497
|
+
const chapter = readString(rawSkill.chapter);
|
|
498
|
+
const title = readString(rawSkill.title);
|
|
499
|
+
const summary = readString(rawSkill.summary);
|
|
500
|
+
if (chapter === undefined || title === undefined || summary === undefined) {
|
|
501
|
+
return {
|
|
502
|
+
ok: true,
|
|
503
|
+
output: {
|
|
504
|
+
accepted: false,
|
|
505
|
+
serverId,
|
|
506
|
+
projectId: input.projectId,
|
|
507
|
+
reason: "No complete skill note was provided.",
|
|
508
|
+
},
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
const note = await input.skillStore.write({ serverId, projectId: input.projectId }, {
|
|
512
|
+
chapter,
|
|
513
|
+
title,
|
|
514
|
+
summary,
|
|
515
|
+
whenToUse: readString(rawSkill.whenToUse),
|
|
516
|
+
do: readStringList(rawSkill.do),
|
|
517
|
+
why: readString(rawSkill.why),
|
|
518
|
+
avoid: readStringList(rawSkill.avoid),
|
|
519
|
+
pitfalls: readStringList(rawSkill.pitfalls),
|
|
520
|
+
});
|
|
521
|
+
return { ok: true, output: { accepted: true, serverId, projectId: input.projectId, note } };
|
|
522
|
+
}
|
|
523
|
+
if (control.controlName === "mcp_plus.expand") {
|
|
524
|
+
const serverId = readString(control.args.server) ?? readString(control.args.serverId) ?? control.serverId;
|
|
525
|
+
const request = readString(control.args.request)?.toLowerCase() ?? "";
|
|
526
|
+
const server = mcpPlusServerSpec(control.manifest, serverId);
|
|
527
|
+
const profile = await input.profileStore.load({ projectId: input.projectId, serverId });
|
|
528
|
+
const indexedTools = server?.manifest?.exposure?.indexedTools ?? profile?.exposure.indexedTools ?? [];
|
|
529
|
+
const activatedTools = request.length === 0
|
|
530
|
+
? indexedTools
|
|
531
|
+
: indexedTools.filter((toolName) => toolName.toLowerCase().includes(request));
|
|
532
|
+
const overlay = await loadOverlay(serverId, control.now);
|
|
533
|
+
await input.overlayStore.save({ sessionId: input.sessionId, serverId }, {
|
|
534
|
+
...overlay,
|
|
535
|
+
mode: "expanded",
|
|
536
|
+
activeTools: [...new Set([...overlay.activeTools, ...activatedTools])],
|
|
537
|
+
updatedAt: control.now,
|
|
538
|
+
});
|
|
539
|
+
return { ok: true, output: { serverId, activatedTools, mode: "expanded" } };
|
|
540
|
+
}
|
|
541
|
+
return { ok: false, error: { code: "MCP_PLUS_CONTROL_UNKNOWN", message: `Unknown MCP+ control tool: ${control.controlName}`, publicSafe: true } };
|
|
542
|
+
},
|
|
543
|
+
async recordToolCall(record) {
|
|
544
|
+
if (!record.ok)
|
|
545
|
+
return false;
|
|
546
|
+
const toolSpec = record.manifest.harness.tools.find((tool) => tool.toolId === record.toolId);
|
|
547
|
+
if (toolSpec?.metadata?.toolProviderKind !== "mcp-static")
|
|
548
|
+
return false;
|
|
549
|
+
const serverId = readString(toolSpec.metadata.serverId);
|
|
550
|
+
const nativeToolName = readString(toolSpec.metadata.nativeToolName);
|
|
551
|
+
if (serverId === undefined || nativeToolName === undefined)
|
|
552
|
+
return false;
|
|
553
|
+
const server = mcpPlusServerSpec(record.manifest, serverId);
|
|
554
|
+
if (server?.mode !== "mcp-plus")
|
|
555
|
+
return false;
|
|
556
|
+
const profile = server.manifest === undefined
|
|
557
|
+
? await input.profileStore.load({ projectId: input.projectId, serverId })
|
|
558
|
+
: undefined;
|
|
559
|
+
const indexedTools = new Set(server.manifest?.exposure?.indexedTools ?? profile?.exposure.indexedTools ?? []);
|
|
560
|
+
const overlay = await loadOverlay(serverId, record.now);
|
|
561
|
+
const consecutiveIndexedToolCalls = { ...overlay.counters.consecutiveIndexedToolCalls };
|
|
562
|
+
let pendingReprofile = overlay.pendingReprofile;
|
|
563
|
+
if (indexedTools.has(nativeToolName)) {
|
|
564
|
+
consecutiveIndexedToolCalls[nativeToolName] = (consecutiveIndexedToolCalls[nativeToolName] ?? 0) + 1;
|
|
565
|
+
if (consecutiveIndexedToolCalls[nativeToolName] >= input.reprofileConsecutiveIndexedCalls) {
|
|
566
|
+
pendingReprofile = true;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
for (const toolName of Object.keys(consecutiveIndexedToolCalls)) {
|
|
571
|
+
consecutiveIndexedToolCalls[toolName] = 0;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
await input.overlayStore.save({ sessionId: input.sessionId, serverId }, {
|
|
575
|
+
...overlay,
|
|
576
|
+
mode: "expanded",
|
|
577
|
+
activeTools: [...new Set([...overlay.activeTools, nativeToolName])],
|
|
578
|
+
pendingReprofile,
|
|
579
|
+
counters: { consecutiveIndexedToolCalls },
|
|
580
|
+
updatedAt: record.now,
|
|
581
|
+
});
|
|
582
|
+
return true;
|
|
583
|
+
},
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
async function discoverRuntimeMcpDynamicTools(manifest, executor, mcpPlusRuntime) {
|
|
349
587
|
const profiles = buildMcpServerProfilesFromManifest(manifest);
|
|
350
588
|
if (profiles.length === 0 || executor.mcp?.listTools === undefined)
|
|
351
589
|
return [];
|
|
@@ -359,7 +597,8 @@ async function discoverRuntimeMcpDynamicTools(manifest, executor) {
|
|
|
359
597
|
.map(normalizeMcpNativeToolDeclaration)
|
|
360
598
|
.filter((tool) => tool !== undefined);
|
|
361
599
|
}
|
|
362
|
-
|
|
600
|
+
await mcpPlusRuntime?.setNativeInventory(inventory);
|
|
601
|
+
return planMcpHarnessExposure(manifest, inventory, await mcpPlusRuntime?.exposureStateByServerId(manifest) ?? {}, await mcpPlusRuntime?.learnedProfilesByServerId(manifest) ?? {}).servers.flatMap((server) => [...server.dynamicToolSpecs]);
|
|
363
602
|
}
|
|
364
603
|
function providerToolMappings(manifest) {
|
|
365
604
|
return createProviderToolMappings(manifest.harness.tools);
|
|
@@ -2562,6 +2801,123 @@ async function executeBaseToolDecision(input) {
|
|
|
2562
2801
|
const dynamicRuntimeArguments = isRecord(dynamicToolSpec?.metadata?.runtimeArguments)
|
|
2563
2802
|
? dynamicToolSpec.metadata.runtimeArguments
|
|
2564
2803
|
: undefined;
|
|
2804
|
+
if (dynamicToolSpec?.metadata?.toolProviderKind === "mcp-plus-control") {
|
|
2805
|
+
const serverId = readString(dynamicToolSpec.metadata.serverId);
|
|
2806
|
+
const controlName = readString(dynamicToolSpec.metadata.controlName);
|
|
2807
|
+
if (serverId === undefined || controlName === undefined || input.mcpPlusRuntime === undefined) {
|
|
2808
|
+
const record = {
|
|
2809
|
+
callId: input.toolCallId,
|
|
2810
|
+
toolId: input.toolId,
|
|
2811
|
+
arguments: input.args,
|
|
2812
|
+
ok: false,
|
|
2813
|
+
error: {
|
|
2814
|
+
code: "MCP_PLUS_CONTROL_UNAVAILABLE",
|
|
2815
|
+
message: "MCP+ control tool is not available in this runtime.",
|
|
2816
|
+
publicSafe: true,
|
|
2817
|
+
},
|
|
2818
|
+
};
|
|
2819
|
+
const observation = createObservationMaterial({
|
|
2820
|
+
observationId: `${input.sessionId}:observation:${input.toolCallId}`,
|
|
2821
|
+
source: "baseTool",
|
|
2822
|
+
status: "failed",
|
|
2823
|
+
title: `MCP+ control ${controlName ?? input.toolId}`,
|
|
2824
|
+
summary: "MCP+ control tool is not available.",
|
|
2825
|
+
refs: [input.toolCallId, input.toolId],
|
|
2826
|
+
payload: record.error,
|
|
2827
|
+
metadata: metadataRecord({ toolCallId: input.toolCallId, toolId: input.toolId }),
|
|
2828
|
+
});
|
|
2829
|
+
return {
|
|
2830
|
+
record,
|
|
2831
|
+
observation,
|
|
2832
|
+
events: ["runtime.mcpPlus.control.unavailable"],
|
|
2833
|
+
governance: {
|
|
2834
|
+
kind: "runtime.execEngine.baseTool.governanceDecision",
|
|
2835
|
+
toolId: input.toolId,
|
|
2836
|
+
status: "deny",
|
|
2837
|
+
risk: "safe",
|
|
2838
|
+
policyProfile: input.manifest.toolPolicy.profile,
|
|
2839
|
+
policyMatrixId: input.manifest.toolPolicy.matrixId,
|
|
2840
|
+
approvalRequired: false,
|
|
2841
|
+
approvalReason: "MCP+ control tool is not available.",
|
|
2842
|
+
sandbox: {
|
|
2843
|
+
sandboxId: input.manifest.sandbox.sandboxId,
|
|
2844
|
+
profile: input.manifest.sandbox.profile,
|
|
2845
|
+
providerFamily: input.manifest.sandbox.providerFamily,
|
|
2846
|
+
isolationLevel: input.manifest.sandbox.isolationLevel,
|
|
2847
|
+
filesystem: input.manifest.sandbox.filesystem,
|
|
2848
|
+
network: input.manifest.sandbox.network,
|
|
2849
|
+
shell: input.manifest.sandbox.shell,
|
|
2850
|
+
hostObserved: input.manifest.sandbox.profile === "host-observed",
|
|
2851
|
+
dependencyRefs: input.manifest.sandbox.dependencyRefs ?? [],
|
|
2852
|
+
},
|
|
2853
|
+
resourceLimits: input.manifest.sandbox.resourceLimits,
|
|
2854
|
+
publicSafe: true,
|
|
2855
|
+
events: ["runtime.mcpPlus.control.unavailable"],
|
|
2856
|
+
metadata: { toolCallId: input.toolCallId },
|
|
2857
|
+
},
|
|
2858
|
+
};
|
|
2859
|
+
}
|
|
2860
|
+
const controlResult = await input.mcpPlusRuntime.callControlTool({
|
|
2861
|
+
manifest: input.manifest,
|
|
2862
|
+
serverId,
|
|
2863
|
+
controlName,
|
|
2864
|
+
args: input.args,
|
|
2865
|
+
now: input.now(),
|
|
2866
|
+
});
|
|
2867
|
+
const record = {
|
|
2868
|
+
callId: input.toolCallId,
|
|
2869
|
+
toolId: input.toolId,
|
|
2870
|
+
arguments: input.args,
|
|
2871
|
+
ok: controlResult.ok,
|
|
2872
|
+
...(controlResult.ok ? { output: controlResult.output } : { error: controlResult.error }),
|
|
2873
|
+
};
|
|
2874
|
+
const controlFailureMessage = controlResult.ok ? undefined : controlResult.error?.message ?? "MCP+ control tool failed.";
|
|
2875
|
+
const observation = createObservationMaterial({
|
|
2876
|
+
observationId: `${input.sessionId}:observation:${input.toolCallId}`,
|
|
2877
|
+
source: "baseTool",
|
|
2878
|
+
status: controlResult.ok ? "completed" : "failed",
|
|
2879
|
+
title: `MCP+ control ${controlName}`,
|
|
2880
|
+
summary: controlResult.ok ? "MCP+ control tool completed." : controlFailureMessage ?? "MCP+ control tool failed.",
|
|
2881
|
+
refs: [input.toolCallId, input.toolId],
|
|
2882
|
+
payload: controlResult.ok ? controlResult.output : controlResult.error ?? { code: "MCP_PLUS_CONTROL_FAILED", message: controlFailureMessage, publicSafe: true },
|
|
2883
|
+
metadata: metadataRecord({
|
|
2884
|
+
toolCallId: input.toolCallId,
|
|
2885
|
+
toolId: input.toolId,
|
|
2886
|
+
serverId,
|
|
2887
|
+
controlName,
|
|
2888
|
+
observationStatus: controlResult.ok ? "completed" : "failed",
|
|
2889
|
+
}),
|
|
2890
|
+
});
|
|
2891
|
+
return {
|
|
2892
|
+
record,
|
|
2893
|
+
observation,
|
|
2894
|
+
events: [controlResult.ok ? "runtime.mcpPlus.control.completed" : "runtime.mcpPlus.control.failed"],
|
|
2895
|
+
governance: {
|
|
2896
|
+
kind: "runtime.execEngine.baseTool.governanceDecision",
|
|
2897
|
+
toolId: input.toolId,
|
|
2898
|
+
status: controlResult.ok ? "allow" : "deny",
|
|
2899
|
+
risk: "safe",
|
|
2900
|
+
policyProfile: input.manifest.toolPolicy.profile,
|
|
2901
|
+
policyMatrixId: input.manifest.toolPolicy.matrixId,
|
|
2902
|
+
approvalRequired: false,
|
|
2903
|
+
sandbox: {
|
|
2904
|
+
sandboxId: input.manifest.sandbox.sandboxId,
|
|
2905
|
+
profile: input.manifest.sandbox.profile,
|
|
2906
|
+
providerFamily: input.manifest.sandbox.providerFamily,
|
|
2907
|
+
isolationLevel: input.manifest.sandbox.isolationLevel,
|
|
2908
|
+
filesystem: input.manifest.sandbox.filesystem,
|
|
2909
|
+
network: input.manifest.sandbox.network,
|
|
2910
|
+
shell: input.manifest.sandbox.shell,
|
|
2911
|
+
hostObserved: input.manifest.sandbox.profile === "host-observed",
|
|
2912
|
+
dependencyRefs: input.manifest.sandbox.dependencyRefs ?? [],
|
|
2913
|
+
},
|
|
2914
|
+
resourceLimits: input.manifest.sandbox.resourceLimits,
|
|
2915
|
+
publicSafe: true,
|
|
2916
|
+
events: [controlResult.ok ? "runtime.mcpPlus.control.completed" : "runtime.mcpPlus.control.failed"],
|
|
2917
|
+
metadata: { serverId, controlName },
|
|
2918
|
+
},
|
|
2919
|
+
};
|
|
2920
|
+
}
|
|
2565
2921
|
if (dynamicRuntimeToolId !== undefined && dynamicRuntimeToolId !== input.toolId) {
|
|
2566
2922
|
const delegatedArgs = dynamicRuntimeToolId === "mcp.use"
|
|
2567
2923
|
? {
|
|
@@ -2576,6 +2932,7 @@ async function executeBaseToolDecision(input) {
|
|
|
2576
2932
|
...input,
|
|
2577
2933
|
toolId: dynamicRuntimeToolId,
|
|
2578
2934
|
args: delegatedArgs,
|
|
2935
|
+
mcpPlusRuntime: input.mcpPlusRuntime,
|
|
2579
2936
|
});
|
|
2580
2937
|
return {
|
|
2581
2938
|
...delegated,
|
|
@@ -3525,6 +3882,7 @@ export class PraxisRuntimeKernel {
|
|
|
3525
3882
|
});
|
|
3526
3883
|
const dryRun = options.dryRun !== false;
|
|
3527
3884
|
manifest = withRuntimeMcpModule(manifest, options.mcpModule);
|
|
3885
|
+
const runtimeMcpBaseManifest = manifest;
|
|
3528
3886
|
const defaultBaseToolPolicy = {
|
|
3529
3887
|
workspaceRoot: toolWorkspaceRoot,
|
|
3530
3888
|
allowedRoots: toolAllowedRoots,
|
|
@@ -3573,10 +3931,25 @@ export class PraxisRuntimeKernel {
|
|
|
3573
3931
|
id: "praxis-runtime-kernel",
|
|
3574
3932
|
sessionId,
|
|
3575
3933
|
};
|
|
3576
|
-
|
|
3934
|
+
const mcpPlusRuntime = createRuntimeMcpPlusController({
|
|
3935
|
+
sessionId,
|
|
3936
|
+
projectId: runtimeMcpPlusProjectId({
|
|
3937
|
+
explicit: options.mcpPlus?.projectId,
|
|
3938
|
+
workspaceRoot: toolWorkspaceRoot,
|
|
3939
|
+
}),
|
|
3940
|
+
profileStore: options.mcpPlus?.profileStore ?? createFileMcpPlusProfileStore(path.join(toolWorkspaceRoot, ".rax_workspace", "mcp-plus")),
|
|
3941
|
+
overlayStore: options.mcpPlus?.overlayStore ?? createInMemoryMcpPlusOverlayStore(),
|
|
3942
|
+
skillStore: options.mcpPlus?.skillStore ?? createFileMcpPlusSkillStore(path.join(toolWorkspaceRoot, ".rax_workspace", "mcp-plus")),
|
|
3943
|
+
reprofileConsecutiveIndexedCalls: options.mcpPlus?.reprofileConsecutiveIndexedCalls ?? 6,
|
|
3944
|
+
});
|
|
3577
3945
|
const maxModelTurns = manifest.harness.loop.maxModelTurns ?? 2;
|
|
3578
3946
|
const maxToolCalls = manifest.harness.loop.maxToolCalls ?? 4;
|
|
3579
|
-
|
|
3947
|
+
let toolMappings = [];
|
|
3948
|
+
async function refreshRuntimeMcpTools(reason) {
|
|
3949
|
+
manifest = withRuntimeHarnessToolLayer(runtimeMcpBaseManifest, await discoverRuntimeMcpDynamicTools(runtimeMcpBaseManifest, executor, mcpPlusRuntime), reason);
|
|
3950
|
+
toolMappings = providerToolMappings(manifest);
|
|
3951
|
+
}
|
|
3952
|
+
await refreshRuntimeMcpTools("session.checkpoint.start");
|
|
3580
3953
|
const providerFamily = providerToolSchemaFamilyForModel(manifest.model);
|
|
3581
3954
|
let toolContextSelection = {
|
|
3582
3955
|
families: normalizedSelection(options.toolContextSelection?.families),
|
|
@@ -4758,6 +5131,7 @@ export class PraxisRuntimeKernel {
|
|
|
4758
5131
|
store,
|
|
4759
5132
|
approvalResolver: options.approvalResolver,
|
|
4760
5133
|
agentReviewResolver: options.agentReviewResolver,
|
|
5134
|
+
mcpPlusRuntime,
|
|
4761
5135
|
preparedSandbox: sandboxPrepared.sandbox,
|
|
4762
5136
|
dependencyRuntime: options.baseToolDependencyRuntime,
|
|
4763
5137
|
now,
|
|
@@ -4770,6 +5144,16 @@ export class PraxisRuntimeKernel {
|
|
|
4770
5144
|
});
|
|
4771
5145
|
toolCalls.push(executed.record);
|
|
4772
5146
|
observations.push(executed.observation);
|
|
5147
|
+
const mcpPlusToolCallUpdated = await mcpPlusRuntime.recordToolCall({
|
|
5148
|
+
manifest,
|
|
5149
|
+
toolId: executed.record.toolId,
|
|
5150
|
+
ok: executed.record.ok,
|
|
5151
|
+
now: now(),
|
|
5152
|
+
});
|
|
5153
|
+
if (executed.record.ok && (mcpPlusToolCallUpdated ||
|
|
5154
|
+
manifest.harness.tools.find((tool) => tool.toolId === executed.record.toolId)?.metadata?.toolProviderKind === "mcp-plus-control")) {
|
|
5155
|
+
await refreshRuntimeMcpTools("session.checkpoint.after_mcp_tool");
|
|
5156
|
+
}
|
|
4773
5157
|
if (invalidatesFileReadCache(executed.record.toolId)) {
|
|
4774
5158
|
sameTurnFileReadCache.clear();
|
|
4775
5159
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ExposureState, type McpCompatibleSurface, type McpPlusManifest, type NativeToolDeclaration } from "@praxis-ai/mcp-plus";
|
|
1
|
+
import { type ExposureMode, type ExposureState, type McpCompatibleSurface, type McpPlusManifest, type NativeToolDeclaration } from "@praxis-ai/mcp-plus";
|
|
2
2
|
import type { ToolSpec } from "../runtimeAgentManifest.js";
|
|
3
3
|
import type { McpRuntimeServerProfile } from "../runtime.execEngine/mcpRuntimeAdapter.js";
|
|
4
4
|
export type McpHarnessServerMode = "native" | "mcp-plus";
|
|
@@ -74,6 +74,90 @@ export type McpExposurePlanServer = {
|
|
|
74
74
|
export type McpHarnessExposurePlan = {
|
|
75
75
|
servers: readonly McpExposurePlanServer[];
|
|
76
76
|
};
|
|
77
|
+
export type McpPlusProfileProposal = {
|
|
78
|
+
serverId: string;
|
|
79
|
+
pinnedTools?: readonly string[];
|
|
80
|
+
warmTools?: readonly string[];
|
|
81
|
+
indexedTools?: readonly string[];
|
|
82
|
+
alwaysIndexTools?: readonly string[];
|
|
83
|
+
toolCards?: Readonly<Record<string, {
|
|
84
|
+
title?: string;
|
|
85
|
+
summary?: string;
|
|
86
|
+
keywords?: readonly string[];
|
|
87
|
+
}>>;
|
|
88
|
+
skillChapters?: readonly {
|
|
89
|
+
id: string;
|
|
90
|
+
title: string;
|
|
91
|
+
summary: string;
|
|
92
|
+
}[];
|
|
93
|
+
rationale?: string;
|
|
94
|
+
metadata?: Readonly<Record<string, unknown>>;
|
|
95
|
+
};
|
|
96
|
+
export type McpPlusLearnedProfile = {
|
|
97
|
+
schemaVersion: "mcp-plus.profile.v1";
|
|
98
|
+
serverId: string;
|
|
99
|
+
projectId: string;
|
|
100
|
+
exposure: NonNullable<McpPlusManifest["exposure"]>;
|
|
101
|
+
skills?: NonNullable<McpPlusManifest["skills"]>;
|
|
102
|
+
rationale?: string;
|
|
103
|
+
createdAt: string;
|
|
104
|
+
updatedAt: string;
|
|
105
|
+
metadata?: Readonly<Record<string, unknown>>;
|
|
106
|
+
};
|
|
107
|
+
export type McpPlusRuntimeOverlay = {
|
|
108
|
+
serverId: string;
|
|
109
|
+
sessionId: string;
|
|
110
|
+
mode: ExposureMode;
|
|
111
|
+
activeTools: readonly string[];
|
|
112
|
+
pendingReprofile?: boolean;
|
|
113
|
+
counters: {
|
|
114
|
+
consecutiveIndexedToolCalls: Readonly<Record<string, number>>;
|
|
115
|
+
};
|
|
116
|
+
updatedAt: string;
|
|
117
|
+
metadata?: Readonly<Record<string, unknown>>;
|
|
118
|
+
};
|
|
119
|
+
export type McpPlusProfileStoreKey = {
|
|
120
|
+
serverId: string;
|
|
121
|
+
projectId: string;
|
|
122
|
+
};
|
|
123
|
+
export type McpPlusOverlayStoreKey = {
|
|
124
|
+
serverId: string;
|
|
125
|
+
sessionId: string;
|
|
126
|
+
};
|
|
127
|
+
export type McpPlusProfileStore = {
|
|
128
|
+
load(key: McpPlusProfileStoreKey): Promise<McpPlusLearnedProfile | undefined>;
|
|
129
|
+
save(key: McpPlusProfileStoreKey, profile: McpPlusLearnedProfile): Promise<void>;
|
|
130
|
+
};
|
|
131
|
+
export type McpPlusOverlayStore = {
|
|
132
|
+
load(key: McpPlusOverlayStoreKey): Promise<McpPlusRuntimeOverlay | undefined>;
|
|
133
|
+
save(key: McpPlusOverlayStoreKey, overlay: McpPlusRuntimeOverlay): Promise<void>;
|
|
134
|
+
};
|
|
135
|
+
export type McpPlusSkillNote = {
|
|
136
|
+
id: string;
|
|
137
|
+
serverId: string;
|
|
138
|
+
projectId: string;
|
|
139
|
+
chapter: string;
|
|
140
|
+
title: string;
|
|
141
|
+
summary: string;
|
|
142
|
+
whenToUse?: string;
|
|
143
|
+
do?: readonly string[];
|
|
144
|
+
why?: string;
|
|
145
|
+
avoid?: readonly string[];
|
|
146
|
+
pitfalls?: readonly string[];
|
|
147
|
+
createdAt: string;
|
|
148
|
+
updatedAt: string;
|
|
149
|
+
metadata?: Readonly<Record<string, unknown>>;
|
|
150
|
+
};
|
|
151
|
+
export type McpPlusSkillStore = {
|
|
152
|
+
list(key: McpPlusProfileStoreKey): Promise<readonly McpPlusSkillNote[]>;
|
|
153
|
+
read(key: McpPlusProfileStoreKey, query: {
|
|
154
|
+
id?: string;
|
|
155
|
+
chapter?: string;
|
|
156
|
+
}): Promise<readonly McpPlusSkillNote[]>;
|
|
157
|
+
write(key: McpPlusProfileStoreKey, note: Omit<McpPlusSkillNote, "id" | "serverId" | "projectId" | "createdAt" | "updatedAt"> & {
|
|
158
|
+
id?: string;
|
|
159
|
+
}): Promise<McpPlusSkillNote>;
|
|
160
|
+
};
|
|
77
161
|
export type McpPlusApplicationServerInput = McpTransportSpec & {
|
|
78
162
|
serverId: string;
|
|
79
163
|
manifest: McpPlusManifest;
|
|
@@ -106,9 +190,36 @@ export declare function buildMcpServerProfilesFromManifest(input: {
|
|
|
106
190
|
};
|
|
107
191
|
}): readonly McpRuntimeServerProfile[];
|
|
108
192
|
export declare function createMcpApplicationStateView(module: McpHarnessModuleSpec | undefined): McpApplicationStateView;
|
|
193
|
+
export declare function createMcpPlusInitToolDeclaration(): NativeToolDeclaration;
|
|
194
|
+
export declare function createMcpPlusReprofileToolDeclaration(): NativeToolDeclaration;
|
|
195
|
+
export declare function createMcpPlusSkillReadToolDeclaration(): NativeToolDeclaration;
|
|
196
|
+
export declare function createMcpPlusSkillWriteToolDeclaration(): NativeToolDeclaration;
|
|
197
|
+
export declare function createMcpPlusFinishToolDeclaration(): NativeToolDeclaration;
|
|
198
|
+
export declare function createInMemoryMcpPlusProfileStore(initial?: readonly McpPlusLearnedProfile[]): McpPlusProfileStore;
|
|
199
|
+
export declare function createInMemoryMcpPlusOverlayStore(initial?: readonly McpPlusRuntimeOverlay[]): McpPlusOverlayStore;
|
|
200
|
+
export declare function createInMemoryMcpPlusSkillStore(initial?: readonly McpPlusSkillNote[]): McpPlusSkillStore;
|
|
201
|
+
export declare function createFileMcpPlusProfileStore(rootDir: string): McpPlusProfileStore;
|
|
202
|
+
export declare function createFileMcpPlusSkillStore(rootDir: string): McpPlusSkillStore;
|
|
203
|
+
export declare function learnedProfileFromProposal(input: {
|
|
204
|
+
proposal: McpPlusProfileProposal;
|
|
205
|
+
nativeTools: readonly NativeToolDeclaration[];
|
|
206
|
+
projectId: string;
|
|
207
|
+
now: string;
|
|
208
|
+
existing?: McpPlusLearnedProfile;
|
|
209
|
+
}): {
|
|
210
|
+
ok: true;
|
|
211
|
+
profile: McpPlusLearnedProfile;
|
|
212
|
+
} | {
|
|
213
|
+
ok: false;
|
|
214
|
+
error: {
|
|
215
|
+
code: string;
|
|
216
|
+
message: string;
|
|
217
|
+
publicSafe: true;
|
|
218
|
+
};
|
|
219
|
+
};
|
|
109
220
|
export declare function planMcpHarnessExposure(manifest: {
|
|
110
221
|
harness: {
|
|
111
222
|
modules: Readonly<Record<string, unknown>>;
|
|
112
223
|
};
|
|
113
|
-
}, nativeToolInventoryByServerId: Readonly<Record<string, readonly NativeToolDeclaration[]>>, stateByServerId?: Readonly<Record<string, Partial<ExposureState
|
|
224
|
+
}, nativeToolInventoryByServerId: Readonly<Record<string, readonly NativeToolDeclaration[]>>, stateByServerId?: Readonly<Record<string, Partial<ExposureState>>>, profileByServerId?: Readonly<Record<string, McpPlusLearnedProfile | undefined>>): McpHarnessExposurePlan;
|
|
114
225
|
export {};
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* 核心目的:把标准 MCP server 声明、MCP+ exposure policy、runtime server profile、
|
|
4
4
|
* dynamic harness tools 和 application view 串成一条 OAO 可编译、可检查、可挂载的合同。
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { compileMcpPlusManifest, createExpandToolDeclaration, lowerExposurePlanToMcpSurface, planExposure, } from "@praxis-ai/mcp-plus";
|
|
7
9
|
export function mcpServer(serverId, input) {
|
|
8
10
|
return {
|
|
9
11
|
...input,
|
|
@@ -102,6 +104,97 @@ export function createMcpApplicationStateView(module) {
|
|
|
102
104
|
function toolIdPart(value) {
|
|
103
105
|
return value.replace(/[^a-zA-Z0-9_-]+/gu, ".").replace(/^\.+|\.+$/gu, "") || "tool";
|
|
104
106
|
}
|
|
107
|
+
function jsonObjectSchema(properties, required = []) {
|
|
108
|
+
return {
|
|
109
|
+
type: "object",
|
|
110
|
+
properties,
|
|
111
|
+
required: [...required],
|
|
112
|
+
additionalProperties: false,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export function createMcpPlusInitToolDeclaration() {
|
|
116
|
+
return {
|
|
117
|
+
name: "mcp_plus.init",
|
|
118
|
+
description: "Submit the first MCP+ profile proposal for this standard MCP server after inspecting its full MCP tools/list surface.",
|
|
119
|
+
inputSchema: profileProposalInputSchema("init"),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function createMcpPlusReprofileToolDeclaration() {
|
|
123
|
+
return {
|
|
124
|
+
name: "mcp_plus.reprofile",
|
|
125
|
+
description: "Submit an updated MCP+ profile proposal for this standard MCP server when runtime usage shows the current profile is stale.",
|
|
126
|
+
inputSchema: profileProposalInputSchema("reprofile"),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
export function createMcpPlusSkillReadToolDeclaration() {
|
|
130
|
+
return {
|
|
131
|
+
name: "mcp_plus.skill_read",
|
|
132
|
+
description: "Read server-bound MCP+ skill notes for the current project when a reusable workflow is relevant.",
|
|
133
|
+
inputSchema: jsonObjectSchema({
|
|
134
|
+
serverId: { type: "string" },
|
|
135
|
+
id: { type: "string" },
|
|
136
|
+
chapter: { type: "string" },
|
|
137
|
+
}, ["serverId"]),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
export function createMcpPlusSkillWriteToolDeclaration() {
|
|
141
|
+
return {
|
|
142
|
+
name: "mcp_plus.skill_write",
|
|
143
|
+
description: "Write a concise server-bound MCP+ skill note for the current project after a reusable workflow, pitfall, or difficult MCP usage is discovered.",
|
|
144
|
+
inputSchema: jsonObjectSchema({
|
|
145
|
+
serverId: { type: "string" },
|
|
146
|
+
chapter: { type: "string" },
|
|
147
|
+
title: { type: "string" },
|
|
148
|
+
summary: { type: "string" },
|
|
149
|
+
whenToUse: { type: "string" },
|
|
150
|
+
do: { type: "array", items: { type: "string" } },
|
|
151
|
+
why: { type: "string" },
|
|
152
|
+
avoid: { type: "array", items: { type: "string" } },
|
|
153
|
+
pitfalls: { type: "array", items: { type: "string" } },
|
|
154
|
+
}, ["serverId", "chapter", "title", "summary"]),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
export function createMcpPlusFinishToolDeclaration() {
|
|
158
|
+
return {
|
|
159
|
+
name: "mcp_plus.finish",
|
|
160
|
+
description: "Finish an MCP+ workflow and optionally submit a reusable skill note. Praxis records the note only when the model provides one.",
|
|
161
|
+
inputSchema: jsonObjectSchema({
|
|
162
|
+
serverId: { type: "string" },
|
|
163
|
+
outcome: { type: "string", enum: ["success", "failure", "partial"] },
|
|
164
|
+
skill: createMcpPlusSkillWriteToolDeclaration().inputSchema,
|
|
165
|
+
}, ["serverId", "outcome"]),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function profileProposalInputSchema(kind) {
|
|
169
|
+
return jsonObjectSchema({
|
|
170
|
+
serverId: { type: "string" },
|
|
171
|
+
pinnedTools: { type: "array", items: { type: "string" } },
|
|
172
|
+
warmTools: { type: "array", items: { type: "string" } },
|
|
173
|
+
indexedTools: { type: "array", items: { type: "string" } },
|
|
174
|
+
alwaysIndexTools: { type: "array", items: { type: "string" } },
|
|
175
|
+
toolCards: {
|
|
176
|
+
type: "object",
|
|
177
|
+
additionalProperties: {
|
|
178
|
+
type: "object",
|
|
179
|
+
properties: {
|
|
180
|
+
title: { type: "string" },
|
|
181
|
+
summary: { type: "string" },
|
|
182
|
+
keywords: { type: "array", items: { type: "string" } },
|
|
183
|
+
},
|
|
184
|
+
additionalProperties: false,
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
skillChapters: {
|
|
188
|
+
type: "array",
|
|
189
|
+
items: jsonObjectSchema({
|
|
190
|
+
id: { type: "string" },
|
|
191
|
+
title: { type: "string" },
|
|
192
|
+
summary: { type: "string" },
|
|
193
|
+
}, ["id", "title", "summary"]),
|
|
194
|
+
},
|
|
195
|
+
rationale: { type: "string", description: `${kind} rationale for the host runtime.` },
|
|
196
|
+
}, ["serverId"]);
|
|
197
|
+
}
|
|
105
198
|
function dynamicToolSpecForNativeTool(serverId, tool) {
|
|
106
199
|
return {
|
|
107
200
|
toolId: `mcp.${toolIdPart(serverId)}.${toolIdPart(tool.name)}`,
|
|
@@ -119,16 +212,278 @@ function dynamicToolSpecForNativeTool(serverId, tool) {
|
|
|
119
212
|
},
|
|
120
213
|
};
|
|
121
214
|
}
|
|
215
|
+
function dynamicToolSpecForMcpPlusControlTool(serverId, tool) {
|
|
216
|
+
return {
|
|
217
|
+
toolId: `mcp.${toolIdPart(serverId)}.${toolIdPart(tool.name)}`,
|
|
218
|
+
family: "mcp",
|
|
219
|
+
group: serverId,
|
|
220
|
+
description: tool.description,
|
|
221
|
+
inputSchema: tool.inputSchema,
|
|
222
|
+
metadata: {
|
|
223
|
+
toolProviderKind: "mcp-plus-control",
|
|
224
|
+
serverId,
|
|
225
|
+
controlName: tool.name,
|
|
226
|
+
source: "runtime.mcpPlane.mcpPlusControlTool",
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function isMcpPlusControlToolName(name) {
|
|
231
|
+
return name.startsWith("mcp_plus.");
|
|
232
|
+
}
|
|
122
233
|
function dynamicToolSpecsForSurface(serverId, surface) {
|
|
123
|
-
return surface.tools
|
|
124
|
-
|
|
125
|
-
|
|
234
|
+
return surface.tools.map((tool) => isMcpPlusControlToolName(tool.name)
|
|
235
|
+
? dynamicToolSpecForMcpPlusControlTool(serverId, tool)
|
|
236
|
+
: dynamicToolSpecForNativeTool(serverId, tool));
|
|
237
|
+
}
|
|
238
|
+
export function createInMemoryMcpPlusProfileStore(initial = []) {
|
|
239
|
+
const profiles = new Map(initial.map((profile) => [`${profile.projectId}:${profile.serverId}`, profile]));
|
|
240
|
+
return {
|
|
241
|
+
async load(key) {
|
|
242
|
+
return profiles.get(`${key.projectId}:${key.serverId}`);
|
|
243
|
+
},
|
|
244
|
+
async save(key, profile) {
|
|
245
|
+
profiles.set(`${key.projectId}:${key.serverId}`, profile);
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
export function createInMemoryMcpPlusOverlayStore(initial = []) {
|
|
250
|
+
const overlays = new Map(initial.map((overlay) => [`${overlay.sessionId}:${overlay.serverId}`, overlay]));
|
|
251
|
+
return {
|
|
252
|
+
async load(key) {
|
|
253
|
+
return overlays.get(`${key.sessionId}:${key.serverId}`);
|
|
254
|
+
},
|
|
255
|
+
async save(key, overlay) {
|
|
256
|
+
overlays.set(`${key.sessionId}:${key.serverId}`, overlay);
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
export function createInMemoryMcpPlusSkillStore(initial = []) {
|
|
261
|
+
const notes = [...initial];
|
|
262
|
+
return {
|
|
263
|
+
async list(key) {
|
|
264
|
+
return notes.filter((note) => note.projectId === key.projectId && note.serverId === key.serverId);
|
|
265
|
+
},
|
|
266
|
+
async read(key, query) {
|
|
267
|
+
return notes.filter((note) => note.projectId === key.projectId &&
|
|
268
|
+
note.serverId === key.serverId &&
|
|
269
|
+
(query.id === undefined || note.id === query.id) &&
|
|
270
|
+
(query.chapter === undefined || note.chapter === query.chapter));
|
|
271
|
+
},
|
|
272
|
+
async write(key, note) {
|
|
273
|
+
const now = new Date().toISOString();
|
|
274
|
+
const noteId = note.id ?? `${key.serverId}:${note.chapter}:${toolIdPart(note.title).toLowerCase()}`;
|
|
275
|
+
const existingIndex = notes.findIndex((item) => item.projectId === key.projectId && item.serverId === key.serverId && item.id === noteId);
|
|
276
|
+
const written = {
|
|
277
|
+
...note,
|
|
278
|
+
id: noteId,
|
|
279
|
+
serverId: key.serverId,
|
|
280
|
+
projectId: key.projectId,
|
|
281
|
+
createdAt: existingIndex === -1 ? now : notes[existingIndex]?.createdAt ?? now,
|
|
282
|
+
updatedAt: now,
|
|
283
|
+
};
|
|
284
|
+
if (existingIndex === -1)
|
|
285
|
+
notes.push(written);
|
|
286
|
+
else
|
|
287
|
+
notes[existingIndex] = written;
|
|
288
|
+
return written;
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
async function readJsonFile(filePath, fallback) {
|
|
293
|
+
try {
|
|
294
|
+
return JSON.parse(await readFile(filePath, "utf8"));
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
return fallback;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
async function writeJsonFile(filePath, value) {
|
|
301
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
302
|
+
await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
126
303
|
}
|
|
127
|
-
|
|
304
|
+
function storeFilePart(value) {
|
|
305
|
+
return toolIdPart(value).replace(/\.+/gu, "-").toLowerCase() || "default";
|
|
306
|
+
}
|
|
307
|
+
export function createFileMcpPlusProfileStore(rootDir) {
|
|
308
|
+
function profilePath(key) {
|
|
309
|
+
return path.join(rootDir, "profiles", storeFilePart(key.projectId), `${storeFilePart(key.serverId)}.json`);
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
async load(key) {
|
|
313
|
+
return await readJsonFile(profilePath(key), undefined);
|
|
314
|
+
},
|
|
315
|
+
async save(key, profile) {
|
|
316
|
+
await writeJsonFile(profilePath(key), profile);
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
export function createFileMcpPlusSkillStore(rootDir) {
|
|
321
|
+
function skillPath(key) {
|
|
322
|
+
return path.join(rootDir, "skills", storeFilePart(key.projectId), `${storeFilePart(key.serverId)}.json`);
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
async list(key) {
|
|
326
|
+
return await readJsonFile(skillPath(key), []);
|
|
327
|
+
},
|
|
328
|
+
async read(key, query) {
|
|
329
|
+
const notes = await readJsonFile(skillPath(key), []);
|
|
330
|
+
return notes.filter((note) => (query.id === undefined || note.id === query.id) &&
|
|
331
|
+
(query.chapter === undefined || note.chapter === query.chapter));
|
|
332
|
+
},
|
|
333
|
+
async write(key, note) {
|
|
334
|
+
const notes = await readJsonFile(skillPath(key), []);
|
|
335
|
+
const now = new Date().toISOString();
|
|
336
|
+
const noteId = note.id ?? `${key.serverId}:${note.chapter}:${toolIdPart(note.title).toLowerCase()}`;
|
|
337
|
+
const existingIndex = notes.findIndex((item) => item.id === noteId);
|
|
338
|
+
const written = {
|
|
339
|
+
...note,
|
|
340
|
+
id: noteId,
|
|
341
|
+
serverId: key.serverId,
|
|
342
|
+
projectId: key.projectId,
|
|
343
|
+
createdAt: existingIndex === -1 ? now : notes[existingIndex]?.createdAt ?? now,
|
|
344
|
+
updatedAt: now,
|
|
345
|
+
};
|
|
346
|
+
if (existingIndex === -1)
|
|
347
|
+
notes.push(written);
|
|
348
|
+
else
|
|
349
|
+
notes[existingIndex] = written;
|
|
350
|
+
await writeJsonFile(skillPath(key), notes);
|
|
351
|
+
return written;
|
|
352
|
+
},
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
function uniqueStrings(values) {
|
|
356
|
+
return [...new Set((values ?? []).filter((value) => typeof value === "string" && value.trim().length > 0).map((value) => value.trim()))];
|
|
357
|
+
}
|
|
358
|
+
function defaultToolCard(tool) {
|
|
359
|
+
return {
|
|
360
|
+
title: tool.name,
|
|
361
|
+
summary: tool.description || tool.name,
|
|
362
|
+
keywords: tool.name.split(/[^a-zA-Z0-9]+/u).filter(Boolean),
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function learnedManifestFromProfile(profile, server) {
|
|
366
|
+
return {
|
|
367
|
+
server: {
|
|
368
|
+
id: server.serverId,
|
|
369
|
+
title: server.title ?? server.serverId,
|
|
370
|
+
summary: server.summary ?? server.manifest?.server.summary ?? "Learned MCP+ profile.",
|
|
371
|
+
},
|
|
372
|
+
exposure: profile.exposure,
|
|
373
|
+
skills: profile.skills,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function fallbackMcpPlusManifest(server, nativeTools) {
|
|
377
|
+
const smallServer = nativeTools.length <= 8;
|
|
378
|
+
return {
|
|
379
|
+
server: {
|
|
380
|
+
id: server.serverId,
|
|
381
|
+
title: server.title ?? server.serverId,
|
|
382
|
+
summary: server.summary ?? "Standard MCP server with Praxis MCP+ native exposure.",
|
|
383
|
+
},
|
|
384
|
+
exposure: smallServer
|
|
385
|
+
? { pinnedTools: nativeTools.map((tool) => tool.name), indexedTools: [] }
|
|
386
|
+
: {
|
|
387
|
+
pinnedTools: nativeTools.slice(0, 3).map((tool) => tool.name),
|
|
388
|
+
indexedTools: nativeTools.slice(3).map((tool) => tool.name),
|
|
389
|
+
toolCards: Object.fromEntries(nativeTools.slice(3).map((tool) => [tool.name, defaultToolCard(tool)])),
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
function bootstrapSurface(server, nativeTools) {
|
|
394
|
+
return {
|
|
395
|
+
tools: [
|
|
396
|
+
...nativeTools,
|
|
397
|
+
createMcpPlusInitToolDeclaration(),
|
|
398
|
+
createMcpPlusSkillReadToolDeclaration(),
|
|
399
|
+
createMcpPlusSkillWriteToolDeclaration(),
|
|
400
|
+
createMcpPlusFinishToolDeclaration(),
|
|
401
|
+
],
|
|
402
|
+
sidecar: {
|
|
403
|
+
serverCard: {
|
|
404
|
+
id: server.serverId,
|
|
405
|
+
title: server.title ?? server.serverId,
|
|
406
|
+
summary: server.summary ?? "Standard MCP server awaiting MCP+ profile initialization.",
|
|
407
|
+
mode: "expanded",
|
|
408
|
+
},
|
|
409
|
+
toolIndex: [],
|
|
410
|
+
skillIndex: [],
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
export function learnedProfileFromProposal(input) {
|
|
415
|
+
if (Object.hasOwn(input.proposal, "modeHint")) {
|
|
416
|
+
return {
|
|
417
|
+
ok: false,
|
|
418
|
+
error: {
|
|
419
|
+
code: "MCP_PLUS_PROFILE_MODE_HINT_UNSUPPORTED",
|
|
420
|
+
message: "MCP+ profile proposals do not accept modeHint in v1; runtime overlays own exposure mode.",
|
|
421
|
+
publicSafe: true,
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
const nativeToolNames = new Set(input.nativeTools.map((tool) => tool.name));
|
|
426
|
+
const referenced = [
|
|
427
|
+
...uniqueStrings(input.proposal.pinnedTools),
|
|
428
|
+
...uniqueStrings(input.proposal.warmTools),
|
|
429
|
+
...uniqueStrings(input.proposal.indexedTools),
|
|
430
|
+
...uniqueStrings(input.proposal.alwaysIndexTools),
|
|
431
|
+
...Object.keys(input.proposal.toolCards ?? {}),
|
|
432
|
+
];
|
|
433
|
+
const unknown = referenced.filter((toolName) => !nativeToolNames.has(toolName));
|
|
434
|
+
if (unknown.length > 0) {
|
|
435
|
+
return {
|
|
436
|
+
ok: false,
|
|
437
|
+
error: {
|
|
438
|
+
code: "MCP_PLUS_PROFILE_UNKNOWN_TOOL",
|
|
439
|
+
message: `MCP+ profile proposal references unknown tool(s): ${unknown.join(", ")}`,
|
|
440
|
+
publicSafe: true,
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
const alwaysIndex = new Set(uniqueStrings(input.proposal.alwaysIndexTools));
|
|
445
|
+
const pinnedAlwaysIndex = uniqueStrings(input.proposal.pinnedTools).filter((toolName) => alwaysIndex.has(toolName));
|
|
446
|
+
if (pinnedAlwaysIndex.length > 0) {
|
|
447
|
+
return {
|
|
448
|
+
ok: false,
|
|
449
|
+
error: {
|
|
450
|
+
code: "MCP_PLUS_PROFILE_ALWAYS_INDEX_PINNED",
|
|
451
|
+
message: `alwaysIndexTools cannot be pinned: ${pinnedAlwaysIndex.join(", ")}`,
|
|
452
|
+
publicSafe: true,
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
const profile = {
|
|
457
|
+
schemaVersion: "mcp-plus.profile.v1",
|
|
458
|
+
serverId: input.proposal.serverId,
|
|
459
|
+
projectId: input.projectId,
|
|
460
|
+
exposure: {
|
|
461
|
+
pinnedTools: uniqueStrings(input.proposal.pinnedTools),
|
|
462
|
+
warmTools: uniqueStrings(input.proposal.warmTools),
|
|
463
|
+
indexedTools: uniqueStrings(input.proposal.indexedTools),
|
|
464
|
+
alwaysIndexTools: uniqueStrings(input.proposal.alwaysIndexTools),
|
|
465
|
+
toolCards: input.proposal.toolCards === undefined ? undefined : Object.fromEntries(Object.entries(input.proposal.toolCards).map(([toolName, card]) => [toolName, {
|
|
466
|
+
title: card.title,
|
|
467
|
+
summary: card.summary,
|
|
468
|
+
keywords: card.keywords === undefined ? undefined : [...card.keywords],
|
|
469
|
+
}])),
|
|
470
|
+
},
|
|
471
|
+
skills: input.proposal.skillChapters === undefined ? input.existing?.skills : {
|
|
472
|
+
...(input.existing?.skills ?? {}),
|
|
473
|
+
chapters: input.proposal.skillChapters.map((chapter) => ({ ...chapter })),
|
|
474
|
+
},
|
|
475
|
+
rationale: input.proposal.rationale,
|
|
476
|
+
createdAt: input.existing?.createdAt ?? input.now,
|
|
477
|
+
updatedAt: input.now,
|
|
478
|
+
metadata: input.proposal.metadata,
|
|
479
|
+
};
|
|
480
|
+
return { ok: true, profile };
|
|
481
|
+
}
|
|
482
|
+
export function planMcpHarnessExposure(manifest, nativeToolInventoryByServerId, stateByServerId = {}, profileByServerId = {}) {
|
|
128
483
|
const module = mcpHarnessModuleFrom(manifest.harness);
|
|
129
484
|
const servers = (module?.servers ?? []).map((server) => {
|
|
130
485
|
const nativeTools = [...(nativeToolInventoryByServerId[server.serverId] ?? [])];
|
|
131
|
-
if (server.mode !== "mcp-plus"
|
|
486
|
+
if (server.mode !== "mcp-plus") {
|
|
132
487
|
const surface = {
|
|
133
488
|
tools: nativeTools,
|
|
134
489
|
sidecar: {
|
|
@@ -149,18 +504,45 @@ export function planMcpHarnessExposure(manifest, nativeToolInventoryByServerId,
|
|
|
149
504
|
dynamicToolSpecs: nativeTools.map((tool) => dynamicToolSpecForNativeTool(server.serverId, tool)),
|
|
150
505
|
};
|
|
151
506
|
}
|
|
152
|
-
const
|
|
507
|
+
const learnedProfile = profileByServerId[server.serverId];
|
|
508
|
+
if (server.manifest === undefined && learnedProfile === undefined) {
|
|
509
|
+
const surface = bootstrapSurface(server, nativeTools);
|
|
510
|
+
return {
|
|
511
|
+
serverId: server.serverId,
|
|
512
|
+
mode: "mcp-plus",
|
|
513
|
+
surface,
|
|
514
|
+
dynamicToolSpecs: dynamicToolSpecsForSurface(server.serverId, surface),
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
const effectiveManifest = server.manifest ?? (learnedProfile === undefined
|
|
518
|
+
? fallbackMcpPlusManifest(server, nativeTools)
|
|
519
|
+
: learnedManifestFromProfile(learnedProfile, server));
|
|
520
|
+
const graph = compileMcpPlusManifest(effectiveManifest, nativeTools);
|
|
153
521
|
const state = {
|
|
154
522
|
serverId: server.serverId,
|
|
155
523
|
mode: stateByServerId[server.serverId]?.mode ?? "expanded",
|
|
156
524
|
activeTools: stateByServerId[server.serverId]?.activeTools ?? [],
|
|
157
525
|
};
|
|
158
|
-
const
|
|
526
|
+
const plan = planExposure(graph, state);
|
|
527
|
+
const surface = lowerExposurePlanToMcpSurface(plan);
|
|
528
|
+
const withNativeControls = {
|
|
529
|
+
tools: [
|
|
530
|
+
...surface.tools.filter((tool) => tool.name !== "mcp_plus.expand"),
|
|
531
|
+
createExpandToolDeclaration(),
|
|
532
|
+
...(stateByServerId[server.serverId]?.mode === "frozen" ? [] : [
|
|
533
|
+
createMcpPlusSkillReadToolDeclaration(),
|
|
534
|
+
createMcpPlusSkillWriteToolDeclaration(),
|
|
535
|
+
createMcpPlusFinishToolDeclaration(),
|
|
536
|
+
]),
|
|
537
|
+
...(stateByServerId[server.serverId]?.activeTools?.includes("mcp_plus.reprofile") ? [createMcpPlusReprofileToolDeclaration()] : []),
|
|
538
|
+
],
|
|
539
|
+
sidecar: surface.sidecar,
|
|
540
|
+
};
|
|
159
541
|
return {
|
|
160
542
|
serverId: server.serverId,
|
|
161
543
|
mode: "mcp-plus",
|
|
162
|
-
surface,
|
|
163
|
-
dynamicToolSpecs: dynamicToolSpecsForSurface(server.serverId,
|
|
544
|
+
surface: withNativeControls,
|
|
545
|
+
dynamicToolSpecs: dynamicToolSpecsForSurface(server.serverId, withNativeControls),
|
|
164
546
|
};
|
|
165
547
|
});
|
|
166
548
|
return { servers };
|