Skip to main content
Glama
OSoliman330

JSON DB MCP Server

by OSoliman330
README.md
# JSON DB MCP Server (FastAPI + FastMCP)

A minimal **Model Context Protocol (MCP)** server that also exposes a small **REST API**.  
Data is stored in a single JSON file (`db.json`) as **collections** of items with auto-generated UUIDs.

- **MCP Tools:** `db.add_item`, `db.get_item`, `db.list_items`, `db.update_item`, `db.delete_item`  
- **REST Endpoints:** `POST/GET/PUT/DELETE /db/{collection}[/{id}]`  
- **Transports:** HTTP (recommended) and SSE (optional)  
- **Stack:** Python, FastAPI, Uvicorn, FastMCP

---

## Features

- Simple JSON-file database with atomic writes
- Thread-safe in a single-process server using a lock
- Shared core logic for both **MCP tools** and **REST**
- Works with **VS Code Copilot (Chat)** and **Gemini CLI** as an MCP server

---

## Requirements

- Python **3.10+**
- [`uv`](https://github.com/astral-sh/uv) (recommended) or `pip`
- Git (optional, for version control)

---

## Project Layout

```
.
├─ main.py
├─ db.json                # created on first write
└─ README.md
```

---

## Installation

### Using `uv` (recommended)

```bash
uv venv
uv pip install fastapi uvicorn fastmcp
```

### Using `pip`

```bash
python -m venv .venv
source .venv/bin/activate
pip install fastapi uvicorn fastmcp
```

---

## Running

### Development (auto-reload)

```bash
uv run main.py
# or
uv run python main.py
# Server: http://localhost:8000
```

> **Default MCP transport:** HTTP at `/mcp/` (recommended).  
> If you prefer SSE, see **MCP over SSE (optional)** below.

---

## REST API

All endpoints operate on a **collection** (created on first write) with items of shape:

```json
{
  "id": "uuid-string",
  "...": "fields you provide"
}
```

### Create

```bash
curl -X POST http://localhost:8000/db/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Omar","role":"admin"}'
```

### List

```bash
curl http://localhost:8000/db/users
```

### Get by ID

```bash
curl http://localhost:8000/db/users/<ITEM_ID>
```

### Update

```bash
curl -X PUT http://localhost:8000/db/users/<ITEM_ID> \
  -H "Content-Type: application/json" \
  -d '{"role":"owner"}'
```

### Delete

```bash
curl -X DELETE http://localhost:8000/db/users/<ITEM_ID>
```

---

## MCP Server (HTTP transport, recommended)

This server mounts FastMCP’s **HTTP** transport at **`/mcp/`**.

### VS Code — GitHub Copilot (Chat)

1. Open **Copilot Chat** → **Tools** (or **Add MCP**).
2. **Transport:** `HTTP`
3. **URL:** `http://localhost:8000/mcp/`  *(include the trailing slash)*
4. Enable the server. You should see tools:
   - `db.add_item`, `db.get_item`, `db.list_items`, `db.update_item`, `db.delete_item`

**Using tools in chat:**  
Select a tool from the list or ask:  
> Use `db.add_item` with `collection="users"` and `payload={"name":"Omar"}`.

### Gemini CLI

Add the server via CLI (writes settings for you):

```bash
# Project-scoped (./.gemini/settings.json)
gemini mcp add --transport http jsondb http://localhost:8000/mcp/

# Or user-scoped (~/.gemini/settings.json)
gemini mcp add --scope user --transport http jsondb http://localhost:8000/mcp/
```

List tools:

```bash
gemini mcp list
```

Call a tool:

```bash
gemini mcp call jsondb db.add_item \
  --args collection=users \
  --args payload='{"name":"Omar"}'
```

---

## MCP over SSE (optional)

If you prefer SSE, change the mount in `main.py`:

```python
# replace the HTTP mount:
# mcp_http_app = mcp.http_app(path="/")
# app.mount("/mcp", mcp_http_app)

mcp_sse_app = mcp.sse_app()
app.mount("/mcp", mcp_sse_app)  # SSE lives under /mcp/sse/
```

- **SSE endpoint:** `http://localhost:8000/mcp/sse/`  *(note trailing slash)*
- **Copilot:** Transport = `SSE`, URL = `http://localhost:8000/mcp/sse/`
- **Gemini CLI:**
  ```bash
  gemini mcp add --scope user --transport sse jsondb http://localhost:8000/mcp/sse/
  ```

> Many HTTP clients require a trailing slash for SSE endpoints; use `/mcp/sse/`.

---

## Health Check (optional)

Add this to `main.py` (after `app = FastAPI(...)`) if you want a friendly root:

```python
@app.get("/")
def health():
    return {"ok": True, "rest": "/db/{collection}", "mcp": "/mcp/"}
```

---

## Persistence & Backups

- Data file: `db.json` (created on first write).
- Writes are **atomic**: a `db.json.tmp` is written then replaced.
- For safety, consider periodic snapshots (e.g., `cp db.json db.json.bak` in a cron job).

---

## Authentication (optional)

For local development MCP/REST are open.  
If you need auth, simplest is an API key header via FastAPI middleware and **headers** in your MCP client configuration. Example:

```python
from fastapi import Request
from fastapi.responses import JSONResponse
import os

API_KEY = os.getenv("API_KEY", "")

@app.middleware("http")
async def api_key_guard(request: Request, call_next):
    if API_KEY and request.headers.get("x-api-key") != API_KEY:
        return JSONResponse(status_code=401, content={"detail": "Unauthorized"})
    return await call_next(request)
```

- Copilot/Gemini: configure an `x-api-key` header in the MCP server settings.

---

## Troubleshooting

- **404 on `/`**  
  Expected. Use REST under `/db/...` and MCP at `/mcp/` (HTTP) or `/mcp/sse/` (SSE).

- **307 redirect** from `/mcp` → `/mcp/`  
  Some SSE clients don’t follow redirects. Use the **trailing slash** or disable redirect slashes:
  ```python
  app.router.redirect_slashes = False
  ```

- **SSE returns 404**  
  Make sure you’re hitting `/mcp/sse/` (not `/mcp/`).

- **Concurrency**  
  Run as a **single process** for file-backed DB. Multi-process/worker deployments need a real DB or OS-level file locks.

- **Git push errors (VS Code AskPass socket)**  
  Unset helper vars or use GitHub CLI / SSH keys:
  ```bash
  unset GIT_ASKPASS SSH_ASKPASS VSCODE_GIT_ASKPASS_NODE VSCODE_GIT_ASKPASS_MAIN
  ```

---

## License

Copyright© - Omar SOLIMAN