add asgi support
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
# Yargı MCP Server Environment Configuration
|
||||||
|
# Copy this file to .env and customize as needed
|
||||||
|
|
||||||
|
# Server Configuration
|
||||||
|
HOST=0.0.0.0
|
||||||
|
PORT=8000
|
||||||
|
LOG_LEVEL=info
|
||||||
|
|
||||||
|
# CORS Configuration
|
||||||
|
# Comma-separated list of allowed origins
|
||||||
|
# Use * to allow all origins (not recommended for production)
|
||||||
|
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080,https://yourdomain.com
|
||||||
|
|
||||||
|
# Authentication (optional)
|
||||||
|
# Uncomment and set to enable token-based authentication
|
||||||
|
# API_TOKEN=your-secret-token-here
|
||||||
|
|
||||||
|
# Worker Configuration
|
||||||
|
# Number of worker processes (for production)
|
||||||
|
# WORKERS=4
|
||||||
|
|
||||||
|
# SSL Configuration (optional)
|
||||||
|
# SSL_CERT_FILE=/path/to/cert.pem
|
||||||
|
# SSL_KEY_FILE=/path/to/key.pem
|
||||||
|
|
||||||
|
# Database Timeouts (seconds)
|
||||||
|
# Adjust based on your network conditions
|
||||||
|
YARGITAY_TIMEOUT=60
|
||||||
|
DANISTAY_TIMEOUT=60
|
||||||
|
BEDESTEN_TIMEOUT=60
|
||||||
|
ANAYASA_TIMEOUT=90
|
||||||
|
KIK_TIMEOUT=45
|
||||||
|
REKABET_TIMEOUT=45
|
||||||
|
UYUSMAZLIK_TIMEOUT=30
|
||||||
|
EMSAL_TIMEOUT=60
|
||||||
|
|
||||||
|
# Development Settings
|
||||||
|
# Enable debug mode (not for production)
|
||||||
|
# DEBUG=false
|
||||||
|
|
||||||
|
# Monitoring (optional)
|
||||||
|
# Sentry DSN for error tracking
|
||||||
|
# SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id
|
||||||
|
|
||||||
|
# OpenTelemetry Configuration (optional)
|
||||||
|
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
|
||||||
|
# OTEL_SERVICE_NAME=yargi-mcp-server
|
||||||
+12
@@ -168,3 +168,15 @@ fast-mcp-docs.md
|
|||||||
debug_*
|
debug_*
|
||||||
test_*
|
test_*
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
|
|
||||||
|
# ASGI/Deployment files
|
||||||
|
ssl/
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
*.crt
|
||||||
|
|
||||||
|
# Docker volumes
|
||||||
|
redis-data/
|
||||||
|
|
||||||
|
# Production logs
|
||||||
|
logs/*.log.*
|
||||||
|
|||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
# Multi-stage Dockerfile for Yargı MCP Server
|
||||||
|
|
||||||
|
# Build stage
|
||||||
|
FROM python:3.12-slim as builder
|
||||||
|
|
||||||
|
# Install build dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
gcc \
|
||||||
|
g++ \
|
||||||
|
git \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy requirements first for better caching
|
||||||
|
COPY pyproject.toml ./
|
||||||
|
COPY README.md ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN pip install --no-cache-dir uv && \
|
||||||
|
uv pip install --system --no-cache-dir .
|
||||||
|
|
||||||
|
# Runtime stage
|
||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
# Install runtime dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
# Required for Playwright
|
||||||
|
libnss3 \
|
||||||
|
libnspr4 \
|
||||||
|
libatk1.0-0 \
|
||||||
|
libatk-bridge2.0-0 \
|
||||||
|
libcups2 \
|
||||||
|
libdrm2 \
|
||||||
|
libdbus-1-3 \
|
||||||
|
libatspi2.0-0 \
|
||||||
|
libx11-6 \
|
||||||
|
libxcomposite1 \
|
||||||
|
libxdamage1 \
|
||||||
|
libxext6 \
|
||||||
|
libxfixes3 \
|
||||||
|
libxrandr2 \
|
||||||
|
libgbm1 \
|
||||||
|
libxcb1 \
|
||||||
|
libxkbcommon0 \
|
||||||
|
libpango-1.0-0 \
|
||||||
|
libcairo2 \
|
||||||
|
libasound2 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN useradd -m -u 1000 mcp && \
|
||||||
|
mkdir -p /app && \
|
||||||
|
chown -R mcp:mcp /app
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy from builder
|
||||||
|
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
|
||||||
|
COPY --from=builder /usr/local/bin /usr/local/bin
|
||||||
|
|
||||||
|
# Copy application code
|
||||||
|
COPY --chown=mcp:mcp . .
|
||||||
|
|
||||||
|
# Install Playwright browsers
|
||||||
|
RUN playwright install chromium
|
||||||
|
|
||||||
|
# Switch to non-root user
|
||||||
|
USER mcp
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
ENV HOST=0.0.0.0
|
||||||
|
ENV PORT=8000
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
|
||||||
|
CMD python -c "import httpx; httpx.get('http://localhost:8000/health').raise_for_status()"
|
||||||
|
|
||||||
|
# Run the ASGI server
|
||||||
|
CMD ["uvicorn", "asgi_app:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
@@ -166,6 +166,32 @@ Bedesten API Bedesten API Dual/Triple API Norm+Bireysel API
|
|||||||
- Kesin arama: `"\"mülkiyet kararı\""` (tam cümle olarak)
|
- Kesin arama: `"\"mülkiyet kararı\""` (tam cümle olarak)
|
||||||
- Daha kesin sonuçlar için hukuki terimler ve kavramlar
|
- Daha kesin sonuçlar için hukuki terimler ve kavramlar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
🌐 **Web Service / ASGI Deployment**
|
||||||
|
|
||||||
|
Yargı MCP artık web servisi olarak da çalıştırılabilir! ASGI desteği sayesinde:
|
||||||
|
|
||||||
|
- **Web API olarak erişim**: HTTP endpoint'leri üzerinden MCP araçlarına erişim
|
||||||
|
- **Cloud deployment**: Heroku, Railway, Google Cloud Run, AWS Lambda desteği
|
||||||
|
- **Docker desteği**: Production-ready Docker container
|
||||||
|
- **FastAPI entegrasyonu**: REST API ve interaktif dokümantasyon
|
||||||
|
|
||||||
|
**Hızlı başlangıç:**
|
||||||
|
```bash
|
||||||
|
# ASGI dependencies yükle
|
||||||
|
pip install yargi-mcp[asgi]
|
||||||
|
|
||||||
|
# Web servisi olarak başlat
|
||||||
|
python run_asgi.py
|
||||||
|
# veya
|
||||||
|
uvicorn asgi_app:app --host 0.0.0.0 --port 8000
|
||||||
|
```
|
||||||
|
|
||||||
|
Detaylı deployment rehberi için: [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
📜 **Lisans**
|
📜 **Lisans**
|
||||||
|
|
||||||
Bu proje MIT Lisansı altında lisanslanmıştır. Detaylar için `LICENSE` dosyasına bakınız.
|
Bu proje MIT Lisansı altında lisanslanmıştır. Detaylar için `LICENSE` dosyasına bakınız.
|
||||||
|
|||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
"""
|
||||||
|
ASGI application for Yargı MCP Server
|
||||||
|
|
||||||
|
This module provides ASGI/HTTP access to the Yargı MCP server,
|
||||||
|
allowing it to be deployed as a web service.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
uvicorn asgi_app:app --host 0.0.0.0 --port 8000
|
||||||
|
|
||||||
|
Or with custom transport:
|
||||||
|
uvicorn asgi_app:sse_app --host 0.0.0.0 --port 8000
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from starlette.middleware import Middleware
|
||||||
|
from starlette.middleware.cors import CORSMiddleware
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import JSONResponse, PlainTextResponse
|
||||||
|
|
||||||
|
# Import the main MCP app
|
||||||
|
from mcp_server_main import app as mcp_server
|
||||||
|
|
||||||
|
# Add a health check endpoint
|
||||||
|
@mcp_server.custom_route("/health", methods=["GET"])
|
||||||
|
async def health_check(request: Request) -> JSONResponse:
|
||||||
|
"""Health check endpoint for monitoring"""
|
||||||
|
return JSONResponse({
|
||||||
|
"status": "healthy",
|
||||||
|
"service": "Yargı MCP Server",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"tools_count": len(mcp_server._tool_manager.tools)
|
||||||
|
})
|
||||||
|
|
||||||
|
@mcp_server.custom_route("/", methods=["GET"])
|
||||||
|
async def root(request: Request) -> JSONResponse:
|
||||||
|
"""Root endpoint with service information"""
|
||||||
|
return JSONResponse({
|
||||||
|
"service": "Yargı MCP Server",
|
||||||
|
"description": "MCP server for Turkish legal databases",
|
||||||
|
"endpoints": {
|
||||||
|
"mcp": "/mcp/",
|
||||||
|
"health": "/health",
|
||||||
|
"status": "/status"
|
||||||
|
},
|
||||||
|
"supported_databases": [
|
||||||
|
"Yargıtay (Court of Cassation)",
|
||||||
|
"Danıştay (Council of State)",
|
||||||
|
"Emsal (Precedent)",
|
||||||
|
"Uyuşmazlık Mahkemesi (Court of Jurisdictional Disputes)",
|
||||||
|
"Anayasa Mahkemesi (Constitutional Court)",
|
||||||
|
"Kamu İhale Kurulu (Public Procurement Authority)",
|
||||||
|
"Rekabet Kurumu (Competition Authority)",
|
||||||
|
"Bedesten API (Multiple courts)"
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
@mcp_server.custom_route("/status", methods=["GET"])
|
||||||
|
async def status(request: Request) -> JSONResponse:
|
||||||
|
"""Status endpoint with detailed information"""
|
||||||
|
tools = []
|
||||||
|
for tool in mcp_server._tool_manager.tools.values():
|
||||||
|
tools.append({
|
||||||
|
"name": tool.name,
|
||||||
|
"description": tool.description[:100] + "..." if len(tool.description) > 100 else tool.description
|
||||||
|
})
|
||||||
|
|
||||||
|
return JSONResponse({
|
||||||
|
"status": "operational",
|
||||||
|
"tools": tools,
|
||||||
|
"total_tools": len(tools),
|
||||||
|
"transport": "streamable_http"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Configure CORS middleware
|
||||||
|
cors_origins = os.getenv("ALLOWED_ORIGINS", "*").split(",")
|
||||||
|
custom_middleware = [
|
||||||
|
Middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=cors_origins,
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["GET", "POST", "OPTIONS"],
|
||||||
|
allow_headers=["Content-Type", "Authorization", "X-Request-ID"],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Create ASGI apps with different transports
|
||||||
|
|
||||||
|
# Recommended: Streamable HTTP transport
|
||||||
|
app = mcp_server.http_app(
|
||||||
|
path="/mcp",
|
||||||
|
middleware=custom_middleware
|
||||||
|
)
|
||||||
|
|
||||||
|
# Alternative: SSE transport (for compatibility)
|
||||||
|
sse_app = mcp_server.http_app(
|
||||||
|
path="/sse",
|
||||||
|
transport="sse",
|
||||||
|
middleware=custom_middleware
|
||||||
|
)
|
||||||
|
|
||||||
|
# Export for uvicorn
|
||||||
|
__all__ = ["app", "sse_app"]
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
yargi-mcp:
|
||||||
|
build: .
|
||||||
|
image: yargi-mcp:latest
|
||||||
|
container_name: yargi-mcp-server
|
||||||
|
ports:
|
||||||
|
- "${PORT:-8000}:8000"
|
||||||
|
environment:
|
||||||
|
- HOST=0.0.0.0
|
||||||
|
- PORT=8000
|
||||||
|
- LOG_LEVEL=${LOG_LEVEL:-info}
|
||||||
|
- ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-*}
|
||||||
|
- API_TOKEN=${API_TOKEN:-}
|
||||||
|
- PYTHONUNBUFFERED=1
|
||||||
|
volumes:
|
||||||
|
# Mount logs directory
|
||||||
|
- ./logs:/app/logs
|
||||||
|
# Mount .env file if it exists
|
||||||
|
- ./.env:/app/.env:ro
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "python", "-c", "import httpx; httpx.get('http://localhost:8000/health').raise_for_status()"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
|
networks:
|
||||||
|
- yargi-network
|
||||||
|
|
||||||
|
# Optional: Nginx reverse proxy
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: yargi-nginx
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
volumes:
|
||||||
|
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
- ./ssl:/etc/nginx/ssl:ro
|
||||||
|
depends_on:
|
||||||
|
- yargi-mcp
|
||||||
|
networks:
|
||||||
|
- yargi-network
|
||||||
|
profiles:
|
||||||
|
- production
|
||||||
|
|
||||||
|
# Optional: Redis for caching (future enhancement)
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
container_name: yargi-redis
|
||||||
|
command: redis-server --appendonly yes
|
||||||
|
volumes:
|
||||||
|
- redis-data:/data
|
||||||
|
networks:
|
||||||
|
- yargi-network
|
||||||
|
profiles:
|
||||||
|
- with-cache
|
||||||
|
|
||||||
|
networks:
|
||||||
|
yargi-network:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
redis-data:
|
||||||
@@ -0,0 +1,428 @@
|
|||||||
|
# Yargı MCP Server Dağıtım Rehberi
|
||||||
|
|
||||||
|
Bu rehber, Yargı MCP Server'ın ASGI web servisi olarak çeşitli dağıtım seçeneklerini kapsar.
|
||||||
|
|
||||||
|
## İçindekiler
|
||||||
|
|
||||||
|
- [Hızlı Başlangıç](#hızlı-başlangıç)
|
||||||
|
- [Yerel Geliştirme](#yerel-geliştirme)
|
||||||
|
- [Production Dağıtımı](#production-dağıtımı)
|
||||||
|
- [Cloud Dağıtımı](#cloud-dağıtımı)
|
||||||
|
- [Docker Dağıtımı](#docker-dağıtımı)
|
||||||
|
- [Güvenlik Hususları](#güvenlik-hususları)
|
||||||
|
- [İzleme](#izleme)
|
||||||
|
|
||||||
|
## Hızlı Başlangıç
|
||||||
|
|
||||||
|
### 1. Bağımlılıkları Yükleyin
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ASGI sunucusu için uvicorn yükleyin
|
||||||
|
pip install uvicorn
|
||||||
|
|
||||||
|
# Veya tüm bağımlılıklarla birlikte yükleyin
|
||||||
|
pip install -e .
|
||||||
|
pip install uvicorn
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Sunucuyu Çalıştırın
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Temel başlatma
|
||||||
|
python run_asgi.py
|
||||||
|
|
||||||
|
# Veya doğrudan uvicorn ile
|
||||||
|
uvicorn asgi_app:app --host 0.0.0.0 --port 8000
|
||||||
|
```
|
||||||
|
|
||||||
|
Sunucu şu adreslerde kullanılabilir olacak:
|
||||||
|
- MCP Endpoint: `http://localhost:8000/mcp/`
|
||||||
|
- Sağlık Kontrolü: `http://localhost:8000/health`
|
||||||
|
- API Durumu: `http://localhost:8000/status`
|
||||||
|
|
||||||
|
## Yerel Geliştirme
|
||||||
|
|
||||||
|
### Otomatik Yeniden Yükleme ile Geliştirme Sunucusu
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python run_asgi.py --reload --log-level debug
|
||||||
|
```
|
||||||
|
|
||||||
|
### FastAPI Entegrasyonunu Kullanma
|
||||||
|
|
||||||
|
Ek REST API endpoint'leri için:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn fastapi_app:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
Bu şunları sağlar:
|
||||||
|
- `/docs` adresinde interaktif API dokümantasyonu
|
||||||
|
- `/api/tools` adresinde araç listesi
|
||||||
|
- `/api/databases` adresinde veritabanı bilgileri
|
||||||
|
|
||||||
|
### Ortam Değişkenleri
|
||||||
|
|
||||||
|
`.env.example` dosyasını temel alarak bir `.env` dosyası oluşturun:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Temel değişkenler:
|
||||||
|
- `HOST`: Sunucu host adresi (varsayılan: 127.0.0.1)
|
||||||
|
- `PORT`: Sunucu portu (varsayılan: 8000)
|
||||||
|
- `ALLOWED_ORIGINS`: CORS kökenleri (virgülle ayrılmış)
|
||||||
|
- `LOG_LEVEL`: Log seviyesi (debug, info, warning, error)
|
||||||
|
|
||||||
|
## Production Dağıtımı
|
||||||
|
|
||||||
|
### 1. Uvicorn ile Çoklu Worker Kullanımı
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python run_asgi.py --host 0.0.0.0 --port 8000 --workers 4
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Gunicorn Kullanımı
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install gunicorn
|
||||||
|
gunicorn asgi_app:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Nginx Reverse Proxy ile
|
||||||
|
|
||||||
|
1. Nginx'i yükleyin
|
||||||
|
2. Sağlanan `nginx.conf` dosyasını kullanın:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp nginx.conf /etc/nginx/sites-available/yargi-mcp
|
||||||
|
sudo ln -s /etc/nginx/sites-available/yargi-mcp /etc/nginx/sites-enabled/
|
||||||
|
sudo nginx -t
|
||||||
|
sudo systemctl reload nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Systemd Servisi
|
||||||
|
|
||||||
|
`/etc/systemd/system/yargi-mcp.service` dosyasını oluşturun:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=Yargı MCP Server
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=exec
|
||||||
|
User=www-data
|
||||||
|
WorkingDirectory=/opt/yargi-mcp
|
||||||
|
Environment="PATH=/opt/yargi-mcp/venv/bin"
|
||||||
|
ExecStart=/opt/yargi-mcp/venv/bin/uvicorn asgi_app:app --host 0.0.0.0 --port 8000 --workers 4
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Etkinleştirin ve başlatın:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable yargi-mcp
|
||||||
|
sudo systemctl start yargi-mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cloud Dağıtımı
|
||||||
|
|
||||||
|
### Heroku
|
||||||
|
|
||||||
|
1. `Procfile` oluşturun:
|
||||||
|
```
|
||||||
|
web: uvicorn asgi_app:app --host 0.0.0.0 --port $PORT
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Dağıtın:
|
||||||
|
```bash
|
||||||
|
heroku create uygulama-isminiz
|
||||||
|
git push heroku main
|
||||||
|
```
|
||||||
|
|
||||||
|
### Railway
|
||||||
|
|
||||||
|
1. `railway.json` ekleyin:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"builder": "NIXPACKS"
|
||||||
|
},
|
||||||
|
"deploy": {
|
||||||
|
"startCommand": "uvicorn asgi_app:app --host 0.0.0.0 --port $PORT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Railway CLI veya GitHub entegrasyonu ile dağıtın
|
||||||
|
|
||||||
|
### Google Cloud Run
|
||||||
|
|
||||||
|
1. Container oluşturun:
|
||||||
|
```bash
|
||||||
|
docker build -t yargi-mcp .
|
||||||
|
docker tag yargi-mcp gcr.io/PROJE_ADINIZ/yargi-mcp
|
||||||
|
docker push gcr.io/PROJE_ADINIZ/yargi-mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Dağıtın:
|
||||||
|
```bash
|
||||||
|
gcloud run deploy yargi-mcp \
|
||||||
|
--image gcr.io/PROJE_ADINIZ/yargi-mcp \
|
||||||
|
--platform managed \
|
||||||
|
--region us-central1 \
|
||||||
|
--allow-unauthenticated
|
||||||
|
```
|
||||||
|
|
||||||
|
### AWS Lambda (Mangum kullanarak)
|
||||||
|
|
||||||
|
1. Mangum'u yükleyin:
|
||||||
|
```bash
|
||||||
|
pip install mangum
|
||||||
|
```
|
||||||
|
|
||||||
|
2. `lambda_handler.py` oluşturun:
|
||||||
|
```python
|
||||||
|
from mangum import Mangum
|
||||||
|
from asgi_app import app
|
||||||
|
|
||||||
|
handler = Mangum(app, lifespan="off")
|
||||||
|
```
|
||||||
|
|
||||||
|
3. AWS SAM veya Serverless Framework kullanarak dağıtın
|
||||||
|
|
||||||
|
## Docker Dağıtımı
|
||||||
|
|
||||||
|
### Tek Container
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Oluşturun
|
||||||
|
docker build -t yargi-mcp .
|
||||||
|
|
||||||
|
# Çalıştırın
|
||||||
|
docker run -p 8000:8000 --env-file .env yargi-mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Compose
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Geliştirme
|
||||||
|
docker-compose up
|
||||||
|
|
||||||
|
# Nginx ile Production
|
||||||
|
docker-compose --profile production up
|
||||||
|
|
||||||
|
# Redis önbellekleme ile
|
||||||
|
docker-compose --profile with-cache up
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kubernetes
|
||||||
|
|
||||||
|
Deployment YAML oluşturun:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: yargi-mcp
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: yargi-mcp
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: yargi-mcp
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: yargi-mcp
|
||||||
|
image: yargi-mcp:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8000
|
||||||
|
env:
|
||||||
|
- name: HOST
|
||||||
|
value: "0.0.0.0"
|
||||||
|
- name: PORT
|
||||||
|
value: "8000"
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 8000
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 30
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: yargi-mcp-service
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: yargi-mcp
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 8000
|
||||||
|
type: LoadBalancer
|
||||||
|
```
|
||||||
|
|
||||||
|
## Güvenlik Hususları
|
||||||
|
|
||||||
|
### 1. Kimlik Doğrulama
|
||||||
|
|
||||||
|
`API_TOKEN` ortam değişkenini ayarlayarak token kimlik doğrulamasını etkinleştirin:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export API_TOKEN=gizli-token-degeri
|
||||||
|
```
|
||||||
|
|
||||||
|
Ardından isteklere ekleyin:
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer gizli-token-degeri" http://localhost:8000/api/tools
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. HTTPS/SSL
|
||||||
|
|
||||||
|
Production için her zaman HTTPS kullanın:
|
||||||
|
|
||||||
|
1. SSL sertifikası edinin (Let's Encrypt vb.)
|
||||||
|
2. Nginx veya cloud sağlayıcıda yapılandırın
|
||||||
|
3. `ALLOWED_ORIGINS` değerini https:// kullanacak şekilde güncelleyin
|
||||||
|
|
||||||
|
### 3. Rate Limiting (Hız Sınırlama)
|
||||||
|
|
||||||
|
Sağlanan Nginx yapılandırması rate limiting içerir:
|
||||||
|
- API endpoint'leri: 10 istek/saniye
|
||||||
|
- MCP endpoint: 100 istek/saniye
|
||||||
|
|
||||||
|
### 4. CORS Yapılandırması
|
||||||
|
|
||||||
|
Production için belirli kaynaklara izin verin:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ALLOWED_ORIGINS=https://app.sizindomain.com,https://www.sizindomain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
## İzleme
|
||||||
|
|
||||||
|
### Sağlık Kontrolleri
|
||||||
|
|
||||||
|
`/health` endpoint'ini izleyin:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Yanıt:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "healthy",
|
||||||
|
"timestamp": "2024-12-26T10:00:00",
|
||||||
|
"uptime_seconds": 3600,
|
||||||
|
"tools_operational": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Loglama
|
||||||
|
|
||||||
|
Ortam değişkeni ile log seviyesini yapılandırın:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
LOG_LEVEL=info # veya debug, warning, error
|
||||||
|
```
|
||||||
|
|
||||||
|
Loglar şuraya yazılır:
|
||||||
|
- Konsol (stdout)
|
||||||
|
- `logs/mcp_server.log` dosyası
|
||||||
|
|
||||||
|
### Metrikler (Opsiyonel)
|
||||||
|
|
||||||
|
OpenTelemetry desteği için:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install opentelemetry-instrumentation-fastapi
|
||||||
|
```
|
||||||
|
|
||||||
|
Ortam değişkenlerini ayarlayın:
|
||||||
|
```bash
|
||||||
|
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
|
||||||
|
OTEL_SERVICE_NAME=yargi-mcp-server
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sorun Giderme
|
||||||
|
|
||||||
|
### Port Zaten Kullanımda
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 8000 portunu kullanan işlemi bulun
|
||||||
|
lsof -i :8000
|
||||||
|
|
||||||
|
# İşlemi sonlandırın
|
||||||
|
kill -9 <PID>
|
||||||
|
```
|
||||||
|
|
||||||
|
### İzin Hataları
|
||||||
|
|
||||||
|
Dosya izinlerinin doğru olduğundan emin olun:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x run_asgi.py
|
||||||
|
chown -R www-data:www-data /opt/yargi-mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bellek Sorunları
|
||||||
|
|
||||||
|
Büyük belge işleme için worker belleğini artırın:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# systemd servisinde
|
||||||
|
Environment="PYTHONMALLOC=malloc"
|
||||||
|
LimitNOFILE=65536
|
||||||
|
```
|
||||||
|
|
||||||
|
### Zaman Aşımı Sorunları
|
||||||
|
|
||||||
|
Zaman aşımlarını ayarlayın:
|
||||||
|
1. Uvicorn: `--timeout-keep-alive 75`
|
||||||
|
2. Nginx: `proxy_read_timeout 300s;`
|
||||||
|
3. Cloud sağlayıcılar: Platform özel zaman aşımı ayarlarını kontrol edin
|
||||||
|
|
||||||
|
## Performans Ayarlama
|
||||||
|
|
||||||
|
### 1. Worker İşlemleri
|
||||||
|
|
||||||
|
- Geliştirme: 1 worker
|
||||||
|
- Production: CPU çekirdeği başına 2-4 worker
|
||||||
|
|
||||||
|
### 2. Bağlantı Havuzlama
|
||||||
|
|
||||||
|
Sunucu varsayılan olarak httpx ile bağlantı havuzlama kullanır.
|
||||||
|
|
||||||
|
### 3. Önbellekleme (Gelecek Geliştirme)
|
||||||
|
|
||||||
|
Redis önbellekleme docker-compose ile etkinleştirilebilir:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose --profile with-cache up
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Veritabanı Zaman Aşımları
|
||||||
|
|
||||||
|
`.env` dosyasında veritabanı başına zaman aşımlarını ayarlayın:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
YARGITAY_TIMEOUT=60
|
||||||
|
DANISTAY_TIMEOUT=60
|
||||||
|
ANAYASA_TIMEOUT=90
|
||||||
|
```
|
||||||
|
|
||||||
|
## Destek
|
||||||
|
|
||||||
|
Sorunlar ve sorular için:
|
||||||
|
- GitHub Issues: https://github.com/saidsurucu/yargi-mcp/issues
|
||||||
|
- Dokümantasyon: README.md dosyasına bakın
|
||||||
+274
@@ -0,0 +1,274 @@
|
|||||||
|
"""
|
||||||
|
FastAPI integration for Yargı MCP Server
|
||||||
|
|
||||||
|
This module demonstrates how to integrate the Yargı MCP server
|
||||||
|
with a FastAPI application, providing additional REST API endpoints
|
||||||
|
alongside the MCP functionality.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
uvicorn fastapi_app:app --host 0.0.0.0 --port 8000
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from typing import List, Dict, Any, Optional
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from fastapi import FastAPI, HTTPException, Query, Depends
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
# Import the main MCP app
|
||||||
|
from mcp_server_main import app as mcp_server
|
||||||
|
from asgi_app import custom_middleware
|
||||||
|
|
||||||
|
# Create MCP ASGI app
|
||||||
|
mcp_asgi_app = mcp_server.http_app(path="/mcp")
|
||||||
|
|
||||||
|
# Create FastAPI app with MCP lifespan
|
||||||
|
app = FastAPI(
|
||||||
|
title="Yargı MCP API",
|
||||||
|
description="Turkish Legal Database MCP Server with REST API",
|
||||||
|
version="0.1.0",
|
||||||
|
lifespan=mcp_asgi_app.lifespan
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add CORS middleware
|
||||||
|
cors_origins = os.getenv("ALLOWED_ORIGINS", "*").split(",")
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=cors_origins,
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mount MCP server
|
||||||
|
app.mount("/mcp-server", mcp_asgi_app)
|
||||||
|
|
||||||
|
# Response models
|
||||||
|
class ToolInfo(BaseModel):
|
||||||
|
name: str
|
||||||
|
description: str
|
||||||
|
parameters: Dict[str, Any]
|
||||||
|
|
||||||
|
class ServerInfo(BaseModel):
|
||||||
|
name: str
|
||||||
|
version: str
|
||||||
|
description: str
|
||||||
|
tools_count: int
|
||||||
|
databases: List[str]
|
||||||
|
mcp_endpoint: str
|
||||||
|
api_docs: str
|
||||||
|
|
||||||
|
class HealthCheck(BaseModel):
|
||||||
|
status: str
|
||||||
|
timestamp: datetime
|
||||||
|
uptime_seconds: Optional[float] = None
|
||||||
|
tools_operational: bool
|
||||||
|
|
||||||
|
# Track server start time
|
||||||
|
SERVER_START_TIME = datetime.now()
|
||||||
|
|
||||||
|
@app.get("/", response_model=ServerInfo)
|
||||||
|
async def root():
|
||||||
|
"""Get server information"""
|
||||||
|
return ServerInfo(
|
||||||
|
name="Yargı MCP Server",
|
||||||
|
version="0.1.0",
|
||||||
|
description="MCP server for Turkish legal databases",
|
||||||
|
tools_count=len(mcp_server._tool_manager.tools),
|
||||||
|
databases=[
|
||||||
|
"Yargıtay (Court of Cassation)",
|
||||||
|
"Danıştay (Council of State)",
|
||||||
|
"Emsal (Precedent)",
|
||||||
|
"Uyuşmazlık Mahkemesi (Court of Jurisdictional Disputes)",
|
||||||
|
"Anayasa Mahkemesi (Constitutional Court)",
|
||||||
|
"Kamu İhale Kurulu (Public Procurement Authority)",
|
||||||
|
"Rekabet Kurumu (Competition Authority)",
|
||||||
|
"Bedesten API (Multiple courts)"
|
||||||
|
],
|
||||||
|
mcp_endpoint="/mcp-server/mcp/",
|
||||||
|
api_docs="/docs"
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.get("/health", response_model=HealthCheck)
|
||||||
|
async def health_check():
|
||||||
|
"""Health check endpoint"""
|
||||||
|
uptime = (datetime.now() - SERVER_START_TIME).total_seconds()
|
||||||
|
|
||||||
|
return HealthCheck(
|
||||||
|
status="healthy",
|
||||||
|
timestamp=datetime.now(),
|
||||||
|
uptime_seconds=uptime,
|
||||||
|
tools_operational=len(mcp_server._tool_manager.tools) > 0
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.get("/api/tools", response_model=List[ToolInfo])
|
||||||
|
async def list_tools(
|
||||||
|
search: Optional[str] = Query(None, description="Search tools by name or description"),
|
||||||
|
database: Optional[str] = Query(None, description="Filter by database name")
|
||||||
|
):
|
||||||
|
"""List all available MCP tools"""
|
||||||
|
tools = []
|
||||||
|
|
||||||
|
for tool in mcp_server._tool_manager.tools.values():
|
||||||
|
# Apply filters if provided
|
||||||
|
if search and search.lower() not in tool.name.lower() and search.lower() not in tool.description.lower():
|
||||||
|
continue
|
||||||
|
|
||||||
|
if database:
|
||||||
|
db_lower = database.lower()
|
||||||
|
if db_lower not in tool.name.lower() and db_lower not in tool.description.lower():
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Extract parameter schema
|
||||||
|
params = {}
|
||||||
|
if hasattr(tool, 'schema') and tool.schema:
|
||||||
|
if hasattr(tool.schema, 'parameters'):
|
||||||
|
params = tool.schema.parameters
|
||||||
|
elif hasattr(tool.schema, '__annotations__'):
|
||||||
|
params = {k: str(v) for k, v in tool.schema.__annotations__.items()}
|
||||||
|
|
||||||
|
tools.append(ToolInfo(
|
||||||
|
name=tool.name,
|
||||||
|
description=tool.description,
|
||||||
|
parameters=params
|
||||||
|
))
|
||||||
|
|
||||||
|
return tools
|
||||||
|
|
||||||
|
@app.get("/api/tools/{tool_name}", response_model=ToolInfo)
|
||||||
|
async def get_tool(tool_name: str):
|
||||||
|
"""Get detailed information about a specific tool"""
|
||||||
|
tool = mcp_server._tool_manager.tools.get(tool_name)
|
||||||
|
|
||||||
|
if not tool:
|
||||||
|
raise HTTPException(status_code=404, detail=f"Tool '{tool_name}' not found")
|
||||||
|
|
||||||
|
# Extract parameter schema
|
||||||
|
params = {}
|
||||||
|
if hasattr(tool, 'schema') and tool.schema:
|
||||||
|
if hasattr(tool.schema, 'parameters'):
|
||||||
|
params = tool.schema.parameters
|
||||||
|
elif hasattr(tool.schema, '__annotations__'):
|
||||||
|
params = {k: str(v) for k, v in tool.schema.__annotations__.items()}
|
||||||
|
|
||||||
|
return ToolInfo(
|
||||||
|
name=tool.name,
|
||||||
|
description=tool.description,
|
||||||
|
parameters=params
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.get("/api/databases")
|
||||||
|
async def list_databases():
|
||||||
|
"""List all supported legal databases"""
|
||||||
|
databases = {
|
||||||
|
"yargitay": {
|
||||||
|
"name": "Yargıtay (Court of Cassation)",
|
||||||
|
"description": "Supreme court for civil and criminal cases",
|
||||||
|
"tools": ["search_yargitay_detailed", "get_yargitay_document_markdown",
|
||||||
|
"search_yargitay_bedesten", "get_yargitay_bedesten_document_markdown"],
|
||||||
|
"chambers": 52
|
||||||
|
},
|
||||||
|
"danistay": {
|
||||||
|
"name": "Danıştay (Council of State)",
|
||||||
|
"description": "Supreme administrative court",
|
||||||
|
"tools": ["search_danistay_by_keyword", "search_danistay_detailed",
|
||||||
|
"get_danistay_document_markdown", "search_danistay_bedesten",
|
||||||
|
"get_danistay_bedesten_document_markdown"],
|
||||||
|
"chambers": 27
|
||||||
|
},
|
||||||
|
"emsal": {
|
||||||
|
"name": "Emsal (Precedent)",
|
||||||
|
"description": "Precedent decisions from various courts",
|
||||||
|
"tools": ["search_emsal_detailed_decisions", "get_emsal_document_markdown"]
|
||||||
|
},
|
||||||
|
"uyusmazlik": {
|
||||||
|
"name": "Uyuşmazlık Mahkemesi",
|
||||||
|
"description": "Court of Jurisdictional Disputes",
|
||||||
|
"tools": ["search_uyusmazlik_decisions", "get_uyusmazlik_document_markdown_from_url"]
|
||||||
|
},
|
||||||
|
"anayasa": {
|
||||||
|
"name": "Anayasa Mahkemesi (Constitutional Court)",
|
||||||
|
"description": "Constitutional review and individual applications",
|
||||||
|
"tools": ["search_anayasa_norm_denetimi_decisions",
|
||||||
|
"get_anayasa_norm_denetimi_document_markdown",
|
||||||
|
"search_anayasa_bireysel_basvuru_report",
|
||||||
|
"get_anayasa_bireysel_basvuru_document_markdown"]
|
||||||
|
},
|
||||||
|
"kik": {
|
||||||
|
"name": "Kamu İhale Kurulu",
|
||||||
|
"description": "Public Procurement Authority",
|
||||||
|
"tools": ["search_kik_decisions", "get_kik_document_markdown"]
|
||||||
|
},
|
||||||
|
"rekabet": {
|
||||||
|
"name": "Rekabet Kurumu",
|
||||||
|
"description": "Competition Authority",
|
||||||
|
"tools": ["search_rekabet_kurumu_decisions", "get_rekabet_kurumu_document"]
|
||||||
|
},
|
||||||
|
"bedesten": {
|
||||||
|
"name": "Bedesten API",
|
||||||
|
"description": "Unified API for multiple courts",
|
||||||
|
"tools": ["search_yerel_hukuk_bedesten", "get_yerel_hukuk_bedesten_document_markdown",
|
||||||
|
"search_istinaf_hukuk_bedesten", "get_istinaf_hukuk_bedesten_document_markdown",
|
||||||
|
"search_kyb_bedesten", "get_kyb_bedesten_document_markdown"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSONResponse(content=databases)
|
||||||
|
|
||||||
|
@app.get("/api/stats")
|
||||||
|
async def get_statistics():
|
||||||
|
"""Get server statistics"""
|
||||||
|
uptime = (datetime.now() - SERVER_START_TIME).total_seconds()
|
||||||
|
|
||||||
|
# Count tools by database
|
||||||
|
tool_counts = {}
|
||||||
|
for tool in mcp_server._tool_manager.tools.values():
|
||||||
|
for db in ["yargitay", "danistay", "emsal", "uyusmazlik", "anayasa", "kik", "rekabet", "bedesten"]:
|
||||||
|
if db in tool.name.lower():
|
||||||
|
tool_counts[db] = tool_counts.get(db, 0) + 1
|
||||||
|
break
|
||||||
|
|
||||||
|
return JSONResponse({
|
||||||
|
"server": {
|
||||||
|
"uptime_seconds": uptime,
|
||||||
|
"start_time": SERVER_START_TIME.isoformat(),
|
||||||
|
"version": "0.1.0"
|
||||||
|
},
|
||||||
|
"tools": {
|
||||||
|
"total": len(mcp_server._tool_manager.tools),
|
||||||
|
"by_database": tool_counts
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"total_chambers": 79, # 52 Yargıtay + 27 Danıştay
|
||||||
|
"date_filtering": True,
|
||||||
|
"exact_phrase_search": True,
|
||||||
|
"dual_api_support": True
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Add a simple authentication example (optional)
|
||||||
|
# Uncomment to enable basic token authentication
|
||||||
|
"""
|
||||||
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||||
|
|
||||||
|
security = HTTPBearer()
|
||||||
|
|
||||||
|
async def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
|
||||||
|
token = credentials.credentials
|
||||||
|
expected_token = os.getenv("API_TOKEN")
|
||||||
|
|
||||||
|
if expected_token and token != expected_token:
|
||||||
|
raise HTTPException(status_code=401, detail="Invalid authentication token")
|
||||||
|
|
||||||
|
return token
|
||||||
|
|
||||||
|
# Then add Depends(verify_token) to any endpoint that needs protection
|
||||||
|
# Example: async def list_tools(..., token: str = Depends(verify_token)):
|
||||||
|
"""
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import uvicorn
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
upstream yargi_mcp {
|
||||||
|
server yargi-mcp:8000;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Rate limiting
|
||||||
|
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
||||||
|
limit_req_zone $binary_remote_addr zone=mcp_limit:10m rate=100r/s;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
# Redirect HTTP to HTTPS in production
|
||||||
|
# return 301 https://$server_name$request_uri;
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Content-Type-Options nosniff;
|
||||||
|
add_header X-Frame-Options DENY;
|
||||||
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin";
|
||||||
|
|
||||||
|
# API endpoints
|
||||||
|
location /api/ {
|
||||||
|
limit_req zone=api_limit burst=20 nodelay;
|
||||||
|
|
||||||
|
proxy_pass http://yargi_mcp;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# Timeouts
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# MCP endpoint (higher rate limit)
|
||||||
|
location /mcp-server/mcp/ {
|
||||||
|
limit_req zone=mcp_limit burst=50 nodelay;
|
||||||
|
|
||||||
|
proxy_pass http://yargi_mcp;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# WebSocket support
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
# Longer timeouts for MCP operations
|
||||||
|
proxy_connect_timeout 300s;
|
||||||
|
proxy_send_timeout 300s;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Health check (no rate limit)
|
||||||
|
location /health {
|
||||||
|
proxy_pass http://yargi_mcp;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Root and other paths
|
||||||
|
location / {
|
||||||
|
limit_req zone=api_limit burst=10 nodelay;
|
||||||
|
|
||||||
|
proxy_pass http://yargi_mcp;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# SSL configuration (uncomment for production)
|
||||||
|
# server {
|
||||||
|
# listen 443 ssl http2;
|
||||||
|
# server_name your-domain.com;
|
||||||
|
#
|
||||||
|
# ssl_certificate /etc/nginx/ssl/cert.pem;
|
||||||
|
# ssl_certificate_key /etc/nginx/ssl/key.pem;
|
||||||
|
# ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
# ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
#
|
||||||
|
# # Include all location blocks from above
|
||||||
|
# }
|
||||||
|
}
|
||||||
@@ -15,6 +15,20 @@ dependencies = [
|
|||||||
"pypdf>=5.5.0",
|
"pypdf>=5.5.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
asgi = [
|
||||||
|
"uvicorn[standard]>=0.30.0",
|
||||||
|
"starlette>=0.37.0",
|
||||||
|
]
|
||||||
|
api = [
|
||||||
|
"fastapi>=0.115.0",
|
||||||
|
"uvicorn[standard]>=0.30.0",
|
||||||
|
]
|
||||||
|
production = [
|
||||||
|
"gunicorn>=22.0.0",
|
||||||
|
"uvicorn[standard]>=0.30.0",
|
||||||
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
yargi-mcp = "mcp_server_main:main"
|
yargi-mcp = "mcp_server_main:main"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://railway.app/railway.schema.json",
|
||||||
|
"build": {
|
||||||
|
"builder": "NIXPACKS",
|
||||||
|
"buildCommand": "pip install -e .[asgi]"
|
||||||
|
},
|
||||||
|
"deploy": {
|
||||||
|
"startCommand": "uvicorn asgi_app:app --host 0.0.0.0 --port $PORT",
|
||||||
|
"healthcheckPath": "/health",
|
||||||
|
"healthcheckTimeout": 30,
|
||||||
|
"restartPolicyType": "ON_FAILURE",
|
||||||
|
"restartPolicyMaxRetries": 3
|
||||||
|
},
|
||||||
|
"variables": {
|
||||||
|
"ALLOWED_ORIGINS": "*",
|
||||||
|
"LOG_LEVEL": "info"
|
||||||
|
}
|
||||||
|
}
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Standalone ASGI server runner for Yargı MCP
|
||||||
|
|
||||||
|
This script provides a simple way to run the Yargı MCP server
|
||||||
|
as a web service using uvicorn.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python run_asgi.py
|
||||||
|
python run_asgi.py --host 0.0.0.0 --port 8080
|
||||||
|
python run_asgi.py --reload # For development
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Add project root to Python path
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
|
||||||
|
try:
|
||||||
|
import uvicorn
|
||||||
|
except ImportError:
|
||||||
|
print("Error: uvicorn is not installed.")
|
||||||
|
print("Please install it with: pip install uvicorn")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||||
|
)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Run Yargı MCP server as an ASGI web service"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--host",
|
||||||
|
type=str,
|
||||||
|
default=os.getenv("HOST", "127.0.0.1"),
|
||||||
|
help="Host to bind to (default: 127.0.0.1)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--port",
|
||||||
|
type=int,
|
||||||
|
default=int(os.getenv("PORT", "8000")),
|
||||||
|
help="Port to bind to (default: 8000)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--reload",
|
||||||
|
action="store_true",
|
||||||
|
help="Enable auto-reload for development"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--transport",
|
||||||
|
choices=["http", "sse"],
|
||||||
|
default="http",
|
||||||
|
help="Transport type (default: http)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--log-level",
|
||||||
|
choices=["debug", "info", "warning", "error"],
|
||||||
|
default=os.getenv("LOG_LEVEL", "info").lower(),
|
||||||
|
help="Log level (default: info)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--workers",
|
||||||
|
type=int,
|
||||||
|
default=1,
|
||||||
|
help="Number of worker processes (default: 1)"
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Select app based on transport
|
||||||
|
app_name = "asgi_app:app" if args.transport == "http" else "asgi_app:sse_app"
|
||||||
|
|
||||||
|
# Configure uvicorn
|
||||||
|
config = {
|
||||||
|
"app": app_name,
|
||||||
|
"host": args.host,
|
||||||
|
"port": args.port,
|
||||||
|
"log_level": args.log_level,
|
||||||
|
"reload": args.reload,
|
||||||
|
"access_log": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add workers only if not in reload mode
|
||||||
|
if not args.reload and args.workers > 1:
|
||||||
|
config["workers"] = args.workers
|
||||||
|
|
||||||
|
# Print startup information
|
||||||
|
print(f"Starting Yargı MCP server...")
|
||||||
|
print(f"Host: {args.host}")
|
||||||
|
print(f"Port: {args.port}")
|
||||||
|
print(f"Transport: {args.transport}")
|
||||||
|
print(f"Log level: {args.log_level}")
|
||||||
|
if args.reload:
|
||||||
|
print("Auto-reload: enabled")
|
||||||
|
else:
|
||||||
|
print(f"Workers: {args.workers}")
|
||||||
|
print(f"\nServer will be available at: http://{args.host}:{args.port}")
|
||||||
|
print(f"MCP endpoint: http://{args.host}:{args.port}/mcp/")
|
||||||
|
print(f"Health check: http://{args.host}:{args.port}/health")
|
||||||
|
print(f"API status: http://{args.host}:{args.port}/status")
|
||||||
|
print("\nPress CTRL+C to stop the server\n")
|
||||||
|
|
||||||
|
# Run uvicorn
|
||||||
|
try:
|
||||||
|
uvicorn.run(**config)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nShutting down server...")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
"""
|
||||||
|
Starlette integration example for Yargı MCP Server
|
||||||
|
|
||||||
|
This module demonstrates how to integrate the Yargı MCP server
|
||||||
|
with a Starlette application, including authentication middleware
|
||||||
|
and custom routing.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
uvicorn starlette_app:app --host 0.0.0.0 --port 8000
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from starlette.applications import Starlette
|
||||||
|
from starlette.routing import Mount, Route
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import JSONResponse, PlainTextResponse, RedirectResponse
|
||||||
|
from starlette.middleware import Middleware
|
||||||
|
from starlette.middleware.cors import CORSMiddleware
|
||||||
|
from starlette.middleware.authentication import AuthenticationMiddleware
|
||||||
|
from starlette.authentication import (
|
||||||
|
AuthenticationBackend, AuthCredentials, SimpleUser, AuthenticationError
|
||||||
|
)
|
||||||
|
|
||||||
|
# Import the main MCP app
|
||||||
|
from mcp_server_main import app as mcp_server
|
||||||
|
|
||||||
|
# Simple token authentication backend
|
||||||
|
class TokenAuthBackend(AuthenticationBackend):
|
||||||
|
async def authenticate(self, request):
|
||||||
|
auth_header = request.headers.get("Authorization")
|
||||||
|
expected_token = os.getenv("API_TOKEN")
|
||||||
|
|
||||||
|
# Skip auth for health check and public endpoints
|
||||||
|
if request.url.path in ["/health", "/", "/login"]:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not expected_token:
|
||||||
|
# No token configured, allow all
|
||||||
|
return AuthCredentials(["authenticated"]), SimpleUser("anonymous")
|
||||||
|
|
||||||
|
if not auth_header:
|
||||||
|
raise AuthenticationError("Authorization header required")
|
||||||
|
|
||||||
|
try:
|
||||||
|
scheme, token = auth_header.split()
|
||||||
|
if scheme.lower() != "bearer":
|
||||||
|
raise AuthenticationError("Invalid authentication scheme")
|
||||||
|
|
||||||
|
if token != expected_token:
|
||||||
|
raise AuthenticationError("Invalid token")
|
||||||
|
|
||||||
|
return AuthCredentials(["authenticated"]), SimpleUser("user")
|
||||||
|
except ValueError:
|
||||||
|
raise AuthenticationError("Invalid authorization header format")
|
||||||
|
|
||||||
|
# Homepage
|
||||||
|
async def homepage(request: Request):
|
||||||
|
return JSONResponse({
|
||||||
|
"service": "Yargı MCP Server",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"endpoints": {
|
||||||
|
"mcp": "/mcp-server/mcp/",
|
||||||
|
"api": "/api/",
|
||||||
|
"health": "/health"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# API info endpoint
|
||||||
|
async def api_info(request: Request):
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
return JSONResponse({"error": "Authentication required"}, status_code=401)
|
||||||
|
|
||||||
|
return JSONResponse({
|
||||||
|
"authenticated_as": request.user.display_name,
|
||||||
|
"available_tools": len(mcp_server._tool_manager.tools),
|
||||||
|
"databases": [
|
||||||
|
"Yargıtay", "Danıştay", "Emsal", "Uyuşmazlık",
|
||||||
|
"Anayasa", "KIK", "Rekabet", "Bedesten"
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
async def health_check(request: Request):
|
||||||
|
return JSONResponse({
|
||||||
|
"status": "healthy",
|
||||||
|
"service": "Yargı MCP Server"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Login example (returns token for demo)
|
||||||
|
async def login(request: Request):
|
||||||
|
token = os.getenv("API_TOKEN", "demo-token")
|
||||||
|
return JSONResponse({
|
||||||
|
"message": "Use this token in Authorization header",
|
||||||
|
"example": f"Authorization: Bearer {token}",
|
||||||
|
"note": "Set API_TOKEN environment variable to change token"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Create MCP ASGI app
|
||||||
|
mcp_app = mcp_server.http_app(path='/mcp')
|
||||||
|
|
||||||
|
# Configure middleware
|
||||||
|
middleware = [
|
||||||
|
Middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=os.getenv("ALLOWED_ORIGINS", "*").split(","),
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
),
|
||||||
|
Middleware(AuthenticationMiddleware, backend=TokenAuthBackend()),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Create routes
|
||||||
|
routes = [
|
||||||
|
Route("/", homepage),
|
||||||
|
Route("/health", health_check),
|
||||||
|
Route("/login", login),
|
||||||
|
Route("/api/info", api_info),
|
||||||
|
Mount("/mcp-server", app=mcp_app),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Create Starlette app
|
||||||
|
app = Starlette(
|
||||||
|
routes=routes,
|
||||||
|
middleware=middleware,
|
||||||
|
lifespan=mcp_app.lifespan
|
||||||
|
)
|
||||||
|
|
||||||
|
# Nested mount example
|
||||||
|
def create_nested_app():
|
||||||
|
"""Example of nested mounting for complex routing structures"""
|
||||||
|
|
||||||
|
# Create inner app with MCP
|
||||||
|
inner_app = Starlette(
|
||||||
|
routes=[Mount("/services", app=mcp_app)],
|
||||||
|
middleware=middleware
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create outer app
|
||||||
|
outer_app = Starlette(
|
||||||
|
routes=[
|
||||||
|
Route("/", homepage),
|
||||||
|
Mount("/v1", app=inner_app),
|
||||||
|
],
|
||||||
|
lifespan=mcp_app.lifespan
|
||||||
|
)
|
||||||
|
|
||||||
|
# MCP would be available at /v1/services/mcp/
|
||||||
|
return outer_app
|
||||||
|
|
||||||
|
# Export both apps
|
||||||
|
nested_app = create_nested_app()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import uvicorn
|
||||||
|
print("Starting Starlette app with authentication...")
|
||||||
|
print("Set API_TOKEN environment variable to enable authentication")
|
||||||
|
print("Example: API_TOKEN=secret-token python starlette_app.py")
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||||
Reference in New Issue
Block a user