Skip to main content
Glama
README.md
# PEP MCP Server

MCP server that exposes on-demand Python PEP lookup tools backed by the live
PEP index.

## Features

- `list_peps`: list only active PEPs
- `search_peps`: search active PEP titles
- `get_pep`: fetch a PEP document by number, optionally returning focused
  excerpts for a query

## Data Sources

- PEP index JSON: `https://peps.python.org/api/peps.json`
- PEP content:
  - `https://raw.githubusercontent.com/python/peps/main/peps/pep-XXXX.rst`
  - Fallback: `https://github.com/python/peps/blob/main/peps/pep-XXXX.rst?plain=1`

## Setup

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

## Run

```bash
.venv/bin/pep-mcp-server
```

or:

```bash
.venv/bin/python -m pep_mcp_server
```

## Docker

Build and tag:

```bash
docker build -t pep-mcp-server:latest .
```

MCP uses stdio, so the container must keep stdin open (`-i`). Example:

```bash
docker run --rm -i pep-mcp-server:latest
```

### Cursor MCP (Docker)

**You do not put PEP or GitHub URLs in `mcp.json`.** Cursor only needs the command that runs the server; listing and fetching PEPs happens inside the process when tools run.

Use `-i` (required for stdio). Optional `-e` lines silence the startup banner and pin transport:

```json
{
  "mcpServers": {
    "pep": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "-e",
        "FASTMCP_TRANSPORT=stdio",
        "-e",
        "FASTMCP_SHOW_SERVER_BANNER=false",
        "pep-mcp-server:latest"
      ]
    }
  }
}
```

If the image is not on this machine yet, build it once from the project directory (see above).

If the MCP log shows `Found 0 tools` but `listOfferingsForUI` / `Not connected` warnings, that is often a Cursor UI race or a separate UI listing path; try reloading the window or invoking a tool from chat. The server still exposes three tools over stdio (verified with the MCP Python client).

## Tool Contracts

### `list_peps() -> list[dict]`

Returns active PEPs with:
- `number`
- `title`
- `type`
- `topic`
- `created`
- `url`

(`status` is omitted; every row is active.)

### `search_peps(query: str) -> list[dict]`

Case-insensitive substring search on active PEP titles.

### `get_pep(pep, query=None, max_full_content_chars=None) -> dict`

- Accepts `8`, `0008`, or `pep-0008`.
- Returns metadata and:
  - `content` when `query` is not provided (capped by default for token efficiency)
  - `excerpt` when `query` is provided and matches
  - `content` fallback when `query` has no matches (also capped by default)
- Optional `max_full_content_chars`: omit or `None` for the default cap; use `0` for the full document (can be very large).

## Tests

```bash
.venv/bin/pytest -q
```

TDQS

A3.8/5.0

Scored across 3 tools

Disambiguation5/5

The three tools have clearly distinct purposes: 'get' retrieves a specific PEP by number, 'list' enumerates all active PEPs, and 'search' finds PEPs by title query. No functional overlap exists between them.

Naming Consistency5/5

All tools follow a consistent snake_case verb_noun pattern (get_pep, list_peps, search_peps). The pluralization appropriately matches the return cardinality (singular for single retrieval, plural for collection operations).

Tool Count5/5

Three tools is the ideal minimum for a read-only document server, covering the essential access patterns: enumeration, search, and specific retrieval. The scope is tightly focused without bloat.

Completeness3/5

While the basic read operations are present, notable gaps exist: list_peps and search_peps are restricted to 'active' PEPs only with no way to access other statuses, search_peps only searches titles (not content or authors), and there are no filtering options for metadata like author or Python version.

Maintenance

ActivityInactive
ResponsivenessNo issues