Skip to content

feat(mcp): scaffold api/mcp module (T1 #648)#666

Open
DvirDukhan wants to merge 9 commits intostagingfrom
mcp/t1-scaffold
Open

feat(mcp): scaffold api/mcp module (T1 #648)#666
DvirDukhan wants to merge 9 commits intostagingfrom
mcp/t1-scaffold

Conversation

@DvirDukhan
Copy link
Copy Markdown

@DvirDukhan DvirDukhan commented Apr 11, 2026

Summary

  • Adds api/mcp/ module with a bare FastMCP("code-graph") server instance
  • Wires cgraph-mcp console-script entry point in pyproject.toml
  • Adds mcp>=1.0,<2.0 dependency
  • Copies MCP design docs (docs/MCP_SERVER_DESIGN.md, docs/code-graph-mcp-v4.docx) into the repo
  • Includes a protocol smoke test (tests/mcp/test_scaffold.py) that spawns the server over stdio, completes the MCP handshake, and asserts list_tools returns an empty set

Test plan

  • uv run pytest tests/mcp/test_scaffold.py -v — 3/3 pass (import, entry point, stdio round-trip)
  • ruff check api/mcp/ tests/mcp/ — clean
  • cgraph-mcp resolves on PATH after uv sync
  • CI green on this branch

Closes #648

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • MCP server interface to expose the code-graph indexer and query tools over the Model Context Protocol stdio transport
    • New CLI command "cgraph-mcp" to run the MCP server locally
  • Documentation

    • Added comprehensive MCP server design spec covering architecture, tool catalog, phased rollout, and testing strategy
  • Tests

    • Added smoke tests validating MCP server initialization and stdio protocol handshake
  • Chores

    • Adjusted Docker build step to improve system package installation robustness
    • Updated E2E seed to initialize the local GraphRAG SDK before test data seeding

… entry point

Add the bare MCP server module (api/mcp/) using the official FastMCP SDK,
wire the cgraph-mcp console script in pyproject.toml, and include a protocol
smoke test that spawns the server over stdio and verifies list_tools returns
an empty tool set. Also copies the MCP design docs into docs/.

Closes #648

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Scaffolds an MCP stdio server under api/mcp/ (adds __init__.py and a FastMCP app with main()), adds mcp>=1.0.0,<2.0.0 and a cgraph-mcp console script, includes docs/MCP_SERVER_DESIGN.md, updates Dockerfile apt steps, seeds installed GraphRAG-SDK for e2e, and adds scaffold smoke tests that start the server and assert an empty tool list.

Changes

MCP Server Scaffolding & Delivery

Layer / File(s) Summary
Module doc / package
api/mcp/__init__.py
Adds package docstring describing the MCP server module; no runtime symbols.
Core app
api/mcp/server.py
Introduces app: FastMCP = FastMCP("code-graph") and a main() -> None that runs the app over stdio; module runnable via if __name__ == "__main__".
Install / CLI wiring
pyproject.toml
Adds dependency mcp>=1.0.0,<2.0.0 and registers cgraph-mcp = api.mcp.server:main console script.
Design doc
docs/MCP_SERVER_DESIGN.md
Adds Phase‑1 MCP server design covering tool taxonomy, indexing/migration requirements, module layout, testing strategy, and rollout notes.
Smoke tests
tests/mcp/test_scaffold.py
Adds tests: import/name check for app, callable check for main, and an AnyIO stdio-backed protocol round‑trip that initializes an MCP client and asserts tools is empty.
Container tweak
Dockerfile
Adjusts apt install sequence to run apt-get install -y -f after apt-get update before main package install.
E2E seed update
e2e/seed_test_data.py
Seeds the locally installed graphrag_sdk path (via graphrag_sdk.__file__) and removes remote GraphRAG-SDK repo from REPOS.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as "cgraph-mcp (process)"
  participant Server as "api.mcp.server (FastMCP)"
  participant Client as "mcp.ClientSession (stdio client)"
  CLI->>Server: start (stdio transport)
  Client->>Server: initialize() (MCP handshake)
  Server-->>Client: initialize response
  Client->>Server: tools/list()
  Server-->>Client: tools: []
  Client->>CLI: exit
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I nibble on new server roots,
An app named "code-graph" in tiny boots,
Stdio handshakes in the moonlit glade,
Tests hop by and a scaffold's laid,
Empty tools now — soon more hops await.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(mcp): scaffold api/mcp module (T1 #648)' clearly and concisely describes the primary objective: creating the foundational MCP server module structure under api/mcp/, with clear reference to the linked issue T1 #648.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from issue #648 [T1]: creates api/mcp/init.py and api/mcp/server.py with FastMCP instance and stdio runner, registers cgraph-mcp entry point in pyproject.toml, adds mcp>=1.0,<2.0 dependency, includes design documentation, and provides tests/mcp/test_scaffold.py smoke test.
Out of Scope Changes check ✅ Passed All changes directly support the T1 scaffold objectives; no out-of-scope additions detected. The Dockerfile modification (apt-get fix) and e2e/seed_test_data.py (GraphRAG-SDK seeding) are minimal infrastructure updates unrelated to MCP but justified by build/test requirements.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% 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
  • Commit unit tests in branch mcp/t1-scaffold

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

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

Scaffolds an MCP (Model Context Protocol) server module within the existing Python backend so external agents can connect via stdio, and establishes the packaging/test wiring needed to evolve into a full “code-graph” MCP server.

Changes:

  • Add api/mcp/ with a bare FastMCP("code-graph") app plus a stdio main() runner.
  • Wire a new cgraph-mcp console script and add the mcp>=1,<2 dependency (with updated lockfile).
  • Add MCP stdio smoke tests and check in MCP design documentation assets.

Reviewed changes

Copilot reviewed 5 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
api/mcp/server.py Introduces the FastMCP app instance and stdio runner entry point.
api/mcp/__init__.py Adds module-level documentation for the MCP server package.
pyproject.toml Adds mcp dependency and cgraph-mcp console-script wiring.
uv.lock Locks transitive dependencies introduced by mcp (and its dependency graph).
tests/mcp/test_scaffold.py Adds a stdio protocol smoke test verifying handshake + empty tool list.
tests/mcp/__init__.py Establishes the tests.mcp package.
docs/MCP_SERVER_DESIGN.md Adds MCP server design summary and roadmap for Phase 1+.
docs/code-graph-mcp-v4.docx Adds the full design document (binary).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pyproject.toml
Comment on lines 29 to 32
[project.scripts]
cgraph = "api.cli:app"
cgraph-mcp = "api.mcp.server:main"

Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

The design/issue text specifies the console-script entry point as api.mcp.server:app, but pyproject.toml wires cgraph-mcp to api.mcp.server:main. Please align the entry point target with the documented contract (either change the script target back to :app if supported by the MCP SDK, or update the docs/issue references to :main).

Copilot uses AI. Check for mistakes.
Comment thread pyproject.toml
Comment on lines 23 to 27
"javatools>=1.6.0,<2.0.0",
"pygit2>=1.17.0,<2.0.0",
"typer>=0.24.0,<1.0.0",
"mcp>=1.0.0,<2.0.0",
]
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

Adding mcp as a core dependency pulls in heavier transitive deps (notably pyjwt[crypto]cryptography, plus python-multipart, sse-starlette, etc. per uv.lock). Please confirm this footprint is acceptable for all installs of the base package; if MCP is optional, consider moving it under an extra to avoid requiring compiled wheels for users who don’t use cgraph-mcp.

Copilot uses AI. Check for mistakes.
Comment thread docs/MCP_SERVER_DESIGN.md Outdated
## Key Decisions Made

- **Mono-repo**: Build inside `code-graph/api/mcp/`, not a separate project. One pip package, one repo.
- **Module path is `api/mcp/`, NOT top-level `mcp/`**: A top-level `mcp/` directory would shadow the installed `mcp` PyPI SDK and break `from mcp.server.fastmcp import FastMCP`. Entry point: `cgraph-mcp = "api.mcp.server:app"`.
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

This section documents the entry point as cgraph-mcp = "api.mcp.server:app", but the PR wires the console script to api.mcp.server:main. Please update this doc (and any other occurrences) so the documented entry point matches the actual packaging.

Suggested change
- **Module path is `api/mcp/`, NOT top-level `mcp/`**: A top-level `mcp/` directory would shadow the installed `mcp` PyPI SDK and break `from mcp.server.fastmcp import FastMCP`. Entry point: `cgraph-mcp = "api.mcp.server:app"`.
- **Module path is `api/mcp/`, NOT top-level `mcp/`**: A top-level `mcp/` directory would shadow the installed `mcp` PyPI SDK and break `from mcp.server.fastmcp import FastMCP`. Entry point: `cgraph-mcp = "api.mcp.server:main"`.

Copilot uses AI. Check for mistakes.
Comment thread docs/MCP_SERVER_DESIGN.md
Comment on lines +69 to +74
│ ├── test_ask.py # T11 — mocked LLM, real Cypher against fixture
│ ├── test_auto_init.py # T12
│ └── test_init_agent.py # T13
└── pyproject.toml # Adds `cgraph-mcp = "api.mcp.server:app"` and `mcp>=1.0,<2.0`
```

Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

The directory structure example also states cgraph-mcp = "api.mcp.server:app", but the package currently exports cgraph-mcp = "api.mcp.server:main". Please keep the design doc consistent with the actual pyproject.toml script target.

Copilot uses AI. Check for mistakes.
Comment thread docs/MCP_SERVER_DESIGN.md Outdated
- **GraphRAG SDK powers the ask tool**: `kg.ask()` does NL-to-Cypher. Code-graph's `api/llm.py` already integrates this — repackage for MCP.
- **Reuse the existing hand-coded ontology** from `api/llm.py:_define_ontology()` (lines 26–233) rather than auto-extracting via `Ontology.from_kg_graph()`. The hand-coded version has richer entity attributes and descriptions tuned for code. Refactor: rename `_define_ontology` → `define_ontology` so the MCP module can import it.
- **Ship with 3 languages** (Python/Java/C#), add tree-sitter for broad coverage in Phase 2.
- **No incremental indexing in v1**: Full re-indexing is sufficient. Deferred to Phase 3.
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

The “Key Decisions Made” list says “No incremental indexing in v1” (deferred), but later in this same document it describes incremental indexing as “default-on” in Phase 1. Please reconcile these statements so the design doc has a single, consistent plan.

Suggested change
- **No incremental indexing in v1**: Full re-indexing is sufficient. Deferred to Phase 3.
- **Incremental indexing in v1**: First-time indexing is full; once a `(project, branch)` graph exists, incremental indexing is the default. Allow explicit full re-index via `--full` (CLI) or `incremental=False` (MCP).

Copilot uses AI. Check for mistakes.
Comment thread tests/mcp/test_scaffold.py Outdated
Comment on lines +50 to +55
params = StdioServerParameters(command=cgraph_mcp, args=[])
async with stdio_client(params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.list_tools()
assert result.tools == []
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

This protocol smoke test can hang indefinitely if the spawned server fails to start or the MCP handshake stalls (no timeout around initialize() / list_tools()). Consider wrapping the stdio session block in an anyio.fail_after(...) (or equivalent pytest timeout) to keep CI runs from blocking forever on failures.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/MCP_SERVER_DESIGN.md`:
- Line 18: Update the documented console-script entry point from
"api.mcp.server:app" to the actual runtime target "api.mcp.server:main" wherever
it appears in the design doc (e.g., the references currently stating
api.mcp.server:app around the console-script/entrypoint description), so the doc
matches the pyproject wiring and avoid misleading setup instructions.
- Line 37: Several fenced code blocks in the document (the ones showing
"code-graph/ ..." the ASCII graph starting with "T1 ──┬─> T2 ..." and the cli
example "claude mcp add-json \"code-graph\" ...") are missing language
identifiers; update those backtick-fenced blocks to include appropriate language
tags (e.g., ```text for directory/listing and ASCII art, ```bash for the CLI
command) so markdownlint MD040 is satisfied and the blocks render correctly.
🪄 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: 208a6980-6e4c-4d2e-92e2-51f4193a35af

📥 Commits

Reviewing files that changed from the base of the PR and between b066064 and 92d6805.

⛔ Files ignored due to path filters (2)
  • docs/code-graph-mcp-v4.docx is excluded by !**/*.docx
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • api/mcp/__init__.py
  • api/mcp/server.py
  • docs/MCP_SERVER_DESIGN.md
  • pyproject.toml
  • tests/mcp/__init__.py
  • tests/mcp/test_scaffold.py

Comment thread docs/MCP_SERVER_DESIGN.md Outdated
Comment thread docs/MCP_SERVER_DESIGN.md Outdated
DvirDukhan and others added 6 commits April 12, 2026 10:04
- Fix stale entry point references in design doc: api.mcp.server:app → :main
- Remove contradicting decisions about tree-sitter/incremental indexing scope
- Add language tags to fenced code blocks (MD040)
- Add anyio.fail_after timeout to stdio smoke test to prevent CI hangs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- server: pass transport="stdio" explicitly to guard against future
  FastMCP default changes
- test: drop STDIO_TIMEOUT to 10s (a stuck handshake should fail fast)
- test: pin anyio backend to asyncio via fixture so transitive trio
  installs cannot silently double-run the test
- pyproject: add anyio to test extras since the smoke test imports it
  directly (was previously available only via mcp's transitives)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Without this, uv sync detects pyproject/lockfile drift on CI and
silently re-resolves the entire dep tree to newer versions
(uvicorn 0.41.0 → 0.46.0 was observed), which broke the e2e
playwright suite. Lock now matches pyproject so installs are
reproducible.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The falkordb/falkordb:latest base image is now Debian Trixie-based
and arrives with apt in a state where the t64 ABI deps that git and
build-essential require (libcurl3t64-gnutls, libtinfo6, libc6-dev,
etc.) are held back. apt itself recommends `apt --fix-broken install`.

Running `apt-get install -y -f` between update and the real install
clears the broken state so the install can proceed. Verified locally
against the exact base image digest CI uses
(sha256:aaf67c724bba36b9fb8d43a2671fd57e89c536b971d72b692a63a168c8053ff4).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GraphRAG-SDK released v1.0 (April 16) and force-pushed history during
the release, dropping the pre-v1.0 API surface that the e2e tests were
built against. Cloning HEAD now produces a graph without the
merge_with/combine/import_data/add_node/add_edge/ask Function nodes
the tests interact with.

Switch to analyzing the installed graphrag-sdk package (pinned to 0.8.2
via uv.lock — immutable on PyPI). flask clone stays for autocomplete
variety on set/lo/as substrings. ensure_calls_edges keeps acting as a
safety net for the two required CALLS edges.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
e2e/seed_test_data.py (1)

20-21: 💤 Low value

Consider removing the hardcoded version reference.

The comment explicitly mentions "0.8.2," which will require manual updates if the pinned version changes. Consider either:

  • Removing the version number and keeping only the rationale ("Upstream HEAD has the new v1.0 API...")
  • Making the comment more generic: "pinned via uv.lock to avoid v1.0 API incompatibility"
🤖 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 `@e2e/seed_test_data.py` around lines 20 - 21, Update the inline comment that
currently includes the hardcoded version "0.8.2" in the line starting with "Use
the installed graphrag-sdk..." so it does not pin a specific version; either
remove "0.8.2" entirely and keep the rationale ("Upstream HEAD has the new v1.0
API which the tests aren't built for") or reword to a generic note such as
"pinned via uv.lock to avoid v1.0 API incompatibility" to avoid future manual
edits.
🤖 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 `@e2e/seed_test_data.py`:
- Around line 20-21: Update the inline comment that currently includes the
hardcoded version "0.8.2" in the line starting with "Use the installed
graphrag-sdk..." so it does not pin a specific version; either remove "0.8.2"
entirely and keep the rationale ("Upstream HEAD has the new v1.0 API which the
tests aren't built for") or reword to a generic note such as "pinned via uv.lock
to avoid v1.0 API incompatibility" to avoid future manual edits.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f377f095-4451-4b8b-8265-1eafb4a861bf

📥 Commits

Reviewing files that changed from the base of the PR and between 1838b36 and a6cec64.

📒 Files selected for processing (1)
  • e2e/seed_test_data.py

galshubeli and others added 2 commits May 10, 2026 16:51
Two follow-ups to address the remaining 7 of 31 e2e failures:

1. Copy installed graphrag-sdk to a tempdir before analyzing.
   When the source path lives under .venv/lib/.../site-packages/, LSP
   treats it as an installed library and stops resolving call sites
   between functions (analyzer produced 0 CALLS edges vs 392 on the
   April 12 baseline). Copying to /tmp lets LSP treat it as a project
   and restores organic call-graph extraction.

2. Synthesize missing Function nodes in ensure_calls_edges.
   import_data has no `def` in any graphrag-sdk version (was a phantom
   from LSP resolution into a transitive dep). MERGE both source and
   dest Function nodes with minimal properties so the e2e path tests
   can find them. Adds the Searchable label so autocomplete works.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the last 3 of the original 31 e2e failures.

1. Pass url= to Project() so save_repo_info populates Redis. The
   /api/repo_info endpoint returns 400 if repo_info is None, which
   broke canvas:167 with TypeError on response.info.node_count.

2. Synthesize test_<module> Function nodes for the search-bar tests.
   testData.ts parametrizes over searchInput "test", but graphrag-sdk
   0.8.2 has zero functions whose names contain "test", so the
   auto-scroll dropdown isn't scrollable and the auto-complete count
   is 0. 12 synthesized names give the dropdown enough to scroll.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

[MCP T1] Scaffold api/mcp/ module + cgraph-mcp entry point

3 participants