patent-mcp
# patent-mcp
特許情報を取得して Agent にコンテキストとして渡すための、ローカル実行の MCP サーバー。
データソースはインターフェースの背後に隠してあるので、あとから
Google Patents → 特許庁 API → EPO OPS のように差し替え・追加ができる。
ツールも `tools/` にファイルを置くだけで増える。
## ツール
| ツール | 用途 |
| --- | --- |
| `search_patents` | キーワード・出願人・発明者・期間で特許を検索する |
| `find_similar_patents` | 既存特許や自由文(アイデア・請求項案)に似た特許を探す |
| `get_patent` | 公開番号を指定して要約・請求項・明細書・分類・引用・ファミリーを取得する |
| `list_patent_providers` | 利用可能なデータソースと対応機能を確認する |
## セットアップ
```bash
uv sync
```
動作確認:
```bash
uv run patent-mcp --help
```
## エディタ / エージェントへの登録
以下、リポジトリの絶対パスを `/path/to/patent-mcp` と表記する(`pwd` で確認できる)。
`uv` のフルパスは `which uv` で確認する(通常 `~/.local/bin/uv`)。
### Claude Code
```bash
claude mcp add patent -- uv --directory /path/to/patent-mcp run patent-mcp
```
### Claude Desktop
`claude_desktop_config.json` に追加:
```json
{
"mcpServers": {
"patent": {
"command": "uv",
"args": ["--directory", "/path/to/patent-mcp", "run", "patent-mcp"],
"env": { "PATENT_MCP_LANG": "ja" }
}
}
}
```
### GitHub Copilot(VS Code)
VS Code の Copilot Chat(Agent モード)は MCP サーバーに対応している。
ワークスペース直下に `.vscode/mcp.json` を作る(チーム共有したくない場合は
`.vscode/mcp.json` を `.gitignore` に入れるか、ユーザー設定側の MCP 設定に書く)。
```json
{
"servers": {
"patent": {
"type": "stdio",
"command": "uv",
"args": ["--directory", "/path/to/patent-mcp", "run", "patent-mcp"],
"env": { "PATENT_MCP_LANG": "ja" }
}
}
}
```
有効化手順:
1. VS Code でこのリポジトリ(または `.vscode/mcp.json` を置いたワークスペース)を開く
2. コマンドパレットで `MCP: List Servers` を実行し、`patent` が認識されているか確認(表示されない場合は `MCP: Add Server` から同じ内容を登録する)
3. Copilot Chat を **Agent モード**にする(チャット入力欄のモード切り替え)
4. ツール一覧(レンチ/ツールアイコン)に `search_patents` などが並んでいることを確認して使う
ユーザー全体で使い回したい場合は、ワークスペースではなくコマンドパレットの
`MCP: Open User Configuration` から同じ内容をユーザー設定側に書いてもよい。
VS Code の MCP サポートは変更が速いので、上記でうまくいかない場合は
VS Code 側の最新ドキュメント(Copilot Chat の MCP servers)を確認すること。
## 設定(環境変数)
| 変数 | 既定値 | 説明 |
| --- | --- | --- |
| `PATENT_MCP_PROVIDER` | `google_patents` | 使用するデータソース |
| `PATENT_MCP_LANG` | `ja` | 本文の言語 |
| `PATENT_MCP_TIMEOUT` | `30` | HTTP タイムアウト(秒) |
| `PATENT_MCP_MAX_RESULTS` | `25` | 1回の検索で返す上限 |
| `PATENT_MCP_MAX_SECTION_CHARS` | `20000` | 1セクションの最大文字数 |
プロバイダ固有の設定は `PATENT_MCP_<プロバイダ名>_<キー>` で渡す。
例: `PATENT_MCP_EPO_OPS_KEY=xxx` → `settings.options_for("epo_ops") == {"key": "xxx"}`
`google_patents` プロバイダ固有:
| 変数 | 既定値 | 説明 |
| --- | --- | --- |
| `PATENT_MCP_GOOGLE_PATENTS_MIN_INTERVAL` | `1.0` | リクエスト間の最小間隔(秒) |
| `PATENT_MCP_GOOGLE_PATENTS_MAX_RETRIES` | `3` | 429/503 を受けたときの再試行回数 |
Google Patents は短時間に連続アクセスすると 503 を返す。
まとめて多数の特許を調べる用途では `MIN_INTERVAL` を 2〜3 秒に上げるとよい。
## 使用例(Agent 側の会話)
- 「全固体電池の界面抵抗低減について、2022年以降の日本の特許を10件調べて」
- 「JP2020123456A に似た特許を、優先日が2019年より前のものに限って探して」
- 「この請求項案に近い先行技術がないか調べて: 〈本文〉」
- 「US9634296B2 の請求項1と、引用されている文献を出して」
## HTML構造の変更をどう検知するか
既定のプロバイダは Google Patents の HTML を読むので、Google が構造を変えると壊れる。
やっかいなのは**例外を出さずに中身だけ欠ける**壊れ方で、放っておくと Agent が
「請求項の無い特許」と誤解して結論を出す。3段構えで検知する。
**1. 実行時の自動チェック(常に効く)**
毎回の取得でパースの妥当性を自己点検し、怪しければ結果の冒頭に警告を出す。
- どの特許にも必ずあるはずのもの(タイトル・要約・請求項・明細書)が空
- セクションは存在するのに中身が抽出できない
- **ページが書いている請求項の件数と、抽出できた件数が食い違う**
```
# JP2020123456A — 蓄電素子、蓄電素子の製造方法
> **取得できていない情報があります**
> - 請求項: ページには16項とあるのに2項しか抽出できていません。内容が欠けています。
>
> この結果だけで「存在しない」と判断せず、元ページを確認してください: ...
```
引用やファミリーは本当に0件のことがあるので対象外。誤検知しない範囲に絞ってある。
**2. ライブ契約テスト(触ったときに実行)**
```bash
uv run python -m pytest -m network
```
実ページに接続し、パーサが依存する11個のセレクタが今も存在するか確認する。
**3. オフラインテスト(編集時の回帰防止)**
HTML をわざと壊して、上記1の検知が働くことまでテストしている。
## テスト
網羅は狙っていない。このプロジェクトのバグは実データで動かしたときにしか
見つからなかったので、テストは「一度やらかした罠の記録」と
「Google の HTML が変わったことの検知」に絞ってある。
```bash
uv run python -m pytest # オフライン20件
uv run python -m pytest -m network # ライブ契約テスト
```
| ファイル | 役割 |
| --- | --- |
| `test_parse_gotchas.py` | 実際に壊れたパースの再発防止(請求項の潰れ、二重エンコード等) |
| `test_number_formats.py` | 「特開2020-123456」等の表記ゆれ吸収 |
| `test_provider_swap.py` | データソース差し替えの前提が壊れていないこと |
| `test_live_contract.py` | **実ページに接続**し、依存セレクタが今も存在するか確認 |
`tests/fixtures/patent_page.html` は実ページのダウンロードではなく、
**構造だけを再現した手書きのファイル**。実ページを置かないのは、
他社の著作物を取り込むことになるうえ、Google 自身のウィジェット用 API キーが
埋まっていてシークレットスキャナが誤検知するため。
手書きなので「パーサを編集して壊したか」は分かるが、
「そもそも Google の HTML を誤解していたか」は分からない。
そこは `-m network` のライブ契約テストが実物に当てて確認する。
セレクタを触ったときや、パース結果が変になったときに実行すること。
ライブ契約テストは既定でスキップされる(レート制限を避けるため)。
レート制限中も失敗ではなくスキップになるので、時間をおいて再実行する。
## 拡張する
### データソースを足す
`src/patent_mcp/providers/` にファイルを1つ作るだけ。
```python
from . import register
from .base import Capability, PatentProvider, SearchQuery
from ..models import SearchResult
@register
class JpoProvider(PatentProvider):
name = "jpo"
description = "特許庁 特許情報取得API"
capabilities = frozenset({Capability.SEARCH, Capability.BIBLIOGRAPHIC})
requires_credentials = True
async def search(self, query: SearchQuery) -> SearchResult:
token = self.options["token"] # PATENT_MCP_JPO_TOKEN
...
```
未実装のメソッドは基底クラスが `NotSupportedError` を投げ、ツールが
「このプロバイダは未対応」と Agent に伝える。登録リストの編集は不要
(`providers/__init__.py` がモジュールを自動検出する)。
`PATENT_MCP_PROVIDER=jpo` で既定を切り替えられるほか、
各ツールの `provider` 引数で呼び出しごとに切り替えられる。
### ツールを足す
`src/patent_mcp/tools/` にファイルを1つ作り、`register` を定義する。
```python
def register(mcp, ctx):
@mcp.tool()
async def my_tool(...) -> str:
"""Agent 向けの説明。"""
provider = ctx.provider()
...
```
## 注意
既定の `google_patents` プロバイダは Google Patents の公開ページを読む非公式実装で、
サイト構造の変更やレート制限の影響を受ける。業務で確実性が必要な場合は、
特許庁の特許情報取得API や EPO OPS のプロバイダを追加して切り替えること。
取得できるのは公開情報であり、権利範囲の解釈や侵害判断は専門家の確認が必要。
TDQS
Scored across 4 tools
Each tool has a distinct purpose: provider metadata, direct retrieval by number, keyword search, and similarity search. Any potential overlap between search_patents and find_similar_patents is clearly resolved through descriptions of input types and use cases.
All tool names follow the consistent verb_noun pattern (list_, get_, search_, find_) with snake_case. The pattern is predictable and makes the tool set easy to navigate.
Four tools is an ideal size for this domain. Each tool is necessary and covers a core aspect of patent search and retrieval without redundancy or bloat.
The set covers the full workflow of patent discovery and examination: find providers, search, retrieve specific patents, and find similar ones. Minor gaps like legal status or batch export exist but are not critical for typical patent research tasks.