-
Notifications
You must be signed in to change notification settings - Fork 48
feat(mcp): scaffold api/mcp module (T1 #648) #666
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: staging
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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``). | ||
| """ |
| 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() |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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", | ||
| ] | ||
|
|
||
| [project.scripts] | ||
| cgraph = "api.cli:app" | ||
| cgraph-mcp = "api.mcp.server:main" | ||
|
|
||
|
Comment on lines
29
to
32
|
||
| [project.optional-dependencies] | ||
| test = [ | ||
|
|
||
| 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 == [] |
Large diffs are not rendered by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding
mcpas a core dependency pulls in heavier transitive deps (notablypyjwt[crypto]→cryptography, pluspython-multipart,sse-starlette, etc. peruv.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 usecgraph-mcp.