Skip to main content
Glama
venkatacloud01

local-mcp-postgres

πŸš€ Operation LOCAL-MCP

A fully local Postgres-backed MCP (Model Context Protocol) server, connected two ways: first to Claude Desktop (which speaks MCP natively), then to a fully local Ollama model via a hand-built bridge β€” since Ollama has no native MCP client. Both paths sit behind a FastAPI + Streamlit chat console.

Built as a diagnostic exercise as much as a build exercise: the core question was "where exactly does local-model tool-calling reliability break down?", and this project isolates that question with a small, controlled two-tool schema.

What it does

Ask questions in plain English about a local inventory/orders database:

  • "What's in the inventory?"

  • "What's John's total order amount?"

  • "How many Widget B do we have?"

The model decides which tool to call, the tool runs a safe parameterized query against Postgres, and the result gets turned back into a natural-language answer.

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     stdio (MCP)     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚Claude Desktop│◄───────────────────►│                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β”‚  server.py       │──► Postgres
                                     β”‚  (FastMCP)        β”‚    (Docker)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   hand-built bridge  β”‚                  β”‚
β”‚  Ollama      │◄───────────────────►│                  β”‚
β”‚ (qwen2.5:7b) β”‚      (bridge.py)    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   HTTP   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  FastAPI     │◄────────►│  Streamlit   β”‚
β”‚  (main.py)   β”‚          β”‚  (app.py)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

server.py is the single source of truth for the tools β€” both Claude Desktop and the Ollama bridge talk to the exact same FastMCP server. This was deliberate: it turns "does Claude Desktop succeed where Ollama fails?" into a clean A/B test, since the only variable that changes is the model, not the tool definitions.

Key finding

The starting hypothesis (from prior local-LLM tooling work) was that 7B–12B models are broadly unreliable at emitting well-formed tool calls. That held for dense, high-overhead schemas (e.g. Claude Code's tool surface), but did not hold here: qwen2.5:7b, given this project's small two-tool schema, correctly selected the right tool on every test β€” including inferring a sensible default parameter (min_quantity: 1) that wasn't stated in the prompt β€” and never once emitted malformed tool-call JSON.

Takeaway: the earlier reliability problem looks like it's driven more by schema size / context overhead than by a hard local-model capability ceiling. Worth revisiting the claude-code-router / hosted-API tradeoff with that in mind β€” schema simplification may solve more of the problem than switching providers does.

Setup

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

docker compose up -d              # Postgres, seeded via init.sql
ollama pull qwen2.5:7b            # or llama3.1:8b β€” confirm `tools` under
ollama show qwen2.5:7b            # Capabilities before relying on it

Running it

Path 1 β€” Claude Desktop (MCP native): Add an entry to claude_desktop_config.json pointing at src/server.py using your venv's Python (see repo history / commit log for the exact config that worked after a couple of path-resolution false starts β€” see Gotchas below).

Path 2 β€” Local Ollama console:

# terminal 1
uvicorn src.main:app --reload

# terminal 2
streamlit run src/app.py

Then open the Streamlit URL and ask questions in the chat box.

Gotchas hit along the way (kept for the next person, including future me)

  • python -m src.server works from a terminal but not from Claude Desktop's launched subprocess β€” its cwd handling doesn't reliably put the project root on Python's module search path for -m resolution. Fix: point claude_desktop_config.json directly at the script file (args: ["/abs/path/src/server.py"]) instead of using -m.

  • tool.inputSchema vs tool.input_schema β€” the wire-protocol field name and the installed mcp SDK's Python attribute name don't match; the SDK uses snake_case. Worth checking installed SDK attribute names directly rather than assuming they mirror the protocol spec.

  • init.sql only runs against Postgres on a fresh Docker volume. If you've already got a pgdata volume from a prior run, editing init.sql and re-running docker compose up silently does nothing β€” you need docker compose down -v first to force re-seeding.

  • Model function-call arguments arrive as either a dict or a JSON string depending on the model/quantization β€” bridge.py's loop handles both.

Known limitations

  • No multi-turn memory. Each /chat request (and each run_tool_call_loop call) starts a fresh conversation with no history from prior turns. The Streamlit UI displays prior turns for readability, but doesn't send them back to the backend β€” a deliberate scope cut for this iteration, not an oversight.

  • Each request respawns the MCP server subprocess rather than keeping one long-lived session β€” simpler and correct for this scale, but adds latency you'd want to remove for anything beyond a local demo.

  • Read-only by design: both tools run fixed, parameterized SELECTs. No write/update tools exist, intentionally, to keep the safety surface small.

Stack

Python 3.12 Β· FastMCP Β· MCP SDK Β· Ollama (qwen2.5:7b) Β· PostgreSQL (Docker) Β· FastAPI Β· Streamlit Β· psycopg2

Repo layout

local-mcp-postgres/
β”œβ”€β”€ README.md
β”œβ”€β”€ MISSION_PLAN.md        # phase-by-phase build log / checklist
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ init.sql
β”œβ”€β”€ requirements.txt
└── src/
    β”œβ”€β”€ server.py           # FastMCP server: fetch_inventory, fetch_order_total
    β”œβ”€β”€ bridge.py            # Ollama-MCP bridge + tool-call loop
    β”œβ”€β”€ main.py               # FastAPI backend
    └── app.py                 # Streamlit console

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/venkatacloud01/local-mcp-postgres'

If you have feedback or need assistance with the MCP directory API, please join our Discord server