Fetch Patents
БесплатноНе проверенEnables fetching patents from multiple global sources by patent ID, returning PDF, Markdown, and metadata with automatic caching.
Описание
Enables fetching patents from multiple global sources by patent ID, returning PDF, Markdown, and metadata with automatic caching.
README
The last patent data tool your agent will ever need.
Give any AI agent instant access to the entire global patent corpus. One MCP tool call. Throw any patent ID at it — US, EP, WO, JP, CN, KR, AU, CA, NZ, BR, IN — in any format, with or without kind codes, even raw Google Patents URLs. Get back PDF, Markdown, and structured metadata in seconds. Ask for it again? Instant, from cache.
Your agent shouldn't have to know that US patents live on USPTO PPUBS, European ones require EPO OPS OAuth2, and PCT applications need to be scraped from WIPO PatentScope. It shouldn't care that Espacenet has different HTML than CIPO, or that Google Patents needs a headless browser. This server ate all of that complexity so your agent never has to think about it.
"Fetch US7654321, EP1234567B1, and WO2024/123456"
→ 3 PDFs, 3 Markdown files, 3 metadata objects — cached forever
9 patent sources. 4 PDF-to-Markdown converters with OCR. Two-layer SQLite cache. Automatic retry with exponential backoff. Web search fallback when everything else fails. Zero configuration required — works out of the box with 6 of 9 sources.
Why this exists
Patent data is the most fragmented public dataset on the planet. Every national patent office has its own API, auth scheme, document format, and rate limits. An agent doing patent analysis shouldn't need to know any of that — it should just say "get me this patent" and get it.
9 patent sources, 1 tool call:
| Source | Coverage | Auth required? |
|---|---|---|
| USPTO PPUBS | US granted + applications | No (session-based) |
| EPO OPS | EP, WO, 100+ offices via exchange data | Yes (OAuth2) |
| Espacenet | EP + EPO member states | No (scraped) |
| WIPO PatentScope | WO / PCT international | No (scraped) |
| IP Australia | AU patents | No (REST API) |
| CIPO | Canadian patents | No (scraped) |
| Google Patents | All jurisdictions | No |
| Google BigQuery | Bulk patent data | Yes (GCP credentials) |
| Web search fallback | Anything missed | No (DuckDuckGo) / Optional (SerpAPI) |
Sources are tried in priority order. First success wins (unless you set PATENT_FETCH_ALL_SOURCES=true to aggregate from all). If every structured source fails, the web search fallback finds the PDF anyway. We really don't like returning empty-handed.
Quick start
Install
# Rust (recommended)
cargo install patent-mcp-server
# or build from source:
cargo build --release --manifest-path src/rust/Cargo.toml
# Python (reference implementation, also works standalone)
pip install patent-mcp-server
Configure your MCP client
Add to your Claude Desktop, Claude Code, Cursor, or Cline config:
{
"mcpServers": {
"patents": {
"command": "patent-mcp-server"
}
}
}
That's the whole setup. No API keys needed — 6 of 9 sources work without auth (USPTO, Espacenet, WIPO, IP Australia, CIPO, DuckDuckGo). Add keys later to unlock EPO OPS, BigQuery, and SerpAPI. See docs/api-keys.md.
For an installed Rust server, secrets can live in ~/.patents.toml, normal environment variables, or ~/.patents-mcp.env autoloaded by the server. A repo-local .env is only useful when the server is launched from that checkout.
If your MCP client launches stdio servers inside a sandboxed subprocess, use the localhost HTTP transport instead:
patent-mcp-server serve-http
Then configure the client to connect to:
http://127.0.0.1:38473/mcp
The repo pins the Python HTTP endpoint to /mcp, and the Rust server uses the same default URL. The shared contract today is that URL and localhost-only binding, not byte-for-byte HTTP transport parity.
Use it
"Fetch patents US7654321 and EP1234567B1, then summarize the key claims."
The agent calls fetch_patents, gets back file paths and metadata, reads the Markdown, and does its thing. You don't configure sources. You don't pick formats. You don't manage cache. It just works.
Tools
fetch_patents
Fetch one or more patents by ID. Accepts any format — bare numbers, jurisdiction-prefixed, with kind codes, even Google Patents URLs.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
patent_ids |
string[] |
Yes | Patent IDs in any format |
force_refresh |
bool |
No | Bypass cache, re-fetch from sources |
formats |
string[] |
No | Requested formats (default: ["pdf", "txt", "md"]) |
postprocess_query |
string |
No | Reserved for v2 post-processing |
Response:
{
"results": [
{
"canonical_id": "US7654321",
"success": true,
"from_cache": false,
"files": {
"pdf": "~/.local/share/patent-cache/patents/US7654321/US7654321.pdf",
"md": "~/.local/share/patent-cache/patents/US7654321/US7654321.md"
},
"metadata": {
"title": "Method and apparatus for ...",
"inventors": ["Jane Doe", "John Smith"],
"assignee": "Acme Corp",
"filing_date": "2005-03-15",
"publication_date": "2010-02-02",
"jurisdiction": "US",
"doc_type": "patent"
},
"sources": [
{"source": "USPTO", "success": true, "elapsed_ms": 1842}
]
}
],
"summary": {
"total": 1,
"success": 1,
"cached": 0,
"errors": 0,
"total_duration_ms": 2105
}
}
list_cached_patents
List all cached patents. No parameters, returns {patents: [...], count: N}.
get_patent_metadata
Cache-only metadata lookup — no network calls, instant response.
Parameters: patent_ids: string[]
Returns: {results: [{patent_id, canonical_id, metadata}]}
Search tools (patent-search MCP server)
The search server (python -m patent_mcp.search) provides tools for natural language patent search, structured queries, citation chains, and research sessions.
It also supports localhost Streamable HTTP:
python -m patent_mcp.search serve-http
Search startup includes a best-effort browser prewarm only when the optional browser dependencies are installed. It is safe to ignore if unavailable, and it does not guarantee that later search calls reuse the same browser/profile.
patent_search_natural
Search using plain English. The planner expands your description into multiple query variants using keyword/synonym expansion, runs them against Google Patents (browser and/or SerpAPI), and reranks results.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
description |
string |
Yes | Natural language description of the technology |
date_cutoff |
string |
No | ISO date — only return patents before this date |
jurisdictions |
string[] |
No | Filter by jurisdiction (e.g. ["US", "EP"]) |
session_id |
string |
No | Auto-save results to a research session |
max_results |
int |
No | Maximum results after ranking (default 25) |
backend |
string |
No | "browser", "serpapi", or "auto" (default) |
enrich_top_n |
int |
No | Enrich top N hits with full metadata (default 5) |
Other search tools
patent_search_structured— Expert Boolean query syntax against USPTO, EPO OPS, Google Patentspatent_citation_chain— Follow citations forward/backward (depth 1-3)patent_classification_search— Search by IPC/CPC codepatent_family_search— Find patent family members across jurisdictionspatent_suggest_queries— Generate search strategy without executingpatent_search_profile_login_start— Launch headed browser for Google login
Research sessions
Sessions persist search results across multiple tool calls:
patent_session_create— Start a new research sessionpatent_session_load/list/note/annotate— Manage session statepatent_session_export— Generate Markdown report
Patent ID formats
Don't worry about formatting. The canonicalizer has seen it all:
| Input | Canonical form | Jurisdiction |
|---|---|---|
US7654321 |
US7654321 |
US |
US7654321B2 |
US7654321 |
US (with kind code) |
US20230001234A1 |
US20230001234 |
US application |
7654321 |
US7654321 |
US (inferred) |
EP1234567B1 |
EP1234567 |
EP |
WO2024/123456 |
WO2024/123456 |
International (PCT) |
JP2023-123456 |
JP2023123456 |
Japan |
CN202310001234A |
CN202310001234 |
China |
KR10-1234567 |
KR10-1234567 |
South Korea |
AU2023123456 |
AU2023123456 |
Australia |
CA3123456 |
CA3123456 |
Canada |
https://patents.google.com/patent/US7654321/en |
US7654321 |
(extracted) |
Also handles NZ, BR, and IN formats. 22+ patterns total. If it looks like a patent number, we'll figure it out.
How it works
Agent MCP Server (Rust)
│ │
├─ fetch_patents ──────────►│
│ ├─ cache lookup (SQLite)
│ │ HIT? return immediately
│ │ MISS?
│ │ ├─ try sources in priority order
│ │ │ USPTO → EPO → Espacenet → WIPO → ...
│ │ ├─ download PDF (async, concurrent)
│ │ ├─ convert PDF → Markdown
│ │ │ (pymupdf4llm → pdfplumber → pdftotext → marker)
│ │ ├─ extract metadata
│ │ └─ store in cache
│◄──────────────────────────┤
│ files + metadata │
Cache: Single global SQLite DB at ~/.local/share/patent-cache/index.db shared across all projects. Patent files live in ~/.local/share/patent-cache/patents/{CANONICAL_ID}/. First fetch takes seconds. Every subsequent fetch is instant — even across different repos.
HTTP transport: Rust, Python fetch, and Python search can each run on localhost HTTP at the default URL http://127.0.0.1:38473/mcp when launched with serve-http. Stdio remains the default when you launch a server without serve-http. The implementations share the same default URL and localhost-only bind, but this repo does not currently claim wire-level Rust/Python HTTP parity.
Activity journal: Each tool call is logged to .patent-activity.jsonl in the current working directory (JSONL format). This file is meant to be git-tracked so each project retains a record of what was searched, fetched, and accessed. Disable with PATENT_ACTIVITY_JOURNAL="".
PDF → Markdown: Four converter backends tried in order (pymupdf4llm → pdfplumber → pdftotext → marker). If one fails, the next picks up. Tables extracted and merged. OCR via tesseract for scanned patent figures. The output is clean enough for an LLM to read directly.
Dual implementation, cross-verified: Rust is the production server — standalone, async, with native HTTP fetchers and retry logic. Python is the reference implementation. 39 cross-implementation parity tests verify both produce identical results for ID canonicalization, source ordering, converter output, and web search queries. When we say they match, we mean it — it's tested.
Configuration
All config via autoloaded env files, ~/.patents.toml, or environment variables (env vars take precedence). The Rust server autoloads ~/.patents-mcp.env and then .env from the current working directory before reading ~/.patents.toml and explicit environment variables.
| Env var | Default | Description |
|---|---|---|
PATENT_CACHE_DIR |
~/.local/share/patent-cache/patents |
Cache directory for patent files |
PATENT_CONCURRENCY |
10 |
Max concurrent fetches |
PATENT_TIMEOUT |
30.0 |
HTTP timeout (seconds) |
PATENT_EPO_KEY |
— | EPO OPS client_id:client_secret |
PATENT_LENS_KEY |
— | Lens.org API key |
PATENT_SERPAPI_KEY |
— | SerpAPI key (web search fallback) |
PATENT_BIGQUERY_PROJECT |
— | GCP project for BigQuery source |
PATENT_FETCH_ALL_SOURCES |
true |
Try all sources even after first success |
PATENT_ACTIVITY_JOURNAL |
.patent-activity.jsonl |
Per-repo activity journal (empty = disabled) |
PATENT_SEARCH_BACKEND_DEFAULT |
browser |
Default search backend (browser, serpapi, auto) |
PATENT_SEARCH_BROWSER_HEADLESS |
true |
Run Playwright in headless mode |
PATENT_SEARCH_BROWSER_IDLE_TIMEOUT |
1800 |
Browser idle timeout in seconds (default 30 min) |
PATENT_SEARCH_BROWSER_MAX_PAGES |
3 |
Max Google Patents result pages per query |
PATENT_SEARCH_ENRICH_TOP_N |
5 |
Enrich top N search results with full metadata |
Development
Running tests
# Rust tests (237 tests, <0.1s)
cargo test --manifest-path src/rust/Cargo.toml
# Start the Rust server over localhost HTTP
just serve-rust-http
# Start the Python fetch server over localhost HTTP
just serve-http
# Start the Python search server over localhost HTTP
just serve-search-http
# Direct stdio MCP smoke test against the Rust dev server
just mcp-smoke-rust
# Direct stdio MCP smoke test against the installed Rust binary
just mcp-smoke-rust-installed
# Fast Python unit tests (<1s, all I/O mocked)
pytest tests/python/ -m "not browser and not integration and not slow"
# Full Python suite (includes fuzz tests via Hypothesis, slow tests)
pytest tests/python/
# Cross-implementation parity — verifies Python == Rust (39 tests)
pytest tests/cross_impl/
# Manual E2E tests — automated script (31 tests)
python3 run_manual_e2e.py
Project structure
src/
python/patent_mcp/ # Python reference implementation
id_canon.py # Patent ID canonicalization (22+ formats)
cache.py # Dual-layer SQLite cache
config.py # TOML + env var config loading
fetchers/
http.py # All HTTP/API source implementations
web_search.py # DuckDuckGo / SerpAPI fallback
orchestrator.py # Priority-ordered source orchestration
converters/ # PDF → Markdown (4 backends + OCR)
server.py # MCP server (FastMCP, stdin/stdout)
rust/ # Rust production server (standalone)
src/
id_canon/ # Canonicalization (must match Python)
cache/ # Two-layer SQLite cache (rusqlite)
config/ # TOML + env var config loading
converters/ # PDF → Markdown (4 backends + OCR)
fetchers/
http/ # 9 native async HTTP sources with retry
web_search/ # DuckDuckGo + SerpAPI fallback
browser.rs # Native Google Patents HTML/microdata fetcher
mod.rs # Priority-ordered source orchestrator
server/ # JSON-RPC 2.0 over stdin/stdout
tests/
python/ # 16 test files: unit, integration, fuzz
cross_impl/ # Python == Rust parity tests
fixtures/ # Shared test data
docs/
api-keys.md # API key setup for each source
What makes this different
Most patent tools give you an API wrapper for one database. This gives your agent the entire global patent system behind a single function call. It handles the authentication, the format differences, the fallbacks, the caching, and the PDF-to-text conversion. Your agent asks for a patent and gets back something it can read. That's it. That's the product.
MCP Debugging
If your editor or MCP client seems to be using a stale server process, test the Rust server directly over stdio JSON-RPC instead of guessing whether the client reloaded:
# Current checkout / dev binary
just mcp-smoke-rust
# Installed binary from ~/.cargo/bin
just mcp-smoke-rust-installed
# Different patent
just mcp-smoke-rust US7654321B2
These recipes send initialize and tools/call requests straight to the server process, so they verify the real binary behavior without depending on Claude/OpenCode reload state.
License
CC0 1.0 Universal
Установить Fetch Patents в Claude Desktop, Claude Code, Cursor
unyly install mcp-fetch-patentsСтавит в Claude Desktop, Claude Code, Cursor и VS Code — сам разбирается с npx, uvx и сборкой из исходников.
Впервые? Поставь CLI: curl -fsSL https://unyly.org/install | sh
Или настроить вручную
Выполни в терминале:
claude mcp add mcp-fetch-patents -- uvx patent-mcp-serverFAQ
Fetch Patents MCP бесплатный?
Да, Fetch Patents MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Fetch Patents?
Нет, Fetch Patents работает без API-ключей и переменных окружения.
Fetch Patents — hosted или self-hosted?
Доступен hosted-вариант: Unyly запускает сервер в облаке, локальная установка не обязательна.
Как установить Fetch Patents в Claude Desktop, Claude Code или Cursor?
Открой Fetch Patents на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
GitHub
PRs, issues, code search, CI status
автор: GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
автор: mcpdotdirectCompare Fetch Patents with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development
