bigtool-ts 0.1.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 (116) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +21 -0
  3. package/README.md +641 -0
  4. package/dist/adapters/agent-protocol.d.ts +149 -0
  5. package/dist/adapters/agent-protocol.d.ts.map +1 -0
  6. package/dist/adapters/agent-protocol.js +133 -0
  7. package/dist/adapters/agent-protocol.js.map +1 -0
  8. package/dist/adapters/index.d.ts +39 -0
  9. package/dist/adapters/index.d.ts.map +1 -0
  10. package/dist/adapters/index.js +42 -0
  11. package/dist/adapters/index.js.map +1 -0
  12. package/dist/adapters/inngest.d.ts +234 -0
  13. package/dist/adapters/inngest.d.ts.map +1 -0
  14. package/dist/adapters/inngest.js +276 -0
  15. package/dist/adapters/inngest.js.map +1 -0
  16. package/dist/adapters/mastra.d.ts +201 -0
  17. package/dist/adapters/mastra.d.ts.map +1 -0
  18. package/dist/adapters/mastra.js +250 -0
  19. package/dist/adapters/mastra.js.map +1 -0
  20. package/dist/adapters/types.d.ts +42 -0
  21. package/dist/adapters/types.d.ts.map +1 -0
  22. package/dist/adapters/types.js +6 -0
  23. package/dist/adapters/types.js.map +1 -0
  24. package/dist/adapters/vercel-ai.d.ts +176 -0
  25. package/dist/adapters/vercel-ai.d.ts.map +1 -0
  26. package/dist/adapters/vercel-ai.js +244 -0
  27. package/dist/adapters/vercel-ai.js.map +1 -0
  28. package/dist/catalog/index.d.ts +177 -0
  29. package/dist/catalog/index.d.ts.map +1 -0
  30. package/dist/catalog/index.js +244 -0
  31. package/dist/catalog/index.js.map +1 -0
  32. package/dist/graph/agent.d.ts +214 -0
  33. package/dist/graph/agent.d.ts.map +1 -0
  34. package/dist/graph/agent.js +196 -0
  35. package/dist/graph/agent.js.map +1 -0
  36. package/dist/graph/index.d.ts +5 -0
  37. package/dist/graph/index.d.ts.map +1 -0
  38. package/dist/graph/index.js +4 -0
  39. package/dist/graph/index.js.map +1 -0
  40. package/dist/graph/nodes.d.ts +100 -0
  41. package/dist/graph/nodes.d.ts.map +1 -0
  42. package/dist/graph/nodes.js +190 -0
  43. package/dist/graph/nodes.js.map +1 -0
  44. package/dist/graph/search-tool.d.ts +34 -0
  45. package/dist/graph/search-tool.d.ts.map +1 -0
  46. package/dist/graph/search-tool.js +54 -0
  47. package/dist/graph/search-tool.js.map +1 -0
  48. package/dist/graph/state.d.ts +26 -0
  49. package/dist/graph/state.d.ts.map +1 -0
  50. package/dist/graph/state.js +29 -0
  51. package/dist/graph/state.js.map +1 -0
  52. package/dist/index.d.ts +69 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +85 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/loader/index.d.ts +172 -0
  57. package/dist/loader/index.d.ts.map +1 -0
  58. package/dist/loader/index.js +179 -0
  59. package/dist/loader/index.js.map +1 -0
  60. package/dist/loader/loader.d.ts +114 -0
  61. package/dist/loader/loader.d.ts.map +1 -0
  62. package/dist/loader/loader.js +185 -0
  63. package/dist/loader/loader.js.map +1 -0
  64. package/dist/search/cache.d.ts +76 -0
  65. package/dist/search/cache.d.ts.map +1 -0
  66. package/dist/search/cache.js +135 -0
  67. package/dist/search/cache.js.map +1 -0
  68. package/dist/search/index.d.ts +63 -0
  69. package/dist/search/index.d.ts.map +1 -0
  70. package/dist/search/index.js +122 -0
  71. package/dist/search/index.js.map +1 -0
  72. package/dist/search/normalize.d.ts +104 -0
  73. package/dist/search/normalize.d.ts.map +1 -0
  74. package/dist/search/normalize.js +211 -0
  75. package/dist/search/normalize.js.map +1 -0
  76. package/dist/search/orama.d.ts +256 -0
  77. package/dist/search/orama.d.ts.map +1 -0
  78. package/dist/search/orama.js +511 -0
  79. package/dist/search/orama.js.map +1 -0
  80. package/dist/search/types.d.ts +96 -0
  81. package/dist/search/types.d.ts.map +1 -0
  82. package/dist/search/types.js +8 -0
  83. package/dist/search/types.js.map +1 -0
  84. package/dist/sources/dynamic.d.ts +200 -0
  85. package/dist/sources/dynamic.d.ts.map +1 -0
  86. package/dist/sources/dynamic.js +194 -0
  87. package/dist/sources/dynamic.js.map +1 -0
  88. package/dist/sources/index.d.ts +11 -0
  89. package/dist/sources/index.d.ts.map +1 -0
  90. package/dist/sources/index.js +14 -0
  91. package/dist/sources/index.js.map +1 -0
  92. package/dist/sources/local.d.ts +128 -0
  93. package/dist/sources/local.d.ts.map +1 -0
  94. package/dist/sources/local.js +155 -0
  95. package/dist/sources/local.js.map +1 -0
  96. package/dist/sources/mcp.d.ts +438 -0
  97. package/dist/sources/mcp.d.ts.map +1 -0
  98. package/dist/sources/mcp.js +438 -0
  99. package/dist/sources/mcp.js.map +1 -0
  100. package/dist/sources/types.d.ts +16 -0
  101. package/dist/sources/types.d.ts.map +1 -0
  102. package/dist/sources/types.js +7 -0
  103. package/dist/sources/types.js.map +1 -0
  104. package/dist/sources/with-metadata.d.ts +7 -0
  105. package/dist/sources/with-metadata.d.ts.map +1 -0
  106. package/dist/sources/with-metadata.js +7 -0
  107. package/dist/sources/with-metadata.js.map +1 -0
  108. package/dist/types/index.d.ts +7 -0
  109. package/dist/types/index.d.ts.map +1 -0
  110. package/dist/types/index.js +8 -0
  111. package/dist/types/index.js.map +1 -0
  112. package/dist/types.d.ts +700 -0
  113. package/dist/types.d.ts.map +1 -0
  114. package/dist/types.js +97 -0
  115. package/dist/types.js.map +1 -0
  116. package/package.json +118 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-01-19
11
+
12
+ ### Added
13
+
14
+ - Initial release of bigtool-ts
15
+ - `ToolCatalog` for managing tool collections with semantic search
16
+ - `ToolLoader` for lazy-loading tools on demand
17
+ - Orama-powered semantic search with caching
18
+ - Tool sources:
19
+ - `LocalToolSource` for statically defined tools
20
+ - `MCPToolSource` for Model Context Protocol servers
21
+ - `DynamicToolSource` for runtime-generated tools
22
+ - `withMetadata` wrapper for enriching tool metadata
23
+ - LangGraph integration:
24
+ - `createBigToolAgent` for building tool-discovery agents
25
+ - Pre-built graph nodes for search and execution
26
+ - TypeScript-first with full type inference
27
+ - Comprehensive test coverage
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Assistant UI Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,641 @@
1
+ # bigtool-ts
2
+
3
+ Dynamic tool discovery for AI agents. Search and load tools on-demand instead of loading all tools upfront.
4
+
5
+ Works with **LangGraph**, **Inngest AgentKit**, **Vercel AI SDK**, **Mastra**, and **Agent Protocol**.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/bigtool-ts)](https://www.npmjs.com/package/bigtool-ts)
8
+ [![license](https://img.shields.io/npm/l/bigtool-ts)](https://github.com/wi-ski/bigtool-ts/blob/main/LICENSE)
9
+
10
+ ## Table of Contents
11
+
12
+ - [The Problem](#the-problem)
13
+ - [Quick Start](#quick-start)
14
+ - [Framework Integrations](#framework-integrations)
15
+ - [LangGraph](#langgraph)
16
+ - [Inngest AgentKit](#inngest-agentkit)
17
+ - [Vercel AI SDK](#vercel-ai-sdk)
18
+ - [Mastra](#mastra)
19
+ - [Agent Protocol](#agent-protocol)
20
+ - [Features](#features)
21
+ - [API Reference](#api-reference)
22
+ - [createAgent](#createagentoptions)
23
+ - [LocalSource](#localsource)
24
+ - [MCPSource / createMCPSource](#mcpsource--createmcpsource)
25
+ - [DynamicSource](#dynamicsource)
26
+ - [withMetadata](#withmetadatatool-enhancement)
27
+ - [OramaSearch](#oramasearch)
28
+ - [Advanced Usage](#advanced-usage)
29
+ - [Architecture](#architecture)
30
+
31
+ ## The Problem
32
+
33
+ When you have 100s or 1000s of tools, loading them all into context explodes the context window and degrades LLM performance. The model struggles to pick the right tool when presented with too many options.
34
+
35
+ **bigtool-ts** solves this by giving your agent a `search_tools` capabilityβ€”it searches for relevant tools, loads only what it needs, and keeps context lean.
36
+
37
+ ## Quick Start
38
+
39
+ ```bash
40
+ pnpm add bigtool-ts
41
+ ```
42
+
43
+ ```typescript
44
+ import { createAgent, LocalSource, OramaSearch } from "bigtool-ts";
45
+ import { ChatOpenAI } from "@langchain/openai";
46
+
47
+ const agent = await createAgent({
48
+ llm: new ChatOpenAI({ model: "gpt-4o" }),
49
+ tools: [myCalculatorTool, myDatabaseTool, myGitHubTool],
50
+ search: new OramaSearch({ mode: "bm25" }),
51
+ });
52
+
53
+ const result = await agent.invoke({
54
+ messages: [{ role: "user", content: "Create a GitHub PR for this change" }],
55
+ });
56
+ ```
57
+
58
+ ## Features
59
+
60
+ - **Dynamic Discovery** β€” Agent searches for tools by natural language query
61
+ - **Lazy Loading** β€” Tools loaded on-demand with LRU caching
62
+ - **Hybrid Search** β€” BM25 text search, vector semantic search, or both
63
+ - **Multiple Sources** β€” Local tools, MCP servers, or custom loaders
64
+ - **Zero Config Search** β€” BM25 mode works out of the box, no API keys needed
65
+ - **Multi-Framework** β€” Works with LangGraph, Inngest, Vercel AI, Mastra, and more
66
+
67
+ ---
68
+
69
+ ## Framework Integrations
70
+
71
+ bigtool-ts works with multiple AI agent frameworks. Choose your framework below for a quick-start snippet.
72
+
73
+ ### LangGraph
74
+
75
+ Native integration via `createAgent()`. Returns a compiled StateGraph.
76
+
77
+ ```typescript
78
+ import { createAgent, LocalSource, OramaSearch } from "bigtool-ts";
79
+ import { ChatOpenAI } from "@langchain/openai";
80
+
81
+ const agent = await createAgent({
82
+ llm: new ChatOpenAI({ model: "gpt-4o" }),
83
+ tools: [myCalculatorTool, myDatabaseTool, myGitHubTool],
84
+ search: new OramaSearch({ mode: "bm25" }),
85
+ });
86
+
87
+ const result = await agent.invoke({
88
+ messages: [{ role: "user", content: "Create a GitHub PR" }],
89
+ });
90
+ ```
91
+
92
+ ### Inngest AgentKit
93
+
94
+ Use the adapter with Inngest's multi-agent orchestration framework.
95
+
96
+ ```bash
97
+ pnpm add @inngest/agent-kit
98
+ ```
99
+
100
+ ```typescript
101
+ import { createAgent, openai } from "@inngest/agent-kit";
102
+ import {
103
+ createInngestAdapter,
104
+ DefaultToolCatalog,
105
+ DefaultToolLoader,
106
+ OramaSearch,
107
+ LocalSource,
108
+ } from "bigtool-ts";
109
+
110
+ // Setup bigtool-ts components
111
+ const catalog = new DefaultToolCatalog();
112
+ await catalog.register(new LocalSource(myTools));
113
+
114
+ const search = new OramaSearch({ mode: "bm25" });
115
+ await search.index(catalog.getAllMetadata());
116
+
117
+ const loader = new DefaultToolLoader(catalog);
118
+
119
+ // Create adapter
120
+ const adapter = createInngestAdapter({ catalog, loader, searchIndex: search });
121
+
122
+ // Use with Inngest agent
123
+ const agent = createAgent({
124
+ name: "tool-user",
125
+ model: openai({ model: "gpt-4o" }),
126
+ tools: [
127
+ adapter.createSearchTool(), // Search for tools
128
+ adapter.createCallToolTool(), // Execute found tools
129
+ ],
130
+ });
131
+ ```
132
+
133
+ ### Vercel AI SDK
134
+
135
+ Use the adapter with Vercel's AI SDK for `generateText()` and `streamText()`.
136
+
137
+ ```bash
138
+ pnpm add ai @ai-sdk/openai
139
+ ```
140
+
141
+ ```typescript
142
+ import { generateText } from "ai";
143
+ import { openai } from "@ai-sdk/openai";
144
+ import {
145
+ createVercelAdapter,
146
+ DefaultToolCatalog,
147
+ DefaultToolLoader,
148
+ OramaSearch,
149
+ LocalSource,
150
+ } from "bigtool-ts";
151
+
152
+ // Setup bigtool-ts components
153
+ const catalog = new DefaultToolCatalog();
154
+ await catalog.register(new LocalSource(myTools));
155
+
156
+ const search = new OramaSearch({ mode: "bm25" });
157
+ await search.index(catalog.getAllMetadata());
158
+
159
+ const loader = new DefaultToolLoader(catalog);
160
+
161
+ // Create adapter
162
+ const adapter = createVercelAdapter({ catalog, loader, searchIndex: search });
163
+
164
+ // Use with Vercel AI SDK
165
+ const result = await generateText({
166
+ model: openai("gpt-4o"),
167
+ tools: {
168
+ search_tools: adapter.createSearchTool(),
169
+ ...await adapter.getToolsAsRecord(["github:create_pr", "slack:send"]),
170
+ },
171
+ prompt: "Create a PR and notify the team on Slack",
172
+ });
173
+ ```
174
+
175
+ ### Mastra
176
+
177
+ Use the adapter with Mastra's AI-native TypeScript framework.
178
+
179
+ ```bash
180
+ pnpm add @mastra/core
181
+ ```
182
+
183
+ ```typescript
184
+ import { Agent } from "@mastra/core/agent";
185
+ import {
186
+ createMastraAdapter,
187
+ DefaultToolCatalog,
188
+ DefaultToolLoader,
189
+ OramaSearch,
190
+ LocalSource,
191
+ } from "bigtool-ts";
192
+
193
+ // Setup bigtool-ts components
194
+ const catalog = new DefaultToolCatalog();
195
+ await catalog.register(new LocalSource(myTools));
196
+
197
+ const search = new OramaSearch({ mode: "bm25" });
198
+ await search.index(catalog.getAllMetadata());
199
+
200
+ const loader = new DefaultToolLoader(catalog);
201
+
202
+ // Create adapter
203
+ const adapter = createMastraAdapter({ catalog, loader, searchIndex: search });
204
+
205
+ // Use with Mastra Agent
206
+ const agent = new Agent({
207
+ id: "my-agent",
208
+ name: "Tool User",
209
+ model: "openai/gpt-4o",
210
+ tools: {
211
+ search_tools: adapter.createSearchTool(),
212
+ ...await adapter.getToolsAsRecord(["github:create_pr"]),
213
+ },
214
+ });
215
+ ```
216
+
217
+ ### Agent Protocol
218
+
219
+ Expose tools via the Agent Protocol REST API specification.
220
+
221
+ ```typescript
222
+ import {
223
+ createAgentProtocolHandler,
224
+ DefaultToolCatalog,
225
+ DefaultToolLoader,
226
+ OramaSearch,
227
+ LocalSource,
228
+ } from "bigtool-ts";
229
+
230
+ // Setup bigtool-ts components
231
+ const catalog = new DefaultToolCatalog();
232
+ await catalog.register(new LocalSource(myTools));
233
+
234
+ const search = new OramaSearch({ mode: "bm25" });
235
+ await search.index(catalog.getAllMetadata());
236
+
237
+ const loader = new DefaultToolLoader(catalog);
238
+
239
+ // Create handler
240
+ const handler = createAgentProtocolHandler({ catalog, loader, searchIndex: search });
241
+
242
+ // Use with Express/Fastify/any HTTP framework
243
+ app.get("/tools", async (req, res) => {
244
+ const tools = await handler.listTools();
245
+ res.json({ tools });
246
+ });
247
+
248
+ app.post("/tools/search", async (req, res) => {
249
+ const tools = await handler.searchTools(req.body.query);
250
+ res.json({ tools });
251
+ });
252
+
253
+ app.post("/tools/execute", async (req, res) => {
254
+ const result = await handler.executeTool(req.body.name, req.body.args);
255
+ res.json(result);
256
+ });
257
+ ```
258
+
259
+ ---
260
+
261
+ ## API Reference
262
+
263
+ ### `createAgent(options)`
264
+
265
+ Creates a LangGraph agent with dynamic tool discovery.
266
+
267
+ ```typescript
268
+ import { createAgent, OramaSearch } from "bigtool-ts";
269
+
270
+ const agent = await createAgent({
271
+ // Required
272
+ llm: new ChatOpenAI({ model: "gpt-4o" }),
273
+ search: new OramaSearch({ mode: "bm25" }),
274
+
275
+ // Tool sources (pick one or combine)
276
+ tools: [tool1, tool2], // Array of StructuredTool
277
+ sources: [localSource, mcpSource], // Array of ToolSource
278
+
279
+ // Optional
280
+ pinnedTools: [alwaysAvailableTool], // Always in context
281
+ systemPrompt: "You are a helpful assistant.",
282
+ searchLimit: 5, // Max tools per search (default: 5)
283
+ cacheSize: 100, // LRU cache size (default: 100)
284
+ });
285
+
286
+ // Use like any LangGraph agent
287
+ const result = await agent.invoke({
288
+ messages: [{ role: "user", content: "..." }],
289
+ });
290
+ ```
291
+
292
+ ### `LocalSource`
293
+
294
+ Wraps in-memory StructuredTool instances.
295
+
296
+ ```typescript
297
+ import { LocalSource } from "bigtool-ts";
298
+
299
+ const source = new LocalSource([
300
+ myCalculatorTool,
301
+ myDatabaseTool,
302
+ myGitHubTool,
303
+ ]);
304
+
305
+ // Custom namespace (default: "local")
306
+ const namedSource = new LocalSource(tools, "my-tools");
307
+ ```
308
+
309
+ ### `MCPSource` / `createMCPSource`
310
+
311
+ Connects to an MCP (Model Context Protocol) server. Two patterns supported:
312
+
313
+ #### Config-based (Recommended)
314
+
315
+ Pass a configuration object and let bigtool-ts manage the connection:
316
+
317
+ ```typescript
318
+ import { createMCPSource } from "bigtool-ts";
319
+
320
+ // stdio transport (local MCP server)
321
+ const githubSource = await createMCPSource({
322
+ name: "github",
323
+ transport: "stdio",
324
+ command: "npx",
325
+ args: ["-y", "@modelcontextprotocol/server-github"],
326
+ env: { GITHUB_TOKEN: process.env.GITHUB_TOKEN },
327
+ });
328
+
329
+ // SSE transport (remote MCP server)
330
+ const remoteSource = await createMCPSource({
331
+ name: "remote-tools",
332
+ transport: "sse",
333
+ url: "https://mcp.example.com/sse",
334
+ headers: { Authorization: `Bearer ${apiKey}` },
335
+ });
336
+
337
+ // Register with catalog
338
+ await catalog.register(githubSource);
339
+ await catalog.register(remoteSource);
340
+ ```
341
+
342
+ #### Pre-connected Client
343
+
344
+ Pass an already-connected MCP client:
345
+
346
+ ```typescript
347
+ import { MCPSource } from "bigtool-ts";
348
+ import { Client } from "@modelcontextprotocol/sdk/client";
349
+
350
+ const mcpClient = new Client({ name: "my-client" });
351
+ await mcpClient.connect(transport);
352
+
353
+ const source = new MCPSource(mcpClient, {
354
+ namespace: "github", // Tool ID prefix (default: client.name)
355
+ refreshInterval: 60_000, // Re-fetch tool list every 60s
356
+ });
357
+
358
+ // Listen for tool list changes
359
+ source.onRefresh.on((metadata) => {
360
+ console.log("Tools refreshed:", metadata.length);
361
+ });
362
+
363
+ // Cleanup
364
+ source.dispose();
365
+ ```
366
+
367
+ #### Loading 100 MCP Servers
368
+
369
+ ```typescript
370
+ import { createMCPSource } from "bigtool-ts";
371
+
372
+ const mcpConfigs = [
373
+ { name: "github", transport: "stdio", command: "npx", args: ["-y", "@mcp/server-github"] },
374
+ { name: "slack", transport: "stdio", command: "npx", args: ["-y", "@mcp/server-slack"] },
375
+ { name: "notion", transport: "stdio", command: "npx", args: ["-y", "@mcp/server-notion"] },
376
+ // ... 97 more
377
+ ];
378
+
379
+ // Connect to all servers in parallel
380
+ const sources = await Promise.all(
381
+ mcpConfigs.map(config => createMCPSource(config))
382
+ );
383
+
384
+ // Register all
385
+ for (const source of sources) {
386
+ await catalog.register(source);
387
+ }
388
+ ```
389
+
390
+ ### `DynamicSource`
391
+
392
+ Lazy-loads tools on demand via a custom loader function.
393
+
394
+ ```typescript
395
+ import { DynamicSource } from "bigtool-ts";
396
+
397
+ const source = new DynamicSource({
398
+ // Metadata provided upfront for search indexing
399
+ metadata: [
400
+ { id: "send_email", name: "send_email", description: "Send an email" },
401
+ { id: "create_task", name: "create_task", description: "Create a task" },
402
+ ],
403
+ // Loader called when tool is actually needed
404
+ loader: async (id) => {
405
+ const module = await import(`./tools/${id}.js`);
406
+ return module.default;
407
+ },
408
+ });
409
+ ```
410
+
411
+ ### `withMetadata(tool, enhancement)`
412
+
413
+ Add search metadata to improve tool discovery.
414
+
415
+ ```typescript
416
+ import { withMetadata } from "bigtool-ts";
417
+
418
+ const enhanced = withMetadata(myGitHubTool, {
419
+ categories: ["github", "git", "version-control"],
420
+ keywords: ["PR", "pull request", "merge", "branch"],
421
+ });
422
+
423
+ // Use enhanced tools in LocalSource
424
+ const source = new LocalSource([enhanced, otherTool]);
425
+ ```
426
+
427
+ ### `OramaSearch`
428
+
429
+ Search index powered by [@orama/orama](https://oramasearch.com/). Supports three modes for different use cases.
430
+
431
+ #### BM25 Mode (Default)
432
+
433
+ Fast keyword-based search. **No API keys needed.**
434
+
435
+ ```typescript
436
+ import { OramaSearch } from "bigtool-ts";
437
+
438
+ const search = new OramaSearch({ mode: "bm25" });
439
+
440
+ // Or with field boosting
441
+ const search = new OramaSearch({
442
+ mode: "bm25",
443
+ boost: {
444
+ name: 2, // Tool name matches worth 2x
445
+ keywords: 1.5, // Keyword matches worth 1.5x
446
+ description: 1, // Normal weight
447
+ categories: 1, // Normal weight
448
+ },
449
+ });
450
+ ```
451
+
452
+ **When to use:** Most cases. Fast, deterministic, works offline.
453
+
454
+ #### Vector Mode
455
+
456
+ Semantic search using embeddings. Finds conceptually similar tools even without exact keyword matches.
457
+
458
+ ```typescript
459
+ import { OramaSearch } from "bigtool-ts";
460
+ import { OpenAIEmbeddings } from "@langchain/openai";
461
+
462
+ const search = new OramaSearch({
463
+ mode: "vector",
464
+ embeddings: new OpenAIEmbeddings(),
465
+ });
466
+ ```
467
+
468
+ **When to use:** When users describe tools conceptually (e.g., "something to post on social media" β†’ finds `twitter_post`, `linkedin_share`).
469
+
470
+ #### Hybrid Mode
471
+
472
+ Combines BM25 and vector search for best of both worlds.
473
+
474
+ ```typescript
475
+ import { OramaSearch } from "bigtool-ts";
476
+ import { OpenAIEmbeddings } from "@langchain/openai";
477
+
478
+ const search = new OramaSearch({
479
+ mode: "hybrid",
480
+ embeddings: new OpenAIEmbeddings(),
481
+ weights: {
482
+ bm25: 0.4, // 40% keyword matching
483
+ vector: 0.6, // 60% semantic similarity
484
+ },
485
+ boost: {
486
+ name: 2,
487
+ keywords: 1.5,
488
+ description: 1,
489
+ categories: 1,
490
+ },
491
+ });
492
+ ```
493
+
494
+ **When to use:** Large tool catalogs where both exact matches and semantic similarity matter.
495
+
496
+ #### Search Mode Comparison
497
+
498
+ | Mode | Speed | API Keys | Best For |
499
+ |------|-------|----------|----------|
500
+ | `bm25` | ⚑ Fast | None | Exact matches, offline use |
501
+ | `vector` | 🐒 Slower | Required | Semantic similarity |
502
+ | `hybrid` | 🐒 Slower | Required | Large catalogs, mixed queries |
503
+
504
+ #### Search Options
505
+
506
+ ```typescript
507
+ const results = await search.search("create github pr", {
508
+ limit: 10, // Max results (default: 5)
509
+ threshold: 0.3, // Min score 0-1 (default: 0)
510
+ categories: ["git"], // Filter by category
511
+ });
512
+
513
+ // Results format
514
+ // [{ toolId: "github:create_pr", score: 0.95, matchType: "bm25" }, ...]
515
+ ```
516
+
517
+ ## Advanced Usage
518
+
519
+ ### Combining Multiple Sources
520
+
521
+ ```typescript
522
+ import { createAgent, LocalSource, MCPSource, OramaSearch } from "bigtool-ts";
523
+
524
+ const agent = await createAgent({
525
+ llm,
526
+ sources: [
527
+ new LocalSource(coreTools, "core"),
528
+ new MCPSource(githubMcp, { namespace: "github" }),
529
+ new MCPSource(slackMcp, { namespace: "slack" }),
530
+ ],
531
+ search: new OramaSearch({ mode: "bm25" }),
532
+ });
533
+ ```
534
+
535
+ ### Pinned Tools (Always Available)
536
+
537
+ Some tools should always be in context without searching:
538
+
539
+ ```typescript
540
+ const agent = await createAgent({
541
+ llm,
542
+ tools: myLargeToolCollection,
543
+ search: new OramaSearch({ mode: "bm25" }),
544
+ pinnedTools: [
545
+ helpTool, // "Show available commands"
546
+ exitTool, // "End the conversation"
547
+ ],
548
+ });
549
+ ```
550
+
551
+ ### Custom Catalog and Loader
552
+
553
+ For advanced use cases, build components individually:
554
+
555
+ ```typescript
556
+ import {
557
+ DefaultToolCatalog,
558
+ DefaultToolLoader,
559
+ OramaSearch,
560
+ LocalSource,
561
+ } from "bigtool-ts";
562
+
563
+ // 1. Create catalog
564
+ const catalog = new DefaultToolCatalog();
565
+
566
+ // 2. Register sources
567
+ await catalog.register(new LocalSource(tools));
568
+
569
+ // 3. Create search index
570
+ const search = new OramaSearch({ mode: "bm25" });
571
+ await search.index(catalog.getAllMetadata());
572
+
573
+ // 4. Create loader with custom cache settings
574
+ const loader = new DefaultToolLoader(catalog, {
575
+ maxSize: 50,
576
+ ttl: 5 * 60 * 1000, // 5 minutes
577
+ });
578
+
579
+ // 5. Listen for catalog changes
580
+ catalog.onToolsChanged.on(({ added, removed }) => {
581
+ console.log(`Tools changed: +${added.length} -${removed.length}`);
582
+ search.reindex();
583
+ });
584
+ ```
585
+
586
+ ## Architecture
587
+
588
+ ```
589
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
590
+ β”‚ createAgent() β”‚
591
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
592
+ β”‚
593
+ β–Ό
594
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
595
+ β”‚ ToolSource[] β”‚
596
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
597
+ β”‚ β”‚ LocalSource β”‚ β”‚ MCPSource β”‚ β”‚DynamicSource β”‚ β”‚
598
+ β”‚ β”‚ (in-memory) β”‚ β”‚ (MCP server) β”‚ β”‚ (lazy load) β”‚ β”‚
599
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
600
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
601
+ β”‚
602
+ β–Ό
603
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
604
+ β”‚ DefaultToolCatalog β”‚
605
+ β”‚ Registry of all tool metadata from sources β”‚
606
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
607
+ β”‚
608
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
609
+ β–Ό β–Ό
610
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
611
+ β”‚ OramaSearch β”‚ β”‚ DefaultToolLoader β”‚
612
+ β”‚ BM25 / Vector / Hybrid β”‚ β”‚ LRU-cached loading β”‚
613
+ β”‚ β”‚ β”‚ β”‚
614
+ β”‚ query β†’ [toolId, ...] β”‚ β”‚ toolId β†’ StructuredToolβ”‚
615
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
616
+ β”‚ β”‚
617
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
618
+ β–Ό
619
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
620
+ β”‚ LangGraph StateGraph β”‚
621
+ β”‚ β”‚
622
+ β”‚ START β†’ agent β†’ route β†’ search/execute β†’ agent β†’ ... β†’ END β”‚
623
+ β”‚ β”‚
624
+ β”‚ Agent node has: β”‚
625
+ β”‚ - search_tools (always available) β”‚
626
+ β”‚ - pinnedTools (always available) β”‚
627
+ β”‚ - selectedTools (loaded after search) β”‚
628
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
629
+ ```
630
+
631
+ **Flow:**
632
+
633
+ 1. **Catalog** collects metadata from all sources
634
+ 2. **Search** indexes metadata for fast querying
635
+ 3. **Agent** calls `search_tools("github PR")` β†’ gets relevant tool IDs
636
+ 4. **Loader** loads actual tool implementations (with LRU caching)
637
+ 5. **Agent** executes tools and continues conversation
638
+
639
+ ## License
640
+
641
+ MIT