Skip to content

fix(mcp): ensure call logs are reliably written to MongoDB#60

Merged
tobias-gp merged 2 commits into
mainfrom
fix/mcp-call-logging
May 20, 2026
Merged

fix(mcp): ensure call logs are reliably written to MongoDB#60
tobias-gp merged 2 commits into
mainfrom
fix/mcp-call-logging

Conversation

@tobias-gp
Copy link
Copy Markdown
Contributor

@tobias-gp tobias-gp commented May 20, 2026

Summary

  • Root cause: The logCall function was fire-and-forget (McpCallLog.create().catch(...)) without calling connectDB() first. Since the app uses bufferCommands: false, any MongoDB write attempted before the connection was established (or after a transient disconnect) would silently fail — the .catch() only logged to console, invisible in the monitoring UI. Additionally, if a tool handler threw an unhandled error, the MCP SDK caught it for the client but logCall was never reached at all.

  • Fix: Replace the fire-and-forget logCall with an async writeCallLog that always calls connectDB() before McpCallLog.create(), ensuring the connection is ready. A new loggedTool wrapper wraps every tool handler in try/catch so that even when a handler throws, the error is still logged before re-throwing.

  • Adds unit tests covering field persistence, call ordering, and resilience to connectDB / create failures.

Test plan

  • writeCallLog calls connectDB before McpCallLog.create (verified by call ordering test)
  • All MCP call log fields are persisted correctly for success and error results
  • writeCallLog does not throw when create or connectDB fails
  • Existing mcp-logs integration tests still pass
  • TypeScript typecheck passes (tsc --noEmit)

Made with Cursor


Note

Medium Risk
Touches MCP tool execution and logging flow by making logging awaited and wrapping all tool handlers in centralized try/catch, which could subtly affect timing and error propagation. Main risk is operational (logging/DB connection overhead) rather than user-facing behavior.

Overview
Makes MCP call logging reliable and consistent. Replaces fire-and-forget logCall with async writeCallLog that always connectDB()s before McpCallLog.create, captures clientIp, and never throws on logging failures.

Wraps every MCP tool in a shared loggedTool wrapper so successes and thrown errors both write a call log; query tools now log a summarised output ("N rows, M columns") via logResult while still returning the full query payload.

Adds a new archmax-server.test.ts suite covering DB connect-before-write ordering, persisted fields for success/error, resilience to DB failures, tool registration count, and logging behavior for success/throwing/query handlers.

Reviewed by Cursor Bugbot for commit 5fe2871. Bugbot is set up for automated code reviews on this repo. Configure here.

The fire-and-forget `logCall` pattern silently dropped writes when the
MongoDB connection was not ready (`bufferCommands: false` rejects
immediately). Additionally, if a tool handler threw an unhandled error
the SDK caught it but `logCall` was never reached, leaving zero trace in
the monitoring page.

- Replace the fire-and-forget `logCall` with an async `writeCallLog`
  that calls `connectDB()` before every `McpCallLog.create()`.
- Wrap every tool handler in a `loggedTool` helper that catches
  exceptions, writes the error log entry, then re-throws so the SDK
  still returns its error payload to the client.
- Extract `summariseQueryResult` for query-tool log abbreviation.
- Add unit tests covering field persistence, call ordering, and
  resilience to connectDB / create failures.

Co-authored-by: Cursor <cursoragent@cursor.com>
@railway-app railway-app Bot temporarily deployed to archmax SemLayer / archmax-pr-60 May 20, 2026 08:10 Destroyed
@railway-app
Copy link
Copy Markdown

railway-app Bot commented May 20, 2026

🚅 Deployed to the archmax-pr-60 environment in archmax SemLayer

Service Status Web Updated (UTC)
archmax_external_dbs ✅ Success (View Logs) May 20, 2026 at 10:00 am
archmax_standalone_with_volume ✅ Success (View Logs) May 20, 2026 at 10:00 am
archmax_standalone ✅ Success (View Logs) May 20, 2026 at 9:59 am

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

No concrete security issues found in the changed files for the requested threat surfaces.

Checked apps/api/src/mcp/archmax-server.ts and the new test, plus the surrounding MCP auth/query/admin-auth paths. MCP bearer-token lookup and project binding still happen in archmax-route before tool registration/dispatch, token scopes are still passed into every semantic-model tool, and invalid tokens return JSON-RPC errors. execute_query still flows through executeScopedQuery, AST validation, scoped model VIEW materialization, query timeout/concurrency controls, and the 1000-row cap. Admin Better Auth startup secret validation, CSRF middleware, and production cookie attributes are unchanged. No new dependencies or committed secrets were introduced.

Verification note: I attempted npx vitest run apps/api/src/mcp/archmax-server.test.ts packages/core/src/services/mcp-tools.test.ts packages/core/src/services/sql-ast-validation.test.ts, but this checkout has no local node_modules/vitest, so the command could not load vitest/config.

Open in Web View Automation 

Sent by Cursor Automation: archmax Security Review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default mode and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 623c582. Configure here.

Comment thread apps/api/src/mcp/archmax-server.ts Outdated
@github-actions
Copy link
Copy Markdown

Docker image ready

docker pull ghcr.io/archmaxai/archmax:pr-60

writeCallLog was called fire-and-forget inside loggedTool — the same
pattern this PR set out to fix. Now properly awaited so the MongoDB
write completes before the tool response returns (or the error
re-throws). Also refactors loggedTool to remove unnecessary generics
and extracts queryToolResult helper to deduplicate query tools.

Adds 7 tests exercising loggedTool via registerArchmaxTools: success
logging, error logging + rethrow, DB-failure resilience, and
query-result summarisation.

Co-authored-by: Cursor <cursoragent@cursor.com>
@railway-app railway-app Bot temporarily deployed to archmax SemLayer / archmax-pr-60 May 20, 2026 09:55 Destroyed
@tobias-gp tobias-gp merged commit 2b54415 into main May 20, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant