Skip to content

feat: remote MCP server via Streamable HTTP + api_keys table (OPE-91)#284

Merged
DevanshuNEU merged 4 commits into
OpenCodeIntel:mainfrom
DevanshuNEU:feat/remote-mcp-server
Mar 7, 2026
Merged

feat: remote MCP server via Streamable HTTP + api_keys table (OPE-91)#284
DevanshuNEU merged 4 commits into
OpenCodeIntel:mainfrom
DevanshuNEU:feat/remote-mcp-server

Conversation

@DevanshuNEU

@DevanshuNEU DevanshuNEU commented Mar 7, 2026

Copy link
Copy Markdown
Collaborator

What

Converts our MCP server from stdio-only (local) to dual transport, enabling remote deployment as a Claude.ai Custom Connector. This is Phase 1 of MCP dogfooding.

Changes

MCP Server

  • server.py: Rewritten with FastMCP, supports stdio and streamable-http transport
  • config.py: New env vars: MCP_TRANSPORT, MCP_HOST, PORT
  • Dockerfile: New, for Railway deployment as separate service
  • /health endpoint returns 200 (Railway health checks)
  • /mcp endpoint handles Streamable HTTP MCP protocol
  • All 45 existing tests pass

Database

  • supabase/migrations/004_api_keys.sql: Creates api_keys table for ci_xxx token auth
  • SHA-256 hashed keys, RLS policies, active flag, tier column

Transport modes

# Local dev (default, same as before)
python server.py

# Remote deployment
MCP_TRANSPORT=streamable-http PORT=8080 python server.py

# CLI override
python server.py --transport http

Summary by CodeRabbit

  • New Features

    • Added Docker containerization for remote deployment capabilities.
    • Enabled HTTP transport support alongside existing communication mode.
    • Introduced API key management and authentication system.
    • Added health check endpoint.
  • Chores

    • Bumped server version to 0.5.0.
    • Updated API endpoint configuration and transport settings.
    • Updated core dependency versions for enhanced functionality.

Summary by CodeRabbit

  • New Features

    • API key management for authenticating requests
    • HTTP transport option for server deployment alongside existing stdio mode
    • Docker-based deployment with built-in health checks
  • Chores

    • Bumped server version to 0.5.0
    • Improved runtime configuration and defaults for deployment
    • Updated dependencies and extended CI to run server tests and security scans

…-91)

Phase 1 of MCP dogfooding. Converts the MCP server from stdio-only
to dual transport: stdio for local dev, streamable-http for remote.

Changes:
- server.py: Rewritten with FastMCP, dual transport support
  - stdio mode: same as before (Claude Desktop config)
  - streamable-http mode: remote deployment for Claude.ai Connectors
  - /health endpoint for Railway health checks
  - /mcp endpoint for MCP protocol (Streamable HTTP)
  - CLI: python server.py --transport http
  - Env: MCP_TRANSPORT=streamable-http

- config.py: Added TRANSPORT, HOST, PORT config from env vars
- requirements.txt: Added uvicorn, bumped mcp>=1.25.0
- Dockerfile: New, for Railway deployment of MCP service
- .env.example: Updated with transport config

- supabase/migrations/004_api_keys.sql: Creates api_keys table
  for MCP authentication (ci_xxx format, SHA-256 hashed, RLS)

Deployment steps in PR description.
@vercel

vercel Bot commented Mar 7, 2026

Copy link
Copy Markdown

@DevanshuNEU is attempting to deploy a commit to the Dev's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Mar 7, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@DevanshuNEU has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 38 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3414bc49-9abb-4eb9-8c1c-beec00719b15

📥 Commits

Reviewing files that changed from the base of the PR and between dd040c4 and 34e33f8.

📒 Files selected for processing (7)
  • .github/workflows/ci.yml
  • backend/middleware/auth.py
  • mcp-server/.env.example
  • mcp-server/.flake8
  • mcp-server/config.py
  • mcp-server/server.py
  • supabase/migrations/004_api_keys.sql
📝 Walkthrough

Walkthrough

Adds streamable-HTTP transport to the MCP server (FastMCP/uvicorn), HTTP health endpoint, Dockerfile for container deployment, env/config updates (TRANSPORT/HOST/PORT, server version), dependency bumps, CI workflow additions, and a new supabase migration introducing an api_keys table with RLS policies.

Changes

Cohort / File(s) Summary
Configuration & Env
mcp-server/.env.example, mcp-server/config.py, mcp-server/requirements.txt
Updated BACKEND_API_URL and API_KEY defaults, added MCP_TRANSPORT/ MCP_HOST/ PORT env vars; bumped SERVER_VERSION to 0.5.0; tightened mcp version and added uvicorn dependency.
Server Implementation
mcp-server/server.py
Replaced legacy stdio-only loop with FastMCP-based server supporting stdio and streamable-http; added _health endpoint, _get_http_app(), public main() entrypoint, CLI transport override, and switched tool registration to FastMCP's internal server.
Containerization
mcp-server/Dockerfile
New Dockerfile using python:3.13-slim, installs deps, sets env for streamable HTTP, exposes port 8080, adds healthcheck, and runs server.py.
Database Migration
supabase/migrations/004_api_keys.sql
New api_keys table with uuid PK, user relation, key_hash, tier, activity timestamps, index on active key_hash; trigger to protect immutable cols and Row Level Security policies for per-user access and insert/update rules.
CI Workflow
.github/workflows/ci.yml
Added MCP path filtering outputs and a conditional test-mcp job to run mcp-server tests; extended security-scan trigger to include MCP changes.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Uvicorn as Uvicorn/HTTP
    participant FastMCP as FastMCP
    participant Tool as ToolRegistry

    Client->>Uvicorn: HTTP POST /mcp (streamable-http)
    Uvicorn->>FastMCP: forward MCP request
    FastMCP->>Tool: handle call_tool / dispatch
    Tool-->>FastMCP: tool response
    FastMCP-->>Uvicorn: MCP response
    Uvicorn-->>Client: HTTP response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 From stdio burrows to HTTP light,
I hop with FastMCP into the night,
Keys in the meadow, Docker afloat,
Uvicorn whistles, servers emote,
A tiny rabbit celebrates this flight!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: enabling remote MCP server deployment via Streamable HTTP and adding the api_keys database table, both of which are substantial parts of this changeset.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
mcp-server/server.py (1)

74-78: CLI argument parsing could be improved with argparse.

The manual sys.argv parsing works but is fragile (e.g., no validation, no help text). Consider using argparse for robustness, though the current implementation is acceptable for a simple single-flag case.

♻️ Optional: Use argparse for CLI
import argparse

def main():
    parser = argparse.ArgumentParser(description=f"{SERVER_NAME} MCP Server")
    parser.add_argument(
        "--transport",
        choices=["stdio", "http", "streamable-http"],
        default=TRANSPORT,
        help="Transport mode (default: from MCP_TRANSPORT env)"
    )
    args = parser.parse_args()
    
    transport = "streamable-http" if args.transport == "http" else args.transport
    # ... rest of main()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcp-server/server.py` around lines 74 - 78, Replace fragile manual sys.argv
parsing with argparse: modify the main entry (where transport is set using
sys.argv and variable transport) to use an argparse.ArgumentParser that defines
a --transport option with choices ("stdio", "http", "streamable-http") and a
default derived from TRANSPORT/env; after parsing, normalize "http" to
"streamable-http" (as current logic does) and assign to transport. Ensure the
parser includes a helpful description and help text so validation and --help
work correctly.
supabase/migrations/004_api_keys.sql (2)

13-13: Consider adding a CHECK constraint for the tier column.

The tier column accepts any text value. If only specific tiers are valid (e.g., 'free', 'pro', 'enterprise'), a CHECK constraint would prevent invalid data.

🛡️ Proposed fix
-  tier text DEFAULT 'free',
+  tier text DEFAULT 'free' CHECK (tier IN ('free', 'pro', 'enterprise')),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/migrations/004_api_keys.sql` at line 13, Add a CHECK constraint to
restrict the tier column to allowed values (e.g., 'free', 'pro', 'enterprise')
by updating the CREATE TABLE statement that defines the tier column (or adding
an ALTER TABLE ... ADD CONSTRAINT tier_check) so that the column "tier" enforces
CHECK (tier IN ('free','pro','enterprise')) to prevent invalid values being
inserted.

10-10: Nullable user_id may cause RLS policy failures for system/service keys.

The user_id column allows NULL values, but RLS policies use auth.uid() = user_id. For system keys (NULL user_id), these policies will never match, making such keys invisible to all authenticated users via RLS. This may be intentional (service_role bypass), but consider documenting this behavior or adding explicit handling.

📝 Suggestion: Add documentation comment
 CREATE TABLE IF NOT EXISTS api_keys (
   id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
-  user_id uuid REFERENCES auth.users(id) ON DELETE CASCADE,
+  -- user_id can be NULL for system/CI keys (accessed only via service_role)
+  user_id uuid REFERENCES auth.users(id) ON DELETE CASCADE,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/migrations/004_api_keys.sql` at line 10, The user_id column is
nullable which causes RLS policies using auth.uid() = user_id to never match for
system/service keys; update the migration to explicitly document this behavior
or adjust schema/logic: either add a SQL COMMENT on the user_id column
explaining that NULL means a system/service key and therefore will not match RLS
policies that compare to auth.uid(), or alter the schema/logical design (e.g.,
add a separate boolean like is_system_key or make user_id NOT NULL and use a
sentinel) and update RLS policies accordingly; reference the user_id column and
the auth.uid() checks in the RLS policies when making the change.
mcp-server/Dockerfile (1)

15-17: Consider adding a HEALTHCHECK instruction for container orchestration.

Since _get_http_app() exposes /health, adding a Docker HEALTHCHECK would help orchestrators (including Railway) detect container readiness and health.

🐳 Proposed addition
 EXPOSE 8080

+HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
+  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:${PORT:-8080}/health')" || exit 1
+
 CMD ["python", "server.py"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcp-server/Dockerfile` around lines 15 - 17, Add a Docker HEALTHCHECK to the
Dockerfile to probe the application's /health endpoint exposed by
_get_http_app(); update the Dockerfile (near the EXPOSE/CMD block) to run a
lightweight HTTP check (e.g., curl or wget) against http://localhost:8080/health
with sensible options for --interval (e.g., 30s), --timeout (e.g., 5s) and
--retries (e.g., 3) and ensure the check returns exit 0 on healthy responses and
non-zero otherwise; this will allow orchestrators to detect readiness and
liveness of the server started by CMD ["python","server.py"].
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mcp-server/config.py`:
- Line 21: The PORT environment parsing currently does int(os.getenv("PORT",
"8080")) which will raise an unhandled ValueError for non-numeric values; wrap
the conversion in a try/except (or validate with str.isdigit()/regex) around the
os.getenv("PORT", "8080") result, log or raise a clear error if invalid, and
fall back to the default 8080 (or exit) so that the module-level PORT variable
is always a valid int; update the PORT assignment and any related error
message/logging accordingly (refer to the PORT variable and os.getenv usage).

In `@mcp-server/server.py`:
- Line 40: The code directly accesses the private attribute _mcp_server on the
mcp module (assigned to _server) which is brittle across versions; update
requirements.txt to pin an upper bound (e.g., change mcp>=1.25.0 to
mcp>=1.25.0,<2.0.0), add a clear inline comment next to the assignment of
_server = mcp._mcp_server explaining why the private API is required (to bypass
FastMCP's automatic schema generation and use custom schemas from tools.py), and
add a short TODO to monitor mcp for a public API or open an upstream issue to
request custom input schema registration so this private access can be removed
when available.

In `@supabase/migrations/004_api_keys.sql`:
- Around line 35-38: The UPDATE policy "Users can deactivate own keys" on
api_keys is too permissive (it allows changing sensitive columns like key_hash
and tier) while APIKeyManager.revoke_key() only intends to toggle active (and
maybe last_used_at); restrict updates so users can only modify the active and
last_used_at columns by either (a) replacing the policy with a condition that
ensures no other columns change, (b) adding a trigger that rejects changes to
any column except active and last_used_at, or (c) create a SECURITY DEFINER
function (e.g., revoke_api_key) that performs the allowed update and keep the
UPDATE policy narrow/removed so callers must use that function — reference the
policy name "Users can deactivate own keys", APIKeyManager.revoke_key(), and the
sensitive columns key_hash and tier when making the change.

---

Nitpick comments:
In `@mcp-server/Dockerfile`:
- Around line 15-17: Add a Docker HEALTHCHECK to the Dockerfile to probe the
application's /health endpoint exposed by _get_http_app(); update the Dockerfile
(near the EXPOSE/CMD block) to run a lightweight HTTP check (e.g., curl or wget)
against http://localhost:8080/health with sensible options for --interval (e.g.,
30s), --timeout (e.g., 5s) and --retries (e.g., 3) and ensure the check returns
exit 0 on healthy responses and non-zero otherwise; this will allow
orchestrators to detect readiness and liveness of the server started by CMD
["python","server.py"].

In `@mcp-server/server.py`:
- Around line 74-78: Replace fragile manual sys.argv parsing with argparse:
modify the main entry (where transport is set using sys.argv and variable
transport) to use an argparse.ArgumentParser that defines a --transport option
with choices ("stdio", "http", "streamable-http") and a default derived from
TRANSPORT/env; after parsing, normalize "http" to "streamable-http" (as current
logic does) and assign to transport. Ensure the parser includes a helpful
description and help text so validation and --help work correctly.

In `@supabase/migrations/004_api_keys.sql`:
- Line 13: Add a CHECK constraint to restrict the tier column to allowed values
(e.g., 'free', 'pro', 'enterprise') by updating the CREATE TABLE statement that
defines the tier column (or adding an ALTER TABLE ... ADD CONSTRAINT tier_check)
so that the column "tier" enforces CHECK (tier IN ('free','pro','enterprise'))
to prevent invalid values being inserted.
- Line 10: The user_id column is nullable which causes RLS policies using
auth.uid() = user_id to never match for system/service keys; update the
migration to explicitly document this behavior or adjust schema/logic: either
add a SQL COMMENT on the user_id column explaining that NULL means a
system/service key and therefore will not match RLS policies that compare to
auth.uid(), or alter the schema/logical design (e.g., add a separate boolean
like is_system_key or make user_id NOT NULL and use a sentinel) and update RLS
policies accordingly; reference the user_id column and the auth.uid() checks in
the RLS policies when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 870d2e9c-7774-4228-b4c0-1dd5868f6a3f

📥 Commits

Reviewing files that changed from the base of the PR and between d8a66b5 and 9e9a0e4.

📒 Files selected for processing (6)
  • mcp-server/.env.example
  • mcp-server/Dockerfile
  • mcp-server/config.py
  • mcp-server/requirements.txt
  • mcp-server/server.py
  • supabase/migrations/004_api_keys.sql

Comment thread mcp-server/config.py Outdated
Comment thread mcp-server/server.py Outdated
Comment thread supabase/migrations/004_api_keys.sql
Review fixes:
- config.py: PORT parsing wrapped in try/except, falls back to 8080
- server.py: pinned mcp<2.0.0, documented _mcp_server private API access
- requirements.txt: mcp>=1.25.0,<2.0.0 upper bound
- Dockerfile: added HEALTHCHECK with curl to /health endpoint
- 004_api_keys.sql: CHECK constraint on tier column,
  immutable column trigger (blocks key_hash/tier/name changes for
  non-service-role), documented NULL user_id = system key behavior

CI pipeline:
- Added mcp-server/** path filter to change detection
- New test-mcp job: Python 3.13, runs pytest on mcp-server/tests/
- Security scan now also triggers on MCP changes
@DevanshuNEU

Copy link
Copy Markdown
Collaborator Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Mar 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
.github/workflows/ci.yml (1)

110-137: Job structure is correct; consider adding linting for consistency.

The test-mcp job is well-structured and correctly mirrors the pattern of other test jobs. The dependencies are properly installed from requirements.txt which includes pytest and pytest-asyncio.

Optional improvements for consistency with the backend job:

  • Add a linting step (e.g., flake8 or ruff)
  • Add coverage reporting (--cov flag)
♻️ Optional: Add linting step
     - name: Install dependencies
       working-directory: ./mcp-server
       run: |
         python -m pip install --upgrade pip
+        pip install flake8
         pip install -r requirements.txt

+    - name: Lint (flake8)
+      working-directory: ./mcp-server
+      run: |
+        flake8 src/
+
     - name: Run tests
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 110 - 137, Add a linting step and
optional coverage reporting to the test-mcp GitHub Actions job: modify the
test-mcp job (job name "test-mcp") to install a linter (e.g., ruff or flake8) in
the "Install dependencies" step or as a separate step, then add a new step that
runs the linter (e.g., "ruff check ." or "flake8 .") before running tests; also
update the "Run tests" step (the pytest invocation) to include coverage
reporting (e.g., add the --cov flag such as "pytest tests/ -v --cov") and, if
desired, add a step to upload coverage reports so it mirrors the backend job's
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mcp-server/server.py`:
- Around line 74-88: The current startup logic quietly falls back to stdio for
any transport other than "streamable-http"; change it to validate the chosen
transport (TRANSPORT or CLI override via "--transport") and only allow known
values ("streamable-http" and "stdio"), mapping the "http" alias to
"streamable-http", and otherwise print a clear error including the bad transport
and exit non-zero; update the block that parses sys.argv and the final dispatch
(the branch that currently calls mcp.run(transport="stdio")) to perform this
validation and call sys.exit(1) on unknown transports, while preserving the
existing uvicorn run path that uses _get_http_app(), SERVER_NAME,
SERVER_VERSION, HOST, and PORT.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 110-137: Add a linting step and optional coverage reporting to the
test-mcp GitHub Actions job: modify the test-mcp job (job name "test-mcp") to
install a linter (e.g., ruff or flake8) in the "Install dependencies" step or as
a separate step, then add a new step that runs the linter (e.g., "ruff check ."
or "flake8 .") before running tests; also update the "Run tests" step (the
pytest invocation) to include coverage reporting (e.g., add the --cov flag such
as "pytest tests/ -v --cov") and, if desired, add a step to upload coverage
reports so it mirrors the backend job's behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fdb9d003-e37c-48cf-92f6-193aaeb6fd99

📥 Commits

Reviewing files that changed from the base of the PR and between 9e9a0e4 and dd040c4.

📒 Files selected for processing (6)
  • .github/workflows/ci.yml
  • mcp-server/Dockerfile
  • mcp-server/config.py
  • mcp-server/requirements.txt
  • mcp-server/server.py
  • supabase/migrations/004_api_keys.sql
🚧 Files skipped from review as they are similar to previous changes (1)
  • mcp-server/Dockerfile

Comment thread mcp-server/server.py
- server.py: unknown transport now prints error and exits non-zero
  instead of silently falling back to stdio
- .flake8: new config matching backend (bugs-only, 120 char lines)
- ci.yml: MCP job now runs flake8 lint + pytest with --cov

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
.github/workflows/ci.yml (1)

20-33: ⚠️ Potential issue | 🟠 Major

Include the Supabase auth migration in this filter.

The new MCP auth surface is not only mcp-server/**; this PR also introduces supabase/migrations/004_api_keys.sql. As written, a PR that only changes the api_keys schema or RLS policies will skip both test-mcp and the widened security-scan, which leaves auth regressions untested and unscanned.

Suggested fix
          mcp:
            - 'mcp-server/**'
+           - 'supabase/migrations/**'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 20 - 33, The mcp filter only matches
'mcp-server/**' so changes to the new Supabase migration
(supabase/migrations/004_api_keys.sql) are not triggering downstream jobs;
update the filter definition used by the dorny/paths-filter step (id: filter) to
include the Supabase migration path (for example add
'supabase/migrations/004_api_keys.sql' or broaden to 'supabase/migrations/**')
under the mcp filter so PRs that touch the api_keys schema or RLS policies will
trigger test-mcp and security-scan.
♻️ Duplicate comments (1)
mcp-server/server.py (1)

74-88: ⚠️ Potential issue | 🟠 Major

Unknown transport values still fall through to stdio.

Anything other than "streamable-http" lands in the else branch, so MCP_TRANSPORT=http, mixed-case values, or typos silently start stdio and break the remote deployment path. Normalize and validate the chosen transport before dispatching instead of treating every unknown value as stdio.

🔧 Suggested fix
 def main():
     """Run with configured transport."""
-    transport = TRANSPORT
+    transport = TRANSPORT.strip().lower()
 
     # CLI override: --transport http
     if "--transport" in sys.argv:
         idx = sys.argv.index("--transport")
         if idx + 1 < len(sys.argv):
-            arg = sys.argv[idx + 1]
-            transport = "streamable-http" if arg in ("http", "streamable-http") else arg
+            transport = sys.argv[idx + 1].strip().lower()
+
+    transport = {"http": "streamable-http"}.get(transport, transport)
+    if transport not in {"stdio", "streamable-http"}:
+        raise SystemExit(
+            f"Unsupported transport '{transport}'. "
+            "Expected 'stdio' or 'streamable-http'."
+        )
 
     if transport == "streamable-http":
         import uvicorn
         print(f"Starting {SERVER_NAME} v{SERVER_VERSION} on {HOST}:{PORT}/mcp")
         uvicorn.run(_get_http_app(), host=HOST, port=PORT)
     else:
-        mcp.run(transport="stdio")
+        mcp.run(transport=transport)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcp-server/server.py` around lines 74 - 88, Normalize and validate the
transport value from TRANSPORT and CLI args before dispatch: lower-case the
chosen transport (use transport = transport.lower()), map accepted aliases
("http" -> "streamable-http") and explicitly handle known transports (e.g.,
"streamable-http" -> start uvicorn via _get_http_app(), "stdio" -> call
mcp.run(transport="stdio")), and if the value is unknown print a clear error and
exit non‑zero instead of falling through to stdio; update the sys.argv parsing
and dispatch logic that currently uses TRANSPORT, transport, uvicorn.run,
_get_http_app, and mcp.run accordingly.
🧹 Nitpick comments (1)
supabase/migrations/004_api_keys.sql (1)

53-70: Consider documenting the absence of DELETE policy.

There's no DELETE policy, meaning users cannot hard-delete their API keys—only soft-delete via setting active = false. This is likely intentional for audit purposes, but consider adding an explicit comment or a deny-all DELETE policy for clarity:

-- Users cannot hard-delete keys; deactivation (active=false) is the intended revocation mechanism.
-- Service role can hard-delete if needed for GDPR/data-retention compliance.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/migrations/004_api_keys.sql` around lines 53 - 70, Add documentation
and/or an explicit deny-all DELETE policy for the api_keys table to clarify that
hard-deletes are disallowed; update the migration near the api_keys RLS block
(where policies "Users can view own keys", "Users can create own keys", and
"Users can deactivate own keys" are defined) to include a comment like "Users
cannot hard-delete keys; deactivation (active=false) is the intended revocation
mechanism. Service role may hard-delete for compliance" and optionally add a
deny-all DELETE policy for readability/explicitness so future reviewers see
DELETE is intentionally restricted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mcp-server/config.py`:
- Around line 21-25: The current parsing of _port_raw into PORT only handles
non-numeric values; update the logic that parses _port_raw and assigns PORT to
validate the TCP port range (1–65535). After converting _port_raw to int (in the
existing try/except around PORT), check that PORT is within 1 and 65535 and if
not either set PORT to the sane default 8080 or raise a configuration error
(choose one consistent behavior for the app) and log or surface the problem;
reference the variables/functions _port_raw and PORT so you update that block
only. Ensure non-numeric values keep the existing fallback behavior and
out-of-range numeric values are handled deterministically.

In `@mcp-server/server.py`:
- Around line 65-69: The app returned by _get_http_app() currently returns the
raw mcp.streamable_http_app() with only a /health route, leaving the MCP
endpoint unprotected; modify _get_http_app() to enforce authentication on the
MCP endpoint before returning the app—either add an authentication middleware to
app or wrap/replace the MCP Route with a guarded handler that validates API keys
or Bearer tokens (use your existing auth validator function or token store) and
returns 401/403 on failure; ensure you apply this protection to the
mcp.streamable_http_app() routes (the MCP handler produced by
mcp.streamable_http_app()) while leaving _health publicly accessible.

In `@supabase/migrations/004_api_keys.sql`:
- Line 19: The last_used_at column is never updated; modify verify_key() in
backend/services/rate_limiter.py so that after a successful lookup of api_keys
(the existing db.table("api_keys").select(...).eq(...).execute() result) you
execute an update on that row to set last_used_at to the current timestamp
(e.g., now()/datetime.utcnow() or DB now()) using
db.table("api_keys").update({...}).eq("id", result.data[0]["id"]).execute();
alternatively, if you prefer not to track usage, remove the last_used_at column
from the schema to avoid misleading audit fields.

---

Outside diff comments:
In @.github/workflows/ci.yml:
- Around line 20-33: The mcp filter only matches 'mcp-server/**' so changes to
the new Supabase migration (supabase/migrations/004_api_keys.sql) are not
triggering downstream jobs; update the filter definition used by the
dorny/paths-filter step (id: filter) to include the Supabase migration path (for
example add 'supabase/migrations/004_api_keys.sql' or broaden to
'supabase/migrations/**') under the mcp filter so PRs that touch the api_keys
schema or RLS policies will trigger test-mcp and security-scan.

---

Duplicate comments:
In `@mcp-server/server.py`:
- Around line 74-88: Normalize and validate the transport value from TRANSPORT
and CLI args before dispatch: lower-case the chosen transport (use transport =
transport.lower()), map accepted aliases ("http" -> "streamable-http") and
explicitly handle known transports (e.g., "streamable-http" -> start uvicorn via
_get_http_app(), "stdio" -> call mcp.run(transport="stdio")), and if the value
is unknown print a clear error and exit non‑zero instead of falling through to
stdio; update the sys.argv parsing and dispatch logic that currently uses
TRANSPORT, transport, uvicorn.run, _get_http_app, and mcp.run accordingly.

---

Nitpick comments:
In `@supabase/migrations/004_api_keys.sql`:
- Around line 53-70: Add documentation and/or an explicit deny-all DELETE policy
for the api_keys table to clarify that hard-deletes are disallowed; update the
migration near the api_keys RLS block (where policies "Users can view own keys",
"Users can create own keys", and "Users can deactivate own keys" are defined) to
include a comment like "Users cannot hard-delete keys; deactivation
(active=false) is the intended revocation mechanism. Service role may
hard-delete for compliance" and optionally add a deny-all DELETE policy for
readability/explicitness so future reviewers see DELETE is intentionally
restricted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 44897365-3d71-442b-8761-f133887c2ae6

📥 Commits

Reviewing files that changed from the base of the PR and between 9e9a0e4 and dd040c4.

📒 Files selected for processing (6)
  • .github/workflows/ci.yml
  • mcp-server/Dockerfile
  • mcp-server/config.py
  • mcp-server/requirements.txt
  • mcp-server/server.py
  • supabase/migrations/004_api_keys.sql
🚧 Files skipped from review as they are similar to previous changes (2)
  • mcp-server/requirements.txt
  • mcp-server/Dockerfile

Comment thread mcp-server/config.py
Comment thread mcp-server/server.py
Comment thread supabase/migrations/004_api_keys.sql
@vercel

vercel Bot commented Mar 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
opencodeintel Ignored Ignored Preview Mar 7, 2026 5:27pm

- config.py: PORT now validated 1-65535, falls back to 8080 if out of range
- config.py: added MCP_AUTH_TOKEN env var
- server.py: Bearer token middleware on /mcp endpoint when MCP_AUTH_TOKEN set;
  /health remains public; no auth → 401, wrong token → 401
- auth.py: _validate_api_key now updates last_used_at on successful lookup
  (fire-and-forget, won't block auth on failure)
- 004_api_keys.sql: documented DELETE policy intent (soft-delete via active flag)
- ci.yml: supabase/migrations/** added to mcp path filter
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