Skip to main content

sqlserver_mcp_catalog/tools/
search_tool.rs

1// SQL Server 2025 - master/msdb/sandbox combined catalog MCP server — generated by mcpify. Do not hand-edit.
2
3use rusqlite::Connection;
4
5use crate::data::store::search_endpoints;
6use crate::services::embedding_service::embed;
7
8/// Cheap diagnostic: compares `semantic_endpoints` row count against
9/// `endpoints` row count for the store behind `conn`. A store can end up
10/// with `endpoints` rows but no matching `semantic_endpoints` rows if the
11/// `populate-embeddings` step silently skipped or partially failed some
12/// rows — this surfaces that as a warning without changing the
13/// success/`[]` return contract for legitimately-empty search results.
14fn warn_if_embeddings_incomplete(conn: &Connection) {
15    let counts: rusqlite::Result<(i64, i64)> = conn.query_row(
16        "SELECT (SELECT COUNT(*) FROM endpoints), (SELECT COUNT(*) FROM semantic_endpoints)",
17        [],
18        |row| Ok((row.get(0)?, row.get(1)?)),
19    );
20    if let Ok((endpoints_count, semantic_count)) = counts
21        && semantic_count < endpoints_count
22    {
23        tracing::warn!(
24            endpoints_count,
25            semantic_count,
26            "semantic_endpoints is missing {} row(s) compared to endpoints — search results \
27             may be incomplete; re-run populate-embeddings for this store",
28            endpoints_count - semantic_count
29        );
30    }
31}
32
33/// Semantic similarity lookup matching a natural-language query against
34/// candidate API operations (PRD §1.5) — so an LLM never needs the full
35/// OpenAPI spec in context to find the right operation.
36pub fn search_operations(
37    conn: &Connection,
38    query: &str,
39    limit: usize,
40) -> anyhow::Result<serde_json::Value> {
41    warn_if_embeddings_incomplete(conn);
42    let query_embedding = embed(query)?;
43    let results = search_endpoints(conn, &query_embedding, limit)?;
44    Ok(serde_json::to_value(results)?)
45}