fix: MongoDB support for the MCP server (driver-aware tools + connection awareness) (#180)#182
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>
The previous commit registered tools per queryMode. That left a gap: a
client that connected before a database was selected (or while a different
DB type was connected) caches tools/list, and the stateless transport
can't push tools/list_changed — so after the user connects/switches to
MongoDB the agent still holds the SQL tools and calling execute_query hits
a server with no such tool ("tool not found"), with no hint why.
Flip the design so the tool list is invariant across connections, which
removes the stale-cache problem entirely:
- Always register both the SQL and MongoDB tool sets plus a new
connection_info tool. tools/list never changes with the DB type, so a
cached list is never wrong.
- Each domain tool validates the live queryMode at call time
(requireMode). A tool from the wrong family returns an actionable error
naming the connected DB type and the tools that apply, and telling the
agent to re-check connection_info / re-list / reconnect.
- connection_info reports the connected DB type, query language, write
state, and recommended tools so the agent can orient up front and
detect a mid-session switch.
- Instructions are generic but name the currently-connected DB and point
at connection_info.
Updates mcp.test.ts: asserts the invariant toolset, connection_info
output, and that cross-family calls return actionable mismatch errors
instead of "tool not found".
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: MongoDB MCP Support (#182)
Overall AssessmentThis is a well-designed, well-motivated fix. The invariant-toolset + per-call mode guard pattern is the right approach for stateless MCP transports where clients cache One medium-severity correctness bug and a few small issues follow. Bug:
|
- aggregate_documents now appends {$limit: cap + 1} to the pipeline so the
MongoDB server bounds the result set instead of loading the whole
aggregation into Node and slicing — matching find_documents. The +1
still lets us report truncation without a full scan.
- Tidy describe_collection's optional getCollectionInfo call.
- Add MCP-layer tests for aggregate_documents (limit + truncation),
count_documents, list_collections, describe_collection, and
delete_document; close client/server pairs in the invariant-toolset test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks for the review — addressed in 73ab9a3. Bug — Minor 1 — Minor 2 — test coverage gaps ✅ Added MCP-layer tests for Minor 3 — test cleanup ✅ The invariant-toolset test now closes both client/server pairs. Minor 4 — empty
|
Fixes #180. (Supersedes #181.)
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
The MCP tool list is now invariant across connections, and each tool validates the live connection at call time. Two layers:
1. MongoDB tools. Native typed tools that expose MQL fields (
filter,projection,sort,pipeline,document,update, …) as parameters and assemble the JSON-encoded request the driver already parses — the model never hand-authors MQL JSON:execute_query,execute_write,list_tables,describe_table(unchanged)find_documents,aggregate_documents,count_documents,list_collections,describe_collectioninsert_document,update_document,delete_documentdescribe_collectionalso surfaces indexes + the JSON Schema validator via the driver's existinggetCollectionInfo().2. The agent always knows SQL vs NoSQL, even across a mid-session connect/switch. This is the key design point. An agent often connects its MCP client before a database is selected (typical for a troubleshooting session). The transport is stateless, so the server can't push
tools/list_changed— a per-mode tool list would leave the agent holding a stale cached set and hitting cryptic "tool not found" after the DB is connected. Instead:connection_infotool), sotools/listnever changes with DB type — there's no stale cache to invalidate.connection_info(always available) reports the connected DB type, query language, write state, and recommended tools — the agent calls it to orient up front and to detect a switch.connection_info.This is client-agnostic (doesn't depend on a client honoring
tools/list_changed) and self-correcting.Tests
New
server/src/mcp.test.ts(9 tests, via the SDK in-memory transport):connection_inforeports DB type / query language / recommended toolsconnection_infofind_documentsassembles the correct{collection, operation:'find', filter, limit}JSON and passes schema throughinsert_documentblocked when writes disabled, allowed when enabledupdate_documentrequires afilteroridAll 171 server tests pass;
mcp.tsis type- and 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.🤖 Generated with Claude Code