Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions api/mcp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""MCP server module for code-graph.

Exposes the code-graph indexer and graph queries as MCP tools so AI coding
agents (Claude Code, Cursor, Copilot, Roo/Cline) can drive the indexer over
the standard Model Context Protocol stdio transport.

Entry point: ``cgraph-mcp`` (defined in ``pyproject.toml``).
"""
26 changes: 26 additions & 0 deletions api/mcp/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""FastMCP server for code-graph.

This is the scaffold (T1). It instantiates a single FastMCP app, exposes it
as ``app`` for tests and embedders, and registers a ``main()`` entry point
that runs the server over stdio. Tools are registered in later tickets
(T4-T8, T11) by importing this module's ``app`` and decorating functions
with ``@app.tool(...)``.
"""

from __future__ import annotations

from mcp.server.fastmcp import FastMCP

app: FastMCP = FastMCP("code-graph")


def main() -> None:
"""Run the MCP server over stdio.

Console-script entry point for ``cgraph-mcp``.
"""
app.run()


if __name__ == "__main__":
main()
321 changes: 321 additions & 0 deletions docs/MCP_SERVER_DESIGN.md

Large diffs are not rendered by default.

Binary file added docs/code-graph-mcp-v4.docx
Binary file not shown.
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ dependencies = [
"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",
]
Comment on lines 23 to 27
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.

[project.scripts]
cgraph = "api.cli:app"
cgraph-mcp = "api.mcp.server:main"

Comment on lines 29 to 32
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.
[project.optional-dependencies]
test = [
Expand Down
Empty file added tests/mcp/__init__.py
Empty file.
59 changes: 59 additions & 0 deletions tests/mcp/test_scaffold.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Scaffold smoke tests for the cgraph-mcp server (T1).

These tests prove the bare module is wired correctly:

1. The FastMCP ``app`` instance is importable.
2. The ``cgraph-mcp`` console script spawns a working stdio MCP server.
3. A client can complete the MCP handshake and ``list_tools`` returns 0
tools (no tools are registered yet — they land in T4-T8, T11).

When tool tickets land they should ADD tests, not modify these — these
guard the scaffold itself.
"""

from __future__ import annotations

import shutil

import anyio
import pytest
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

STDIO_TIMEOUT = 30 # seconds — prevents CI from hanging if the server fails to start


def test_app_is_importable() -> None:
"""The FastMCP instance can be imported and is named ``code-graph``."""
from api.mcp.server import app

assert app is not None
assert app.name == "code-graph"


def test_main_entry_point_exists() -> None:
"""``main()`` is exposed for the console script."""
from api.mcp import server

assert callable(server.main)


@pytest.mark.anyio
async def test_stdio_server_lists_zero_tools() -> None:
"""Spawn ``cgraph-mcp`` over stdio and verify the protocol handshake.

The scaffold registers no tools, so ``list_tools`` must return an
empty list. Tool tickets (T4-T8, T11) extend this expectation.
"""
cgraph_mcp = shutil.which("cgraph-mcp")
assert cgraph_mcp is not None, (
"cgraph-mcp not on PATH; run `uv pip install -e .` first"
)

params = StdioServerParameters(command=cgraph_mcp, args=[])
with anyio.fail_after(STDIO_TIMEOUT):
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 == []
138 changes: 138 additions & 0 deletions uv.lock

Large diffs are not rendered by default.

Loading