chore(vendor): import pi 1.3.0

Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
2026-10-15 17:58:48 -04:00
parent 719370c8f7
commit 6081e3e325
1743 changed files with 205460 additions and 131705 deletions

View File

@@ -0,0 +1,118 @@
export interface ContentAnnotations {
audience?: ("user" | "assistant")[];
priority?: number;
lastModified?: string;
}
export interface TextContent {
type: "text";
text: string;
annotations?: ContentAnnotations;
_meta?: Record<string, unknown>;
}
export interface ImageContent {
type: "image";
data: string;
mimeType: string;
annotations?: ContentAnnotations;
_meta?: Record<string, unknown>;
}
export interface AudioContent {
type: "audio";
data: string;
mimeType: string;
annotations?: ContentAnnotations;
_meta?: Record<string, unknown>;
}
export interface ResourceLinkContent {
type: "resource_link";
uri: string;
name: string;
title?: string;
description?: string;
mimeType?: string;
size?: number;
annotations?: ContentAnnotations;
_meta?: Record<string, unknown>;
}
export interface TextResourceContents {
uri: string;
mimeType?: string;
text: string;
_meta?: Record<string, unknown>;
}
export interface BlobResourceContents {
uri: string;
mimeType?: string;
blob: string;
_meta?: Record<string, unknown>;
}
export interface EmbeddedResourceContent {
type: "resource";
resource: TextResourceContents | BlobResourceContents;
annotations?: ContentAnnotations;
_meta?: Record<string, unknown>;
}
export type ContentBlock = TextContent | ImageContent | AudioContent | ResourceLinkContent | EmbeddedResourceContent;
export interface CallToolResult {
content: ContentBlock[];
/** Any JSON value, including arrays and `null` (MCP 2026-07-28; earlier revisions allowed only objects). */
structuredContent?: unknown;
isError?: boolean;
_meta?: Record<string, unknown>;
}
/**
* Tool result content in the shape LLM APIs accept: text and base64 images. Matches the
* `TextContent` and `ImageContent` types of `@earendil-works/pi-ai`.
*/
export type LlmContent = { type: "text"; text: string } | { type: "image"; data: string; mimeType: string };
function blockToLlmContent(block: ContentBlock): LlmContent {
switch (block.type) {
case "text":
return { type: "text", text: block.text };
case "image":
return { type: "image", data: block.data, mimeType: block.mimeType };
case "audio":
return { type: "text", text: `[audio ${block.mimeType} omitted]` };
case "resource_link":
return { type: "text", text: `${block.name}: ${block.uri}` };
case "resource": {
const resource = block.resource;
if ("text" in resource) return { type: "text", text: resource.text };
if (resource.mimeType?.startsWith("image/")) {
return { type: "image", data: resource.blob, mimeType: resource.mimeType };
}
return {
type: "text",
text: `[binary resource ${resource.uri} (${resource.mimeType ?? "unknown type"}) omitted]`,
};
}
default:
return { type: "text", text: `[unsupported MCP content ${(block as { type: string }).type}]` };
}
}
/**
* Convert a tool result to text and image content for a model. Text and images pass through,
* embedded text resources become text, embedded image resources become images, and other blocks
* (audio, resource links, binary resources) become a short text placeholder. A result without
* content blocks but with `structuredContent` becomes its JSON, since servers should, but do not
* always, mirror structured results as text.
*/
export function toLlmContent(result: Pick<CallToolResult, "content" | "structuredContent">): LlmContent[] {
const content = (result.content ?? []).map(blockToLlmContent);
if (content.length === 0 && result.structuredContent !== undefined) {
content.push({ type: "text", text: JSON.stringify(result.structuredContent, null, 2) });
}
return content;
}

View File

@@ -0,0 +1,113 @@
export type JsonRpcId = string | number;
export interface JsonRpcRequest {
jsonrpc: "2.0";
id: JsonRpcId;
method: string;
params?: unknown;
}
export interface JsonRpcNotification {
jsonrpc: "2.0";
method: string;
params?: unknown;
}
export interface JsonRpcErrorObject {
code: number;
message: string;
data?: unknown;
}
export interface JsonRpcSuccessResponse {
jsonrpc: "2.0";
id: JsonRpcId;
result: unknown;
}
export interface JsonRpcErrorResponse {
jsonrpc: "2.0";
id: JsonRpcId;
error: JsonRpcErrorObject;
}
export type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse;
export type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcResponse;
export const JSON_RPC_ERROR_CODES = {
parseError: -32700,
invalidRequest: -32600,
methodNotFound: -32601,
invalidParams: -32602,
internalError: -32603,
} as const;
export class McpError extends Error {
readonly code: number;
readonly data: unknown;
constructor(code: number, message: string, data?: unknown) {
super(message);
this.name = "McpError";
this.code = code;
this.data = data;
}
}
export class McpConnectionClosedError extends Error {
constructor(message = "MCP connection closed") {
super(message);
this.name = "McpConnectionClosedError";
}
}
export class McpTimeoutError extends Error {
readonly timeoutMs: number;
constructor(timeoutMs: number) {
super(`MCP request timed out after ${timeoutMs}ms`);
this.name = "McpTimeoutError";
this.timeoutMs = timeoutMs;
}
}
export class McpAbortError extends Error {
constructor(message = "MCP request aborted") {
super(message);
this.name = "AbortError";
}
}
export function isObject(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
export function toError(value: unknown): Error {
return value instanceof Error ? value : new Error(String(value));
}
export function isJsonRpcId(value: unknown): value is JsonRpcId {
return typeof value === "string" || (typeof value === "number" && Number.isFinite(value));
}
export function isJsonRpcRequest(message: unknown): message is JsonRpcRequest {
return (
isObject(message) && message.jsonrpc === "2.0" && isJsonRpcId(message.id) && typeof message.method === "string"
);
}
export function isJsonRpcNotification(message: unknown): message is JsonRpcNotification {
return isObject(message) && message.jsonrpc === "2.0" && !("id" in message) && typeof message.method === "string";
}
export function isJsonRpcResponse(message: unknown): message is JsonRpcResponse {
if (!isObject(message) || message.jsonrpc !== "2.0" || !isJsonRpcId(message.id)) return false;
if ("result" in message) return !("error" in message);
if (!("error" in message) || !isObject(message.error)) return false;
return typeof message.error.code === "number" && typeof message.error.message === "string";
}
export function parseJsonRpcMessage(value: unknown): JsonRpcMessage {
if (isJsonRpcRequest(value) || isJsonRpcNotification(value) || isJsonRpcResponse(value)) return value;
throw new McpError(JSON_RPC_ERROR_CODES.invalidRequest, "Invalid JSON-RPC message");
}

View File

@@ -0,0 +1,131 @@
import type { BlobResourceContents, ContentAnnotations, TextResourceContents } from "./content.ts";
import type { JsonRpcId } from "./jsonrpc.ts";
export const LATEST_PROTOCOL_VERSION = "2025-11-25";
/**
* Versions the client accepts from a server. Servers that do not support the requested version answer
* with their own latest one, so older versions stay accepted for servers built on older SDKs.
*/
export const SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05"] as const;
export type SupportedProtocolVersion = (typeof SUPPORTED_PROTOCOL_VERSIONS)[number];
export interface Implementation {
name: string;
version: string;
title?: string;
}
export interface Root {
uri: string;
name?: string;
}
export interface ClientCapabilities {
experimental?: Record<string, unknown>;
roots?: { listChanged?: boolean };
sampling?: Record<string, unknown>;
elicitation?: Record<string, unknown>;
}
export interface ServerCapabilities {
experimental?: Record<string, unknown>;
logging?: Record<string, unknown>;
prompts?: { listChanged?: boolean };
resources?: { subscribe?: boolean; listChanged?: boolean };
tools?: { listChanged?: boolean };
completions?: Record<string, unknown>;
}
export interface InitializeParams {
protocolVersion: string;
capabilities: ClientCapabilities;
clientInfo: Implementation;
}
export interface InitializeResult {
protocolVersion: string;
capabilities: ServerCapabilities;
serverInfo: Implementation;
instructions?: string;
}
export interface ProgressNotification {
progressToken: string | number;
progress: number;
total?: number;
message?: string;
}
export interface CancelledNotification {
requestId: JsonRpcId;
reason?: string;
}
export interface ToolAnnotations {
title?: string;
readOnlyHint?: boolean;
destructiveHint?: boolean;
idempotentHint?: boolean;
openWorldHint?: boolean;
}
export interface ToolExecution {
taskSupport?: "forbidden" | "optional" | "required";
}
export interface Tool {
name: string;
title?: string;
description?: string;
inputSchema: Record<string, unknown>;
outputSchema?: Record<string, unknown>;
annotations?: ToolAnnotations;
execution?: ToolExecution;
_meta?: Record<string, unknown>;
}
export interface ListToolsResult {
tools: Tool[];
nextCursor?: string;
_meta?: Record<string, unknown>;
}
/** A resource a server lists in `resources/list`. */
export interface Resource {
uri: string;
name: string;
title?: string;
description?: string;
mimeType?: string;
size?: number;
annotations?: ContentAnnotations;
_meta?: Record<string, unknown>;
}
/** A family of resources, addressed by an RFC 6570 URI template, from `resources/templates/list`. */
export interface ResourceTemplate {
uriTemplate: string;
name: string;
title?: string;
description?: string;
mimeType?: string;
annotations?: ContentAnnotations;
_meta?: Record<string, unknown>;
}
export interface ListResourcesResult {
resources: Resource[];
nextCursor?: string;
_meta?: Record<string, unknown>;
}
export interface ListResourceTemplatesResult {
resourceTemplates: ResourceTemplate[];
nextCursor?: string;
_meta?: Record<string, unknown>;
}
export interface ReadResourceResult {
contents: (TextResourceContents | BlobResourceContents)[];
_meta?: Record<string, unknown>;
}