@polpo-ai/tools 0.6.24 → 0.6.26

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mcp-client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/mcp-client.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,158 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { resolveAgentMcpTools } from "../mcp-client.js";
3
+ /**
4
+ * These tests cover the wiring around the AI SDK MCP client (vault
5
+ * templating, host allowlist, namespacing, dispose) by mocking the
6
+ * client itself. We do NOT spin up a real MCP server here — that's
7
+ * an integration concern. The unit-level invariants we lock in are:
8
+ *
9
+ * - `${vault:service:key}` is replaced with the resolved credential.
10
+ * - A missing vault credential throws a clean, named error.
11
+ * - Tools are namespaced `mcp__<server>__<tool>` so collisions across
12
+ * servers are impossible.
13
+ * - One bad server doesn't take down the whole resolve — the rest
14
+ * of the agent's MCPs still come up.
15
+ * - `dispose()` closes every transport that was successfully opened.
16
+ * - `POLPO_MCP_ALLOWED_HOSTS` rejects off-list hosts before any
17
+ * network I/O.
18
+ */
19
+ const closeMock = vi.fn(async () => { });
20
+ const toolsMock = vi.fn(async () => ({
21
+ ping: {
22
+ description: "ping the server",
23
+ inputSchema: { type: "object", properties: {} },
24
+ execute: vi.fn(async () => "pong"),
25
+ },
26
+ echo: {
27
+ description: "echo back",
28
+ inputSchema: { type: "object", properties: { msg: { type: "string" } } },
29
+ execute: vi.fn(async (args) => `echo:${args.msg}`),
30
+ },
31
+ }));
32
+ const createClientMock = vi.fn(async () => ({
33
+ tools: toolsMock,
34
+ close: closeMock,
35
+ }));
36
+ vi.mock("@ai-sdk/mcp", () => ({
37
+ createMCPClient: (arg) => createClientMock(arg),
38
+ }));
39
+ vi.mock("@ai-sdk/mcp/mcp-stdio", () => ({
40
+ Experimental_StdioMCPTransport: class {
41
+ spec;
42
+ constructor(spec) {
43
+ this.spec = spec;
44
+ }
45
+ },
46
+ }));
47
+ const fakeVault = {
48
+ getKey(service, key) {
49
+ if (service === "polpo" && key === "api_key")
50
+ return "secret-123";
51
+ return undefined;
52
+ },
53
+ };
54
+ describe("resolveAgentMcpTools", () => {
55
+ beforeEach(() => {
56
+ closeMock.mockClear();
57
+ toolsMock.mockClear();
58
+ createClientMock.mockClear();
59
+ delete process.env.POLPO_MCP_ALLOWED_HOSTS;
60
+ });
61
+ afterEach(() => {
62
+ delete process.env.POLPO_MCP_ALLOWED_HOSTS;
63
+ });
64
+ it("returns empty + no-op dispose when the agent declares no servers", async () => {
65
+ const result = await resolveAgentMcpTools("agent-1", undefined, undefined);
66
+ expect(result.tools).toEqual([]);
67
+ await expect(result.dispose()).resolves.toBeUndefined();
68
+ expect(createClientMock).not.toHaveBeenCalled();
69
+ });
70
+ it("namespaces tools as mcp__<server>__<tool>", async () => {
71
+ const servers = {
72
+ polpo: { type: "http", url: "https://api.polpo.sh/mcp" },
73
+ };
74
+ const result = await resolveAgentMcpTools("orchestrator", servers, undefined);
75
+ const names = result.tools.map((t) => t.name).sort();
76
+ expect(names).toEqual(["mcp__polpo__echo", "mcp__polpo__ping"]);
77
+ });
78
+ it("templates ${vault:service:key} into header values", async () => {
79
+ const servers = {
80
+ polpo: {
81
+ type: "http",
82
+ url: "https://api.polpo.sh/mcp",
83
+ headers: { Authorization: "Bearer ${vault:polpo:api_key}" },
84
+ },
85
+ };
86
+ await resolveAgentMcpTools("agent-1", servers, fakeVault);
87
+ const call = createClientMock.mock.calls[0]?.[0];
88
+ expect(call.transport.headers.Authorization).toBe("Bearer secret-123");
89
+ });
90
+ it("errors clearly when a vault placeholder cannot be resolved", async () => {
91
+ // We swallow per-server errors and log them, so the resolve still
92
+ // succeeds with an empty tool array — the agent simply doesn't get
93
+ // tools from the broken server.
94
+ const errSpy = vi.spyOn(console, "error").mockImplementation(() => { });
95
+ const servers = {
96
+ bad: {
97
+ type: "http",
98
+ url: "https://api.example.com/mcp",
99
+ headers: { Authorization: "Bearer ${vault:nonexistent:key}" },
100
+ },
101
+ };
102
+ const result = await resolveAgentMcpTools("agent-1", servers, fakeVault);
103
+ expect(result.tools).toEqual([]);
104
+ expect(errSpy.mock.calls[0]?.[1]).toContain('Vault credential "nonexistent:key"');
105
+ errSpy.mockRestore();
106
+ });
107
+ it("isolates failures — one broken server doesn't kill the rest", async () => {
108
+ const errSpy = vi.spyOn(console, "error").mockImplementation(() => { });
109
+ // First call throws, second succeeds.
110
+ createClientMock
111
+ .mockRejectedValueOnce(new Error("boom"))
112
+ .mockResolvedValueOnce({ tools: toolsMock, close: closeMock });
113
+ const servers = {
114
+ broken: { type: "http", url: "https://broken.example.com/mcp" },
115
+ working: { type: "http", url: "https://working.example.com/mcp" },
116
+ };
117
+ const result = await resolveAgentMcpTools("agent-1", servers, undefined);
118
+ expect(result.tools.map((t) => t.name)).toEqual([
119
+ "mcp__working__ping",
120
+ "mcp__working__echo",
121
+ ]);
122
+ errSpy.mockRestore();
123
+ });
124
+ it("dispose closes every successfully-opened transport", async () => {
125
+ const servers = {
126
+ a: { type: "http", url: "https://a.example.com/mcp" },
127
+ b: { type: "http", url: "https://b.example.com/mcp" },
128
+ };
129
+ const result = await resolveAgentMcpTools("agent-1", servers, undefined);
130
+ await result.dispose();
131
+ expect(closeMock).toHaveBeenCalledTimes(2);
132
+ });
133
+ it("rejects HTTP servers outside POLPO_MCP_ALLOWED_HOSTS", async () => {
134
+ const errSpy = vi.spyOn(console, "error").mockImplementation(() => { });
135
+ process.env.POLPO_MCP_ALLOWED_HOSTS = "*.polpo.sh,internal.example.com";
136
+ const servers = {
137
+ external: { type: "http", url: "https://attacker.example.com/mcp" },
138
+ ok: { type: "http", url: "https://api.polpo.sh/mcp" },
139
+ };
140
+ const result = await resolveAgentMcpTools("agent-1", servers, undefined);
141
+ expect(result.tools.map((t) => t.name)).toEqual([
142
+ "mcp__ok__ping",
143
+ "mcp__ok__echo",
144
+ ]);
145
+ expect(errSpy.mock.calls[0]?.[1]).toContain("not in POLPO_MCP_ALLOWED_HOSTS");
146
+ errSpy.mockRestore();
147
+ });
148
+ it("adapts MCP execute to Polpo's ToolResult shape (text content)", async () => {
149
+ const servers = {
150
+ polpo: { type: "http", url: "https://api.polpo.sh/mcp" },
151
+ };
152
+ const { tools } = await resolveAgentMcpTools("agent-1", servers, undefined);
153
+ const echo = tools.find((t) => t.name === "mcp__polpo__echo");
154
+ const result = await echo.execute("call-1", { msg: "hi" });
155
+ expect(result.content).toEqual([{ type: "text", text: "echo:hi" }]);
156
+ });
157
+ });
158
+ //# sourceMappingURL=mcp-client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client.test.js","sourceRoot":"","sources":["../../src/__tests__/mcp-client.test.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -15,6 +15,8 @@ export type { ExtendedToolName, CreateAllToolsOptions } from "./system-tools.js"
15
15
  export { createOutcomeTools } from "./outcome-tools.js";
16
16
  export { createHttpTools, ALL_HTTP_TOOL_NAMES } from "./http-tools.js";
17
17
  export { createVaultToolsCore, createVaultTools, ALL_VAULT_TOOL_NAMES } from "./vault-tools.js";
18
+ export { resolveAgentMcpTools } from "./mcp-client.js";
19
+ export type { McpServerSpec, ResolvedMcpTools, VaultLookup } from "./mcp-client.js";
18
20
  export { createBrowserTools, ALL_BROWSER_TOOL_NAMES, cleanupAgentBrowserSession } from "./browser-tools.js";
19
21
  export { createEmailTools, ALL_EMAIL_TOOL_NAMES } from "./email-tools.js";
20
22
  export { createExcelTools, ALL_EXCEL_TOOL_NAMES } from "./excel-tools.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,IAAI,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC9K,YAAY,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAGjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAGhG,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAC5G,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAGrD,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC1F,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnD,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,IAAI,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC9K,YAAY,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAGjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAChG,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGpF,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAC5G,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAGrD,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC1F,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnD,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ export { createSystemTools, createSystemTools as createCodingTools, createAllToo
16
16
  export { createOutcomeTools } from "./outcome-tools.js";
17
17
  export { createHttpTools, ALL_HTTP_TOOL_NAMES } from "./http-tools.js";
18
18
  export { createVaultToolsCore, createVaultTools, ALL_VAULT_TOOL_NAMES } from "./vault-tools.js";
19
+ export { resolveAgentMcpTools } from "./mcp-client.js";
19
20
  // Extended tool factories
20
21
  export { createBrowserTools, ALL_BROWSER_TOOL_NAMES, cleanupAgentBrowserSession } from "./browser-tools.js";
21
22
  export { createEmailTools, ALL_EMAIL_TOOL_NAMES } from "./email-tools.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,oBAAoB;AACpB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,IAAI,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAG9K,qDAAqD;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAEhG,0BAA0B;AAC1B,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAC5G,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,8CAA8C;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,qBAAqB;AACrB,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC1F,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,oBAAoB;AACpB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,IAAI,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAG9K,qDAAqD;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAChG,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAGvD,0BAA0B;AAC1B,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAC5G,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,8CAA8C;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,qBAAqB;AACrB,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC1F,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Resolve MCP-server-provided tools for a single agent and adapt them to
3
+ * Polpo's internal `PolpoTool` shape. This is the agent-side glue that
4
+ * lets a Polpo agent declare `mcpServers` and have the resulting tools
5
+ * appear next to its native ones (read/write/bash/...) with the same
6
+ * runtime contract.
7
+ *
8
+ * Tools are namespaced as `mcp__<serverName>__<toolName>` so two servers
9
+ * can both expose `read_file` (or whatever) without collision, mirroring
10
+ * Claude Code's convention. The LLM picks the right one because the
11
+ * server-prefixed name is unambiguous.
12
+ *
13
+ * Lifecycle: returns a `dispose()` that closes every opened transport.
14
+ * Callers must invoke it once the request finishes (typically wired into
15
+ * `streamText`'s `onFinish`). Skipping leaks file descriptors / open
16
+ * HTTP keep-alives.
17
+ *
18
+ * Auth: header values support the existing `${vault:KEY}` template
19
+ * syntax, resolved against the agent's vault entries. A missing vault
20
+ * key surfaces as an `Error` so the agent runtime fails fast and visibly
21
+ * — better than silently sending `Bearer ${vault:FOO}` to the server.
22
+ *
23
+ * Security: when an allowlist of hostnames is configured (env
24
+ * `POLPO_MCP_ALLOWED_HOSTS`, comma-separated, supports `*.foo.com`
25
+ * wildcards), HTTP/SSE servers outside the list are refused. Critical
26
+ * in cloud to block SSRF / metadata-IP pivots; in self-host the
27
+ * allowlist is unset and any host works.
28
+ */
29
+ import type { PolpoTool } from "@polpo-ai/core";
30
+ /** A single MCP server config — mirrors the type in `@polpo-ai/sdk`. */
31
+ export type McpServerSpec = {
32
+ type?: "stdio";
33
+ command: string;
34
+ args?: string[];
35
+ env?: Record<string, string>;
36
+ } | {
37
+ type: "sse";
38
+ url: string;
39
+ headers?: Record<string, string>;
40
+ } | {
41
+ type: "http";
42
+ url: string;
43
+ headers?: Record<string, string>;
44
+ };
45
+ /**
46
+ * Minimal subset of the existing `ResolvedVault` interface we depend on.
47
+ * Decoupled to keep `@polpo-ai/tools` from importing the shell's vault
48
+ * resolver — callers pass in any object that conforms.
49
+ */
50
+ export interface VaultLookup {
51
+ getKey(service: string, key: string): string | undefined;
52
+ }
53
+ export interface ResolvedMcpTools {
54
+ /** Polpo-format tools — drop straight into the agent's tool array. */
55
+ tools: PolpoTool<any>[];
56
+ /** Close every opened transport. Best-effort; idempotent. */
57
+ dispose: () => Promise<void>;
58
+ }
59
+ /**
60
+ * Open every MCP server declared on the agent config. Returns the
61
+ * aggregated Polpo tools + a single `dispose` that closes them all.
62
+ *
63
+ * Failures on individual servers are logged but don't abort the rest:
64
+ * an agent with one broken MCP and one good one still gets the good
65
+ * tools, with a console error pointing at the bad config.
66
+ */
67
+ export declare function resolveAgentMcpTools(agentName: string, mcpServers: Record<string, McpServerSpec> | undefined, vault: VaultLookup | undefined): Promise<ResolvedMcpTools>;
68
+ //# sourceMappingURL=mcp-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client.d.ts","sourceRoot":"","sources":["../src/mcp-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,gBAAgB,CAAC;AAE5D,wEAAwE;AACxE,MAAM,MAAM,aAAa,GACrB;IACE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B,GACD;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC;AAEpE;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAC1D;AAED,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,KAAK,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;IACxB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AA2ID;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,SAAS,EACrD,KAAK,EAAE,WAAW,GAAG,SAAS,GAC7B,OAAO,CAAC,gBAAgB,CAAC,CAoF3B"}
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Resolve MCP-server-provided tools for a single agent and adapt them to
3
+ * Polpo's internal `PolpoTool` shape. This is the agent-side glue that
4
+ * lets a Polpo agent declare `mcpServers` and have the resulting tools
5
+ * appear next to its native ones (read/write/bash/...) with the same
6
+ * runtime contract.
7
+ *
8
+ * Tools are namespaced as `mcp__<serverName>__<toolName>` so two servers
9
+ * can both expose `read_file` (or whatever) without collision, mirroring
10
+ * Claude Code's convention. The LLM picks the right one because the
11
+ * server-prefixed name is unambiguous.
12
+ *
13
+ * Lifecycle: returns a `dispose()` that closes every opened transport.
14
+ * Callers must invoke it once the request finishes (typically wired into
15
+ * `streamText`'s `onFinish`). Skipping leaks file descriptors / open
16
+ * HTTP keep-alives.
17
+ *
18
+ * Auth: header values support the existing `${vault:KEY}` template
19
+ * syntax, resolved against the agent's vault entries. A missing vault
20
+ * key surfaces as an `Error` so the agent runtime fails fast and visibly
21
+ * — better than silently sending `Bearer ${vault:FOO}` to the server.
22
+ *
23
+ * Security: when an allowlist of hostnames is configured (env
24
+ * `POLPO_MCP_ALLOWED_HOSTS`, comma-separated, supports `*.foo.com`
25
+ * wildcards), HTTP/SSE servers outside the list are refused. Critical
26
+ * in cloud to block SSRF / metadata-IP pivots; in self-host the
27
+ * allowlist is unset and any host works.
28
+ */
29
+ /**
30
+ * Replace `${vault:service:key}` placeholders in a string. Reuses the
31
+ * existing service/key-credential model — the MCP credentials live in
32
+ * the same vault as everything else (no parallel abstraction).
33
+ *
34
+ * Example: `Bearer ${vault:polpo:api_key}` resolves to the `api_key`
35
+ * credential of the `polpo` vault entry.
36
+ */
37
+ function applyVault(input, vault) {
38
+ if (!vault)
39
+ return input;
40
+ return input.replace(/\$\{vault:([a-z0-9_-]+):([a-z0-9_-]+)\}/gi, (_, service, key) => {
41
+ const value = vault.getKey(service, key);
42
+ if (value === undefined) {
43
+ throw new Error(`Vault credential "${service}:${key}" not found — required by MCP server config`);
44
+ }
45
+ return value;
46
+ });
47
+ }
48
+ function resolveHeaders(headers, vault) {
49
+ if (!headers)
50
+ return undefined;
51
+ const out = {};
52
+ for (const [k, v] of Object.entries(headers)) {
53
+ out[k] = applyVault(v, vault);
54
+ }
55
+ return out;
56
+ }
57
+ function parseAllowedHosts() {
58
+ const raw = process.env.POLPO_MCP_ALLOWED_HOSTS;
59
+ if (!raw)
60
+ return null;
61
+ return raw
62
+ .split(",")
63
+ .map((s) => s.trim())
64
+ .filter(Boolean);
65
+ }
66
+ function hostAllowed(hostname, patterns) {
67
+ for (const p of patterns) {
68
+ if (p === hostname)
69
+ return true;
70
+ if (p.startsWith("*.") && hostname.endsWith(p.slice(1)))
71
+ return true;
72
+ }
73
+ return false;
74
+ }
75
+ /**
76
+ * Adapt an AI SDK `Tool` (from `mcpClient.tools()`) into a Polpo
77
+ * `PolpoTool`. The runtime contracts overlap on `description` + an input
78
+ * schema + an executor; the wrapper bridges the small differences:
79
+ *
80
+ * - AI SDK execute takes `(args, ctx)` and returns the raw server output
81
+ * (string, object, or `CallToolResult`). Polpo's executor returns
82
+ * `{ content: [{ type: "text", text }], details }`.
83
+ * - MCP can return image content; we pass it through verbatim when
84
+ * present so vision-capable agents can consume it.
85
+ * - Errors from the underlying transport bubble as `Error` text content
86
+ * — the agent loop already knows how to display tool errors.
87
+ */
88
+ function adaptMcpTool(serverName, toolName, aiTool) {
89
+ return {
90
+ name: `mcp__${serverName}__${toolName}`,
91
+ label: toolName,
92
+ description: aiTool.description ?? "",
93
+ // AI SDK wraps schemas in a Zod/JSON Schema container; Polpo passes
94
+ // the raw schema through to `jsonSchema()` downstream which accepts
95
+ // arbitrary JSON-Schema-shaped objects, so this works without a
96
+ // typebox conversion.
97
+ parameters: aiTool.inputSchema,
98
+ execute: async (toolCallId, params, signal) => {
99
+ try {
100
+ const raw = await aiTool.execute(params, {
101
+ toolCallId,
102
+ messages: [],
103
+ abortSignal: signal,
104
+ });
105
+ return coerceMcpResult(raw);
106
+ }
107
+ catch (err) {
108
+ const message = err instanceof Error ? err.message : String(err);
109
+ return {
110
+ content: [{ type: "text", text: `MCP tool error: ${message}` }],
111
+ details: { error: message },
112
+ };
113
+ }
114
+ },
115
+ };
116
+ }
117
+ /**
118
+ * MCP servers can return: a plain string, a structured object, or a
119
+ * `CallToolResult` with a `content[]` array. Normalize into Polpo's
120
+ * `ToolResult`. Pass image content through; serialize unknown shapes
121
+ * as JSON so the LLM at least gets readable text.
122
+ */
123
+ function coerceMcpResult(raw) {
124
+ if (raw == null) {
125
+ return { content: [{ type: "text", text: "" }], details: null };
126
+ }
127
+ if (typeof raw === "string") {
128
+ return { content: [{ type: "text", text: raw }], details: raw };
129
+ }
130
+ if (typeof raw === "object" && raw !== null && Array.isArray(raw.content)) {
131
+ const content = raw.content
132
+ .map((part) => {
133
+ if (part?.type === "text" && typeof part.text === "string") {
134
+ return { type: "text", text: part.text };
135
+ }
136
+ if (part?.type === "image" && typeof part.data === "string") {
137
+ return {
138
+ type: "image",
139
+ data: part.data,
140
+ mimeType: part.mimeType ?? "image/png",
141
+ };
142
+ }
143
+ // Unknown content part — serialize so we don't lose information
144
+ return { type: "text", text: JSON.stringify(part) };
145
+ })
146
+ .filter(Boolean);
147
+ return { content, details: raw };
148
+ }
149
+ return {
150
+ content: [{ type: "text", text: JSON.stringify(raw) }],
151
+ details: raw,
152
+ };
153
+ }
154
+ /**
155
+ * Open every MCP server declared on the agent config. Returns the
156
+ * aggregated Polpo tools + a single `dispose` that closes them all.
157
+ *
158
+ * Failures on individual servers are logged but don't abort the rest:
159
+ * an agent with one broken MCP and one good one still gets the good
160
+ * tools, with a console error pointing at the bad config.
161
+ */
162
+ export async function resolveAgentMcpTools(agentName, mcpServers, vault) {
163
+ if (!mcpServers || Object.keys(mcpServers).length === 0) {
164
+ return { tools: [], dispose: async () => { } };
165
+ }
166
+ // Lazy-loaded so projects that never use MCP don't pay the import cost
167
+ // (the SDK pulls in @modelcontextprotocol/sdk transitively which is
168
+ // non-trivial — a few hundred KB at module-init time).
169
+ // Cast through any: vitest's vi.mock substitutes a single-arg factory
170
+ // that doesn't match the published SDK's variadic typing. Runtime is
171
+ // identical.
172
+ const { createMCPClient } = (await import("@ai-sdk/mcp"));
173
+ const allowedHosts = parseAllowedHosts();
174
+ const closers = [];
175
+ const tools = [];
176
+ for (const [serverName, spec] of Object.entries(mcpServers)) {
177
+ try {
178
+ // Validate host upfront for HTTP/SSE — stdio is local-only and
179
+ // already gated by the sandbox boundary.
180
+ if (spec.type === "http" || spec.type === "sse") {
181
+ if (allowedHosts) {
182
+ const url = new URL(spec.url);
183
+ if (!hostAllowed(url.hostname, allowedHosts)) {
184
+ throw new Error(`MCP host "${url.hostname}" not in POLPO_MCP_ALLOWED_HOSTS`);
185
+ }
186
+ }
187
+ }
188
+ let transport;
189
+ if (spec.type === "sse") {
190
+ transport = {
191
+ type: "sse",
192
+ url: spec.url,
193
+ headers: resolveHeaders(spec.headers, vault),
194
+ };
195
+ }
196
+ else if (spec.type === "stdio" || spec.command) {
197
+ const stdioSpec = spec;
198
+ const { Experimental_StdioMCPTransport } = await import("@ai-sdk/mcp/mcp-stdio");
199
+ transport = new Experimental_StdioMCPTransport({
200
+ command: stdioSpec.command,
201
+ args: stdioSpec.args,
202
+ env: stdioSpec.env,
203
+ });
204
+ }
205
+ else {
206
+ // Default to HTTP — handles both `{ type: "http", url }` and the
207
+ // forgiving `{ url }` shorthand.
208
+ const httpSpec = spec;
209
+ transport = {
210
+ type: "http",
211
+ url: httpSpec.url,
212
+ headers: resolveHeaders(httpSpec.headers, vault),
213
+ // Defense-in-depth: refuse 3xx so an attacker can't bounce us
214
+ // off-allowlist via a redirect.
215
+ redirect: "error",
216
+ };
217
+ }
218
+ const client = await createMCPClient({ transport });
219
+ closers.push(() => client.close().catch(() => { }));
220
+ const serverTools = await client.tools();
221
+ for (const [toolName, aiTool] of Object.entries(serverTools)) {
222
+ tools.push(adaptMcpTool(serverName, toolName, aiTool));
223
+ }
224
+ }
225
+ catch (err) {
226
+ console.error(`[mcp] agent="${agentName}" server="${serverName}" failed:`, err instanceof Error ? err.message : err);
227
+ }
228
+ }
229
+ return {
230
+ tools,
231
+ dispose: async () => {
232
+ await Promise.all(closers.map((fn) => fn()));
233
+ },
234
+ };
235
+ }
236
+ //# sourceMappingURL=mcp-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client.js","sourceRoot":"","sources":["../src/mcp-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AA+BH;;;;;;;GAOG;AACH,SAAS,UAAU,CAAC,KAAa,EAAE,KAA8B;IAC/D,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,OAAO,KAAK,CAAC,OAAO,CAClB,2CAA2C,EAC3C,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE;QAClB,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACzC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CACb,qBAAqB,OAAO,IAAI,GAAG,6CAA6C,CACjF,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CACF,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CACrB,OAA2C,EAC3C,KAA8B;IAE9B,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,GAAG,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,iBAAiB;IACxB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC;IAChD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO,GAAG;SACP,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,WAAW,CAAC,QAAgB,EAAE,QAAkB;IACvD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAChC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;IACvE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,YAAY,CACnB,UAAkB,EAClB,QAAgB,EAChB,MAAW;IAEX,OAAO;QACL,IAAI,EAAE,QAAQ,UAAU,KAAK,QAAQ,EAAE;QACvC,KAAK,EAAE,QAAQ;QACf,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE;QACrC,oEAAoE;QACpE,oEAAoE;QACpE,gEAAgE;QAChE,sBAAsB;QACtB,UAAU,EAAE,MAAM,CAAC,WAAkB;QACrC,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YAC5C,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE;oBACvC,UAAU;oBACV,QAAQ,EAAE,EAAE;oBACZ,WAAW,EAAE,MAAM;iBACpB,CAAC,CAAC;gBACH,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC;YAC9B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjE,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,OAAO,EAAE,EAAE,CAAC;oBAC/D,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;iBACP,CAAC;YACzB,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAC,GAAY;IACnC,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QAChB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAClE,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IAClE,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAE,GAAW,CAAC,OAAO,CAAC,EAAE,CAAC;QACnF,MAAM,OAAO,GAAK,GAAW,CAAC,OAAiB;aAC5C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACZ,IAAI,IAAI,EAAE,IAAI,KAAK,MAAM,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC3D,OAAO,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;YACpD,CAAC;YACD,IAAI,IAAI,EAAE,IAAI,KAAK,OAAO,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC5D,OAAO;oBACL,IAAI,EAAE,OAAgB;oBACtB,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,WAAW;iBACvC,CAAC;YACJ,CAAC;YACD,gEAAgE;YAChE,OAAO,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/D,CAAC,CAAC;aACD,MAAM,CAAC,OAAO,CAAC,CAAC;QACnB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IACnC,CAAC;IACD,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;QACtD,OAAO,EAAE,GAAG;KACb,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,SAAiB,EACjB,UAAqD,EACrD,KAA8B;IAE9B,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxD,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,CAAC;IAChD,CAAC;IAED,uEAAuE;IACvE,oEAAoE;IACpE,uDAAuD;IACvD,sEAAsE;IACtE,qEAAqE;IACrE,aAAa;IACb,MAAM,EAAE,eAAe,EAAE,GAAG,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,CAAQ,CAAC;IAEjE,MAAM,YAAY,GAAG,iBAAiB,EAAE,CAAC;IACzC,MAAM,OAAO,GAA+B,EAAE,CAAC;IAC/C,MAAM,KAAK,GAAqB,EAAE,CAAC;IAEnC,KAAK,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5D,IAAI,CAAC;YACH,+DAA+D;YAC/D,yCAAyC;YACzC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;gBAChD,IAAI,YAAY,EAAE,CAAC;oBACjB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBAC9B,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,CAAC;wBAC7C,MAAM,IAAI,KAAK,CACb,aAAa,GAAG,CAAC,QAAQ,kCAAkC,CAC5D,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;YAED,IAAI,SAAc,CAAC;YACnB,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;gBACxB,SAAS,GAAG;oBACV,IAAI,EAAE,KAAc;oBACpB,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC;iBAC7C,CAAC;YACJ,CAAC;iBAAM,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,IAAK,IAAY,CAAC,OAAO,EAAE,CAAC;gBAC1D,MAAM,SAAS,GAAG,IAAmD,CAAC;gBACtE,MAAM,EAAE,8BAA8B,EAAE,GAAG,MAAM,MAAM,CACrD,uBAAuB,CACxB,CAAC;gBACF,SAAS,GAAG,IAAI,8BAA8B,CAAC;oBAC7C,OAAO,EAAE,SAAS,CAAC,OAAO;oBAC1B,IAAI,EAAE,SAAS,CAAC,IAAI;oBACpB,GAAG,EAAE,SAAS,CAAC,GAAG;iBACnB,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,iEAAiE;gBACjE,iCAAiC;gBACjC,MAAM,QAAQ,GAAG,IAAgD,CAAC;gBAClE,SAAS,GAAG;oBACV,IAAI,EAAE,MAAe;oBACrB,GAAG,EAAE,QAAQ,CAAC,GAAG;oBACjB,OAAO,EAAE,cAAc,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;oBAChD,8DAA8D;oBAC9D,gCAAgC;oBAChC,QAAQ,EAAE,OAAgB;iBAC3B,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;YACpD,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC;YAEnD,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACzC,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC7D,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CACX,gBAAgB,SAAS,aAAa,UAAU,WAAW,EAC3D,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CACzC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK;QACL,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/C,CAAC;KACF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polpo-ai/tools",
3
- "version": "0.6.24",
3
+ "version": "0.6.26",
4
4
  "description": "Agent tools for Polpo — coding, browser, email, search, and more. Core tools work everywhere, extended tools are opt-in.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -16,19 +16,21 @@
16
16
  "README.md"
17
17
  ],
18
18
  "dependencies": {
19
+ "@ai-sdk/mcp": "^1.0.36",
20
+ "@modelcontextprotocol/sdk": "^1.29.0",
19
21
  "@sinclair/typebox": "^0.34.0",
20
22
  "nanoid": "^5.0.0",
21
- "@polpo-ai/core": "0.6.24"
23
+ "@polpo-ai/core": "0.6.26"
22
24
  },
23
25
  "optionalDependencies": {
26
+ "docx": "^9.5.0",
27
+ "exceljs": "^4.4.0",
24
28
  "execa": "^9.0.0",
25
- "playwright-core": "^1.52.0",
26
- "nodemailer": "^8.0.0",
27
29
  "imapflow": "^1.2.0",
28
- "exceljs": "^4.4.0",
30
+ "mammoth": "^1.11.0",
31
+ "nodemailer": "^8.0.0",
29
32
  "pdf-lib": "^1.17.0",
30
- "docx": "^9.5.0",
31
- "mammoth": "^1.11.0"
33
+ "playwright-core": "^1.52.0"
32
34
  },
33
35
  "peerDependencies": {
34
36
  "zod": ">=3.0.0 || >=4.0.0"