File size: 7,456 Bytes
ee0b555
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3ecc591
ee0b555
 
 
 
 
 
 
 
 
3ecc591
 
 
 
 
 
 
 
 
ee0b555
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# pwc MCP server

Anonymous, read-only Model Context Protocol access to the public
[Papers With Code](https://paperswithcode.co) catalog.

The server uses stock-client MCP `2025-11-25` over Streamable HTTP and also
serves experimental `2026-07-28` discovery on the same `/mcp` endpoint. It
returns versioned structured output with compact Markdown fallbacks.

## Run locally

```bash
uv run --project mcp_server pwc-mcp
```

The default listener is `127.0.0.1:7860`. Set `PWC_MCP_HOST`, `PORT`, or
`PWC_API_URL` to override the bind address, HTTP port, or compatible PwC API.

```bash
curl http://127.0.0.1:7860/health
```

## Tools

Every read-only `pwc` CLI research command is one tool, and every research flag
of that command is a tool parameter, so results match `pwc ... --json`. Each
result carries `schema_version` and `data`, the complete CLI JSON payload, next
to typed projections such as `items` or `evaluations`.

| Tool | CLI command |
| --- | --- |
| `search_papers` | `pwc search` |
| `get_paper_info` | `pwc paper info` |
| `get_paper_evaluations` | `pwc paper evaluations` |
| `read_paper` | `pwc paper read` (64 KiB chunks with a continuation cursor) |
| `list_papers` | `pwc paper list` |
| `list_recent_papers` | `pwc paper recent` |
| `list_trending_papers` | `pwc paper trending` |
| `get_related_papers` | `pwc paper related` |
| `get_paper_lineage` | `pwc paper lineage list` |
| `get_task` | `pwc task --name` |
| `list_tasks` | `pwc task list` |
| `get_method` | `pwc method --name` |
| `list_methods` | `pwc method list` |
| `get_conference` | `pwc conference --name` |
| `list_conferences` | `pwc conference list` |
| `get_organization` | `pwc organization --name` |
| `list_organizations` | `pwc organization list` |
| `get_framework` | `pwc framework --name` |
| `list_frameworks` | `pwc framework list` |
| `get_benchmark` | `pwc benchmark --name` |
| `list_benchmarks` | `pwc benchmark list` |

Parameter names follow the CLI flags except for the established MCP names
`published_after`/`published_before` (`--start-date`/`--end-date`), `limit`
(`--page-size`), `authors` (`--author`), `order_direction` (`--order-dir`),
`minimum_evaluations` (`--min-eval-count`), and `include_evaluations`
(`--include-evals`). Terminal-only flags (`--json`,
`--implementation-coverage`, `--flat`) have no parameter because MCP output is
always structured. Hosted differences from the CLI: `limit` is capped at 25,
`get_paper_info` returns compact official-first resources, and `read_paper`
is chunked. `search_papers` defaults to `hybrid` mode like `pwc search`.
`tests/test_parity.py` fails when the CLI parser and the tool schemas drift.

All tools are annotated read-only and idempotent. Search is deterministic;
the caller controls hybrid (default), keyword, or semantic mode. `read_paper`
fetches at most one 64 KiB catalog chunk per call and returns a signed, one-hour
continuation cursor when more Markdown remains. Continuations stay pinned to
the resolved paper and content version, so a changed paper fails with an
explicit restart response; any reference that resolves to the same paper (the
numeric catalog ID after starting from the arXiv ID, say) may carry the cursor.
A placeholder cursor such as `"next"` with nothing saved reads from the
beginning.

Paper tools fall back to the public API's `public-metadata` endpoint for papers
the catalog does not hold: `get_paper_info` and `read_paper` return Hugging Face
Papers, arXiv, or Crossref (DOI) metadata and the abstract with
`in_catalog: false`, a `note`, and labelled `available_at` links to cite instead
of a Papers With Code page; `get_related_papers` returns a catalog search for the
public title with the same `note` and links. A reference the endpoint maps to a
catalog paper returns the catalog paper. Titles go to the sibling
`public-metadata/search` endpoint, which accepts only an exact arXiv or Crossref
title match, or the single arXiv paper sharing the "Name:" before its colon.
`get_benchmark` answers a metric the leaderboard does not report with the
unfiltered leaderboard and a `metric_note` naming the available metrics.

## Resources

- `pwc://papers/{paper}`
- `pwc://papers/{paper}/markdown`
- `pwc://tasks/{task}`
- `pwc://benchmarks/{benchmark}`

## Configuration

| Variable | Purpose |
| --- | --- |
| `PWC_API_URL` | Compatible catalog API; defaults to production PwC |
| `PWC_MCP_HOST` | Listener address; defaults to loopback (`127.0.0.1`) |
| `PWC_MCP_ALLOWED_HOSTS` | Comma-separated HTTP Host allowlist |
| `PWC_MCP_ALLOWED_ORIGINS` | Comma-separated browser Origin allowlist |
| `PWC_MCP_CURSOR_KEY_CURRENT` | Required secret used to sign continuation cursors |
| `PWC_MCP_CURSOR_KEY_PREVIOUS` | Optional previous secret accepted during key rotation |
| `PORT` | HTTP port; defaults to `7860` |
| `PWC_MCP_REQUEST_LIMIT` | Requests per client per minute; defaults to `60` |
| `PWC_MCP_SEMANTIC_LIMIT` | Semantic or hybrid searches per client per minute; defaults to `10` |
| `PWC_MCP_CONCURRENCY_LIMIT` | Concurrent requests per client; defaults to `4` |
| `PWC_MCP_GLOBAL_CONCURRENCY_LIMIT` | Concurrent requests across all clients; defaults to `128` and sizes the tool thread pool |
| `PWC_MCP_TRUSTED_PROXY_CIDRS` | Comma-separated networks of a non-loopback reverse proxy whose `X-Forwarded-For` is trusted; empty by default |
| `LOG_LEVEL` | Content-free operational log level |

Native clients may omit `Origin`. Browser requests must match the configured
allowlist. A first-party client on the same host may send
`X-PwC-MCP-Client: <token>` to name its rate-limit identity (for example one
hashed chat session); the header counts only on a direct loopback connection
without `X-Forwarded-For`, so proxied public traffic cannot use it. The server does not log queries, paper references, request bodies,
raw IP addresses, or authorization headers.

Behind a reverse proxy that does not run on the same host, such as the Hugging
Face Spaces ingress, set `PWC_MCP_TRUSTED_PROXY_CIDRS` to the proxy's networks.
A request whose direct peer falls in one of them is limited by the right-most
`X-Forwarded-For` hop outside those networks; hops further left are
client-supplied and ignored. Such requests can never name an identity with
`X-PwC-MCP-Client`. Loopback peers keep the behaviour above. List only
networks that hold nothing but the proxy: any peer inside them can choose its
own rate-limit identity.

The hosted defaults allow 60 total requests and 10 semantic or hybrid searches per minute
per client IP, with at most four concurrent requests per IP and 128 globally.
Tool inputs cap list results at 25, catalog calls time out after 25 seconds, and
request, upstream, and serialized MCP response bodies are bounded to 2 MiB.
The global ceiling is 128 concurrent requests by default.
Markdown chunks use a bounded 256-entry/16 MiB in-memory cache.

`GET /.well-known/mcp` exposes connection metadata and `GET /docs` publishes
the live tool schema. A bare `GET /mcp` returns `405`; protocol requests use
`POST /mcp`. The server also exposes `find_papers`, `compare_leaderboard`, and
`survey_task` prompts.

## Test

```bash
uv run --project mcp_server pytest mcp_server/tests
```

The canonical hosted service is deployed as an immutable VPS component and
published through `https://paperswithcode.co/mcp`. The
`huggingface/paperswithcode-mcp` Docker Space remains an optional fallback; its
container explicitly binds to `0.0.0.0`.