willmehr について
willmehrは、オーストリアのwillhaben.atで出品を検索し、検索結果の中央値より安い品を順位付けするMCPサーバーです。公開検索はアカウント不要で、セッションCookieが必要なアカウント機能はローカルのfullモードでのみ使えます。
主な機能
- 01構造化フィルターで検索し自動ページ送り
- 02説明・画像・出品者情報を取得
- 03結果の中央値より安い出品を順位付け
- 04カテゴリーとブランドのフィルターを検出
- 05プロフィール・ウォッチリスト・会話を確認
活用シーン
- 01地域やカテゴリーで中古品を探す
- 02市場中央値より安い出品を見つける
- 03候補出品の詳細と出品者情報を確認
- 04保存した出品や最近の会話を確認
MCP 対応
MCPクライアントにwillhaben.atの検索、出品詳細、価格分析、アカウント用ツールを提供します。ローカルstdioとセルフホストのStreamable HTTPに対応し、アカウント用ツールにはローカルfullモードでセッションCookieが必要です。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
npx -y willmehrWILLMEHR_MODE'full' (default) registers the account tools; 'public' runs anonymously, reads no credential and paces requests for a shared IP.WILLHABEN_COOKIEシークレットSession cookie from a logged-in willhaben browser session, needed only by the willhaben_my_* tools. Must contain BBX_JSESSIONID and x-bbx-csrf-token. Sessions expire within roughly half an hour.WILLHABEN_MIN_INTERVAL_MSMinimum spacing between requests to willhaben, in milliseconds.ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
5掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| pages | integer | いいえ | 取得する検索結果ページ数。1 ページあたり約 25 件です。デフォルトは 1、最大は 5 です。 |
| query | string | いいえ | 検索語。例:'ThinkPad T14'、'Fahrrad 28 Zoll'。 |
| location | string | いいえ | 都市名またはドイツの郵便番号。例:'Berlin'、'10115'。 |
| maxPrice | integer | いいえ | 最高価格(EUR) |
| minPrice | integer | いいえ | EUR 建ての最低価格 |
| radiusKm | integer | いいえ | `location` 周辺の検索半径(キロメートル単位) |
| publishedAfter | string | いいえ | このローカル ISO 8601 日時以降に公開された出品のみを対象とします。例:'2026-08-01T00:00:00'。より古い出品が現れた時点でページ取得も停止します。 |
詳細
willmehr
ドイツ語圏のクラシファイドサイトでお得な商品を探すための MCP サーバーです。TypeScript 製で、ローカルでは stdio 経由、セルフホスト環境では Streamable HTTP 経由で動作し、Claude Code、Claude Desktop、その他の MCP クライアントで利用できます。
このリポジトリからは、個別に登録される 2 つのサーバーが提供されます。
| サーバー | サイト | エントリーポイント |
|---|---|---|
willmehr | willhaben.at(オーストリア) | dist/index.js |
kleinanzeigen-mcp | kleinanzeigen.de(ドイツ) | dist/kleinanzeigen/index.js |
npm パッケージは willmehr と kleinanzeigen-mcp の 2 つとして公開され、同じバージョンでこのリポジトリから同時にリリースされます。
willhaben サーバーは、HAR キャプチャーをもとに willhaben ウェブアプリ独自の JSON API をリバースエンジニアリングして作成されました。Kleinanzeigen サーバーは jnslmk/kleinanzeigen-mcp の移植版です。詳しくは、以下の Kleinanzeigen をご覧ください。
クイックスタート
claude mcp add willmehr -- npx -y willmehr
claude mcp add kleinanzeigen -- npx -y kleinanzeigen-mcp
または、開発やセルフホストに適した、リポジトリをクローンする方法もあります。
git clone https://github.com/taneron/willmehr.git && cd willmehr
npm install && npm run build
claude mcp add willmehr -- node "$PWD/dist/index.js"
claude mcp add kleinanzeigen -- node "$PWD/dist/kleinanzeigen/index.js"
どちらの方法でも、ローカルで両方のサーバーを full モードで利用できます。両方とも公式 MCP レジストリに io.github.taneron/willmehr と io.github.taneron/kleinanzeigen として掲載されているため、レジストリを参照するクライアントであれば、この README がなくても見つけられます。willhaben のアカウント用ツールには、ご自身のセッションクッキーが必要です。認証をご覧ください。それ以外は匿名で利用でき、セットアップは不要です。
共有 HTTP サーバーとしてご自身のマシンで実行する方法は、ホスティングをご覧ください。このプロジェクトから、他者のインスタンスに情報が送信されることはありません。
モード
両方のサーバーは、2 つのモードのいずれかで動作します。コードとバイナリは同じで、起動時に --public / --full または WILLMEHR_MODE でモードを選択します。デフォルトは full です。
full(デフォルト) | public | |
|---|---|---|
| willhaben アカウント用ツール | 登録される | 登録されない |
.env / WILLHABEN_COOKIE | 読み込まれる | 読み込まれない。stderr に警告を出して無視される |
| willhaben リクエストの間隔 | 400 ms | 900 ms |
| Kleinanzeigen の間隔 / ページ数 / バッチサイズ | 800 ms / 5 / 20 | 1500 ms / 3 / 10 |
他の人向けに安全にホストできるのは、public モードだからです。プロセスは認証情報を保持せず、それを使用する可能性のあるツールも登録しません。そのため、共有環境から読み込まれていないセッションが漏れることはありません。リクエスト間隔を広げることも重要です。public モードでは全ユーザーが 1 つの送信元 IP を共有し、両サイトとも IP 単位でレート制限を行います。
node dist/index.js --public # willhaben, anonymous
WILLMEHR_MODE=public node dist/kleinanzeigen/index.js
アカウント用ツールには、ホスト型の代替手段をあえて用意していません。willhaben は PKCE を使用して Keycloak で認証するため、第三者に付与できるスコープ付きトークンがありません。ホストされたサーバーが要求できるのは生のセッションクッキーだけで、それを使うとメッセージの閲覧やアカウントの操作が可能になります。この 4 つのツールを使いたい場合は、ご自身のクッキーを設定してローカルで full モードを実行してください。ホスティングをご覧ください。
willhaben
機能
アカウント不要の公開ツールが 4 つあります。
| ツール | 用途 |
|---|---|
willhaben_search | 構造化フィルターを使ってマーケットプレイスを検索します。ページネーションは自動で行われます |
willhaben_get_ad | 1 件の広告の詳細を取得します。説明、画像、販売者の信頼性を示す情報を含みます |
willhaben_find_deals | 検索語句に該当する市場価格を調べ、中央値を下回る出品を順位付けします |
willhaben_discover_filters | willhaben の不透明な数値カテゴリ ID/ブランド ID を解決します |
セッションクッキーが必要なアカウント用ツールが 4 つあります。
| ツール | 用途 |
|---|---|
willhaben_my_profile | プロフィールと数値形式のユーザー ID |
willhaben_my_watchlist | ウォッチリストに保存した広告 |
willhaben_my_conversations | 最近の購入者・販売者とのチャット |
willhaben_my_alert_count | 有効な保存済み検索アラートの件数 |
インストール
claude mcp add willmehr -- npx -y willmehr
または、ソースからインストールします。
npm install
npm run build
npm run pack:kz # stage build/kleinanzeigen-mcp for publishing
willmehr はリポジトリのルートから公開します。kleinanzeigen-mcp は
npm run pack:kz でステージングされた build/kleinanzeigen-mcp から公開します。これは、Kleinanzeigen のエントリーポイントが参照するビルド済み出力(独自のディレクトリと共有のモードモジュール)をコピーし、その周囲にマニフェストを生成するため、両方のパッケージで同じコミットを同じバージョンで公開できます。server.json と server.kleinanzeigen.json は、対応する MCP レジストリーのエントリーです。
Claude Code に登録します。
claude mcp add willmehr -- node "$PWD/dist/index.js"
または、クライアントの MCP 設定に追加します。
{
"mcpServers": {
"willmehr": {
"command": "node",
"args": ["/path/to/willmehr/dist/index.js"],
"env": { "WILLHABEN_COOKIE": "BBX_JSESSIONID=...; x-bbx-csrf-token=..." }
}
}
}
認証
willhaben は PKCE を使用して Keycloak で認証するため、自動化できるユーザー名/パスワード認証はありません。代わりに、ログイン済みブラウザーからセッションを取得します。
- willhaben.at にログインし、プロフィールまたはメッセージにアクセスして、ページが認証済みエンドポイントを読み込むようにします。
- DevTools → Network → エクスポートのドロップダウン → 「Export HAR (with sensitive data)」。通常のダウンロードボタンでは Cookie が除去されるため、このサーバーで使えないファイルが生成されます。
npm run session -- ~/Downloads/www.willhaben.at.harnpm run check:auth
npm run session は .env(モード 0600、gitignore 対象)を書き込み、Cookie の値を表示することはありません。npm run check:auth は 4 つすべてのアカウントツールを実行し、最初の 401 で停止するため、アカウントへの認証に繰り返し失敗することはありません。
Cookie には BBX_JSESSIONID(セッション)と x-bbx-csrf-token が含まれている必要があります。CSRF トークンは Cookie から自動的に読み取られます。willhaben のウェブアプリは、すべての /webapi/ 呼び出しでその Cookie を同名のリクエストヘッダーに反映し、サーバーも同じように処理します。WILLHABEN_CSRF_TOKEN は上書きする場合にのみ使用します。
サーバーはパッケージのルートから .env を読み込むため、MCP クライアントの設定で Cookie を渡す必要はありません。
セッションの有効期限は Cookie の表示より短いです
BBX_JSESSIONID は Max-Age が 5 日で送信されますが、サーバーはそれよりかなり早く無効にします。取得から 38 分後 に再利用したセッションは、すでに拒否されました。これは IP やヘッダーに紐づくものではありません。同じマシン、同じ IP から、同じ Cookie jar とブラウザーの完全なヘッダーセットをそのまま使っても 401 が返される一方で、匿名リクエストは引き続き成功します。
セッションは短期間で期限切れになるものとして扱ってください。取得したセッションが何日も使えると期待するのではなく、アカウントツールを使う直前に npm run session を再実行してください。
環境変数
| 変数 | デフォルト | 用途 |
|---|---|---|
WILLHABEN_COOKIE | — | アカウントツール用のセッション Cookie |
WILLHABEN_CSRF_TOKEN | Cookie から取得 | CSRF トークンを上書きします |
WILLHABEN_MIN_INTERVAL_MS | 400(公開では 900) | リクエスト間隔の最小値 |
WILLMEHR_MODE | full | full または public — モードを参照してください |
効果的な使い方
絞り込みの前に ID を解決します。 カテゴリーとブランドは、意味のない整数で表されます。まず willhaben_discover_filters に問い合わせてください。現在のクエリに対する各フィルター値とその ID、さらにヒット数が返されます。
今、ウィーンで 300 ユーロ未満で出品されているノートパソコンのブランドは何ですか?
お買い得品を順位付けする前に、検索を絞り込みます。 willhaben_find_deals は各出品を検索結果の中央値と比較するため、クエリでは同じ種類の商品を指定する必要があります。thinkpad で検索すると、900 ユーロのノートパソコンと 5 ユーロのドッキングステーションが混在し、報告される「割引」はすべてノイズになります。カテゴリー 5831(Notebooks)内で priceFrom: 150 を指定して thinkpad x1 carbon を検索すると、比較する価値のある中央値が得られます。
suspiciouslyCheap に注意します。 中央値の 20% 未満の出品にはフラグが付けられます。本当のお買い得品の場合もありますが、多くは仮の価格、部品のみの出品、または誘導目的の出品です。このフラグは広告を確認するための合図であり、判定結果ではありません。
新着を狙う場合。 sort: "newest" と maxAgeHours: 2 を組み合わせると、ここ数時間に掲載されたものだけを確認できます。
例
ウィーンで、個人出品者が過去1日以内に投稿した、相場より安い ThinkPad X1 Carbon を探して、問い合わせる価値があるものを教えてください。
エージェントは上位候補について、willhaben_discover_filters → willhaben_find_deals →
willhaben_get_ad の順にツールを呼び出し、出品者の登録期間と広告文を確認します。
仕組み
公開ドキュメントがないため、HAR の分析から得られたメモを記します。
x-wh-clientは必須です。/webapi/の各ルートは、これがないと空の本文でそのまま400を返します。これは不正なクエリパラメーターとまったく同じように見えるため、最も簡単に行き詰まるポイントです。- 検索に Cookie は一切必要ありません。 Cookie が必要なのはアカウント関連のルートだけです。
- フィルターは自己記述型です。 検索レスポンスには
navigatorGroups[]が含まれます。各ファセットには、対応するクエリパラメーターと、ヒット数付きの有効な値がすべて記載されています。willhaben_discover_filtersはその情報を薄く投影したものなので、willhaben がカテゴリーを追加しても正確さを保てます。 - ソート ID はドキュメント化されていない整数です。 実地検証済みの値は、
1が新着順、3が価格の昇順、4が価格の降順、0が関連度順です。 - 公開された単一広告用の JSON エンドポイントはありません。
publicapi.willhaben.atとapi.willhaben.atは、どちらも匿名の呼び出しを拒否します。広告ページは Next.js で構築されているため、完全なペイロードは__NEXT_DATA__から読み取ります。buildId が判明している場合は、より軽量な/_next/data/<buildId>/...jsonルートを使用し、willhaben の再デプロイで ID が変わった場合は HTML にフォールバックします。 - すべてのフィールドは
{name, values[]}の形式で届きます。src/normalize.tsがフラット化します。
レート制限と利用規約
リクエストは単一キューを通して直列化され、最低 400 ms の間隔を空けます。また、429/5xx の場合はジッター付きの指数バックオフを行います。この設定はそのまま維持してください。
willhaben の robots.txt には、"It is expressively forbidden to use
spiders, search robots or other automatic methods to access willhaben.at" と明記されており、/webapi/ と、このサーバーが使用する keyword、PRICE_FROM、PRICE_TO、periode の各パラメーターへのアクセスも禁止されています。利用規約でも、同意のない自動コピーは禁止されています。このツールは、まさにそのようなアクセスを行います。
これは形式的な注意事項ではなく、実際の制約です。停止される可能性があるのはあなたのアカウントです。このツールは、自分のアカウントでの個人的かつ低頻度・不定期の利用を想定して作られています。大量収集に使わず、データを再配布せず、短い間隔で定期実行しないでください。
先行プロジェクト
aliildan/willhaben-mcp は、より多くの分野(自動車、不動産、求人)をカバーしていますが、匿名利用のみで、価格分析機能はありません。マーケットプレイスでお買い得品を探すのではなく、幅広く読み取り専用の閲覧をしたい場合は、そちらを使ってください。
Kleinanzeigen
jnslmk/kleinanzeigen-mcp(Python)を TypeScript に移植したもので、ドイツ最大のクラシファイドサイトである kleinanzeigen.de をカバーしています。アカウントは不要です。
ツール
| ツール | 用途 |
|---|---|
kleinanzeigen_search | キーワード、場所、半径、価格で検索します。ページネーションは自動です |
kleinanzeigen_search_by_url | 貼り付けた Kleinanzeigen の URL から検索します。構造化検索では指定できないフィルターも保持します |
kleinanzeigen_get_listing | 1 件の広告の詳細情報を取得します。説明、画像、出品者、属性が含まれます |
kleinanzeigen_get_listings_batch | 複数 ID の詳細情報を一度に取得します。検索後に通常使用するツールです |
kleinanzeigen_parse_search_url | URL に含まれるフィルターを説明します。ネットワークリクエストは行いません |
想定される流れは kleinanzeigen_search → 興味のある ID を選択 →
kleinanzeigen_get_listings_batch です。検索結果にはサイト独自の短い紹介文しか含まれないため、幅広く検索してもモデルのコンテキストウィンドウを圧迫しません。
インストール
willhaben の部分とは別のパッケージなので、単独でインストールできます。
claude mcp add kleinanzeigen -- npx -y kleinanzeigen-mcp
ソースからインストールする場合:
npm install && npm run build
claude mcp add kleinanzeigen -- node "$PWD/dist/kleinanzeigen/index.js"
クライアントの MCP 設定で使用する場合:
{
"mcpServers": {
"kleinanzeigen": {
"command": "node",
"args": ["/path/to/willmehr/dist/kleinanzeigen/index.js"]
}
}
}
環境変数
| 変数 | デフォルト | 用途 |
|---|---|---|
KZ_MIN_INTERVAL_MS | 800(パブリックでは1500) | リクエスト間隔の最小値 |
KZ_MAX_PAGE_COUNT | 5(パブリックでは3) | 検索ごとのページ数上限(各ページ約25件の掲載情報) |
KZ_MAX_BATCH_SIZE | 20(パブリックでは10) | 1回のバッチ呼び出しで指定できるID数の上限 |
WILLMEHR_MODE | full | fullまたはpublic — モードを参照 |
TZ | ホストのデフォルト | 掲載日のタイムゾーンは指定されていません。正確にするにはEurope/Berlinを設定してください |
移植で変更した点
ブラウザーは不要です。 元のPython実装は、コミットを固定したDanielWTE/ebay-kleinanzeigen-apiを介してヘッドレスChromiumを操作し、約1.5 GBのイメージに組み込んでいました。しかし、これは不要だと分かりました。検索結果、広告の詳細ページ、閲覧数カウンターはすべてサーバー側で完全にレンダリングされ、ブラウザーに似たUser-Agentを指定した通常のGETに応答します。この移植版はfetchのみを使用するため、コンテナー、約1.5 GBのメモリー要件、ブラウザーコンテキストのプールが不要になり、元の実装で対処が必要だったデッドロックやコンテキストリークのバグもなくなりました。
セマフォではなく、単一のリクエストキューを使用します。 Kleinanzeigenは、間隔を空けた場合でも同じIPからの並列リクエストをブロックします。そのため、検索ページ、広告ページ、閲覧数カウンターを含むすべてのリクエストを、最小間隔800ミリ秒、さらに429/5xx時にはジッターを加えた指数バックオフを行う単一のキューで順番に処理します。これにより、ブラウザープールのサイズ調整に使われていた上流のKZ_MAX_CONCURRENT/KZ_MAX_CONTEXTS設定が不要になります。
各項目の名前を統一しました。 上流実装では検索時にadid、詳細ページではidを返し、同じ設定項目を一方のツールではpage_count、もう一方ではmax_pagesと呼んでいました。Pythonサーバーには両方に対応する互換性シムがありました。ここではそうした不統一がないため、シムはなくなり、どこでもidとpagesを使います。
文字列ではなく、型付きの値を返します。 価格は、あらかじめ加工された文字列ではなく、{amount, label, negotiable, previousAmount}として返されます。amount: 0はZu verschenken(無料)を意味し、nullは金額が表示されていないことを意味します。削除された広告は、nullだらけの空のオブジェクトを返すのではなく、ListingGoneErrorを発生させます。バッチ呼び出しでは、該当する広告はerrorsに入り、残りのIDの結果は引き続き返されます。
元の実装で取得できなかったフィールドで、掲載情報の意味合いを変えるものです。
wanted— Gesuchの掲載情報は、購入を希望している人のものです。上流実装ではこれらが販売情報に混在して返されるため、モデルには不自然に安い販売品として解釈されます。distanceKm— 検索結果ページでは、検索範囲を指定すると場所に「(4 km)」が追加されます。これは広告ではなく検索に属する情報なので、分けてあります。shippingPossible/tags、previousAmount、seller.shopUrl。- 地域の構成要素は、その実態を表す名前にしています。上流実装では「13088 Pankow - Weissensee」を
{zip, city: "Weissensee", state: "Pankow"}として解析していましたが、ここでは{postcode, city: "Pankow", district: "Weissensee"}となります。
閲覧数の取得はオプトインです。 ページ上でJavaScriptが値を設定するのは、/s-vac-inc-get.jsonから取得する#viewad-cntr-numだけです。このエンドポイントは通常のGETに応答するため、閲覧数を取得できます。ただし、「inc」は文字どおりの意味で、読み取るとページを開いたときと同じように出品者のカウンターが増えます。閲覧数が必要な場合はincludeViewCount: trueを指定してください。広告ごとに追加のリクエストが1回発生します。
移植していないもの: HTTPトランスポートと/healthzルートです。どちらのサーバーもstdioを使用しており、ローカルクライアントへの登録方法に合わせています。
注意事項
KleinanzeigenにはパブリックAPIがないため、この実装はサイトのHTMLを読み取ります。マークアップが変更されると動作しなくなる可能性があります。そのための解析チェックがnpm run smoke:kzです。大量または並列の利用はボット検出を招きます。また、データセンターのIPアドレスでは、家庭用回線なら発生しない403が返されます。
スクレイピングはKleinanzeigenの利用規約にも抵触します。リクエストのスロットリングには理由があります。設定を維持し、個人利用の規模にとどめてください。
ホスティング
dist/serve-http.js は MCP の Streamable HTTP トランスポートを介して両方のサーバーを提供するため、何もインストールせず、クライアントから URL に接続したい場合に利用できます。
npm run build && npm run serve # http://0.0.0.0:8080
docker build -t willmehr-http . && docker run -p 8080:8080 willmehr-http
| エンドポイント | 内容 |
|---|---|
POST /willhaben/mcp | 公開されている willhaben ツール 4 つ |
POST /kleinanzeigen/mcp | Kleinanzeigen のツール 5 つすべて |
GET /healthz | 稼働状態の確認 |
claude mcp add --transport http willhaben https://<your-host>/willhaben/mcp
claude mcp add --transport http kleinanzeigen https://<your-host>/kleinanzeigen/mcp
モードは public に固定されており、上書きできません。WILLMEHR_MODE=full が設定されていると、ホストは起動を拒否し、運用者自身のアカウントがすべての呼び出し元に公開されるのを防ぎます。
トランスポートはステートレスです。リクエストごとに新しいサーバーが作成され、リクエスト間で何も保持されません。2 つの上流クライアントは意図的にプロセス全体で共有されます。これらのキューにより、デプロイ全体の各サイトへのリクエストが、適切な速度の 1 つのストリームに制限されます。
| 変数 | デフォルト | 用途 |
|---|---|---|
PORT / HOST | 8080 / 0.0.0.0 | リッスンアドレス |
MCP_RATE_BURST | 10 | 1 つの IP アドレスからバースト可能なリクエスト数 |
MCP_RATE_PER_MINUTE | 20 | IP アドレスごとの補充レート |
MCP_MAX_INFLIGHT | 8 | 503 を返すまでに許可するツール呼び出しの同時実行数 |
MCP_MAX_BODY_BYTES | 1000000 | リクエスト本文の上限 |
MCP_REQUEST_TIMEOUT_MS | 120000 | 1 つのリクエストのタイムアウト上限 |
TRUST_PROXY | off | X-Forwarded-For からクライアント IP を取得します(リバースプロキシの背後に設置する場合に設定) |
MCP_ALLOWED_ORIGINS | 未設定(許可) | カンマ区切りの Origin 許可リスト |
MCP_ALLOWED_HOSTS | 未設定(許可) | カンマ区切りの Host 許可リスト |
新しい VPS へのデプロイ
月額 4 ユーロのサーバーで十分です。これは CPU 負荷ではなく、2 つのウェブサイトへの I/O 待ちが中心です。他の用途には使わないマシンに設置してください。分類サイトに IP アドレスをブロックされても、この用途以外には影響しません。
-
VPS を作成します。 Debian 12 または Ubuntu 24.04 の最小サイズを選びます(Hetzner CX22、DO basic など)。作成時に SSH キーを追加してください。
-
DNS を設定します。 使用するサブドメインの
Aレコードを、サーバーの IPv4 アドレスに向けます。Caddy が証明書を発行するには、名前解決できる必要があるため、先に設定してください。dig +short mcp.example.comで確認します。 -
サーバーを準備します(Docker をインストールし、80/443 を開放して、自動更新を有効にします)。
ssh root@<ip> 'bash -s' < deploy/bootstrap-server.sh -
デプロイします。
HOST=root@<ip> MCP_HOSTNAME=mcp.example.com ./deploy/deploy.shツリーを同期し、サーバーの
.envを書き込み、ビルドと起動を行い、https://mcp.example.com/healthzが応答するまで待機します。変更を反映するには、再実行してください。Caddy の証明書は名前付きボリュームに保存され、再発行されません。 -
登録します。
claude mcp add --transport http willhaben https://mcp.example.com/willhaben/mcp claude mcp add --transport http kleinanzeigen https://mcp.example.com/kleinanzeigen/mcp
deploy.sh は意図的に同期対象から .env と *.har を除外します。ローカルのファイルには willhaben のセッション Cookie と、その取得元のキャプチャが含まれているためです。サーバー側では、ホスト名だけを含む独自の .env が書き込まれます。
運用方法:
ssh <host> 'cd /opt/willmehr && docker compose logs -f --tail 100'
ssh <host> 'cd /opt/willmehr && docker compose restart mcp'
curl -s https://mcp.example.com/healthz # mode, in-flight count, endpoints
ホスティングにかかるコスト
秘密情報ではありません。秘密情報がサーバーに送られることはありません。コストとなるのは IP アドレスです。ローカル環境では、各ユーザーが自分のアドレスからスクレイピングします。ホスティング環境では、全員のトラフィックがあなたのアドレスを経由し、両サイトとも IP ごとにボットを検出します。1 つのアドレスからの Kleinanzeigen への同時リクエストは、間隔を空けても検出されます。そのため、送信キューをグローバルにし、公開向けのデフォルト設定を低速にしています。レスポンスキャッシュが必要になることを見込み、ユーザーが数人を超えたら、エグレスプロキシも必要になります。大手サーバーレスプラットフォームが使うデータセンターの IP 範囲は、特に厳しく検査されます。
第三者向けにスクレイパーをホストすることは、自分でスクレイピングする場合とは、両サイトの利用規約に対する姿勢も異なります。デプロイは小規模かつ非営利にとどめ、簡単に停止できるようにしてください。
開発
npm run typecheck # tsc --noEmit
npm run smoke # end-to-end checks against the live willhaben API
npm run smoke:kz # end-to-end checks against live kleinanzeigen.de
npm run session -- <path-to.har> # refresh the account session from a HAR
npm run check:auth # verify the session + account tools
npm run build
scripts/smoke.ts は実環境での統合テストです。フィルターで実際に結果が絞り込まれること、price_asc が本当に昇順で並べ替えること、広告の詳細を検索結果から取得できること、取引のランキングが順序どおりで中央値を下回ることを検証します。
scripts/smoke-kleinanzeigen.ts は Kleinanzeigen サーバーについて同様のテストを行います。また、純粋な解析処理(ドイツ語の日付、価格、URL フィルター、ページへの注入)をネットワーク呼び出しなしでテストします。そのため、そこで失敗する場合はロジックのバグであり、それ以降で失敗する場合はサイトのマークアップが変更されたことを意味します。
ライセンス
MIT — LICENSEを参照してください。Kleinanzeigen サーバーは
jnslmk/kleinanzeigen-mcp(MIT、
© Jonas Lemke)を移植したもので、同プロジェクトの告知はNOTICEに保持されています。
どちらのサイトも、これを推奨しているわけではありません。このツールは公開ページとwillhabenのウェブアプリ独自のJSON APIを、意図的に低速で読み取ります。他の人向けにホストする場合は、まず ホスティングにかかるコストをお読みください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。