Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9869051

Browse files
refactor to run with --mcp and add metrics
1 parent 46e9749 commit 9869051

File tree

6 files changed

+115
-23
lines changed

6 files changed

+115
-23
lines changed

‎CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
### Features
66

77
* **mcp:** Add Model Context Protocol (MCP) server support
8-
- New `--mcp-server` CLI option to start MCP server
8+
- New `--mcp` CLI option to start MCP server
99
- `ingest_repository` tool for LLM integration
1010
- Full MCP protocol compliance with stdio transport
1111
- Enhanced MCP client examples for stdio transport

‎examples/mcp-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"mcpServers": {
33
"gitingest": {
44
"command": "gitingest",
5-
"args": ["--mcp-server"],
5+
"args": ["--mcp"],
66
"env": {
77
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
88
}

‎src/gitingest/__main__.py

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from __future__ import annotations
55

66
import asyncio
7+
import os
78
from typing import TypedDict
89

910
import click
@@ -15,6 +16,15 @@
1516
# Import logging configuration first to intercept all logging
1617
from gitingest.utils.logging_config import get_logger
1718

19+
# Optional MCP imports
20+
try:
21+
from gitingest.mcp_server import start_mcp_server
22+
from mcp_server.main import start_mcp_server_tcp
23+
24+
MCP_AVAILABLE = True
25+
except ImportError:
26+
MCP_AVAILABLE = False
27+
1828
# Initialize logger for this module
1929
logger = get_logger(__name__)
2030

@@ -29,7 +39,10 @@ class _CLIArgs(TypedDict):
2939
include_submodules: bool
3040
token: str | None
3141
output: str | None
32-
mcp_server: bool
42+
mcp: bool
43+
transport: str
44+
host: str
45+
port: int
3346

3447

3548
@click.command()
@@ -78,11 +91,31 @@ class _CLIArgs(TypedDict):
7891
help="Output file path (default: digest.txt in current directory). Use '-' for stdout.",
7992
)
8093
@click.option(
81-
"--mcp-server",
94+
"--mcp",
8295
is_flag=True,
8396
default=False,
8497
help="Start the MCP (Model Context Protocol) server for LLM integration",
8598
)
99+
@click.option(
100+
"--transport",
101+
type=click.Choice(["stdio", "tcp"]),
102+
default="stdio",
103+
show_default=True,
104+
help="Transport protocol for MCP communication (only used with --mcp)",
105+
)
106+
@click.option(
107+
"--host",
108+
default="127.0.0.1",
109+
show_default=True,
110+
help="Host to bind TCP server (only used with --mcp --transport tcp)",
111+
)
112+
@click.option(
113+
"--port",
114+
type=int,
115+
default=8001,
116+
show_default=True,
117+
help="Port for TCP server (only used with --mcp --transport tcp)",
118+
)
86119
def main(**cli_kwargs: Unpack[_CLIArgs]) -> None:
87120
"""Run the CLI entry point to analyze a repo / directory and dump its contents.
88121
@@ -107,7 +140,8 @@ def main(**cli_kwargs: Unpack[_CLIArgs]) -> None:
107140
$ gitingest https://github.com/user/repo --output -
108141
109142
MCP server mode:
110-
$ gitingest --mcp-server
143+
$ gitingest --mcp
144+
$ gitingest --mcp --transport tcp --host 0.0.0.0 --port 8001
111145
112146
With filtering:
113147
$ gitingest -i "*.py" -e "*.log"
@@ -135,7 +169,10 @@ async def _async_main(
135169
include_submodules: bool = False,
136170
token: str | None = None,
137171
output: str | None = None,
138-
mcp_server: bool = False,
172+
mcp: bool = False,
173+
transport: str = "stdio",
174+
host: str = "127.0.0.1",
175+
port: int = 8001,
139176
) -> None:
140177
"""Analyze a directory or repository and create a text dump of its contents.
141178
@@ -165,8 +202,14 @@ async def _async_main(
165202
output : str | None
166203
The path where the output file will be written (default: ``digest.txt`` in current directory).
167204
Use ``"-"`` to write to ``stdout``.
168-
mcp_server : bool
205+
mcp : bool
169206
If ``True``, starts the MCP (Model Context Protocol) server instead of normal operation (default: ``False``).
207+
transport : str
208+
Transport protocol for MCP communication: "stdio" or "tcp" (default: "stdio").
209+
host : str
210+
Host to bind TCP server (only used with transport="tcp", default: "127.0.0.1").
211+
port : int
212+
Port for TCP server (only used with transport="tcp", default: 8001).
170213
171214
Raises
172215
------
@@ -177,17 +220,21 @@ async def _async_main(
177220
178221
"""
179222
# Check if MCP server mode is requested
180-
if mcp_server:
181-
# Dynamic import to avoid circular imports and optional dependency
182-
try:
183-
from gitingest.mcp_server import ( # noqa: PLC0415 # pylint: disable=import-outside-toplevel
184-
start_mcp_server,
185-
)
186-
223+
if mcp:
224+
if not MCP_AVAILABLE:
225+
msg = "MCP server dependencies not installed"
226+
raise click.ClickException(msg)
227+
228+
if transport == "tcp":
229+
# Use TCP transport with FastMCP and metrics support
230+
# Enable metrics for TCP mode if not already set
231+
if os.getenv("GITINGEST_METRICS_ENABLED") is None:
232+
os.environ["GITINGEST_METRICS_ENABLED"] = "true"
233+
234+
await start_mcp_server_tcp(host, port)
235+
else:
236+
# Use stdio transport (default) - metrics not available in stdio mode
187237
await start_mcp_server()
188-
except ImportError as e:
189-
msg = f"MCP server dependencies not installed: {e}"
190-
raise click.ClickException(msg) from e
191238
return
192239

193240
try:

‎src/gitingest/mcp_server.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from mcp.server import Server # pylint: disable=import-error
88
from mcp.server.stdio import stdio_server # pylint: disable=import-error
99
from mcp.types import TextContent, Tool # pylint: disable=import-error
10+
from prometheus_client import Counter
1011

1112
from gitingest.entrypoint import ingest_async
1213
from gitingest.utils.logging_config import get_logger
@@ -17,6 +18,10 @@
1718
# Initialize logger for this module
1819
logger = get_logger(__name__)
1920

21+
# Create Prometheus metrics
22+
mcp_ingest_counter = Counter("gitingest_mcp_ingest_total", "Number of MCP ingests", ["status"])
23+
mcp_tool_calls_counter = Counter("gitingest_mcp_tool_calls_total", "Number of MCP tool calls", ["tool_name", "status"])
24+
2025
# Create the MCP server instance
2126
app = Server("gitingest")
2227

@@ -84,11 +89,18 @@ async def list_tools() -> list[Tool]:
8489
async def call_tool(name: str, arguments: dict[str, Any]) -> Sequence[TextContent]:
8590
"""Execute a tool."""
8691
try:
92+
mcp_tool_calls_counter.labels(tool_name=name, status="started").inc()
93+
8794
if name == "ingest_repository":
88-
return await _handle_ingest_repository(arguments)
95+
result = await _handle_ingest_repository(arguments)
96+
mcp_tool_calls_counter.labels(tool_name=name, status="success").inc()
97+
return result
98+
99+
mcp_tool_calls_counter.labels(tool_name=name, status="unknown_tool").inc()
89100
return [TextContent(type="text", text=f"Unknown tool: {name}")]
90101
except Exception as e:
91102
logger.exception("Error in tool call %s", name)
103+
mcp_tool_calls_counter.labels(tool_name=name, status="error").inc()
92104
return [TextContent(type="text", text=f"Error executing {name}: {e!s}")]
93105

94106

@@ -143,10 +155,12 @@ async def _handle_ingest_repository(arguments: dict[str, Any]) -> Sequence[TextC
143155
*Generated by Gitingest MCP Server*
144156
"""
145157

158+
mcp_ingest_counter.labels(status="success").inc()
146159
return [TextContent(type="text", text=response_content)]
147160

148161
except Exception as e:
149162
logger.exception("Error during ingestion")
163+
mcp_ingest_counter.labels(status="error").inc()
150164
return [TextContent(type="text", text=f"Error ingesting repository: {e!s}")]
151165

152166

‎src/mcp_server/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
@click.option(
2929
"--port",
3030
type=int,
31-
default=8001,
31+
default=8000,
3232
show_default=True,
3333
help="Port for TCP server (only used with --transport tcp)",
3434
)

‎src/mcp_server/main.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,31 @@
22

33
from __future__ import annotations
44

5+
import os
6+
import threading
7+
8+
import uvicorn
9+
from fastapi import FastAPI
10+
from fastapi.middleware.cors import CORSMiddleware
11+
from fastapi.responses import JSONResponse
512
from mcp.server.fastmcp import FastMCP # pylint: disable=import-error
13+
from prometheus_client import Counter
614

715
from gitingest.entrypoint import ingest_async
816
from gitingest.utils.logging_config import get_logger
17+
from server.metrics_server import start_metrics_server
918

1019
# Initialize logger for this module
1120
logger = get_logger(__name__)
1221

22+
# Create Prometheus metrics
23+
fastmcp_ingest_counter = Counter("gitingest_fastmcp_ingest_total", "Number of FastMCP ingests", ["status"])
24+
fastmcp_tool_calls_counter = Counter(
25+
"gitingest_fastmcp_tool_calls_total",
26+
"Number of FastMCP tool calls",
27+
["tool_name", "status"],
28+
)
29+
1330
# Create the FastMCP server instance
1431
mcp = FastMCP("gitingest")
1532

@@ -40,6 +57,7 @@ async def ingest_repository(
4057
4158
"""
4259
try:
60+
fastmcp_tool_calls_counter.labels(tool_name="ingest_repository", status="started").inc()
4361
logger.info("Starting MCP ingestion", extra={"source": source})
4462

4563
# Convert patterns to sets if provided
@@ -58,8 +76,14 @@ async def ingest_repository(
5876
token=token,
5977
output=None, # Don't write to file, return content instead
6078
)
79+
80+
fastmcp_ingest_counter.labels(status="success").inc()
81+
fastmcp_tool_calls_counter.labels(tool_name="ingest_repository", status="success").inc()
82+
6183
except Exception:
6284
logger.exception("Error during ingestion")
85+
fastmcp_ingest_counter.labels(status="error").inc()
86+
fastmcp_tool_calls_counter.labels(tool_name="ingest_repository", status="error").inc()
6387
return "Error ingesting repository: An internal error occurred"
6488

6589
# Create a structured response and return directly
@@ -85,10 +109,17 @@ async def start_mcp_server_tcp(host: str = "127.0.0.1", port: int = 8001) -> Non
85109
"""Start the MCP server with HTTP transport using SSE."""
86110
logger.info("Starting Gitingest MCP server with HTTP/SSE transport on %s:%s", host, port)
87111

88-
import uvicorn # noqa: PLC0415 # pylint: disable=import-outside-toplevel
89-
from fastapi import FastAPI # noqa: PLC0415 # pylint: disable=import-outside-toplevel
90-
from fastapi.middleware.cors import CORSMiddleware # noqa: PLC0415 # pylint: disable=import-outside-toplevel
91-
from fastapi.responses import JSONResponse # noqa: PLC0415 # pylint: disable=import-outside-toplevel
112+
# Start metrics server in a separate thread if enabled
113+
if os.getenv("GITINGEST_METRICS_ENABLED") is not None:
114+
metrics_host = os.getenv("GITINGEST_METRICS_HOST", "127.0.0.1")
115+
metrics_port = int(os.getenv("GITINGEST_METRICS_PORT", "9090"))
116+
metrics_thread = threading.Thread(
117+
target=start_metrics_server,
118+
args=(metrics_host, metrics_port),
119+
daemon=True,
120+
)
121+
metrics_thread.start()
122+
logger.info("Started metrics server on %s:%s", metrics_host, metrics_port)
92123

93124
tcp_app = FastAPI(title="Gitingest MCP Server", description="MCP server over HTTP/SSE")
94125

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /