@dexto/tools-builtins 1.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.
Files changed (77) hide show
  1. package/LICENSE +44 -0
  2. package/dist/builtin-tools-factory.cjs +95 -0
  3. package/dist/builtin-tools-factory.d.cts +19 -0
  4. package/dist/builtin-tools-factory.d.ts +17 -0
  5. package/dist/builtin-tools-factory.d.ts.map +1 -0
  6. package/dist/builtin-tools-factory.js +69 -0
  7. package/dist/builtin-tools-factory.test.cjs +26 -0
  8. package/dist/builtin-tools-factory.test.d.cts +2 -0
  9. package/dist/builtin-tools-factory.test.d.ts +2 -0
  10. package/dist/builtin-tools-factory.test.d.ts.map +1 -0
  11. package/dist/builtin-tools-factory.test.js +25 -0
  12. package/dist/implementations/ask-user-tool.cjs +65 -0
  13. package/dist/implementations/ask-user-tool.d.cts +46 -0
  14. package/dist/implementations/ask-user-tool.d.ts +45 -0
  15. package/dist/implementations/ask-user-tool.d.ts.map +1 -0
  16. package/dist/implementations/ask-user-tool.js +41 -0
  17. package/dist/implementations/delegate-to-url-tool.cjs +183 -0
  18. package/dist/implementations/delegate-to-url-tool.d.cts +27 -0
  19. package/dist/implementations/delegate-to-url-tool.d.ts +26 -0
  20. package/dist/implementations/delegate-to-url-tool.d.ts.map +1 -0
  21. package/dist/implementations/delegate-to-url-tool.js +159 -0
  22. package/dist/implementations/delegate-to-url-tool.test.cjs +75 -0
  23. package/dist/implementations/delegate-to-url-tool.test.d.cts +2 -0
  24. package/dist/implementations/delegate-to-url-tool.test.d.ts +2 -0
  25. package/dist/implementations/delegate-to-url-tool.test.d.ts.map +1 -0
  26. package/dist/implementations/delegate-to-url-tool.test.js +74 -0
  27. package/dist/implementations/exa-code-search-tool.cjs +57 -0
  28. package/dist/implementations/exa-code-search-tool.d.cts +21 -0
  29. package/dist/implementations/exa-code-search-tool.d.ts +20 -0
  30. package/dist/implementations/exa-code-search-tool.d.ts.map +1 -0
  31. package/dist/implementations/exa-code-search-tool.js +33 -0
  32. package/dist/implementations/exa-mcp.cjs +98 -0
  33. package/dist/implementations/exa-mcp.d.cts +24 -0
  34. package/dist/implementations/exa-mcp.d.ts +23 -0
  35. package/dist/implementations/exa-mcp.d.ts.map +1 -0
  36. package/dist/implementations/exa-mcp.js +74 -0
  37. package/dist/implementations/exa-tools.test.cjs +91 -0
  38. package/dist/implementations/exa-tools.test.d.cts +2 -0
  39. package/dist/implementations/exa-tools.test.d.ts +2 -0
  40. package/dist/implementations/exa-tools.test.d.ts.map +1 -0
  41. package/dist/implementations/exa-tools.test.js +90 -0
  42. package/dist/implementations/exa-web-search-tool.cjs +63 -0
  43. package/dist/implementations/exa-web-search-tool.d.cts +30 -0
  44. package/dist/implementations/exa-web-search-tool.d.ts +29 -0
  45. package/dist/implementations/exa-web-search-tool.d.ts.map +1 -0
  46. package/dist/implementations/exa-web-search-tool.js +39 -0
  47. package/dist/implementations/get-resource-tool.cjs +121 -0
  48. package/dist/implementations/get-resource-tool.d.cts +22 -0
  49. package/dist/implementations/get-resource-tool.d.ts +21 -0
  50. package/dist/implementations/get-resource-tool.d.ts.map +1 -0
  51. package/dist/implementations/get-resource-tool.js +97 -0
  52. package/dist/implementations/http-request-tool.cjs +327 -0
  53. package/dist/implementations/http-request-tool.d.cts +31 -0
  54. package/dist/implementations/http-request-tool.d.ts +30 -0
  55. package/dist/implementations/http-request-tool.d.ts.map +1 -0
  56. package/dist/implementations/http-request-tool.js +303 -0
  57. package/dist/implementations/invoke-skill-tool.cjs +134 -0
  58. package/dist/implementations/invoke-skill-tool.d.cts +26 -0
  59. package/dist/implementations/invoke-skill-tool.d.ts +25 -0
  60. package/dist/implementations/invoke-skill-tool.d.ts.map +1 -0
  61. package/dist/implementations/invoke-skill-tool.js +110 -0
  62. package/dist/implementations/list-resources-tool.cjs +99 -0
  63. package/dist/implementations/list-resources-tool.d.cts +26 -0
  64. package/dist/implementations/list-resources-tool.d.ts +25 -0
  65. package/dist/implementations/list-resources-tool.d.ts.map +1 -0
  66. package/dist/implementations/list-resources-tool.js +75 -0
  67. package/dist/implementations/sleep-tool.cjs +45 -0
  68. package/dist/implementations/sleep-tool.d.cts +16 -0
  69. package/dist/implementations/sleep-tool.d.ts +15 -0
  70. package/dist/implementations/sleep-tool.d.ts.map +1 -0
  71. package/dist/implementations/sleep-tool.js +21 -0
  72. package/dist/index.cjs +33 -0
  73. package/dist/index.d.cts +3 -0
  74. package/dist/index.d.ts +17 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +7 -0
  77. package/package.json +38 -0
@@ -0,0 +1,33 @@
1
+ import { z } from "zod";
2
+ import { defineTool } from "@dexto/core";
3
+ import { callExaTool } from "./exa-mcp.js";
4
+ const CodeSearchInputSchema = z.object({
5
+ query: z.string().min(1).describe(
6
+ "Search query for code examples and documentation (e.g., 'React useState examples', 'Express middleware', 'Python pandas dataframe filtering')"
7
+ ),
8
+ tokensNum: z.number().int().min(1e3).max(5e4).optional().default(5e3).describe("Approximate token budget to return (1000\u201350000, default: 5000)")
9
+ }).strict();
10
+ function createCodeSearchTool() {
11
+ return defineTool({
12
+ id: "code_search",
13
+ displayName: "Code Search",
14
+ description: "Search for code examples and documentation across sources like official docs, GitHub, and Stack Overflow. Returns formatted text context.",
15
+ inputSchema: CodeSearchInputSchema,
16
+ async execute(input, context) {
17
+ const { query, tokensNum } = input;
18
+ return await callExaTool({
19
+ logger: context.logger,
20
+ toolId: "code_search",
21
+ toolName: "get_code_context_exa",
22
+ args: {
23
+ query,
24
+ tokensNum
25
+ },
26
+ timeoutMs: 3e4
27
+ });
28
+ }
29
+ });
30
+ }
31
+ export {
32
+ createCodeSearchTool
33
+ };
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var exa_mcp_exports = {};
20
+ __export(exa_mcp_exports, {
21
+ callExaTool: () => callExaTool
22
+ });
23
+ module.exports = __toCommonJS(exa_mcp_exports);
24
+ var import_core = require("@dexto/core");
25
+ var import_core2 = require("@dexto/core");
26
+ const EXA_SERVER_URL = "https://mcp.exa.ai/mcp";
27
+ function isPlainObject(value) {
28
+ return typeof value === "object" && value !== null && !Array.isArray(value);
29
+ }
30
+ function asExaToolResult(value) {
31
+ if (!isPlainObject(value)) return null;
32
+ const content = value.content;
33
+ const isError = value.isError;
34
+ const result = {};
35
+ if (Array.isArray(content)) {
36
+ result.content = content.filter(isPlainObject).map((entry) => {
37
+ const mapped = {};
38
+ if (typeof entry.type === "string") {
39
+ mapped.type = entry.type;
40
+ }
41
+ if (typeof entry.text === "string") {
42
+ mapped.text = entry.text;
43
+ }
44
+ return mapped;
45
+ });
46
+ }
47
+ if (typeof isError === "boolean") {
48
+ result.isError = isError;
49
+ }
50
+ return result;
51
+ }
52
+ function extractFirstText(result) {
53
+ if (!result.content) return null;
54
+ for (const entry of result.content) {
55
+ if (entry.type === "text" && typeof entry.text === "string" && entry.text.trim()) {
56
+ return entry.text;
57
+ }
58
+ }
59
+ return null;
60
+ }
61
+ async function callExaTool(options) {
62
+ const { logger, toolId, toolName, args, timeoutMs } = options;
63
+ const mcpClient = new import_core.DextoMcpClient(logger);
64
+ const config = import_core.McpServerConfigSchema.parse({
65
+ type: "http",
66
+ enabled: true,
67
+ url: EXA_SERVER_URL,
68
+ headers: {},
69
+ timeout: timeoutMs,
70
+ connectionMode: "lenient"
71
+ });
72
+ try {
73
+ await mcpClient.connect(config, "exa");
74
+ const client = await mcpClient.getConnectedClient();
75
+ const result = await client.callTool({ name: toolName, arguments: args }, void 0, {
76
+ timeout: timeoutMs,
77
+ resetTimeoutOnProgress: true
78
+ });
79
+ const parsed = asExaToolResult(result);
80
+ if (parsed?.isError) {
81
+ const message = extractFirstText(parsed) ?? "Unknown error from Exa MCP tool";
82
+ throw import_core2.ToolError.executionFailed(toolId, message);
83
+ }
84
+ const text = parsed ? extractFirstText(parsed) : null;
85
+ return text ?? "No results found. Try a different query.";
86
+ } catch (error) {
87
+ if (error instanceof Error) {
88
+ throw import_core2.ToolError.executionFailed(toolId, error.message);
89
+ }
90
+ throw import_core2.ToolError.executionFailed(toolId, String(error));
91
+ } finally {
92
+ await mcpClient.disconnect();
93
+ }
94
+ }
95
+ // Annotate the CommonJS export names for ESM import in node:
96
+ 0 && (module.exports = {
97
+ callExaTool
98
+ });
@@ -0,0 +1,24 @@
1
+ import { Logger } from '@dexto/core';
2
+
3
+ type ExaToolName = 'web_search_exa' | 'get_code_context_exa';
4
+ /**
5
+ * Exa search tools are implemented as internal tools (not exposed as `mcp--...` tools).
6
+ *
7
+ * Why:
8
+ * - We want stable tool IDs and consistent display/approval UX.
9
+ * - We intentionally do NOT register Exa as an MCP server in the agent-wide MCPManager, because that would:
10
+ * - expose extra Exa MCP tools (e.g. company research) that we don't want in the tool surface
11
+ * - allow the model to bypass curated wrappers by calling `mcp--...` tools directly
12
+ *
13
+ * Implementation detail:
14
+ * - We still use the MCP SDK via Dexto's MCP client wrapper (no bespoke fetch/SSE parsing).
15
+ */
16
+ declare function callExaTool(options: {
17
+ logger: Logger;
18
+ toolId: string;
19
+ toolName: ExaToolName;
20
+ args: Record<string, unknown>;
21
+ timeoutMs: number;
22
+ }): Promise<string>;
23
+
24
+ export { callExaTool };
@@ -0,0 +1,23 @@
1
+ import type { Logger } from '@dexto/core';
2
+ type ExaToolName = 'web_search_exa' | 'get_code_context_exa';
3
+ /**
4
+ * Exa search tools are implemented as internal tools (not exposed as `mcp--...` tools).
5
+ *
6
+ * Why:
7
+ * - We want stable tool IDs and consistent display/approval UX.
8
+ * - We intentionally do NOT register Exa as an MCP server in the agent-wide MCPManager, because that would:
9
+ * - expose extra Exa MCP tools (e.g. company research) that we don't want in the tool surface
10
+ * - allow the model to bypass curated wrappers by calling `mcp--...` tools directly
11
+ *
12
+ * Implementation detail:
13
+ * - We still use the MCP SDK via Dexto's MCP client wrapper (no bespoke fetch/SSE parsing).
14
+ */
15
+ export declare function callExaTool(options: {
16
+ logger: Logger;
17
+ toolId: string;
18
+ toolName: ExaToolName;
19
+ args: Record<string, unknown>;
20
+ timeoutMs: number;
21
+ }): Promise<string>;
22
+ export {};
23
+ //# sourceMappingURL=exa-mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exa-mcp.d.ts","sourceRoot":"","sources":["../../src/implementations/exa-mcp.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,KAAK,WAAW,GAAG,gBAAgB,GAAG,sBAAsB,CAAC;AAmD7D;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,WAAW,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqClB"}
@@ -0,0 +1,74 @@
1
+ import { DextoMcpClient, McpServerConfigSchema } from "@dexto/core";
2
+ import { ToolError } from "@dexto/core";
3
+ const EXA_SERVER_URL = "https://mcp.exa.ai/mcp";
4
+ function isPlainObject(value) {
5
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6
+ }
7
+ function asExaToolResult(value) {
8
+ if (!isPlainObject(value)) return null;
9
+ const content = value.content;
10
+ const isError = value.isError;
11
+ const result = {};
12
+ if (Array.isArray(content)) {
13
+ result.content = content.filter(isPlainObject).map((entry) => {
14
+ const mapped = {};
15
+ if (typeof entry.type === "string") {
16
+ mapped.type = entry.type;
17
+ }
18
+ if (typeof entry.text === "string") {
19
+ mapped.text = entry.text;
20
+ }
21
+ return mapped;
22
+ });
23
+ }
24
+ if (typeof isError === "boolean") {
25
+ result.isError = isError;
26
+ }
27
+ return result;
28
+ }
29
+ function extractFirstText(result) {
30
+ if (!result.content) return null;
31
+ for (const entry of result.content) {
32
+ if (entry.type === "text" && typeof entry.text === "string" && entry.text.trim()) {
33
+ return entry.text;
34
+ }
35
+ }
36
+ return null;
37
+ }
38
+ async function callExaTool(options) {
39
+ const { logger, toolId, toolName, args, timeoutMs } = options;
40
+ const mcpClient = new DextoMcpClient(logger);
41
+ const config = McpServerConfigSchema.parse({
42
+ type: "http",
43
+ enabled: true,
44
+ url: EXA_SERVER_URL,
45
+ headers: {},
46
+ timeout: timeoutMs,
47
+ connectionMode: "lenient"
48
+ });
49
+ try {
50
+ await mcpClient.connect(config, "exa");
51
+ const client = await mcpClient.getConnectedClient();
52
+ const result = await client.callTool({ name: toolName, arguments: args }, void 0, {
53
+ timeout: timeoutMs,
54
+ resetTimeoutOnProgress: true
55
+ });
56
+ const parsed = asExaToolResult(result);
57
+ if (parsed?.isError) {
58
+ const message = extractFirstText(parsed) ?? "Unknown error from Exa MCP tool";
59
+ throw ToolError.executionFailed(toolId, message);
60
+ }
61
+ const text = parsed ? extractFirstText(parsed) : null;
62
+ return text ?? "No results found. Try a different query.";
63
+ } catch (error) {
64
+ if (error instanceof Error) {
65
+ throw ToolError.executionFailed(toolId, error.message);
66
+ }
67
+ throw ToolError.executionFailed(toolId, String(error));
68
+ } finally {
69
+ await mcpClient.disconnect();
70
+ }
71
+ }
72
+ export {
73
+ callExaTool
74
+ };
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var import_vitest = require("vitest");
3
+ var import_core = require("@dexto/core");
4
+ var import_exa_web_search_tool = require("./exa-web-search-tool.js");
5
+ var import_exa_code_search_tool = require("./exa-code-search-tool.js");
6
+ function createMockLogger() {
7
+ const logger = {
8
+ debug: import_vitest.vi.fn(),
9
+ silly: import_vitest.vi.fn(),
10
+ info: import_vitest.vi.fn(),
11
+ warn: import_vitest.vi.fn(),
12
+ error: import_vitest.vi.fn(),
13
+ trackException: import_vitest.vi.fn(),
14
+ createChild: import_vitest.vi.fn(() => logger),
15
+ setLevel: import_vitest.vi.fn(),
16
+ getLevel: import_vitest.vi.fn(() => "debug"),
17
+ getLogFilePath: import_vitest.vi.fn(() => null),
18
+ destroy: import_vitest.vi.fn(async () => void 0)
19
+ };
20
+ return logger;
21
+ }
22
+ function createToolContext(logger) {
23
+ return { logger };
24
+ }
25
+ (0, import_vitest.describe)("Exa built-in tools", () => {
26
+ const connectSpy = import_vitest.vi.spyOn(import_core.DextoMcpClient.prototype, "connect");
27
+ const disconnectSpy = import_vitest.vi.spyOn(import_core.DextoMcpClient.prototype, "disconnect");
28
+ const getConnectedClientSpy = import_vitest.vi.spyOn(import_core.DextoMcpClient.prototype, "getConnectedClient");
29
+ (0, import_vitest.beforeEach)(() => {
30
+ connectSpy.mockResolvedValue({});
31
+ disconnectSpy.mockResolvedValue();
32
+ });
33
+ (0, import_vitest.afterEach)(() => {
34
+ import_vitest.vi.resetAllMocks();
35
+ });
36
+ (0, import_vitest.it)("web_search calls web_search_exa and returns the first text content", async () => {
37
+ const callTool = import_vitest.vi.fn().mockResolvedValue({
38
+ content: [{ type: "text", text: "hello from exa" }]
39
+ });
40
+ getConnectedClientSpy.mockResolvedValue({ callTool });
41
+ const tool = (0, import_exa_web_search_tool.createWebSearchTool)();
42
+ const logger = createMockLogger();
43
+ const context = createToolContext(logger);
44
+ const input = tool.inputSchema.parse({ query: "what is dexto" });
45
+ const result = await tool.execute(input, context);
46
+ (0, import_vitest.expect)(result).toBe("hello from exa");
47
+ (0, import_vitest.expect)(callTool).toHaveBeenCalledWith(
48
+ {
49
+ name: "web_search_exa",
50
+ arguments: import_vitest.expect.objectContaining({ query: "what is dexto" })
51
+ },
52
+ void 0,
53
+ import_vitest.expect.objectContaining({ timeout: 25e3, resetTimeoutOnProgress: true })
54
+ );
55
+ });
56
+ (0, import_vitest.it)("code_search calls get_code_context_exa and returns the first text content", async () => {
57
+ const callTool = import_vitest.vi.fn().mockResolvedValue({
58
+ content: [{ type: "text", text: "code context" }]
59
+ });
60
+ getConnectedClientSpy.mockResolvedValue({ callTool });
61
+ const tool = (0, import_exa_code_search_tool.createCodeSearchTool)();
62
+ const logger = createMockLogger();
63
+ const context = createToolContext(logger);
64
+ const input = tool.inputSchema.parse({ query: "react useState", tokensNum: 2e3 });
65
+ const result = await tool.execute(input, context);
66
+ (0, import_vitest.expect)(result).toBe("code context");
67
+ (0, import_vitest.expect)(callTool).toHaveBeenCalledWith(
68
+ {
69
+ name: "get_code_context_exa",
70
+ arguments: import_vitest.expect.objectContaining({ query: "react useState", tokensNum: 2e3 })
71
+ },
72
+ void 0,
73
+ import_vitest.expect.objectContaining({ timeout: 3e4, resetTimeoutOnProgress: true })
74
+ );
75
+ });
76
+ (0, import_vitest.it)("throws ToolError.executionFailed when Exa returns an MCP error result", async () => {
77
+ const callTool = import_vitest.vi.fn().mockResolvedValue({
78
+ isError: true,
79
+ content: [{ type: "text", text: "bad input" }]
80
+ });
81
+ getConnectedClientSpy.mockResolvedValue({ callTool });
82
+ const tool = (0, import_exa_web_search_tool.createWebSearchTool)();
83
+ const logger = createMockLogger();
84
+ const context = createToolContext(logger);
85
+ const input = tool.inputSchema.parse({ query: "x" });
86
+ await (0, import_vitest.expect)(tool.execute(input, context)).rejects.toMatchObject({
87
+ name: "DextoRuntimeError",
88
+ code: "tools_execution_failed"
89
+ });
90
+ });
91
+ });
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=exa-tools.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exa-tools.test.d.ts","sourceRoot":"","sources":["../../src/implementations/exa-tools.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,90 @@
1
+ import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
2
+ import { DextoMcpClient } from "@dexto/core";
3
+ import { createWebSearchTool } from "./exa-web-search-tool.js";
4
+ import { createCodeSearchTool } from "./exa-code-search-tool.js";
5
+ function createMockLogger() {
6
+ const logger = {
7
+ debug: vi.fn(),
8
+ silly: vi.fn(),
9
+ info: vi.fn(),
10
+ warn: vi.fn(),
11
+ error: vi.fn(),
12
+ trackException: vi.fn(),
13
+ createChild: vi.fn(() => logger),
14
+ setLevel: vi.fn(),
15
+ getLevel: vi.fn(() => "debug"),
16
+ getLogFilePath: vi.fn(() => null),
17
+ destroy: vi.fn(async () => void 0)
18
+ };
19
+ return logger;
20
+ }
21
+ function createToolContext(logger) {
22
+ return { logger };
23
+ }
24
+ describe("Exa built-in tools", () => {
25
+ const connectSpy = vi.spyOn(DextoMcpClient.prototype, "connect");
26
+ const disconnectSpy = vi.spyOn(DextoMcpClient.prototype, "disconnect");
27
+ const getConnectedClientSpy = vi.spyOn(DextoMcpClient.prototype, "getConnectedClient");
28
+ beforeEach(() => {
29
+ connectSpy.mockResolvedValue({});
30
+ disconnectSpy.mockResolvedValue();
31
+ });
32
+ afterEach(() => {
33
+ vi.resetAllMocks();
34
+ });
35
+ it("web_search calls web_search_exa and returns the first text content", async () => {
36
+ const callTool = vi.fn().mockResolvedValue({
37
+ content: [{ type: "text", text: "hello from exa" }]
38
+ });
39
+ getConnectedClientSpy.mockResolvedValue({ callTool });
40
+ const tool = createWebSearchTool();
41
+ const logger = createMockLogger();
42
+ const context = createToolContext(logger);
43
+ const input = tool.inputSchema.parse({ query: "what is dexto" });
44
+ const result = await tool.execute(input, context);
45
+ expect(result).toBe("hello from exa");
46
+ expect(callTool).toHaveBeenCalledWith(
47
+ {
48
+ name: "web_search_exa",
49
+ arguments: expect.objectContaining({ query: "what is dexto" })
50
+ },
51
+ void 0,
52
+ expect.objectContaining({ timeout: 25e3, resetTimeoutOnProgress: true })
53
+ );
54
+ });
55
+ it("code_search calls get_code_context_exa and returns the first text content", async () => {
56
+ const callTool = vi.fn().mockResolvedValue({
57
+ content: [{ type: "text", text: "code context" }]
58
+ });
59
+ getConnectedClientSpy.mockResolvedValue({ callTool });
60
+ const tool = createCodeSearchTool();
61
+ const logger = createMockLogger();
62
+ const context = createToolContext(logger);
63
+ const input = tool.inputSchema.parse({ query: "react useState", tokensNum: 2e3 });
64
+ const result = await tool.execute(input, context);
65
+ expect(result).toBe("code context");
66
+ expect(callTool).toHaveBeenCalledWith(
67
+ {
68
+ name: "get_code_context_exa",
69
+ arguments: expect.objectContaining({ query: "react useState", tokensNum: 2e3 })
70
+ },
71
+ void 0,
72
+ expect.objectContaining({ timeout: 3e4, resetTimeoutOnProgress: true })
73
+ );
74
+ });
75
+ it("throws ToolError.executionFailed when Exa returns an MCP error result", async () => {
76
+ const callTool = vi.fn().mockResolvedValue({
77
+ isError: true,
78
+ content: [{ type: "text", text: "bad input" }]
79
+ });
80
+ getConnectedClientSpy.mockResolvedValue({ callTool });
81
+ const tool = createWebSearchTool();
82
+ const logger = createMockLogger();
83
+ const context = createToolContext(logger);
84
+ const input = tool.inputSchema.parse({ query: "x" });
85
+ await expect(tool.execute(input, context)).rejects.toMatchObject({
86
+ name: "DextoRuntimeError",
87
+ code: "tools_execution_failed"
88
+ });
89
+ });
90
+ });
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var exa_web_search_tool_exports = {};
20
+ __export(exa_web_search_tool_exports, {
21
+ createWebSearchTool: () => createWebSearchTool
22
+ });
23
+ module.exports = __toCommonJS(exa_web_search_tool_exports);
24
+ var import_zod = require("zod");
25
+ var import_core = require("@dexto/core");
26
+ var import_exa_mcp = require("./exa-mcp.js");
27
+ const WebSearchInputSchema = import_zod.z.object({
28
+ query: import_zod.z.string().min(1).describe("Web search query"),
29
+ numResults: import_zod.z.number().int().positive().optional().default(8).describe("Number of results to return (default: 8)"),
30
+ livecrawl: import_zod.z.enum(["fallback", "preferred"]).optional().default("fallback").describe(
31
+ "Live crawl mode - 'fallback' uses cached content when available, 'preferred' prioritizes live crawling"
32
+ ),
33
+ type: import_zod.z.enum(["auto", "fast"]).optional().default("auto").describe("Search type - 'auto' (default) or 'fast'"),
34
+ contextMaxCharacters: import_zod.z.number().int().positive().optional().default(1e4).describe("Maximum context length in characters (default: 10000)")
35
+ }).strict();
36
+ function createWebSearchTool() {
37
+ return (0, import_core.defineTool)({
38
+ id: "web_search",
39
+ displayName: "Web Search",
40
+ description: "Search the web for current information and return clean, ready-to-use text. Use for news, facts, and up-to-date context.",
41
+ inputSchema: WebSearchInputSchema,
42
+ async execute(input, context) {
43
+ const { query, numResults, livecrawl, type, contextMaxCharacters } = input;
44
+ return await (0, import_exa_mcp.callExaTool)({
45
+ logger: context.logger,
46
+ toolId: "web_search",
47
+ toolName: "web_search_exa",
48
+ args: {
49
+ query,
50
+ numResults,
51
+ livecrawl,
52
+ type,
53
+ contextMaxCharacters
54
+ },
55
+ timeoutMs: 25e3
56
+ });
57
+ }
58
+ });
59
+ }
60
+ // Annotate the CommonJS export names for ESM import in node:
61
+ 0 && (module.exports = {
62
+ createWebSearchTool
63
+ });
@@ -0,0 +1,30 @@
1
+ import { z } from 'zod';
2
+ import { Tool } from '@dexto/core';
3
+
4
+ declare const WebSearchInputSchema: z.ZodObject<{
5
+ query: z.ZodString;
6
+ numResults: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
7
+ livecrawl: z.ZodDefault<z.ZodOptional<z.ZodEnum<["fallback", "preferred"]>>>;
8
+ type: z.ZodDefault<z.ZodOptional<z.ZodEnum<["auto", "fast"]>>>;
9
+ contextMaxCharacters: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
10
+ }, "strict", z.ZodTypeAny, {
11
+ type: "auto" | "fast";
12
+ query: string;
13
+ numResults: number;
14
+ livecrawl: "fallback" | "preferred";
15
+ contextMaxCharacters: number;
16
+ }, {
17
+ query: string;
18
+ type?: "auto" | "fast" | undefined;
19
+ numResults?: number | undefined;
20
+ livecrawl?: "fallback" | "preferred" | undefined;
21
+ contextMaxCharacters?: number | undefined;
22
+ }>;
23
+ /**
24
+ * Create the `web_search` tool.
25
+ *
26
+ * Performs a web search by calling Exa's MCP endpoint via the MCP SDK.
27
+ */
28
+ declare function createWebSearchTool(): Tool<typeof WebSearchInputSchema>;
29
+
30
+ export { createWebSearchTool };
@@ -0,0 +1,29 @@
1
+ import { z } from 'zod';
2
+ import type { Tool } from '@dexto/core';
3
+ declare const WebSearchInputSchema: z.ZodObject<{
4
+ query: z.ZodString;
5
+ numResults: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
6
+ livecrawl: z.ZodDefault<z.ZodOptional<z.ZodEnum<["fallback", "preferred"]>>>;
7
+ type: z.ZodDefault<z.ZodOptional<z.ZodEnum<["auto", "fast"]>>>;
8
+ contextMaxCharacters: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
9
+ }, "strict", z.ZodTypeAny, {
10
+ type: "auto" | "fast";
11
+ query: string;
12
+ numResults: number;
13
+ livecrawl: "fallback" | "preferred";
14
+ contextMaxCharacters: number;
15
+ }, {
16
+ query: string;
17
+ type?: "auto" | "fast" | undefined;
18
+ numResults?: number | undefined;
19
+ livecrawl?: "fallback" | "preferred" | undefined;
20
+ contextMaxCharacters?: number | undefined;
21
+ }>;
22
+ /**
23
+ * Create the `web_search` tool.
24
+ *
25
+ * Performs a web search by calling Exa's MCP endpoint via the MCP SDK.
26
+ */
27
+ export declare function createWebSearchTool(): Tool<typeof WebSearchInputSchema>;
28
+ export {};
29
+ //# sourceMappingURL=exa-web-search-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exa-web-search-tool.d.ts","sourceRoot":"","sources":["../../src/implementations/exa-web-search-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAG9D,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;EA8Bb,CAAC;AACd;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAAC,OAAO,oBAAoB,CAAC,CAyBvE"}
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ import { defineTool } from "@dexto/core";
3
+ import { callExaTool } from "./exa-mcp.js";
4
+ const WebSearchInputSchema = z.object({
5
+ query: z.string().min(1).describe("Web search query"),
6
+ numResults: z.number().int().positive().optional().default(8).describe("Number of results to return (default: 8)"),
7
+ livecrawl: z.enum(["fallback", "preferred"]).optional().default("fallback").describe(
8
+ "Live crawl mode - 'fallback' uses cached content when available, 'preferred' prioritizes live crawling"
9
+ ),
10
+ type: z.enum(["auto", "fast"]).optional().default("auto").describe("Search type - 'auto' (default) or 'fast'"),
11
+ contextMaxCharacters: z.number().int().positive().optional().default(1e4).describe("Maximum context length in characters (default: 10000)")
12
+ }).strict();
13
+ function createWebSearchTool() {
14
+ return defineTool({
15
+ id: "web_search",
16
+ displayName: "Web Search",
17
+ description: "Search the web for current information and return clean, ready-to-use text. Use for news, facts, and up-to-date context.",
18
+ inputSchema: WebSearchInputSchema,
19
+ async execute(input, context) {
20
+ const { query, numResults, livecrawl, type, contextMaxCharacters } = input;
21
+ return await callExaTool({
22
+ logger: context.logger,
23
+ toolId: "web_search",
24
+ toolName: "web_search_exa",
25
+ args: {
26
+ query,
27
+ numResults,
28
+ livecrawl,
29
+ type,
30
+ contextMaxCharacters
31
+ },
32
+ timeoutMs: 25e3
33
+ });
34
+ }
35
+ });
36
+ }
37
+ export {
38
+ createWebSearchTool
39
+ };