agentmail-toolkit 0.5.1 → 0.6.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/mcp.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["import { z } from 'zod'\nimport { type ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { type ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'\n\nimport { ListToolkit } from './toolkit.js'\nimport { type Tool } from './tools.js'\nimport { safeFunc, normalize, truncateForLog } from './util.js'\n\ninterface McpTool {\n name: string\n title: string\n description: string\n inputSchema: z.ZodRawShape\n outputSchema: z.ZodRawShape\n callback: ToolCallback<z.ZodRawShape>\n annotations?: ToolAnnotations\n}\n\nexport class AgentMailToolkit extends ListToolkit<McpTool> {\n protected buildTool(tool: Tool): McpTool {\n return {\n name: tool.name,\n title: tool.title,\n description: tool.description,\n inputSchema: tool.paramsSchema.shape,\n outputSchema: tool.outputSchema.shape,\n callback: async (args) => {\n const { isError, result, statusCode, body } = await safeFunc(tool.func, this.client, args)\n if (isError) {\n // Errors are returned as isError tool results (HTTP 200), so they never\n // reach the host's error logs on their own. Log here, at the real catch\n // site, so failures are actually observable. Error results are never\n // validated against outputSchema - the MCP SDK itself skips output\n // validation when isError is true, so we don't either.\n console.error('[agentmail-toolkit] tool error', {\n tool: tool.name,\n statusCode,\n body: truncateForLog(body),\n })\n return {\n content: [{ type: 'text' as const, text: String(result) }],\n isError: true,\n }\n }\n\n // JSON-safe the result (Date -> ISO string) and validate it against the\n // tool's own output schema before returning structuredContent. Schema\n // drift (a func/schema mismatch) must fail visibly rather than silently\n // handing the client malformed structured content.\n //\n // Parse in strip mode (z.object of the same shape), not with the loose\n // schema directly: the MCP SDK reconstructs a plain z.object from the raw\n // shape we register and advertises it to clients with a strict\n // (additionalProperties: false) root. Stripping unknown top-level keys\n // here keeps structuredContent conformant with that ADVERTISED schema, so\n // a future SDK field is dropped instead of failing validation in strict\n // clients. Nested objects keep their looseness (they serialize from the\n // real looseObject instances in the shape).\n const parsed = z.object(tool.outputSchema.shape).safeParse(normalize(result))\n if (!parsed.success) {\n console.error('[agentmail-toolkit] output schema mismatch', {\n tool: tool.name,\n issues: parsed.error.issues,\n })\n return {\n content: [{ type: 'text' as const, text: `Internal error: ${tool.name} result did not match its declared output schema` }],\n isError: true,\n }\n }\n\n // Backwards compatibility: also serialize the same structuredContent as text.\n const structuredContent = parsed.data as Record<string, unknown>\n const text = JSON.stringify(structuredContent)\n return {\n structuredContent,\n content: [{ type: 'text' as const, text }],\n isError: false,\n }\n },\n annotations: tool.annotations,\n }\n }\n}\n"],"mappings":";;;;;;;;AAAA,SAAS,SAAS;AAkBX,IAAM,mBAAN,cAA+B,YAAqB;AAAA,EAC7C,UAAU,MAAqB;AACrC,WAAO;AAAA,MACH,MAAM,KAAK;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,aAAa,KAAK;AAAA,MAClB,aAAa,KAAK,aAAa;AAAA,MAC/B,cAAc,KAAK,aAAa;AAAA,MAChC,UAAU,OAAO,SAAS;AACtB,cAAM,EAAE,SAAS,QAAQ,YAAY,KAAK,IAAI,MAAM,SAAS,KAAK,MAAM,KAAK,QAAQ,IAAI;AACzF,YAAI,SAAS;AAMT,kBAAQ,MAAM,kCAAkC;AAAA,YAC5C,MAAM,KAAK;AAAA,YACX;AAAA,YACA,MAAM,eAAe,IAAI;AAAA,UAC7B,CAAC;AACD,iBAAO;AAAA,YACH,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,OAAO,MAAM,EAAE,CAAC;AAAA,YACzD,SAAS;AAAA,UACb;AAAA,QACJ;AAeA,cAAM,SAAS,EAAE,OAAO,KAAK,aAAa,KAAK,EAAE,UAAU,UAAU,MAAM,CAAC;AAC5E,YAAI,CAAC,OAAO,SAAS;AACjB,kBAAQ,MAAM,8CAA8C;AAAA,YACxD,MAAM,KAAK;AAAA,YACX,QAAQ,OAAO,MAAM;AAAA,UACzB,CAAC;AACD,iBAAO;AAAA,YACH,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,mBAAmB,KAAK,IAAI,mDAAmD,CAAC;AAAA,YACzH,SAAS;AAAA,UACb;AAAA,QACJ;AAGA,cAAM,oBAAoB,OAAO;AACjC,cAAM,OAAO,KAAK,UAAU,iBAAiB;AAC7C,eAAO;AAAA,UACH;AAAA,UACA,SAAS,CAAC,EAAE,MAAM,QAAiB,KAAK,CAAC;AAAA,UACzC,SAAS;AAAA,QACb;AAAA,MACJ;AAAA,MACA,aAAa,KAAK;AAAA,IACtB;AAAA,EACJ;AACJ;","names":[]}
1
+ {"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["import { z } from 'zod'\nimport { type ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { type ToolAnnotations, type CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport { type AgentMailClient } from 'agentmail'\n\nimport { ListToolkit } from './toolkit.js'\nimport { type Tool, tools } from './tools.js'\nimport { safeFunc, normalize, truncateForLog } from './util.js'\n\ninterface McpTool {\n name: string\n title: string\n description: string\n inputSchema: z.ZodRawShape\n outputSchema: z.ZodRawShape\n callback: ToolCallback<z.ZodRawShape>\n annotations?: ToolAnnotations\n}\n\n// Name -> Tool, built once at module load. Lets `invoke` dispatch by name without\n// reconstructing the whole tool set per call.\nconst toolsByName: ReadonlyMap<string, Tool> = new Map(tools.map((tool) => [tool.name, tool]))\n\n// Execute one tool's func with an explicit client and shape the outcome as an MCP\n// CallToolResult. Shared by the per-instance `callback` (client bound at\n// construction) and `invoke` (client supplied per call) so both paths behave\n// identically - the client is the only thing that differs between them.\nasync function runTool(\n tool: Tool,\n client: AgentMailClient,\n args: Record<string, unknown>\n): Promise<CallToolResult> {\n // The MCP SDK validates tools/call args against a plain z.object it rebuilds\n // from the raw shape we register, which drops root-level refinements (e.g.\n // ReplyToMessageParams' replyAll/to exclusivity). Re-parse with the tool's own\n // schema so those cross-field rules are enforced before the func runs.\n //\n // normalize() first: the SDK's parse has already run the shape's coerce pipes,\n // so date filters (before/after/sendAt: z.string().pipe(z.coerce.date())) arrive\n // as Date objects, which z.string() would reject on a second parse. normalize\n // converts Date back to ISO string, making the re-parse a true round-trip. On\n // the invoke() path args are raw JSON and normalize is a no-op.\n const preflight = tool.paramsSchema.safeParse(normalize(args))\n if (!preflight.success) {\n const message = preflight.error.issues.map((issue) => issue.message).join('; ')\n return {\n content: [{ type: 'text' as const, text: `Invalid arguments: ${message}` }],\n isError: true,\n }\n }\n\n const { isError, result, statusCode, body } = await safeFunc(tool.func, client, preflight.data as Record<string, unknown>)\n if (isError) {\n // Errors are returned as isError tool results (HTTP 200), so they never\n // reach the host's error logs on their own. Log here, at the real catch\n // site, so failures are actually observable. Error results are never\n // validated against outputSchema - the MCP SDK itself skips output\n // validation when isError is true, so we don't either.\n console.error('[agentmail-toolkit] tool error', {\n tool: tool.name,\n statusCode,\n body: truncateForLog(body),\n })\n return {\n content: [{ type: 'text' as const, text: String(result) }],\n isError: true,\n }\n }\n\n // JSON-safe the result (Date -> ISO string) and validate it against the\n // tool's own output schema before returning structuredContent. Schema\n // drift (a func/schema mismatch) must fail visibly rather than silently\n // handing the client malformed structured content.\n //\n // Parse in strip mode: the MCP SDK reconstructs a plain z.object from the\n // raw shape we register and advertises it to clients with a strict\n // (additionalProperties: false) root, so stripping keeps structuredContent\n // conformant with that ADVERTISED schema. The output schemas are plain\n // z.object at every nesting level (see output-schemas.ts), so this parse\n // also drops undeclared NESTED fields the SDK passes through unrecognized\n // API keys (organization_id, pod_id, debug data), and they must never reach\n // the model (OpenAI app review data-minimization requirement).\n const parsed = z.object(tool.outputSchema.shape).safeParse(normalize(result))\n if (!parsed.success) {\n console.error('[agentmail-toolkit] output schema mismatch', {\n tool: tool.name,\n issues: parsed.error.issues,\n })\n return {\n content: [{ type: 'text' as const, text: `Internal error: ${tool.name} result did not match its declared output schema` }],\n isError: true,\n }\n }\n\n // Backwards compatibility: also serialize the same structuredContent as text.\n const structuredContent = parsed.data as Record<string, unknown>\n const text = JSON.stringify(structuredContent)\n return {\n structuredContent,\n content: [{ type: 'text' as const, text }],\n isError: false,\n }\n}\n\nexport class AgentMailToolkit extends ListToolkit<McpTool> {\n protected buildTool(tool: Tool): McpTool {\n return {\n name: tool.name,\n title: tool.title,\n description: tool.description,\n inputSchema: tool.paramsSchema.shape,\n outputSchema: tool.outputSchema.shape,\n callback: async (args) => runTool(tool, this.client, args as Record<string, unknown>),\n annotations: tool.annotations,\n }\n }\n\n /**\n * Invoke a tool by name with a per-call client, bypassing the client bound\n * at construction, and return the same MCP CallToolResult a tools/call\n * would.\n *\n * This lets a host that serves many users (e.g. a hosted MCP server) build\n * the toolkit ONCE and hand each request its own client, instead of\n * constructing a fresh toolkit per request and again per tool call - which,\n * under long-lived/streaming connections, retains a per-call object graph\n * and drives heap growth.\n *\n * An unknown tool name returns an isError result rather than throwing, so a\n * bad name can never crash the caller. `args` is assumed already validated\n * against the tool's paramsSchema (the MCP SDK validates tools/call\n * arguments before dispatch).\n */\n async invoke(\n name: string,\n client: AgentMailClient,\n args: Record<string, unknown>\n ): Promise<CallToolResult> {\n const tool = toolsByName.get(name)\n if (!tool) {\n return {\n content: [{ type: 'text' as const, text: `Unknown tool: ${name}` }],\n isError: true,\n }\n }\n return runTool(tool, client, args)\n }\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,SAAS;AAqBlB,IAAM,cAAyC,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC;AAM7F,eAAe,QACX,MACA,QACA,MACuB;AAWvB,QAAM,YAAY,KAAK,aAAa,UAAU,UAAU,IAAI,CAAC;AAC7D,MAAI,CAAC,UAAU,SAAS;AACpB,UAAM,UAAU,UAAU,MAAM,OAAO,IAAI,CAAC,UAAU,MAAM,OAAO,EAAE,KAAK,IAAI;AAC9E,WAAO;AAAA,MACH,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,sBAAsB,OAAO,GAAG,CAAC;AAAA,MAC1E,SAAS;AAAA,IACb;AAAA,EACJ;AAEA,QAAM,EAAE,SAAS,QAAQ,YAAY,KAAK,IAAI,MAAM,SAAS,KAAK,MAAM,QAAQ,UAAU,IAA+B;AACzH,MAAI,SAAS;AAMT,YAAQ,MAAM,kCAAkC;AAAA,MAC5C,MAAM,KAAK;AAAA,MACX;AAAA,MACA,MAAM,eAAe,IAAI;AAAA,IAC7B,CAAC;AACD,WAAO;AAAA,MACH,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,OAAO,MAAM,EAAE,CAAC;AAAA,MACzD,SAAS;AAAA,IACb;AAAA,EACJ;AAeA,QAAM,SAAS,EAAE,OAAO,KAAK,aAAa,KAAK,EAAE,UAAU,UAAU,MAAM,CAAC;AAC5E,MAAI,CAAC,OAAO,SAAS;AACjB,YAAQ,MAAM,8CAA8C;AAAA,MACxD,MAAM,KAAK;AAAA,MACX,QAAQ,OAAO,MAAM;AAAA,IACzB,CAAC;AACD,WAAO;AAAA,MACH,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,mBAAmB,KAAK,IAAI,mDAAmD,CAAC;AAAA,MACzH,SAAS;AAAA,IACb;AAAA,EACJ;AAGA,QAAM,oBAAoB,OAAO;AACjC,QAAM,OAAO,KAAK,UAAU,iBAAiB;AAC7C,SAAO;AAAA,IACH;AAAA,IACA,SAAS,CAAC,EAAE,MAAM,QAAiB,KAAK,CAAC;AAAA,IACzC,SAAS;AAAA,EACb;AACJ;AAEO,IAAM,mBAAN,cAA+B,YAAqB;AAAA,EAC7C,UAAU,MAAqB;AACrC,WAAO;AAAA,MACH,MAAM,KAAK;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,aAAa,KAAK;AAAA,MAClB,aAAa,KAAK,aAAa;AAAA,MAC/B,cAAc,KAAK,aAAa;AAAA,MAChC,UAAU,OAAO,SAAS,QAAQ,MAAM,KAAK,QAAQ,IAA+B;AAAA,MACpF,aAAa,KAAK;AAAA,IACtB;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,OACF,MACA,QACA,MACuB;AACvB,UAAM,OAAO,YAAY,IAAI,IAAI;AACjC,QAAI,CAAC,MAAM;AACP,aAAO;AAAA,QACH,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,iBAAiB,IAAI,GAAG,CAAC;AAAA,QAClE,SAAS;AAAA,MACb;AAAA,IACJ;AACA,WAAO,QAAQ,MAAM,QAAQ,IAAI;AAAA,EACrC;AACJ;","names":[]}
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "agentmail-toolkit",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "AgentMail Toolkit",
5
5
  "scripts": {
6
6
  "build": "tsup",
7
+ "prepublishOnly": "tsup",
7
8
  "test": "vitest run",
8
9
  "lint": "eslint .",
9
10
  "prettier": "prettier --write ."