Skip to content

feat: Native MCP support over HTTP#977

Merged
arxdsilva merged 9 commits into
mainfrom
native-mcp
Jul 9, 2026
Merged

feat: Native MCP support over HTTP#977
arxdsilva merged 9 commits into
mainfrom
native-mcp

Conversation

@arxdsilva

@arxdsilva arxdsilva commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

Summary

  • New Features
    • Added a read-only MCP-compatible HTTP endpoint at /_mcp supporting JSON-RPC initialize, tools/list, and tools/call, including schema-aware per-table select tools.
    • Tool discovery and execution are permission-aware, and HTTP access is enforced when authentication is enabled.
  • Documentation
    • Updated the README with /_mcp usage, supported tools/methods, safety guarantees, and error/auth expectations.
  • Tests
    • Expanded unit and integration coverage for the MCP endpoint, plus additional validation for auth password verification and option-driven catalog/CRUD query behavior.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: adb53ce8-bf8f-4970-b8d1-955e196c0933

📥 Commits

Reviewing files that changed from the base of the PR and between 9a8bd30 and 7d25645.

📒 Files selected for processing (4)
  • .cursor/rules/agentic-loop.mdc
  • .cursor/rules/git-commits.mdc
  • .gitignore
  • controllers/mcp_test.go
✅ Files skipped from review due to trivial changes (2)
  • .gitignore
  • .cursor/rules/agentic-loop.mdc

📝 Walkthrough

Walkthrough

This PR adds a read-only MCP endpoint at /_mcp, wires it into routing and handler construction, expands alias-aware database support, adds controller and integration coverage, updates README documentation, adds Cursor rules, and adjusts one ignore pattern.

Changes

MCP Endpoint Implementation

Layer / File(s) Summary
MCP request entrypoints
controllers/mcp.go
Defines the MCP handler, request/response models, and HTTP entrypoints for discovery and JSON-RPC handling.
RPC dispatch and tool calls
controllers/mcp.go
Implements JSON-RPC validation, initialize and tools/list dispatch, tool call routing, and dynamic select tool parsing.
Read-only tools and discovery
controllers/mcp.go
Implements read-only database, schema, table, describe, and select operations, plus tool discovery, schema generation, SQL helpers, and result decoding.
Alias-aware registry support
adapters/database_registry.go, adapters/mock/mock.go, adapters/mockgen/adapter.go, adapters/mockgen/database_registry.go, adapters/postgres/postgres.go
Extends database registry alias enumeration and updates postgres and mock implementations to expose aliases.
Handler wiring and route registration
controllers/deps.go, controllers/deps_test.go, router/router.go
Adds MCP handler construction, registers /_mcp, and applies auth middleware when enabled.
MCP unit and integration tests
controllers/mcp_test.go, integration/controllers/mcp_test.go, integration/router/routes_test.go
Adds controller and integration coverage for discovery, JSON-RPC dispatch, tool calls, permissions, alias handling, and route/auth behavior.
MCP documentation
README.md
Documents the MCP endpoint, supported methods, tool set, safety model, and test coverage pointers.

Controller Test Additions

Layer / File(s) Summary
Auth password verification tests
controllers/auth_test.go
Adds bcrypt and legacy digest password verification coverage plus helper checks.
Catalog and CRUD query tests
controllers/catalog_test.go, controllers/crud_test.go
Adds tests for option-driven catalog listing and CRUD select/count-first query behavior.

Cursor Rule Files

Layer / File(s) Summary
Agentic loop rule
.cursor/rules/agentic-loop.mdc
Defines the agentic development loop, implementation conventions, validation steps, self-review checklist, and completion criteria.
Git commit safety rule
.cursor/rules/git-commits.mdc
Defines commit-related prohibitions, read-only git inspection, handoff steps, commit message style, and push constraints.

Repository Housekeeping

Layer / File(s) Summary
Ignore pattern update
.gitignore
Replaces the specific coverage.out ignore entry with a wildcard for *.out files.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • prest/prest#968: The MCP handler wiring builds on the controller dependency structure and handler construction used here.
  • prest/prest#973: Both changes extend multi-database alias behavior through DatabaseRegistry and alias-aware selection.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete: it only references issue #959 and omits the required branch check, contribution guidelines, and change summary. Expand the description to state the PR purpose, target issue, branch compliance, and confirmation that contributing guidelines were read.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: native MCP support over HTTP.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch native-mcp

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
controllers/mcp.go (2)

346-348: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Log errors from describeToolDescription instead of silently swallowing them.

When describeToolDescription fails for a table, the error is silently ignored with continue. This could hide real database issues or permission problems, making the tool list incomplete without any indication of why.

🔍 Proposed fix: log at debug level
 		desc, err := h.describeToolDescription(r, defaultDB, schema, table)
 		if err != nil {
+			slog.Debug("mcp: skipping tool for table", "schema", schema, "table", table, "error", err)
 			continue
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controllers/mcp.go` around lines 346 - 348, The error from
describeToolDescription is being swallowed by the continue in the tool
enumeration flow, which can hide real failures. Update the logic around
describeToolDescription in the MCP tool listing path to log the returned err at
debug level before continuing, so failures are observable while preserving the
existing fallback behavior. Use the surrounding tool-building code in
controllers/mcp.go and the describeToolDescription call site as the anchor for
the fix.

25-25: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove or document the unused builder field.

builder is set in NewMCPHandler (line 36) but never referenced anywhere in mcp.go. Either remove it or add a comment explaining its intended future use.

♻️ Proposed fix: remove the field
 type MCPHandler struct {
 	catalog  adapters.CatalogQuerier
-	builder  adapters.RequestQueryBuilder
 	executor adapters.QueryExecutor
 	db       adapters.DatabaseRegistry
 	singleDB bool
 	pgDB     string
 }

And in the constructor:

 func NewMCPHandler(deps Deps) *MCPHandler {
 	return &MCPHandler{
 		catalog:  deps.Catalog,
-		builder:  deps.Builder,
 		executor: deps.Executor,
 		db:       deps.DB,
 		singleDB: deps.SingleDB,
 		pgDB:     deps.PGDatabase,
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controllers/mcp.go` at line 25, The MCP handler currently carries an unused
builder field that is assigned in NewMCPHandler but never referenced anywhere in
mcp.go. Remove the builder field from the handler struct and update
NewMCPHandler to stop wiring it in; if it must stay for future use, add a brief
comment near the MCPHandler definition explaining its purpose, but the preferred
fix is to delete the dead field and constructor assignment.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@controllers/mcp.go`:
- Around line 346-348: The error from describeToolDescription is being swallowed
by the continue in the tool enumeration flow, which can hide real failures.
Update the logic around describeToolDescription in the MCP tool listing path to
log the returned err at debug level before continuing, so failures are
observable while preserving the existing fallback behavior. Use the surrounding
tool-building code in controllers/mcp.go and the describeToolDescription call
site as the anchor for the fix.
- Line 25: The MCP handler currently carries an unused builder field that is
assigned in NewMCPHandler but never referenced anywhere in mcp.go. Remove the
builder field from the handler struct and update NewMCPHandler to stop wiring it
in; if it must stay for future use, add a brief comment near the MCPHandler
definition explaining its purpose, but the preferred fix is to delete the dead
field and constructor assignment.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 817d772f-edb0-4fc9-b066-75b641d6fa97

📥 Commits

Reviewing files that changed from the base of the PR and between 8784a25 and b84ce86.

📒 Files selected for processing (11)
  • README.md
  • controllers/auth_test.go
  • controllers/catalog_test.go
  • controllers/crud_test.go
  • controllers/deps.go
  • controllers/deps_test.go
  • controllers/mcp.go
  • controllers/mcp_test.go
  • integration/controllers/mcp_test.go
  • integration/router/routes_test.go
  • router/router.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
integration/router/routes_test.go (1)

16-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider verifying response body content for the POST initialize path.

The where parameter ("MCPInitialize") is just a label in DoRequest — no body content is actually asserted since no variadic expectedBody strings are passed. The test only confirms HTTP 200. For the initialize JSON-RPC call, asserting that the response body contains expected fields (e.g., "serverInfo", "capabilities") would strengthen coverage and catch regressions in the response shape.

♻️ Optional: add body content assertions
 func TestMCPRoute(t *testing.T) {
 	base := helpers.ServerURL(t)
 	testutils.DoRequest(t, base+"/_mcp", nil, "GET", http.StatusOK, "MCPDiscovery")
 
 	payload := map[string]any{
 		"jsonrpc": "2.0",
 		"id":      1,
 		"method":  "initialize",
 	}
-	testutils.DoRequest(t, base+"/_mcp", payload, "POST", http.StatusOK, "MCPInitialize")
+	testutils.DoRequest(t, base+"/_mcp", payload, "POST", http.StatusOK, "MCPInitialize", "serverInfo", "capabilities")
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@integration/router/routes_test.go` around lines 16 - 26, Update TestMCPRoute
so the POST initialize request actually asserts response body content instead of
only relying on the DoRequest label "MCPInitialize"; use the existing
testutils.DoRequest call site to add expectedBody checks for key JSON-RPC
response fields such as serverInfo and capabilities, ensuring the initialize
path validates the returned payload shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@integration/router/routes_test.go`:
- Around line 16-26: Update TestMCPRoute so the POST initialize request actually
asserts response body content instead of only relying on the DoRequest label
"MCPInitialize"; use the existing testutils.DoRequest call site to add
expectedBody checks for key JSON-RPC response fields such as serverInfo and
capabilities, ensuring the initialize path validates the returned payload shape.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 772ed604-5167-433c-9375-e16b4a63d0ca

📥 Commits

Reviewing files that changed from the base of the PR and between b84ce86 and 50c6752.

📒 Files selected for processing (2)
  • integration/controllers/mcp_test.go
  • integration/router/routes_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • integration/controllers/mcp_test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
controllers/mcp.go (1)

359-379: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Restrict describe_table to permitted columns. describeColumns returns every column from ShowTableCtx, so callers can inspect table metadata even when list_tables/select_table would hide the table or yield no readable fields. Reuse selectableColumns here and reject empty results.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controllers/mcp.go` around lines 359 - 379, The describeTable handler is
exposing all columns via describeColumns without enforcing the same column
अनुमति checks used elsewhere. Update describeTable to filter the result through
selectableColumns for the resolved database/schema/table, and reject the request
when no selectable columns remain; keep the validation flow in
MCPHandler.describeTable and reuse the existing access-control helpers so the
returned metadata only includes permitted columns.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controllers/mcp.go`:
- Around line 469-513: `MCPHandler.tools` is recomputing table permissions and
columns twice per table during discovery: once indirectly through
`tableRows`/`filterAccessibleTables` and again via `selectableColumns` when
building each tool schema. Update the discovery flow so column metadata is
computed only once per table and reused for both filtering and schema
generation, either by having `filterAccessibleTables` return the columns with
the rows or by making `tools()` work from raw rows and call `selectableColumns`
a single time per table. Keep the fix localized to `tools`, `tableRows`, and
`selectableColumns` so discovery no longer repeats the same DB checks on every
request.
- Around line 310-344: In listSchemas on MCPHandler, the current len(schemas) ==
0 check conflates “no schemas exist” with “permissions filtered everything out,”
causing a schema-name leak. Update the filtering logic to branch on whether
h.perms is set (matching filterAccessibleTables) rather than the size of the map
returned by accessibleSchemas, and only fall back to showing all rows when there
is truly no permission filtering in effect.

---

Outside diff comments:
In `@controllers/mcp.go`:
- Around line 359-379: The describeTable handler is exposing all columns via
describeColumns without enforcing the same column अनुमति checks used elsewhere.
Update describeTable to filter the result through selectableColumns for the
resolved database/schema/table, and reject the request when no selectable
columns remain; keep the validation flow in MCPHandler.describeTable and reuse
the existing access-control helpers so the returned metadata only includes
permitted columns.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f593eeff-ff5c-4ada-896c-e2147cb09ab7

📥 Commits

Reviewing files that changed from the base of the PR and between 50c6752 and c913cd4.

📒 Files selected for processing (12)
  • README.md
  • adapters/database_registry.go
  • adapters/mock/mock.go
  • adapters/mockgen/adapter.go
  • adapters/mockgen/database_registry.go
  • adapters/postgres/postgres.go
  • controllers/crud_test.go
  • controllers/mcp.go
  • controllers/mcp_test.go
  • integration/controllers/mcp_test.go
  • integration/router/routes_test.go
  • router/router.go
✅ Files skipped from review due to trivial changes (3)
  • adapters/mockgen/adapter.go
  • adapters/mockgen/database_registry.go
  • README.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • integration/router/routes_test.go
  • integration/controllers/mcp_test.go
  • controllers/crud_test.go

Comment thread controllers/mcp.go
Comment thread controllers/mcp.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
.cursor/rules/agentic-loop.mdc (1)

83-90: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Align the adapter-test command with the canonical validation path.

This direct go test invocation drops the repo-standard -race, -count=1, and coverage flags that make test-unit already applies. Either point this section at make test-unit or mirror those flags here so race regressions in adapters/postgres don’t slip through.

🔧 Suggested replacement
-```sh
-go test -timeout 30s -parallel 8 ./adapters/postgres/...
-```
+```sh
+make test-unit
+# or, if you need a focused run:
+go test -timeout 30s -race -count=1 -tags prest_test_hooks -parallel 8 ./adapters/postgres/...
+```
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.cursor/rules/agentic-loop.mdc around lines 83 - 90, The Postgres adapter
test command in the agentic-loop rules is bypassing the repo’s canonical
validation flags, so update the guidance to use make test-unit or match its
behavior in the adapters/postgres test example. Keep the reference anchored
around the Postgres adapter unit tests section and the go test invocation, and
ensure the command includes the standard race, count, and test-hook flags used
by the main unit-test path so regressions are caught consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.cursor/rules/agentic-loop.mdc:
- Around line 49-50: Narrow the resilience policy in the config guidance so it
does not imply a blanket safe default/zero for all settings; the current wording
around unmarshalKeyOrZero, ensure*Path, and ensureJWTConfig should distinguish
non-security config from security-sensitive inputs. Update the Config row in
agentic-loop.mdc to keep warn-and-continue only for non-sensitive values, and
explicitly fail closed or require hard errors for auth, ACL, JWT, secret, and
DB-credential settings.

---

Nitpick comments:
In @.cursor/rules/agentic-loop.mdc:
- Around line 83-90: The Postgres adapter test command in the agentic-loop rules
is bypassing the repo’s canonical validation flags, so update the guidance to
use make test-unit or match its behavior in the adapters/postgres test example.
Keep the reference anchored around the Postgres adapter unit tests section and
the go test invocation, and ensure the command includes the standard race,
count, and test-hook flags used by the main unit-test path so regressions are
caught consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cfdc5b4c-646d-4c5e-a68d-200f78ea795d

📥 Commits

Reviewing files that changed from the base of the PR and between 9c15b63 and 9a8bd30.

📒 Files selected for processing (1)
  • .cursor/rules/agentic-loop.mdc

Comment thread .cursor/rules/agentic-loop.mdc Outdated
arxdsilva added 3 commits July 8, 2026 13:57
- Updated .gitignore to ignore all output files with the .out extension.
- Clarified resilience policy in agentic-loop guidelines to specify that non-sensitive configurations should log warnings and continue startup, while sensitive configurations should fail closed.
- Introduced a new document outlining the safety protocols for AI interactions with git commits, emphasizing that commits must be executed by humans only.
- Defined clear boundaries for AI capabilities, including inspection commands and drafting commit messages, while prohibiting direct execution of git commands.
- Established a structured approach for suggesting commits, ensuring compliance with repository commit message styles and safety protocols.
- Updated the agentic-loop configuration guidelines to clarify the handling of non-sensitive and sensitive settings during startup.
- Specified that non-sensitive configurations should only log warnings and continue, while sensitive configurations must fail closed to maintain security integrity.
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

Merging this branch changes the coverage (2 decrease, 1 increase)

Impacted Packages Coverage Δ 🤖
adapters 0.00% (ø)
adapters/mock 95.73% (-0.59%) 👎
adapters/mockgen 0.00% (ø)
adapters/postgres 79.42% (-0.47%) 👎
controllers 79.28% (+4.94%) 👍
integration/controllers 0.00% (ø)
integration/router 0.00% (ø)
router 0.00% (ø)

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
adapters/database_registry.go 0.00% (ø) 0 0 0
adapters/mock/mock.go 97.48% (-0.62%) 159 (+1) 155 4 (+1) 👎
adapters/mockgen/adapter.go 0.00% (ø) 461 (+6) 0 461 (+6)
adapters/mockgen/database_registry.go 0.00% (ø) 32 (+6) 0 32 (+6)
adapters/postgres/postgres.go 82.89% (-0.56%) 1198 (+8) 993 205 (+8) 👎
controllers/deps.go 100.00% (ø) 7 7 0
controllers/mcp.go 81.78% (+81.78%) 483 (+483) 395 (+395) 88 (+88) 🌟
router/router.go 0.00% (ø) 23 (+4) 0 23 (+4)

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

Changed unit test files

  • controllers/auth_test.go
  • controllers/catalog_test.go
  • controllers/crud_test.go
  • controllers/deps_test.go
  • controllers/mcp_test.go
  • integration/controllers/mcp_test.go
  • integration/router/routes_test.go

@arxdsilva
arxdsilva merged commit f5e5e5e into main Jul 9, 2026
15 checks passed
@arxdsilva
arxdsilva deleted the native-mcp branch July 9, 2026 17:35
@arxdsilva

Copy link
Copy Markdown
Member Author

ref #959

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.

1 participant