Content
# Luna-MCP-Server
[](https://github.com/Drago-03/Luna-MCP-Services/actions/workflows/ci.yml)
[](https://github.com/astral-sh/ruff)
[](https://mypy-lang.org/)
[](https://github.com/aquasecurity/trivy-action)
[](https://results.pre-commit.ci/latest/github/Drago-03/Luna-MCP-Services/main)
[](https://codecov.io/github/Drago-03/Luna-MCP-Services)
## Coverage Icicle Graph

> Indie Hub submission for the **Puch AI Hackathon**
> Team: **Indie Hub** • Member: **Mantej Singh** • Server: **Luna MCP Server**

Thin, production-oriented MCP (Model Context Protocol) HTTP server that:
- Implements the Puch AI MCP contract (single `/mcp` JSON-RPC 2.0 POST endpoint)
- Extends the official [Puch AI MCP Starter](https://github.com/TurboML-Inc/mcp-starter)
- Bridges advanced AI & multimodal workloads to **Luna Services** ([repo](https://github.com/Drago-03/Luna-Services)) — Gemini code generation, voice synthesis, Supabase data, and image transforms
- Ships a developer tool suite (GitHub automation, CI triggers, scaffolding, tests, Docker build, image utilities)
- Auth: Bearer token (strict) + optional GitHub OAuth (future-ready)
- Deploys: local (`uvicorn`), public tunnel (`ngrok`), containerized (`docker-compose`)
- Deploys: local (`uvicorn`), public tunnel (`ngrok`), containerized (`docker-compose`), serverless (Vercel)
---
## Tool Registry (MCP Methods)
| Method | Type | Description |
|-------------------|-----------|-------------|
| `code_gen` | Forwarder | Gemini-style code generation via Luna Services (`/api/ai/code`) with graceful fallback |
| `git_clone` | Local | Shallow + partial clone (`--depth 1 --filter=blob:none`) into `./repos/<name>` |
| `ci_trigger` | Forwarder | Dispatch a GitHub Actions workflow (requires `GITHUB_TOKEN`) |
| `scaffold_project`| Local | Create a minimal Python package + optional test |
| `run_tests` | Local | Execute `pytest -q`; summarizes result |
| `img_bw` | Local | Fetch image URL → convert to grayscale (Pillow) → base64 PNG |
| `voice_speak` | Forwarder | Text-to-audio via Luna Services (`/api/ai/voice`) |
| `bw_remote` | Forwarder | Remote grayscale transform through Luna Services (`/api/image/bw`) |
| `create_branch` | GitHub | Create branch from base ref |
| `commit_file` | GitHub | Create/update file (base64 content) |
| `open_pr` | GitHub | Open pull request |
| `list_issues` | GitHub | Enumerate open issues |
| `validate` | Local | Return server validation number |
### Streaming Support
`code_gen` now supports simulated streaming via Server-Sent Events (SSE). While upstream model
tokens are currently buffered server-side, the server slices and emits chunks incrementally for
responsive clients.
SSE endpoint:
```text
GET /public/stream?method=code_gen&prompt=Write%20a%20Python%20CLI
```
Events:
- `start` – initial metadata
- (default) – chunk messages: `{ "chunk": "..." }`
- `end` – completion marker `{ "ok": true }`
- `error` – `{ "error": "..." }`
Example curl consumption:
```bash
curl -N 'https://<host>/public/stream?method=code_gen&prompt=FizzBuzz%20in%20Rust'
```
Client JS example:
```js
const es = new EventSource('/public/stream?method=code_gen&prompt=FizzBuzz');
es.onmessage = e => { const {chunk} = JSON.parse(e.data); if(chunk) append(chunk); };
es.addEventListener('end', ()=> es.close());
```
> Acceptance test only depends on: `code_gen`, `git_clone`, `img_bw`
---
## Environment Variables
| Name | Required | Purpose |
|------|----------|---------|
| `AUTH_TOKEN` | Yes | Bearer token expected in `Authorization: Bearer <token>` |
| `LUNA_URL` | No (default) | Upstream Luna Services base URL (default `http://localhost:8000`) |
| `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` | No | For optional OAuth extension |
| `GITHUB_TOKEN` | Conditional | Needed for write GitHub APIs & CI triggers |
| `SUPABASE_URL` / `SUPABASE_KEY` | Optional | Passed through for Luna Services usage |
| `NGROK_TOKEN` | Recommended | For public tunneling via docker-compose ngrok service |
| `PUBLIC_TOOLS` | Optional | Comma list of tools exposed at `/public/execute` (default `code_gen,validate`) |
| `PUBLIC_BASE_URL` | Optional | External base URL used in OAuth metadata |
| `OAUTH_SIGNING_KEY` | Optional | HMAC secret for signing short-lived auth tokens |
Sample file: `.env.example`
---
## Quick Start (Local Dev)
Prereqs:
- Python 3.11+
- `uv` (fast dependency manager) – <https://github.com/astral-sh/uv>
- `git`, `ngrok` (if tunneling), optional Docker
```bash
uv venv
source .venv/bin/activate
uv sync
cp .env.example .env
# Edit .env and set AUTH_TOKEN=your_secret
uvicorn mcp-bearer-token.luna_mcp:app --host 0.0.0.0 --port 8086 --reload
# Programmatic import alternative:
# from mcp_bearer_token import app
```
Public tunnel:
```bash
ngrok http 8086
```
---
## Connect Puch AI MCP Client
Once ngrok URL is live (e.g. `https://abc123.ngrok-free.app`):
```bash
/mcp connect https://abc123.ngrok-free.app/mcp your_secret
```
Test tools:
```bash
/mcp call code_gen '{"prompt":"Write hello world in Rust"}'
/mcp call git_clone '{"url":"https://github.com/tensorflow/tensorflow"}'
/mcp call img_bw '{"image_url":"https://picsum.photos/300"}'
```
All should return a JSON-RPC `result` within 5 seconds.
---
## Docker & Compose
```bash
docker compose up --build
```
Services:
- `mcp`: FastAPI app on `8086`
- `ngrok`: Tunnel container (needs `NGROK_TOKEN`)
---
## Adding a New Tool
Steps:
1. Open `mcp-bearer-token/luna_mcp.py`.
2. Define an async function and decorate it with `@tool`.
Example:
```python
@tool("echo", "Echo back a message")
async def echo(message: str) -> dict:
return {"echo": message}
```
Guidelines:
- Return JSON-serializable data only.
- Keep execution under a few seconds for good UX.
- Avoid side-effects in tools you expose publicly.
---
## Auth Model
1. All `/mcp` calls must include `Authorization: Bearer <AUTH_TOKEN>`
2. GitHub mutations require either:
- `GITHUB_TOKEN` PAT (scopes: `repo`, `workflow`)
- Future: OAuth exchange (placeholder in `github_oauth/oauth_config.py`)
Unauthorized calls → HTTP 401 (not JSON-RPC envelope).
### Public Facade
Unauthenticated endpoints:
- `GET /public/health` – minimal status + public tool list
- `GET /public/tools` – list public tools
- `POST /public/execute` – invoke allow‑listed tool (sanitized output)
- `GET /public/stream` – SSE stream wrapper (chunked output for streaming-capable tools)
- `GET /public/metrics` – aggregated latency metrics (avg, p95) per tool
Configure with `PUBLIC_TOOLS` env var (comma separated). Keep this list restricted to idempotent, non-sensitive tools.
### OAuth (Experimental Placeholder)
Endpoints provided for future full auth code flow:
- `/.well-known/oauth-authorization-server`
- `/authorize`
- `/token`
Currently they return placeholder responses. To harden:
1. Generate an `OAUTH_SIGNING_KEY` (32+ random bytes).
2. Implement user consent & redirect with `code` param in `/authorize`.
3. Exchange `code` for JWT access token in `/token` (signed via HS256).
Example minimal signing snippet (not yet wired):
```python
import jwt, time, os
token = jwt.encode({"sub":"user123","exp":int(time.time())+900}, os.environ['OAUTH_SIGNING_KEY'], algorithm='HS256')
```
PRs welcome to complete the flow.
## Vercel Deployment
Vercel can serve the FastAPI app via ASGI using `vercel-python`. Quick path:
1. Add a `vercel.json` with a serverless function entry.
2. Expose `AUTH_TOKEN` (and others) as Vercel Project Environment Variables.
3. Deploy via `vercel` CLI or Git integration.
Sample `vercel.json` (add to repo root):
```json
{
"functions": {
"api/index.py": { "runtime": "python3.11" }
},
"routes": [
{ "src": "/mcp", "dest": "/api/index.py" },
{ "src": "/public/(.*)", "dest": "/api/index.py" },
{ "src": "/(.*)", "dest": "/public/index.html" }
]
}
```
Create `api/index.py` wrapper that loads the FastAPI `app`:
```python
from mcp_bearer_token import app as _app
from fastapi.middleware.cors import CORSMiddleware
app = _app # Vercel expects variable named app
# (Optional) adjust any middleware for serverless nuances here
```
Note: For long‑running operations (e.g., git clone of large repos) serverless timeouts may apply. Consider keeping those tools disabled via `PUBLIC_TOOLS` or offloading heavy tasks.
### 405 Method Not Allowed (Troubleshooting MCP Connect)
If you receive HTTP 405 when connecting a MCP client:
1. Ensure endpoint URL includes `/mcp` (not root).
2. Confirm POST is allowed by making a manual request:
```bash
curl -i -X POST https://<your-host>/mcp -H 'Authorization: Bearer <token>' -d '{"jsonrpc":"2.0","id":1,"method":"validate","params":{}}'
```
1. GET `/mcp` should return discovery JSON (unauthenticated) confirming the route.
2. If deploying on Vercel, verify `vercel.json` routes include `"^/(mcp|... )$"` mapping to your function.
Checklist:
- [ ] `/mcp` path present
- [ ] POST supported
- [ ] Auth header present
- [ ] No extra path prefix (e.g. `/api/mcp` unless client configured)
---
## Test / Reliability Notes
- `git_clone` uses `--depth 1 --filter=blob:none` for speed (<5s on large repos)
- Basic test suite (`pytest -q`) ensures health endpoint & tool registry presence
- CI runs ruff + mypy + pytest + docker build + Trivy scan
- `code_gen` gracefully falls back to a deterministic sample if upstream unreachable
- `img_bw` enforces network + Pillow decode boundaries (default httpx timeout 20s)
- `run_tests` degrades if `pytest` missing (returns informative result)
---
## Architecture
See: [ARCHITECTURE.md](ARCHITECTURE.md)
---
## References
- Puch AI MCP Starter: <https://github.com/TurboML-Inc/mcp-starter>
- Luna Services Backend: <https://github.com/Drago-03/Luna-Services>
- Puch AI Hackathon: <https://puch.ai/hack>
- Mermaid Live: <https://mermaid.live>
---
## Acceptance Checklist
| Item | Status |
|------|--------|
| Bearer Auth | ✅ |
| Tools: core 6 | ✅ |
| Forwarders to Luna Services | ✅ |
| JSON-RPC shape | ✅ |
| ngrok integration | ✅ |
| docker-compose | ✅ |
| Dockerfile | ✅ |
| Docs + diagrams | ✅ |
| Quickstart script | ✅ |
| Acceptance script | ✅ |
| Basic tests | ✅ |
---
## How to Demo (Condensed)
```bash
bash quickstart.sh
# Copy ngrok URL
/mcp connect https://<ngrok>.ngrok-free.app/mcp my_secret
/mcp call code_gen '{"prompt":"Write hello world in Rust"}'
/mcp call git_clone '{"url":"https://github.com/tensorflow/tensorflow"}'
/mcp call img_bw '{"image_url":"https://picsum.photos/300"}'
/mcp call validate '{}'
./acceptance_test.sh https://<ngrok>.ngrok-free.app my_secret
```
---
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
servers
Model Context Protocol Servers
servers
Model Context Protocol Servers
Time
A Model Context Protocol server for time and timezone conversions.