@bridgenet-tech/spear-data 1.5.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.
@@ -0,0 +1,147 @@
1
+ import { z } from "zod";
2
+ import { getPostgres } from "../db.js";
3
+ const InputSchema = z.object({
4
+ target: z.enum(["local", "cloud"])
5
+ .default("cloud")
6
+ .describe("Which database to inspect: 'local' (Docker) or 'cloud' (Neon)"),
7
+ table: z.string()
8
+ .optional()
9
+ .describe("Optional: specific table name to get detailed info for. If omitted, returns all tables with column summaries.")
10
+ }).strict();
11
+ export function registerSchema(server) {
12
+ server.registerTool("spear_schema", {
13
+ title: "Database Schema",
14
+ description: `Return the Spear PSA database schema — tables, columns, types, and relationships.
15
+
16
+ Use this before writing SQL queries to understand the data model. Returns column names, data types, nullable status, and foreign key relationships.
17
+
18
+ Tables: tickets, organizations, contacts, agents, ticket_notes, ticket_note_chunks, assets, contracts, projects, tasks, time_entries, teams_messages, teams_message_chunks, pipeline_runs, sync_state, ticket_statuses, ticket_priorities, ticket_types, ticket_sources, ticket_queues, ticket_audit_log, sla_policies.
19
+
20
+ Key relationships:
21
+ - tickets.organization_id → organizations.id
22
+ - tickets.assigned_agent_id → agents.id
23
+ - tickets.contact_id → contacts.id
24
+ - ticket_notes.ticket_id → tickets.id
25
+ - time_entries.ticket_id → tickets.id
26
+ - time_entries.agent_id → agents.id
27
+ - assets.organization_id → organizations.id
28
+ - contracts.organization_id → organizations.id
29
+ - ticket_note_chunks.ticket_id → tickets.id (ON DELETE CASCADE)`,
30
+ inputSchema: InputSchema,
31
+ annotations: {
32
+ readOnlyHint: true,
33
+ destructiveHint: false,
34
+ idempotentHint: true,
35
+ openWorldHint: false
36
+ }
37
+ }, async (params) => {
38
+ const start = performance.now();
39
+ try {
40
+ const sql = getPostgres(params.target);
41
+ if (params.table) {
42
+ // Detailed schema for a specific table
43
+ const columns = await sql `
44
+ SELECT c.column_name, c.data_type, c.is_nullable, c.column_default,
45
+ c.character_maximum_length,
46
+ tc.constraint_type,
47
+ ccu.table_name as referenced_table
48
+ FROM information_schema.columns c
49
+ LEFT JOIN information_schema.key_column_usage kcu
50
+ ON c.table_name = kcu.table_name AND c.column_name = kcu.column_name
51
+ AND c.table_schema = kcu.table_schema
52
+ LEFT JOIN information_schema.table_constraints tc
53
+ ON kcu.constraint_name = tc.constraint_name
54
+ AND tc.constraint_type = 'FOREIGN KEY'
55
+ LEFT JOIN information_schema.constraint_column_usage ccu
56
+ ON tc.constraint_name = ccu.constraint_name
57
+ WHERE c.table_schema = 'public'
58
+ AND c.table_name = ${params.table}
59
+ ORDER BY c.ordinal_position
60
+ `;
61
+ // Get row count — validate table name to prevent injection
62
+ const validTables = [
63
+ "tickets", "organizations", "contacts", "agents", "ticket_notes",
64
+ "ticket_note_chunks", "assets", "contracts", "projects", "tasks",
65
+ "time_entries", "teams_messages", "teams_message_chunks", "pipeline_runs",
66
+ "sync_state", "ticket_statuses", "ticket_priorities", "ticket_types",
67
+ "ticket_sources", "ticket_queues", "ticket_audit_log", "sla_policies"
68
+ ];
69
+ let rowCount = "unknown";
70
+ if (validTables.includes(params.table)) {
71
+ const countResult = await sql.unsafe(`SELECT COUNT(*)::int as count FROM "${params.table}"`).catch(() => [{ count: null }]);
72
+ rowCount = countResult[0]?.count ?? "unknown";
73
+ }
74
+ const ms = Math.round(performance.now() - start);
75
+ return {
76
+ content: [{
77
+ type: "text",
78
+ text: JSON.stringify({
79
+ _meta: { source: "postgres", target: params.target, execution_ms: ms },
80
+ table: params.table,
81
+ row_count: rowCount,
82
+ columns
83
+ }, null, 2)
84
+ }]
85
+ };
86
+ }
87
+ // All tables overview
88
+ const tables = await sql `
89
+ SELECT t.table_name,
90
+ (SELECT COUNT(*)
91
+ FROM information_schema.columns c
92
+ WHERE c.table_name = t.table_name AND c.table_schema = 'public'
93
+ )::int as column_count
94
+ FROM information_schema.tables t
95
+ WHERE t.table_schema = 'public' AND t.table_type = 'BASE TABLE'
96
+ ORDER BY t.table_name
97
+ `;
98
+ // Get columns for all tables in one query
99
+ const allColumns = await sql `
100
+ SELECT table_name, column_name, data_type, is_nullable
101
+ FROM information_schema.columns
102
+ WHERE table_schema = 'public'
103
+ ORDER BY table_name, ordinal_position
104
+ `;
105
+ // Group columns by table
106
+ const tableColumns = {};
107
+ for (const col of allColumns) {
108
+ const tn = col.table_name;
109
+ if (!tableColumns[tn])
110
+ tableColumns[tn] = [];
111
+ tableColumns[tn].push({
112
+ column_name: col.column_name,
113
+ data_type: col.data_type,
114
+ is_nullable: col.is_nullable
115
+ });
116
+ }
117
+ const ms = Math.round(performance.now() - start);
118
+ return {
119
+ content: [{
120
+ type: "text",
121
+ text: JSON.stringify({
122
+ _meta: { source: "postgres", target: params.target, execution_ms: ms },
123
+ tables: tables.map(t => ({
124
+ name: t.table_name,
125
+ column_count: t.column_count,
126
+ columns: tableColumns[t.table_name] ?? []
127
+ }))
128
+ }, null, 2)
129
+ }]
130
+ };
131
+ }
132
+ catch (e) {
133
+ const ms = Math.round(performance.now() - start);
134
+ return {
135
+ isError: true,
136
+ content: [{
137
+ type: "text",
138
+ text: JSON.stringify({
139
+ _meta: { source: "postgres", target: params.target, execution_ms: ms },
140
+ error: e.message
141
+ }, null, 2)
142
+ }]
143
+ };
144
+ }
145
+ });
146
+ }
147
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/tools/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerSemanticSearch(server: McpServer): void;
@@ -0,0 +1,432 @@
1
+ import { z } from "zod";
2
+ import { getPostgres } from "../db.js";
3
+ const GOOGLE_AI_API_KEY = process.env.GOOGLE_API_KEY ?? "";
4
+ const EMBED_SERVER_URL = process.env.EMBED_SERVER_URL ?? "http://studio:11434";
5
+ const EMBED_MODEL_GOOGLE = "gemini-embedding-001";
6
+ const EMBED_MODEL_SERVER = "nomic-embed-text";
7
+ const EMBED_DIM = 768;
8
+ const ENTITY_TYPES = [
9
+ "tickets", "organizations", "contacts", "agents", "assets",
10
+ "contracts", "projects", "tasks", "time_entries", "ticket_note_chunks",
11
+ "teams_message_chunks", "all"
12
+ ];
13
+ const InputSchema = z.object({
14
+ query: z.string()
15
+ .min(1, "Search query is required")
16
+ .describe("Natural language search query — will be embedded and matched against entity vectors via pgvector"),
17
+ entity: z.enum(ENTITY_TYPES)
18
+ .default("tickets")
19
+ .describe("Entity type to search: tickets (default), organizations, contacts, agents, assets, contracts, projects, tasks, time_entries, ticket_note_chunks, teams_message_chunks, or 'all' to search everything"),
20
+ target: z.enum(["local", "cloud"])
21
+ .default("cloud")
22
+ .describe("Which database to search: 'local' (Docker) or 'cloud' (Neon)"),
23
+ limit: z.number()
24
+ .int()
25
+ .min(1)
26
+ .max(50)
27
+ .default(10)
28
+ .describe("Maximum results to return (default 10, max 50)"),
29
+ score_threshold: z.number()
30
+ .min(0)
31
+ .max(1)
32
+ .default(0.3)
33
+ .describe("Minimum cosine similarity score (0-1) to include in results (default 0.3)")
34
+ }).strict();
35
+ async function embedQuery(text) {
36
+ // Try Google AI Studio first (globally accessible, best for production)
37
+ if (GOOGLE_AI_API_KEY) {
38
+ try {
39
+ const resp = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${EMBED_MODEL_GOOGLE}:embedContent?key=${GOOGLE_AI_API_KEY}`, {
40
+ method: "POST",
41
+ headers: { "Content-Type": "application/json" },
42
+ body: JSON.stringify({
43
+ model: `models/${EMBED_MODEL_GOOGLE}`,
44
+ content: { parts: [{ text }] },
45
+ outputDimensionality: EMBED_DIM
46
+ })
47
+ });
48
+ if (resp.ok) {
49
+ const data = await resp.json();
50
+ if (data.embedding?.values?.length === EMBED_DIM) {
51
+ return data.embedding.values;
52
+ }
53
+ }
54
+ // Google failed — fall through to remote server
55
+ }
56
+ catch {
57
+ // Google unreachable — fall through to remote server
58
+ }
59
+ }
60
+ // Fallback: remote embedding server (Mac Studio or similar)
61
+ const resp = await fetch(`${EMBED_SERVER_URL}/api/embed`, {
62
+ method: "POST",
63
+ headers: { "Content-Type": "application/json" },
64
+ body: JSON.stringify({ model: EMBED_MODEL_SERVER, input: [text] })
65
+ });
66
+ if (!resp.ok) {
67
+ const body = await resp.text();
68
+ throw new Error(`Embed server failed (${resp.status}): ${body}`);
69
+ }
70
+ const data = await resp.json();
71
+ if (!data.embeddings?.[0] || data.embeddings[0].length !== EMBED_DIM) {
72
+ throw new Error(`Expected ${EMBED_DIM}-dim vector, got ${data.embeddings?.[0]?.length ?? 0}`);
73
+ }
74
+ return data.embeddings[0];
75
+ }
76
+ async function searchTickets(sql, vectorStr, threshold, limit) {
77
+ return sql `
78
+ SELECT t.id,
79
+ t.ticket_number,
80
+ t.title,
81
+ LEFT(t.description, 300) AS description_snippet,
82
+ t.status,
83
+ t.priority,
84
+ t.ticket_type,
85
+ t.source,
86
+ o.name AS org_name,
87
+ COALESCE(a.first_name || ' ' || a.last_name, '') AS agent_name,
88
+ t.created_at,
89
+ t.completed_date,
90
+ t.sla_resolution_breached,
91
+ 1 - (t.embedding <=> ${vectorStr}::vector) AS similarity
92
+ FROM tickets t
93
+ LEFT JOIN organizations o ON o.id = t.organization_id
94
+ LEFT JOIN agents a ON a.id = t.assigned_agent_id
95
+ WHERE t.embedding IS NOT NULL
96
+ AND 1 - (t.embedding <=> ${vectorStr}::vector) >= ${threshold}
97
+ ORDER BY t.embedding <=> ${vectorStr}::vector
98
+ LIMIT ${limit}
99
+ `;
100
+ }
101
+ async function searchOrganizations(sql, vectorStr, threshold, limit) {
102
+ return sql `
103
+ SELECT o.id,
104
+ o.name,
105
+ o.phone,
106
+ o.city,
107
+ o.state,
108
+ o.tier,
109
+ o.active,
110
+ 1 - (o.embedding <=> ${vectorStr}::vector) AS similarity
111
+ FROM organizations o
112
+ WHERE o.embedding IS NOT NULL
113
+ AND 1 - (o.embedding <=> ${vectorStr}::vector) >= ${threshold}
114
+ ORDER BY o.embedding <=> ${vectorStr}::vector
115
+ LIMIT ${limit}
116
+ `;
117
+ }
118
+ async function searchContacts(sql, vectorStr, threshold, limit) {
119
+ return sql `
120
+ SELECT c.id,
121
+ c.first_name,
122
+ c.last_name,
123
+ c.email,
124
+ c.phone,
125
+ o.name AS org_name,
126
+ 1 - (c.embedding <=> ${vectorStr}::vector) AS similarity
127
+ FROM contacts c
128
+ LEFT JOIN organizations o ON o.id = c.organization_id
129
+ WHERE c.embedding IS NOT NULL
130
+ AND 1 - (c.embedding <=> ${vectorStr}::vector) >= ${threshold}
131
+ ORDER BY c.embedding <=> ${vectorStr}::vector
132
+ LIMIT ${limit}
133
+ `;
134
+ }
135
+ async function searchAgents(sql, vectorStr, threshold, limit) {
136
+ return sql `
137
+ SELECT a.id,
138
+ a.first_name,
139
+ a.last_name,
140
+ a.email,
141
+ a.active AS is_active,
142
+ 1 - (a.embedding <=> ${vectorStr}::vector) AS similarity
143
+ FROM agents a
144
+ WHERE a.embedding IS NOT NULL
145
+ AND 1 - (a.embedding <=> ${vectorStr}::vector) >= ${threshold}
146
+ ORDER BY a.embedding <=> ${vectorStr}::vector
147
+ LIMIT ${limit}
148
+ `;
149
+ }
150
+ async function searchAssets(sql, vectorStr, threshold, limit) {
151
+ return sql `
152
+ SELECT a.id,
153
+ a.name,
154
+ a.serial_number,
155
+ a.asset_type,
156
+ o.name AS org_name,
157
+ 1 - (a.embedding <=> ${vectorStr}::vector) AS similarity
158
+ FROM assets a
159
+ LEFT JOIN organizations o ON o.id = a.organization_id
160
+ WHERE a.embedding IS NOT NULL
161
+ AND 1 - (a.embedding <=> ${vectorStr}::vector) >= ${threshold}
162
+ ORDER BY a.embedding <=> ${vectorStr}::vector
163
+ LIMIT ${limit}
164
+ `;
165
+ }
166
+ async function searchContracts(sql, vectorStr, threshold, limit) {
167
+ return sql `
168
+ SELECT c.id,
169
+ c.name,
170
+ c.contract_type,
171
+ c.status,
172
+ o.name AS org_name,
173
+ 1 - (c.embedding <=> ${vectorStr}::vector) AS similarity
174
+ FROM contracts c
175
+ LEFT JOIN organizations o ON o.id = c.organization_id
176
+ WHERE c.embedding IS NOT NULL
177
+ AND 1 - (c.embedding <=> ${vectorStr}::vector) >= ${threshold}
178
+ ORDER BY c.embedding <=> ${vectorStr}::vector
179
+ LIMIT ${limit}
180
+ `;
181
+ }
182
+ async function searchProjects(sql, vectorStr, threshold, limit) {
183
+ return sql `
184
+ SELECT p.id,
185
+ p.name,
186
+ p.status,
187
+ o.name AS org_name,
188
+ 1 - (p.embedding <=> ${vectorStr}::vector) AS similarity
189
+ FROM projects p
190
+ LEFT JOIN organizations o ON o.id = p.organization_id
191
+ WHERE p.embedding IS NOT NULL
192
+ AND 1 - (p.embedding <=> ${vectorStr}::vector) >= ${threshold}
193
+ ORDER BY p.embedding <=> ${vectorStr}::vector
194
+ LIMIT ${limit}
195
+ `;
196
+ }
197
+ async function searchTasks(sql, vectorStr, threshold, limit) {
198
+ return sql `
199
+ SELECT tk.id,
200
+ tk.title,
201
+ tk.status,
202
+ p.name AS project_name,
203
+ 1 - (tk.embedding <=> ${vectorStr}::vector) AS similarity
204
+ FROM tasks tk
205
+ LEFT JOIN projects p ON p.id = tk.project_id
206
+ WHERE tk.embedding IS NOT NULL
207
+ AND 1 - (tk.embedding <=> ${vectorStr}::vector) >= ${threshold}
208
+ ORDER BY tk.embedding <=> ${vectorStr}::vector
209
+ LIMIT ${limit}
210
+ `;
211
+ }
212
+ async function searchTimeEntries(sql, vectorStr, threshold, limit) {
213
+ return sql `
214
+ SELECT te.id,
215
+ te.summary,
216
+ te.hours_worked,
217
+ te.date_worked,
218
+ t.ticket_number,
219
+ COALESCE(a.first_name || ' ' || a.last_name, '') AS agent_name,
220
+ 1 - (te.embedding <=> ${vectorStr}::vector) AS similarity
221
+ FROM time_entries te
222
+ LEFT JOIN tickets t ON t.id = te.ticket_id
223
+ LEFT JOIN agents a ON a.id = te.agent_id
224
+ WHERE te.embedding IS NOT NULL
225
+ AND 1 - (te.embedding <=> ${vectorStr}::vector) >= ${threshold}
226
+ ORDER BY te.embedding <=> ${vectorStr}::vector
227
+ LIMIT ${limit}
228
+ `;
229
+ }
230
+ async function searchTicketNoteChunks(sql, vectorStr, threshold, limit) {
231
+ return sql `
232
+ SELECT c.id,
233
+ LEFT(c.chunk_text, 500) AS chunk_snippet,
234
+ c.note_count,
235
+ c.chunk_index,
236
+ t.ticket_number,
237
+ t.title AS ticket_title,
238
+ t.status AS ticket_status,
239
+ t.priority AS ticket_priority,
240
+ o.name AS org_name,
241
+ 1 - (c.embedding <=> ${vectorStr}::vector) AS similarity
242
+ FROM ticket_note_chunks c
243
+ JOIN tickets t ON t.id = c.ticket_id
244
+ LEFT JOIN organizations o ON o.id = t.organization_id
245
+ WHERE c.embedding IS NOT NULL
246
+ AND 1 - (c.embedding <=> ${vectorStr}::vector) >= ${threshold}
247
+ ORDER BY c.embedding <=> ${vectorStr}::vector
248
+ LIMIT ${limit}
249
+ `;
250
+ }
251
+ async function searchTeamsMessageChunks(sql, vectorStr, threshold, limit) {
252
+ return sql `
253
+ SELECT c.id,
254
+ LEFT(c.chunk_text, 500) AS chunk_snippet,
255
+ c.channel_name,
256
+ c.message_count,
257
+ c.chunk_index,
258
+ array_to_string(c.author_names, ', ') AS authors,
259
+ c.earliest_message_at,
260
+ c.latest_message_at,
261
+ 1 - (c.embedding <=> ${vectorStr}::vector) AS similarity
262
+ FROM teams_message_chunks c
263
+ WHERE c.embedding IS NOT NULL
264
+ AND 1 - (c.embedding <=> ${vectorStr}::vector) >= ${threshold}
265
+ ORDER BY c.embedding <=> ${vectorStr}::vector
266
+ LIMIT ${limit}
267
+ `;
268
+ }
269
+ // Map entity name to its search function
270
+ const SEARCH_FNS = {
271
+ tickets: searchTickets,
272
+ organizations: searchOrganizations,
273
+ contacts: searchContacts,
274
+ agents: searchAgents,
275
+ assets: searchAssets,
276
+ contracts: searchContracts,
277
+ projects: searchProjects,
278
+ tasks: searchTasks,
279
+ time_entries: searchTimeEntries,
280
+ ticket_note_chunks: searchTicketNoteChunks,
281
+ teams_message_chunks: searchTeamsMessageChunks,
282
+ };
283
+ // Count embedded rows for a given table
284
+ async function countEmbedded(sql, table) {
285
+ const result = await sql.unsafe(`SELECT COUNT(*) AS total FROM ${table} WHERE embedding IS NOT NULL`);
286
+ return Number(result[0].total);
287
+ }
288
+ // Searchable entity types (excludes "all")
289
+ const SEARCHABLE_ENTITIES = ENTITY_TYPES.filter((e) => e !== "all");
290
+ export function registerSemanticSearch(server) {
291
+ server.registerTool("spear_semantic_search", {
292
+ title: "Semantic Search",
293
+ description: `Search for similar records across all entity types using natural language via pgvector in PostgreSQL.
294
+
295
+ Entities are embedded using gemini-embedding-001 (768-dim) stored directly in each table via pgvector.
296
+
297
+ Supported entity types: tickets (default), organizations, contacts, agents, assets, contracts, projects, tasks, time_entries, ticket_note_chunks, teams_message_chunks, or 'all' to search across every type simultaneously.
298
+
299
+ Each entity returns its relevant columns plus a cosine similarity score. When entity='all', results from all tables with embeddings are merged and sorted by score, with an entity_type field on each result.
300
+
301
+ ticket_note_chunks contains grouped technician notes (~3000 chars each) — use this to find specific resolution steps, troubleshooting details, or recurring patterns buried in note history.
302
+
303
+ teams_message_chunks contains grouped Microsoft Teams messages from TechTeam channels — use this to find informal tech discussions, escalation context, and troubleshooting knowledge that doesn't appear in formal ticket notes.
304
+
305
+ Use this for fuzzy/conceptual queries like "printer problems at law firms" or "recurring VPN issues" where exact SQL matching would miss results.
306
+
307
+ Results are ranked by cosine similarity with scores included. Higher score = more similar.`,
308
+ inputSchema: InputSchema,
309
+ annotations: {
310
+ readOnlyHint: true,
311
+ destructiveHint: false,
312
+ idempotentHint: true,
313
+ openWorldHint: true
314
+ }
315
+ }, async (params) => {
316
+ const start = performance.now();
317
+ try {
318
+ const sql = getPostgres(params.target);
319
+ const entity = params.entity;
320
+ // Determine which entities to search
321
+ const entitiesToSearch = entity === "all" ? [...SEARCHABLE_ENTITIES] : [entity];
322
+ // Check embedding counts for the target entities
323
+ const countResults = await Promise.all(entitiesToSearch.map(async (e) => ({
324
+ entity: e,
325
+ count: await countEmbedded(sql, e),
326
+ })));
327
+ const totalEmbedded = countResults.reduce((sum, r) => sum + r.count, 0);
328
+ const embeddedCounts = {};
329
+ for (const r of countResults) {
330
+ embeddedCounts[r.entity] = r.count;
331
+ }
332
+ // Filter to entities that actually have embeddings
333
+ const entitiesWithEmbeddings = countResults
334
+ .filter(r => r.count > 0)
335
+ .map(r => r.entity);
336
+ if (entitiesWithEmbeddings.length === 0) {
337
+ const ms = Math.round(performance.now() - start);
338
+ return {
339
+ content: [{
340
+ type: "text",
341
+ text: JSON.stringify({
342
+ _meta: {
343
+ source: "pgvector",
344
+ target: params.target,
345
+ entity: entity,
346
+ execution_ms: ms,
347
+ total_embedded: 0,
348
+ embedded_counts: embeddedCounts
349
+ },
350
+ results: [],
351
+ message: `No embeddings found for ${entity === "all" ? "any entity type" : entity}. Run the ingest pipeline with embedding enabled to populate pgvector embeddings.`
352
+ }, null, 2)
353
+ }]
354
+ };
355
+ }
356
+ // Embed the query
357
+ const embedStart = performance.now();
358
+ const queryVector = await embedQuery(params.query);
359
+ const embedMs = Math.round(performance.now() - embedStart);
360
+ const vectorStr = `[${queryVector.join(",")}]`;
361
+ const searchStart = performance.now();
362
+ let results;
363
+ if (entity === "all") {
364
+ // Run parallel searches across all entities with embeddings
365
+ const perEntityLimit = params.limit; // fetch full limit per entity, then merge
366
+ const allResults = await Promise.all(entitiesWithEmbeddings.map(async (e) => {
367
+ const rows = await SEARCH_FNS[e](sql, vectorStr, params.score_threshold, perEntityLimit);
368
+ return rows.map(r => ({
369
+ entity_type: e,
370
+ ...r,
371
+ score: Math.round(Number(r.similarity) * 1000) / 1000,
372
+ }));
373
+ }));
374
+ // Merge all results, sort by score descending, take top N
375
+ const merged = allResults.flat();
376
+ merged.sort((a, b) => b.score - a.score);
377
+ results = merged.slice(0, params.limit).map(r => {
378
+ const { similarity, ...rest } = r;
379
+ return rest;
380
+ });
381
+ }
382
+ else {
383
+ // Single-entity search
384
+ const searchResults = await SEARCH_FNS[entity](sql, vectorStr, params.score_threshold, params.limit);
385
+ results = searchResults.map(r => {
386
+ const { similarity, ...rest } = r;
387
+ return {
388
+ ...rest,
389
+ score: Math.round(Number(r.similarity) * 1000) / 1000,
390
+ };
391
+ });
392
+ }
393
+ const searchMs = Math.round(performance.now() - searchStart);
394
+ const ms = Math.round(performance.now() - start);
395
+ const embedProvider = GOOGLE_AI_API_KEY ? `Google ${EMBED_MODEL_GOOGLE}` : `${EMBED_MODEL_SERVER} @ ${EMBED_SERVER_URL}`;
396
+ const result = {
397
+ _meta: {
398
+ source: "pgvector",
399
+ target: params.target,
400
+ entity: entity,
401
+ execution_ms: ms,
402
+ embed_ms: embedMs,
403
+ search_ms: searchMs,
404
+ total_embedded: totalEmbedded,
405
+ embedded_counts: embeddedCounts,
406
+ result_count: results.length,
407
+ model: embedProvider,
408
+ query: params.query,
409
+ score_threshold: params.score_threshold
410
+ },
411
+ results
412
+ };
413
+ return {
414
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
415
+ };
416
+ }
417
+ catch (e) {
418
+ const ms = Math.round(performance.now() - start);
419
+ return {
420
+ isError: true,
421
+ content: [{
422
+ type: "text",
423
+ text: JSON.stringify({
424
+ _meta: { source: "pgvector", target: params.target, execution_ms: ms },
425
+ error: e.message
426
+ }, null, 2)
427
+ }]
428
+ };
429
+ }
430
+ });
431
+ }
432
+ //# sourceMappingURL=semantic-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-search.js","sourceRoot":"","sources":["../../src/tools/semantic-search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,WAAW,EAAe,MAAM,UAAU,CAAC;AAEpD,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;AAC3D,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,qBAAqB,CAAC;AAC/E,MAAM,kBAAkB,GAAG,sBAAsB,CAAC;AAClD,MAAM,kBAAkB,GAAG,kBAAkB,CAAC;AAC9C,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB,MAAM,YAAY,GAAG;IACnB,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ;IAC1D,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,oBAAoB;IACtE,sBAAsB,EAAE,KAAK;CACrB,CAAC;AAIX,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;SACd,GAAG,CAAC,CAAC,EAAE,0BAA0B,CAAC;SAClC,QAAQ,CAAC,kGAAkG,CAAC;IAC/G,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;SACzB,OAAO,CAAC,SAAS,CAAC;SAClB,QAAQ,CAAC,sMAAsM,CAAC;IACnN,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;SAC/B,OAAO,CAAC,OAAO,CAAC;SAChB,QAAQ,CAAC,8DAA8D,CAAC;IAC3E,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;SACd,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,EAAE,CAAC;SACP,OAAO,CAAC,EAAE,CAAC;SACX,QAAQ,CAAC,gDAAgD,CAAC;IAC7D,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;SACxB,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,CAAC,CAAC;SACN,OAAO,CAAC,GAAG,CAAC;SACZ,QAAQ,CAAC,2EAA2E,CAAC;CACzF,CAAC,CAAC,MAAM,EAAE,CAAC;AAIZ,KAAK,UAAU,UAAU,CAAC,IAAY;IACpC,wEAAwE;IACxE,IAAI,iBAAiB,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,KAAK,CACtB,2DAA2D,kBAAkB,qBAAqB,iBAAiB,EAAE,EACrH;gBACE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,KAAK,EAAE,UAAU,kBAAkB,EAAE;oBACrC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE;oBAC9B,oBAAoB,EAAE,SAAS;iBAChC,CAAC;aACH,CACF,CAAC;YACF,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAyC,CAAC;gBACtE,IAAI,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;oBACjD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;gBAC/B,CAAC;YACH,CAAC;YACD,gDAAgD;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,qDAAqD;QACvD,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,gBAAgB,YAAY,EAAE;QACxD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;KACnE,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAgC,CAAC;IAC7D,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,YAAY,SAAS,oBAAoB,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;AAC5B,CAAC;AAQD,KAAK,UAAU,aAAa,CAC1B,GAAmC,EACnC,SAAiB,EACjB,SAAiB,EACjB,KAAa;IAEb,OAAO,GAAG,CAAA;;;;;;;;;;;;;;kCAcsB,SAAS;;;;;iCAKV,SAAS,gBAAgB,SAAS;+BACpC,SAAS;YAC5B,KAAK;GACd,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,mBAAmB,CAChC,GAAmC,EACnC,SAAiB,EACjB,SAAiB,EACjB,KAAa;IAEb,OAAO,GAAG,CAAA;;;;;;;;kCAQsB,SAAS;;;iCAGV,SAAS,gBAAgB,SAAS;+BACpC,SAAS;YAC5B,KAAK;GACd,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,cAAc,CAC3B,GAAmC,EACnC,SAAiB,EACjB,SAAiB,EACjB,KAAa;IAEb,OAAO,GAAG,CAAA;;;;;;;kCAOsB,SAAS;;;;iCAIV,SAAS,gBAAgB,SAAS;+BACpC,SAAS;YAC5B,KAAK;GACd,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,YAAY,CACzB,GAAmC,EACnC,SAAiB,EACjB,SAAiB,EACjB,KAAa;IAEb,OAAO,GAAG,CAAA;;;;;;kCAMsB,SAAS;;;iCAGV,SAAS,gBAAgB,SAAS;+BACpC,SAAS;YAC5B,KAAK;GACd,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,YAAY,CACzB,GAAmC,EACnC,SAAiB,EACjB,SAAiB,EACjB,KAAa;IAEb,OAAO,GAAG,CAAA;;;;;;kCAMsB,SAAS;;;;iCAIV,SAAS,gBAAgB,SAAS;+BACpC,SAAS;YAC5B,KAAK;GACd,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,eAAe,CAC5B,GAAmC,EACnC,SAAiB,EACjB,SAAiB,EACjB,KAAa;IAEb,OAAO,GAAG,CAAA;;;;;;kCAMsB,SAAS;;;;iCAIV,SAAS,gBAAgB,SAAS;+BACpC,SAAS;YAC5B,KAAK;GACd,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,cAAc,CAC3B,GAAmC,EACnC,SAAiB,EACjB,SAAiB,EACjB,KAAa;IAEb,OAAO,GAAG,CAAA;;;;;kCAKsB,SAAS;;;;iCAIV,SAAS,gBAAgB,SAAS;+BACpC,SAAS;YAC5B,KAAK;GACd,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,GAAmC,EACnC,SAAiB,EACjB,SAAiB,EACjB,KAAa;IAEb,OAAO,GAAG,CAAA;;;;;mCAKuB,SAAS;;;;kCAIV,SAAS,gBAAgB,SAAS;gCACpC,SAAS;YAC7B,KAAK;GACd,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,iBAAiB,CAC9B,GAAmC,EACnC,SAAiB,EACjB,SAAiB,EACjB,KAAa;IAEb,OAAO,GAAG,CAAA;;;;;;;mCAOuB,SAAS;;;;;kCAKV,SAAS,gBAAgB,SAAS;gCACpC,SAAS;YAC7B,KAAK;GACd,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,sBAAsB,CACnC,GAAmC,EACnC,SAAiB,EACjB,SAAiB,EACjB,KAAa;IAEb,OAAO,GAAG,CAAA;;;;;;;;;;kCAUsB,SAAS;;;;;iCAKV,SAAS,gBAAgB,SAAS;+BACpC,SAAS;YAC5B,KAAK;GACd,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,wBAAwB,CACrC,GAAmC,EACnC,SAAiB,EACjB,SAAiB,EACjB,KAAa;IAEb,OAAO,GAAG,CAAA;;;;;;;;;kCASsB,SAAS;;;iCAGV,SAAS,gBAAgB,SAAS;+BACpC,SAAS;YAC5B,KAAK;GACd,CAAC;AACJ,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,GAA6D;IAC3E,OAAO,EAAE,aAAa;IACtB,aAAa,EAAE,mBAAmB;IAClC,QAAQ,EAAE,cAAc;IACxB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;IACpB,SAAS,EAAE,eAAe;IAC1B,QAAQ,EAAE,cAAc;IACxB,KAAK,EAAE,WAAW;IAClB,YAAY,EAAE,iBAAiB;IAC/B,kBAAkB,EAAE,sBAAsB;IAC1C,oBAAoB,EAAE,wBAAwB;CAC/C,CAAC;AAEF,wCAAwC;AACxC,KAAK,UAAU,aAAa,CAC1B,GAAmC,EACnC,KAAa;IAEb,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAC7B,iCAAiC,KAAK,8BAA8B,CACrE,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAED,2CAA2C;AAC3C,MAAM,mBAAmB,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAmC,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;AAErG,MAAM,UAAU,sBAAsB,CAAC,MAAiB;IACtD,MAAM,CAAC,YAAY,CACjB,uBAAuB,EACvB;QACE,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE;;;;;;;;;;;;;;2FAcwE;QACrF,WAAW,EAAE,WAAW;QACxB,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,IAAI;SACpB;KACF,EACD,KAAK,EAAE,MAAa,EAAE,EAAE;QACtB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACvC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YAE7B,qCAAqC;YACrC,MAAM,gBAAgB,GACpB,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAEzD,iDAAiD;YACjD,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,GAAG,CACpC,gBAAgB,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;gBACjC,MAAM,EAAE,CAAC;gBACT,KAAK,EAAE,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC,CAAC;aACnC,CAAC,CAAC,CACJ,CAAC;YAEF,MAAM,aAAa,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YACxE,MAAM,cAAc,GAA2B,EAAE,CAAC;YAClD,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;gBAC7B,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YACrC,CAAC;YAED,mDAAmD;YACnD,MAAM,sBAAsB,GAAG,YAAY;iBACxC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;iBACxB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAEtB,IAAI,sBAAsB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;gBACjD,OAAO;oBACL,OAAO,EAAE,CAAC;4BACR,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACnB,KAAK,EAAE;oCACL,MAAM,EAAE,UAAU;oCAClB,MAAM,EAAE,MAAM,CAAC,MAAM;oCACrB,MAAM,EAAE,MAAM;oCACd,YAAY,EAAE,EAAE;oCAChB,cAAc,EAAE,CAAC;oCACjB,eAAe,EAAE,cAAc;iCAChC;gCACD,OAAO,EAAE,EAAE;gCACX,OAAO,EAAE,2BAA2B,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM,mFAAmF;6BACrK,EAAE,IAAI,EAAE,CAAC,CAAC;yBACZ,CAAC;iBACH,CAAC;YACJ,CAAC;YAED,kBAAkB;YAClB,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YACrC,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,CAAC;YAE3D,MAAM,SAAS,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAC/C,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YAEtC,IAAI,OAAuC,CAAC;YAE5C,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;gBACrB,4DAA4D;gBAC5D,MAAM,cAAc,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,0CAA0C;gBAC/E,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAClC,sBAAsB,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;oBACrC,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC;oBACzF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;wBACpB,WAAW,EAAE,CAAC;wBACd,GAAG,CAAC;wBACJ,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI;qBACtD,CAAC,CAAC,CAAC;gBACN,CAAC,CAAC,CACH,CAAC;gBAEF,0DAA0D;gBAC1D,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;gBACjC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;gBACzC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;oBAC9C,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC;oBAClC,OAAO,IAAI,CAAC;gBACd,CAAC,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,uBAAuB;gBACvB,MAAM,aAAa,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;gBACrG,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;oBAC9B,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC;oBAClC,OAAO;wBACL,GAAG,IAAI;wBACP,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI;qBACtD,CAAC;gBACJ,CAAC,CAAC,CAAC;YACL,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC,CAAC;YAC7D,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;YACjD,MAAM,aAAa,GAAG,iBAAiB,CAAC,CAAC,CAAC,UAAU,kBAAkB,EAAE,CAAC,CAAC,CAAC,GAAG,kBAAkB,MAAM,gBAAgB,EAAE,CAAC;YAEzH,MAAM,MAAM,GAAG;gBACb,KAAK,EAAE;oBACL,MAAM,EAAE,UAAU;oBAClB,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,MAAM,EAAE,MAAM;oBACd,YAAY,EAAE,EAAE;oBAChB,QAAQ,EAAE,OAAO;oBACjB,SAAS,EAAE,QAAQ;oBACnB,cAAc,EAAE,aAAa;oBAC7B,eAAe,EAAE,cAAc;oBAC/B,YAAY,EAAE,OAAO,CAAC,MAAM;oBAC5B,KAAK,EAAE,aAAa;oBACpB,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,eAAe,EAAE,MAAM,CAAC,eAAe;iBACxC;gBACD,OAAO;aACR,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aAC5E,CAAC;QACJ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;YACjD,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,KAAK,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,EAAE;4BACtE,KAAK,EAAG,CAAW,CAAC,OAAO;yBAC5B,EAAE,IAAI,EAAE,CAAC,CAAC;qBACZ,CAAC;aACH,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerSqlQuery(server: McpServer): void;