@skydiveai/pi-extensions 0.1.0-beta.2304 → 0.1.0-beta.2305
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/index.mjs +88 -12
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -12,6 +12,8 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
12
12
|
import { StdioClientTransport, getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
13
13
|
import { StreamableHTTPClientTransport, StreamableHTTPError } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
14
14
|
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
15
|
+
import { Ajv } from "ajv";
|
|
16
|
+
import addFormats from "ajv-formats";
|
|
15
17
|
import { Check, Errors } from "typebox/value";
|
|
16
18
|
import { hc } from "hono/client";
|
|
17
19
|
import { ROOT_CONTEXT, SpanStatusCode, propagation, trace } from "@opentelemetry/api";
|
|
@@ -259,7 +261,7 @@ function createHealthHandler({ metadata }) {
|
|
|
259
261
|
* read on the hot path before every LLM call), it falls back to the default
|
|
260
262
|
* for that knob and logs once.
|
|
261
263
|
*/
|
|
262
|
-
const log$
|
|
264
|
+
const log$16 = logger.child({ module: "context-management-config" });
|
|
263
265
|
const DEFAULT_CONTEXT_MANAGEMENT_CONFIG = {
|
|
264
266
|
enabled: false,
|
|
265
267
|
perResultMaxBytes: 16 * 1024,
|
|
@@ -307,7 +309,7 @@ function resolveContextManagementConfig(env = process.env) {
|
|
|
307
309
|
maxModelCallsPerTurn: env.SKYDIVE_CTX_MAX_MODEL_CALLS
|
|
308
310
|
});
|
|
309
311
|
if (!parsed.success) {
|
|
310
|
-
log$
|
|
312
|
+
log$16.warn({
|
|
311
313
|
event: "context_management_config_invalid",
|
|
312
314
|
err: parsed.error
|
|
313
315
|
}, "falling back to default context-management config");
|
|
@@ -419,7 +421,7 @@ function installIterationCap({ session, log }, configOverride = null) {
|
|
|
419
421
|
const CAPABILITY_SOUL_NUDGE = "New capability gained — once the current task is done, if this changes what you can do for the user, record it in `soul.md` so it carries into future conversations rather than being rediscovered from scratch (then commit and push).";
|
|
420
422
|
//#endregion
|
|
421
423
|
//#region src/extensions/tool-call-summary.ts
|
|
422
|
-
const log$
|
|
424
|
+
const log$15 = logger.child({ module: "tool-call-summary-extension" });
|
|
423
425
|
/**
|
|
424
426
|
* The injected parameter name: a namespaced sentinel, so it can never collide
|
|
425
427
|
* with a real tool argument and is unmistakable in transcripts and logs. The
|
|
@@ -627,7 +629,7 @@ function withSummaryStrippedBeforeValidation(tool) {
|
|
|
627
629
|
try {
|
|
628
630
|
stripped = withoutInjectedSummary(args);
|
|
629
631
|
} catch (err) {
|
|
630
|
-
log$
|
|
632
|
+
log$15.error({
|
|
631
633
|
err,
|
|
632
634
|
event: "tool_call_summary_prepare_strip_failed",
|
|
633
635
|
toolName: tool.name
|
|
@@ -657,7 +659,7 @@ function stripInjectedSummary(pi, toolName, input) {
|
|
|
657
659
|
if (toolDeclaresSummaryParam(pi, toolName)) return;
|
|
658
660
|
delete input[TOOL_CALL_SUMMARY_FIELD];
|
|
659
661
|
} catch (err) {
|
|
660
|
-
log$
|
|
662
|
+
log$15.error({
|
|
661
663
|
err,
|
|
662
664
|
event: "tool_call_summary_strip_failed",
|
|
663
665
|
toolName
|
|
@@ -673,7 +675,7 @@ function buildInjectedPayload(pi, payload) {
|
|
|
673
675
|
try {
|
|
674
676
|
return injectToolCallSummary(payload, getStrictToolNames(pi));
|
|
675
677
|
} catch (err) {
|
|
676
|
-
log$
|
|
678
|
+
log$15.error({
|
|
677
679
|
err,
|
|
678
680
|
event: "tool_call_summary_injection_failed"
|
|
679
681
|
}, "tool_call_summary injection failed; passing payload through unchanged");
|
|
@@ -710,7 +712,7 @@ const toolCallSummaryExtension = (pi) => {
|
|
|
710
712
|
* or `ToolDefinition[]`. Files starting with `_` or `.` are skipped, so
|
|
711
713
|
* `tools/_example.ts` documents the shape without registering.
|
|
712
714
|
*/
|
|
713
|
-
const log$
|
|
715
|
+
const log$14 = logger.child({ module: "local-tools-extension" });
|
|
714
716
|
const TOOLS_DIRNAME = "tools";
|
|
715
717
|
const fileState = /* @__PURE__ */ new Map();
|
|
716
718
|
let pendingLocalToolsUpdate = null;
|
|
@@ -854,7 +856,7 @@ async function reconcileAndQueue({ pi, dir, reason }) {
|
|
|
854
856
|
dir
|
|
855
857
|
});
|
|
856
858
|
if (reason !== "session_start" && summaryHasChanges$1(summary)) pendingLocalToolsUpdate = summary;
|
|
857
|
-
log$
|
|
859
|
+
log$14.info({
|
|
858
860
|
event: "local_tools_reconcile",
|
|
859
861
|
reason,
|
|
860
862
|
total_tools: summary.totalTools,
|
|
@@ -876,7 +878,7 @@ const localToolsExtension = (pi) => {
|
|
|
876
878
|
reason: "session_start"
|
|
877
879
|
});
|
|
878
880
|
} catch (err) {
|
|
879
|
-
log$
|
|
881
|
+
log$14.error({
|
|
880
882
|
err,
|
|
881
883
|
event: "local_tools_reconcile_failed"
|
|
882
884
|
}, "local tools reconcile failed");
|
|
@@ -888,7 +890,7 @@ const localToolsExtension = (pi) => {
|
|
|
888
890
|
try {
|
|
889
891
|
current = await listToolFiles(dir);
|
|
890
892
|
} catch (err) {
|
|
891
|
-
log$
|
|
893
|
+
log$14.warn({
|
|
892
894
|
err,
|
|
893
895
|
event: "local_tools_listing_failed"
|
|
894
896
|
}, "tools/ listing failed");
|
|
@@ -910,7 +912,7 @@ const localToolsExtension = (pi) => {
|
|
|
910
912
|
reason: "auto_reload"
|
|
911
913
|
});
|
|
912
914
|
} catch (err) {
|
|
913
|
-
log$
|
|
915
|
+
log$14.error({
|
|
914
916
|
err,
|
|
915
917
|
event: "local_tools_auto_reload_failed"
|
|
916
918
|
}, "auto-reload after tools/ change failed");
|
|
@@ -941,6 +943,77 @@ function createStderrBuffer({ maxBytes }) {
|
|
|
941
943
|
};
|
|
942
944
|
}
|
|
943
945
|
//#endregion
|
|
946
|
+
//#region src/extensions/mcp/schema-validator.ts
|
|
947
|
+
/**
|
|
948
|
+
* Lenient JSON-schema validator for MCP tool output schemas.
|
|
949
|
+
*
|
|
950
|
+
* The MCP SDK's `Client.listTools()` eagerly compiles every tool's
|
|
951
|
+
* `outputSchema` (`cacheToolMetadata`), and its default AJV instance
|
|
952
|
+
* compiles `pattern` with the `u` (unicode) flag. Real vendor servers
|
|
953
|
+
* ship legacy Annex-B escapes in their patterns — PayPal's hosted MCP
|
|
954
|
+
* publishes `^https\:\/\/`, whose `\:` is a hard SyntaxError under `/u`
|
|
955
|
+
* ("Invalid regular expression: /^https\:\/\//u: Invalid escape").
|
|
956
|
+
* With the default validator that one schema throws inside `listTools`,
|
|
957
|
+
* the whole server lands in `failed`, and zero tools bind even though
|
|
958
|
+
* the credential is live.
|
|
959
|
+
*
|
|
960
|
+
* Two layers of leniency, both scoped to *output* validation (tool
|
|
961
|
+
* results), never to anything security-relevant:
|
|
962
|
+
*
|
|
963
|
+
* 1. `unicodeRegExp: false` — compile `pattern` without the `u` flag,
|
|
964
|
+
* which restores Annex-B tolerance for escapes like `\:` and matches
|
|
965
|
+
* what most other MCP clients (and JSON Schema's own spec, which
|
|
966
|
+
* does not require unicode mode) accept.
|
|
967
|
+
* 2. A compile failure on one tool's schema must not kill the server:
|
|
968
|
+
* `getValidator` catches the throw and degrades to a pass-through
|
|
969
|
+
* validator for that tool only, so its results are simply not
|
|
970
|
+
* output-validated while every other tool stays fully functional.
|
|
971
|
+
*/
|
|
972
|
+
const log$13 = logger.child({ module: "mcp-schema-validator" });
|
|
973
|
+
function createLenientAjv() {
|
|
974
|
+
const ajv = new Ajv({
|
|
975
|
+
strict: false,
|
|
976
|
+
validateFormats: true,
|
|
977
|
+
validateSchema: false,
|
|
978
|
+
allErrors: true,
|
|
979
|
+
unicodeRegExp: false
|
|
980
|
+
});
|
|
981
|
+
addFormats(ajv);
|
|
982
|
+
return ajv;
|
|
983
|
+
}
|
|
984
|
+
var LenientJsonSchemaValidator = class {
|
|
985
|
+
ajv = createLenientAjv();
|
|
986
|
+
getValidator(schema) {
|
|
987
|
+
let compiled = null;
|
|
988
|
+
try {
|
|
989
|
+
compiled = this.ajv.compile(schema);
|
|
990
|
+
} catch (err) {
|
|
991
|
+
log$13.warn({
|
|
992
|
+
err,
|
|
993
|
+
event: "mcp_output_schema_compile_failed"
|
|
994
|
+
}, "tool outputSchema failed to compile; skipping output validation for this tool");
|
|
995
|
+
return (input) => ({
|
|
996
|
+
valid: true,
|
|
997
|
+
data: input,
|
|
998
|
+
errorMessage: void 0
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
const validate = compiled;
|
|
1002
|
+
return (input) => {
|
|
1003
|
+
if (validate(input)) return {
|
|
1004
|
+
valid: true,
|
|
1005
|
+
data: input,
|
|
1006
|
+
errorMessage: void 0
|
|
1007
|
+
};
|
|
1008
|
+
return {
|
|
1009
|
+
valid: false,
|
|
1010
|
+
data: void 0,
|
|
1011
|
+
errorMessage: this.ajv.errorsText(validate.errors)
|
|
1012
|
+
};
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
1016
|
+
//#endregion
|
|
944
1017
|
//#region src/extensions/mcp/connect-client.ts
|
|
945
1018
|
/**
|
|
946
1019
|
* Spawns an MCP server's transport and races the initialize handshake
|
|
@@ -1091,7 +1164,10 @@ async function connectClient(id, config, opts = {}) {
|
|
|
1091
1164
|
const client = new Client({
|
|
1092
1165
|
name: "skydive-harness",
|
|
1093
1166
|
version: "0.1.0"
|
|
1094
|
-
}, {
|
|
1167
|
+
}, {
|
|
1168
|
+
capabilities: {},
|
|
1169
|
+
jsonSchemaValidator: new LenientJsonSchemaValidator()
|
|
1170
|
+
});
|
|
1095
1171
|
if (config.transport === "http") return connectHttp(id, config, client);
|
|
1096
1172
|
const params = {
|
|
1097
1173
|
command: config.command,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skydiveai/pi-extensions",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.2305",
|
|
4
4
|
"homepage": "https://skydive.com",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Create, Inc.",
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
"@opentelemetry/sdk-trace-node": "^1.30.1",
|
|
37
37
|
"@opentelemetry/semantic-conventions": "^1.28.0",
|
|
38
38
|
"@skydiveai/pi-server": "^0.1.0",
|
|
39
|
+
"ajv": "^8.17.1",
|
|
40
|
+
"ajv-formats": "^3.0.1",
|
|
39
41
|
"hono": "^4.6.14",
|
|
40
42
|
"pino": "^9.6.0",
|
|
41
43
|
"shell-quote": "^1.8.4",
|