Skip to content

[POC - Do not review / Merge] Compass mcp integration#8084

Open
mcasimir wants to merge 28 commits into
mainfrom
compass-mcp-skunk
Open

[POC - Do not review / Merge] Compass mcp integration#8084
mcasimir wants to merge 28 commits into
mainfrom
compass-mcp-skunk

Conversation

@mcasimir
Copy link
Copy Markdown
Collaborator

@mcasimir mcasimir commented May 18, 2026

Poc: install the mcp server from Compass, share connections and expose saved queries as prompts (I made sure new 3rd party deps are ok).

Screenshot 2026-05-18 at 08 51 04 Screenshot 2026-05-18 at 08 51 29 Screenshot 2026-05-18 at 08 51 58 Screenshot 2026-05-18 at 08 57 47 Screenshot 2026-05-18 at 09 01 41 Screenshot 2026-05-18 at 09 01 57 Screenshot 2026-05-18 at 09 03 07 Screenshot 2026-05-13 at 20 45 36

mcasimir added 28 commits May 11, 2026 16:38
Embeds a Streamable HTTP MCP server inside Compass so tools like Claude
Desktop and Cursor can run read-only MongoDB queries against Compass-managed
connections.

What's included:
- New `@mongodb-js/compass-mcp-server` package wrapping `mongodb-mcp-server`
  with Bearer token auth, Origin validation (DNS-rebinding protection), and
  loopback-only binding (127.0.0.1:27097)
- Per-MCP-session `CompassConnectionManager` that resolves connection IDs from
  Compass storage and runs a per-connection consent flow before connecting
- `list-connections` custom tool so AI clients can discover available connections
- Per-connection consent stored as `mcpAccess` on `ConnectionInfo` (persisted
  via existing `ConnectionStorage`)
- `enableMcpServer` user preference (toggle) and `mcpServerToken` internal
  preference (32-byte random, OS-encrypted via safeStorage)
- Main-process setup: token generation, IPC consent dialog, start/stop on
  preference change, graceful shutdown on exit
- "AI Tools" settings tab with server status badge, URL, masked token, and
  Claude Desktop config snippet with one-click copy
- Renderer-side `McpConsentDialog` with "Remember my choice" checkbox
- Security regression test suite (401/403/loopback enforcement)
- Generate bearer token at setupMcpServer() startup rather than lazily
  on first server start, so it appears in Settings → AI Tools immediately
- Register ipcMain.respondTo('mcp:get-status') handler for synchronous
  status queries from the renderer
- Query status on mount in mcp-server-settings to show correct badge
  when the settings panel opens after the server has already started
Move all MCP server lifecycle logic (token generation, start/stop,
IPC handlers, preference listener) out of application.ts and into
CompassMcpServerManager in the compass-mcp-server package.

The call site in application.ts now mirrors setupCompassAuthService():
a 5-line method that calls CompassMcpServerManager.init() and registers
an exit handler via onExit().
…ayout

- Move MCP server settings into the Artificial Intelligence tab instead
  of a separate AI Tools tab; remove the mcp SettingsTabId
- Read mcpServerToken via usePreferences() hook so it is visible even
  when the server has not yet started (was unset in the Redux settings
  sandbox which only loads UserConfigurablePreferences)
- Show URL, token and snippet unconditionally; only the server status
  badge is gated on enableMcpServer being true
- Initialize status as null so no fake Stopped badge appears before
  the IpcRenderer.call('mcp:get-status') round-trip completes
- Add vertical tab layout for Claude Desktop, Cursor, VS Code, Windsurf
  — each tab shows the correct snippet format for that client plus
  the config file path to paste it into
- VS Code uses "servers" key (not "mcpServers") per its MCP spec
- Remove enableMcpServer from usePreferences (status display no longer
  gated on the preference value)
- Replace AllTools spread with an explicit allowlist of read-only
  MongoDB data-plane tools: list-databases, list-collections, schema,
  indexes, storage-size, find, count, aggregate, db-stats, explain
- Remove all Atlas control-plane tools (clusters, projects, orgs,
  alerts, db users, etc.), Atlas Local deployment management tools,
  knowledge/assistant tools, and the export/logs tools
- Add CompassConnectTool that accepts a connectionId argument and a
  description that points at list-connections, so external AI clients
  cannot supply a raw MongoDB connection string. The upstream connect
  tool is no longer registered.
Adds an Install / Update / Installed-checkmark button to each client tab
in Settings → AI. Clicking it writes (or updates) the mongodb-compass
entry in that client's user-level MCP config file — claude_desktop_config.json,
cursor/mcp.json, Code/User/mcp.json, windsurf/mcp_config.json — using
jsonc-parser to preserve any existing entries, comments, and trailing
commas the user already has in those files.

This mirrors the behavior of `npx mongodb-mcp-server@latest setup`, but
driven from the existing Settings UI rather than a CLI wizard, since we
already know the bridge command (we generate it from process.execPath)
and the client (the active tab).

Modules:
- auto-setup.ts: install/update/uninstall/detect with jsonc-parser
- client-paths.ts: single source of truth for per-client config paths and
  the mcpServers vs servers JSON key
- new IPC handlers in CompassMcpServerManager: mcp:install-in-client,
  mcp:uninstall-from-client, mcp:detect-in-client
- mcp-server-settings.tsx: detection-driven button with three states
  (Install / Update / Installed ✓), with a per-tag nonce + per-client
  result tag to avoid races when switching tabs quickly
…o mode

In dev mode webpack-dev-server's HMR client logs '[HMR] Waiting for
update signal from WDS...' to stdout, corrupting the MCP JSON-RPC
stream and producing "Unexpected token 'H'" errors in Claude Desktop.

Install a stdout filter as the very first statement in the entry: any
write whose first byte isn't `{` (JSON object) or `\n` (trailing
newline from a previous chunk) is redirected to stderr. The MCP bridge
only ever emits newline-delimited JSON objects on stdout, so this is
a safe filter; HMR / WDS / webpack diagnostic noise still shows up in
stderr for debugging but no longer breaks the protocol.
Large JSON-RPC responses from socket.pipe(process.stdout) — like
tools/list with all the schemas, ~13KB — arrive at the bridge as
multiple ~8KB chunks. The previous filter only looked at the leading
byte of each chunk: the first chunk passed (starts with '{'), but
continuation chunks (starting mid-string) were misclassified as noise
and redirected to stderr. The receiving MCP client got a JSON message
truncated at position 8192 and failed with "Expected ',' or '}' after
property value in JSON at position 8192".

Track whether we're at a message boundary across writes. When at a
boundary, gate the chunk on its first byte ('{' = MCP, anything else =
HMR/WDS noise to stderr). When mid-message, forward verbatim and only
flip back to boundary state when we see a trailing '\n'. This makes
the filter correct for any chunking the kernel/Node hand us.
Adds a new MCP tool that, instead of returning data, asks the running
Compass GUI to open a collection in a workspace tab so the user can
browse it interactively. Useful for "let me show you" responses where
the AI hands off to the GUI rather than dumping documents back as text.

Wiring:
- New CompassOpenCollectionTool registered in COMPASS_TOOLS, accepts
  connectionId + database + collection.
- New CompassToolContext shared by all Compass tools, with an
  openCollection(connectionId, namespace) callback alongside the
  existing getAllConnections.
- Manager's openCollection implementation broadcasts mcp:open-collection
  to the focused window via hadron-ipc.broadcastFocused.
- New McpNavigationListener renderer component (mounted inside the
  WorkspacesProvider scope) listens for that IPC and calls
  useOpenWorkspace().openCollectionWorkspace.

The tool never returns any data — it's pure navigation intent. If the
connection isn't active, Compass handles the connect prompt itself.
Renamed ListConnectionsContext -> CompassToolContext to reflect that
it's now the shared per-session context for all Compass tools.
When an AI client calls compass-open-collection, the OS-focused window
is the AI client (Claude Desktop / Cursor / ...) — not Compass — so
`ipcMain.broadcastFocused` silently dropped the message. Use
`ipcMain.broadcast` instead so every Compass renderer receives the
event regardless of focus.

Also adds a one-line console.log in the renderer listener so we can
verify IPC delivery in DevTools while iterating on this feature.
Two changes that together fix "tabs disappear" when the AI calls
compass-open-collection:

1. Pass { newTab: true } to openCollectionWorkspace. With the default
   (newTab=false) the workspaces reducer REPLACES the currently active
   tab — when the user is on, say, the Welcome screen, that tab gets
   swapped out for the collection tab, which gave the impression that
   "every tab disappeared". With newTab:true the collection tab is
   inserted next to the active one, never destroying existing state.

2. Subscribe to the IPC channel exactly once on mount by parking
   openCollectionWorkspace on a ref. The hook returns a fresh function
   each render; the previous code re-attached the listener on every
   render which was harmless but noisy.

Also wraps the dispatch in try/catch so any failure surfaces in
DevTools instead of silently breaking the render.
When the AI calls compass-open-collection for a connection that is
saved in Compass but not currently active, openCollectionWorkspace
opens a tab whose backing data service does not exist — the tab strip
loses focus and nothing visibly renders, even though the new tab is
in state.

The renderer listener now mirrors the sidebar's behavior for a click
on a saved (disconnected) connection: look up the ConnectionState, and
if status !== 'connected' dispatch the standard `connect(info)` action
(triggering the same auth / elicitation modals the user would see
clicking the connection themselves). We await that dispatch and only
then open the workspace tab so the Collection view has a working
data service to render against.
The tool now hands a richer intent to Compass:

  subtab       Documents | Aggregations | Schema | Indexes | Validation
  filter, projection, sort, limit  -> pre-fills the documents query bar
  pipeline     -> pre-fills the aggregation builder; auto-implies the
                  Aggregations subtab when not set explicitly

These map to openCollectionWorkspace's `initialSubtab`,
`initialQuery`, `initialPipeline` options so the user lands directly
in the right view with the AI's draft loaded. Tool description nudges
the model to use this for HAND-OFF (iterating, exploring, optimizing)
— not for showing query results, which still go inline.

Two cross-cutting UX bits:

- Subtle attention nudge when a tab opens: app.dock.bounce on macOS,
  BrowserWindow.flashFrame on Linux/Windows. Doesn't steal focus from
  the AI client but makes the hand-off visible.
- newTab:false in the renderer listener, matching the sidebar's
  "click a collection" semantics. Compass's canReplaceTab() protects
  tabs holding unsaved work, so we de-dupe onto an existing tab when
  one matches and otherwise replace the (likely-idle) active tab.
Two related discoverability changes.

1. Move MCP server settings to a dedicated 'MCP Server' tab in Settings.

   The Artificial Intelligence tab had become cramped — toggle + four
   client setup tabs + install buttons + config snippets stacked below
   the GenAI toggles. Restore 'mcp' as a SettingsTabId, register the
   tab in the settings modal next to AI, and remove the embedded
   <McpServerSettings /> from gen-ai-settings.tsx. The AI tab returns
   to just the GenAI controls.

2. Welcome tab announcement card.

   Add a sibling card to the existing Atlas help block on the desktop
   welcome tab that announces the MCP integration and deep-links into
   Settings -> MCP Server in one click. Emits
   globalAppRegistry.emit('open-compass-settings', 'mcp') — the same
   mechanism welcome-modal-store already uses to deep-link Privacy.

   The card is hidden once enableMcpServer is true so existing users
   don't see a permanent nag; renders for first-time users in either
   the "no connections yet" or "has saved connections" welcome states.

Three new tests cover: presence when MCP is off, hidden when on, and
the open-compass-settings emit on button click. 21/21 welcome tests
pass.
…l-access)

ConnectionInfo.mcpAccess becomes a discriminated union with three
modes (denied / ask / allowed) and a preset when allowed:

  type McpAccess =
    | { mode: 'denied' }
    | { mode: 'ask' }
    | { mode: 'allowed'; preset: McpPreset };

  type McpPreset = 'metadata-only' | 'read-only' | 'full-access';

Legacy 'allowed'/'denied' string values are read transparently via
normalizeMcpAccess() — 'allowed' maps to read-only (today's behavior),
'denied' to denied, anything else (or undefined) to ask.

Per-preset tool allowlist lives in compass-mcp-server/src/presets.ts:

  - metadata-only: list/schema/indexes/storage-size/db-stats/explain
  - read-only:     above + find / count / aggregate
  - full-access:   above + writes (insert/update/delete + DDL)

Enforcement
-----------
CompassConnectionManager.connect() resolves the connection's preset
(persisting the user's consent-dialog choice if 'ask') and stores it
as `activePreset`. The per-session tool context wraps it in a
checkAccess(toolName) helper that throws McpAccessDeniedError when
the tool isn't in the preset's allowlist.

Our own tools call checkAccess at the top of execute(). Upstream
mongodb-mcp-server tools are wrapped via withAccessCheck() — a small
HOC that subclasses each ToolBase to inject the gate before execute()
fires. Lives in build-tool-context.ts alongside buildToolContext().

Consent dialog
--------------
mcp-consent-dialog renders three preset radios with explanatory
descriptions. Default is read-only (matches today's allow behavior).
Full-access reveals an inline warning and requires a second confirm
checkbox before Allow is enabled. The 'remember' checkbox persists
the full McpAccess struct (mode + preset).

Connection form AI access tab is intentionally deferred to a
follow-up — the consent dialog covers the v1 surface area; the tab
requires plumbing mcpAccess through the connection-form's update
mechanism (which today only handles ConnectionOptions fields).

Tests
-----
New presets.spec.ts: preset allowlist invariants (metadata-only ⊂
read-only ⊂ full-access, always-on tools, unknown tools rejected) +
normalizeMcpAccess legacy-shape migration. 25/25 mcp-server tests
pass including all 7 existing security regressions.
…d access; welcome card stays visible

Three smaller v2 items shipped together:

1.5 Fail-safe normalizeMcpAccess
   When the on-disk mcpAccess shape is {mode: 'allowed'} with an unknown
   or missing preset, treat it as {mode: 'ask'} rather than silently
   downgrading to read-only. Corrupted records, downgrade from a future
   schema, or tampering all fail toward re-prompting the user rather
   than toward an implicit allow. Legacy known strings ('allowed' /
   'denied') keep their deterministic migration path.

2  Welcome card stays visible when MCP is on
   The discovery card on the Welcome tab no longer disappears once
   enableMcpServer becomes true. Instead, the body copy and CTA label
   adapt to a 'manage MCP setup' variant pointing at Settings -> MCP
   Server. This is the right entry point for users coming back to
   install MCP in another AI client or refresh a snippet after switching
   between dev / packaged Compass.

3  Full-access really means full
   - Register the upstream write/DDL tools (InsertMany, UpdateMany,
     DeleteMany, CreateIndex, DropIndex, CreateCollection,
     DropCollection, RenameCollection, DropDatabase) wrapped in
     withAccessCheck so they are denied by the gate for non-full-access
     presets and run normally for full-access.
   - Flip userConfig.readOnly: false in both transports so upstream
     doesn't strip $out / $merge inside the aggregate tool.
   - Add aggregateStageGate to enforce the equivalent restriction at our
     own layer for read-only / metadata-only — pipelines containing
     $out / $merge are rejected with McpAccessDeniedError unless the
     active preset is full-access.

32/32 mcp-server tests pass (6 new aggregate-stage gate tests +
updated fail-safe tests). 21/21 welcome tests pass.
Adds an "AI access" tab inside the Connection form for editing
ConnectionInfo.mcpAccess — the per-connection MCP policy that
previously could only be set via the consent dialog at first use.

Surface
-------
- Access mode radio: Deny / Ask each session / Allow
- When Allow is selected, a preset radio: Metadata only / Read-only
  data / Full access. Full-access requires a confirmation checkbox
  before propagating to form state (mirrors the consent dialog).
- A tool-list preview below the preset radio renders presetTools(p),
  so users see exactly which MCP tools the AI will be able to use.

Opt-in flag — does not affect other products
--------------------------------------------
Following the showCSFLE precedent, the tab is gated on a new
ConnectionFormSettings.showAiAccess flag, defaulting to false. The
desktop Compass useConnectionFormPreferences sets it to true only
when HADRON_DISTRIBUTION !== 'compass-web', so compass-web (and any
other host that embeds connection-form without running an MCP server)
sees zero UI change. ConnectionInfo.mcpAccess is still
read-through/write-through unconditionally so the data is never lost
even when the tab is hidden.

Form plumbing
-------------
- ConnectFormState gains `mcpAccess: McpAccess`, initialized via
  normalizeMcpAccess(initialConnectionInfo.mcpAccess).
- New `UpdateMcpAccessAction` action + reducer case (an early-return
  branch in handleConnectionFormFieldUpdate that touches nothing else).
- isDirty flips when mcpAccess changes, so Save activates.
- getConnectionInfoToSave copies state.mcpAccess back into the
  resulting ConnectionInfo. Same precedent as `personalizationOptions`
  -> `favorite`.

Adds @mongodb-js/compass-mcp-server as a dep of connection-form so
the tab can pull presetTools / presetLabel directly from the same
source of truth the runtime gate uses — guarantees the displayed
tool list always matches what the AI will actually be able to call.

455/455 existing connection-form tests pass; no test changes required.
…ngs context

The previous commit added showAiAccess to ConnectionFormSettings and
opted in from useConnectionFormPreferences, but the
ConnectionFormWithSettings wrapper explicitly destructures every
known setting prop and rebuilds the context value — I forgot to
include showAiAccess in that list. Result: the prop reached
ConnectionFormModal but got dropped before the context provider, so
useConnectionFormSetting('showAiAccess') always fell back to its
default `false` and the tab never rendered.

Add showAiAccess to both the destructure and the memoized context
value (matches the pattern for all other settings).
The first version of the AI access tab used a custom containerStyles
flexbox + gap, a redundant <Subtitle> heading (the tab name already
says "AI access"), and hand-rolled description divs under each radio
with negative margins. Result: inconsistent padding compared to the
other Advanced Options tabs and uneven vertical rhythm.

Switch to the same primitives the other tabs and the create-collection
form use:

- Drop the <Subtitle> ("AI access for this connection") — tab name
  alone is sufficient, matches General / Authentication / TLS pattern.
- Wrap each section in <FormFieldContainer> instead of custom flex
  containers. Consistent spacing across tabs.
- Use <Radio size="small" description="...">label</Radio> — same
  pattern as compass-data-modeling's export-diagram-modal. The Radio
  component renders the description in the right place at the right
  size; no manual divs or margin overrides.
- Tools list label uses <Label htmlFor> + a sibling div, matching how
  other field groups in the form expose a textual block.
…access tab

The version we use of @leafygreen-ui/radio-group does not auto-derive
each <Radio>'s checked state from the parent <RadioGroup value="...">
prop alone — every working Radio usage elsewhere in the repo (e.g.
compass-data-modeling/export-diagram-modal.tsx) sets both `checked`
and `onClick` directly on each Radio. Without that, all three of our
mode radios rendered unselected and the preset section never appeared
because the click never propagated through RadioGroup's onChange.

Mirror that pattern: each Radio gets `checked={mode === opt.value}`
and `onClick={() => onModeChange(opt.value)}` (same for the preset
radios). RadioGroup still carries the `value` prop for accessibility.
The ConnectionSchema Zod validator in compass-main-connection-storage
still constrained mcpAccess to the legacy `'allowed' | 'denied'`
string enum. Saving a connection from the new AI access tab — which
writes `{ mode, preset }` — failed with:

  Expected 'allowed' | 'denied', received object
   path: connectionInfo.mcpAccess

Widen the schema to a union that accepts:
- the two legacy strings (so existing connection records still load)
- `{ mode: 'denied' }`
- `{ mode: 'ask' }`
- `{ mode: 'allowed', preset: 'metadata-only' | 'read-only' | 'full-access' }`

Both forms are normalized at read time via normalizeMcpAccess, so the
runtime gate and the AI access tab always see the discriminated union.
Rewrites the welcome card copy after pressure-testing the previous
draft. Three issues addressed:

1. The on-state title ("Connect AI tools to MongoDB Compass") was the
   same as the off-state — telling a user who already enabled MCP to
   "connect AI tools" reads as a bug. Now reads "MCP server is running".

2. The on-state body used "exposing your saved connections", which
   overstates the model (connections in `ask`/`denied` aren't exposed)
   and reads alarmist. Replaced with a neutral description of the
   actual posture: server listening on localhost; per-connection
   policies (deny/ask/allow with preset) gate every access.

3. The on-state body told users to manage per-connection access in
   Settings → MCP Server — wrong location. Per-connection access
   lives on each connection's AI access tab. Split the two surfaces
   in the copy so users know where each control is.

Off-state body also dropped the "read-only" promise since v2 added
the full-access preset; replaced with "per-connection access you
control" which is accurate at any preset level.

Button label: "MANAGE MCP SETUP" -> "MANAGE INSTALLED CLIENTS"
since the deep-link target is specifically the client setup tab.

Test updated to assert the new on-state title + button label.
The client tab strip + Install button + snippet + config-path link
were previously hidden when enableMcpServer was off. That was wrong:
- The bridge command (process.execPath --mcp-stdio) doesn't depend on
  the server being running; the snippet is correct either way.
- Users often want to wire up AI clients first and toggle the server
  on later, or vice versa.
- Hiding the only UI for managing client installations behind the
  toggle made the Settings tab feel half-empty when MCP is off, with
  no actionable next step beyond flipping the toggle.

Drop the enableMcpServer gate around the install section. The toggle
itself stays at the top (now the toggle is the ONLY thing affected
by enableMcpServer in this view; the bridge/snippet/install UI is
always visible).
…guidance

Observed AI behavior: Cursor asked 'what is the longest citybike trip
in compass-data-sets', the AI called list-databases on our MCP without
first calling connect, got upstream mongodb-mcp-server's standard
not-connected message ('please use connect or update the MCP server
config to include a connection string'), and gave up — falling back
to a web search.

The upstream message is correct for the standalone mongodb-mcp-server
but wrong for ours: our connect tool takes a connectionId from
list-connections, never a connection string. The AI dutifully looked
for a connection string in the user's message, found none, and
concluded it couldn't help.

Override the session-level connectionErrorHandler in both transports
(CompassHttpRunner + CompassSocketServer) with a Compass-specific
handler that returns:

  No active MongoDB connection. To work with a database in MongoDB
  Compass:
    1. Call `list-connections` to get the ids of the user's saved
       Compass connections.
    2. Call `connect` with `connectionId` set to the id of the
       connection you want to use.
  Do NOT ask the user for a connection string and do NOT pass a
  connection string to `connect` — this server only accepts Compass
  connection ids.

Also expanded the connect tool's own description to spell out the
same two-step workflow + 'never connection strings' rule. That way
the AI has the right mental model from the tool catalog alone,
before it ever hits the error path.

32/32 mcp-server tests still green.
…ific workflow

Upstream mongodb-mcp-server hardcodes the MCP `initialize` response's
`instructions` field via TransportRunnerBase.getInstructions() and
returns a generic one-liner ("This is the MongoDB MCP server."). AI
clients (Claude Desktop, Cursor) inject this string into the model's
system prompt at session start, so getting it right means the AI has
the right mental model BEFORE it tries any tool.

Override the static at module load with a Compass-specific text that
explicitly covers:

- Required workflow: list-connections, then connect(connectionId),
  then read/metadata tools. No connection strings, ever.
- Per-connection access presets and what to tell the user when a tool
  gets denied.
- Cost-aware exploration: prefer collection-storage-size over count
  for "how big" questions; call schema/indexes before designing
  aggregations; use explain to verify plans before heavy queries.
- compass-open-collection as the "show me the data" handoff to UI.

Implementation: createServer in upstream calls
TransportRunnerBase.getInstructions directly (not virtually), so
subclassing doesn't help — but the static is a plain function
property that's reassignable. Install once at compass-mcp-server
package entry (idempotent).

Pairs with the connectionErrorHandler override from the previous
commit: the AI now hears the same workflow guidance at session start
(via instructions) AND on any "not connected" tool-call failure (via
the error handler) AND in the connect tool's description (via the
catalog). Three reinforcing channels mean the AI shouldn't dead-end
on "I need a connection string and you didn't give me one".

32/32 mcp-server tests still green.
…AI integration

Adds first-class description and MCP prompt name fields to saved queries and
aggregation pipelines, surfaces saved queries to AI agents via dedicated MCP
tools and slash-command prompts, and shows the loaded favorite's identity as
an editable chip in the collection header breadcrumb row.

Highlights:
- query-bar: SaveDraftAsFavoriteModal (low-level Modal + plain Button) +
  SaveQueryMenu (save / save-as / update-in-place); loaded-favorite bridge
  broadcasts the loaded-favorite identity on localAppRegistry so external
  surfaces can subscribe without a circular dep
- collection-header: editable Chip rendered next to Breadcrumbs reflects the
  current loaded favorite + dirty state; click name to rename in place
- compass-aggregations + compass-saved-aggregations-queries: pipeline name,
  description and MCP prompt name fields plumbed through the saving /
  editing modals
- my-queries-storage: shared MCP prompt name validator + schema additions on
  both query and pipeline storage
- compass-mcp-server: list-saved-queries / save-saved-query tools,
  saved-query prompts (slash commands), Compass-specific initialize +
  not-connected instructions
Copilot AI review requested due to automatic review settings May 18, 2026 07:12
@mcasimir mcasimir requested a review from a team as a code owner May 18, 2026 07:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a proof-of-concept MCP (Model Context Protocol) integration into Compass, enabling external AI clients to connect to user-saved Compass connections, use a per-connection access policy, and expose described saved queries/aggregations as discoverable MCP prompts.

Changes:

  • Adds a new @mongodb-js/compass-mcp-server package (socket server + stdio bridge + tool/preset enforcement + client auto-setup).
  • Extends saved query / saved pipeline storage + UI to support AI-facing descriptions and optional MCP prompt names.
  • Adds per-connection MCP access policy plumbing (schema/types, connection form “AI access” tab, consent dialog, and navigation tool support).

Reviewed changes

Copilot reviewed 112 out of 115 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/my-queries-storage/src/query-storage-schema.ts Adds persisted metadata for AI (description/authoredBy) + MCP prompt name validation on saved queries
packages/my-queries-storage/src/provider.ts Re-exports MCP prompt-name helpers/types for renderer consumption
packages/my-queries-storage/src/pipeline-storage-schema.ts Adds description/authoredBy/mcpPromptName to saved pipeline schema with shared validation
packages/my-queries-storage/src/mcp-prompt-name.ts Introduces shared MCP prompt-name validation/suggestion helpers
packages/my-queries-storage/src/mcp-prompt-name.spec.ts Adds unit tests for prompt-name validation/suggestion helpers
packages/my-queries-storage/src/index.ts Exposes MCP prompt-name helpers from the package entrypoint
packages/connection-storage/src/compass-main-connection-storage.ts Allows legacy + new mcpAccess shapes in persisted connections
packages/connection-info/src/index.ts Exports MCP access types + normalization helper
packages/connection-info/src/connection-info.ts Defines McpAccess/McpPreset and implements normalizeMcpAccess
packages/connection-form/src/utils/validation.ts Adds aiAccess tab id to validation tab union
packages/connection-form/src/hooks/use-connect-form.ts Adds mcpAccess state/actions and dirty tracking for the AI access tab
packages/connection-form/src/hooks/use-connect-form-settings.tsx Adds showAiAccess setting with default off
packages/connection-form/src/components/connection-form.tsx Plumbs mcpAccess through save payload and advanced options
packages/connection-form/src/components/advanced-options-tabs/advanced-options-tabs.tsx Adds conditional “AI access” tab and shared props wiring
packages/connection-form/src/components/advanced-connection-options.tsx Forwards mcpAccess into the advanced options tabs component
packages/connection-form/package.json Adds dependency needed by the AI access tab for preset labeling/tool preview
packages/compass/src/main/main-app.ts Extracts main bootstrap logic into a standalone module for conditional loading
packages/compass/src/main/index.ts Adds dispatcher to start either GUI or MCP stdio bridge mode
packages/compass/src/main/application.ts Initializes MCP server manager in the main process lifecycle
packages/compass/src/app/components/workspace.tsx Mounts MCP navigation listener in the workspace tree
packages/compass/src/app/components/mcp-navigation-listener.tsx Adds renderer-side IPC listener to open collections on MCP tool requests
packages/compass/src/app/components/mcp-consent-dialog.tsx Adds renderer consent modal for per-connection MCP access grants
packages/compass/src/app/application.tsx Mounts consent dialog at app root so it can respond to IPC events
packages/compass/package.json Adds dependency on @mongodb-js/compass-mcp-server
packages/compass-welcome/src/components/mcp-welcome-section.tsx Adds Welcome-tab discoverability CTA into MCP settings
packages/compass-welcome/src/components/desktop-welcome-tab.tsx Renders MCP welcome card in both empty/non-empty connection states
packages/compass-welcome/src/components/desktop-welcome-tab.spec.tsx Adds tests for MCP welcome section rendering and deep-link emission
packages/compass-settings/src/stores/settings.ts Adds mcp settings tab id
packages/compass-settings/src/components/modal.tsx Registers new “MCP Server” settings panel
packages/compass-settings/package.json Adds dependency on @mongodb-js/compass-mcp-server
packages/compass-saved-aggregations-queries/src/stores/edit-item.ts Extends edit flow to persist description + MCP prompt name
packages/compass-saved-aggregations-queries/src/components/saved-item-card.tsx Renames “Rename” action label to “Edit” to match expanded modal
packages/compass-saved-aggregations-queries/src/components/edit-item-modal.tsx Adds description + prompt name fields with validation + uniqueness check
packages/compass-query-bar/src/stores/query-bar-store.ts Adds loaded-favorite pub/sub bridge + rename callback exposure
packages/compass-query-bar/src/stores/query-bar-store.spec.ts Updates activation helper stubs for new cleanup API usage
packages/compass-query-bar/src/loaded-favorite-bridge.ts Defines cross-package event + sticky keys contract for loaded favorites
packages/compass-query-bar/src/components/save-query-menu.tsx Adds save menu supporting “Save” vs “Save as” based on loaded favorite
packages/compass-query-bar/src/components/save-draft-as-favorite-modal.spec.tsx Adds coverage for modal behavior and new metadata fields
packages/compass-query-bar/src/components/query-history/recent-list.tsx Removes inline “favorite recent” UI in favor of dedicated save flow
packages/compass-query-bar/src/components/query-history/recent-list.spec.tsx Updates tests for recent list props after UI removal
packages/compass-query-bar/src/components/query-history/index.tsx Removes now-unused “save favorite” callback wiring in query history
packages/compass-query-bar/src/components/query-history/index.spec.tsx Removes obsolete test for inline favorite-from-recent flow
packages/compass-query-bar/src/components/query-history/favorite-list.tsx Applies favorites while preserving favorite identity (for update-in-place)
packages/compass-preferences-model/src/preferences-schema.tsx Adds enableMcpServer user pref and mcpStdio CLI flag
packages/compass-mcp-server/tsconfig.json Adds TypeScript config for the new MCP server package
packages/compass-mcp-server/tsconfig-build.json Adds build tsconfig for dist output
packages/compass-mcp-server/src/test/presets.spec.ts Adds tests for preset allowlists, normalization, and aggregate gating
packages/compass-mcp-server/src/test/instructions.spec.ts Adds tests for MCP instruction monkey-patch behavior/content
packages/compass-mcp-server/src/test/connection-error-handler.spec.ts Adds tests ensuring Compass-specific connection error guidance
packages/compass-mcp-server/src/test/compass-socket-server.spec.ts Adds integration smoke test for socket server + initialize handshake
packages/compass-mcp-server/src/test/auto-setup.spec.ts Adds tests for writing/patching AI client config files (JSONC-safe)
packages/compass-mcp-server/src/stdio-bridge.ts Implements stdio-to-socket bridge with fallback MCP server when GUI absent
packages/compass-mcp-server/src/socket-path.ts Defines cross-platform socket/named-pipe path resolution
packages/compass-mcp-server/src/presets.ts Defines MCP access presets and per-preset tool allowlists
packages/compass-mcp-server/src/mcp-saved-query-storage.ts Defines catalog interface/types for described saved queries/aggregations
packages/compass-mcp-server/src/list-saved-queries-tool.ts Adds MCP tool to list described saved queries/aggregations
packages/compass-mcp-server/src/list-connections-tool.ts Adds MCP tool to list Compass saved connections
packages/compass-mcp-server/src/ipc-channels.ts Centralizes renderer/main IPC channel names for MCP flows
packages/compass-mcp-server/src/instructions.ts Adds Compass-specific MCP instructions string + upstream patch install
packages/compass-mcp-server/src/index.ts Exposes the public surface for consumers (Compass + connection-form)
packages/compass-mcp-server/src/connection-error-handler.ts Replaces upstream guidance to forbid connection strings and require ids
packages/compass-mcp-server/src/compass-tools.ts Registers Compass MCP tool set and wraps upstream tools with access checks
packages/compass-mcp-server/src/compass-tool-context.ts Defines shared tool context + access-denied error
packages/compass-mcp-server/src/compass-socket-server.ts Implements per-session MCP servers over local sockets + prompt registry
packages/compass-mcp-server/src/compass-open-collection-tool.ts Adds MCP tool to navigate the Compass UI without returning data
packages/compass-mcp-server/src/compass-connection-manager.ts Implements connection-id-based connect + consent + preset tracking
packages/compass-mcp-server/src/compass-connect-tool.ts Replaces upstream connect tool to accept Compass connection ids
packages/compass-mcp-server/src/client-paths.ts Defines per-client config path resolution for auto-setup
packages/compass-mcp-server/src/build-tool-context.ts Builds tool context and wraps tools with preset enforcement and stage gate
packages/compass-mcp-server/src/auto-setup.ts Installs/uninstalls/detects Compass MCP entry in AI client config (JSONC)
packages/compass-mcp-server/scripts/dev-stdio-bridge.js Dev-only wrapper to filter HMR stdout noise from the MCP wire
packages/compass-mcp-server/package.json Declares new internal package and its deps/scripts
packages/compass-mcp-server/.mocharc.js Adds mocha config hook for the new package
packages/compass-mcp-server/.eslintrc.js Adds ESLint config for the new package
packages/compass-mcp-server/.eslintignore Ignores build/artifacts for linting
packages/compass-mcp-server/.depcheckrc Configures depcheck ignore patterns
packages/compass-connections/src/hooks/use-connection-form-preferences.tsx Enables AI access tab only for desktop distribution builds
packages/compass-components/src/index.ts Re-exports ModalFooter from LeafyGreen surface
packages/compass-collection/src/components/collection-header/use-loaded-favorite.ts Adds hook subscribing to query-bar loaded-favorite bridge
packages/compass-collection/src/components/collection-header/use-loaded-favorite.spec.tsx Tests loaded-favorite hook sticky + event update behavior
packages/compass-collection/src/components/collection-header/loaded-favorite-breadcrumb-chip.tsx Adds breadcrumb-adjacent chip with inline rename + dirty indicator
packages/compass-collection/src/components/collection-header/loaded-favorite-breadcrumb-chip.spec.tsx Tests chip rendering and rename UX behaviors
packages/compass-collection/src/components/collection-header/collection-header.tsx Renders loaded-favorite chip next to breadcrumbs
packages/compass-collection/src/components/collection-header/collection-header.spec.tsx Adds integration-level assertions that bridge emission reaches header UI
packages/compass-aggregations/src/modules/saving-pipeline.ts Extends save-pipeline modal state/actions to include description + prompt name
packages/compass-aggregations/src/modules/saving-pipeline.spec.ts Updates tests for new modal fields being forwarded/trimmed
packages/compass-aggregations/src/modules/saved-pipeline.ts Persists description + prompt name on saved pipelines
packages/compass-aggregations/src/modules/mcp-prompt-name.ts Adds top-level reducer slice for pipeline MCP prompt name
packages/compass-aggregations/src/modules/index.ts Wires new description + mcpPromptName reducers into root reducer
packages/compass-aggregations/src/modules/description.ts Adds top-level reducer slice for pipeline description
packages/compass-aggregations/src/components/saving-pipeline-modal/saving-pipeline-modal.tsx Adds description + prompt name inputs with validation to save modal
packages/compass-aggregations/src/components/pipeline/pipeline.tsx Plumbs new modal props/actions through pipeline component
packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-name.tsx Replaces toolbar name display with a favorite-style identity chip
packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-name.spec.tsx Updates tests to match new chip-based UI and dirty indicator
packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-menus.tsx Ensures “Save As” preserves description/prompt name when cloning
packages/compass-aggregations/src/components/aggregations/aggregations.tsx Wires new saving-pipeline actions into connected aggregations component
.gitignore Ignores .tmp directory

Comment on lines +185 to +188
} catch (err: any) {
// eslint-disable-next-line no-console
console.error('Failed to perform operation', err?.messsage ?? err);
exitCode = 1;
Comment on lines +73 to +77
function runFallbackServer(): void {
const MESSAGE =
'MongoDB Compass is not running, so no connections are available. Start ' +
'Compass and enable the MCP server in Settings → Artificial Intelligence, ' +
'then ask me again.';
Comment on lines +93 to +98
// @ts-expect-error typescript is correctly highlighting that this esm import
// is missing a path, but we're passing this through bundler, so that's kinda
// expected. In theory we should switch tsconfing to moduleResolution: bundler
// to fix this, on practice switching to anything that prefers esm over cjs
// causes a lot of code to stop compiling because types are not being resolved
// correctly, so we're just ignoring this error for now
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants