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.
@@ -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(input: SaveChainInput, signal?: AbortSignal): Promise<SavedChainView> {
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 applyEnabled(
138
- changes: readonly ChainEnabledChange[],
136
+ async setEnabled(
137
+ name: string,
138
+ scope: ChainScope,
139
+ enabled: boolean,
140
+ traceId: string,
139
141
  signal?: AbortSignal,
140
- ): Promise<ManagerApplyResult> {
142
+ ): Promise<SavedChainView> {
141
143
  const result = await this.lifecycle.request(
142
- "apply_manager_changes",
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 views = parseViewList(result.chains, "apply_manager_changes.chains");
149
- const status = requireRecord(result.status, "apply_manager_changes.status");
150
- this.synchronizeViews(views);
151
- return { chains: views, status };
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(name: string, scope: ChainScope, signal?: AbortSignal): Promise<SavedChainView> {
155
- const result = await this.lifecycle.request("revalidate_chain", { name, scope }, signal);
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(name: string, scope: ChainScope, signal?: AbortSignal): Promise<SavedChainView[]> {
163
- const result = await this.lifecycle.request("delete_chain", { name, scope }, signal);
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(_toolCallId, params, signal, onUpdate) {
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
- const error =
190
- typeof result.error === "string" ? result.error : `Saved chain ${chain.name} failed`;
191
- throw new Error(error);
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 updatedServers: JsonRecord = { ...servers };
12
+ const server = requireJsonObject(servers[name], `MCP server ${JSON.stringify(name)}`);
13
+ const updatedServer: JsonRecord = { ...server };
29
14
 
30
- for (const change of changes) {
31
- const server = requireJsonObject(
32
- servers[change.name],
33
- `MCP server ${JSON.stringify(change.name)}`,
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 }),