Skip to main content
Glama
README.md
# toolkit-mcp — an MCP server for Claude

A small but real [Model Context Protocol](https://modelcontextprotocol.io) server
that gives Claude three capabilities it doesn't have on its own: querying a local
SQLite database, searching files inside one directory, and looking up weather.

Built on the official Python SDK (`mcp` v2). Works with Claude Desktop, Claude
Code, and any other MCP client.

---

## The problem

Claude can't see your database, your files, or anything that happened after its
training cutoff. Copy-pasting a schema and some rows into the chat works once and
doesn't scale — and it gives Claude a stale snapshot rather than the ability to
go and look.

## The solution

MCP is the standard interface for handing an assistant real tools. This server
implements five of them, with the boring parts done properly: the database
connection is genuinely read-only, file access can't escape its root, and every
error comes back as data the model can read and correct rather than a crash.

---

## The tools

| Tool | What it does |
|---|---|
| `describe_database` | Lists tables, columns, types and row counts — call it first to learn the schema |
| `query_database` | Runs a read-only `SELECT` / `WITH` query and returns rows |
| `search_files` | Finds files by filename glob or by text content |
| `read_file` | Reads one text file from inside the configured root |
| `get_weather` | Current conditions + forecast via Open-Meteo (free, **no API key**) |

### Security, and why it's two layers deep

`query_database` is the tool most likely to be handed something destructive, so
it has two independent guards:

1. **SQLite's own read-only mode.** The connection is opened as
   `file:store.db?mode=ro`, so a write is refused by the database engine — not by
   my code.
2. **Statement validation.** Only `SELECT` and `WITH…SELECT` are accepted.
   Multiple statements, `PRAGMA`, `ATTACH`, and every DDL/DML keyword are
   rejected with a message explaining why. SQL comments are stripped first, so a
   forbidden keyword can't hide behind `--`.

`search_files` and `read_file` resolve every path and confirm it is still inside
the configured root. `../../../../etc/passwd`, absolute paths, and symlinks
pointing outside are all refused.

All of this is covered by tests — see below.

---

## Setup

```bash
git clone https://github.com/harshhh817/mcp-server-demo.git
cd mcp-server-demo
pip install -r requirements.txt
python make_sample_db.py     # builds data/store.db
```

Verify it works before wiring it into anything:

```bash
python test_server.py
```

### Claude Code

From inside the repo:

```bash
claude mcp add toolkit -- python /absolute/path/to/mcp-server-demo/server.py
```

Or commit a `.mcp.json` at your project root so the whole team gets it:

```json
{
  "mcpServers": {
    "toolkit": {
      "command": "python",
      "args": ["/absolute/path/to/mcp-server-demo/server.py"],
      "env": {
        "TOOLKIT_DB_PATH": "/absolute/path/to/mcp-server-demo/data/store.db",
        "TOOLKIT_FILES_ROOT": "/absolute/path/to/mcp-server-demo/sandbox"
      }
    }
  }
}
```

Then check it registered:

```bash
claude mcp list
```

### Claude Desktop

Edit `claude_desktop_config.json`:

- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "toolkit": {
      "command": "python",
      "args": ["/absolute/path/to/mcp-server-demo/server.py"],
      "env": {
        "TOOLKIT_DB_PATH": "/absolute/path/to/mcp-server-demo/data/store.db",
        "TOOLKIT_FILES_ROOT": "/absolute/path/to/mcp-server-demo/sandbox"
      }
    }
  }
}
```

Restart Claude Desktop. The tools appear under the tools icon in the chat input.

**Two things that trip people up:** paths must be absolute — MCP servers don't
inherit your shell's working directory. And `"command": "python"` must be a
Python that has `mcp` installed; if you use a virtualenv, point at
`/path/to/venv/bin/python` instead.

---

## Configuration

| Variable | Default | Purpose |
|---|---|---|
| `TOOLKIT_DB_PATH` | `./data/store.db` | SQLite file to query |
| `TOOLKIT_FILES_ROOT` | `./sandbox` | The only directory file tools can reach |

Point these at your own database and folder and the server is immediately useful
— nothing in the tool code is specific to the sample data.

---

## Try it

Once connected, ask Claude things like:

> *What tables are in the database?*

> *Which customer has spent the most, and on what?*

> *Find every file mentioning connection pooling and summarise the issue.*

> *What's the weather in Delhi this week?*

Claude picks the tool, writes the SQL, and reads the result. For the second
question it typically calls `describe_database`, then issues a three-table join
on its own.

---

## Tests

`test_server.py` spawns the server as a subprocess and drives it as a **real MCP
client over stdio** — the same transport Claude Desktop uses. It isn't testing
the functions in-process; it's testing the server.

```bash
python test_server.py
```

Real output:

```
connected to 'toolkit' v1.0.0

tool discovery
  ok    server advertises 5 tools  (describe_database, get_weather, query_database, read_file, search_files)
  ...
query_database - rejected queries
  ok    DROP is rejected  (only SELECT (or WITH ... SELECT) queries are allowed)
  ok    stacked statements rejected  (only one statement per call; remove the extra ';')
  ok    comment-hidden DROP rejected  (only one statement per call; remove the extra ';')
  ok    database still intact after attacks
path traversal is refused
  ok    ../ escape refused  ('../server.py' resolves outside the allowed root)
  ok    absolute path refused  ('/etc/passwd' resolves outside the allowed root)
get_weather (live network call)
  ok    resolves the location  (Delhi, National Capital Territory of Delhi, India)
  ok    returns a temperature  (29.2degC, overcast)

40 passed, 0 failed
```

---

## Tech

Python 3.10+ · `mcp` v2 (official SDK) · SQLite · stdlib `urllib` (no HTTP
dependency for the weather tool)

```
server.py            tool definitions + MCPServer wiring
toolkit_mcp/
├── database.py      read-only SQL, validation, schema introspection
├── files.py         sandboxed search and read
└── weather.py       Open-Meteo client
make_sample_db.py    seeded sample database
test_server.py       end-to-end test over real stdio MCP transport
sandbox/             what the file tools are allowed to see
```

> **Note on SDK versions:** `mcp` v2 renamed `FastMCP` to `MCPServer`
> (`from mcp.server.mcpserver import MCPServer`). Most tutorials online still
> show the v1 `FastMCP` import, which raises `ModuleNotFoundError` on v2. This
> repo targets v2.

---

## Building one for your stack

The pattern generalises. A tool is a decorated function with a clear
description and typed arguments — the description is what Claude reads to decide
whether to call it, so it's worth writing carefully. Common asks I can build:

- Query your internal Postgres/MySQL read replica safely
- Search and summarise a documentation or ticket archive
- Call your company's REST API with auth handled server-side
- Read and write to a Google Sheet or Notion database

---

## Hire me

I build MCP servers and Claude Code workflows — tested, documented, and
delivered fast.

- **Fiverr:** [My Fiverr profile](https://www.fiverr.com/harshgupta381)
- **Upwork:** [My Upwork profile](<UPWORK_PROFILE_URL>)
- **GitHub:** [github.com/harshhh817](https://github.com/harshhh817)

Final-year B.Tech CSE · AWS Certified Cloud Practitioner · Delhi, India

MIT licensed — see [LICENSE](LICENSE).