MCP server source

This commit is contained in:
root
2026-07-15 17:53:14 -04:00
commit 7fd711a83f
3 changed files with 75 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY server.py ./
ENV PYTHONUNBUFFERED=1
EXPOSE 8903
CMD ["python3", "server.py"]
+2
View File
@@ -0,0 +1,2 @@
fastmcp>=3.0.0
httpx>=0.27.0
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""Git/Gitea MCP Server — Browse repos via Gitea API."""
from fastmcp import FastMCP
import httpx, os, base64
GITEA_URL = os.getenv('GITEA_URL', 'https://git.itpropartner.com')
API = f'{GITEA_URL}/api/v1'
mcp = FastMCP('Git/Gitea', version='1.0.0')
async def _get(path: str):
async with httpx.AsyncClient(verify=False, timeout=15) as c:
r = await c.get(f'{API}{path}')
r.raise_for_status()
return r.json()
@mcp.tool(description='List all public repos on git.itpropartner.com')
async def list_repos() -> str:
"""List all Gitea repos."""
repos = await _get('/repos/search?limit=50&q=&template=false')
if isinstance(repos, dict) and 'data' in repos:
repos = repos['data']
if not repos:
return 'No repos found. Gitea may need initial setup at https://git.itpropartner.com'
lines = [f'{len(repos)} repos:']
for r in repos[:30]:
lines.append(f' {r["full_name"]}{r.get("description","")} ({r.get("stars_count",0)}★)')
return '\n'.join(lines)
@mcp.tool(description='Search Gitea repos by name or description')
async def search_repos(query: str) -> str:
"""Search repos."""
repos = await _get(f'/repos/search?q={query}&limit=20')
if isinstance(repos, dict) and 'data' in repos:
repos = repos['data']
if not repos:
return f'No repos matching "{query}"'
lines = [f'Results for "{query}":']
for r in repos[:20]:
lines.append(f' {r["full_name"]}{r.get("description","")}')
return '\n'.join(lines)
@mcp.tool(description='Browse files and folders in a Gitea repo')
async def browse_repo(owner: str, repo: str, path: str = '') -> str:
"""List contents of a repo directory."""
contents = await _get(f'/repos/{owner}/{repo}/contents/{path}')
if not contents:
return f'Empty: {owner}/{repo}/{path}'
lines = [f'{owner}/{repo}/{path or "/"}:']
for item in contents[:50]:
typ = '[DIR]' if item['type'] == 'dir' else f'[{item["type"]}]'
lines.append(f' {typ} {item["name"]} ({item.get("size",0):,} bytes)')
return '\n'.join(lines)
@mcp.tool(description='Read a file from a Gitea repo')
async def read_file(owner: str, repo: str, path: str) -> str:
"""Read file contents."""
data = await _get(f'/repos/{owner}/{repo}/contents/{path}')
if 'content' in data:
content = base64.b64decode(data['content']).decode('utf-8', errors='replace')
return f'{owner}/{repo}/{path} ({data["size"]:,} bytes):\n\n{content[:10000]}'
return f'Could not read {owner}/{repo}/{path}'
if __name__ == '__main__':
mcp.run(transport='http', host='0.0.0.0', port=8903, path='/mcp')