Skip to content

Commit 4222c04

Browse files
committed
Cleanup
1 parent e3c57b4 commit 4222c04

File tree

6 files changed

+30
-295
lines changed

6 files changed

+30
-295
lines changed

.env.example

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
11
# GitHub Configuration
22
GITHUB_TOKEN=ghp_your_token_here
33
GITHUB_API_BASE_URL=https://api.github.com
4-
5-
# Server Configuration
6-
MCP_SERVER_PORT=7860
7-
LOG_LEVEL=INFO
8-
9-
# Docker BuildKit (enable for faster builds with cache mounts)
10-
DOCKER_BUILDKIT=1
11-
COMPOSE_DOCKER_CLI_BUILD=1

CLAUDE.md

Lines changed: 29 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -2,147 +2,57 @@
22

33
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44

5-
## Overview
5+
## Project Overview
66

7-
GitHub MCP Server is a Model Context Protocol (MCP) server that provides access to GitHub documentation via the GitHub API. It's built with FastMCP and runs as a stdio-based MCP server, designed to be integrated with Claude Desktop or other MCP clients.
7+
GitHub MCP Server - A Model Context Protocol (MCP) server that provides GitHub API access for fetching and searching documentation in `/doc` folders across GitHub organizations. Built with FastMCP.
88

99
## Development Commands
1010

11-
### Environment Setup
12-
1311
```bash
14-
# Create and activate virtual environment
12+
# Setup
1513
python3 -m venv .venv
16-
source .venv/bin/activate # On Windows: .venv\Scripts\activate
17-
18-
# Install dependencies
14+
source .venv/bin/activate
1915
pip install -r requirements.txt
16+
cp .env.example .env # Add GITHUB_TOKEN
2017

21-
# Configure environment
22-
cp .env.example .env
23-
# Edit .env and add your GITHUB_TOKEN
24-
```
25-
26-
### Running the Server
27-
28-
```bash
29-
# Run directly with Python (stdio mode for MCP)
18+
# Run locally (stdio transport for Claude Desktop)
3019
python main.py
3120

32-
# Run with Docker
33-
docker build -t github-mcp-server:local .
34-
docker run -i --rm -e GITHUB_TOKEN='your_token' github-mcp-server:local
35-
36-
# Run with docker-compose
37-
docker-compose build
38-
docker-compose up
21+
# Run with HTTP transport (for Hugging Face Spaces)
22+
# Set MCP_HTTP_TRANSPORT=true or modify main.py to use mcp.run(transport="http")
3923
```
4024

41-
### Docker Build Performance
42-
43-
The Dockerfile is optimized for fast rebuilds (10-20x faster):
44-
- Uses BuildKit for layer caching
45-
- Separates dependency installation from code copying
46-
- Enable BuildKit: `export DOCKER_BUILDKIT=1`
47-
4825
## Architecture
4926

50-
### Single-File Architecture
51-
52-
The entire server is implemented in `main.py` (~650 lines) with a clear functional organization:
27+
**Single-file MCP server** (`main.py`) using FastMCP framework:
5328

54-
1. **Configuration & Setup** (lines 1-37): Environment loading, logging, MCP initialization
55-
2. **Helper Functions** (lines 39-114): GitHub API headers, /doc folder detection, content type determination
56-
3. **Business Logic Functions** (lines 116-442): Core async functions that implement GitHub API interactions
57-
4. **MCP Tools Registration** (lines 444-569): Decorated wrappers that expose business logic as MCP tools
58-
5. **MCP Resources** (lines 571-638): URI-based resource handlers for documentation access
59-
6. **Main Entry Point** (lines 640-652): Server startup
29+
1. **MCP Tools** (decorated with `@mcp.tool()`):
30+
- `get_org_repos_tool` - List repos with `/doc` folder detection (uses Search API first, falls back to listing all)
31+
- `get_repo_docs_tool` - Get documentation files from repo's `/doc` folder
32+
- `get_file_content_tool` - Fetch and decode file content (handles base64)
33+
- `search_documentation_tool` - Search docs across org using GitHub Code Search API
6034

61-
### Key Design Patterns
35+
2. **MCP Resources** (decorated with `@mcp.resource()`):
36+
- `documentation://{org}/{repo}` - List docs
37+
- `content://{org}/{repo}/{path}` - Get file content
6238

63-
**Separation of Concerns**: Business logic functions (e.g., `get_org_repos()`) are separate from MCP tool decorators (e.g., `get_org_repos_tool()`). This allows the core logic to be testable independently of MCP.
39+
3. **Business Logic Functions** (async, testable):
40+
- `get_org_repos()`, `get_repo_docs()`, `get_file_content()`, `search_documentation()`
41+
- Each tool is a thin wrapper calling these functions
6442

65-
**Async/Await Throughout**: All GitHub API interactions use `aiohttp` for async HTTP requests. Functions reuse `ClientSession` objects for connection pooling.
43+
## Key Implementation Details
6644

67-
**Two-Strategy Approach**: `get_org_repos()` tries GitHub Search API first (efficient, one request), then falls back to listing all repos individually if search fails.
45+
- **GitHub API strategy**: Search API first for efficiency, fallback to paginated list + check each repo
46+
- **Supported doc types**: `.md`, `.mmd`, `.mermaid`, `.svg`, `.yml`, `.yaml`, `.json`
47+
- **Content decoding**: Base64 content from GitHub API is automatically decoded
48+
- **Health check**: `/health` endpoint available via `@mcp.custom_route()`
6849

69-
**Base64 Decoding**: GitHub API returns file content as base64-encoded strings. The `get_file_content()` function automatically decodes this to UTF-8 text.
50+
## Environment Variables
7051

71-
## MCP Integration
72-
73-
### Tools (4 total)
74-
75-
- `get_org_repos_tool(org)` - Lists repositories with /doc folder detection
76-
- `get_repo_docs_tool(org, repo)` - Lists documentation files in a repo's /doc folder
77-
- `get_file_content_tool(org, repo, path)` - Fetches and decodes file content
78-
- `search_documentation_tool(org, query)` - Searches docs across repos using GitHub Code Search API
79-
80-
### Resources (2 patterns)
81-
82-
- `documentation://{org}/{repo}` - Lists documentation files in formatted text
83-
- `content://{org}/{repo}/{path}` - Returns raw file content
84-
85-
### Environment Variables
86-
87-
Required:
88-
- `GITHUB_TOKEN` - GitHub personal access token (scopes: `repo`, `read:org`, `read:user`)
89-
90-
Optional:
52+
- `GITHUB_TOKEN` - Required for API access (scopes: `repo`, `read:org`, `read:user`)
9153
- `GITHUB_API_BASE_URL` - Default: `https://api.github.com`
9254
- `LOG_LEVEL` - Default: `INFO`
93-
- `MCP_SERVER_PORT` - Default: `8000` (note: not used in stdio mode)
94-
95-
## Supported File Types
96-
97-
The server filters for specific documentation file types in /doc folders:
98-
- Markdown: `.md`
99-
- Mermaid diagrams: `.mmd`, `.mermaid`
100-
- SVG images: `.svg`
101-
- OpenAPI specs: `.yml`, `.yaml`, `.json`
102-
- Postman collections: `.json` (filename must start with "postman")
103-
104-
## Error Handling
105-
106-
- 404 responses return empty lists or specific error messages
107-
- Rate limiting (403 from Search API) returns user-facing error message
108-
- All GitHub API errors include status code and response text
109-
- Missing GITHUB_TOKEN triggers warning but allows unauthenticated requests (with rate limits)
110-
111-
## Claude Desktop Configuration
112-
113-
Add to `claude_desktop_config.json`:
114-
115-
**Docker deployment:**
116-
```json
117-
{
118-
"mcpServers": {
119-
"github-docs": {
120-
"command": "docker",
121-
"args": ["run", "-i", "--rm", "-e", "GITHUB_TOKEN", "ghcr.io/sperekrestova/github-mcp-server:latest"],
122-
"env": {"GITHUB_TOKEN": "ghp_your_token_here"}
123-
}
124-
}
125-
}
126-
```
127-
128-
**Python deployment:**
129-
```json
130-
{
131-
"mcpServers": {
132-
"github-docs": {
133-
"command": "python3",
134-
"args": ["/absolute/path/to/main.py"],
135-
"env": {"GITHUB_TOKEN": "ghp_your_token_here"}
136-
}
137-
}
138-
}
139-
```
14055

141-
## Testing Approach
56+
## CI/CD
14257

143-
Currently no test suite exists. When adding tests:
144-
- Test business logic functions separately from MCP tool wrappers
145-
- Mock `aiohttp.ClientSession` for GitHub API interactions
146-
- Test base64 decoding edge cases in `get_file_content()`
147-
- Test /doc folder detection logic
148-
- Test file type filtering against supported extensions
58+
Docker image published to `ghcr.io/sperekrestova/github-mcp-server` on push to main or version tags. Multi-platform build (amd64/arm64).

Dockerfile

Lines changed: 0 additions & 17 deletions
This file was deleted.

docker-compose.yml

Lines changed: 0 additions & 17 deletions
This file was deleted.

main.py

Lines changed: 0 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@
1010
from typing import List, Dict, Any
1111

1212
import aiohttp
13-
import gradio as gr
1413
from fastmcp import FastMCP
1514
from starlette.requests import Request
1615
from starlette.responses import PlainTextResponse
1716

1817
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
19-
SERVER_PORT = int(os.getenv("SERVER_PORT", "8003"))
20-
SERVER_HOST = os.getenv("MCP_SERVER_HOST", "0.0.0.0")
2118
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN", "random")
2219
GITHUB_API_BASE = os.getenv("GITHUB_API_BASE_URL", "https://api.github.com")
2320

@@ -537,143 +534,15 @@ async def content_resource(org: str, repo: str, path: str) -> str:
537534
file_data = await get_file_content(org, repo, path)
538535
return file_data["content"]
539536

540-
# Create Gradio Interface
541-
def create_gradio_interface():
542-
"""Create Gradio interface to display MCP server information"""
543-
544-
async def get_server_info():
545-
"""Get server status and information"""
546-
# Get tools using await (Gradio supports async functions)
547-
tools_dict = await mcp.get_tools()
548-
tools_list = list(tools_dict.values())
549-
550-
# Build tools information
551-
tools_info = f"## 🛠️ Available MCP Tools ({len(tools_list)})\n\n"
552-
for tool in tools_list:
553-
tools_info += f"### {tool.name}\n"
554-
tools_info += f"{tool.description or 'No description available'}\n\n"
555-
556-
if hasattr(tool, 'parameters') and tool.parameters:
557-
if hasattr(tool.parameters, 'properties'):
558-
params = list(tool.parameters.properties.keys())
559-
tools_info += f"**Parameters:** {', '.join(f'`{p}`' for p in params)}\n\n"
560-
561-
return tools_info
562-
563-
async def check_mcp_status():
564-
"""Check if MCP endpoint is responding"""
565-
try:
566-
# Try to get tools to verify MCP server is working
567-
tools_dict = await mcp.get_tools()
568-
tool_count = len(tools_dict)
569-
return f"✅ MCP Server is running and responding\n{tool_count} tools available at `/mcp` endpoint"
570-
except Exception as e:
571-
return f"❌ MCP Server error: {str(e)}"
572-
573-
with gr.Blocks(title="GitHub MCP Server") as demo:
574-
demo.theme = gr.themes.Soft()
575-
gr.Markdown(
576-
"""
577-
# 🐙 GitHub MCP Server
578-
579-
Model Context Protocol server for accessing GitHub documentation via API.
580-
"""
581-
)
582-
583-
with gr.Tab("📡 MCP Endpoint"):
584-
gr.Markdown(
585-
"""
586-
### Connection Information
587-
588-
**Endpoint:** `/mcp`
589-
**Protocol:** MCP over HTTP (Streamable HTTP)
590-
**Status:** Active
591-
592-
### How to Connect
593-
594-
**Claude Desktop (Pro/Max/Team):**
595-
1. Open Settings → Connectors → Add Custom Integration
596-
2. Enter this Space's URL + `/mcp`
597-
3. Example: `https://your-username-space-name.hf.space/mcp`
598-
599-
**Claude Desktop (Free tier):**
600-
Use `mcp-remote` proxy in your `claude_desktop_config.json`:
601-
```json
602-
{
603-
"mcpServers": {
604-
"github-docs-remote": {
605-
"command": "npx",
606-
"args": ["-y", "mcp-remote", "https://your-space-url.hf.space/mcp"]
607-
}
608-
}
609-
}
610-
```
611-
"""
612-
)
613-
614-
status_btn = gr.Button("Check MCP Status", variant="primary")
615-
status_output = gr.Textbox(label="Status", interactive=False)
616-
status_btn.click(check_mcp_status, outputs=status_output)
617-
618-
with gr.Tab("🛠️ Available Tools"):
619-
gr.Markdown("View all available MCP tools and their parameters.")
620-
621-
tools_btn = gr.Button("Load Tools", variant="primary")
622-
tools_output = gr.Markdown()
623-
tools_btn.click(get_server_info, outputs=tools_output)
624-
625-
with gr.Tab("📚 Resources"):
626-
gr.Markdown(
627-
"""
628-
### MCP Resources
629-
630-
This server provides MCP resources for accessing documentation:
631-
632-
- `documentation://{org}/{repo}` - List documentation files in a repository
633-
- `content://{org}/{repo}/{path}` - Get content of a specific file
634-
635-
### Supported File Types
636-
637-
- Markdown (`.md`)
638-
- Mermaid diagrams (`.mmd`, `.mermaid`)
639-
- SVG images (`.svg`)
640-
- OpenAPI specs (`.yml`, `.yaml`, `.json`)
641-
- Postman collections (`.json`)
642-
"""
643-
)
644-
645-
with gr.Tab("ℹ️ About"):
646-
gr.Markdown(
647-
f"""
648-
### Server Information
649-
650-
**GitHub Token:** {'✅ Configured' if GITHUB_TOKEN else '❌ Not configured'}
651-
**Port:** {SERVER_PORT}
652-
**API Base:** {GITHUB_API_BASE}
653-
654-
### Links
655-
656-
- [FastMCP Documentation](https://github.com/jlowin/fastmcp)
657-
- [MCP Protocol Specification](https://modelcontextprotocol.io)
658-
- [Source Code](https://github.com/SPerekrestova/GitHub_MCP_Server)
659-
"""
660-
)
661-
662-
return demo
663-
664537
# Add health check endpoint
665538
@mcp.custom_route("/health", methods=["GET"])
666539
async def health_check(request: Request) -> PlainTextResponse:
667540
return PlainTextResponse("OK")
668541

669-
# Create Gradio blocks
670-
gradio_blocks = create_gradio_interface()
671-
672542

673543
# ============================================================================
674544
# Main Entry Point
675545
# ============================================================================
676546

677547
if __name__ == "__main__":
678-
#gradio_blocks.launch(share=True)
679548
mcp.run()

requirements.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,4 @@ aiohttp>=3.13.3
33
python-dotenv>=1.0.0
44
pydantic>=2.0.0
55
uvicorn[standard]>=0.27.0
6-
gradio>=4.0.0
7-
starlette
8-
fastapi
6+
starlette

0 commit comments

Comments
 (0)