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
- 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
- Python 3.10+
uv
(recommended) orpip
- Git (optional, for version control)
.
├─ main.py
├─ db.json # created on first write
└─ README.md
uv venv
uv pip install fastapi uvicorn fastmcp
python -m venv .venv
source .venv/bin/activate
pip install fastapi uvicorn fastmcp
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.
All endpoints operate on a collection (created on first write) with items of shape:
{
"id": "uuid-string",
"...": "fields you provide"
}
curl -X POST http://localhost:8000/db/users \
-H "Content-Type: application/json" \
-d '{"name":"Omar","role":"admin"}'
curl http://localhost:8000/db/users
curl http://localhost:8000/db/users/<ITEM_ID>
curl -X PUT http://localhost:8000/db/users/<ITEM_ID> \
-H "Content-Type: application/json" \
-d '{"role":"owner"}'
curl -X DELETE http://localhost:8000/db/users/<ITEM_ID>
This server mounts FastMCP’s HTTP transport at /mcp/
.
- Open Copilot Chat → Tools (or Add MCP).
- Transport:
HTTP
- URL:
http://localhost:8000/mcp/
(include the trailing slash) - 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
withcollection="users"
andpayload={"name":"Omar"}
.
Add the server via CLI (writes settings for you):
# 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:
gemini mcp list
Call a tool:
gemini mcp call jsondb db.add_item \
--args collection=users \
--args payload='{"name":"Omar"}'
If you prefer SSE, change the mount in main.py
:
# 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:
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/
.
Add this to main.py
(after app = FastAPI(...)
) if you want a friendly root:
@app.get("/")
def health():
return {"ok": True, "rest": "/db/{collection}", "mcp": "/mcp/"}
- 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).
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:
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.
-
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: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:unset GIT_ASKPASS SSH_ASKPASS VSCODE_GIT_ASKPASS_NODE VSCODE_GIT_ASKPASS_MAIN
Copyright© - Omar SOLIMAN