Skip to main content
Glama
MCP Server Toolkit

MCP Server Toolkit

by gupta93
README.md
# MCP Server Toolkit

Production-style [Model Context Protocol](https://modelcontextprotocol.io/) server demo in Python. It showcases how to ship practical tools, safe SQL access, external HTTP integrations, structured logging, tests, and CI—the kind of integration work you'd put in a freelance portfolio.

## Features

- **Four tools**: GitHub repo/issue summaries (public API), read-only SQL (SQLite demo dataset + optional Postgres), OpenAPI-driven HTTP calls
- **Transports**: stdio (Claude Desktop / Cursor) and **streamable HTTP**
- **Config**: Pydantic Settings via `MCP_TOOLKIT_*` environment variables
- **Observability**: structlog JSON or console logs
- **Quality**: pytest suite, Ruff lint/format, GitHub Actions CI, Dockerfile

## Architecture

```mermaid
flowchart TB
  subgraph clients [MCP Clients]
    Cursor[Cursor / Claude Desktop]
    HTTP[HTTP MCP client]
  end

  subgraph server [mcp-server-toolkit]
    CLI[CLI / entrypoint]
    MCP[MCPServer]
    GH[GitHub tools]
    SQL[SQL tool + safety layer]
    OAPI[OpenAPI HTTP tool]
    CFG[Pydantic Settings]
    LOG[structlog]
  end

  subgraph external [External systems]
    GHA[GitHub REST API]
    DB[(SQLite demo DB / Postgres)]
    APIs[Third-party HTTP APIs]
  end

  Cursor -->|stdio| CLI
  HTTP -->|streamable HTTP| CLI
  CLI --> MCP
  MCP --> GH
  MCP --> SQL
  MCP --> OAPI
  CFG --> MCP
  LOG --> MCP
  GH --> GHA
  SQL --> DB
  OAPI --> APIs
```

## Quickstart

### Requirements

- Python 3.11+
- Optional: `MCP_TOOLKIT_GITHUB_TOKEN` for higher GitHub rate limits (public repos work without it)
- Required for networked OpenAPI calls: `MCP_TOOLKIT_OPENAPI_ALLOWED_HOSTS`

### Install

```bash
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```

Copy `.env.example` to `.env` and adjust values if needed.

### Run (stdio)

```bash
mcp-server-toolkit --transport stdio
```

### Run (streamable HTTP)

```bash
mcp-server-toolkit --transport streamable-http --host 127.0.0.1 --port 8000
```

MCP endpoint: `http://127.0.0.1:8000/mcp` (override with `MCP_TOOLKIT_HTTP_PATH`).

### Docker

```bash
docker build -t mcp-server-toolkit .
docker run --rm -p 8000:8000 -v mcp-data:/data mcp-server-toolkit
```

The demo SQLite database is seeded once at startup into `MCP_TOOLKIT_DATA_DIR` (default `/data` in the container).

## Client configuration

### Cursor / Claude Desktop (stdio)

```json
{
  "mcpServers": {
    "mcp-server-toolkit": {
      "command": "mcp-server-toolkit",
      "args": ["--transport", "stdio"],
      "env": {
        "MCP_TOOLKIT_LOG_LEVEL": "INFO",
        "MCP_TOOLKIT_OPENAPI_ALLOWED_HOSTS": "httpbin.org"
      }
    }
  }
}
```

Use the full path to the virtualenv binary if `mcp-server-toolkit` is not on your `PATH`.

### Streamable HTTP (example)

Point your MCP client at `http://127.0.0.1:8000/mcp` when the server is running with `--transport streamable-http`.

## Tools

| Tool | Description |
|------|-------------|
| `summarize_github_repository` | Fetches repo metadata and lists recent open issues (`owner`, `repo`). Slugs must match `[A-Za-z0-9._-]`. |
| `summarize_github_issue` | Summarizes one issue (`owner`, `repo`, `issue_number`). |
| `run_readonly_sql` | Runs a validated `SELECT`/`WITH` against the **configured** database only (no per-call URL override). |
| `call_openapi_operation` | Loads an OpenAPI 3 spec (`spec_url` or `spec_text`) and calls an operation by `operationId`. Outbound HTTP requires an allowlist. |

### Database configuration

- **SQLite (default):** `MCP_TOOLKIT_DATABASE_URL=sqlite:///demo.db` seeds a demo database from packaged `seed.sql` into `MCP_TOOLKIT_DATA_DIR` on first startup. Queries open the file in SQLite **read-only** mode.
- **Postgres (optional):** install `mcp-server-toolkit[postgres]` and set `MCP_TOOLKIT_DATABASE_URL=postgresql://…`. Queries run in a **READ ONLY** transaction with `statement_timeout`.
- The SQL tool never accepts a caller-supplied connection string; change `MCP_TOOLKIT_DATABASE_URL` and restart the server instead.

### SQL safety

- Single statement only
- Must start with `SELECT` or `WITH`
- Blocks write/DDL/admin keywords (`INSERT`, `DROP`, `PRAGMA`, …)
- Enforces row caps with `SELECT * FROM (<query>) AS _mcp_toolkit_limited LIMIT n` so trailing comments or nested `LIMIT` clauses cannot bypass the cap (`MCP_TOOLKIT_MAX_SQL_ROWS`, default 100)

### OpenAPI outbound HTTP safety

- `MCP_TOOLKIT_OPENAPI_ALLOWED_HOSTS` (comma-separated) must include every host used by the spec URL and the operation URL
- Empty allowlist blocks all outbound OpenAPI HTTP until configured
- `http`/`https` only; redirects disabled; resolved IPs must not be private, loopback, or link-local
- Response bodies capped by `MCP_TOOLKIT_OPENAPI_MAX_RESPONSE_BYTES`; `Set-Cookie` headers are stripped from tool output
- **DNS rebinding:** hostnames are resolved and checked immediately before each request. This does not eliminate every time-of-check/time-of-use race against a malicious DNS resolver that changes answers between validation and connect; treat the allowlist as a guardrail for demo use, not a complete SSRF boundary on its own.

### Self-contained `httpbinGet` example

Set `MCP_TOOLKIT_OPENAPI_ALLOWED_HOSTS=httpbin.org`, then call `call_openapi_operation` with:

```json
{
  "operation_id": "httpbinGet",
  "spec_text": "{\"openapi\":\"3.0.3\",\"info\":{\"title\":\"Demo\",\"version\":\"1.0.0\"},\"servers\":[{\"url\":\"https://httpbin.org\"}],\"paths\":{\"/get\":{\"get\":{\"operationId\":\"httpbinGet\",\"responses\":{\"200\":{\"description\":\"OK\"}}}}}}",
  "query_params": {"greeting": "hello"}
}
```

### Example prompts

- "Summarize the GitHub repo `modelcontextprotocol/python-sdk`."
- "Run read-only SQL: total order quantity by product name from the demo database."
- "Call OpenAPI operation `httpbinGet` against httpbin with `greeting=hello`."

## Development

```bash
ruff check .
ruff format .
pytest -q
python -m mcp_server_toolkit --transport stdio
```

Postgres support is optional: `pip install mcp-server-toolkit[postgres]` and set `MCP_TOOLKIT_DATABASE_URL` to a `postgresql://` URL.

## License

MIT — see [LICENSE](LICENSE).