@open-mercato/ai-assistant 0.4.2-canary-c02407ff85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1090 -0
- package/README.md +607 -0
- package/build.mjs +92 -0
- package/dist/di.js +8 -0
- package/dist/di.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandFooter.js +80 -0
- package/dist/frontend/components/CommandPalette/CommandFooter.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandHeader.js +53 -0
- package/dist/frontend/components/CommandPalette/CommandHeader.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandInput.js +29 -0
- package/dist/frontend/components/CommandPalette/CommandInput.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandItem.js +92 -0
- package/dist/frontend/components/CommandPalette/CommandItem.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandPalette.js +244 -0
- package/dist/frontend/components/CommandPalette/CommandPalette.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandPaletteProvider.js +42 -0
- package/dist/frontend/components/CommandPalette/CommandPaletteProvider.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandPaletteWrapper.js +18 -0
- package/dist/frontend/components/CommandPalette/CommandPaletteWrapper.js.map +7 -0
- package/dist/frontend/components/CommandPalette/DebugPanel.js +215 -0
- package/dist/frontend/components/CommandPalette/DebugPanel.js.map +7 -0
- package/dist/frontend/components/CommandPalette/MessageBubble.js +64 -0
- package/dist/frontend/components/CommandPalette/MessageBubble.js.map +7 -0
- package/dist/frontend/components/CommandPalette/ToolCallConfirmation.js +91 -0
- package/dist/frontend/components/CommandPalette/ToolCallConfirmation.js.map +7 -0
- package/dist/frontend/components/CommandPalette/ToolCallDisplay.js +47 -0
- package/dist/frontend/components/CommandPalette/ToolCallDisplay.js.map +7 -0
- package/dist/frontend/components/CommandPalette/ToolChatPage.js +74 -0
- package/dist/frontend/components/CommandPalette/ToolChatPage.js.map +7 -0
- package/dist/frontend/components/CommandPalette/index.js +28 -0
- package/dist/frontend/components/CommandPalette/index.js.map +7 -0
- package/dist/frontend/constants.js +41 -0
- package/dist/frontend/constants.js.map +7 -0
- package/dist/frontend/hooks/index.js +13 -0
- package/dist/frontend/hooks/index.js.map +7 -0
- package/dist/frontend/hooks/useCommandPalette.js +1094 -0
- package/dist/frontend/hooks/useCommandPalette.js.map +7 -0
- package/dist/frontend/hooks/useMcpTools.js +66 -0
- package/dist/frontend/hooks/useMcpTools.js.map +7 -0
- package/dist/frontend/hooks/usePageContext.js +48 -0
- package/dist/frontend/hooks/usePageContext.js.map +7 -0
- package/dist/frontend/hooks/useRecentActions.js +56 -0
- package/dist/frontend/hooks/useRecentActions.js.map +7 -0
- package/dist/frontend/hooks/useRecentTools.js +55 -0
- package/dist/frontend/hooks/useRecentTools.js.map +7 -0
- package/dist/frontend/index.js +35 -0
- package/dist/frontend/index.js.map +7 -0
- package/dist/frontend/types.js +1 -0
- package/dist/frontend/types.js.map +7 -0
- package/dist/frontend/utils/index.js +7 -0
- package/dist/frontend/utils/index.js.map +7 -0
- package/dist/frontend/utils/toolMatcher.js +95 -0
- package/dist/frontend/utils/toolMatcher.js.map +7 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +7 -0
- package/dist/modules/ai_assistant/acl.js +14 -0
- package/dist/modules/ai_assistant/acl.js.map +7 -0
- package/dist/modules/ai_assistant/api/chat/route.js +152 -0
- package/dist/modules/ai_assistant/api/chat/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/health/route.js +27 -0
- package/dist/modules/ai_assistant/api/health/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/route/route.js +123 -0
- package/dist/modules/ai_assistant/api/route/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/settings/route.js +60 -0
- package/dist/modules/ai_assistant/api/settings/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/tools/execute/route.js +58 -0
- package/dist/modules/ai_assistant/api/tools/execute/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/tools/route.js +48 -0
- package/dist/modules/ai_assistant/api/tools/route.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js +10 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js +28 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js.map +7 -0
- package/dist/modules/ai_assistant/cli.js +192 -0
- package/dist/modules/ai_assistant/cli.js.map +7 -0
- package/dist/modules/ai_assistant/di.js +11 -0
- package/dist/modules/ai_assistant/di.js.map +7 -0
- package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js +257 -0
- package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js.map +7 -0
- package/dist/modules/ai_assistant/index.js +13 -0
- package/dist/modules/ai_assistant/index.js.map +7 -0
- package/dist/modules/ai_assistant/lib/ai-sdk.js +13 -0
- package/dist/modules/ai_assistant/lib/ai-sdk.js.map +7 -0
- package/dist/modules/ai_assistant/lib/api-discovery-tools.js +249 -0
- package/dist/modules/ai_assistant/lib/api-discovery-tools.js.map +7 -0
- package/dist/modules/ai_assistant/lib/api-endpoint-index-config.js +177 -0
- package/dist/modules/ai_assistant/lib/api-endpoint-index-config.js.map +7 -0
- package/dist/modules/ai_assistant/lib/api-endpoint-index.js +210 -0
- package/dist/modules/ai_assistant/lib/api-endpoint-index.js.map +7 -0
- package/dist/modules/ai_assistant/lib/auth.js +87 -0
- package/dist/modules/ai_assistant/lib/auth.js.map +7 -0
- package/dist/modules/ai_assistant/lib/chat-config.js +117 -0
- package/dist/modules/ai_assistant/lib/chat-config.js.map +7 -0
- package/dist/modules/ai_assistant/lib/client-factory.js +60 -0
- package/dist/modules/ai_assistant/lib/client-factory.js.map +7 -0
- package/dist/modules/ai_assistant/lib/http-server.js +367 -0
- package/dist/modules/ai_assistant/lib/http-server.js.map +7 -0
- package/dist/modules/ai_assistant/lib/in-process-client.js +126 -0
- package/dist/modules/ai_assistant/lib/in-process-client.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-client.js +146 -0
- package/dist/modules/ai_assistant/lib/mcp-client.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-dev-server.js +283 -0
- package/dist/modules/ai_assistant/lib/mcp-dev-server.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-server-config.js +160 -0
- package/dist/modules/ai_assistant/lib/mcp-server-config.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-server.js +156 -0
- package/dist/modules/ai_assistant/lib/mcp-server.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-tool-adapter.js +44 -0
- package/dist/modules/ai_assistant/lib/mcp-tool-adapter.js.map +7 -0
- package/dist/modules/ai_assistant/lib/opencode-client.js +247 -0
- package/dist/modules/ai_assistant/lib/opencode-client.js.map +7 -0
- package/dist/modules/ai_assistant/lib/opencode-handlers.js +398 -0
- package/dist/modules/ai_assistant/lib/opencode-handlers.js.map +7 -0
- package/dist/modules/ai_assistant/lib/schema-utils.js +94 -0
- package/dist/modules/ai_assistant/lib/schema-utils.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-executor.js +55 -0
- package/dist/modules/ai_assistant/lib/tool-executor.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-index-config.js +125 -0
- package/dist/modules/ai_assistant/lib/tool-index-config.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-loader.js +88 -0
- package/dist/modules/ai_assistant/lib/tool-loader.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-registry.js +65 -0
- package/dist/modules/ai_assistant/lib/tool-registry.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-search.js +192 -0
- package/dist/modules/ai_assistant/lib/tool-search.js.map +7 -0
- package/dist/modules/ai_assistant/lib/types.js +1 -0
- package/dist/modules/ai_assistant/lib/types.js.map +7 -0
- package/package.json +108 -0
- package/src/di.ts +11 -0
- package/src/frontend/components/CommandPalette/CommandFooter.tsx +113 -0
- package/src/frontend/components/CommandPalette/CommandHeader.tsx +76 -0
- package/src/frontend/components/CommandPalette/CommandInput.tsx +50 -0
- package/src/frontend/components/CommandPalette/CommandItem.tsx +111 -0
- package/src/frontend/components/CommandPalette/CommandPalette.tsx +276 -0
- package/src/frontend/components/CommandPalette/CommandPaletteProvider.tsx +60 -0
- package/src/frontend/components/CommandPalette/CommandPaletteWrapper.tsx +21 -0
- package/src/frontend/components/CommandPalette/DebugPanel.tsx +257 -0
- package/src/frontend/components/CommandPalette/MessageBubble.tsx +73 -0
- package/src/frontend/components/CommandPalette/ToolCallConfirmation.tsx +130 -0
- package/src/frontend/components/CommandPalette/ToolCallDisplay.tsx +57 -0
- package/src/frontend/components/CommandPalette/ToolChatPage.tsx +125 -0
- package/src/frontend/components/CommandPalette/index.ts +14 -0
- package/src/frontend/constants.ts +35 -0
- package/src/frontend/hooks/index.ts +5 -0
- package/src/frontend/hooks/useCommandPalette.ts +1389 -0
- package/src/frontend/hooks/useMcpTools.ts +73 -0
- package/src/frontend/hooks/usePageContext.ts +61 -0
- package/src/frontend/hooks/useRecentActions.ts +64 -0
- package/src/frontend/hooks/useRecentTools.ts +69 -0
- package/src/frontend/index.ts +39 -0
- package/src/frontend/types.ts +260 -0
- package/src/frontend/utils/index.ts +1 -0
- package/src/frontend/utils/toolMatcher.ts +127 -0
- package/src/index.ts +92 -0
- package/src/modules/ai_assistant/acl.ts +10 -0
- package/src/modules/ai_assistant/api/chat/route.ts +213 -0
- package/src/modules/ai_assistant/api/health/route.ts +30 -0
- package/src/modules/ai_assistant/api/route/route.ts +149 -0
- package/src/modules/ai_assistant/api/settings/route.ts +73 -0
- package/src/modules/ai_assistant/api/tools/execute/route.ts +71 -0
- package/src/modules/ai_assistant/api/tools/route.ts +57 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/page.meta.ts +26 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/page.tsx +12 -0
- package/src/modules/ai_assistant/cli.ts +233 -0
- package/src/modules/ai_assistant/di.ts +9 -0
- package/src/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.tsx +418 -0
- package/src/modules/ai_assistant/index.ts +11 -0
- package/src/modules/ai_assistant/lib/ai-sdk.ts +5 -0
- package/src/modules/ai_assistant/lib/api-discovery-tools.ts +334 -0
- package/src/modules/ai_assistant/lib/api-endpoint-index-config.ts +243 -0
- package/src/modules/ai_assistant/lib/api-endpoint-index.ts +381 -0
- package/src/modules/ai_assistant/lib/auth.ts +185 -0
- package/src/modules/ai_assistant/lib/chat-config.ts +152 -0
- package/src/modules/ai_assistant/lib/client-factory.ts +130 -0
- package/src/modules/ai_assistant/lib/http-server.ts +498 -0
- package/src/modules/ai_assistant/lib/in-process-client.ts +205 -0
- package/src/modules/ai_assistant/lib/mcp-client.ts +221 -0
- package/src/modules/ai_assistant/lib/mcp-dev-server.ts +373 -0
- package/src/modules/ai_assistant/lib/mcp-server-config.ts +287 -0
- package/src/modules/ai_assistant/lib/mcp-server.ts +214 -0
- package/src/modules/ai_assistant/lib/mcp-tool-adapter.ts +76 -0
- package/src/modules/ai_assistant/lib/opencode-client.ts +426 -0
- package/src/modules/ai_assistant/lib/opencode-handlers.ts +676 -0
- package/src/modules/ai_assistant/lib/schema-utils.ts +142 -0
- package/src/modules/ai_assistant/lib/tool-executor.ts +71 -0
- package/src/modules/ai_assistant/lib/tool-index-config.ts +178 -0
- package/src/modules/ai_assistant/lib/tool-loader.ts +149 -0
- package/src/modules/ai_assistant/lib/tool-registry.ts +114 -0
- package/src/modules/ai_assistant/lib/tool-search.ts +308 -0
- package/src/modules/ai_assistant/lib/types.ts +147 -0
- package/test-schema.ts +37 -0
- package/tsconfig.json +10 -0
- package/watch.mjs +6 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const TOOL_ENTITY_ID = "ai_assistant:mcp_tool";
|
|
2
|
+
const GLOBAL_TENANT_ID = "00000000-0000-0000-0000-000000000000";
|
|
3
|
+
const ESSENTIAL_TOOLS = [
|
|
4
|
+
"context_whoami",
|
|
5
|
+
// Auth context awareness
|
|
6
|
+
"search_query",
|
|
7
|
+
// Universal search
|
|
8
|
+
"search_schema",
|
|
9
|
+
// Entity discovery
|
|
10
|
+
"search_status"
|
|
11
|
+
// Check integrations
|
|
12
|
+
];
|
|
13
|
+
const TOOL_SEARCH_CONFIG = {
|
|
14
|
+
/** Maximum tools to return from search */
|
|
15
|
+
defaultLimit: 12,
|
|
16
|
+
/** Minimum relevance score (0-1) */
|
|
17
|
+
minScore: 0.2,
|
|
18
|
+
/** Strategies to use (in priority order) */
|
|
19
|
+
strategies: ["fulltext", "vector", "tokens"]
|
|
20
|
+
};
|
|
21
|
+
const toolSearchEntityConfig = {
|
|
22
|
+
entityId: TOOL_ENTITY_ID,
|
|
23
|
+
enabled: true,
|
|
24
|
+
priority: 100,
|
|
25
|
+
// High priority for tool results
|
|
26
|
+
/**
|
|
27
|
+
* Build searchable content from a tool definition.
|
|
28
|
+
*/
|
|
29
|
+
buildSource: (ctx) => {
|
|
30
|
+
const tool = ctx.record;
|
|
31
|
+
const name = tool.name || "";
|
|
32
|
+
const description = tool.description || "";
|
|
33
|
+
const moduleId = ctx.record.moduleId;
|
|
34
|
+
const normalizedName = name.replace(/[_.-]/g, " ");
|
|
35
|
+
const textContent = [
|
|
36
|
+
normalizedName,
|
|
37
|
+
description,
|
|
38
|
+
moduleId ? `module ${moduleId}` : ""
|
|
39
|
+
].filter(Boolean).join(" | ");
|
|
40
|
+
return {
|
|
41
|
+
text: textContent,
|
|
42
|
+
fields: {
|
|
43
|
+
name: normalizedName,
|
|
44
|
+
originalName: name,
|
|
45
|
+
description,
|
|
46
|
+
moduleId: moduleId ?? null,
|
|
47
|
+
requiredFeatures: tool.requiredFeatures ?? []
|
|
48
|
+
},
|
|
49
|
+
presenter: {
|
|
50
|
+
title: name,
|
|
51
|
+
subtitle: description.slice(0, 100),
|
|
52
|
+
icon: "tool"
|
|
53
|
+
},
|
|
54
|
+
checksumSource: { name, description, moduleId }
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
/**
|
|
58
|
+
* Format result for display in search UI.
|
|
59
|
+
*/
|
|
60
|
+
formatResult: (ctx) => {
|
|
61
|
+
const tool = ctx.record;
|
|
62
|
+
return {
|
|
63
|
+
title: tool.name || "Unknown Tool",
|
|
64
|
+
subtitle: (tool.description || "").slice(0, 100),
|
|
65
|
+
icon: "tool"
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
/**
|
|
69
|
+
* Field policy for search strategies.
|
|
70
|
+
*/
|
|
71
|
+
fieldPolicy: {
|
|
72
|
+
searchable: ["name", "description", "moduleId"],
|
|
73
|
+
hashOnly: [],
|
|
74
|
+
excluded: ["requiredFeatures", "inputSchema", "handler"]
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
function toolToIndexableRecord(tool, moduleId) {
|
|
78
|
+
const normalizedName = tool.name.replace(/[_.-]/g, " ");
|
|
79
|
+
const description = tool.description || "";
|
|
80
|
+
const embeddingText = `${normalizedName} | ${description}`;
|
|
81
|
+
const presenter = {
|
|
82
|
+
title: tool.name,
|
|
83
|
+
subtitle: description.slice(0, 100),
|
|
84
|
+
icon: "tool"
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
entityId: TOOL_ENTITY_ID,
|
|
88
|
+
recordId: tool.name,
|
|
89
|
+
tenantId: GLOBAL_TENANT_ID,
|
|
90
|
+
organizationId: null,
|
|
91
|
+
fields: {
|
|
92
|
+
name: normalizedName,
|
|
93
|
+
originalName: tool.name,
|
|
94
|
+
description,
|
|
95
|
+
moduleId: moduleId ?? null,
|
|
96
|
+
requiredFeatures: tool.requiredFeatures ?? []
|
|
97
|
+
},
|
|
98
|
+
presenter,
|
|
99
|
+
text: embeddingText,
|
|
100
|
+
checksumSource: {
|
|
101
|
+
name: tool.name,
|
|
102
|
+
description,
|
|
103
|
+
moduleId
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function computeToolsChecksum(tools) {
|
|
108
|
+
const content = tools.map((t) => `${t.name}:${t.description}`).sort().join("|");
|
|
109
|
+
let hash = 0;
|
|
110
|
+
for (let i = 0; i < content.length; i++) {
|
|
111
|
+
const char = content.charCodeAt(i);
|
|
112
|
+
hash = (hash << 5) - hash + char | 0;
|
|
113
|
+
}
|
|
114
|
+
return hash.toString(16);
|
|
115
|
+
}
|
|
116
|
+
export {
|
|
117
|
+
ESSENTIAL_TOOLS,
|
|
118
|
+
GLOBAL_TENANT_ID,
|
|
119
|
+
TOOL_ENTITY_ID,
|
|
120
|
+
TOOL_SEARCH_CONFIG,
|
|
121
|
+
computeToolsChecksum,
|
|
122
|
+
toolSearchEntityConfig,
|
|
123
|
+
toolToIndexableRecord
|
|
124
|
+
};
|
|
125
|
+
//# sourceMappingURL=tool-index-config.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/lib/tool-index-config.ts"],
|
|
4
|
+
"sourcesContent": ["import type {\n SearchEntityConfig,\n SearchResultPresenter,\n IndexableRecord,\n} from '@open-mercato/search/types'\nimport type { McpToolDefinition } from './types'\n\n/**\n * Entity ID for MCP tools in the search index.\n * Following the module:entity naming convention.\n */\nexport const TOOL_ENTITY_ID = 'ai_assistant:mcp_tool' as const\n\n/**\n * Tenant ID for global tools.\n * Tools are not tenant-scoped, so we use a special \"system\" UUID.\n * This is the nil UUID (all zeros) reserved for system-wide resources.\n */\nexport const GLOBAL_TENANT_ID = '00000000-0000-0000-0000-000000000000'\n\n/**\n * Essential tools that should always be available regardless of search results.\n * These provide fundamental functionality for the AI assistant.\n */\nexport const ESSENTIAL_TOOLS = [\n 'context_whoami', // Auth context awareness\n 'search_query', // Universal search\n 'search_schema', // Entity discovery\n 'search_status', // Check integrations\n] as const\n\n/**\n * Default configuration for tool search.\n */\nexport const TOOL_SEARCH_CONFIG = {\n /** Maximum tools to return from search */\n defaultLimit: 12,\n /** Minimum relevance score (0-1) */\n minScore: 0.2,\n /** Strategies to use (in priority order) */\n strategies: ['fulltext', 'vector', 'tokens'] as const,\n} as const\n\n/**\n * Search entity configuration for MCP tools.\n * This configures how tools are indexed and searched.\n */\nexport const toolSearchEntityConfig: SearchEntityConfig = {\n entityId: TOOL_ENTITY_ID,\n enabled: true,\n priority: 100, // High priority for tool results\n\n /**\n * Build searchable content from a tool definition.\n */\n buildSource: (ctx) => {\n const tool = ctx.record as unknown as McpToolDefinition\n const name = tool.name || ''\n const description = tool.description || ''\n const moduleId = (ctx.record as Record<string, unknown>).moduleId as string | undefined\n\n // Normalize name: replace underscores/dots with spaces for better search\n const normalizedName = name.replace(/[_.-]/g, ' ')\n\n // Build text content for embedding and fulltext search\n const textContent = [\n normalizedName,\n description,\n moduleId ? `module ${moduleId}` : '',\n ]\n .filter(Boolean)\n .join(' | ')\n\n return {\n text: textContent,\n fields: {\n name: normalizedName,\n originalName: name,\n description,\n moduleId: moduleId ?? null,\n requiredFeatures: tool.requiredFeatures ?? [],\n },\n presenter: {\n title: name,\n subtitle: description.slice(0, 100),\n icon: 'tool',\n },\n checksumSource: { name, description, moduleId },\n }\n },\n\n /**\n * Format result for display in search UI.\n */\n formatResult: (ctx) => {\n const tool = ctx.record as unknown as McpToolDefinition\n return {\n title: tool.name || 'Unknown Tool',\n subtitle: (tool.description || '').slice(0, 100),\n icon: 'tool',\n }\n },\n\n /**\n * Field policy for search strategies.\n */\n fieldPolicy: {\n searchable: ['name', 'description', 'moduleId'],\n hashOnly: [],\n excluded: ['requiredFeatures', 'inputSchema', 'handler'],\n },\n}\n\n/**\n * Convert an MCP tool definition to an indexable record for search.\n *\n * @param tool - The tool definition to index\n * @param moduleId - The module that registered this tool\n * @returns IndexableRecord ready for search indexing\n */\nexport function toolToIndexableRecord(\n tool: McpToolDefinition,\n moduleId?: string\n): IndexableRecord {\n const normalizedName = tool.name.replace(/[_.-]/g, ' ')\n const description = tool.description || ''\n\n // Build text for vector embedding\n const embeddingText = `${normalizedName} | ${description}`\n\n const presenter: SearchResultPresenter = {\n title: tool.name,\n subtitle: description.slice(0, 100),\n icon: 'tool',\n }\n\n return {\n entityId: TOOL_ENTITY_ID,\n recordId: tool.name,\n tenantId: GLOBAL_TENANT_ID,\n organizationId: null,\n fields: {\n name: normalizedName,\n originalName: tool.name,\n description,\n moduleId: moduleId ?? null,\n requiredFeatures: tool.requiredFeatures ?? [],\n },\n presenter,\n text: embeddingText,\n checksumSource: {\n name: tool.name,\n description,\n moduleId,\n },\n }\n}\n\n/**\n * Compute a simple checksum for tool definitions.\n * Used to detect changes and avoid unnecessary re-indexing.\n */\nexport function computeToolsChecksum(\n tools: Array<{ name: string; description: string }>\n): string {\n const content = tools\n .map((t) => `${t.name}:${t.description}`)\n .sort()\n .join('|')\n\n // Simple hash using string code points\n let hash = 0\n for (let i = 0; i < content.length; i++) {\n const char = content.charCodeAt(i)\n hash = ((hash << 5) - hash + char) | 0\n }\n return hash.toString(16)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { registerMcpTool, getToolRegistry } from "./tool-registry.js";
|
|
3
|
+
import { ToolSearchService } from "./tool-search.js";
|
|
4
|
+
import { loadApiDiscoveryTools } from "./api-discovery-tools.js";
|
|
5
|
+
const contextWhoamiTool = {
|
|
6
|
+
name: "context_whoami",
|
|
7
|
+
description: "Get the current authentication context including tenant ID, organization ID, user ID, and available features. Use this to understand your current scope before performing operations.",
|
|
8
|
+
inputSchema: z.object({}),
|
|
9
|
+
requiredFeatures: [],
|
|
10
|
+
// No specific feature required - available to all authenticated users
|
|
11
|
+
handler: async (_input, ctx) => {
|
|
12
|
+
return {
|
|
13
|
+
tenantId: ctx.tenantId,
|
|
14
|
+
organizationId: ctx.organizationId,
|
|
15
|
+
userId: ctx.userId,
|
|
16
|
+
isSuperAdmin: ctx.isSuperAdmin,
|
|
17
|
+
features: ctx.userFeatures,
|
|
18
|
+
featureCount: ctx.userFeatures.length
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
function loadModuleTools(moduleId, tools) {
|
|
23
|
+
for (const tool of tools) {
|
|
24
|
+
registerMcpTool(
|
|
25
|
+
{
|
|
26
|
+
name: tool.name,
|
|
27
|
+
description: tool.description,
|
|
28
|
+
inputSchema: tool.inputSchema,
|
|
29
|
+
requiredFeatures: tool.requiredFeatures,
|
|
30
|
+
handler: tool.handler
|
|
31
|
+
},
|
|
32
|
+
{ moduleId }
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function loadAllModuleTools() {
|
|
37
|
+
registerMcpTool(contextWhoamiTool, { moduleId: "context" });
|
|
38
|
+
console.error("[MCP Tools] Registered built-in context_whoami tool");
|
|
39
|
+
const moduleToolPaths = [
|
|
40
|
+
{ moduleId: "search", importPath: "@open-mercato/search/modules/search/ai-tools" }
|
|
41
|
+
// Add more modules here as they define ai-tools.ts
|
|
42
|
+
];
|
|
43
|
+
for (const { moduleId, importPath } of moduleToolPaths) {
|
|
44
|
+
try {
|
|
45
|
+
const module = await import(importPath);
|
|
46
|
+
const tools = module.aiTools ?? module.default ?? [];
|
|
47
|
+
if (Array.isArray(tools) && tools.length > 0) {
|
|
48
|
+
loadModuleTools(moduleId, tools);
|
|
49
|
+
console.error(`[MCP Tools] Loaded ${tools.length} tools from ${moduleId}`);
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error(`[MCP Tools] Could not load tools from ${moduleId}:`, error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const apiToolCount = await loadApiDiscoveryTools();
|
|
57
|
+
console.error(`[MCP Tools] Loaded ${apiToolCount} API discovery tools`);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("[MCP Tools] Could not load API discovery tools:", error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function indexToolsForSearch(searchService, force = false) {
|
|
63
|
+
const registry = getToolRegistry();
|
|
64
|
+
const toolSearchService = new ToolSearchService(searchService, registry);
|
|
65
|
+
try {
|
|
66
|
+
const result = await toolSearchService.indexTools(force);
|
|
67
|
+
console.error(`[MCP Tools] Indexed ${result.indexed} tools for search`);
|
|
68
|
+
console.error(`[MCP Tools] Search strategies available: ${result.strategies.join(", ")}`);
|
|
69
|
+
if (result.skipped > 0) {
|
|
70
|
+
console.error(`[MCP Tools] Skipped ${result.skipped} tools (unchanged)`);
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error("[MCP Tools] Failed to index tools for search:", error);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function createToolSearchService(searchService) {
|
|
79
|
+
const registry = getToolRegistry();
|
|
80
|
+
return new ToolSearchService(searchService, registry);
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
createToolSearchService,
|
|
84
|
+
indexToolsForSearch,
|
|
85
|
+
loadAllModuleTools,
|
|
86
|
+
loadModuleTools
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=tool-loader.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/lib/tool-loader.ts"],
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport type { SearchService } from '@open-mercato/search/service'\nimport { registerMcpTool, getToolRegistry } from './tool-registry'\nimport type { McpToolDefinition, McpToolContext } from './types'\nimport { ToolSearchService } from './tool-search'\nimport { loadApiDiscoveryTools } from './api-discovery-tools'\n\n/**\n * Module tool definition as exported from ai-tools.ts files.\n */\ntype ModuleAiTool = {\n name: string\n description: string\n inputSchema: any\n requiredFeatures?: string[]\n handler: (input: any, ctx: any) => Promise<unknown>\n}\n\n/**\n * Built-in context.whoami tool that returns the current authentication context.\n * This is useful for AI to understand its current tenant/org scope.\n */\nconst contextWhoamiTool: McpToolDefinition = {\n name: 'context_whoami',\n description:\n 'Get the current authentication context including tenant ID, organization ID, user ID, and available features. Use this to understand your current scope before performing operations.',\n inputSchema: z.object({}),\n requiredFeatures: [], // No specific feature required - available to all authenticated users\n handler: async (_input: unknown, ctx: McpToolContext) => {\n return {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n userId: ctx.userId,\n isSuperAdmin: ctx.isSuperAdmin,\n features: ctx.userFeatures,\n featureCount: ctx.userFeatures.length,\n }\n },\n}\n\n/**\n * Load and register AI tools from a module's ai-tools.ts export.\n *\n * @param moduleId - The module identifier (e.g., 'search', 'customers')\n * @param tools - Array of tool definitions from the module\n */\nexport function loadModuleTools(moduleId: string, tools: ModuleAiTool[]): void {\n for (const tool of tools) {\n registerMcpTool(\n {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n requiredFeatures: tool.requiredFeatures,\n handler: tool.handler,\n } as McpToolDefinition,\n { moduleId }\n )\n }\n}\n\n/**\n * Dynamically load tools from known module paths.\n * This is called during MCP server startup.\n */\nexport async function loadAllModuleTools(): Promise<void> {\n // 1. Register built-in tools\n registerMcpTool(contextWhoamiTool, { moduleId: 'context' })\n console.error('[MCP Tools] Registered built-in context_whoami tool')\n\n // 2. Load manual ai-tools.ts files from modules\n const moduleToolPaths = [\n { moduleId: 'search', importPath: '@open-mercato/search/modules/search/ai-tools' },\n // Add more modules here as they define ai-tools.ts\n ]\n\n for (const { moduleId, importPath } of moduleToolPaths) {\n try {\n const module = await import(importPath)\n const tools = module.aiTools ?? module.default ?? []\n\n if (Array.isArray(tools) && tools.length > 0) {\n loadModuleTools(moduleId, tools)\n console.error(`[MCP Tools] Loaded ${tools.length} tools from ${moduleId}`)\n }\n } catch (error) {\n // Module might not have ai-tools.ts or import failed\n // This is not an error - modules can optionally provide tools\n console.error(`[MCP Tools] Could not load tools from ${moduleId}:`, error)\n }\n }\n\n // 3. Load API discovery tools (api_discover, api_execute, api_schema)\n try {\n const apiToolCount = await loadApiDiscoveryTools()\n console.error(`[MCP Tools] Loaded ${apiToolCount} API discovery tools`)\n } catch (error) {\n console.error('[MCP Tools] Could not load API discovery tools:', error)\n }\n}\n\n/**\n * Index all registered tools for hybrid search discovery.\n * This should be called after loadAllModuleTools() when the search service is available.\n *\n * @param searchService - The search service from DI container\n * @param force - Force re-indexing even if checksums match\n * @returns Indexing result with statistics\n */\nexport async function indexToolsForSearch(\n searchService: SearchService,\n force = false\n): Promise<{\n indexed: number\n skipped: number\n strategies: string[]\n checksum: string\n}> {\n const registry = getToolRegistry()\n const toolSearchService = new ToolSearchService(searchService, registry)\n\n try {\n const result = await toolSearchService.indexTools(force)\n\n console.error(`[MCP Tools] Indexed ${result.indexed} tools for search`)\n console.error(`[MCP Tools] Search strategies available: ${result.strategies.join(', ')}`)\n\n if (result.skipped > 0) {\n console.error(`[MCP Tools] Skipped ${result.skipped} tools (unchanged)`)\n }\n\n return result\n } catch (error) {\n console.error('[MCP Tools] Failed to index tools for search:', error)\n throw error\n }\n}\n\n/**\n * Create a ToolSearchService instance for tool discovery.\n * Use this to get a configured service for discovering relevant tools.\n *\n * @param searchService - The search service from DI container\n * @returns Configured ToolSearchService\n */\nexport function createToolSearchService(searchService: SearchService): ToolSearchService {\n const registry = getToolRegistry()\n return new ToolSearchService(searchService, registry)\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,SAAS;AAElB,SAAS,iBAAiB,uBAAuB;AAEjD,SAAS,yBAAyB;AAClC,SAAS,6BAA6B;AAiBtC,MAAM,oBAAuC;AAAA,EAC3C,MAAM;AAAA,EACN,aACE;AAAA,EACF,aAAa,EAAE,OAAO,CAAC,CAAC;AAAA,EACxB,kBAAkB,CAAC;AAAA;AAAA,EACnB,SAAS,OAAO,QAAiB,QAAwB;AACvD,WAAO;AAAA,MACL,UAAU,IAAI;AAAA,MACd,gBAAgB,IAAI;AAAA,MACpB,QAAQ,IAAI;AAAA,MACZ,cAAc,IAAI;AAAA,MAClB,UAAU,IAAI;AAAA,MACd,cAAc,IAAI,aAAa;AAAA,IACjC;AAAA,EACF;AACF;AAQO,SAAS,gBAAgB,UAAkB,OAA6B;AAC7E,aAAW,QAAQ,OAAO;AACxB;AAAA,MACE;AAAA,QACE,MAAM,KAAK;AAAA,QACX,aAAa,KAAK;AAAA,QAClB,aAAa,KAAK;AAAA,QAClB,kBAAkB,KAAK;AAAA,QACvB,SAAS,KAAK;AAAA,MAChB;AAAA,MACA,EAAE,SAAS;AAAA,IACb;AAAA,EACF;AACF;AAMA,eAAsB,qBAAoC;AAExD,kBAAgB,mBAAmB,EAAE,UAAU,UAAU,CAAC;AAC1D,UAAQ,MAAM,qDAAqD;AAGnE,QAAM,kBAAkB;AAAA,IACtB,EAAE,UAAU,UAAU,YAAY,+CAA+C;AAAA;AAAA,EAEnF;AAEA,aAAW,EAAE,UAAU,WAAW,KAAK,iBAAiB;AACtD,QAAI;AACF,YAAM,SAAS,MAAM,OAAO;AAC5B,YAAM,QAAQ,OAAO,WAAW,OAAO,WAAW,CAAC;AAEnD,UAAI,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,GAAG;AAC5C,wBAAgB,UAAU,KAAK;AAC/B,gBAAQ,MAAM,sBAAsB,MAAM,MAAM,eAAe,QAAQ,EAAE;AAAA,MAC3E;AAAA,IACF,SAAS,OAAO;AAGd,cAAQ,MAAM,yCAAyC,QAAQ,KAAK,KAAK;AAAA,IAC3E;AAAA,EACF;AAGA,MAAI;AACF,UAAM,eAAe,MAAM,sBAAsB;AACjD,YAAQ,MAAM,sBAAsB,YAAY,sBAAsB;AAAA,EACxE,SAAS,OAAO;AACd,YAAQ,MAAM,mDAAmD,KAAK;AAAA,EACxE;AACF;AAUA,eAAsB,oBACpB,eACA,QAAQ,OAMP;AACD,QAAM,WAAW,gBAAgB;AACjC,QAAM,oBAAoB,IAAI,kBAAkB,eAAe,QAAQ;AAEvE,MAAI;AACF,UAAM,SAAS,MAAM,kBAAkB,WAAW,KAAK;AAEvD,YAAQ,MAAM,uBAAuB,OAAO,OAAO,mBAAmB;AACtE,YAAQ,MAAM,4CAA4C,OAAO,WAAW,KAAK,IAAI,CAAC,EAAE;AAExF,QAAI,OAAO,UAAU,GAAG;AACtB,cAAQ,MAAM,uBAAuB,OAAO,OAAO,oBAAoB;AAAA,IACzE;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,YAAQ,MAAM,iDAAiD,KAAK;AACpE,UAAM;AAAA,EACR;AACF;AASO,SAAS,wBAAwB,eAAiD;AACvF,QAAM,WAAW,gBAAgB;AACjC,SAAO,IAAI,kBAAkB,eAAe,QAAQ;AACtD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
class ToolRegistryImpl {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.tools = /* @__PURE__ */ new Map();
|
|
4
|
+
this.moduleMap = /* @__PURE__ */ new Map();
|
|
5
|
+
}
|
|
6
|
+
registerTool(tool, options) {
|
|
7
|
+
if (!tool?.name) {
|
|
8
|
+
throw new Error("MCP tool must define a name");
|
|
9
|
+
}
|
|
10
|
+
if (this.tools.has(tool.name)) {
|
|
11
|
+
console.warn(`[McpToolRegistry] Tool "${tool.name}" already registered, overwriting`);
|
|
12
|
+
}
|
|
13
|
+
this.tools.set(tool.name, tool);
|
|
14
|
+
if (options?.moduleId) {
|
|
15
|
+
const existing = this.moduleMap.get(options.moduleId) ?? [];
|
|
16
|
+
if (!existing.includes(tool.name)) {
|
|
17
|
+
existing.push(tool.name);
|
|
18
|
+
}
|
|
19
|
+
this.moduleMap.set(options.moduleId, existing);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
getTools() {
|
|
23
|
+
return new Map(this.tools);
|
|
24
|
+
}
|
|
25
|
+
getTool(name) {
|
|
26
|
+
return this.tools.get(name);
|
|
27
|
+
}
|
|
28
|
+
listToolNames() {
|
|
29
|
+
return Array.from(this.tools.keys());
|
|
30
|
+
}
|
|
31
|
+
listToolsByModule(moduleId) {
|
|
32
|
+
return this.moduleMap.get(moduleId) ?? [];
|
|
33
|
+
}
|
|
34
|
+
unregisterTool(name) {
|
|
35
|
+
this.tools.delete(name);
|
|
36
|
+
for (const [moduleId, tools] of this.moduleMap.entries()) {
|
|
37
|
+
const index = tools.indexOf(name);
|
|
38
|
+
if (index !== -1) {
|
|
39
|
+
tools.splice(index, 1);
|
|
40
|
+
this.moduleMap.set(moduleId, tools);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
clear() {
|
|
45
|
+
this.tools.clear();
|
|
46
|
+
this.moduleMap.clear();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const toolRegistry = new ToolRegistryImpl();
|
|
50
|
+
function registerMcpTool(tool, options) {
|
|
51
|
+
toolRegistry.registerTool(tool, options);
|
|
52
|
+
}
|
|
53
|
+
function getToolRegistry() {
|
|
54
|
+
return toolRegistry;
|
|
55
|
+
}
|
|
56
|
+
function unregisterMcpTool(name) {
|
|
57
|
+
toolRegistry.unregisterTool(name);
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
getToolRegistry,
|
|
61
|
+
registerMcpTool,
|
|
62
|
+
toolRegistry,
|
|
63
|
+
unregisterMcpTool
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=tool-registry.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/lib/tool-registry.ts"],
|
|
4
|
+
"sourcesContent": ["import type { McpToolDefinition, McpToolRegistry, ToolRegistrationOptions } from './types'\n\n/**\n * Global tool registry singleton.\n * Modules call registerMcpTool() to add their tools.\n */\nclass ToolRegistryImpl implements McpToolRegistry {\n private tools = new Map<string, McpToolDefinition>()\n private moduleMap = new Map<string, string[]>()\n\n registerTool<TInput, TOutput>(\n tool: McpToolDefinition<TInput, TOutput>,\n options?: ToolRegistrationOptions\n ): void {\n if (!tool?.name) {\n throw new Error('MCP tool must define a name')\n }\n\n if (this.tools.has(tool.name)) {\n console.warn(`[McpToolRegistry] Tool \"${tool.name}\" already registered, overwriting`)\n }\n\n this.tools.set(tool.name, tool as McpToolDefinition)\n\n if (options?.moduleId) {\n const existing = this.moduleMap.get(options.moduleId) ?? []\n if (!existing.includes(tool.name)) {\n existing.push(tool.name)\n }\n this.moduleMap.set(options.moduleId, existing)\n }\n }\n\n getTools(): Map<string, McpToolDefinition> {\n return new Map(this.tools)\n }\n\n getTool(name: string): McpToolDefinition | undefined {\n return this.tools.get(name)\n }\n\n listToolNames(): string[] {\n return Array.from(this.tools.keys())\n }\n\n listToolsByModule(moduleId: string): string[] {\n return this.moduleMap.get(moduleId) ?? []\n }\n\n unregisterTool(name: string): void {\n this.tools.delete(name)\n for (const [moduleId, tools] of this.moduleMap.entries()) {\n const index = tools.indexOf(name)\n if (index !== -1) {\n tools.splice(index, 1)\n this.moduleMap.set(moduleId, tools)\n }\n }\n }\n\n clear(): void {\n this.tools.clear()\n this.moduleMap.clear()\n }\n}\n\nexport const toolRegistry = new ToolRegistryImpl()\n\n/**\n * Register an MCP tool from any module.\n *\n * Note: Tool names must match the pattern ^[a-zA-Z0-9_-]{1,128}$\n * (no dots allowed - use underscores instead).\n *\n * @example\n * ```typescript\n * import { registerMcpTool } from '@open-mercato/ai-assistant/tools'\n * import { z } from 'zod'\n *\n * registerMcpTool({\n * name: 'customers_search',\n * description: 'Search for customers by name or email',\n * inputSchema: z.object({\n * query: z.string(),\n * limit: z.number().optional().default(10),\n * }),\n * requiredFeatures: ['customers.people.view'],\n * handler: async (input, ctx) => {\n * const queryEngine = ctx.container.resolve('queryEngine')\n * // ... implementation\n * }\n * }, { moduleId: 'customers' })\n * ```\n */\nexport function registerMcpTool<TInput, TOutput>(\n tool: McpToolDefinition<TInput, TOutput>,\n options?: ToolRegistrationOptions\n): void {\n toolRegistry.registerTool(tool, options)\n}\n\n/**\n * Get the global tool registry instance.\n */\nexport function getToolRegistry(): McpToolRegistry {\n return toolRegistry\n}\n\n/**\n * Unregister an MCP tool by name.\n */\nexport function unregisterMcpTool(name: string): void {\n toolRegistry.unregisterTool(name)\n}\n"],
|
|
5
|
+
"mappings": "AAMA,MAAM,iBAA4C;AAAA,EAAlD;AACE,SAAQ,QAAQ,oBAAI,IAA+B;AACnD,SAAQ,YAAY,oBAAI,IAAsB;AAAA;AAAA,EAE9C,aACE,MACA,SACM;AACN,QAAI,CAAC,MAAM,MAAM;AACf,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AAEA,QAAI,KAAK,MAAM,IAAI,KAAK,IAAI,GAAG;AAC7B,cAAQ,KAAK,2BAA2B,KAAK,IAAI,mCAAmC;AAAA,IACtF;AAEA,SAAK,MAAM,IAAI,KAAK,MAAM,IAAyB;AAEnD,QAAI,SAAS,UAAU;AACrB,YAAM,WAAW,KAAK,UAAU,IAAI,QAAQ,QAAQ,KAAK,CAAC;AAC1D,UAAI,CAAC,SAAS,SAAS,KAAK,IAAI,GAAG;AACjC,iBAAS,KAAK,KAAK,IAAI;AAAA,MACzB;AACA,WAAK,UAAU,IAAI,QAAQ,UAAU,QAAQ;AAAA,IAC/C;AAAA,EACF;AAAA,EAEA,WAA2C;AACzC,WAAO,IAAI,IAAI,KAAK,KAAK;AAAA,EAC3B;AAAA,EAEA,QAAQ,MAA6C;AACnD,WAAO,KAAK,MAAM,IAAI,IAAI;AAAA,EAC5B;AAAA,EAEA,gBAA0B;AACxB,WAAO,MAAM,KAAK,KAAK,MAAM,KAAK,CAAC;AAAA,EACrC;AAAA,EAEA,kBAAkB,UAA4B;AAC5C,WAAO,KAAK,UAAU,IAAI,QAAQ,KAAK,CAAC;AAAA,EAC1C;AAAA,EAEA,eAAe,MAAoB;AACjC,SAAK,MAAM,OAAO,IAAI;AACtB,eAAW,CAAC,UAAU,KAAK,KAAK,KAAK,UAAU,QAAQ,GAAG;AACxD,YAAM,QAAQ,MAAM,QAAQ,IAAI;AAChC,UAAI,UAAU,IAAI;AAChB,cAAM,OAAO,OAAO,CAAC;AACrB,aAAK,UAAU,IAAI,UAAU,KAAK;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,SAAK,MAAM,MAAM;AACjB,SAAK,UAAU,MAAM;AAAA,EACvB;AACF;AAEO,MAAM,eAAe,IAAI,iBAAiB;AA4B1C,SAAS,gBACd,MACA,SACM;AACN,eAAa,aAAa,MAAM,OAAO;AACzC;AAKO,SAAS,kBAAmC;AACjD,SAAO;AACT;AAKO,SAAS,kBAAkB,MAAoB;AACpD,eAAa,eAAe,IAAI;AAClC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TOOL_ENTITY_ID,
|
|
3
|
+
GLOBAL_TENANT_ID,
|
|
4
|
+
TOOL_SEARCH_CONFIG,
|
|
5
|
+
toolToIndexableRecord,
|
|
6
|
+
computeToolsChecksum
|
|
7
|
+
} from "./tool-index-config.js";
|
|
8
|
+
class ToolSearchService {
|
|
9
|
+
constructor(searchService, toolRegistry) {
|
|
10
|
+
this.searchService = searchService;
|
|
11
|
+
this.toolRegistry = toolRegistry;
|
|
12
|
+
this.lastIndexChecksum = null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get status of available search strategies.
|
|
16
|
+
* This helps the AI understand which integrations are available.
|
|
17
|
+
*/
|
|
18
|
+
async getStrategyStatus() {
|
|
19
|
+
const [fulltext, vector, tokens] = await Promise.all([
|
|
20
|
+
this.searchService.isStrategyAvailable("fulltext"),
|
|
21
|
+
this.searchService.isStrategyAvailable("vector"),
|
|
22
|
+
this.searchService.isStrategyAvailable("tokens")
|
|
23
|
+
]);
|
|
24
|
+
const available = [];
|
|
25
|
+
if (fulltext) available.push("fulltext");
|
|
26
|
+
if (vector) available.push("vector");
|
|
27
|
+
if (tokens) available.push("tokens");
|
|
28
|
+
const defaultStrategies = this.searchService.getDefaultStrategies();
|
|
29
|
+
return {
|
|
30
|
+
fulltext,
|
|
31
|
+
vector,
|
|
32
|
+
tokens,
|
|
33
|
+
available,
|
|
34
|
+
default: defaultStrategies.filter((s) => available.includes(s))
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Search for relevant tools using hybrid strategies.
|
|
39
|
+
*
|
|
40
|
+
* @param query - User's search query or message
|
|
41
|
+
* @param options - Search options
|
|
42
|
+
* @returns Array of matching tools sorted by relevance
|
|
43
|
+
*/
|
|
44
|
+
async searchTools(query, options = {}) {
|
|
45
|
+
const {
|
|
46
|
+
limit = TOOL_SEARCH_CONFIG.defaultLimit,
|
|
47
|
+
strategies,
|
|
48
|
+
userFeatures = [],
|
|
49
|
+
isSuperAdmin = false,
|
|
50
|
+
minScore = TOOL_SEARCH_CONFIG.minScore
|
|
51
|
+
} = options;
|
|
52
|
+
const searchResults = await this.searchService.search(query, {
|
|
53
|
+
tenantId: GLOBAL_TENANT_ID,
|
|
54
|
+
organizationId: null,
|
|
55
|
+
entityTypes: [TOOL_ENTITY_ID],
|
|
56
|
+
strategies,
|
|
57
|
+
limit: limit * 2
|
|
58
|
+
// Get extra results to account for ACL filtering
|
|
59
|
+
});
|
|
60
|
+
const toolResults = [];
|
|
61
|
+
for (const result of searchResults) {
|
|
62
|
+
if (result.score < minScore) continue;
|
|
63
|
+
const metadata = result.metadata;
|
|
64
|
+
const requiredFeatures = metadata?.requiredFeatures ?? [];
|
|
65
|
+
const moduleId = metadata?.moduleId;
|
|
66
|
+
if (!this.hasFeatureAccess(requiredFeatures, userFeatures, isSuperAdmin)) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
toolResults.push({
|
|
70
|
+
toolName: result.recordId,
|
|
71
|
+
score: result.score,
|
|
72
|
+
moduleId,
|
|
73
|
+
requiredFeatures
|
|
74
|
+
});
|
|
75
|
+
if (toolResults.length >= limit) break;
|
|
76
|
+
}
|
|
77
|
+
return toolResults;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Index all tools from the registry using available strategies.
|
|
81
|
+
* Uses checksum-based change detection to avoid unnecessary re-indexing.
|
|
82
|
+
*
|
|
83
|
+
* @param force - Force re-indexing even if checksum hasn't changed
|
|
84
|
+
* @returns Indexing result with statistics
|
|
85
|
+
*/
|
|
86
|
+
async indexTools(force = false) {
|
|
87
|
+
const tools = Array.from(this.toolRegistry.getTools().values());
|
|
88
|
+
const modules = this.getToolModules();
|
|
89
|
+
const checksum = computeToolsChecksum(
|
|
90
|
+
tools.map((t) => ({ name: t.name, description: t.description }))
|
|
91
|
+
);
|
|
92
|
+
if (!force && this.lastIndexChecksum === checksum) {
|
|
93
|
+
const status2 = await this.getStrategyStatus();
|
|
94
|
+
return {
|
|
95
|
+
indexed: 0,
|
|
96
|
+
skipped: tools.length,
|
|
97
|
+
strategies: status2.available,
|
|
98
|
+
checksum
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const records = [];
|
|
102
|
+
for (const tool of tools) {
|
|
103
|
+
const moduleId = modules.get(tool.name);
|
|
104
|
+
records.push(toolToIndexableRecord(tool, moduleId));
|
|
105
|
+
}
|
|
106
|
+
if (records.length > 0) {
|
|
107
|
+
await this.searchService.bulkIndex(records);
|
|
108
|
+
}
|
|
109
|
+
this.lastIndexChecksum = checksum;
|
|
110
|
+
const status = await this.getStrategyStatus();
|
|
111
|
+
return {
|
|
112
|
+
indexed: records.length,
|
|
113
|
+
skipped: 0,
|
|
114
|
+
strategies: status.available,
|
|
115
|
+
checksum
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Purge all tool records from the search index.
|
|
120
|
+
* Useful for cleanup or complete re-indexing.
|
|
121
|
+
*/
|
|
122
|
+
async purgeIndex() {
|
|
123
|
+
await this.searchService.purge(TOOL_ENTITY_ID, GLOBAL_TENANT_ID);
|
|
124
|
+
this.lastIndexChecksum = null;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get tools by module ID from the registry.
|
|
128
|
+
*/
|
|
129
|
+
getToolsByModule(moduleId) {
|
|
130
|
+
return this.toolRegistry.listToolsByModule(moduleId);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Build a map of tool name to module ID.
|
|
134
|
+
*/
|
|
135
|
+
getToolModules() {
|
|
136
|
+
const modules = /* @__PURE__ */ new Map();
|
|
137
|
+
const modulePatterns = [
|
|
138
|
+
{ pattern: /^customers_/, moduleId: "customers" },
|
|
139
|
+
{ pattern: /^sales_/, moduleId: "sales" },
|
|
140
|
+
{ pattern: /^catalog_/, moduleId: "catalog" },
|
|
141
|
+
{ pattern: /^staff_/, moduleId: "staff" },
|
|
142
|
+
{ pattern: /^resources_/, moduleId: "resources" },
|
|
143
|
+
{ pattern: /^planner/, moduleId: "planner" },
|
|
144
|
+
{ pattern: /^search_/, moduleId: "search" },
|
|
145
|
+
{ pattern: /^context_/, moduleId: "context" },
|
|
146
|
+
{ pattern: /^auth_/, moduleId: "auth" },
|
|
147
|
+
{ pattern: /^directory_/, moduleId: "directory" },
|
|
148
|
+
{ pattern: /^dashboards_/, moduleId: "dashboards" },
|
|
149
|
+
{ pattern: /^entities_/, moduleId: "entities" },
|
|
150
|
+
{ pattern: /^dictionaries_/, moduleId: "dictionaries" },
|
|
151
|
+
{ pattern: /^workflows_/, moduleId: "workflows" },
|
|
152
|
+
{ pattern: /^business_rules_/, moduleId: "business_rules" },
|
|
153
|
+
{ pattern: /^audit_logs_/, moduleId: "audit_logs" },
|
|
154
|
+
{ pattern: /^attachments_/, moduleId: "attachments" },
|
|
155
|
+
{ pattern: /^feature_toggles_/, moduleId: "feature_toggles" },
|
|
156
|
+
{ pattern: /^currencies_/, moduleId: "currencies" },
|
|
157
|
+
{ pattern: /^example_/, moduleId: "example" },
|
|
158
|
+
{ pattern: /^cli_/, moduleId: "cli" }
|
|
159
|
+
];
|
|
160
|
+
for (const toolName of this.toolRegistry.listToolNames()) {
|
|
161
|
+
for (const { pattern, moduleId } of modulePatterns) {
|
|
162
|
+
if (pattern.test(toolName)) {
|
|
163
|
+
modules.set(toolName, moduleId);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return modules;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Check if user has required features to access a tool.
|
|
172
|
+
*/
|
|
173
|
+
hasFeatureAccess(requiredFeatures, userFeatures, isSuperAdmin) {
|
|
174
|
+
if (isSuperAdmin) return true;
|
|
175
|
+
if (!requiredFeatures?.length) return true;
|
|
176
|
+
return requiredFeatures.every((required) => {
|
|
177
|
+
if (userFeatures.includes(required)) return true;
|
|
178
|
+
if (userFeatures.includes("*")) return true;
|
|
179
|
+
return userFeatures.some((feature) => {
|
|
180
|
+
if (feature.endsWith(".*")) {
|
|
181
|
+
const prefix = feature.slice(0, -2);
|
|
182
|
+
return required.startsWith(prefix + ".");
|
|
183
|
+
}
|
|
184
|
+
return false;
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
export {
|
|
190
|
+
ToolSearchService
|
|
191
|
+
};
|
|
192
|
+
//# sourceMappingURL=tool-search.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/lib/tool-search.ts"],
|
|
4
|
+
"sourcesContent": ["import type { SearchService } from '@open-mercato/search/service'\nimport type { SearchStrategyId, IndexableRecord } from '@open-mercato/search/types'\nimport type { McpToolRegistry, McpToolDefinition } from './types'\nimport {\n TOOL_ENTITY_ID,\n GLOBAL_TENANT_ID,\n TOOL_SEARCH_CONFIG,\n toolToIndexableRecord,\n computeToolsChecksum,\n} from './tool-index-config'\n\n/**\n * Result from tool search.\n */\nexport type ToolSearchResult = {\n /** Tool name (recordId) */\n toolName: string\n /** Relevance score */\n score: number\n /** Module that registered the tool */\n moduleId?: string\n /** Features required to use this tool */\n requiredFeatures?: string[]\n}\n\n/**\n * Status of available search strategies.\n */\nexport type StrategyStatus = {\n fulltext: boolean\n vector: boolean\n tokens: boolean\n /** Ordered list of available strategies */\n available: SearchStrategyId[]\n /** Strategies that will be used by default */\n default: SearchStrategyId[]\n}\n\n/**\n * Result from tool indexing operation.\n */\nexport type IndexingResult = {\n indexed: number\n skipped: number\n strategies: SearchStrategyId[]\n checksum: string\n}\n\n/**\n * Options for searching tools.\n */\nexport type ToolSearchOptions = {\n /** Maximum results to return (default: 12) */\n limit?: number\n /** Override strategies to use */\n strategies?: SearchStrategyId[]\n /** User's features for ACL filtering */\n userFeatures?: string[]\n /** Is user a super admin (bypasses ACL) */\n isSuperAdmin?: boolean\n /** Minimum score threshold (default: 0.2) */\n minScore?: number\n}\n\n/**\n * Service for searching and indexing MCP tools using hybrid search strategies.\n *\n * Uses the existing search module infrastructure to provide:\n * - Fulltext search (Meilisearch) when configured\n * - Vector/semantic search (PgVector) when configured\n * - Token-based search (PostgreSQL hash) as fallback\n *\n * Results are merged using Reciprocal Rank Fusion (RRF) with weights:\n * - fulltext: 1.2\n * - vector: 1.0\n * - tokens: 0.8\n */\nexport class ToolSearchService {\n private lastIndexChecksum: string | null = null\n\n constructor(\n private readonly searchService: SearchService,\n private readonly toolRegistry: McpToolRegistry\n ) {}\n\n /**\n * Get status of available search strategies.\n * This helps the AI understand which integrations are available.\n */\n async getStrategyStatus(): Promise<StrategyStatus> {\n const [fulltext, vector, tokens] = await Promise.all([\n this.searchService.isStrategyAvailable('fulltext'),\n this.searchService.isStrategyAvailable('vector'),\n this.searchService.isStrategyAvailable('tokens'),\n ])\n\n const available: SearchStrategyId[] = []\n if (fulltext) available.push('fulltext')\n if (vector) available.push('vector')\n if (tokens) available.push('tokens')\n\n const defaultStrategies = this.searchService.getDefaultStrategies()\n\n return {\n fulltext,\n vector,\n tokens,\n available,\n default: defaultStrategies.filter((s) => available.includes(s)),\n }\n }\n\n /**\n * Search for relevant tools using hybrid strategies.\n *\n * @param query - User's search query or message\n * @param options - Search options\n * @returns Array of matching tools sorted by relevance\n */\n async searchTools(\n query: string,\n options: ToolSearchOptions = {}\n ): Promise<ToolSearchResult[]> {\n const {\n limit = TOOL_SEARCH_CONFIG.defaultLimit,\n strategies,\n userFeatures = [],\n isSuperAdmin = false,\n minScore = TOOL_SEARCH_CONFIG.minScore,\n } = options\n\n // Use hybrid search with all available strategies\n const searchResults = await this.searchService.search(query, {\n tenantId: GLOBAL_TENANT_ID,\n organizationId: null,\n entityTypes: [TOOL_ENTITY_ID],\n strategies,\n limit: limit * 2, // Get extra results to account for ACL filtering\n })\n\n // Map to tool results and filter by ACL\n const toolResults: ToolSearchResult[] = []\n\n for (const result of searchResults) {\n // Skip results below minimum score\n if (result.score < minScore) continue\n\n const metadata = result.metadata as Record<string, unknown> | undefined\n const requiredFeatures = (metadata?.requiredFeatures as string[]) ?? []\n const moduleId = metadata?.moduleId as string | undefined\n\n // Filter by user's feature access\n if (!this.hasFeatureAccess(requiredFeatures, userFeatures, isSuperAdmin)) {\n continue\n }\n\n toolResults.push({\n toolName: result.recordId,\n score: result.score,\n moduleId,\n requiredFeatures,\n })\n\n // Stop when we have enough results\n if (toolResults.length >= limit) break\n }\n\n return toolResults\n }\n\n /**\n * Index all tools from the registry using available strategies.\n * Uses checksum-based change detection to avoid unnecessary re-indexing.\n *\n * @param force - Force re-indexing even if checksum hasn't changed\n * @returns Indexing result with statistics\n */\n async indexTools(force = false): Promise<IndexingResult> {\n const tools = Array.from(this.toolRegistry.getTools().values())\n const modules = this.getToolModules()\n\n // Compute checksum to detect changes\n const checksum = computeToolsChecksum(\n tools.map((t) => ({ name: t.name, description: t.description }))\n )\n\n // Skip if checksum matches and not forced\n if (!force && this.lastIndexChecksum === checksum) {\n const status = await this.getStrategyStatus()\n return {\n indexed: 0,\n skipped: tools.length,\n strategies: status.available,\n checksum,\n }\n }\n\n // Build indexable records for all tools\n const records: IndexableRecord[] = []\n for (const tool of tools) {\n const moduleId = modules.get(tool.name)\n records.push(toolToIndexableRecord(tool, moduleId))\n }\n\n // Bulk index using available strategies\n if (records.length > 0) {\n await this.searchService.bulkIndex(records)\n }\n\n // Update checksum\n this.lastIndexChecksum = checksum\n\n const status = await this.getStrategyStatus()\n return {\n indexed: records.length,\n skipped: 0,\n strategies: status.available,\n checksum,\n }\n }\n\n /**\n * Purge all tool records from the search index.\n * Useful for cleanup or complete re-indexing.\n */\n async purgeIndex(): Promise<void> {\n await this.searchService.purge(TOOL_ENTITY_ID, GLOBAL_TENANT_ID)\n this.lastIndexChecksum = null\n }\n\n /**\n * Get tools by module ID from the registry.\n */\n getToolsByModule(moduleId: string): string[] {\n return this.toolRegistry.listToolsByModule(moduleId)\n }\n\n /**\n * Build a map of tool name to module ID.\n */\n private getToolModules(): Map<string, string> {\n const modules = new Map<string, string>()\n\n // Common module prefixes that indicate tool ownership\n const modulePatterns = [\n { pattern: /^customers_/, moduleId: 'customers' },\n { pattern: /^sales_/, moduleId: 'sales' },\n { pattern: /^catalog_/, moduleId: 'catalog' },\n { pattern: /^staff_/, moduleId: 'staff' },\n { pattern: /^resources_/, moduleId: 'resources' },\n { pattern: /^planner/, moduleId: 'planner' },\n { pattern: /^search_/, moduleId: 'search' },\n { pattern: /^context_/, moduleId: 'context' },\n { pattern: /^auth_/, moduleId: 'auth' },\n { pattern: /^directory_/, moduleId: 'directory' },\n { pattern: /^dashboards_/, moduleId: 'dashboards' },\n { pattern: /^entities_/, moduleId: 'entities' },\n { pattern: /^dictionaries_/, moduleId: 'dictionaries' },\n { pattern: /^workflows_/, moduleId: 'workflows' },\n { pattern: /^business_rules_/, moduleId: 'business_rules' },\n { pattern: /^audit_logs_/, moduleId: 'audit_logs' },\n { pattern: /^attachments_/, moduleId: 'attachments' },\n { pattern: /^feature_toggles_/, moduleId: 'feature_toggles' },\n { pattern: /^currencies_/, moduleId: 'currencies' },\n { pattern: /^example_/, moduleId: 'example' },\n { pattern: /^cli_/, moduleId: 'cli' },\n ]\n\n for (const toolName of this.toolRegistry.listToolNames()) {\n for (const { pattern, moduleId } of modulePatterns) {\n if (pattern.test(toolName)) {\n modules.set(toolName, moduleId)\n break\n }\n }\n }\n\n return modules\n }\n\n /**\n * Check if user has required features to access a tool.\n */\n private hasFeatureAccess(\n requiredFeatures: string[] | undefined,\n userFeatures: string[],\n isSuperAdmin: boolean\n ): boolean {\n if (isSuperAdmin) return true\n if (!requiredFeatures?.length) return true\n\n return requiredFeatures.every((required) => {\n // Direct match\n if (userFeatures.includes(required)) return true\n // Wildcard match\n if (userFeatures.includes('*')) return true\n\n // Prefix wildcard match (e.g., 'customers.*' matches 'customers.people.view')\n return userFeatures.some((feature) => {\n if (feature.endsWith('.*')) {\n const prefix = feature.slice(0, -2)\n return required.startsWith(prefix + '.')\n }\n return false\n })\n })\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["status"]
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|