Staan について
Staan-MCPは、独自インデックスを持ち欧州市場を主な対象とする検索サービスSTAANのAPIにAIクライアントを接続します。ページ本文を含む検索結果と、出典付きの回答を生成するツールを提供します。
主な機能
- 01Staan市場を横断した検索
- 02MarkdownまたはHTMLでのページ全文取得
- 03ドメインの指定と除外
- 04出典に基づく回答の生成
- 05stdioとStreamable HTTPでの実行
活用シーン
- 01複数の欧州市場を検索
- 02検索結果とともにページ本文を取得
- 03出典を示す回答の生成
MCP 対応
MCPサーバーは`web_search`と`answer`のツールを公開します。stdioとStreamable HTTPに対応し、インストール版ではHTTPにOAuth 2.1ゲートを追加できます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
2掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| query | string | はい | 自然言語による検索クエリ。 |
| market | any | いいえ | 検索対象の言語・国市場:fr-fr、de-de、en-us、en-gb、en-fr、en-ca、en-au、en-in、en-ie、en-nz、en-za、en-sg。単一の値、リスト、またはワイルドカード('en-*' はすべての英語市場、'*' はすべて)を指定します。複数市場の結果は順位順に統合され、URL で重複排除されます。市場ごとに個別の課金対象となるため、より広い範囲が必要な場合を除き、1 つの市場を指定してください。 |
| offset | enum | いいえ | ページング用の最初の結果のオフセットです。1 ページあたり最大 10 件、クエリあたり最大 40 件です。0 | 10 | 20 | 30 |
| full_content | enum | いいえ | 各結果とともに返されるページのテキストです。デフォルトの 'none' では省略されます。'markdown' では追加され、レスポンスが非常に大きくなります。'html' ではさらに大きくなります。none | markdown | html |
| exclude_domains | any | いいえ | これらのサイトの結果を除外します。例:['pinterest.com']。サブドメインも除外されます。最大 10 件です。include_domains とは併用できません。 |
| include_domains | any | いいえ | これらのサイトの結果のみを返します。例:['wikipedia.org']。サブドメインも一致します。最大 10 件です。exclude_domains とは併用できません。 |
| simple_response | boolean | いいえ | false の場合、フラットリストではなく、市場ごとに 1 件の生の Staan レスポンスを返します。 |
詳細
Staan-MCP
European Perspective SAS が構築した、欧州の検索エンジン STAAN(Search Trusted API Access Network)向けの MCP サーバーです。同社は Qwant および Ecosia の検索エンジンが共同所有しています。Exa Search やその他の SERP サービスに対するオープンな欧州の代替サービスであり、独自のインデックスを備え、主に欧州市場(一部の英連邦諸国を含む)を対象としているため、他の検索プロバイダーにはない独自の視点を提供します。無料プランでは月に 1,000 件のリクエストを利用できます。
MCP には 2 つのツールがあります。
web_search:1 つまたは複数の Staan 市場を対象に、一度にウェブ検索できます。ページ全体のコンテンツ(Markdown または HTML)も任意で取得できます。answer:根拠に基づく回答を、GitHub 形式の脚注付き Markdown として表示します。
stdio(デフォルト)、ストリーミング可能な HTTP、またはステートレスなストリーミング可能な HTTP で実行でき、HTTP トランスポートはオプションの OAuth 2.1 リソースサーバーゲートで保護できます。stdio だけが必要な場合は、何もインストールせずに実行することもできます。リポジトリの ZIP をダウンロードし、このファイル(README.md)を AI エージェントに指定するだけです。
注:
staan-mcpは Staan、European Perspective、Qwant、Ecosia とは無関係です。リリース時点で存在していなかった、高品質な Python MCP を提供するための個人プロジェクトです。
重要:
answerAPI エンドポイントは招待制です。招待を受けていても、いつでも一時的に無効化されることがあり、その場合は{"message":"Forbidden","statusCode":403}が返されます。
クイックスタート
Staan API キーが必要です。Staan コンソールで作成してください。無料プランでは月に 1,000 件のリクエストを利用できます。uv がインストールされていれば、ハーネスを設定するだけで使えます。このリポジトリから何かをダウンロードする必要はありません。uv の uvx コマンドが最新バージョンの MCP を自動的にダウンロードします。
{
"mcpServers": {
"staan": {
"command": "uvx",
"args": ["staan-mcp"],
"env": { "STAAN_API_KEY": "your-staan-api-key" }
}
}
}
この JSON は Claude Desktop と Cursor でそのまま動作します。Claude Code、VS Code、Hermes Agent では異なる形式を使用します。クライアントの設定を参照してください。
LLM をお使いの場合や、単に詳細を知りたい場合は、この先をお読みください。
詳細なセットアップ
実行する3つの方法
- インストール済みパッケージ
uvx staan-mcp、または pip install staan-mcp を実行すると、MCP SDK、httpx2、pydantic、anyio(OAuth ゲート用の pyjwt も含む)など、複数の依存関係がインストールされます。その代わり、stdio だけでなく、Streamable HTTP とステートレスな Streamable HTTP 接続も利用できます。どちらの HTTP 接続にも OAuth ゲートがあります。
- ダウンロードして実行
GitHub の Code->Download ZIP からダウンロードするか、git clone で取得します。その後、stdio_server.py を使って MCP を実行するように設定できます(インストールガイドの何もインストールしない場合を参照してください)。
- リリース経由
最新のリリースをダウンロードします。
| 1. インストール済みパッケージ | 2. ダウンロードして実行(チェックアウト) | 3. 単一ファイルバンドル | |
|---|---|---|---|
| 方法 | uvx staan-mcp、または pip install staan-mcp | git clone(またはソースツリーの zip)を実行し、python src/staan_mcp/stdio_server.py を実行 | 最新リリースから staan-mcp-stdio.pyz をダウンロードし、python staan-mcp-stdio.pyz を実行 |
| Python | 3.11 以降 | 3.11 以降。通常のインタープリターのみで、インストールは不要 | 3.11 以降。通常のインタープリターのみで、インストールは不要 |
| 依存関係 | MCP SDK、httpx2、pydantic、anyio(OAuth ゲート用の pyjwt も含む) | なし(標準ライブラリのみ) | なし(標準ライブラリのみ) |
| トランスポート | stdio、Streamable HTTP、ステートレスな Streamable HTTP | stdio | stdio |
| HTTP 用 OAuth ゲート | あり | なし | なし |
| ツール、パラメーター、結果、エラーメッセージ | リファレンス | 同一(以下を参照) | 同一(同じコード) |
HTTP と OAuth ゲートを提供するのは、インストール済みパッケージのみです。方法2と3では同じ
stdio_server.py を実行します。バンドルは、このファイルとそれがインポートする少数の標準ライブラリモジュールを1つのファイルに zip したもので、任意の場所に配置できます。
クライアントの設定
すべての例では、uvx でサーバーを起動し、STAAN_API_KEY
環境変数を使ってキーを渡します。your-staan-api-key を自分のキーに置き換えてください。
Claude Desktop
claude_desktop_config.json を編集します(Settings、Developer、Edit Config)。
{
"mcpServers": {
"staan": {
"command": "uvx",
"args": ["staan-mcp"],
"env": { "STAAN_API_KEY": "your-staan-api-key" }
}
}
}
Claude Code
claude mcp add staan --env STAAN_API_KEY=your-staan-api-key -- uvx staan-mcp
すべてのプロジェクトで利用できるようにするには --scope user を追加します。または、共有可能な .mcp.json に書き込むには --scope project を追加します(コミットするファイルにキーを記述しないでください。そこでは "STAAN_API_KEY": "${STAAN_API_KEY}" のように環境変数を展開してください)。
VS Code
.vscode/mcp.json を作成します。VS Code はキーの入力を一度だけ求め、安全に保存します。
{
"inputs": [
{
"type": "promptString",
"id": "staan-api-key",
"description": "Staan API key",
"password": true
}
],
"servers": {
"staan": {
"type": "stdio",
"command": "uvx",
"args": ["staan-mcp"],
"env": { "STAAN_API_KEY": "${input:staan-api-key}" }
}
}
}
Cursor
プロジェクト内に .cursor/mcp.json を作成するか、すべてのプロジェクトで使う場合は ~/.cursor/mcp.json を作成します。Cursor は ${env:NAME} を展開するため、キーはシェル環境に設定したままにできます。
{
"mcpServers": {
"staan": {
"command": "uvx",
"args": ["staan-mcp"],
"env": { "STAAN_API_KEY": "${env:STAAN_API_KEY}" }
}
}
}
このリポジトリは Cursor プラグイン(.cursor-plugin/plugin.json と mcp.json)でもあります。プラグインとしてインストールすると、Cursor はシェルからキーを読み取る代わりに、プラグイン変数として Staan API キーの入力を求めます。
Hermes Agent
Hermes は ~/.hermes/config.yaml の mcp_servers から MCP サーバーを読み込みます。
mcp_servers:
staan:
command: "uvx"
args: ["staan-mcp"]
env:
STAAN_API_KEY: "your-staan-api-key"
Hermes には、別プロセスを起動せずに staan_web_search と
staan_answer として同じ2つのツールを提供するネイティブプラグインもあります。src/hermes を参照してください。これはサーバーとは別のもので、staan-mcp パッケージには含まれていません。
その他のクライアント
stdio コマンドを起動できるクライアントなら、どれでも動作します。環境変数に STAAN_API_KEY を設定して
uvx staan-mcp を実行してください。HTTP クライアントについては、トランスポートを参照してください。
インストール不要で使う
方法2と3では、pip install、仮想環境、サードパーティーパッケージは必要ありません。必要なのは標準の Python 3.11 以降のみで、stdio のみを提供します(HTTP と OAuth ゲートにはインストール済みパッケージが必要です)。
チェックアウトから使う場合(方法2)。 リポジトリをダウンロードし(git clone またはソースツリーの zip を使用)、src/staan_mcp/ をまとめて保持したうえで、クライアントから次のファイルを指定します。
src/staan_mcp/stdio_server.py:
{
"mcpServers": {
"staan": {
"command": "python",
"args": ["/path/to/Staan-MCP/src/staan_mcp/stdio_server.py"],
"env": { "STAAN_API_KEY": "your-staan-api-key" }
}
}
}
このスクリプトは独自の src ディレクトリを sys.path に追加し、パッケージの標準ライブラリモジュール(市場、ドメイン、結果の書式設定、エラー、設定、ツールのテキスト)を再利用するため、単独でコピーして使うことはできません。リポジトリから切り離してコピーすると、その旨を1行表示して終了コード 2 で終了し、トレースバックは表示しません。代わりにバンドルを使用してください。
Python の選択。 どちらの方法にも Python 3.11 以降が必要です。Windows では、python が見つからない場合や Microsoft Store のスタブである場合があります。その場合は、"command": "py", "args": ["-3", "C:\\path\\to\\staan-mcp-stdio.pyz"] を指定してランチャーを使うか(JSON ではバックスラッシュを二重にする必要があります)、インタープリターのフルパスを指定してください。macOS と Linux では、python3 が使われることがよくあります。古い Python では、ファイルはすぐに staan-mcp needs Python 3.11 or newer; this is X.Y.Z (path) と表示し、終了コード 2 で終了します。python --version で確認してください。
仕組み。 MCP SDK、httpx2、pydantic、anyio、pyjwt は一切インポートしません。テストでは python -I -S と、標準ライブラリ以外のすべてのインポートをブロックする仕組みを使ってこれを検証し、ビルド済みバンドルも同じ方法で実行します。インストール済みパッケージと同じ web_search および answer ツール、環境変数、結果、エラーメッセージを stdio のみで提供します(同じ入力で両者を比較するテストを実施しています)。httpx2 の代わりに独自の小さな urllib トランスポートを使い、同じ再試行ポリシーを採用しています。パッケージのほかの部分と同じリンター、型、複雑度、カバレッジの基準で検証されます。ミューテーションテストのみ対象外です。
単一ファイルのバンドル(方法3)。 最新リリースから staan-mcp-stdio.pyz をダウンロードし、任意の場所に置いて、クライアントから指定します。
{
"mcpServers": {
"staan": {
"command": "python",
"args": ["/path/to/staan-mcp-stdio.pyz"],
"env": { "STAAN_API_KEY": "your-staan-api-key" }
}
}
}
このファイルは Python の zip アプリケーションです。python staan-mcp-stdio.pyz --version を実行すると、staan-stdio X.Y.Z と表示されます。stdio_server.py がインポートするパッケージの標準ライブラリモジュールのみを含み、それ以外は含みません。各リリースでは、wheel および sdist と並んでこのファイルが添付され、署名付きビルド来歴証明書も付属します(gh attestation verify staan-mcp-stdio.pyz --repo MarcinWolnyEU/Staan-MCP)。ビルドは再現可能です。タグをチェックアウトしたリポジトリで python scripts/build_zipapp.py を実行すると、バイト単位で同一の出力が書き込まれ、SHA-256 が表示されます。
ツールリファレンス
どちらのツールも読み取り専用で、オープンワールド型です(外部サービスにアクセスしますが、何も変更しません)。エラーは、キーが含まれない安全な短いメッセージとともに、MCP ツールエラーとして報告されます。
web_search
POST /v2/search/web を通じてウェブを検索します。各呼び出しでは、マーケットごとに最大 10 件の結果を含む 1 ページが返されます。
| パラメーター | 型 | デフォルト | 説明 |
|---|---|---|---|
query | 1~400 文字の文字列 | 必須 | 検索クエリです。site: および -site: 演算子に対応しています。 |
market | 文字列または文字列のリスト | "en-gb" | 1 つ以上の Staan マーケットです。ワイルドカードに対応しています。詳しくは以下をご覧ください。 |
full_content | "none"、"markdown"、または "html" | "none" | 各結果ページのテキストを指定した形式で返します。デフォルトの "none" ではテキストが省略されるため、レスポンスとトークン使用量を抑えられます。以下の注記のとおり、コストとレイテンシーはほとんど変わりません。 |
offset | 0、10、20、または 30 | 0 | ページング時の結果オフセットです。各ページは個別に課金されるリクエストです。 |
include_domains | 文字列のリスト、最大 10 件 | なし | 指定したドメインに結果を絞り込みます。ドメインにはサブドメインも含まれます。 |
exclude_domains | 文字列のリスト、最大 10 件 | なし | 指定したドメインの結果を除外します。サブドメインも含まれます。include_domains とは併用できません。 |
simple_response | ブール値 | true | true の場合、フラットでコンパクトなリストを返します。false の場合、未加工の上流レスポンスを返します。 |
注記:
countはパラメーターではありません。Staan が受け付けるページサイズは 10 のみで、サーバーは常にこれを送信します。ページングにはoffsetを使用してください。- ドメインは自動的に正規化されます。たとえば、
https://www.Example.com:443/a?bはexample.comになり、en.wikipedia.orgはen.wikipedia.orgのままです。 - サーバーは常に追加のスニペットをリクエストします。API キーのプランに追加のスニペットが含まれている場合、各結果の
snippetに統合されます。含まれていない場合、このフィールドは上流レスポンスに含まれず、問題は発生しません。 - 指定したドメインにはサブドメインも含まれます。2026-10-05 に実際に確認したところ、
include_domains: ["python.org"]はdiscuss.python.org、docs.python.org、wiki.python.org、www.python.orgを返し、exclude_domains: ["python.org"]はこれらすべてを除外しました。サブドメインを個別に列挙する必要はありません。 full_contentが変えるのはレスポンスのサイズであり、コストではありません。 サーバーは常に Staan に追加のスニペットを要求します。これによって Staan はページテキストを取得してランク付けするため、"none"のリクエストも"markdown"と同じ料金区分で課金され、所要時間もほぼ同じです(2026-10-05 の測定では、差は約 0.1~0.5 秒でした)。異なるのはテキストの量です。単一のマーケットに対するen-gbの呼び出しで"markdown"を指定すると、ページテキストが約 130,000 文字(約 30,000~40,000 トークン)返されました。"html"はその数倍のサイズになります。デフォルトの"none"では、同じ呼び出しのレスポンスがごく一部(このテストでは約 20 KB)になります。ページを読む必要がある場合にのみ"markdown"を指定してください。full_content: "markdown"または"html"の場合、デフォルトでは出力サイズに上限がありません。多くのクライアントにはツール結果の上限(Claude Code のデフォルトは約 25,000 トークン)があるため、このような呼び出しは途中で切り詰められたり、拒否されたりする場合があります。また、market: "*"を指定すると、サイズが最大 12 倍になります。オペレーターはSTAAN_MCP_MAX_CONTENT_CHARS(設定を参照)を使って各ページに上限を設定できます。長いページは段落の区切りで切り詰められ、末尾に[... truncated: N of M characters shown ...]が付加されます。スニペットと追加のスニペットは切り詰められません。デフォルトでは上限は無効です。クライアントがツールの出力を制限していて、ページテキストも取得したい場合は、STAAN_MCP_MAX_CONTENT_CHARS=8000から試すのが妥当です。
マーケットとワイルドカード
Staan は 12 のマーケットに対応しています:fr-fr、de-de、en-us、en-gb、en-fr、en-ca、en-au、
en-in、en-ie、en-nz、en-za、en-sg。コードの大文字と小文字は区別されません。
market には、単一のコード、コードのリスト、またはシェル形式のワイルドカード(*、?、[...]。12 個のコードに対して照合されます)を使ったパターンを指定できます:
| 値 | 展開結果 |
|---|---|
"en-gb" | en-gb |
["en-us", "en-gb"] | en-us、en-gb |
"en-*" または "en*" | 10 個の en- マーケット |
"*-fr" | fr-fr、en-fr |
"*" | 12 個すべてのマーケット |
重複は削除され、最初に出現した順序が維持されます。値が空の場合、コードが不明な場合、またはパターンに一致するものがない場合は、有効なマーケットの一覧を示すエラーになります。
警告:マーケットごと、ページごとに 1 件の課金対象リクエストが発生します。
market: "*"では 1 回の呼び出しにつき 12 件のリクエストが、market: "en-*"では 10 件のリクエストが発生します。Staan では、1 秒あたり 20 件のレート制限も適用されます。サーバーはリクエストを遅らせて制限を守ります(レート制限を参照)。 マーケットは、明示的に短いリストで指定することをおすすめします。最新の料金については Staan の料金を確認してください(執筆時点では、月間最初の 1,000 件のリクエストは無料で、通常のウェブ検索は 1,000 件あたり 1 EUR、追加のスニペットまたは全文コンテンツを含むリクエストは 1,000 件あたり 2 EUR でした)。
複数のマーケットを指定すると、リクエストは並行して実行されます。一部のマーケットで失敗しても、ほかのマーケットの結果は返され、失敗したマーケット名を示す警告が stderr に記録されます。すべてのマーケットで失敗した場合、またはキー自体が拒否された場合(無効、無効化済み、または使用許可がない場合)に限り、ツールは失敗します。これらのエラーはすべてのマーケットで繰り返し発生するためです。
simple_response=true(デフォルト)の出力
テキストコンテンツとして返される単一の JSON 配列です。複数のマーケットを指定した場合、結果は順位ごとにラウンドロビン方式で交互に並べられ(最初のマーケットの順位 0、2 番目のマーケットの順位 0、……、次に順位 1、という順)、URL で重複が除去されるため、最初に出現した結果が残ります。
[
{
"title": "Top 7 open source LLMs for 2026",
"url": "https://www.example.com/open-source-llms-2026",
"snippet": "Top open source LLMs in 2026 (...) Open source LLMs are fully accessible for anyone to use, modify and distribute.",
"full_content": "# Top 7 open source LLMs for 2026\n\nLarge Language Models are ..."
},
{
"title": "A list of open LLMs available for commercial use",
"url": "https://github.com/example/open-llms",
"snippet": "A list of open LLMs available for commercial use."
}
]
snippetは上流からのスニペットに、追加のスニペットがある場合は(...)で連結したものです。full_contentは、要求しており、かつ Staan が空でないテキストを返した場合にのみ含まれます。取得できなかったページにはfull_contentキーがありません。この例では最初の結果にfull_content: "markdown"が示されています。デフォルトでは、どの結果にもこのキーはありません。
simple_response=false の出力
要求されたマーケットごとに 1 つのエントリを含む JSON 配列です。マーケットの順序は指定順です。成功したマーケットのエントリは上流からの生のレスポンス(ここでは一部を省略)です。失敗したマーケットのエントリは、マーケットとメッセージを含むオブジェクトです。
[
{
"search_id": "0d920ee2fc05ffd5fc834ba3ed346b02",
"query": { "q": "open source llms", "market": "en-gb", "count": 10, "offset": 0 },
"web": {
"results": [
{
"title": "Top 7 open source LLMs for 2026",
"url": "https://www.example.com/open-source-llms-2026",
"snippet": "Top open source LLMs in 2026 ...",
"display_url": "https://www.example.com/open-source-llms-2026",
"hostname": "www.example.com",
"extra_snippets": [{ "chunk": "Open source LLMs are ...", "score": 0.99 }],
"full_content": { "text": "# Top 7 ...", "format": "markdown", "length": 5120 }
}
]
}
},
{ "market": "de-de", "error": "Staan rate limit reached (HTTP 429). Try again shortly." }
]
ツール呼び出しの例:
{ "query": "python 3.14 release notes", "full_content": "markdown" }
{ "query": "bundesliga tabelle", "market": "de-de", "include_domains": ["kicker.de"] }
{ "query": "oat milk prices", "market": ["en-gb", "en-ie"], "offset": 10, "simple_response": false }
answer
ウェブソース(または指定したソース)に基づく回答を、POST /v2/answer に要求します。サーバーは常に、ストリーミングなしの Markdown 形式で回答を要求し、会話履歴は送信しません。
| パラメーター | 型 | デフォルト | 説明 |
|---|---|---|---|
query | string、1~2,000 文字 | 必須 | 質問です。 |
filter | string | なし | 検索対象を 1 つのドメインに限定します。例:frandroid.com。URL も受け付け、ドメイン部分のみに変換されます。空文字列の場合、フィルターは適用されません。sources が設定されている場合、Staan では無視されます。 |
language | "en" または "fr" | "en" | 回答の言語です。(Staan 自体のデフォルトはフランス語です。) |
mode | "short" または "long" | "short" | 回答の長さです。 |
sources | ソースオブジェクトのリスト、最大 10 個 | なし | ウェブ検索の代わりに、指定したソースに基づいて回答します。 |
sources_mode | "search" または "context" | "search" | "search" はソースを取得して再ランク付けし、"context" はソースの content を直接挿入します。sources が設定されている場合にのみ送信されます。 |
simple_response | boolean | true | true の場合は、引用付きの回答テキストのみを返します。false の場合は、上流からの生のオブジェクトを返します。 |
ソースオブジェクトには、任意の url、title(最大 500 文字)、content(最大 50,000 文字)を指定できます。各ソースには、url または content のいずれかが必要です。
Staan は渡された url の値を取得するため、まず検証されます。絶対 http または https URL でホストを含み、長さが 2,048 文字以下で、空白文字や制御文字を含まず、プライベート、ループバック、リンクローカル、予約済みの IP アドレス(または localhost)を直接指定していない必要があります。それ以外は、リクエスト送信前に拒否されます。これは URL の文字列そのものに対するフィルターであり、ホスト名の名前解決結果を保証するものではありません。
simple_response=true(デフォルト)の場合の出力
キーが 1 つのオブジェクトです。引用は GitHub 形式の Markdown 脚注に変換されます。引用された箇所に [^1] のようなマーカーが挿入され、ソース URL を含む脚注リストが追加されます。脚注番号は初出順に付けられ、引用されたソースのみが記載されます。
{
"answer": "The Eiffel Tower is about 330 metres tall.[^1] It was completed in 1889 for the World Fair.[^2] Millions of people visit it every year.\n\n[^1]: https://example.org/eiffel-tower\n[^2]: https://example.org/world-fair-1889"
}
使用可能な引用が応答に含まれていない場合、回答は変更されずに返されます。テキスト形式の answer フィールドを含まない応答は、(simple_response の値にかかわらず)ツールエラーとして報告されます。
脚注のレンダリングに関する詳細:
- 引用は
source_idによってソースと照合されます。名前ではなく番号が付けられたソース(ID がまったくない場合)は、位置("2"は 2 番目のソース)で照合されます。ソースに ID がある場合、未知の ID は推測で補わずに除外されます。誤った引用は、引用がないよりも悪いためです。 - コードフェンスや表の内部、または末尾にマーカーが配置されることはありません。ブロックが壊れて脚注リストが取り込まれてしまうためです。マーカーはブロック直前のテキスト末尾に移動します(回答の先頭にあるブロックへの引用は除外されます)。
- 長さが 2,048 文字以下で、空白文字や制御文字を含まない
httpおよびhttpsのソース URL のみが記載されます。それ以外は無視されるため、上流のテキストによって脚注の後に Markdown が挿入されることはありません。
このツールは出力スキーマを宣言します。必須の answer 文字列を含むオブジェクトであり、simple_response が false の場合は、その他の上流フィールドも含まれます。テキストコンテンツは同じ JSON で、コンパクト形式です。
simple_response=false の場合の出力
Staan のドキュメントに記載されている、生の上流オブジェクトです(抜粋):
{
"request_id": "01938fc2-1b3a-7e4d-9c12-a4f8e3c20d91",
"answer": "The Eiffel Tower is about 330 metres tall. It was completed in 1889 ...",
"citations": [{ "position": 42, "source_id": "1" }, { "position": 87, "source_id": "3" }],
"sources": [
{ "id": "1", "url": "https://example.org/eiffel-tower", "title": "Eiffel Tower facts" },
{ "id": "3", "url": "https://example.org/world-fair-1889", "title": "The 1889 World Fair" }
],
"related_queries": ["How was the Eiffel Tower built?"],
"usages": [{ "step": "answer", "input_tokens": 2840, "output_tokens": 194 }]
}
ツール呼び出しの例:
{ "query": "What changed in the latest EU AI Act guidance?", "mode": "long" }
{ "query": "meilleur smartphone 2026", "language": "fr", "filter": "frandroid.com" }
{
"query": "Summarise our release policy",
"sources_mode": "context",
"sources": [{ "title": "Policy", "content": "Releases happen on the first Monday of the month ..." }]
}
エラーレスポンス
| 状況 | ツールが報告する内容 |
|---|---|
STAAN_API_KEY が設定されていない | 変数名を示す設定エラーです。 |
| キーが拒否された(401) | 認証エラーです。キーを確認してください。 |
キーが無効化されている(403、KEY_DISABLED) | 無効化されたキーのエラーです。 |
| 許可されていない(403) | answer の場合、この API キーでは有効化されていません(招待制のため、[email protected] にお問い合わせください)。または、Staan により一時的に無効化されています。 |
| 無効な引数(400、422) | Staan の検証メッセージを ; で連結したものです。 |
| レート制限(429)、サーバーエラー(5xx)、接続失敗 | リクエストは再試行され(STAAN_MAX_RETRIES を参照)、その後、レート制限、サーバー、または通信エラーが報告されます。上流からのサーバーエラーのテキストがそのまま出力されることはありません。 |
読み取りタイムアウト(STAAN_TIMEOUT 内に応答がない) | 直ちに報告され、再試行されません。リクエストはすでに送信されているため、再試行すると二重に課金されます。 |
レート制限により、STAAN_DEADLINE を過ぎてからでないとリクエストを送信できない場合 | 同じ期限超過エラーが直ちに発生し、そのリクエストは送信されません。 |
呼び出しが STAAN_DEADLINE を超えてかかる場合 | 既定では 50 秒後に通信エラーになります。再試行と待機時間も含まれるため、一般的な 60 秒のクライアントタイムアウトより前に、モデルに明確なエラーが返されます。 |
STAAN_BASE_URL または STAAN_API_KEY が HTTP リクエストに使用できない場合 | 両方の変数名を示す設定エラーです(キー自体は決して表示されません)。市場の数にかかわらず、呼び出し全体が直ちに終了します。 |
| 32 MiB を超える応答 | 応答エラーになり、本文は破棄されます。 |
レート制限
Staan では、1 つの API キーあたり 1 秒間に 20 件のリクエストに制限しています。market: "*" は一度に 12 件のリクエストを送信するため、同時に 2 回呼び出すと(24 件のリクエスト)、制限を超えます。また、再試行もそれぞれ 1 件のリクエストとして数えられます。そのため、サーバーは送信するリクエストを数え、制限内に収まらないリクエストを遅延させます。拒否されることはなく、呼び出しに少し時間がかかるだけです。
- カウント対象。 1 つのサーバーが Staan に送信するすべてのリクエストです。
web_searchとanswer、呼び出しの各市場、すべての再試行が対象です。1 秒間の任意の期間(スライディングウィンドウのため、2 秒間に 40 件が集中することはありません)に開始できるのは最大でSTAAN_RATE_LIMIT件(既定では 20 件)です。また、タイミングのずれを吸収するため、各期間に 50 ms の安全マージンが加わります。既定値の場合は、1.05 秒あたり 20 件です。リクエストは送信された順に処理されます。 - 例。
market: "*"の呼び出しを 2 件同時に行う場合、24 件のリクエストのうち 20 件が直ちに開始され、残り 4 件は 1.05 秒後に開始されます。どちらの呼び出しも完全な結果を返します。 - 設定方法。
STAAN_RATE_LIMITは 1 秒あたりのリクエスト数です。1 以上では、小数は切り捨てられます(2.5の場合、1 秒あたり 2 件までです)。1 未満では、リクエストが1 / rate秒間隔で送信されます(0.5の場合、2 秒ごとに 1 件です)。0にするとレート制限は無効になります。20 を超える値も設定できます(ご利用のプランでは、より多くのリクエストが許可されている場合があります)が、Staan の既定の上限は 20 です。上限がそれより高いと確認できている場合にのみ、値を引き上げてください。 - 期限。 待機時間は呼び出し時間に含まれるため、
STAAN_DEADLINEの対象になります(1 件の通信中リクエストの所要時間を計測するSTAAN_TIMEOUTの対象ではありません)。期限を過ぎてからでないと送信できないリクエストは、期限超過エラーが直ちに発生し、送信されません。待機中に呼び出しがキャンセルされた場合、保留中のリクエストは送信されず、それらに割り当てられていた枠はほかのリクエストが利用できます。 - 適用範囲。 枠は 1 つのサーバープロセスに属します。同じ API キーを共有する複数のサーバープロセス(2 つのクライアント、または Docker コンテナーとローカルサーバーなど)は互いの状態を認識しないため、枠はそれぞれ加算されます。
STAAN_RATE_LIMITで枠を分配してください。 - ログ。 遅延したリクエストは、遅延時間とともに
DEBUGレベルで記録されます(ダウンロードして実行するタイプのバリアントでは、stderr に 1 行出力されます)。枠内のリクエストはログに記録されません。
設定
すべてのオプションは、フラグまたは環境変数で設定できます。環境変数よりフラグが優先されます。 空の環境変数は未設定として扱われます。
ダウンロードして実行する形式では、以下のStaan API変数のみを読み込み、--versionと--helpだけを受け付けます。サーバーとOAuthの表は、インストール済みパッケージに適用されます。
Staan API
| 変数 | デフォルト | 説明 |
|---|---|---|
STAAN_API_KEY | なし | Staan APIキー。上流にはAuthorization: Bearerとして送信されます。ツールを呼び出すには必須です。スペースを含まない表示可能なASCII文字である必要があります(貼り付けたスマート引用符を含むキーは、起動時に拒否されます。キーの値は表示されません)。 |
STAAN_BASE_URL | https://api.staan.ai/v2 | APIベースURL。ホストがループバックでない限りhttpsである必要があり、有効なポートを指定し、認証情報(user:password@)を含めることはできません。 |
STAAN_TIMEOUT | 30 | 1回の試行のタイムアウト(秒)。0より大きい値を指定してください。 |
STAAN_DEADLINE | 50 | リトライと待機を含む、1回のツール呼び出しの全体制限時間(秒)。0より大きい値を指定してください。クライアント独自のツールタイムアウト(多くの場合60秒)より短くしてください。 |
STAAN_MAX_RETRIES | 2 | 429、500、502、503、504、および接続エラーに対するリトライ回数(0~10)。読み取りおよび書き込みのタイムアウトはリトライされません。バックオフ時間はmin(0.5 * 2**attempt, 8)秒で、Retry-Afterが指定されている場合はその時間(最大30秒)が加算されます。 |
STAAN_RATE_LIMIT | 20 | リトライを含む、すべてのツールと市場を合わせた、サーバーが開始する1秒あたりの最大リクエスト数です。それ以降のリクエストは待機します(レート制限を参照)。数値を指定します。1以上の場合は切り捨てられ、1未満の場合はリクエスト間隔が1 / rate秒になります。0にすると制限が無効になります。Staanのデフォルト上限は20です。 |
STAAN_MCP_MAX_CONTENT_CHARS | 0(制限なし) | 検索結果ごとに保持するfull_contentテキストの最大文字数(0、または200以上)。これを超えるページは段落の境界で切り詰められ、[... truncated ...]と表示されます。どちらのsimple_responseモードにも適用されます。 |
サーバー
| フラグ | 環境変数 | デフォルト | 説明 |
|---|---|---|---|
--transport {stdio,http,stateless} | STAAN_MCP_TRANSPORT | stdio | 使用するトランスポートです。 |
--host HOST | STAAN_MCP_HOST | 127.0.0.1 | バインドアドレス(HTTPトランスポート)。 |
--port PORT | STAAN_MCP_PORT | 8000 | バインドポート(HTTPトランスポート)。 |
--path PATH | STAAN_MCP_PATH | /mcp | MCPエンドポイントのURLパスです。 |
--allowed-host HOST | STAAN_MCP_ALLOWED_HOSTS | なし | 許可するHostヘッダーの値です。フラグは複数回指定するか、変数ではカンマ区切りで指定してください。 |
--allowed-origin ORIGIN | STAAN_MCP_ALLOWED_ORIGINS | なし | 許可するOriginヘッダーの値です。同じ構文を使用します。 |
--insecure-no-auth | なし | オフ | OAuthゲートなしで、ループバック以外のHTTPバインドを許可します。信頼できる認証済みプロキシの背後でのみ使用してください。 |
--log-level LEVEL | STAAN_MCP_LOG_LEVEL | WARNING | ログレベル。HTTPサーバーにも適用されます(WARNINGでは、リクエストごとのアクセスログが非表示になります)。ログはstderrのみに出力されます。 |
--version | なし | なし | バージョンを表示して終了します。 |
OAuth ゲート
STAAN_MCP_AUTH_* 変数を設定すると、ゲートが有効になります(HTTP トランスポートのみ)。設定の一部が欠けている場合や、設定に矛盾がある場合は、起動エラーになります。
| 変数 | 必須 | 説明 |
|---|---|---|
STAAN_MCP_AUTH_ISSUER_URL | はい | 認可サーバーの発行者 URL です。トークンには、この値の iss が含まれている必要があります。 |
STAAN_MCP_AUTH_RESOURCE_URL | はい | この MCP エンドポイントの正規の公開 URL です。例:https://mcp.example.com/mcp。トークンのオーディエンスはこの値である必要があります。 |
STAAN_MCP_AUTH_SCOPES | いいえ | すべてのトークンに必要なスコープです。スペース区切りまたはカンマ区切りで指定します。 |
STAAN_MCP_AUTH_JWKS_URL | いずれか一方 | JWT アクセストークンの検証に使う JWKS URL です。oauth エクストラが必要です。 |
STAAN_MCP_AUTH_INTROSPECTION_URL | いずれか一方 | RFC 7662 のイントロスペクションエンドポイントです。クライアント ID とシークレットが必要です。 |
STAAN_MCP_AUTH_CLIENT_ID | イントロスペクション使用時 | イントロスペクション用のクライアント ID(HTTP Basic)です。 |
STAAN_MCP_AUTH_CLIENT_SECRET | イントロスペクション使用時 | イントロスペクション用のクライアントシークレットです。 |
STAAN_MCP_AUTH_ALGORITHMS | いいえ | 受け入れる JWT アルゴリズムです。デフォルトは RS256,ES256 です。none は常に拒否されます。 |
ホストがループバックの場合を除き、すべての URL は https である必要があり、認証情報(user:password@)を含めることはできません。JWKS URL とイントロスペクション URL のいずれか一方だけを設定する必要があります。
パスのないオリジンのみの URL(例:https://auth.example.com)は、SDK によって正規化されるため、末尾にスラッシュを付けて保護リソースのメタデータに公開されます。そのため、オリジンのみの発行者 URL とリソース URL は、どちらの表記もチェックで受け入れられます。aud または iss が https://host でも https://host/ でも、トークンは有効とみなされます。パスを含む URL(https://host/mcp)は完全一致で照合されます。
JWT の検証では、タイムアウトを 10 秒として JWKS を取得し、最大でも 30 秒に 1 回だけ更新します。これにより、kid が不明なトークンによってサーバーが ID プロバイダーに過剰なリクエストを送ることを防ぎます。この機能には PyJWT 2.14 以降が必要です。oauth エクストラでは、この要件が指定されています。イントロスペクションでは、共有接続プールを使って、認可サーバーに対しトークンごとに認証情報付きリクエストを 1 回送信します。4,096 文字を超えるトークン、または可視 ASCII 文字以外を含むトークンは、リクエストを送信せずにローカルで拒否されます。
セキュリティ:OAuth ゲートと脅威モデル
認証情報
- 上流:Staan は OAuth を使用しません。上流の認証情報は
STAAN_API_KEYの API キーのみです。このキーはサーバーが保持し、Authorization: Bearerとして送信します。ログに記録されることも、エラーメッセージに含まれることも、設定オブジェクトのrepr()に表示されることもありません。 - インバウンド:サーバーが受信したアクセストークンを Staan に転送することはありません(MCP 仕様で「トークンパススルー」が禁止されているためです)。トークンは、このサーバーの利用を許可するユーザーを判定するためにのみ使用されます。リクエストごとのクライアントキー用ヘッダーも、静的な共有ベアラートークンもありません。
OAuth ゲート
設定すると、HTTP トランスポートは MCP 認可仕様で説明されている OAuth 2.1 リソースサーバーとして動作します。認可サーバー(JWT を発行できるサーバー、または RFC 7662 のイントロスペクションに応答できるサーバー)は、ご自身で運用します。ゲートは次の処理を行います。
- RFC 9728 の保護リソースメタデータを公開し、認証されていないリクエストには、そのメタデータを参照する
WWW-Authenticateヘッダーを付けて401を返します。 - 各トークンについて、署名(JWKS)または
active(イントロスペクション)、exp、発行者、STAAN_MCP_AUTH_RESOURCE_URLと等しいオーディエンス、および必要なスコープを検証します。 - 設定されたアルゴリズムのみを受け入れ、
noneは決して受け入れません。 - 検証に失敗した場合はすべて無効なトークンとして扱い、その理由をトークンを含めずにデバッグレベルでログに記録します。
脅威モデル
| 脅威 | 軽減策 | 残存リスク |
|---|---|---|
| HTTP ポートが公開され、第三者に Staan の利用枠を消費される | デフォルトではループバックにバインドします。ループバック以外にバインドするには、OAuth ゲートまたは明示的な --insecure-no-auth に加え、--allowed-host が必要です。 | --insecure-no-auth を使うかどうかはご自身の判断です。使用する場合は、手前に認証を設けてください。 |
| DNS リバインディング、または悪意のある Web ページからローカルサーバーへの呼び出し | Host と Origin を検証します(ループバックでは SDK による保護、それ以外では許可リストを使用)。 | ワイルドカードを誤って許可リストに設定すると、保護が弱まります。 |
| 別のサービス向けに発行されたトークンがここで再利用される | Audience が正規のリソース URL と一致している必要があります。発行者、有効期限、スコープも検証します。 | 認可サーバーが正しい audience を発行することが前提です。 |
| アクセストークンの窃取 | 有効期間の短いトークン、スコープの確認、プロキシでの TLS を使用します。 | 有効なトークンは、有効期限が切れるまでは有効なユーザーとして扱われます。認可サーバーで失効させてください。 |
| ログやエラーによるキー漏えい | ログは stderr にのみ出力します。キー、Authorization ヘッダー、トークンはログに記録もエコーもしません。上流からの 5xx のテキストを中継することもありません。 | ご自身のクライアントやプロキシが引数やヘッダーをログに記録する可能性があります。 |
検索結果(full_content、スニペット)を介したプロンプトインジェクション | どちらのツールも読み取り専用で副作用がなく、ファイルへの書き込み、コードの実行、他のサービスの呼び出しはできません。 | 返されるページのテキストは、信頼できないサードパーティーのコンテンツです。MCP クライアントとモデルはそのように扱う必要があります。また、信頼できないページを確認せずに読むエージェントには、ほかの強力なツールを与えないでください。 |
コストの増大(market: "*"、多数のページ、再試行) | マーケットとドメインに上限を設け、再試行回数を制限します。すでに送信済みのリクエスト(読み取りタイムアウト)は再試行せず、呼び出し全体の期限を設け、課金に関する注意事項を文書化します。 | 1 回の呼び出しで、最大 12 件の課金対象リクエストが発生する可能性があります。Staan の利用制御を使用してください。 |
プロンプトインジェクションされたモデルが answer のソースを通じて、Staan に file://、内部向け URL、またはサイズの大きな URL を取得させる | ソース URL は、最大 2,048 文字の一般公開されているように見える絶対 http(s) URL でなければならず、プライベート、ループバック、リンクローカルの IP をリテラルで指定できません。 | 検査対象はリテラルの URL です。内部アドレスに解決されるホスト名は、この検査では検出されません。取得処理は Staan が行います。 |
| 匿名の呼び出し元が(偽造 JWT や無効な bearer トークンを使って)ID プロバイダーに大量のリクエストを送る | JWKS の更新をクールダウンし(PyJWT 2.14+)、タイムアウトを 10 秒に設定します。共有のイントロスペクション接続プールを 1 つ使用し、妥当でないトークンはローカルで拒否します。 | 妥当な未知のトークンごとに、リクエストごとのイントロスペクションで認可サーバーへ 1 件のリクエストが送信されます。プロキシでレート制限を行ってください。 |
| 通信経路上の平文トラフィック | すべての上流トラフィックで https を使用します(ループバック以外では http のベース URL を拒否)。OAuth URL も https を使用します。 | サーバー自身の HTTP リスナーは平文の HTTP です。手前で TLS を終端してください。 |
対象外:認可サーバーの運用、ユーザーごとの利用枠、マルチテナントでのキー分離。脆弱性を報告する場合は、公開 issue ではなく非公開のセキュリティアドバイザリーを利用してください。
トラブルシューティング
| 症状 | 原因と対処方法 |
|---|---|
ツールから STAAN_API_KEY is not set と表示される | クライアントの env ブロックにキーを追加するか、サーバーを起動する前に環境変数として設定してください。 |
| 認証エラー | キーが間違っているか、取り消されています。Staan のコンソールで新しいキーを作成してください。 |
answer が、この API キーでは有効になっていないと表示する | Answer API は招待制であり、Staan によって一時的に無効化される場合もあります。問題が続く場合は [email protected] にお問い合わせください。 |
market で有効な市場の一覧に関するエラーが表示される | 12 個のコードのいずれか、または en-* のようなワイルドカードを使用してください。 |
| 起動時にサーバーがステータス 2 で終了する | stderr のメッセージを確認してください。通常は、OAuth または --allowed-host を指定せずにループバック以外のホストを使用しています。 |
staan-mcp: cannot import the staan_mcp package(1 行、ステータス 2) | stdio_server.py がリポジトリから別の場所にコピーされています。他のモジュールと同じ src/staan_mcp/ に置くか、単一の staan-mcp-stdio.pyz バンドルを使用するか、インストール済みパッケージを使用してください。 |
staan-mcp needs Python 3.11 or newer; this is ...(ステータス 2) | クライアントの command が古いバージョンの Python を実行しています。必要に応じてフルパスで、3.11 以降のインタープリターを指定してください。 |
クライアントが python を起動できない | Windows では "command": "py", "args": ["-3", "C:\\path\\to\\staan-mcp-stdio.pyz"](またはインタープリターのフルパス)を使用してください。macOS と Linux では、コマンドに python3 を使うことがよくあります。 |
HTTP クライアントで 421 Misdirected Request が発生する | Host ヘッダーが許可されていません。--allowed-host で追加してください。 |
| ツールの結果がクライアントで切り詰められる、または拒否される | full_content: "markdown" または "html" を指定しています。これはサイズが大きく(1 回の呼び出しで数万トークン、複数の市場を指定するとさらに増加します)、デフォルトの "none" を使用するか、市場の数を減らすか、STAAN_MCP_MAX_CONTENT_CHARS を設定してください。 |
| 応答が遅い | 複数の市場は並列で処理されるため、1 回の呼び出しにかかる時間は最も遅い処理と同じになります。full_content による遅延の増加はわずかです。デフォルトの "none" では、主に回答が小さくなります。呼び出しは STAAN_DEADLINE 秒後に打ち切られます。1 秒間に 20 件を超えるリクエスト(market: "*" の呼び出し 2 件)は、レート制限により遅延します。 |
The request to Staan could not be built | STAAN_BASE_URL のポートが正しくないか、STAAN_API_KEY にヘッダーで送信できない文字が含まれています。メッセージで示された変数を修正してください。 |
| クライアントに何も表示されない | サーバーをターミナルで実行して stderr を確認してください:uvx staan-mcp --log-level DEBUG、または python staan-mcp-stdio.pyz(ダウンロードして実行する各方法ではフラグを指定できませんが、問題は stderr に記録されます)。stdio モードでは stdout に書き込まないでください。 |
開発
uv が必要です。適切な Python とすべてのツールをインストールします。
git clone https://github.com/MarcinWolnyEU/Staan-MCP.git
cd Staan-MCP
uv sync # creates .venv with all dependency groups (use UV_LINK_MODE=copy on Windows across drives)
uv run staan-mcp --version
品質ゲートは、すべて CI で必須です。
uv run --frozen ruff check . # every rule enabled, documented opt-outs only
uv run --frozen ruff format --check .
uv run --frozen mypy # --strict
uv run --frozen pylint src tests scripts # 10.00/10
uv run --frozen xenon --max-absolute B --max-modules A --max-average A src
uv run --frozen pytest # 100% line and branch coverage required
uv build && uv run --frozen --only-group dist twine check --strict dist/* && uv run --frozen --only-group dist check-wheel-contents dist/*.whl
uv run python scripts/build_zipapp.py # builds dist-bundle/staan-mcp-stdio.pyz; tests/repo run it
このプロジェクトは、OMP(Opus 5.5、Sonnet 5.5、Mimo V2.6 Pro)、 VSC、git と、 pyproject.toml に記載されている依存関係を使用して構築されました。
- ミューテーションテストには mutmut を使用します。Linux、macOS、または
WSL が必要です(ネイティブの Windows では実行できません。Windows では Docker を使用してください)。
uv run mutmut runを実行し、続けてuv run mutmut export-cicd-statsを実行します。CI はmutants/mutmut-cicd-stats.jsonをゲートとして使用します。スキップされるモジュールはstdio_server.pyのみです。 - ライブテストでは実際の Staan API を呼び出します。
STAAN_API_KEYが設定され、さらに--run-liveを指定した場合を除き、テストはスキップされます。実行するにはuv run pytest tests/live --run-live --no-covを使用します。CI では実行されません。 - フック:
uv tool install prek && prek install(またはpre-commit)を実行すると、ruff、mypy、 pylint、xenon、zizmor、actionlint、typos、server.json、ワークフロー、Dependabot のスキーマチェック、 シークレットスキャン(detect-private-keyと gitleaks)、Conventional Commits チェックが実行されます。CI では すべてのファイルに対して同じフックを実行し(プルリクエストのタイトルもチェックします)、git の 全履歴も gitleaks でスキャンします。可能であれば、キーを含むファイルはリポジトリのディレクトリの 外に保管してください。.gitignoreでは*key*.txt、*.pem、*.key、credentials*も対象にしています。 - Staan API、MCP SDK、OAuth に関する調査メモは
docs/research/にあります。変更内容については 変更履歴をご覧ください。
貢献
PR を作成し、パイプラインがすべて成功することを確認してください (コードカバレッジが 100%であること、lint と型チェックが通ること、プロジェクトの現在の互換性が維持されていることなどを含みます)。 変更を実装した 理由 と、変更に伴う リスク を説明してください。PR は私自身がレビューします。
すべてのプルリクエストには 2 週間以内に回答します。
フィードバックとバグ報告
Issues タブから報告してください。セキュリティに関する問題には 専用フォームを使用してください。
すべての Issue には 2 週間以内に、セキュリティアドバイザリには 48 時間以内に回答します。
ライセンス
MIT(c)2026 Marcin Wolny。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。