@praxis-ai/praxis 0.1.1 → 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 +45 -6
- package/dist/agentCore/index.js +14 -2
- package/dist/applicationLayer/applicationContract.d.ts +2 -0
- package/dist/applicationLayer/applicationRuntime.d.ts +13 -1
- package/dist/applicationLayer/applicationRuntime.js +39 -3
- package/dist/applicationLayer/index.d.ts +2 -0
- package/dist/applicationLayer/index.js +1 -0
- package/dist/basetool/core/shellRun.js +6 -1
- package/dist/rax_packageManager/raxCli.js +42 -1
- package/dist/runtimeImplementation/praxisRuntimeKernel.d.ts +13 -0
- package/dist/runtimeImplementation/praxisRuntimeKernel.js +550 -15
- package/dist/runtimeImplementation/runtime.componentPlane/runtimeComponentRegistry.d.ts +1 -1
- package/dist/runtimeImplementation/runtime.componentPlane/runtimeComponentRegistry.js +2 -2
- package/dist/runtimeImplementation/runtime.dependencyPlane/dependencySourceRegistry.d.ts +1 -1
- package/dist/runtimeImplementation/runtime.dependencyPlane/dependencySourceRegistry.js +12 -0
- package/dist/runtimeImplementation/runtime.dependencyPlane/dependencyTypes.js +2 -0
- package/dist/runtimeImplementation/runtime.execEngine/baseToolExecutorPortFactory.d.ts +3 -0
- package/dist/runtimeImplementation/runtime.execEngine/baseToolExecutorPortFactory.js +45 -7
- package/dist/runtimeImplementation/runtime.execEngine/mcpRuntimeAdapter.js +56 -0
- package/dist/runtimeImplementation/runtime.mcpPlane/index.d.ts +225 -0
- package/dist/runtimeImplementation/runtime.mcpPlane/index.js +549 -0
- package/dist/runtimeImplementation/runtime.sandboxPlane/baseToolSandboxPlanner.js +0 -2
- package/dist/runtimeImplementation/runtime.sandboxPlane/raxcellSandboxProvider.d.ts +19 -0
- package/dist/runtimeImplementation/runtime.sandboxPlane/raxcellSandboxProvider.js +172 -0
- package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxCommandRunner.d.ts +13 -1
- package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxCommandRunner.js +230 -186
- package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxPolicyMiddleware.d.ts +175 -0
- package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxPolicyMiddleware.js +142 -0
- package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxRuntimeProvider.d.ts +9 -0
- package/dist/runtimeImplementation/runtime.sandboxPlane/sandboxRuntimeProvider.js +115 -205
- package/dist/runtimeImplementation/runtimeAgentManifest.js +7 -3
- package/package.json +3 -1
- package/raxode-tui/dist/raxode-cli/backend/agents/codingAgent/agent.js +3 -3
- package/raxode-tui/dist/raxode-cli/backend/application/backendModuleInventory.js +3 -3
- package/raxode-tui/dist/raxode-cli/backend/application/localReadinessProbe.d.ts +1 -0
- package/raxode-tui/dist/raxode-cli/backend/application/localReadinessProbe.js +50 -4
- package/raxode-tui/dist/raxode-cli/backend/application/raxcellSandboxProvider.d.ts +12 -0
- package/raxode-tui/dist/raxode-cli/backend/application/raxcellSandboxProvider.js +58 -0
- package/raxode-tui/dist/raxode-cli/backend/application/runtimeReadiness.d.ts +1 -0
- package/raxode-tui/dist/raxode-cli/backend/application/runtimeReadiness.js +3 -1
- package/raxode-tui/dist/raxode-cli/backend/application/stdioApplicationServer.d.ts +2 -0
- package/raxode-tui/dist/raxode-cli/backend/application/stdioApplicationServer.js +7 -0
- package/raxode-tui/dist/raxode-cli/backend/directApplicationBackend.d.ts +2 -0
- package/raxode-tui/dist/raxode-cli/backend/directApplicationBackend.js +21 -1
- package/raxode-tui/dist/raxode-cli/backend/raxodeBackend.d.ts +1 -1
- package/raxode-tui/dist/raxode-cli/backend/raxodeBackend.js +8 -0
- package/raxode-tui/dist/raxode-cli/frontend/tui/cli/raxode-cli.js +19 -1
- package/raxode-tui/package.json +2 -1
- package/tsconfig.json +16 -1
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 文件定位:Runtime MCP Plane / 官方 MCP 与 MCP+ 组件接入面。
|
|
3
|
+
* 核心目的:把标准 MCP server 声明、MCP+ exposure policy、runtime server profile、
|
|
4
|
+
* dynamic harness tools 和 application view 串成一条 OAO 可编译、可检查、可挂载的合同。
|
|
5
|
+
*/
|
|
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";
|
|
9
|
+
export function mcpServer(serverId, input) {
|
|
10
|
+
return {
|
|
11
|
+
...input,
|
|
12
|
+
serverId,
|
|
13
|
+
mode: input.mode ?? (input.manifest === undefined ? "native" : "mcp-plus"),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export const mcp = {
|
|
17
|
+
module(input) {
|
|
18
|
+
return {
|
|
19
|
+
kind: "praxis.mcp.module",
|
|
20
|
+
version: "praxis.mcp.v1",
|
|
21
|
+
servers: input.servers,
|
|
22
|
+
recommended: true,
|
|
23
|
+
metadata: input.metadata,
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
stdio(serverId, input) {
|
|
27
|
+
return mcpServer(serverId, { ...input, transport: "stdio" });
|
|
28
|
+
},
|
|
29
|
+
http(serverId, input) {
|
|
30
|
+
return mcpServer(serverId, { ...input, transport: "http" });
|
|
31
|
+
},
|
|
32
|
+
sse(serverId, input) {
|
|
33
|
+
return mcpServer(serverId, { ...input, transport: "sse" });
|
|
34
|
+
},
|
|
35
|
+
recommendedTools() {
|
|
36
|
+
return [
|
|
37
|
+
{
|
|
38
|
+
toolId: "mcp.use",
|
|
39
|
+
metadata: { source: "praxis.mcp.recommendedTools", toolProviderKind: "mcp-static" },
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
toolId: "mcp.resources",
|
|
43
|
+
metadata: { source: "praxis.mcp.recommendedTools", toolProviderKind: "mcp-static" },
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
export function isMcpHarnessModuleSpec(value) {
|
|
49
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
50
|
+
return false;
|
|
51
|
+
const record = value;
|
|
52
|
+
return record.kind === "praxis.mcp.module" && Array.isArray(record.servers);
|
|
53
|
+
}
|
|
54
|
+
export function mcpHarnessModuleFrom(input) {
|
|
55
|
+
const candidate = input.modules?.mcp;
|
|
56
|
+
return isMcpHarnessModuleSpec(candidate) ? candidate : undefined;
|
|
57
|
+
}
|
|
58
|
+
export function runtimeRequirementsForMcpModule(module) {
|
|
59
|
+
return module === undefined || module.servers.length === 0 ? [] : ["runtime.mcp"];
|
|
60
|
+
}
|
|
61
|
+
export function toMcpRuntimeServerProfile(server) {
|
|
62
|
+
if (server.transport === "stdio") {
|
|
63
|
+
return {
|
|
64
|
+
serverId: server.serverId,
|
|
65
|
+
transport: "stdio",
|
|
66
|
+
command: server.command,
|
|
67
|
+
args: server.args,
|
|
68
|
+
cwd: server.cwd,
|
|
69
|
+
env: server.env,
|
|
70
|
+
timeoutMs: server.timeoutMs,
|
|
71
|
+
framing: server.framing,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
serverId: server.serverId,
|
|
76
|
+
transport: server.transport,
|
|
77
|
+
url: server.url,
|
|
78
|
+
sseUrl: server.sseUrl,
|
|
79
|
+
headers: server.headers,
|
|
80
|
+
timeoutMs: server.timeoutMs,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export function buildMcpServerProfilesFromManifest(input) {
|
|
84
|
+
const module = mcpHarnessModuleFrom(input.harness);
|
|
85
|
+
return (module?.servers ?? []).map(toMcpRuntimeServerProfile);
|
|
86
|
+
}
|
|
87
|
+
export function createMcpApplicationStateView(module) {
|
|
88
|
+
return {
|
|
89
|
+
servers: (module?.servers ?? []).map((server) => ({
|
|
90
|
+
serverId: server.serverId,
|
|
91
|
+
mode: server.mode,
|
|
92
|
+
transport: server.transport,
|
|
93
|
+
title: server.title ?? server.manifest?.server.title,
|
|
94
|
+
summary: server.summary ?? server.manifest?.server.summary,
|
|
95
|
+
manifestPresent: server.manifest !== undefined,
|
|
96
|
+
status: "declared",
|
|
97
|
+
publicSafe: true,
|
|
98
|
+
})),
|
|
99
|
+
recommendedMode: "mcp-plus",
|
|
100
|
+
nativeCompatible: true,
|
|
101
|
+
publicSafe: true,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function toolIdPart(value) {
|
|
105
|
+
return value.replace(/[^a-zA-Z0-9_-]+/gu, ".").replace(/^\.+|\.+$/gu, "") || "tool";
|
|
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
|
+
}
|
|
198
|
+
function dynamicToolSpecForNativeTool(serverId, tool) {
|
|
199
|
+
return {
|
|
200
|
+
toolId: `mcp.${toolIdPart(serverId)}.${toolIdPart(tool.name)}`,
|
|
201
|
+
family: "mcp",
|
|
202
|
+
group: serverId,
|
|
203
|
+
description: tool.description,
|
|
204
|
+
inputSchema: tool.inputSchema,
|
|
205
|
+
metadata: {
|
|
206
|
+
toolProviderKind: "mcp-static",
|
|
207
|
+
serverId,
|
|
208
|
+
nativeToolName: tool.name,
|
|
209
|
+
runtimeToolId: "mcp.use",
|
|
210
|
+
runtimeArguments: { serverId, toolName: tool.name },
|
|
211
|
+
source: "runtime.mcpPlane.dynamicTool",
|
|
212
|
+
},
|
|
213
|
+
};
|
|
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
|
+
}
|
|
233
|
+
function dynamicToolSpecsForSurface(serverId, surface) {
|
|
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 });
|
|
303
|
+
}
|
|
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 = {}) {
|
|
483
|
+
const module = mcpHarnessModuleFrom(manifest.harness);
|
|
484
|
+
const servers = (module?.servers ?? []).map((server) => {
|
|
485
|
+
const nativeTools = [...(nativeToolInventoryByServerId[server.serverId] ?? [])];
|
|
486
|
+
if (server.mode !== "mcp-plus") {
|
|
487
|
+
const surface = {
|
|
488
|
+
tools: nativeTools,
|
|
489
|
+
sidecar: {
|
|
490
|
+
serverCard: {
|
|
491
|
+
id: server.serverId,
|
|
492
|
+
title: server.title ?? server.serverId,
|
|
493
|
+
summary: server.summary ?? "Native MCP server.",
|
|
494
|
+
mode: "expanded",
|
|
495
|
+
},
|
|
496
|
+
toolIndex: [],
|
|
497
|
+
skillIndex: [],
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
return {
|
|
501
|
+
serverId: server.serverId,
|
|
502
|
+
mode: "native",
|
|
503
|
+
surface,
|
|
504
|
+
dynamicToolSpecs: nativeTools.map((tool) => dynamicToolSpecForNativeTool(server.serverId, tool)),
|
|
505
|
+
};
|
|
506
|
+
}
|
|
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);
|
|
521
|
+
const state = {
|
|
522
|
+
serverId: server.serverId,
|
|
523
|
+
mode: stateByServerId[server.serverId]?.mode ?? "expanded",
|
|
524
|
+
activeTools: stateByServerId[server.serverId]?.activeTools ?? [],
|
|
525
|
+
};
|
|
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
|
+
};
|
|
541
|
+
return {
|
|
542
|
+
serverId: server.serverId,
|
|
543
|
+
mode: "mcp-plus",
|
|
544
|
+
surface: withNativeControls,
|
|
545
|
+
dynamicToolSpecs: dynamicToolSpecsForSurface(server.serverId, withNativeControls),
|
|
546
|
+
};
|
|
547
|
+
});
|
|
548
|
+
return { servers };
|
|
549
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RaxcellClient } from "@praxis-ai/raxcell";
|
|
2
|
+
import type { RunRequest } from "@praxis-ai/raxcell";
|
|
3
|
+
import type { SandboxExecutionProviderPort, SandboxProviderRunRequest } from "./sandboxPolicyMiddleware.js";
|
|
4
|
+
export type RaxcellClientLike = Pick<RaxcellClient, "prepareRun" | "run">;
|
|
5
|
+
export type RaxcellSandboxProviderOptions = {
|
|
6
|
+
client: RaxcellClientLike;
|
|
7
|
+
providerId?: string;
|
|
8
|
+
} | {
|
|
9
|
+
binaryPath: string;
|
|
10
|
+
providerId?: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const raxcellSandboxProviderDescriptor: {
|
|
13
|
+
readonly surface: "runtime.sandboxPlane.raxcellSandboxProvider";
|
|
14
|
+
readonly providerFamily: "linux-bubblewrap";
|
|
15
|
+
readonly policyOwner: "praxis";
|
|
16
|
+
readonly role: "environment-and-execution";
|
|
17
|
+
};
|
|
18
|
+
export declare function mapSandboxProviderRequestToRaxcell(request: SandboxProviderRunRequest): RunRequest;
|
|
19
|
+
export declare function createRaxcellSandboxProvider(options: RaxcellSandboxProviderOptions): SandboxExecutionProviderPort;
|