diff --git a/Dockerfile b/Dockerfile index 45c51ec..2212b74 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,8 @@ RUN uv sync ENV MCP_SSE_PORT=8000 ENV MCP_STREAMABLE_HTTP_PORT=8080 +ENV FASTMCP_SSE_PATH=/sourcegraph/sse +ENV FASTMCP_MESSAGE_PATH=/sourcegraph/messages/ EXPOSE ${MCP_SSE_PORT} ${MCP_STREAMABLE_HTTP_PORT} diff --git a/README.md b/README.md index 25bd58e..a95d79c 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,8 @@ docker run -p 9000:9000 -p 9080:9080 \ - `SRC_ACCESS_TOKEN`: Authentication token for private Sourcegraph instances - `MCP_SSE_PORT`: SSE server port (default: 8000) - `MCP_STREAMABLE_HTTP_PORT`: HTTP server port (default: 8080) +- `FASTMCP_SSE_PATH`: SSE endpoint path (default: /sourcegraph/sse) +- `FASTMCP_MESSAGE_PATH`: SSE messages endpoint path (default: /sourcegraph/messages/) ## Usage with AI Tools diff --git a/pyproject.toml b/pyproject.toml index d934862..7e9bd93 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ classifiers = [ ] dependencies = [ - "fastmcp==2.4.0", + "fastmcp>=2.11.2", "requests==2.32.4", "jinja2==3.1.6", "pyyaml==6.0.2", diff --git a/src/.env.sample b/src/.env.sample index be15b6b..69a4958 100644 --- a/src/.env.sample +++ b/src/.env.sample @@ -2,3 +2,5 @@ SRC_ACCESS_TOKEN= SRC_ENDPOINT=https://sourcegraph.com MCP_SSE_PORT=8000 MCP_STREAMABLE_HTTP_PORT=8080 +FASTMCP_SSE_PATH=/sourcegraph/sse +FASTMCP_MESSAGE_PATH=/sourcegraph/messages/ diff --git a/src/main.py b/src/main.py index e6ce9ae..40ba719 100644 --- a/src/main.py +++ b/src/main.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Main entry point for Sourcegraph MCP server.""" -from server import main +from sourcegraph_mcp.server import main if __name__ == "__main__": main() diff --git a/src/server.py b/src/server.py index 82303d0..dbefa45 100644 --- a/src/server.py +++ b/src/server.py @@ -8,10 +8,9 @@ import requests from dotenv import load_dotenv from fastmcp import FastMCP - -from backends import SourcegraphClient, SourcegraphContentFetcher -from backends.models import FormattedResult -from core import PromptManager +from sourcegraph_mcp.backends import SourcegraphClient, SourcegraphContentFetcher +from sourcegraph_mcp.backends.models import FormattedResult +from sourcegraph_mcp.core import PromptManager logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) @@ -37,7 +36,7 @@ def _get_required_env(key: str) -> str: config = ServerConfig() -server = FastMCP(sse_path="/sourcegraph/sse", message_path="/sourcegraph/messages/") +server = FastMCP() search_client = SourcegraphClient(endpoint=config.sourcegraph_endpoint, token=config.sourcegraph_token) content_fetcher = SourcegraphContentFetcher(endpoint=config.sourcegraph_endpoint, token=config.sourcegraph_token) @@ -66,6 +65,7 @@ def signal_handler(sig: int, frame: Any) -> None: _shutdown_requested = True +@server.tool(description=FETCH_CONTENT_DESCRIPTION) def fetch_content(repo: str, path: str) -> str: if _shutdown_requested: logger.info("Shutdown in progress, declining new requests") @@ -82,6 +82,7 @@ def fetch_content(repo: str, path: str) -> str: return "error fetching content" +@server.tool(description=SEARCH_TOOL_DESCRIPTION) def search(query: str) -> List[FormattedResult]: if _shutdown_requested: logger.info("Shutdown in progress, declining new requests") @@ -101,6 +102,7 @@ def search(query: str) -> List[FormattedResult]: return [] +@server.tool(description=SEARCH_PROMPT_GUIDE_DESCRIPTION) def search_prompt_guide(objective: str) -> str: if _shutdown_requested: logger.info("Shutdown in progress, declining new prompt guide requests") @@ -120,26 +122,6 @@ def search_prompt_guide(objective: str) -> str: return "".join(prompt_parts) -def _register_tools() -> None: - """Register MCP tools with the server.""" - tool_descriptions = { - "search": SEARCH_TOOL_DESCRIPTION, - "search_prompt_guide": SEARCH_PROMPT_GUIDE_DESCRIPTION, - "fetch_content": FETCH_CONTENT_DESCRIPTION, - } - - tools = [ - (search, "search"), - (search_prompt_guide, "search_prompt_guide"), - (fetch_content, "fetch_content"), - ] - - for tool_func, tool_name in tools: - description = tool_descriptions.get(tool_name, "") - server.add_tool(tool_func, tool_name, description) - logger.info(f"Registered tool: {tool_name}") - - async def _run_server() -> None: """Run the FastMCP server with both HTTP and SSE transports.""" tasks = [ @@ -158,8 +140,6 @@ def main() -> None: signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) - _register_tools() - try: logger.info("Starting Sourcegraph MCP server...") asyncio.run(_run_server())