pi-mcp-adapter 3.3.0 → 4.0.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/CHANGELOG.md +51 -0
- package/OAUTH.md +4 -2
- package/README.md +61 -9
- package/commands.ts +20 -2
- package/config.ts +19 -1
- package/direct-tool-surface.ts +3 -2
- package/direct-tools.ts +5 -3
- package/dist/config.d.ts +6 -0
- package/dist/config.js +15 -0
- package/dist/config.js.map +1 -1
- package/dist/mcp-auth-flow.d.ts +2 -0
- package/dist/mcp-auth-flow.js +12 -3
- package/dist/mcp-auth-flow.js.map +1 -1
- package/dist/metadata-cache.d.ts +12 -0
- package/dist/metadata-cache.js +45 -5
- package/dist/metadata-cache.js.map +1 -1
- package/dist/output-shape.d.ts +18 -5
- package/dist/output-shape.js +162 -18
- package/dist/output-shape.js.map +1 -1
- package/dist/package-mcp-loader.js +9 -5
- package/dist/package-mcp-loader.js.map +1 -1
- package/dist/server-manager.js +27 -16
- package/dist/server-manager.js.map +1 -1
- package/dist/state.d.ts +2 -0
- package/dist/types.d.ts +11 -1
- package/dist/types.js.map +1 -1
- package/index.ts +72 -12
- package/init.ts +20 -5
- package/mcp-auth-flow.ts +13 -2
- package/mcp-code.ts +125 -77
- package/mcp-output-guard.ts +24 -5
- package/mcp-script-worker.mjs +6 -2
- package/mcp-setup-panel.ts +583 -302
- package/metadata-cache.ts +55 -5
- package/output-shape.ts +161 -22
- package/package-mcp-loader.ts +8 -5
- package/package.json +5 -5
- package/proxy-modes.ts +125 -109
- package/server-manager.ts +28 -15
- package/skills/mcp-scripting/SKILL.md +24 -30
- package/skills/mcp-scripting/references/jev.md +3 -0
- package/state.ts +2 -0
- package/types.ts +8 -1
package/mcp-output-guard.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type { ContentBlock, McpSettings } from "./types.ts";
|
|
|
14
14
|
export const DEFAULT_MCP_OUTPUT_MAX_BYTES = DEFAULT_MAX_BYTES;
|
|
15
15
|
export const DEFAULT_MCP_OUTPUT_MAX_LINES = DEFAULT_MAX_LINES;
|
|
16
16
|
export const DEFAULT_MCP_DETAILS_MAX_BYTES = 16 * 1024;
|
|
17
|
+
const SCRIPT_PIPE_HINT_MIN_BYTES = 8 * 1024;
|
|
17
18
|
|
|
18
19
|
const CONTENT_SUMMARY_LIMIT = 20;
|
|
19
20
|
const KEY_PREVIEW_LIMIT = 20;
|
|
@@ -63,6 +64,8 @@ export interface McpOutputGuardOptions {
|
|
|
63
64
|
enabled?: boolean;
|
|
64
65
|
prefix?: string;
|
|
65
66
|
suffix?: string;
|
|
67
|
+
/** Optional guidance appended when it fits the output limits. */
|
|
68
|
+
footer?: string;
|
|
66
69
|
emptyTextFallback?: string;
|
|
67
70
|
maxBytes?: number;
|
|
68
71
|
maxLines?: number;
|
|
@@ -93,6 +96,18 @@ export function resolveMcpOutputGuardOptions(settings?: McpSettings): Pick<McpOu
|
|
|
93
96
|
};
|
|
94
97
|
}
|
|
95
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Models retype large results into the next call's arguments unless told otherwise at the moment
|
|
101
|
+
* they see the result; the same wording in tool descriptions did not change that.
|
|
102
|
+
*/
|
|
103
|
+
export function scriptPipeHint(scriptTool: boolean | undefined, content: ContentBlock[]): { footer?: string } {
|
|
104
|
+
if (scriptTool !== true) return {};
|
|
105
|
+
const bytes = content.reduce((total, block) => total + (block.type === "text" ? byteLength(block.text) : 0), 0);
|
|
106
|
+
return bytes >= SCRIPT_PIPE_HINT_MIN_BYTES
|
|
107
|
+
? { footer: "\n\n[Use mcpScript to pass this result to another MCP call without copying it through the conversation.]" }
|
|
108
|
+
: {};
|
|
109
|
+
}
|
|
110
|
+
|
|
96
111
|
/** Spread helper for tool-result details: includes mcpResult/outputGuard only when present. */
|
|
97
112
|
export function guardedMcpDetails(guarded: GuardedMcpOutput): Record<string, unknown> {
|
|
98
113
|
return {
|
|
@@ -115,6 +130,7 @@ export async function guardMcpOutput(
|
|
|
115
130
|
const detailsMaxBytes = options.detailsMaxBytes ?? DEFAULT_MCP_DETAILS_MAX_BYTES;
|
|
116
131
|
const prefix = options.prefix ?? "";
|
|
117
132
|
const suffix = options.suffix ?? "";
|
|
133
|
+
const footer = options.footer ?? "";
|
|
118
134
|
|
|
119
135
|
const normalizedContent = withEmptyTextFallback(
|
|
120
136
|
content.length > 0
|
|
@@ -125,7 +141,7 @@ export async function guardMcpOutput(
|
|
|
125
141
|
|
|
126
142
|
if (options.enabled === false) {
|
|
127
143
|
return {
|
|
128
|
-
content: addAffixes(normalizedContent, prefix, suffix),
|
|
144
|
+
content: addAffixes(normalizedContent, prefix, `${suffix}${footer}`),
|
|
129
145
|
...(options.rawMcpResult !== undefined ? { mcpResult: options.rawMcpResult } : {}),
|
|
130
146
|
};
|
|
131
147
|
}
|
|
@@ -136,15 +152,18 @@ export async function guardMcpOutput(
|
|
|
136
152
|
.map((block) => (block as { text: string }).text)
|
|
137
153
|
.join("\n");
|
|
138
154
|
const composedOutput = `${prefix}${textOutput}${suffix}`;
|
|
139
|
-
const truncation = truncateHead(composedOutput
|
|
155
|
+
const truncation = truncateHead(`${composedOutput}${footer}`, { maxBytes, maxLines });
|
|
140
156
|
|
|
141
|
-
let guardedContent: ContentBlock[] = addAffixes(normalizedContent, prefix, suffix);
|
|
157
|
+
let guardedContent: ContentBlock[] = addAffixes(normalizedContent, prefix, `${suffix}${footer}`);
|
|
142
158
|
let outputGuard: McpOutputGuardDetails | undefined;
|
|
143
159
|
|
|
144
160
|
if (truncation.truncated) {
|
|
145
161
|
const { path: fullOutputPath, error: writeError } = await saveArtifact("output", composedOutput);
|
|
146
162
|
const initialNotice = formatTruncationNotice(truncation, fullOutputPath, writeError);
|
|
147
|
-
|
|
163
|
+
// Footers are optional guidance: drop one that would not fit beside the notice rather than exceed the limits.
|
|
164
|
+
const noticeWithFooter = textStats(`\n\n${initialNotice}${footer}`);
|
|
165
|
+
const keptFooter = noticeWithFooter.bytes < maxBytes && noticeWithFooter.lines < maxLines ? footer : "";
|
|
166
|
+
const previewBudget = reserveBudget(maxBytes, maxLines, `${initialNotice}${keptFooter}`);
|
|
148
167
|
const preview = truncateHead(composedOutput, {
|
|
149
168
|
maxBytes: previewBudget.maxBytes,
|
|
150
169
|
maxLines: previewBudget.maxLines,
|
|
@@ -154,7 +173,7 @@ export async function guardMcpOutput(
|
|
|
154
173
|
fullOutputPath,
|
|
155
174
|
writeError,
|
|
156
175
|
);
|
|
157
|
-
const finalText = `${preview.content}\n\n${notice}`;
|
|
176
|
+
const finalText = `${preview.content}\n\n${notice}${keptFooter}`;
|
|
158
177
|
const finalStats = textStats(finalText);
|
|
159
178
|
|
|
160
179
|
guardedContent = [{ type: "text" as const, text: finalText }, ...imageBlocks];
|
package/mcp-script-worker.mjs
CHANGED
|
@@ -129,11 +129,15 @@ const PRELUDE_SOURCE = `(function (bridge) {
|
|
|
129
129
|
get(_target, property) {
|
|
130
130
|
if (property === "search") return (input) => request("search", { input });
|
|
131
131
|
if (property === "describe") return (input) => request("describe", { input });
|
|
132
|
-
if (property === "call") return (path, args) => {
|
|
132
|
+
if (property === "call") return (path, args, options) => {
|
|
133
133
|
if (typeof path !== "string" || path.trim() === "") {
|
|
134
134
|
return Promise.resolve({ ok: false, error: { code: "invalid_tool_path", message: "tools.call(path, args) requires a non-empty tool path." } });
|
|
135
135
|
}
|
|
136
|
-
|
|
136
|
+
const server = options?.server;
|
|
137
|
+
if (server !== undefined && (typeof server !== "string" || server.trim() === "")) {
|
|
138
|
+
return Promise.resolve({ ok: false, error: { code: "invalid_tool_server", message: "tools.call(path, args, { server }) requires a non-empty server name." } });
|
|
139
|
+
}
|
|
140
|
+
return request("call", server === undefined ? { path, args } : { path, args, server });
|
|
137
141
|
};
|
|
138
142
|
if (typeof property !== "string" || reserved.has(property)) return undefined;
|
|
139
143
|
return (args) => request("call", { path: property, args });
|