@ian-pascoe/pi-mcp 0.3.1 → 0.4.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/dist/pi-mcp-cli.js +5 -2
- package/package.json +1 -1
- package/src/mcp-oauth.ts +5 -2
- package/src/mcp-presentation.ts +16 -15
- package/src/mcp-tool-catalog.ts +87 -3
package/dist/pi-mcp-cli.js
CHANGED
|
@@ -9488,7 +9488,7 @@ var McpAuthStore = class {
|
|
|
9488
9488
|
import { execFile as execFileCallback } from "node:child_process";
|
|
9489
9489
|
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
9490
9490
|
import { createServer } from "node:http";
|
|
9491
|
-
import { auth, resourceUrlFromServerUrl } from "@modelcontextprotocol/client";
|
|
9491
|
+
import { auth, checkResourceAllowed, resourceUrlFromServerUrl } from "@modelcontextprotocol/client";
|
|
9492
9492
|
var McpOAuthPersistenceError = class extends Error {
|
|
9493
9493
|
constructor(operation) {
|
|
9494
9494
|
super(`MCP OAuth persistence failed during ${operation}`);
|
|
@@ -9703,7 +9703,10 @@ var McpOAuthProvider = class {
|
|
|
9703
9703
|
return void 0;
|
|
9704
9704
|
}
|
|
9705
9705
|
const resource = discovery.protectedResourceMetadata?.resource;
|
|
9706
|
-
if (resource !== void 0 && (typeof resource !== "string" || !URL.canParse(resource) ||
|
|
9706
|
+
if (resource !== void 0 && (typeof resource !== "string" || !URL.canParse(resource) || !checkResourceAllowed({
|
|
9707
|
+
configuredResource: resource,
|
|
9708
|
+
requestedResource: resourceUrlFromServerUrl(this.options.serverUrl)
|
|
9709
|
+
}))) {
|
|
9707
9710
|
return void 0;
|
|
9708
9711
|
}
|
|
9709
9712
|
const resourceMetadataUrl = discovery.resourceMetadataUrl;
|
package/package.json
CHANGED
package/src/mcp-oauth.ts
CHANGED
|
@@ -12,7 +12,7 @@ import type {
|
|
|
12
12
|
StoredOAuthClientInformation,
|
|
13
13
|
StoredOAuthTokens,
|
|
14
14
|
} from "@modelcontextprotocol/client";
|
|
15
|
-
import { auth, resourceUrlFromServerUrl } from "@modelcontextprotocol/client";
|
|
15
|
+
import { auth, checkResourceAllowed, resourceUrlFromServerUrl } from "@modelcontextprotocol/client";
|
|
16
16
|
import { type McpAuthBinding, type McpAuthEntry, McpAuthStore } from "./mcp-auth-store.js";
|
|
17
17
|
import type { McpStoreJsonObject, McpStoreJsonValue } from "./mcp-settings-store.js";
|
|
18
18
|
|
|
@@ -384,7 +384,10 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
384
384
|
resource !== undefined &&
|
|
385
385
|
(typeof resource !== "string" ||
|
|
386
386
|
!URL.canParse(resource) ||
|
|
387
|
-
|
|
387
|
+
!checkResourceAllowed({
|
|
388
|
+
configuredResource: resource,
|
|
389
|
+
requestedResource: resourceUrlFromServerUrl(this.options.serverUrl),
|
|
390
|
+
}))
|
|
388
391
|
) {
|
|
389
392
|
return undefined;
|
|
390
393
|
}
|
package/src/mcp-presentation.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* oxlint-disable anti-slop/no-conditional-empty-object-spread, anti-slop/no-known-value-widening, anti-slop/no-runtime-typeof, anti-slop/no-unknown-parameters, anti-slop/no-unknown-returns, anti-slop/no-unsafe-dictionary-type -- MCP Transcript Presentation owns Pi's untyped historical result, tool-argument, and custom-message rendering boundaries. */
|
|
2
|
+
import type { JSONValue } from "@modelcontextprotocol/client";
|
|
2
3
|
import {
|
|
3
4
|
DEFAULT_MAX_BYTES,
|
|
4
5
|
DEFAULT_MAX_LINES,
|
|
@@ -32,24 +33,23 @@ const MCP_PRESENTATION_TEXT_STYLE_RESET = "\u001b[22;39m";
|
|
|
32
33
|
const MCP_PRESENTATION_METADATA_LIMIT = 20;
|
|
33
34
|
const MCP_PRESENTATION_RESERVED_BYTES = 8 * 1024;
|
|
34
35
|
const MCP_PRESENTATION_RESERVED_LINES = 64;
|
|
35
|
-
|
|
36
|
+
/** MCP result marker schema shared by persisted-result parsing and tool output metadata. */
|
|
37
|
+
export const McpResultMarkerSchema = Type.Object(
|
|
36
38
|
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
serverId: Type.Optional(Type.String()),
|
|
45
|
-
toolName: Type.Optional(Type.String()),
|
|
46
|
-
},
|
|
47
|
-
{ additionalProperties: true },
|
|
48
|
-
),
|
|
49
|
-
result: Type.Any(),
|
|
39
|
+
isError: Type.Boolean(),
|
|
40
|
+
operation: Type.Optional(Type.String()),
|
|
41
|
+
outputSchemaError: Type.Optional(Type.String()),
|
|
42
|
+
outputSchemaValid: Type.Optional(Type.Boolean()),
|
|
43
|
+
owner: Type.Literal(MCP_DETAILS_OWNER),
|
|
44
|
+
serverId: Type.Optional(Type.String()),
|
|
45
|
+
toolName: Type.Optional(Type.String()),
|
|
50
46
|
},
|
|
51
47
|
{ additionalProperties: true },
|
|
52
48
|
);
|
|
49
|
+
const McpResultDetailsMarkerSchema = Type.Object(
|
|
50
|
+
{ mcp: McpResultMarkerSchema, result: Type.Any() },
|
|
51
|
+
{ additionalProperties: true },
|
|
52
|
+
);
|
|
53
53
|
|
|
54
54
|
/** Theme operations used by MCP Transcript Presentation and custom-message renderers. */
|
|
55
55
|
export type McpRenderTheme = Pick<Theme, "bg" | "bold" | "fg">;
|
|
@@ -68,10 +68,11 @@ export interface McpResultMarker {
|
|
|
68
68
|
readonly toolName?: string;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
/**
|
|
71
|
+
/** MCP tool details containing host metadata, mapped result details, and optional structured output. */
|
|
72
72
|
export interface McpResultDetails {
|
|
73
73
|
readonly mcp: McpResultMarker;
|
|
74
74
|
readonly result: unknown;
|
|
75
|
+
readonly structuredContent?: JSONValue;
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
/** One role-faithful Prompt message persisted for context replay and TUI presentation. */
|
package/src/mcp-tool-catalog.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
} from "@earendil-works/pi-coding-agent";
|
|
16
16
|
import type { TSchema } from "typebox";
|
|
17
17
|
import {
|
|
18
|
+
McpResultMarkerSchema,
|
|
18
19
|
parseMcpResultDetails,
|
|
19
20
|
renderMcpResourceToolCall,
|
|
20
21
|
renderMcpServerToolCall,
|
|
@@ -45,6 +46,69 @@ const ReadResourceSchema = {
|
|
|
45
46
|
additionalProperties: false,
|
|
46
47
|
} as const;
|
|
47
48
|
|
|
49
|
+
type McpOutputSchemaToolDefinition<TParameters extends TSchema = TSchema> = ToolDefinition<
|
|
50
|
+
TParameters,
|
|
51
|
+
unknown
|
|
52
|
+
> & {
|
|
53
|
+
readonly outputSchema: JsonSchemaType;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function mcpResultDetailsOutputSchema(structuredContentSchema?: JsonSchemaType): JsonSchemaType {
|
|
57
|
+
return {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: {
|
|
60
|
+
mcp: McpResultMarkerSchema,
|
|
61
|
+
result: {},
|
|
62
|
+
...(structuredContentSchema === undefined
|
|
63
|
+
? {}
|
|
64
|
+
: { structuredContent: structuredContentSchema }),
|
|
65
|
+
},
|
|
66
|
+
required: ["mcp", "result"],
|
|
67
|
+
additionalProperties: false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function validatedMcpResultDetailsOutputSchema(
|
|
72
|
+
structuredContentSchema: JsonSchemaType,
|
|
73
|
+
): JsonSchemaType {
|
|
74
|
+
const validationMarker = (valid: boolean): JsonSchemaType => ({
|
|
75
|
+
allOf: [
|
|
76
|
+
McpResultMarkerSchema,
|
|
77
|
+
{
|
|
78
|
+
type: "object",
|
|
79
|
+
properties: { outputSchemaValid: { const: valid } },
|
|
80
|
+
required: ["outputSchemaValid"],
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
anyOf: [
|
|
86
|
+
{
|
|
87
|
+
type: "object",
|
|
88
|
+
properties: {
|
|
89
|
+
mcp: validationMarker(true),
|
|
90
|
+
result: {},
|
|
91
|
+
structuredContent: structuredContentSchema,
|
|
92
|
+
},
|
|
93
|
+
required: ["mcp", "result"],
|
|
94
|
+
additionalProperties: false,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
type: "object",
|
|
98
|
+
properties: {
|
|
99
|
+
mcp: validationMarker(false),
|
|
100
|
+
result: {},
|
|
101
|
+
structuredContent: {},
|
|
102
|
+
},
|
|
103
|
+
required: ["mcp", "result"],
|
|
104
|
+
additionalProperties: false,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const RESOURCE_RESULT_DETAILS_OUTPUT_SCHEMA = mcpResultDetailsOutputSchema();
|
|
111
|
+
|
|
48
112
|
/** Minimal public Pi surface required to register and activate MCP tools. */
|
|
49
113
|
export interface McpToolCatalogPi {
|
|
50
114
|
/** Register or replace one Pi tool by name. */
|
|
@@ -298,9 +362,16 @@ export class McpToolCatalog {
|
|
|
298
362
|
this.syncActiveTools(activeNames);
|
|
299
363
|
}
|
|
300
364
|
|
|
301
|
-
private serverToolDefinition(
|
|
365
|
+
private serverToolDefinition(
|
|
366
|
+
compiled: CompiledServerTool,
|
|
367
|
+
piToolName: string,
|
|
368
|
+
): McpOutputSchemaToolDefinition {
|
|
302
369
|
// SAFETY: MCP's parsed Tool contract requires inputSchema to be JSON Schema; fromJsonSchema compiled this exact object above. Pi accepts the same structural schema without TypeBox metadata.
|
|
303
370
|
const parameters = compiled.definition.inputSchema as TSchema;
|
|
371
|
+
const outputSchema =
|
|
372
|
+
compiled.outputValidator !== undefined && compiled.definition.outputSchema !== undefined
|
|
373
|
+
? validatedMcpResultDetailsOutputSchema(compiled.definition.outputSchema)
|
|
374
|
+
: mcpResultDetailsOutputSchema({});
|
|
304
375
|
return {
|
|
305
376
|
name: piToolName,
|
|
306
377
|
label:
|
|
@@ -310,6 +381,7 @@ export class McpToolCatalog {
|
|
|
310
381
|
description:
|
|
311
382
|
compiled.definition.description ?? `Call MCP Server Tool ${compiled.definition.name}.`,
|
|
312
383
|
parameters,
|
|
384
|
+
outputSchema,
|
|
313
385
|
renderCall: (arguments_, theme, context) =>
|
|
314
386
|
renderMcpServerToolCall(
|
|
315
387
|
compiled.serverId,
|
|
@@ -378,7 +450,16 @@ export class McpToolCatalog {
|
|
|
378
450
|
? {}
|
|
379
451
|
: { serverId: compiled.serverId, toolName: compiled.definition.name }),
|
|
380
452
|
};
|
|
381
|
-
return {
|
|
453
|
+
return {
|
|
454
|
+
content,
|
|
455
|
+
details: {
|
|
456
|
+
mcp,
|
|
457
|
+
result: result.details,
|
|
458
|
+
...(result.structuredContent === undefined
|
|
459
|
+
? {}
|
|
460
|
+
: { structuredContent: result.structuredContent }),
|
|
461
|
+
},
|
|
462
|
+
};
|
|
382
463
|
}
|
|
383
464
|
|
|
384
465
|
private registerResourceTools(): void {
|
|
@@ -387,6 +468,7 @@ export class McpToolCatalog {
|
|
|
387
468
|
label: "List MCP Resources",
|
|
388
469
|
description: "List Resources advertised by connected MCP Servers.",
|
|
389
470
|
parameters: ListResourcesSchema,
|
|
471
|
+
outputSchema: RESOURCE_RESULT_DETAILS_OUTPUT_SCHEMA,
|
|
390
472
|
renderCall: (parameters, theme, context) =>
|
|
391
473
|
renderMcpResourceToolCall(
|
|
392
474
|
"list_resources",
|
|
@@ -411,6 +493,7 @@ export class McpToolCatalog {
|
|
|
411
493
|
label: "List MCP Resource Templates",
|
|
412
494
|
description: "List Resource Templates advertised by connected MCP Servers.",
|
|
413
495
|
parameters: ListResourcesSchema,
|
|
496
|
+
outputSchema: RESOURCE_RESULT_DETAILS_OUTPUT_SCHEMA,
|
|
414
497
|
renderCall: (parameters, theme, context) =>
|
|
415
498
|
renderMcpResourceToolCall(
|
|
416
499
|
"list_resource_templates",
|
|
@@ -435,6 +518,7 @@ export class McpToolCatalog {
|
|
|
435
518
|
label: "Read MCP Resource",
|
|
436
519
|
description: "Read one Resource from a connected MCP Server.",
|
|
437
520
|
parameters: ReadResourceSchema,
|
|
521
|
+
outputSchema: RESOURCE_RESULT_DETAILS_OUTPUT_SCHEMA,
|
|
438
522
|
renderCall: (parameters, theme, context) =>
|
|
439
523
|
renderMcpResourceToolCall(
|
|
440
524
|
"read_resource",
|
|
@@ -457,7 +541,7 @@ export class McpToolCatalog {
|
|
|
457
541
|
}
|
|
458
542
|
|
|
459
543
|
private registerResourceTool<TParameters extends TSchema>(
|
|
460
|
-
tool:
|
|
544
|
+
tool: McpOutputSchemaToolDefinition<TParameters>,
|
|
461
545
|
): void {
|
|
462
546
|
this.ownedToolNames.add(tool.name);
|
|
463
547
|
this.pi.registerTool(tool);
|