@ian-pascoe/pi-mcp 0.3.2 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ian-pascoe/pi-mcp",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "description": "A complete Model Context Protocol Host for Pi",
6
6
  "keywords": [
@@ -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
- const McpResultDetailsMarkerSchema = Type.Object(
36
+ /** MCP result marker schema shared by persisted-result parsing and tool output metadata. */
37
+ export const McpResultMarkerSchema = Type.Object(
36
38
  {
37
- mcp: Type.Object(
38
- {
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()),
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
- /** Existing persisted MCP tool details consumed without changing their stored shape. */
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. */
@@ -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(compiled: CompiledServerTool, piToolName: string): ToolDefinition {
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 { content, details: { mcp, result: result.details } };
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: ToolDefinition<TParameters, unknown>,
544
+ tool: McpOutputSchemaToolDefinition<TParameters>,
461
545
  ): void {
462
546
  this.ownedToolNames.add(tool.name);
463
547
  this.pi.registerTool(tool);