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
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions src/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ SRC_ACCESS_TOKEN=<your-sourcegraph-access-token-optional>
SRC_ENDPOINT=https://sourcegraph.com
MCP_SSE_PORT=8000
MCP_STREAMABLE_HTTP_PORT=8080
FASTMCP_SSE_PATH=/sourcegraph/sse
FASTMCP_MESSAGE_PATH=/sourcegraph/messages/
2 changes: 1 addition & 1 deletion src/main.py
Original file line number Diff line number Diff line change
@@ -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()
34 changes: 7 additions & 27 deletions src/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand All @@ -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)
Expand Down Expand Up @@ -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")
Expand All @@ -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")
Expand All @@ -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")
Expand All @@ -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 = [
Expand All @@ -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())
Expand Down