pi-codemcp 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/extensions/index.ts +67 -77
- package/package.json +1 -1
- package/sidecar/chains.py +17 -44
- package/sidecar/cli.py +27 -7
- package/sidecar/executor.py +207 -35
- package/sidecar/gateway.py +505 -141
- package/sidecar/mcp_config.py +27 -2
- package/sidecar/refinement_cache.py +154 -0
- package/sidecar/sandbox_api.py +44 -0
- package/sidecar/stats.py +801 -194
- package/sidecar/tool_catalog.py +1 -9
- package/src/chains.ts +63 -34
- package/src/config.ts +8 -39
- package/src/mcp-client.ts +2 -1
- package/src/modal.ts +87 -248
- package/src/prompts.ts +3 -3
- package/src/tools.ts +37 -17
package/sidecar/tool_catalog.py
CHANGED
|
@@ -24,6 +24,7 @@ from .json_types import (
|
|
|
24
24
|
JsonValue,
|
|
25
25
|
)
|
|
26
26
|
from .models import SearchDetail, ToolSchemaView
|
|
27
|
+
from .sandbox_api import STUB_PRELUDE
|
|
27
28
|
|
|
28
29
|
if TYPE_CHECKING:
|
|
29
30
|
from collections.abc import Iterable
|
|
@@ -32,15 +33,6 @@ if TYPE_CHECKING:
|
|
|
32
33
|
|
|
33
34
|
SEARCH_SCORE_CUTOFF = 20.0
|
|
34
35
|
MIN_PLURAL_TOKEN_LENGTH = 4
|
|
35
|
-
STUB_IMPORTS = "from typing import Literal, Never, NotRequired, TypeAlias, TypedDict"
|
|
36
|
-
JSON_TYPE_STUBS = (
|
|
37
|
-
"JsonScalar: TypeAlias = bool | int | float | str | None",
|
|
38
|
-
'JsonValue: TypeAlias = JsonScalar | list["JsonValue"] | dict[str, "JsonValue"]',
|
|
39
|
-
)
|
|
40
|
-
INSPECT_JSON_STUB = (
|
|
41
|
-
"def inspect_json(value: JsonValue, *, samples: int = 2, max_depth: int = 3) -> JsonValue: ..."
|
|
42
|
-
)
|
|
43
|
-
STUB_PRELUDE = "\n\n".join([STUB_IMPORTS, *JSON_TYPE_STUBS, INSPECT_JSON_STUB])
|
|
44
36
|
|
|
45
37
|
|
|
46
38
|
class ToolSpec(BaseModel):
|
package/src/chains.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
3
|
import { join } from "node:path";
|
|
3
4
|
import { type ExtensionAPI, highlightCode, type Theme } from "@earendil-works/pi-coding-agent";
|
|
@@ -46,17 +47,6 @@ export interface SavedChainView {
|
|
|
46
47
|
calledBy: string[];
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
export interface ChainEnabledChange {
|
|
50
|
-
name: string;
|
|
51
|
-
scope: ChainScope;
|
|
52
|
-
enabled: boolean;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface ManagerApplyResult {
|
|
56
|
-
chains: SavedChainView[];
|
|
57
|
-
status: Record<string, unknown>;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
50
|
export interface SaveChainInput {
|
|
61
51
|
scope: ChainScope;
|
|
62
52
|
name: string;
|
|
@@ -78,6 +68,10 @@ interface LoadedChains {
|
|
|
78
68
|
|
|
79
69
|
const CHAIN_NAME = /^[a-z][a-z0-9_]{0,63}$/;
|
|
80
70
|
|
|
71
|
+
export function newCodeMcpTraceId(source: string): string {
|
|
72
|
+
return `${source}:${randomUUID()}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
81
75
|
export class SavedChainManager {
|
|
82
76
|
readonly startupErrors: string[] = [];
|
|
83
77
|
private readonly manifests = new Map<string, ScopedSavedChain>();
|
|
@@ -105,7 +99,11 @@ export class SavedChainManager {
|
|
|
105
99
|
}
|
|
106
100
|
}
|
|
107
101
|
|
|
108
|
-
async save(
|
|
102
|
+
async save(
|
|
103
|
+
input: SaveChainInput,
|
|
104
|
+
traceId: string,
|
|
105
|
+
signal?: AbortSignal,
|
|
106
|
+
): Promise<SavedChainView> {
|
|
109
107
|
assertChainName(input.name);
|
|
110
108
|
this.assertToolNameAvailable(input.name);
|
|
111
109
|
const result = await this.lifecycle.request(
|
|
@@ -117,6 +115,7 @@ export class SavedChainManager {
|
|
|
117
115
|
code: input.code,
|
|
118
116
|
input_schema: input.inputSchema,
|
|
119
117
|
output_schema: input.outputSchema,
|
|
118
|
+
trace_id: traceId,
|
|
120
119
|
},
|
|
121
120
|
signal,
|
|
122
121
|
);
|
|
@@ -127,40 +126,59 @@ export class SavedChainManager {
|
|
|
127
126
|
return view;
|
|
128
127
|
}
|
|
129
128
|
|
|
130
|
-
async list(signal?: AbortSignal): Promise<SavedChainView[]> {
|
|
131
|
-
const result = await this.lifecycle.request("list_chains", {}, signal);
|
|
129
|
+
async list(traceId: string, signal?: AbortSignal): Promise<SavedChainView[]> {
|
|
130
|
+
const result = await this.lifecycle.request("list_chains", { trace_id: traceId }, signal);
|
|
132
131
|
const views = parseViewList(result.chains, "list_chains.chains");
|
|
133
132
|
this.synchronizeViews(views);
|
|
134
133
|
return views;
|
|
135
134
|
}
|
|
136
135
|
|
|
137
|
-
async
|
|
138
|
-
|
|
136
|
+
async setEnabled(
|
|
137
|
+
name: string,
|
|
138
|
+
scope: ChainScope,
|
|
139
|
+
enabled: boolean,
|
|
140
|
+
traceId: string,
|
|
139
141
|
signal?: AbortSignal,
|
|
140
|
-
): Promise<
|
|
142
|
+
): Promise<SavedChainView> {
|
|
141
143
|
const result = await this.lifecycle.request(
|
|
142
|
-
"
|
|
143
|
-
{
|
|
144
|
-
changes: changes.map((change) => ({ ...change })),
|
|
145
|
-
},
|
|
144
|
+
"set_chain_enabled",
|
|
145
|
+
{ name, scope, enabled, trace_id: traceId },
|
|
146
146
|
signal,
|
|
147
147
|
);
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
this.
|
|
151
|
-
return
|
|
148
|
+
const view = parseSavedChainView(result, "set_chain_enabled");
|
|
149
|
+
this.upsertView(view);
|
|
150
|
+
this.refreshNativeTools();
|
|
151
|
+
return view;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
async revalidate(
|
|
155
|
-
|
|
154
|
+
async revalidate(
|
|
155
|
+
name: string,
|
|
156
|
+
scope: ChainScope,
|
|
157
|
+
traceId: string,
|
|
158
|
+
signal?: AbortSignal,
|
|
159
|
+
): Promise<SavedChainView> {
|
|
160
|
+
const result = await this.lifecycle.request(
|
|
161
|
+
"revalidate_chain",
|
|
162
|
+
{ name, scope, trace_id: traceId },
|
|
163
|
+
signal,
|
|
164
|
+
);
|
|
156
165
|
const view = parseSavedChainView(result, "revalidate_chain");
|
|
157
166
|
this.upsertView(view);
|
|
158
167
|
this.refreshNativeTools();
|
|
159
168
|
return view;
|
|
160
169
|
}
|
|
161
170
|
|
|
162
|
-
async delete(
|
|
163
|
-
|
|
171
|
+
async delete(
|
|
172
|
+
name: string,
|
|
173
|
+
scope: ChainScope,
|
|
174
|
+
traceId: string,
|
|
175
|
+
signal?: AbortSignal,
|
|
176
|
+
): Promise<SavedChainView[]> {
|
|
177
|
+
const result = await this.lifecycle.request(
|
|
178
|
+
"delete_chain",
|
|
179
|
+
{ name, scope, trace_id: traceId },
|
|
180
|
+
signal,
|
|
181
|
+
);
|
|
164
182
|
const views = parseViewList(result.chains, "delete_chain.chains");
|
|
165
183
|
this.synchronizeViews(views);
|
|
166
184
|
return views;
|
|
@@ -174,7 +192,7 @@ export class SavedChainManager {
|
|
|
174
192
|
label: chain.name,
|
|
175
193
|
description: chain.description,
|
|
176
194
|
parameters: chain.inputSchema,
|
|
177
|
-
async execute(
|
|
195
|
+
async execute(toolCallId, params, signal, onUpdate) {
|
|
178
196
|
onUpdate?.({
|
|
179
197
|
content: [{ type: "text", text: `Running saved MCP chain ${chain.name}...` }],
|
|
180
198
|
details: undefined,
|
|
@@ -182,13 +200,24 @@ export class SavedChainManager {
|
|
|
182
200
|
const arguments_ = requireRecord(params, `${chain.name} arguments`);
|
|
183
201
|
const result = await manager.lifecycle.request(
|
|
184
202
|
"execute_chain",
|
|
185
|
-
{ name: chain.name, arguments: arguments_ },
|
|
203
|
+
{ name: chain.name, arguments: arguments_, trace_id: toolCallId },
|
|
186
204
|
signal,
|
|
187
205
|
);
|
|
188
206
|
if (result.ok !== true) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
207
|
+
throw new Error(
|
|
208
|
+
JSON.stringify({
|
|
209
|
+
failure_stage: result.failure_stage,
|
|
210
|
+
error:
|
|
211
|
+
typeof result.error === "string"
|
|
212
|
+
? result.error
|
|
213
|
+
: `Saved chain ${chain.name} failed`,
|
|
214
|
+
failure: result.failure,
|
|
215
|
+
result_ref: result.result_ref,
|
|
216
|
+
expires_in_seconds: result.expires_in_seconds,
|
|
217
|
+
calls_made: result.calls_made,
|
|
218
|
+
chain_calls: result.chain_calls,
|
|
219
|
+
}),
|
|
220
|
+
);
|
|
192
221
|
}
|
|
193
222
|
const settings = manager.lifecycle.loadSettings();
|
|
194
223
|
const output = formatCodeMcpOutput(result.result, {
|
package/src/config.ts
CHANGED
|
@@ -5,54 +5,23 @@ import {
|
|
|
5
5
|
writeJsonObjectAtomically,
|
|
6
6
|
} from "./json-file.js";
|
|
7
7
|
|
|
8
|
-
export interface McpServerEnabledChange {
|
|
9
|
-
name: string;
|
|
10
|
-
enabled: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
8
|
export function setMcpServerEnabled(configPath: string, name: string, enabled: boolean): void {
|
|
14
|
-
setMcpServersEnabled(configPath, [{ name, enabled }]);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function setMcpServersEnabled(
|
|
18
|
-
configPath: string,
|
|
19
|
-
changes: readonly McpServerEnabledChange[],
|
|
20
|
-
): void {
|
|
21
|
-
if (changes.length === 0) return;
|
|
22
|
-
const duplicate = duplicateName(changes.map((change) => change.name));
|
|
23
|
-
if (duplicate) throw new Error(`Duplicate MCP server change: ${JSON.stringify(duplicate)}`);
|
|
24
|
-
|
|
25
9
|
const root = readJsonObject(configPath, "mcp.json root");
|
|
26
10
|
const hasServerBlock = Object.hasOwn(root, "mcpServers");
|
|
27
11
|
const servers = hasServerBlock ? requireJsonObject(root.mcpServers, "mcp.json mcpServers") : root;
|
|
28
|
-
const
|
|
12
|
+
const server = requireJsonObject(servers[name], `MCP server ${JSON.stringify(name)}`);
|
|
13
|
+
const updatedServer: JsonRecord = { ...server };
|
|
29
14
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const updatedServer: JsonRecord = { ...server };
|
|
36
|
-
if (typeof server.enabled === "boolean") {
|
|
37
|
-
updatedServer.enabled = change.enabled;
|
|
38
|
-
delete updatedServer.disabled;
|
|
39
|
-
} else {
|
|
40
|
-
updatedServer.disabled = !change.enabled;
|
|
41
|
-
}
|
|
42
|
-
updatedServers[change.name] = updatedServer;
|
|
15
|
+
if (typeof server.enabled === "boolean") {
|
|
16
|
+
updatedServer.enabled = enabled;
|
|
17
|
+
delete updatedServer.disabled;
|
|
18
|
+
} else {
|
|
19
|
+
updatedServer.disabled = !enabled;
|
|
43
20
|
}
|
|
44
21
|
|
|
22
|
+
const updatedServers: JsonRecord = { ...servers, [name]: updatedServer };
|
|
45
23
|
const updatedRoot: JsonRecord = hasServerBlock
|
|
46
24
|
? { ...root, mcpServers: updatedServers }
|
|
47
25
|
: updatedServers;
|
|
48
26
|
writeJsonObjectAtomically(configPath, updatedRoot);
|
|
49
27
|
}
|
|
50
|
-
|
|
51
|
-
function duplicateName(names: readonly string[]): string | undefined {
|
|
52
|
-
const seen = new Set<string>();
|
|
53
|
-
for (const name of names) {
|
|
54
|
-
if (seen.has(name)) return name;
|
|
55
|
-
seen.add(name);
|
|
56
|
-
}
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
package/src/mcp-client.ts
CHANGED
|
@@ -22,11 +22,11 @@ export type SidecarToolName =
|
|
|
22
22
|
| "inspect"
|
|
23
23
|
| "discover"
|
|
24
24
|
| "reload_settings"
|
|
25
|
-
| "apply_manager_changes"
|
|
26
25
|
| "execute"
|
|
27
26
|
| "save_chain"
|
|
28
27
|
| "list_chains"
|
|
29
28
|
| "execute_chain"
|
|
29
|
+
| "set_chain_enabled"
|
|
30
30
|
| "revalidate_chain"
|
|
31
31
|
| "delete_chain"
|
|
32
32
|
| "stats"
|
|
@@ -99,6 +99,7 @@ export class SidecarClient {
|
|
|
99
99
|
...definedProcessEnvironment(),
|
|
100
100
|
...(options.environment ?? {}),
|
|
101
101
|
PI_CODEMCP_AGENT_DIR: this.agentDir,
|
|
102
|
+
PI_CODEMCP_PACKAGE_VERSION: this.packageVersion,
|
|
102
103
|
...(this.projectChainsDirectory === undefined
|
|
103
104
|
? {}
|
|
104
105
|
: { PI_CODEMCP_PROJECT_CHAINS_DIR: this.projectChainsDirectory }),
|