Discourse Mcp Server について
Discourse MCP は stdio 方式の MCP サーバーで、Discourse フォーラムの機能をツールとリソースとして AI エージェントに提供します。セルフホストとクラウドホストの両方のサイトに対応し、API キーを生成するだけで接続でき、書き込み操作は既定で無効です。
主な機能
- 01フォーラム検索、トピック・投稿の読み取りツール
- 02--allow_writes で有効化する任意の書き込みツール
- 03有効にするツールセットを選べる絞り込み機能
- 04プロファイルファイルによる複数サイトの認証設定
- 05stdio と loopback の Streamable HTTP トランスポート
活用シーン
- 01AI エージェントによる Discourse のトピック・投稿の検索と閲覧
- 02MCP クライアントからの Data Explorer の読み取り専用クエリ実行
- 03明示的に許可した後のエージェントによる投稿返信やドラフト管理
MCP 対応
このプロジェクト自体が MCP サーバーであり、Discourse のツールとリソースを MCP クライアントに公開します。クライアントは npx または discourse-mcp バイナリで起動できます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
12掲載時のツール一覧 · 3 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| site | string | はい | Discourse サイトのベース URL |
詳細
Discourse MCP
AI エージェント向けに、Discourse フォーラムの機能をツールおよびリソースとして公開する Model Context Protocol(MCP)stdio サーバーです。
- エントリーポイント:
src/index.ts→dist/index.jsにコンパイルされます(バイナリ名:discourse-mcp) - SDK:
@modelcontextprotocol/sdk - Node:>= 24
- バージョン:0.3.1(書き込みのオプトインを簡素化し、
--allow_writesだけで有効化できるようにしました。また、read_only=falseは非推奨になりました。0.3.0 ではオペレーターが選択できるツールセット、構造化されたディレクトリ出力、オプトインで利用できる管理機能の拡充が追加されました。0.2.x では、JSON のみのツール出力を含む、0.1.x からの破壊的変更が導入されました。カテゴリ/グループのリソースは、正式な一覧表示ツールと併存する非推奨の互換性機能です)
クイックスタート(リリース版)
- 実行(読み取り専用、まずはこちらがおすすめです)
npx -y @discourse/mcp@latest
次に、MCP クライアントで以下のいずれかを行います。
-
discourse_select_siteツールを{ "site": "https://try.discourse.org" }とともに呼び出してサイトを選択する、または -
--site https://try.discourse.orgを指定して、サイトに接続した状態でサーバーを起動する(この場合、discourse_select_siteは非表示になります)。 -
書き込みを有効にする(オプトイン、保護措置付き)
npx -y @discourse/mcp@latest --allow_writes --auth_pairs '[{"site":"https://try.discourse.org","api_key":"'$DISCOURSE_API_KEY'","api_username":"system"}]'
- Data Explorer の組み込みツールのみで実行する
npx -y @discourse/mcp@latest --toolsets data_explorer --tools_mode discourse_api_only
これにより、discourse_select_site と、読み取り専用の Data Explorer ツールが公開されます。必要に応じて --site、認証、書き込みフラグを追加してください。詳しくは組み込みツールセットを参照してください。
- MCP クライアントで使用する(例:Claude Desktop)— npx 経由
{
"mcpServers": {
"discourse": {
"command": "npx",
"args": ["-y", "@discourse/mcp@latest"],
"env": {}
}
}
}
代替方法:インストール後にグローバルバイナリを使用する場合、パッケージは
discourse-mcpを公開します。{ "mcpServers": { "discourse": { "command": "discourse-mcp", "args": [] } } }
設定
このサーバーは、MCP サーバー名 @discourse/mcp でツールを登録します。次のいずれかの方法で接続先の Discourse サイトを選択します。
-
実行時に
discourse_select_siteツールを使う(/about.json経由で検証されます)、または -
起動時に
--site <url>を指定して、サーバーを単一のサイトに接続する(/about.json経由で検証され、discourse_select_siteは非表示になります)。 -
認証
- デフォルトではなしです。
- 管理者 API キー(管理者権限が必要):
--auth_pairs '[{"site":"https://example.com","api_key":"...","api_username":"system"}]' - ユーザー API キー(任意のユーザーが生成可能):
--auth_pairs '[{"site":"https://example.com","user_api_key":"...","user_api_client_id":"..."}]' - HTTP Basic 認証(リバースプロキシの背後にあるサイト向け):任意の
auth_pairsエントリーにhttp_basic_userとhttp_basic_passを追加します。これは、リバースプロキシレベルで HTTP Basic 認証により保護されている Discourse サイトで便利です。 auth_pairsには複数のエントリーを含められます。選択されたサイトに一致するエントリーが使用されます。同じサイトにuser_api_keyとapi_keyの両方が指定されている場合は、user_api_keyが優先されます。
-
書き込みの安全性
- 書き込みはデフォルトで無効です。
- 組み込みの書き込みツールは、
--allow_writesが有効な場合にのみ登録されます。対象には、投稿、トピック、プライベートメッセージ、カテゴリ、ユーザー、アップロード、下書き、保存済みの Data Explorer クエリの変更が含まれます。 - プライベートメッセージの一覧表示と読み取りにも、一致する認証済みサイトが必要です。PM データは公開されないためです。
- ツールセットの選択によって書き込みの安全性が回避されることはありません。書き込みツールを選択しても、書き込みが有効になっていなければ利用できません。
- 書き込みツールには、選択されたサイトに一致する
auth_pairsエントリーが必要です。一致するエントリーがない場合、エラーが返されます。 - 書き込みアクションには約 1 リクエスト/秒のレート制限が適用されます。
-
フラグとデフォルト値
--help、-h、または位置引数のhelp:プロファイルの読み込みやトランスポートの開始前に、現在の CLI ヘルプを表示して正常終了します。--version、-v、または位置引数のversion:パッケージのバージョンを 1 行で表示して正常終了します。-vはバージョンを意味します。ログの詳細度には--log_levelを使用します。--allow_writes(デフォルト:false):変更ツールを有効にします。この明示的なオプトインだけで十分です。--read_only <boolean>:非推奨の互換設定です。trueは明示的な読み取り専用の上書き設定となり、--allow_writesと競合します。falseは効果がないため、コマンドやプロファイルから削除してください。--timeout_ms <number>(デフォルト:15000)--concurrency <number>(デフォルト:4)--log_level <silent|error|info|debug>(デフォルト:info)debug:HTTP リクエスト URL、ステータス、詳細なネットワーク情報と再試行情報を表示します(管理 API が機密情報を含む内容を返す場合があるため、レスポンス本文はログに記録されません)。info:再試行の試みと一般的な運用メッセージを表示します。error:エラーのみを表示します。silent:ログを出力しません。
--show_emails(デフォルト:false)。ユーザーツールにメールアドレスを含めます。管理者アクセスが必要です。--tools_mode <auto|discourse_api_only|tool_exec_api>(デフォルト:auto)--toolsets <name[,name...]>:選択した組み込みドメインを公開します。コンパクトなデフォルトカタログ(オプトイン不要のすべてのドメイン)を使用する場合は省略してください。オプトインが必要なカテゴリ/グループ/タググループ、モデレーション、ワークフロー、AI 管理の各ドメインを含めるには、--toolsets allを使用します。組み込みツールセットを参照してください。--site <url>:MCP を単一のサイトに固定し、discourse_select_siteを非表示にします。--default-search <prefix>:すべての検索クエリの先頭に、指定した文字列を必ず付加します(例:tag:ai order:latest)。--max-read-length <number>:投稿内容として返す最大文字数(デフォルト:50000)。discourse_read_postと、discourse_read_topicおよびdiscourse_read_private_messageの各投稿内容に適用されます。ツールはinclude_raw=trueを指定してrawコンテンツをリクエストします。--allowed_upload_paths <paths>:ローカルファイルのアップロードを許可するディレクトリの、カンマ区切りリストまたは JSON 配列です。discourse_upload_fileでローカルファイルのアップロードを有効にするには必須です。例:--allowed_upload_paths "/home/user/images,/tmp/uploads"または--allowed_upload_paths '["/home/user/images"]'。セキュリティ上重要なこれらのパスでは、~は展開されません。--transport <stdio|http>(デフォルト:stdio):デフォルトでは標準入出力を使用します。または、ループバック接続のみを受け付ける JSON レスポンス付き Streamable HTTP を使用します。HTTP は、プロセスごとにステートフルな MCP クライアント/セッションを 1 つだけ明示的にサポートします。初期化後のすべてのリクエストには、返されたMcp-Session-Idを含める必要があります。2 回目の初期化は拒否されます。セッションの DELETE/クローズ後に別のクライアントを接続するには、プロセスを再起動してください。クローズ状態では、/healthは503 restart_requiredを返します。リクエスト本文の上限は 4 MiB です。--port <number>(デフォルト:3000):HTTP トランスポートを使用する場合にリッスンするポートです。--cache_dir <path>(予約済み)--profile <path.json>(下記を参照)
-
プロファイルファイル(シークレットをコマンドラインに記載しないでください)
{
"auth_pairs": [
{
"site": "https://try.discourse.org",
"api_key": "<redacted>",
"api_username": "system"
},
{
"site": "https://example.com",
"user_api_key": "<user_api_key>",
"user_api_client_id": "<client_id>"
},
{
"site": "https://protected.example.com",
"api_key": "<redacted>",
"api_username": "system",
"http_basic_user": "username",
"http_basic_pass": "password"
}
],
"allow_writes": true,
"show_emails": true,
"log_level": "info",
"tools_mode": "auto",
"site": "https://try.discourse.org",
"default_search": "tag:ai order:latest",
"max_read_length": 50000,
"transport": "stdio",
"port": 3000,
"allowed_upload_paths": ["/home/user/images", "/tmp/uploads"]
}
次のコマンドで実行します:
node dist/index.js --profile /absolute/path/to/profile.json
# Current-user home expansion is also supported:
node dist/index.js --profile ~/discourse-mcp-profile.json
フラグは、プロファイルの値よりも優先されます。プロファイルのパスの先頭にある現在のユーザーの ~、~/、または ~\ は、現在のホームディレクトリに展開されます。~otheruser、その他の場所でのシェル形式の展開、アップロード許可リストの展開には意図的に対応していません。
組み込みツールセット
ツールセットを使うと、MCP クライアントに必要な組み込みドメインだけを公開できます。ツールセットは任意です。--toolsets とプロファイルのフィールドをどちらも省略すると、サーバーはデフォルトカタログ(search、discourse_search、discourse_filter_topics を含む)を登録します。下記で (opt-in) とマークされた管理用および専門的なドメイン(themes を含む)は、明示的に選択する必要があります。すべての組み込みドメインが意図的に必要な場合に限り、--toolsets all を使用してください。
名前を 1 つ指定するか、カンマ区切りで複数の名前を指定します:
# Data Explorer reads, plus the site-selection bootstrap tool
npx -y @discourse/mcp@latest \
--toolsets data_explorer \
--tools_mode discourse_api_only
# Search and topic tools, retaining canonical registration order
npx -y @discourse/mcp@latest \
--toolsets search,topics \
--tools_mode discourse_api_only
# Every built-in domain, including opt-in workflows
npx -y @discourse/mcp@latest \
--toolsets all \
--tools_mode discourse_api_only
# Author, test, and run workflows (admin key required)
npx -y @discourse/mcp@latest \
--toolsets workflows \
--site https://forum.example.com \
--auth_pairs '[{"site":"https://forum.example.com","api_key":"...","api_username":"system"}]' \
--allow_writes \
--tools_mode discourse_api_only
プロファイルでは配列を使用します(カンマ区切りの文字列も使用できます):
{
"toolsets": ["users", "uploads"]
}
利用可能なツールセットは次のとおりです:
| ツールセット | 組み込みツール |
|---|---|
site | discourse_select_site(未紐付けのサブセットに対するブートストラップとしても暗黙的に保持されます) |
search | トピックレベルの検索/フィルタリングと、投稿レベルのキーワードに基づく根拠 |
topics | トピック/投稿の基本的な読み取り、ストリームの厳密な選択、投稿検索、ユーザーの投稿アクティビティ、変更操作 |
users | ユーザーの検索/一覧表示、ユーザーの投稿アクティビティ、ユーザーの変更操作 |
chat | チャットメッセージの取得 |
drafts | 下書きの取得、保存、削除 |
uploads | ファイルのアップロード |
data_explorer | クエリの取得、実行、作成、更新、削除 |
private_messages | 認証済みユーザーの個人/グループ PM の一覧表示と読み取り、および書き込みが許可された作成、返信、参加者の招待 |
activity (オプトイン) | 返信関係、サイト全体の投稿アクティビティ、トピックの閲覧履歴、ユーザーのアクティビティ概要とタイムライン、ディレクトリ/コホートの指標 |
administration (オプトイン) | カテゴリーの検出、管理者に表示されるサイト設定、明示的な確認を伴うユーザーの有効化/承認状態の変更 |
site_settings (オプトイン、管理者向け機密操作) | マスクされたサイト設定の確認と、書き込みが許可され、事前チェック済みの通常の非機密設定の更新 |
webhooks (オプトイン、管理者向け機密操作) | Webhook と配信履歴の安全な確認、および書き込みが許可されたライフサイクル、ping、単一イベントの再配信操作 |
themes (オプトイン、管理者向け機密操作) | テーマ/コンポーネントの確認と、書き込みが許可されたローカルでの作成、編集、インストール、リモート同期、アセットのアップロード、保護された削除 |
groups (オプトイン) | 入力が空の場合のグループディレクトリ全件一覧、明示的なページ/フィルター互換モード、グループの完全な CRUD とメンバーシップ操作、固定ページに基づくグループ作成投稿の根拠 |
tag_groups (オプトイン、スタッフ向け機密操作) | 公開範囲でフィルタリングされた検索と、スタッフ向けの一覧/詳細表示、および書き込みが許可され、事前チェック済みの作成/更新/削除ライフサイクル |
moderation (オプトイン) | 認証済みのレビューキューのトリアージ、ユーザーの行動カウンター、範囲を限定した投稿リビジョン、新たに事前チェックされたレビュー可能な操作1件 |
workflows (オプトイン) | 管理者専用のワークフロー検出、グラフの作成、式の評価、ピンデータ、下書き実行、ステップ実行、実行履歴、バージョン管理 |
ai_agents (オプトイン) | 管理者専用の AI エージェント検出、型付きライフサイクル、ボットユーザーの作成、ポータブルなインポート/エクスポート |
ai_custom_tools (オプトイン) | 管理者専用のデータベースベースのスクリプト式カスタムツールのガイド、ライフサイクル、実際の実行テスト、インポート/エクスポート |
ai_features (オプトイン) | 管理者専用の AI 機能検出と、厳密な領域指定による非機密の機能設定更新。エージェントの検出も含みます |
analytics (オプトイン) | スタッフに表示される Discourse レポートの検出/実行と、Discourse Solved サポートダッシュボード |
ai_insights (オプトイン) | 読み取りを中心とする Discourse AI のキャッシュ済み要約、セマンティック検索、スタッフ向け感情分類 |
all (センチネル) | オプトイン領域を含むすべての組み込みツールセットに展開され、他の選択を吸収します |
ツールセットへの所属は、安全性および認可とは意図的に分離されています。
- 選択したツールセットは統合されます。複数の選択済みセットに含まれるツールも、正規の組み込み順序で一度だけ登録されます。
allはすべての実ドメインに展開され、ツールのメタデータにはなりません。 - 選択を省略すると、オプトイン専用のツールは除外されます。ツール定義でオプトインとデフォルトの両方の所属を混在させることはできないため、意図しないデフォルト公開を防げます。
discourse_select_siteは、サイトに紐付いていないすべてのサブセットで、ブートストラップ機能として自動的に保持されます。--siteを指定した場合は、通常どおり非表示のままです。- 読み取り専用モードでは、書き込みの有効化が必要なツールも引き続き削除されます。たとえば、
--toolsets data_explorerを指定すると、クエリの取得と実行がデフォルトで公開されます。保存済みクエリの変更も公開するには、--allow_writesを追加してください。 - 既存の呼び出し時認証と管理者チェックに変更はありません。Data Explorer ツールの呼び出しには引き続き管理者アクセスが必要です。グループ操作には、Discourse 独自のスタッフ、オーナー、公開範囲、セルフサービスに関する認可ルールが適用されます。
- ツールセットがフィルタリングするのは組み込みツールのみであり、認可や完全な機能境界ではありません。MCP リソースとプロンプトは引き続き利用可能で、既存の呼び出し時アクセスチェックが引き続きすべての判断基準となります。リモート Tool Execution API の検出は
--tools_modeで独立して制御されます。MCP ツール一覧を選択した組み込みドメインのみにする必要がある場合は、--tools_mode discourse_api_onlyを使用してください。選択したツールセットとリモート検出を併用すると、サーバーは情報通知をログに記録します。 - 現在の安全設定では、選択したドメインにツールが含まれない場合があります。たとえば、読み取り専用モードの
uploadsが該当します。この場合、サーバーは情報通知をログに記録します。 - 不明または空のツールセット選択は設定エラーです。値は空白をトリミングした後に重複が除去されます。
カテゴリー、グループ、タググループのディレクトリ
ディレクトリ機能は意図的にオプトイン方式になっているため、--toolsets を省略してもカテゴリー/グループ/タググループのツールは一切追加されません。
discourse_list_categoriesを使用するにはadministrationを選択します。入力が空の場合、デプロイ環境が許可していれば、1 始まりのカテゴリー検索エンドポイントを使用して、範囲を制限した網羅的な走査を行います。匿名 POST が拒否された場合は、ページネーション付きのネストされたカテゴリーインデックス GET を返します。それも拒否された場合に限り、レガシーの/site.jsonを返します。いずれの場合も、結果にはanonymous_fallback/legacy_site_jsonの不完全性を示すメタデータが明示され、網羅的な結果としては返されません。任意のterm、max_pages、max_requests、max_results、deadline_msを指定すると、対象を絞った検出を制限できます。カテゴリーインデックスでは検索語がサポートされていないため、フォールバック時の語句一致はローカルで適用されます。カテゴリーのレコードには URL/階層フィールドが保持されます。parent_category_idが正規のフィールドで、値は null になる場合があります。一方、pidは互換性のために維持されるレガシーのエイリアスです。入力がない場合の既存の情報量豊富な出力は意図的なものです。src/test/directory_tools.test.tsにある再現可能な 300 レコードのフィクスチャは約 45 KB になるため、このリリースでは別のfields契約を追加せず、有用な件数/アクセスフィールドを維持しています。discourse_list_groupsを使用するにはgroupsを選択します。{}が正規の網羅的な重複排除操作です。{ "page": 0 }や{ "asc": false }など、既存キーを明示的に指定すると、従来の 1 ページ/フィルター付きクエリの動作が維持されます。どちらのモードも{ groups, meta, extras?, total_rows_groups?, load_more_groups? }を返します。フィルターモードでは、意図的にcomplete: falseとなります。- ディレクトリとタググループの成功結果では、MCP
outputSchemaが提示され、structuredContentが返されます。JSON テキストコンテンツは、構造化出力を利用しないクライアント向けに、同じ正規化済みの値になります。不正な形式の上流レコードは、プロトコルの出力検証エラーではなく、通常のisError: trueツール結果を返します。
6 つのツールを使用するには、専用の tag_groups ドメインを選択します。
discourse_search_tag_groupsは公開されており、Guardian によるフィルタリングが適用されます。常に明示的な上限を送信し、結果が切り詰められている可能性を報告します。検索結果にはタググループ ID、親グループ、権限が含まれないため、正式な一覧としては扱えません。大文字と小文字を区別しないグループ名の完全一致が照合キーになります。qとnamesは AND 条件で組み合わされ、上流では%と_が SQL LIKE のワイルドカードとして扱われます。discourse_list_tag_groupsとdiscourse_get_tag_groupの実行には、設定済みの API 認証情報の形式と、上流でのスタッフ権限が必要です。ローカルのヘルパーではスタッフロールを確認できません。正規の判定は Discourse が行い、非スタッフにはプライバシー保護のため 404 を返します。タグ付けが無効でも、読み取りは可能な場合があります。discourse_create_tag_group、discourse_update_tag_group、discourse_delete_tag_groupの実行には、さらに有効な書き込みモードと、上流のtagging_enabledが必要です。MCP の入力および正規化された出力では、権限を明示的なエントリとして表します。たとえば[{"group_id":0,"access":"full"},{"group_id":9,"access":"readonly"}]のようになります。グループ ID0は、Discourse に組み込まれている「全員」グループです。サーバーは HTTP 境界でのみ、これらのエントリを Discourse の数値による権限マップ(1= full、3= readonly)に変換します。parent_tagは{id}または{name}を指定する省略可能なセレクターです。親グループなしで作成する場合は省略するか、nullを指定します。クライアント側の空欄のプレースホルダーは省略として扱われます。セレクターに新しい名前を指定するにはallow_tag_creation=trueが必要です。永続タグが作成され、通常のインデックス作成やプラグインフックが実行されるためです。- 更新には最新の
expected_state_hashが必要です。省略されたフィールドはローカルでマージされ、部分的な上流リクエストボディでは状態がクリアされるため、タグ、親グループ、トピックごとに 1 つという設定、権限をすべて送信します。タグや親グループの削除、権限の置換、シリアライザーが補完した「全員/full」のレガシー権限が実体化する可能性がある場合は、明示的な確認が必要です(acknowledge_possible_synthetic_permission_materializationも含みます)。ハッシュは MCP の楽観的な事前条件であり、上流のアトミックロックではありません。事前確認の後も競合が発生する可能性があります。 - 削除には、ID、名前、ハッシュの完全一致に加え、カスケード削除と未解決のプラグインに関する明示的な確認が必要です。削除ではメンバーシップ、権限、カテゴリの許可/必須の関連付けがカスケード削除されますが、タグやトピックとタグの関連行は削除されません。プラグイン依存関係の検出は網羅的ではありません。Discourse のスコープ付き API キーのアクションマップでは、削除が許可されない場合があります。削除後の GET で対象が存在しないことを確認できるまでは、200 の応答確認を成功とはみなしません。ディスパッチ後の結果が不確かな場合は、再試行不可の
outcome_unknownエラーになります。
これらのツールセットが制御するのは検出のみです。スタッフロール、Guardian の可視性、スコープ付きキーの権限、書き込みモード、サイト設定は、呼び出し時に上流側で判定されます。
Webhook とサイト設定
webhooks および site_settings のツールセットは、オプトイン方式で、管理者権限に関わる機能です。どちらかのツールセットを選択して制御できるのは、ツールの検出のみです。認可ではありません。各呼び出しには、選択したサイトに一致する管理者相当の認証が必要です。Discourse が最終的な認可と検証の権限を持ち、変更にはさらに --allow_writes が必要です。既存のサイト設定の読み取りは引き続き administration から利用できますが、サイト設定の変更を検出できるのは、明示的に site_settings を選択した場合のみです。
# Inspect safe webhook summaries and bounded delivery diagnostics
npx -y @discourse/mcp@latest --site https://forum.example.com \
--toolsets webhooks --tools_mode discourse_api_only \
--auth_pairs '[{"site":"https://forum.example.com","api_key":"...","api_username":"system"}]'
# Deliberately enable guarded site-wide setting changes
npx -y @discourse/mcp@latest --site https://forum.example.com \
--toolsets site_settings --tools_mode discourse_api_only \
--auth_pairs '[{"site":"https://forum.example.com","api_key":"...","api_username":"system"}]' \
--allow_writes
Webhook の配信、ping、再配信では外部システムにリクエストが送信されます。エンキューまたは HTTP の成功は、送信先でイベントが正しく処理されたことを証明するものではありません。Webhook のシークレットは返されず、URL のユーザー情報は削除され、クエリ値はマスクされ、生のイベントヘッダーは引き継がれません。イベントペイロード/本文のプレビューには、機密コンテンツの明示的な確認が必要であり、サイズ上限が設けられ、認証情報は除去されます。一括再配信には意図的に対応していません。
サイト設定はフォーラム全体に影響します。読み取り時には、上流でシークレットとして扱われる名前と、認証情報に似た名前の両方がマスクされます。現在の値がデフォルト値と異なる設定のみを一覧表示するには、overridden_only: true を指定します。更新できるのは、直近の読み取りで確認できる通常の設定を一度に 1 件だけです。期待する現在の値と確認が必要であり、結果は値を完全一致で再読み取りして検証します。シークレット/認証情報、アップロード、アップロード済み画像の一覧、構造化オブジェクトの各設定、一括更新、および既存ユーザーへのバックフィルには意図的に対応していません。
テーマ管理
オプトイン方式の themes ツールセットは管理者権限に関わる機能であり、デフォルトのカタログには含まれません。読み取り専用で選択した場合は、discourse_list_themes と discourse_get_theme のみが登録されます。変更にはすべて --allow_writes が必要です。ツールセットの選択によって管理者アクセス権が付与されることはありません。一致するサイト認証を設定してください。管理者権限、リポジトリの許可リスト、依存関係、コンパイラ、インポート、移行の確認は、引き続き Discourse が最終的な権限を持ちます。
テーマの HTML、JavaScript、SCSS、設定の移行、アセット、サードパーティのリポジトリは、すべての訪問者に対してコードを実行またはデプロイする可能性があります。ツールには操作ごとの確認が必要ですが、サードパーティのコードをサンドボックスで実行したり、検証したり、安全だと保証したりするものではありません。ローカルアーカイブとアセットを使用できるのは、サイズ上限内の Base64 入力、またはシンボリックリンクを解決した --allowed_upload_paths のルート配下にある通常ファイルのみです。
すべてのツールセットを有効にするのではなく、運用者が意図を持って設定してください。
discourse-mcp \
--toolsets themes \
--site https://forum.example.com \
--auth_pairs '[{"site":"https://forum.example.com","api_key":"...","api_username":"system"}]' \
--allow_writes \
--allowed_upload_paths /srv/discourse-theme-inputs
ローカルテーマと ZIP からインポートしたテーマは直接編集できます(ただし、Discourse の詳細シリアライザーでは ZIP のソース値が省略されます)。Git 管理のテーマは、リポジトリ内で変更して同期する必要があります。コンポーネントをデフォルトまたはユーザー選択可能にすることはできず、独自のカラースキームを持つこともできません。テキストフィールドとアップロードフィールドは別々のスキーマバリアントです。SCSS/HTML/JavaScript にプレースホルダーのアップロード ID を送信しないでください。
{
"fields": [{
"name": "scss",
"target": "common",
"operation": "replace",
"type": "scss",
"value": "body { background: #241914; }"
}]
}
インストールでも、ネストされたソースバリアントを 1 つ使用します。リポジトリからインストールする場合、アーカイブのプレースホルダーは不要です。
{
"source": {
"kind": "repository",
"remote_url": "https://github.com/example/discourse-theme.git",
"branch": "main"
},
"confirm_external_code": true
}
このリリースでは、プライベートリポジトリの鍵管理、リポジトリの向き先変更、エクスポート、一括削除、任意のテーマ対応サイト設定の変更、および汎用的なコントローラーのパラメーター引き継ぎには意図的に対応していません。
グループ管理
オプトインの groups ツールセットでは、カスタムグループのライフサイクル全体を扱えます。ディレクトリの一覧表示と詳細情報の完全な読み取り、作成、更新、完全削除、ページ分割されたメンバーとオーナーの読み取り、ユーザー名・数値のユーザー ID・既存アカウントのメールアドレスを指定してメンバーを追加・削除したり、オーナーに昇格・降格させたりする明示的なセレクター別ツール、保留中のリクエストの一覧表示と承認・拒否、認証済みのリクエスト、公開参加、公開退出の各フローに対応しています。アカウントをまだ持っていない可能性がある宛先には別の招待ツールを使うため、アカウントの検索とフォーラムへの招待を混同せずに済みます。
すべての変更には --allow_writes が必要です。作成と削除にはさらに、MCP アクセスゲートでスタッフ/管理者相当の API 認証情報が必要です。Guardian のチェック、グループの可視性、スタッフとオーナーの権限、自動グループの制限、メンバーシップ設定、招待の上限、呼び出し元が更新できるフィールドは、引き続き Discourse が決定します。コアの自動グループは作成・削除できず、メンバーシップやオーナーシップも変更できません。ただし、許可されたスタッフは表示設定と操作設定を更新できます。ツールセットを選択しても、これらの権限が付与されるわけではありません。
モデレーションキュー
オプトインの moderation ツールセットでは、discourse_get_review_queue_count、discourse_list_reviewables、discourse_list_reviewable_topics、discourse_get_reviewable を読み取り専用で利用できます。これらのツールには認証設定が必要ですが、意図的に MCP の管理者限定ゲートは設けていません。スタッフおよびカテゴリーモデレーターに対する可視性は、引き続き Discourse Guardian が決定します。ツールセットを選択しても、モデレーション権限が付与されるわけではありません。
キューの合計数には discourse_get_review_queue_count を使用します。その count は、呼び出し元に表示される保留中のレビュー対象レコード数であり、個々のフラグ数ではありません。通常のトリアージには、discourse_list_reviewables を status: "pending" と offset: 0 のみを指定して使ってください。トピック、カテゴリ、種類、ユーザーのフィルターを作り出してはいけません。has_more が false になるまで next_offset をたどってください。数値のトピック/カテゴリのプレースホルダー 0 と、任意のテキストプレースホルダーである空欄/all/any は指定なしとして扱われるため、厳密なスキーマを持つクライアントが誤って ID 1 でフィルターしたり、無効な全件指定のセンチネル値を送信したりすることはありません。一覧結果には、範囲を制限した証拠と動的なアクションがすでに含まれているため、キュー内の各項目に対して discourse_read_topic や詳細取得を大量に呼び出さないでください。discourse_list_reviewable_topics は利便性のための集約にすぎません。上流の処理では、レビュー優先度が最低しきい値以上の保留中のトピックが含まれ、トピックのないキュー項目は省かれます。また、score_count はレビュー対象項目数ではなく、レビューのスコア/フラグレコード数を示します。完全なキューサイズの推定には使用しないでください。
--allow_writes が設定されている場合、discourse_perform_reviewable_action も登録されます。まず一覧または詳細を取得し、正確な available_actions[].id を1つ、confirm: true とともに送信してください。「投稿を削除」のような繰り返し表示されるラベルではなく、アクションの説明全体に基づいて選択してください。Discourse の UI アクション ID には post-… や user-… のプレフィックスが付く場合がありますが、ルートでは対応する server_action が必要です。MCP がこれを検証して自動的に対応付けます。モデレーションの変更は、最新の GET から PUT までの処理全体で直列化され、適切な間隔を設けて実行されるため、モデルによる同時バッチ処理で書き込みスロットルを回避することはできません。このツールは任意の期待バージョンを確認し、告知されていないフィールドを拒否して、正規化された成功/件数フィールドを返します。PUT 後に失敗した場合、結果は識別子付きで不明として示されます。むやみに再試行せず、検証してください。申告情報、楽観的競合、アクションの有効性、Guardian の権限は、引き続き Discourse が強制します。これらのツールが提供するのは証拠と明示的な操作であり、モデレーションの判断を推奨するものではありません。
プライベートメッセージ
デフォルトの private_messages ツールセットでは、汎用的な公開トピックの変更機能を再利用するのではなく、プライベートメッセージに対応したインターフェースを提供します。一覧表示と読み取りには、認証の設定が必要です。作成、返信、招待には、さらに --allow_writes が必要です。メールボックスの可視性、プライベートメッセージのメンバーシップ、受信者数の上限、グループへのメッセージ送信可否、およびすべての Guardian/API キーのチェックについては、Discourse が引き続き正規の判断元となります。
個人のメールボックスでは、inbox、sent、archive、unread、new を利用できます。グループのメールボックスでは、sent を除くすべてを利用できます。個人の受信トレイには、すべてのグループ受信トレイが含まれるわけではありません。discourse_list_private_messages で username を省略すると、/session/current.json を通じて認証済みユーザーが特定されます。username を指定すると、そのユーザーになりすますのではなく、メールボックスのパスが選択されます。Discourse では、認可ルールで許可されている場合に限り、他のユーザーの受信トレイ、送信済み、アーカイブを参照できます。一方、管理者であっても unread と new は所有者本人のみが利用できます。
プライベートメッセージの受信者には、ユーザー名、グループ名、またはメールアドレスを指定します。作成時、メッセージを送信できるグループが同名のユーザーよりも Discourse の上流の分類で優先されます。そのため、存在しない、またはメッセージを送信できない group_names の値は、上流でユーザーが見つからない場合と同様のエラーになることがあります。サイト設定と送信者の権限で許可されている場合、未知のメールアドレス宛てに送信すると、ステージングユーザーがすぐに作成されることがあります。ステージングユーザーの作成を意図していない場合は、正式なユーザー名を使用してください。
メールアドレスへの招待は、意図的に詳細が返されない仕組みになっています。成功応答は、配信されたことや、参加者がすぐにアクセスできることを確認するものではありません。既存アカウントに属するメールアドレスの場合、成功しても何も行われないことがあります。一方、新しいメールアドレスでは、招待を受諾した後にのみアクセスできるようになります。既知のアカウントをすぐに追加するには、username を使用してください。グループの招待検索では大文字と小文字が区別されるため、正式なグループ名を使用してください。省略可能な author_username を指定すると、Api-Username が送信されます。ユーザーの切り替えは、適切なグローバル API キーでのみサポートされます。ユーザー API キーは所有者に紐付けられたままです。
ワークフローの作成
workflows ツールセットは、実験的な discourse-workflows プラグイン(enable_discourse_workflows)を対象としており、管理者 API キーが必要です。一般的な手順は次のとおりです。
- ノードタイプを一覧表示し、次に特定の
identifierを指定して、そのパラメーターのスキーマ、出力ポート、$jsonのコントラクトを取得します。 - カテゴリー、タグ、グループ、ユーザー、バッジ、チャットチャンネル、またはデータテーブルの ID を解決します。
- テンプレートから作成するか、小規模なグラフ全体を送信します。
- 編集の直前にワークフローを GET します。完全な
nodesとconnectionsを指定して置き換えるか、機械的な編集には MCP 側のoperations[]を使用します。グラフ全体を更新する際にノードを省略すると、そのノードは削除されます。 - ピンデータとステップ実行を追加するか、現在のドラフトを手動で実行し、
discourse_get_workflow_executionをポーリングします。 - テスト後に
published: trueを設定します。
[{"from":"Start","to":"Check","type":"main"}] のようなフラット形式の接続が受け付けられ、Discourse のネストされたワイヤー形式に変換されます。ソースノードのカタログにある出力キーを使用してください。条件/フィルターのポートは、常に main とは限らず、true と false です。MCP は HTTP を呼び出す前に、片側のみのグラフ更新を拒否します。実行はドライランではなく、投稿の作成、チャットメッセージの送信、外部 HTTP の呼び出しが行われることがあります。
Discourse AI の管理
3 つの AI 管理ドメインを利用するには、Discourse 管理 API キー(または選択したエンドポイントで使用できる管理者ユーザーの API キー)が必要です。各ドメインは個別にオプトインでき、デフォルトでは無効です。変更操作とカスタムツールのテスト実行には、--allow_writes も必要です。
# Configure agents without exposing scripted source management
npx -y @discourse/mcp@latest --site https://forum.example.com \
--toolsets ai_agents --tools_mode discourse_api_only \
--auth_pairs '[{"site":"https://forum.example.com","api_key":"...","api_username":"system"}]' \
--allow_writes
# Assign agents and update safe feature settings, but do not expose custom-tool code editing
npx -y @discourse/mcp@latest --site https://forum.example.com \
--toolsets ai_agents,ai_features --tools_mode discourse_api_only \
--auth_pairs '[{"site":"https://forum.example.com","api_key":"...","api_username":"system"}]' \
--allow_writes
エージェントインデックスは、デフォルトでは意図的に簡潔な内容になっています。discourse_ai_list_agents はシステムプロンプトとエージェントごとの設定を省略し、subagent_count を含む概要カウントに加えて、簡潔なツール/モデルカタログを返します。特定のエージェントの全設定を確認するには、ID を指定して discourse_ai_get_agent を使用してください。view: "full" は、完全な上流インデックスを明示的に必要とするクライアントのみが利用できます。
エージェントの作成/更新スキーマでは、subagent_ids を指定できます。これは、親エージェントが委任できる、既存エージェント ID の順序付き許可リストで、最大 20 個の重複しない ID を指定できます。システムエージェントには負の ID も使用できます。Discourse はすべての ID が存在することを検証し、自己委任を拒否するとともに、設定済みのツールが生成された spawn_agent ツールと衝突しないようにします。書き込みを行う前に、エージェント一覧または詳細レスポンスで ID を確認してください。
discourse_ai_list_custom_tools も同じ形式で、スクリプト、バインディング、詳細なパラメーターのドキュメントを含まない簡潔なレコードとプリセットのシグネチャを返します。保存済みのツールを 1 つ確認するには discourse_ai_get_custom_tool を使用してください。また、完全なプリセット例を 1 つ確認するには、topic: "presets" と preset_id を指定してガイドを呼び出してください。
ai_custom_tools は、Discourse のデータベースに保存される AiTool レコードを管理します。これは、/ai/tools で動的に検出されるリモートツールとは別の機能であり、リモートツールは引き続き --tools_mode で制御されます。スクリプトの作成には、同期型の MiniRacer JavaScript を使用します。invoke(parameters) を定義してください。async、ブラウザー API、Node モジュールは使用しないでください。必要な内容に絞って discourse_ai_get_custom_tool_guide を呼び出してください。preset_id は任意で、topic: "presets" の場合にのみ意味があります。それ以外のトピックでは無視されます。スクリプトを作成するとき、または大幅に変更するときは、選択したサーバーの正確な契約内容を確認するために topic: "preamble" を使用してください。同じ正確なライブプリアンブルと最小テンプレートは、このツールセットが選択されている場合に限り、条件付きリソース discourse://ai/custom-tools/authoring-guide としても公開されます。リソースの取得はアプリケーション側で行われますが、ガイドツールの呼び出しはモデル側で制御されます。そのため、自律型クライアントはホストがリソースを添付したと想定せず、ツールを使用してください。将来的に作成者向けプロンプトを任意で追加する場合、それはユーザーが制御し、明示的に開始されたワークフローをガイドするものであり、モデルが呼び出せる検出機能に取って代わるものではありません。
安全性: discourse_ai_test_custom_tool はコードを実際に実行するため、外部リクエストを送信したり、サイトに副作用を生じさせたりする可能性があります。機能の更新は本番環境の動作を即座に変更し、1 つの正確な ai-features/<module> 領域から返される、秘密情報以外の設定に制限されます。カスタムツールのソース、プロンプト、バインディング、エクスポート、テストパラメーターは機密情報として扱ってください。必要最小限のツールセットを組み合わせ、まず本番以外のサイトでテストしてください。
-
リモートツール実行 API(任意)
tools_mode=auto(デフォルト)またはtool_exec_apiの場合、サイトを選択した後(または--siteが指定されていれば起動時に直ちに)、サーバーは GET/ai/toolsでリモートツールを検出し、動的に登録します。リモートツールの検出を無効にするには、--tools_mode=discourse_api_onlyを設定してください。
-
ネットワークと復元力
- 429/5xx エラー時に、バックオフを適用して再試行します(3 回)。
- 選択したエンドポイントへの軽量なインメモリ GET キャッシュ。
-
プライバシー
- ログではシークレット情報がマスキングされます。エラーは、人が読めるメッセージとして MCP クライアントに返されます。
MCP リソース
リソースは、アプリケーションからアクセスできる静的/準静的な読み取り専用データを提供します。カテゴリリソースとグループリソースは、非推奨の互換性インターフェースとして維持されています。これらに代わる正規のモデル呼び出し可能な機能は、上記のオプトイン方式のディレクトリツールです。その他のリソースは、MCP ホストがアタッチする場合に引き続き適しています。
-
discourse://site/categories (非推奨;
--toolsets administrationを指定してdiscourse_list_categoriesを使用してください)- 同じ完全な範囲制限付き/キャッシュ済みのカテゴリ取得機能を使用し、その後、範囲を限定した ID チャンク単位で権限を補完します。
- 出力:
{ categories: [{id, name, slug, parent_category_id, pid, read_restricted, topic_count, post_count, perms?}], meta: {total, reported_total, pages_fetched, complete, has_more, truncated_reason?} } parent_category_idが正規のフィールドです。pidはレガシー互換性のための別名です。permsは{gid, perm}の配列で、perm:1=フル、2=投稿作成、3=読み取り専用です。permsは、選択された ID で権限の補完情報を取得できる場合にのみ設定されます。それ以外の場合は、値を捏造せず省略されます。
-
discourse://site/tags
- 使用数とともにすべてのタグを一覧表示します
- 出力:
{ tags: [{id, name, count}], meta: {total} }
-
discourse://site/groups (非推奨;
--toolsets groupsを指定してdiscourse_list_groupsを使用してください)- 同じ完全な範囲制限付き/キャッシュ済みの網羅的なグループ取得機能を使用し、アップストリームのエラーを、サイトにグループがないかのように扱わず、明示的に報告します。
- 出力:
{ groups: [{id, name, automatic, user_count, vis, members_vis, mention, msg, public_admission, public_exit, allow_membership_requests}], meta: {total, reported_total, pages_fetched, complete, has_more, truncated_reason?} } - レベル(0~4):0=公開、1=ログイン済みユーザー、2=メンバー、3=スタッフ、4=オーナー
- ユースケース:カテゴリ権限の
gid値からグループ名を特定する、移行時にグループ設定を複製する
-
discourse://chat/channels
- 公開チャットチャンネルをすべて一覧表示します
- 出力:
{ channels: [{id, title, slug, status, members_count, description}], meta: {total} }
-
discourse://user/chat-channels
- 未読数/メンション数とともに、ユーザーのチャットチャンネル(公開チャンネル+DM)を一覧表示します
- 出力:
{ public_channels: [...], dm_channels: [...], meta: {total} } - 認証が必要です
-
discourse://user/drafts
- ユーザーの下書きを一覧表示します
- 出力:
{ drafts: [{draft_key, sequence, title, category_id, created_at, reply_preview}], meta: {total} } - 認証が必要です
-
discourse://ai/custom-tools/authoring-guide (条件付き)
ai_custom_toolsが選択されている場合にのみ登録されます(allを通じて選択された場合を含む)- 選択されたサイトの
empty_toolJavaScript プリセットそのものを返します。内容は、Discourse の現在のプリアンブルと最小限のinvoke/detailsテンプレートです。 - MIME タイプ:
text/javascript。選択されたサイトの管理者認証情報が必要です。 - アプリケーションはこのリソースをアタッチできます。モデルは
topic: "preamble"を指定したdiscourse_ai_get_custom_tool_guideで同じ内容を取得できます。
エビデンスと分析機能
拡張された読み取りカタログでは、MCP が作成した判断ではなく、上流のエビデンスを公開します。
discourse_searchは引き続きトピックに焦点を当てます。一致した投稿が必要な場合は、discourse_search_postsを使用してください。強調表示された概要、投稿者、トピック/カテゴリ、正確な継続情報とともに、件数を制限した投稿 ID を保持します。これは、トピック検索結果に上限のない、投稿 ID のみのリストを追加するという以前の提案に代わるものです。discourse_ai_semantic_searchは、個別に有効化する Discourse AI の埋め込み検索です。オプトインのactivityツールdiscourse_list_latest_postsは、投稿 ID カーソルを使う固定 50 行の時系列フィードであり、検索ではありません。- デフォルトの
discourse_read_topic_postsは、正確な ID、最初/最新の投稿、特定の投稿を中心としたウィンドウ、またはユーザー名で絞り込んだ投稿を選択します。最初/最新の投稿を選択する場合、上流へのリクエストは最大 2 回で、選択件数は 50 件までです。オプトインのactivityツールdiscourse_get_post_repliesは、再帰的な子孫 ID、20 行の直接返信、サイトの上限内にある祖先の履歴を区別します。 - トピックと投稿の読み取りでは、プラグインが提供する場合、Discourse Solved のフィールドを保持します。承認済みの回答は解決状況を推測するための情報であり、元の投稿者が満足している証拠ではありません。
- オプトインの
activityドメインには、プロフィールで確認できる集計情報を取得するdiscourse_get_user_summary、名前付きイベントのページネーション対応タイムラインを取得するdiscourse_list_user_actions、公開されているディレクトリ/コホート指標を取得するdiscourse_list_directory_itemsが含まれます。既存のデフォルトのdiscourse_list_user_postsは、引き続き互換性のある投稿/返信ビューを提供します。 - オプトインの
administrationドメインでは、カテゴリと管理者に表示されるサイト設定をモデルから呼び出せるようにし、確認済みの有効化/承認状態の変更機能を提供します。ユーザーの作成にはグローバル管理者 API キーが必要です。上流からの応答にuser_idが含まれない場合、未確認であることを明示します。 - トピック/投稿の
author_usernameにはグローバル API キーが必要です。また、作成時の応答では、要求された帰属先と実際の帰属先の両方を報告するため、偽装が無視された場合に成功と誤認することはありません。 - オプトインの
analyticsドメインでは、レポートを実行する前にスタッフが閲覧できるレポートカタログを検出し、Solved サポートダッシュボードを公開します。ダッシュボードの「未回答」は、該当する通常の返信がなく、未解決であることを意味します。指定されたチームから返信がないという意味ではありません。 - オプトインの
ai_insightsドメインには Discourse AI が必要です。キャッシュされた要約は古さを報告し、セマンティック検索は引き続き Guardian のフィルターが適用されます。センチメント値は上流モデルによる分類であり、議論、満足度、リスクを客観的に示すラベルではありません。
プラグイン固有の 404 応答は、意図的に capability_or_resource_unavailable として報告されます。同じ上流応答が、プラグイン/設定の無効化、非表示のリソース、または存在しないリソースを意味する場合があるためです。ツールセットを選択しても、閲覧権限やスタッフ権限が付与されるわけではありません。
構成例:
latest/replies_onlyモードでdiscourse_read_topic_postsを使ってスレッドの最新状況を把握し、その後、呼び出し元のモデルで順序付けられたエビデンスを要約します。- Solved のトピックフィルター、承認済み回答のフィールド、サポートダッシュボードを組み合わせて回答状況を評価し、「解決済み」はあくまで推測のための情報であることを明示します。
discourse_list_group_postsとトピック ID を使ってグループの参加状況を確認します。グループのメンバーによる投稿はエビデンスですが、組織上の責任を証明するものではありません。- 正確な投稿/返信チェーンを取得し、必要に応じて上流のセンチメント分類も確認して、潜在的な対立を調べます。意味に基づく判断とその説明は、呼び出し元のモデルが行います。
ツール
組み込みツール(特に記載がない限り常に利用可能)。すべてのツールは厳密な JSONを返します(Markdown ではありません)。
discourse_search- 入力:
{ query: string; max_results?: number (1–50, default 10) } - 出力:
{ results: [{id, slug, title}], meta: {total, has_more} }
- 入力:
discourse_read_topic- 入力:
{ topic_id: number; post_limit?: number (1–50, default 5); start_post_number?: number } - 出力:
{ id, title, slug, category_id, tags, posts_count, posts: [{id, post_number, username, created_at, raw}], meta }
- 入力:
discourse_read_post- 入力:
{ post_id: number } - 出力:
{ id, topic_id, topic_slug, post_number, username, created_at, raw, truncated }
- 入力:
discourse_get_user- 入力:
{ username: string } - 出力:
{ id, username, name, trust_level, created_at, bio, admin, moderator }
- 入力:
discourse_list_user_posts- 入力:
{ username: string; page?: number (0-based); limit?: number (1–50, default 30) } - 出力:
{ posts: [{id, topic_id, post_number, slug, title, created_at, excerpt, category_id}], meta: {page, limit, has_more} }
- 入力:
discourse_filter_topics- 入力:
{ filter?: string; view?: "filtered"|"top"|"hot" (default "filtered"); top_period?: "daily"|"weekly"|"monthly"|"quarterly"|"yearly"|"all"; page?: number (0-based); per_page?: number (1–50) } - フィルター表示では空白以外の文字を含む
filterが必須で、/filter.jsonを使用します。トップ表示ではfilterを受け付けず、/top.jsonを使用し、既定値は weekly です。ホット表示ではfilterとtop_periodを受け付けず、Discourse の日次トップスコアとして厳密に定義されています。意味的な論争度、有害性、リアルタイムの勢いを示すものではありません。 - 出力:
{ results: [{id, slug, title, category_id, tags, created_at, last_posted_at, bumped_at, posts_count, reply_count, views, like_count, posters_count, closed, archived, pinned, visible, last_poster_username, posters}], meta: {view, top_period, page, per_page, returned, has_more, total?} }。省略可能な値がない場合はnullのままとなります。totalと継続情報を捏造することはありません。 - フィルターのクエリー言語(簡潔な説明):スペースで区切られた key:value トークン。category/categories(カンマは OR、
=categoryはサブカテゴリなし、-接頭辞は除外);tag/tags(カンマは OR、+は AND)および tag_group;status:(open|closed|archived|listed|unlisted|public);個人用のin:(bookmarked|watching|tracking|muted|pinned);日付:created/activity/latest-post-(before|after) にYYYY-MM-DDまたは相対日数Nを指定;数値:likes[-op]-(min|max)、posts-(min|max)、posters-(min|max)、views-(min|max);並べ替え:activity|created|latest-post|likes|likes-op|posters|title|views|category(任意で-ascを指定);フリーテキストの語句も照合されます。
- 入力:
- モデレーションツール(
--toolsets moderationの場合のみ利用可能。すべて認証が必要です)discourse_get_review_queue_count:{}→{ count, unit: "pending_reviewable_queue_items", status: "pending", scope }。countは、個々のフラグ数ではなく、呼び出し元に表示される保留中のレビュー対象レコードの確定数です。discourse_list_reviewables:安定したレビュー用フィルターとオフセットページネーションにより、名前付きステータスと数値のstatus_id、現在のバージョン、範囲を限定した証拠、スコア、対象、および動的アクションを含む正規化済みレビュー対象を返します。通常のトリアージではstatusとoffsetのみを指定してください。meta.totalを使用し、next_offsetに従ってください。上流のページサイズは 10 固定です。discourse_list_reviewable_topics:{}→ Discourse の最小レビュー優先度以上のトピックを集約して返しますが、網羅的ではありません。score_countはレビュー対象キュー項目ではなく、レビューのスコア/フラグレコード数を数えます。トピックのないキュー項目は含まれません。discourse_get_reviewable:{ reviewable_id; include_explanation? }→ 範囲を限定した最新のコンテキスト、サイドロードされた参照、編集可能なフィールド、スコアの証拠、利用可能なアクションの全件を返します。推奨は生成されません。一覧結果にはトリアージに必要な証拠がすでに含まれているため、大量のファンアウトは避けてください。discourse_perform_reviewable_action(書き込みが有効な場合のみ):{ reviewable_id; action_id; expected_version?; additional_fields?; confirm: true }→ シリアライズされ、最新の状態で事前検証されたアクションを実行し、正規化された成功情報と残数を返します。表示された動的アクション ID を送信してください。MCP はそのserver_actionを Discourse のルートにマッピングします。
discourse_get_chat_messages- 入力:
{ channel_id: number; page_size?: number (1–50, default 50); target_message_id?: number; direction?: "past" | "future"; target_date?: string (ISO 8601) } - 出力:
{ channel_id, messages: [{id, username, created_at, message, edited, thread_id, in_reply_to_id}], meta }
- 入力:
discourse_get_draft- 入力:
{ draft_key: string; sequence?: number } - 出力:
{ draft_key, sequence, found, data: {title, reply, category_id, tags, action} }
- 入力:
discourse_save_draft(書き込みが有効な場合のみ。Write safety を参照)- 入力:
{ draft_key: string; reply: string; title?: string; category_id?: number; tags?: string[]; sequence?: number (default 0); action?: "createTopic" | "reply" | "edit" | "privateMessage" } - 出力:
{ draft_key, sequence, saved }
- 入力:
discourse_delete_draft(書き込みが有効な場合のみ。Write safety を参照)- 入力:
{ draft_key: string; sequence: number } - 出力:
{ draft_key, deleted }
- 入力:
discourse_list_private_messages(認証が必要です)- 入力:
{ username?: string; mailbox?: "inbox"|"sent"|"archive"|"unread"|"new"; group_name?: string; page?: number (0-based, default 0); per_page?: number (1–100, default 30) } - 出力:
{ mailbox, username, group_name, messages: [{topic_id, slug, title, posts_count, reply_count, created_at, last_posted_at, bumped_at, last_read_post_number, unread_posts, unseen, topic_archived, message_archived, notification_level, recent_participants}], meta: {page, per_page, has_more} } usernameを省略すると、認証済みユーザーが使用されます。グループのメールボックスではsentをサポートしません。また、unreadとnewで別のユーザーを指定することはできません。
- 入力:
discourse_read_private_message(認証が必要です)- 入力:
{ topic_id: number; post_limit?: number (1–50, default 5); start_post_number?: number } - 出力:
{ topic_id, slug, title, archetype, subtype, posts_count, last_read_post_number, topic_archived, message_archived, allowed_users, allowed_groups, posts, meta } allowed_usersとallowed_groupsは直接のレコードであり、展開された ACL ではありません。公開トピックは拒否されます。
- 入力:
discourse_create_private_message(書き込みが有効な場合のみ。Write safety を参照)- 入力:
{ title: string; raw: string (<= 30k chars); usernames?: string[]; group_names?: string[]; email_addresses?: string[]; author_username?: string }(受信者を少なくとも 1 人指定してください) - 出力:
{ id, topic_id, post_number, slug, title } - 未知のメールアドレスでは、ステージングユーザーが作成される場合があります。同名のユーザー名より、メッセージを送信可能なグループ名が優先されます。
- 入力:
discourse_reply_private_message(書き込みが有効な場合のみ。Write safety を参照)- 入力:
{ topic_id: number; raw: string (<= 30k chars); reply_to_post_number?: number; author_username?: string } - 出力:
{ id, topic_id, post_number, reply_to_post_number, slug } - 投稿前にキャッシュを使用せず PM アーキタイプの安全性を確認します。既読状態は更新されません。
- 入力:
discourse_invite_to_private_message(書き込みが有効な場合のみ。Write safety を参照)- 入力:
{ topic_id: number; username?: string; group_name?: string; email_address?: string; notify_group_members?: boolean; custom_message?: string (<= 3000 chars); author_username?: string }(受信者をちょうど 1 人指定してください) - 出力:ユーザー/グループを直ちに正規化して追加するか、
{ topic_id, recipient_type: "email", status: "submitted", participant_added: false, outcome_confirmed: false } - メール送信では、配信やアクセスを保証しません。
custom_messageはメールでのみ使用され、グループ通知は既定で有効、グループ名は大文字・小文字を区別して完全一致します。
- 入力:
discourse_create_post(書き込みが有効な場合のみ。Write safety を参照)- 入力:
{ topic_id: number; raw: string (<= 30k chars); author_username?: string } - 出力:
{ id, topic_id, post_number }
- 入力:
discourse_create_topic(書き込みが有効な場合のみ。Write safety を参照)- 入力:
{ title: string; raw: string (<= 30k chars); category_id?: number; tags?: string[]; author_username?: string } - 出力:
{ id, topic_id, slug, title }
- 入力:
discourse_update_topic(書き込みが有効な場合のみ。Write safety を参照)- 入力:
{ topic_id: number; title?: string; category_id?: number; tags?: string[]; featured_link?: string; original_title?: string; original_tags?: string[] } - 出力:
{ success, topic_id, updated_fields, topic: {id, title, slug, category_id, tags, featured_link} }
- 入力:
discourse_list_users(管理者 API キーが必要です)- 入力:
{ query?: "active"|"new"|"staff"|"suspended"|"silenced"|"pending"|"staged"; filter?: string; order?: "created"|"last_emailed"|"seen"|"username"|"trust_level"|"days_visited"|"posts"; asc?: boolean; page?: number } - 出力:
{ users: [{id, username, name, email, avatar_template, trust_level, created_at, last_seen_at, admin, moderator, suspended, silenced}], meta: {page, has_more} } - 注:1 ページあたり約 100 人のユーザーを返します(Discourse の固定ページサイズ)。
avatar_templateには{size}プレースホルダーが含まれています。アバター URL を取得するには、これをピクセル単位のサイズ(例:120)に置き換えてください。
- 入力:
discourse_create_user(書き込みが有効な場合のみ。Write safety を参照)- 入力:
{ username: string (1-20 chars); email: string; name: string; password: string; active?: boolean; approved?: boolean; upload_id?: number } - 出力:
{ success, username, name, email, active, avatar_updated, message, avatar_error? } - 注:
upload_idが指定されていてもアバターの更新に失敗した場合、avatar_errorにエラーメッセージが含まれます。
- 入力:
discourse_update_user(書き込みが有効な場合のみ。Write safety を参照)- 入力:
{ username: string; name?: string; bio_raw?: string; location?: string; website?: string; title?: string; date_of_birth?: string; locale?: string; profile_background_upload_url?: string; card_background_upload_url?: string; upload_id?: number } - 出力:
{ success, username, updated_fields, avatar_updated, user: {...}, avatar_error? } - 注:
upload_idが指定されていてもアバターの更新に失敗した場合、avatar_errorにエラーメッセージが含まれます。
- 入力:
discourse_upload_file(書き込みが有効な場合のみ。Write safety を参照)- 入力:
{ upload_type: "avatar"|"profile_background"|"card_background"|"composer"; image_data?: string (base64); url?: string; filename?: string; user_id?: number } - 出力:
{ id, url, short_url, short_path, original_filename, extension, width, height, filesize, human_filesize } - 制約:
image_data(filenameが必要)、リモートの HTTP(S) URL、絶対ローカルファイルパスのいずれか 1 つだけを指定してください。- アバター/profile_background/card_background のアップロードには
user_idが必要です。 - ローカルファイルのアップロードには
--allowed_upload_pathsの設定が必要です(セキュリティ対策として、任意のファイル読み取りを防ぎます)。
- 注:投稿に画像を埋め込むには、
short_url(例:upload://abc123.png)を使用してください。
- 入力:
discourse_create_category(書き込みが有効な場合のみ。Write safety を参照)- 入力:
{ name: string; color?: hex; text_color?: hex; emoji?: string; icon?: string; parent_category_id?: number; description?: string } - 出力:
{ id, slug, name }
- 入力:
discourse_select_site(--siteが指定されている場合は非表示)- 入力:
{ site: string } - 出力:
{ site, title }
- 入力:
開発
-
要件:Node >= 24、
pnpm。 -
インストール/ビルド/型チェック/テスト
pnpm install --frozen-lockfile
pnpm typecheck
pnpm build
pnpm test
pnpm lint
- ローカルで実行(ソースマップ付き)
pnpm build && pnpm dev
-
プロジェクト構成
- サーバーと CLI:
src/index.ts - HTTP クライアント:
src/http/client.ts - ツールレジストリ:
src/tools/registry.ts - リソースレジストリ:
src/resources/registry.ts - 組み込みツール:
src/tools/builtin/* - リモートツール:
src/tools/remote/tool_exec_api.ts - JSON ヘルパー:
src/util/json_response.ts - ロギング/リダクション:
src/util/logger.ts、src/util/redact.ts
- サーバーと CLI:
-
依存関係とロックファイルのポリシー
- pnpm(
packageManager: [email protected])を正式な開発ワークフローとして使用しますが、下流の npm 互換性のため、pnpm-lock.yamlとpackage-lock.jsonの両方を管理対象としています。依存関係を変更した場合は、両方を再生成してコミットしてください。CI では frozen pnpm とクリーンなnpm ciビルドを実行し、どちらかが意図せず乖離しないようにしています。 @modelcontextprotocol/sdkは、レビュー済みの1.30.0リリースに意図的に固定しています。SDK の更新は、セキュリティ/互換性に関わる意図的な変更であり、型チェック、実際のトランスポート/出力スキーマのテスト、両ロックファイルを対象とした本番環境の高重大度監査、パッケージングのスモークテストに合格する必要があります。開発時のみの監査例外には、日付を明記した担当者と修正計画が必要です。
- pnpm(
-
テストに関する注意
- テストは Node のテストランナーを使い、コンパイル済みの成果物(
dist/test/**/*.js)に対して実行します。スクリプトを個別に実行する場合は、pnpm testの前にpnpm buildを実行してください。
- テストは Node のテストランナーを使い、コンパイル済みの成果物(
-
公開(任意)
- パッケージは
@discourse/mcpとして公開され、discourse-mcpという名前のbinを提供します。手軽に使用するには、npx @discourse/mcp@latestを推奨します。
- パッケージは
-
規約
- エージェントがプログラムで確実に解析できるよう、すべての出力は JSON のみです。
- 書き込み操作には注意し、明示的な選択制にしたうえで、レート制限を設けてください。
エージェントフレームワークからこのサーバーを使用する際の追加ガイダンスについては、AGENTS.md を参照してください。
使用例
ユーザー API キーでクイックスタート(管理者権限不要)
# Step 1: Generate a User API Key
npx @discourse/mcp@latest generate-user-api-key \
--site https://discourse.example.com \
--save-to profile.json
# Step 2: Visit the authorization URL shown, approve the request, and paste the payload
# Step 3: Run the MCP server with your new key
npx @discourse/mcp@latest --profile profile.json --allow_writes
その他の使用例
try.discourse.orgに対する読み取り専用セッション:
npx -y @discourse/mcp@latest --log_level debug
# In client: call discourse_select_site with {"site":"https://try.discourse.org"}
- 単一のサイトに接続:
npx -y @discourse/mcp@latest --site https://try.discourse.org
- 管理者 API キーで投稿を作成(書き込みを有効化):
npx -y @discourse/mcp@latest --allow_writes --auth_pairs '[{"site":"https://try.discourse.org","api_key":"'$DISCOURSE_API_KEY'","api_username":"system"}]'
- ユーザー API キーで投稿を作成(書き込みを有効化、管理者権限不要):
npx -y @discourse/mcp@latest --allow_writes --auth_pairs '[{"site":"https://try.discourse.org","user_api_key":"'$DISCOURSE_USER_API_KEY'"}]'
- カテゴリーを作成(書き込みを有効化):
npx -y @discourse/mcp@latest --allow_writes --auth_pairs '[{"site":"https://try.discourse.org","api_key":"'$DISCOURSE_API_KEY'","api_username":"system"}]'
# In your MCP client, call discourse_create_category with for example:
# { "name": "AI Research", "color": "0088CC", "text_color": "FFFFFF", "description": "Discussions about AI research" }
- トピックを作成(書き込みを有効化):
npx -y @discourse/mcp@latest --allow_writes --auth_pairs '[{"site":"https://try.discourse.org","api_key":"'$DISCOURSE_API_KEY'","api_username":"system"}]'
# In your MCP client, call discourse_create_topic, for example:
# { "title": "Agentic workflows", "raw": "Let's discuss agent workflows.", "category_id": 1, "tags": ["ai","agents"] }
- 認証済みの一覧取得/読み取りと、書き込み制限付きの作成/返信/招待を行うプライベートメッセージのワークフロー:
npx -y @discourse/mcp@latest \
--site https://try.discourse.org \
--toolsets private_messages \
--tools_mode discourse_api_only \
--auth_pairs '[{"site":"https://try.discourse.org","user_api_key":"'$DISCOURSE_USER_API_KEY'"}]' \
--allow_writes
# In your MCP client:
# discourse_list_private_messages: { "mailbox": "inbox", "page": 0 }
# discourse_read_private_message: { "topic_id": 123, "post_limit": 10 }
# discourse_reply_private_message: { "topic_id": 123, "raw": "Here is the result.", "reply_to_post_number": 3 }
# discourse_create_private_message: { "title": "Claim review", "raw": "Please review.", "usernames": ["alice"], "group_names": ["reviewers"], "email_addresses": ["[email protected]"] }
# discourse_invite_to_private_message: { "topic_id": 123, "group_name": "reviewers", "notify_group_members": false }
- HTTP トランスポートで実行(ポート 3000):
npx -y @discourse/mcp@latest --transport http --port 3000 --site https://try.discourse.org
# Server will start on http://localhost:3000
# Health check: http://localhost:3000/health
# MCP endpoint: http://localhost:3000/mcp
- HTTP Basic Auth の背後にあるサイトに接続:
npx -y @discourse/mcp@latest --auth_pairs '[{"site":"https://protected.example.com","api_key":"'$DISCOURSE_API_KEY'","api_username":"system","http_basic_user":"username","http_basic_pass":"password"}]' --site https://protected.example.com
認証
管理者 API キーとユーザー API キー
この MCP サーバーは、2 種類の Discourse API 認証に対応しています。
-
管理者 API キー(
api_key+api_username)- 生成には管理者/モデレーター権限が必要です
- 管理パネル → API → 新しい API キーから作成します
- ユーザー/カテゴリーの作成を含む、すべての操作を実行できます
- ヘッダーには
Api-KeyとApi-Usernameを使用します
-
ユーザー API キー(
user_api_key+ 任意のuser_api_client_id)- すべてのユーザーが生成できます(管理者権限不要)
- ユーザーごとの権限とレート制限が適用されます
- 個人利用や管理者権限を必要としない操作に最適です
- ヘッダーには
User-Api-KeyとUser-Api-Client-Idを使用します - 非アクティブ状態が 180 日続くと自動的に有効期限が切れます(サイトごとに設定可能)
- 詳細はこちら:https://meta.discourse.org/t/user-api-keys-specification/48536
ユーザー API キーの取得方法
簡単な方法:組み込みジェネレーター(推奨)
このパッケージには、ユーザー API キーを生成する便利なコマンドが含まれています。
# Interactive mode - follow the prompts
npx @discourse/mcp@latest generate-user-api-key --site https://discourse.example.com
# Save directly to a profile file
npx @discourse/mcp@latest generate-user-api-key --site https://discourse.example.com --save-to profile.json
# Specify custom scopes
npx @discourse/mcp@latest generate-user-api-key --site https://discourse.example.com --scopes "read,write,notifications"
# Get help
npx @discourse/mcp@latest generate-user-api-key --help
このコマンドは、対応サイト(Discourse 2026.6.0 以降)で Discourse のデバイス認可フローを使用します。
- RSA キーペアを生成し、有効期間の短い認可を要求します。
- アクティベーション URL と、
ABCD-2345のような短いコードを表示します。 - URL を開いてコードを入力し、スコープを確認して、リクエストを認可します。
- コマンドが Discourse をポーリングし、暗号化されたユーザー API キーを自動的に取得します。
- 応答を検証して復号し、設定を表示するか、プロファイルに保存します。
暗号化されたペイロードをターミナルにコピーして戻す必要はありません。古い Discourse サイトの場合、コマンドは自動的に従来の認可 URL とペイロード入力に切り替わります。
手動の方法
ユーザー API キーには、https://meta.discourse.org/t/user-api-keys-specification/48536 で説明されている OAuth に似たフローが必要です。主な手順は次のとおりです。
- 公開鍵と秘密鍵のペアを生成します
- 公開鍵、アプリケーション名、クライアント ID、要求するスコープを指定して、
/user-api-key/new経由で認可を要求します - ユーザーがリクエストを承認します(必要に応じてログイン後)
- Discourse がユーザー API キーを含む暗号化されたペイロードを返します
- 秘密鍵を使って復号し、設定でキーを使用します
Discourse の UI からユーザー API キーを手動で作成することもできます(サイトで有効になっている場合)。
- ユーザー設定 → セキュリティ → API に移動します
- または、ユーザー API キーのフローを実装したサードパーティ製ツールを使用します
FAQ
create_postが見つからないのはなぜですか? 読み取り専用モードになっています。上記の説明に従って書き込みを有効にしてください。- リモートツールの検出を無効にできますか? はい。
--tools_mode=discourse_api_onlyを指定して実行します。 discourse_select_siteの公開を避けることはできますか? はい。--site <url>を指定して起動し、単一のサイトに固定します。- タイムアウトやレート制限が発生します。
--timeout_msを増やしてください。また、429/5xx では組み込みの再試行とバックオフが行われます。 - Admin API Keys と User API Keys のどちらを使うべきですか? 個人利用には User API Keys を使用してください(管理者権限は不要です)。管理者レベルの操作が必要な場合や、システム全体のインテグレーションを設定する場合に限り、Admin API Keys を使用してください。
- 「fetch failed」エラーが発生します。
--log_level debugを指定して実行すると、次のような詳細なエラー情報を確認できます。- リクエスト先の正確な URL
- HTTP ステータスコード(レスポンス本文には機密情報が含まれる可能性があるため、意図的にログに記録されません)
- ネットワークレベルのエラー(DNS、SSL/TLS、接続の問題)
- 再試行の回数とタイミング
- タイムアウトの診断情報
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。