fix: driver-aware MCP toolset so MongoDB connections work (#180)#181
fix: driver-aware MCP toolset so MongoDB connections work (#180)#181eugene-mobile wants to merge 1 commit into
Conversation
The MCP server always registered SQL-shaped tools (execute_query,
execute_write, ...) regardless of the connected driver. On a MongoDB
connection the SQL keyword gate and the MQL driver imposed mutually
exclusive requirements, so no query could return a single row.
buildMcpServer() now branches on getDriver().queryMode and registers
one of two mutually-exclusive tool sets:
- sql -> execute_query / execute_write / list_tables / describe_table
- mql -> find_documents / aggregate_documents / count_documents /
list_collections / describe_collection (reads) and
insert_document / update_document / delete_document (writes,
gated by "Allow MCP to modify data")
The Mongo tools expose MQL fields as typed params and assemble the
JSON-encoded request the driver already parses, so the model never
hand-authors MQL JSON. Server instructions are now mode-aware and name
the connected DB type, so the agent knows upfront whether to use SQL or
MongoDB tools and never attempts the wrong query language.
Adds server/src/mcp.test.ts covering toolset selection per mode,
mode-aware instructions, MQL request assembly, and write gating.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@claude review this PR |
|
Claude finished @eugene-mobile's task in 2m 23s —— View job PR Review: fix/mcp-mongodb-tools
SummaryThis is a well-designed fix for a real, clearly-documented bug. The architecture is clean: Below are the findings, ordered by severity. Medium — Aggregate limit is applied client-side (memory footprint)File:
// find_documents — server-side limit ✓
const result = await runMql(
{ collection, operation: 'find', ..., limit: cap, skip },
schema,
);
// aggregate_documents — all rows fetched, THEN sliced ✗
const result = await runMql({ collection, operation: 'aggregate', pipeline }, schema);
const totalRows = result.rows.length;
const capped = result.rows.slice(0, cap);The tool description says "For large pipelines, add an explicit Suggested fix: append a const limitedPipeline = [...pipeline, { $limit: cap }];
const result = await runMql({ collection, operation: 'aggregate', pipeline: limitedPipeline }, schema);This makes the behavior consistent with Medium — Empty
|
Follow-up: handle DB connect/switch mid-sessionAddressing review feedback — the original per-mode registration had a real gap, not a minor one: an agent that connects its MCP client before a database is selected (the common case for a troubleshooting session) caches Fix: invariant tool list + per-call mode guardsThe tool list is now the same regardless of connected DB, so there is no stale cache to invalidate:
This is client-agnostic (doesn't depend on a client honoring |
Fixes #180.
Problem
When Helix is connected to MongoDB, the MCP server is unusable for AI agents — no query returns a single row. The tools were hard-coded to be SQL-shaped regardless of the connected driver, and the SQL keyword gate (
firstKeyword) and the MQL driver impose mutually-exclusive requirements:See #180 for the full write-up and an agent transcript hitting the dead-end.
Fix
buildMcpServer()now branches ongetDriver().queryModeand registers one of two mutually-exclusive tool sets:sqlexecute_query,execute_write,list_tables,describe_table(unchanged)mqlfind_documents,aggregate_documents,count_documents,list_collections,describe_collection· writes (gated):insert_document,update_document,delete_documentThe MongoDB tools expose MQL fields (
filter,projection,sort,pipeline,document,update, …) as typed parameters and assemble the JSON-encoded request the driver already parses — the model never hand-authors MQL JSON.describe_collectionalso surfaces indexes + the JSON Schema validator via the driver's existinggetCollectionInfo().Agents know SQL vs NoSQL upfront two ways: (1) the wrong tools aren't registered (an agent on Mongo never sees
execute_query), and (2) the MCPinstructionsare mode-aware and name the connected DB type, e.g. "This is a MongoDB (document) database… Do NOT write SQL."This slots into the existing architecture cleanly: the transport is stateless and
buildMcpServer()already runs per request, sotools/listreflects the currently-connected database.Tests
New
server/src/mcp.test.ts(7 tests, via the SDK in-memory transport):execute_query/execute_writefind_documentsassembles the correct{collection, operation:'find', filter, limit}JSON and passes schema throughinsert_documentblocked when writes disabled, allowed when enabledupdate_documentrequires afilteroridAll 169 server tests pass;
mcp.tsandmcp.test.tsare type-clean andmcp.tsis lint-clean. (3 pre-existingtscerrors in unrelated driver test files are untouched.)Known follow-ups (out of scope)
_idObjectId matching infind/countfilters (string_idmatched literally; relates to existing TODO Create MongoDB DbDriver implementation #107). Anidconvenience param is exposed on update/delete.tools/list_changedpush if the user switches DB type mid-session.🤖 Generated with Claude Code