PluginWorld
Lo

local-data-mcp

MCP

Enables LLM clients to explore and query a local SQLite database through read-only, typed tools for table listing, schema inspection, ad-hoc SQL, customer lookup, and sales summaries.

@Stephaniew1 · MIT License · updated today

SECURITY

B

SCORE

60

STARS

▲ 0

PLUG IN

git clone https://github.com/Stephaniew1/local-data-mcp.git

See the README to configure this MCP server

README

local-data-mcp

An MCP server that exposes a local SQLite database to LLM clients (Claude Desktop, Claude Code, MCP Inspector, …) as structured, read-only tools.

Every tool returns a Pydantic model, so clients get both an outputSchema up front and typed structuredContent in each result (plus a JSON text fallback for older clients).

Quick start

uv sync
uv run local-data-mcp --seed          # creates data/store.db with sample store data
uv run pytest                         # 16 tests, incl. a real stdio round-trip
uv run local-data-mcp                 # serve over stdio

Point it at your own database with --db path/to/file.db or LOCAL_DATA_DB=.... The generic tools (list_tables, describe_table, run_query) work on any SQLite file. The domain tools assume the sample schema, so replace them with ones that fit your data (see "Adapting" below).

What it exposes

Kind Name Purpose
tool list_tables Tables + row counts
tool describe_table Columns, FKs, row count, 3 sample rows
tool run_query Ad-hoc read-only SQL (SELECT/WITH/EXPLAIN), row-limited
tool search_products Filter by name, category, price range, stock
tool get_customer Customer by id/email + lifetime value + recent orders with items
tool sales_summary Revenue/units/orders grouped by month, category or product, with a date range
resource schema://database Full DDL
prompt analyze_sales Canned analysis workflow using the tools above

All tools carry readOnlyHint / idempotentHint annotations.

Safety model

  • Connections open the file with mode=ro, so SQLite itself refuses writes.
  • run_query also installs a SQLite authorizer that denies everything except SELECT/READ/FUNCTION/RECURSIVE. That covers cases like WITH x AS (...) DELETE ..., ATTACH and pragmas.
  • Only one statement is allowed per call, and results are capped at limit (max 1000), with a truncated flag.
  • Table names in describe_table are checked against sqlite_master and never interpolated from raw input.
  • Bad input becomes a tool error (isError: true) that the model can read and recover from, not a crash.

Connect a client

Claude Code

claude mcp add local-data -- uv --directory /ABSOLUTE/PATH/local-data-mcp run local-data-mcp

Claude Desktop (claude_desktop_config.json)

{
  "mcpServers": {
    "local-data": {
      "command": "uv",
      "args": ["--directory", "/ABSOLUTE/PATH/local-data-mcp", "run", "local-data-mcp"],
      "env": { "LOCAL_DATA_DB": "/ABSOLUTE/PATH/local-data-mcp/data/store.db" }
    }
  }
}

MCP Inspector (interactive debugging)

npx @modelcontextprotocol/inspector uv run local-data-mcp

Over HTTP: uv run local-data-mcp --transport streamable-http serves at http://127.0.0.1:8000/mcp.

In stdio mode, stdout carries the protocol. Never print() to stdout in server code; log to stderr.

Layout

src/local_data_mcp/
  __init__.py   CLI entry point (--db, --seed, --transport)
  server.py     MCPServer: tools, resource, prompt
  models.py     Pydantic output models -> outputSchema / structuredContent
  db.py         read-only SQLite access + authorizer
  seed.py       deterministic sample database
tests/          in-memory client tests + stdio end-to-end test

Adapting to your own data

  1. Swap Database in db.py for your source (CSV via csv/DuckDB, a JSON folder, Postgres, …).
  2. Model each answer shape in models.py.
  3. Add one @mcp.tool() per question the model should be able to answer. Give each a clear docstring (it becomes the tool description) and Annotated[..., Field(description=...)] parameters (they become the input schema).

SIMILAR PLUGINS