关于 Discourse Mcp Server
Discourse MCP 是一个基于 stdio 的 MCP 服务,将 Discourse 论坛的功能以工具和资源的形式提供给 AI Agent。它兼容自托管与 discourse.org 云托管站点,只需生成 API 密钥即可连接,写操作默认关闭。
主要功能
- 01以工具形式提供论坛搜索、主题与帖子读取
- 02可选的写操作,需通过 --allow_writes 启用
- 03按需启用的工具集,可精简暴露的工具
- 04支持多站点认证配置与配置文件
- 05支持 stdio 和回环地址上的 Streamable HTTP 传输
适用场景
- 01让 AI Agent 检索并阅读 Discourse 论坛的主题与帖子
- 02通过 Data Explorer 工具执行只读查询
- 03在授权后由 Agent 协助回复帖子或管理草稿
MCP 相关
该项目本身是 MCP 服务,向 MCP 客户端暴露 Discourse 的工具与资源。客户端可通过 npx 或 discourse-mcp 命令启动它。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
12收录的工具列表 · 更新于 3 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| site | string | 是 | Discourse 站点的基础 URL |
详细介绍
Discourse MCP
一个 Model Context Protocol(MCP)stdio 服务,可将 Discourse 论坛功能作为工具和资源提供给 AI Agent。
- 入口:
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 引入了相较于 0.1.x 的破坏性变更,包括仅输出 JSON;分类/群组资源仍作为弃用的兼容接口,与规范的列表工具并存)
快速开始(发布版)
- 运行(只读,建议先从这里开始)
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 基本身份验证(适用于位于反向代理之后的站点):在任意
auth_pairs条目中添加http_basic_user和http_basic_pass。这适用于在反向代理层受 HTTP 基本身份验证保护的 Discourse 站点。 auth_pairs中可以包含多个条目;系统会使用与所选站点匹配的条目。如果同一站点同时提供了user_api_key和api_key,则优先使用user_api_key。
-
写入安全性
- 默认禁用写入。
- 只有启用
--allow_writes后,才会注册内置写入工具。包括帖子、主题、私信、分类、用户、上传、草稿和已保存 Data Explorer 查询的变更工具。 - 列出和读取私信也需要匹配的已通过身份验证的站点,因为私信数据绝不会公开。
- 选择工具集不会绕过写入安全措施。除非启用写入,否则所选的写入工具仍不会注册。
- 写入工具要求所选站点存在匹配的
auth_pairs条目;否则会返回错误。 - 写入操作限制为每秒约 1 次请求。
-
标志和默认值
--help、-h或位置参数help:打印当前 CLI 帮助信息,并在加载配置文件或启动传输之前成功退出。--version、-v或位置参数version:打印一行软件包版本信息,并成功退出。-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...]>:公开选定的内置领域。省略此项时使用精简的默认目录(包含所有非选择加入的领域);使用--toolsets all可包含选择加入的类别/分组/标签组、审核、工作流和 AI 管理领域。参见内置工具集。--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 客户端/会话。每个初始化后的请求都必须携带返回的Mcp-Session-Id;第二次初始化请求会被拒绝。会话 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、其他位置的 shell 风格展开以及上传允许列表路径展开。
内置工具集
工具集允许操作者仅向 MCP 客户端公开所需的内置领域。此项为可选:省略 --toolsets 和配置文件字段时,服务会注册默认目录(包括 search、discourse_search 和 discourse_filter_topics)。下文标记为选择加入的管理和专用领域(包括 themes)必须显式选定。只有在明确需要所有内置领域时,才使用 --toolsets all。
传入一个名称或以逗号分隔的名称组合:
# 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 | 已认证用户的个人/群组私信列表和读取,以及受写入权限控制的创建、回复和参与者邀请 |
activity (选择加入) | 回复关系、全站帖子活动、主题浏览历史、用户活动摘要和时间线,以及目录/群组指标 |
administration (选择加入) | 类别发现、管理员可见的站点设置,以及需明确确认的用户激活/审批状态变更 |
site_settings (选择加入,管理员敏感) | 遮蔽站点设置检查,以及受写入权限控制并经过预检的普通非机密设置更新 |
webhooks (选择加入,管理员敏感) | 安全的 webhook 和投递历史检查,以及受写入权限控制的生命周期、ping 和单事件重新投递操作 |
themes (选择加入,管理员敏感) | 主题/组件检查,以及受写入权限控制的本地创建、编辑、安装、远程同步、资源上传和受保护的删除 |
groups (选择加入) | 空输入时穷举列出群组目录、显式分页/筛选兼容模式、完整的群组 CRUD 和成员操作,以及固定分页的群组发帖证据 |
tag_groups (选择加入,员工敏感) | 按公开可见性筛选的搜索,以及员工库存/详情和受写入权限控制并经过预检的创建/更新/删除生命周期 |
moderation (选择加入) | 已认证的审核队列分流、用户行为计数器、有界帖子修订,以及一次经过最新预检的可审核操作 |
workflows (选择加入) | 仅限管理员的工作流发现、图编写、表达式求值、固定数据、草稿运行、步骤运行、执行和版本管理 |
ai_agents (选择加入) | 仅限管理员的 AI Agent 发现、类型化生命周期、机器人用户创建,以及可移植导入/导出 |
ai_custom_tools (选择加入) | 仅限管理员的基于数据库的脚本化自定义工具指南、生命周期、实际执行测试,以及导入/导出 |
ai_features (选择加入) | 仅限管理员的 AI 功能发现和指定区域的非机密功能设置更新;也包含 Agent 发现 |
analytics (选择加入) | 员工可见的 Discourse 报告发现/执行,以及 Discourse Solved 支持仪表板 |
ai_insights (选择加入) | 面向读取的 Discourse AI 缓存摘要、语义搜索和员工情绪分类 |
all (哨兵) | 扩展为所有内置工具集,包括选择加入的领域;并吸收其他选择 |
工具集成员资格与安全性和授权有意分开处理:
- 选定的工具集会合并为一个并集。同一工具若属于多个选定集合,只会按规范的内置顺序注册一次。
all会扩展为每个实际域,且绝不会作为工具元数据出现。 - 未指定选择时,不包含仅选择加入的工具。工具定义不得混合选择加入和默认成员资格,以避免意外暴露。
- 对于任何未绑定站点的子集,系统都会自动保留
discourse_select_site作为引导能力。使用--site时,它仍会按惯例隐藏。 - 只读模式仍会移除需要启用写入的工具。例如,
--toolsets data_explorer默认会公开查询检索和执行功能;添加--allow_writes可公开已保存查询的变更操作。 - 现有的调用时身份验证和管理员检查保持不变。调用 Data Explorer 工具仍需要管理员权限;群组操作仍遵循 Discourse 自身的员工、所有者、可见性和自助服务授权规则。
- 工具集仅筛选内置工具;它们不是授权边界,也不是完整的能力边界。MCP 资源和提示词仍然可用,所有现有的调用时访问检查仍具有权威性。远程工具执行 API 的发现由
--tools_mode独立控制;当 MCP 工具列表必须仅包含选定的内置域时,请使用--tools_mode discourse_api_only。当选定的工具集与远程发现结合使用时,服务会记录一条提示信息。 - 在当前安全配置下,选定的域可能不提供任何工具,例如只读模式下的
uploads。出现这种情况时,服务会记录一条提示信息。 - 未知或为空的工具集选择属于配置错误。去除空白后会对值进行去重。
分类、群组和标签群组目录
目录功能默认选择加入,因此省略 --toolsets 时不会添加任何分类/群组/标签群组工具:
- 选择
administration以使用discourse_list_categories。当部署允许时,空输入会通过 1-based 分类搜索端点进行有界穷尽遍历。如果匿名 POST 被拒绝,则会返回有界分页的嵌套分类索引 GET(仅在该请求被拒绝时,才使用旧版/site.json),并明确标注anonymous_fallback/legacy_site_json不完整元数据——绝不会将其标记为穷尽。可选的term、max_pages、max_requests、max_results和deadline_ms可限制定向发现;由于分类索引不支持搜索词,因此会在本地对回退结果应用词项匹配。分类记录会保留 URL/层级字段;parent_category_id是规范字段,可为空;pid是为兼容性保留的旧别名。现有的丰富无输入投影是有意设计的:src/test/directory_tools.test.ts中可复现的 300 条记录测试样本约为 45 KB,因此此版本保留了有用的计数/访问字段,而不再添加第二套fields契约。 - 选择
groups以使用discourse_list_groups。{}是规范的穷尽去重操作。提供任何显式的现有键(包括{ "page": 0 }或{ "asc": false })都会保留历史上的单页/筛选查询行为。两种模式都会返回{ groups, meta, extras?, total_rows_groups?, load_more_groups? };筛选模式会有意标记为complete: false。 - 目录和标签群组工具调用成功时会声明 MCP
outputSchema,并返回structuredContent。对于不使用结构化输出的客户端,JSON 文本内容是相同的规范化值。格式错误的上游记录会返回普通的isError: true工具结果,而不是协议输出验证失败。 - 选择专用的
tag_groups域以使用 6 个工具:
discourse_search_tag_groups是公开的、经 Guardian 过滤的发现工具。它始终会发送明确的限制数量,并报告结果是否可能被截断。搜索结果不包含标签组 ID、父级和权限,因此不是权威清单;不区分大小写的精确标签组名称是关联的依据。q和names按 AND 语义组合,上游会将%/_视为 SQL LIKE 通配符。discourse_list_tag_groups和discourse_get_tag_group要求已配置的 API 凭据格式正确,并且调用者在上游具有 staff 权限。本地辅助程序无法验证 staff 角色;Discourse 是权威来源,并会出于隐私保护向非 staff 用户返回 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 内置的 everyone 组。服务端仅在 HTTP 边界处将这些条目转换为 Discourse 的数字权限映射(1= full,3= readonly)。parent_tag是可选的{id}或{name}选择器:创建时若不指定父级,请省略该字段或使用null;空白客户端占位值会被视为未提供。新的选择器名称要求allow_tag_creation=true,因为这会创建持久标签,并运行常规索引和插件钩子。- 更新要求提供最新的
expected_state_hash,会在本地合并未提供的字段,并发送完整的标签、父级、每个主题一个标签设置和权限,因为不完整的上游请求体会清除状态。移除标签/父级、替换权限,以及可能将序列化器合成的 everyone/full 旧版权限实体化,都要求明确确认(包括acknowledge_possible_synthetic_permission_materialization)。该哈希是 MCP 乐观前置条件,不是上游原子锁;预检后仍可能发生竞态。 - 删除要求提供完全匹配的 ID/名称/哈希,并明确确认级联删除和未解决的插件依赖。删除会级联删除成员关系、权限,以及分类允许/必需关系,但不会删除标签或主题-标签行。插件依赖发现并不全面。Discourse 的受限 API 密钥操作映射可能不允许删除。收到 200 确认并不代表操作成功,只有删除后 GET 确认资源不存在才算成功;分发后结果不确定的情况会返回不可重试的
outcome_unknown错误。
这些工具集仅控制发现功能。staff 角色、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,即可仅列出当前值与默认值不同的设置。更新一次只支持一个刚刚可见的普通设置,并要求提供预期的当前值和确认,同时通过精确的重新读取来验证结果。有意不支持密钥/凭据、上传、已上传图片列表和结构化对象设置、批量更新以及现有用户的回填操作。
主题管理
选择加入的 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; }"
}]
}
安装同样使用一种嵌套源变体。安装仓库无需使用压缩包占位项:
{
"source": {
"kind": "repository",
"remote_url": "https://github.com/example/discourse-theme.git",
"branch": "main"
},
"confirm_external_code": true
}
此版本有意不支持私有仓库密钥管理、仓库重定向、导出、批量删除、任意主题化站点设置修改,以及通用控制器参数透传。
群组管理
可选择启用的 groups 工具集涵盖自定义群组的完整生命周期:列出目录并读取完整详情;创建、更新和永久删除;分页读取成员和所有者;提供针对特定选择器的专用工具,可按用户名、数字用户 ID 或现有账户的电子邮件地址添加/移除成员,以及提升/降级所有者;列出待处理请求并批准/拒绝;以及经过身份验证的请求、公开加入和公开退出流程。单独的邀请工具用于处理可能尚无账户的电子邮件地址,避免混淆账户查找与论坛邀请。
所有变更操作都需要 --allow_writes。此外,创建和删除还需要在 MCP 访问门禁处提供员工/管理员级别的 API 凭据。Discourse 仍是 Guardian 检查、群组可见性、员工与所有者权限、自动群组限制、成员资格设置、邀请限制以及调用方可更新字段的权威依据。核心自动群组不得创建或删除,也不得更改其成员或所有权;经授权的员工仍可更新这些群组允许修改的展示和交互设置。选择该工具集并不会授予上述任何权限。
审核队列
可选择启用的 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 表示调用方可见的待审核记录数量,而非单个标记的数量。进行常规分流时,请只使用 status: "pending" 和 offset: 0 调用 discourse_list_reviewables——不要自行添加主题、类别、类型或用户筛选条件——并持续获取结果,直到 has_more 为 false。数字主题/类别占位值 0 以及可选文本占位值空白/all/any 都会被视为未提供,因此严格模式客户端不会意外筛选到 ID 1,也不会发送无效的通用哨兵值。列表结果已包含有界范围内的证据和动态操作;避免针对队列中的每一项调用 discourse_read_topic 或详情工具。discourse_list_reviewable_topics 仅是便捷的聚合工具:上游会包含审核优先级不低于其最低阈值的待审核主题,省略没有主题的队列项目,并将 score_count 报告为审核评分/标记记录的数量,而非待审核项目的数量。不得用它推断完整队列大小。
设置 --allow_writes 后,还会注册 discourse_perform_reviewable_action。请先调用列表/详情工具,然后提交一个确切的 available_actions[].id,并设置 confirm: true;应根据完整操作说明进行选择,而不是依据“删除帖子”这类重复标签。Discourse UI 操作 ID 可能带有前缀(post-… 或 user-…),而路由需要对应的 server_action;MCP 会自动验证并映射。审核变更操作会在整个全新 GET/PUT 操作期间串行执行并控制频率,因此并发模型批次无法绕过写入节流。该工具会检查可选的预期版本,拒绝未公布的字段,并返回规范化的成功/计数字段。若 PUT 后发生失败,结果会标记为状态未知,并附带标识符;必须先核实结果,不得盲目重试。Discourse 仍会强制执行声明、乐观冲突检查、操作有效性检查和 Guardian 权限检查。这些工具提供证据和明确操作,但不会建议审核决定。
私信
默认的 private_messages 工具集提供的是支持私信的接口,而不是复用通用的公开主题变更操作。列出和读取需要配置身份验证。创建、回复和邀请还需要 --allow_writes。Discourse 仍是邮箱可见性、私信成员资格、收件人上限、群组可发送消息状态以及所有 Guardian/API 密钥检查的权威来源。
个人邮箱支持 inbox、sent、archive、unread 和 new。群组邮箱支持除 sent 以外的所有类别;个人收件箱不包含所有群组收件箱。如果 discourse_list_private_messages 省略 username,则会通过 /session/current.json 获取已通过身份验证的用户。提供的用户名用于选择邮箱路径,不会模拟该用户。只有在授权规则允许时,Discourse 才允许查看其他用户的收件箱、已发送邮件或归档邮件;即使是管理员,unread 和 new 也仍仅限邮箱所有者访问。
私信收件人可以是用户名、群组名称或电子邮件地址。创建消息时,如果某个可发送消息的群组与用户同名,Discourse 上游分类会优先匹配群组。因此,不存在或不可发送消息的 group_names 值可能会导致类似“找不到用户”的上游错误。如果站点设置和发件人权限允许,未知的电子邮件收件人可能会立即创建待处理用户;如果不打算创建待处理用户,请使用规范用户名。
电子邮件邀请会刻意保持不透明。成功响应不代表邀请已送达或参与者可立即访问:属于现有账户的地址可能会返回成功但不执行任何操作,而新地址只有在兑换邀请后才会获得访问权限。要立即添加已知账户,请使用 username。群组邀请查找区分大小写,因此请使用规范群组名称。可选的 author_username 会发送 Api-Username;只有适当的全局 API 密钥才支持切换身份,而 User API Keys 始终绑定其所有者。
工作流创作
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 的嵌套连线格式。使用源节点目录中的输出键:条件/筛选器端口为 true 和 false,并非总是 main。MCP 会在发起 HTTP 请求前拒绝单边图更新。运行并非试运行,可能会创建帖子、发送聊天消息或调用外部 HTTP 服务。
Discourse AI 管理
这三个 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
Agent 索引默认保持简洁:discourse_ai_list_agents 会省略系统提示词和每个 Agent 的配置,只返回摘要计数(包括 subagent_count)以及精简的工具和模型目录。使用带 ID 的 discourse_ai_get_agent 查看单个 Agent 的完整配置。只有明确需要完整上游索引的客户端才可使用 view: "full"。
Agent 创建/更新架构接受 subagent_ids,这是一个有序允许列表,最多包含 20 个互不重复的现有 Agent ID,供父 Agent 委派任务。系统 Agent 的负 ID 也有效。Discourse 会验证每个 ID 是否存在、拒绝委派给自身,并防止已配置的工具与自动生成的 spawn_agent 工具冲突;写入前请使用完整 Agent 列表或详情响应来确认 ID。
discourse_ai_list_custom_tools 遵循相同模式:它会返回精简记录和预设签名,不包含脚本、绑定或冗长的参数文档。使用 discourse_ai_get_custom_tool 查看一个已存储的工具,或者使用 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 时,只指定所需的主题 topic。preset_id 为可选项,且仅对 topic: "presets" 有意义;其他主题会忽略它。创建脚本或对脚本进行重大修改前,请使用 topic: "preamble" 获取所选服务器的准确约定。当此工具集被选中时,同一份准确的实时前置说明和最小模板也会作为条件资源 discourse://ai/custom-tools/authoring-guide 提供。资源由应用驱动;指南工具由模型控制,因此自主客户端应使用该工具,而不要假定宿主已附加此资源。未来可选的编写提示词将由用户控制,用于引导用户明确发起的工作流,而不会取代模型可调用的发现功能。
安全:discourse_ai_test_custom_tool 会实际执行代码,并可能发出外部请求或导致站点副作用。功能更新会立即改变生产环境行为,且仅限于从一个准确的 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 资源
资源提供可由应用寻址的静态/半静态只读数据。类别和群组资源仅作为已弃用的兼容接口保留;其标准的、可由模型调用的替代项是上方的 opt-in 目录工具。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;否则会省略该字段,而不会捏造数据。
-
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
- 列出用户的聊天频道(公开频道和私信),并显示未读/提及次数
- 输出:
{ 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;需要所选站点的管理员凭据 - 应用可附加此资源;模型可通过
discourse_ai_get_custom_tool_guide并指定topic: "preamble"获取相同内容
- 仅当选择了
证据与分析功能
扩展后的读取目录会展示上游证据,而非由 MCP 作出的判断:
discourse_search仍以主题为中心。需要匹配的帖子时,请使用discourse_search_posts:它会保留数量受限的帖子 ID 以及高亮摘要、作者、主题/类别和真实的续页信息。这取代了早先关于在主题搜索结果中添加不设上限的纯帖子 ID 列表的提议。discourse_ai_semantic_search是一项独立的可选 Discourse AI 嵌入搜索。可选的activity工具discourse_list_latest_posts是按时间顺序排列的固定 50 行动态流,使用帖子 ID 作为游标,并非搜索。- 默认工具
discourse_read_topic_posts可按确切 ID、最早/最新帖子、指定帖子周边的窗口或用户名筛选帖子。选择最新/最早帖子时,最多发送两次上游请求,并将所选集合限制为 50 条。可选的activity工具discourse_get_post_replies会分别提供递归后代 ID、每页 20 条的直接回复,以及受站点限制的祖先历史记录。 - 主题和帖子读取会在插件提供时保留 Discourse 的已解决字段。已接受的回答只是问题解决情况的代理指标,并不能证明原发帖者对此满意。
- 可选的
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:相同的上游响应可能表示插件/设置已停用、资源不可见或资源不存在。选择工具集不会授予可见权限或员工访问权限。
组合示例:
- 使用
discourse_read_topic_posts的latest/replies_only模式跟进讨论串,然后让调用模型总结按顺序排列的证据。 - 结合 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) } - filtered 视图要求提供非空白的
filter,并使用/filter.json。top 视图不接受filter,使用/top.json,默认周期为 weekly。hot 视图不接受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(仅在启用写入时提供;参见写入安全)- 输入:
{ 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(仅在启用写入时提供;参见写入安全)- 输入:
{ 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(仅在启用写入时提供;参见写入安全)- 输入:
{ title: string; raw: string (<= 30k chars); usernames?: string[]; group_names?: string[]; email_addresses?: string[]; author_username?: string }(至少需要一位收件人) - 输出:
{ id, topic_id, post_number, slug, title } - 未知邮箱可能会创建待审核用户。同名的可收发消息群组优先于用户名。
- 输入:
discourse_reply_private_message(仅在启用写入时提供;参见写入安全)- 输入:
{ 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-archetype 安全检查,且不会推进已读状态。
- 输入:
discourse_invite_to_private_message(仅在启用写入时提供;参见写入安全)- 输入:
{ topic_id: number; username?: string; group_name?: string; email_address?: string; notify_group_members?: boolean; custom_message?: string (<= 3000 chars); author_username?: string }(必须且只能指定一位收件人) - 输出:立即规范化的用户/群组添加结果,或
{ topic_id, recipient_type: "email", status: "submitted", participant_added: false, outcome_confirmed: false } - 提交邮件不代表邮件已送达或收件人已获得访问权限。
custom_message仅用于邮件;群组通知默认启用,群组名称区分大小写且必须完全匹配。
- 输入:
discourse_create_post(仅在启用写入时提供;参见写入安全)- 输入:
{ topic_id: number; raw: string (<= 30k chars); author_username?: string } - 输出:
{ id, topic_id, post_number }
- 输入:
discourse_create_topic(仅在启用写入时提供;参见写入安全)- 输入:
{ title: string; raw: string (<= 30k chars); category_id?: number; tags?: string[]; author_username?: string } - 输出:
{ id, topic_id, slug, title }
- 输入:
discourse_update_topic(仅在启用写入时提供;参见写入安全)- 输入:
{ 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} } - 注意:每页返回约 100 位用户(Discourse 的固定页面大小)。
avatar_template包含{size}占位符;替换为像素尺寸(例如 120)即可获取头像 URL。
- 输入:
discourse_create_user(仅在启用写入时提供;参见写入安全)- 输入:
{ 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(仅在启用写入时提供;参见写入安全)- 输入:
{ 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(仅在启用写入时提供;参见写入安全)- 输入:
{ 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 或绝对本地文件路径 - 上传头像/个人资料背景/卡片背景时,必须提供
user_id - 上传本地文件需要配置
--allowed_upload_paths(安全措施:防止任意读取文件)
- 必须且只能提供以下其中一项:
- 注意:请使用
short_url(例如upload://abc123.png)在帖子中嵌入图片。
- 输入:
discourse_create_category(仅在启用写入时提供;参见写入安全)- 输入:
{ 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])是权威的开发工作流,但pnpm-lock.yaml和package-lock.json都会纳入版本控制,以兼容下游项目和 npm。依赖发生变化时,重新生成并提交两个锁文件;CI 会使用冻结的 pnpm 安装和干净的npm ci构建,确保两者都不会悄然偏离。 @modelcontextprotocol/sdk有意固定为经过审核的1.30.0版本。SDK 更新属于有意进行的安全性/兼容性更改,必须通过类型检查、真实传输/输出架构测试、针对两个锁文件的生产环境高严重性审计,以及打包冒烟测试。仅限开发依赖的审计例外必须注明日期、负责人和修复计划。
- pnpm(
-
测试说明
- 测试使用 Node 测试运行器,针对已编译产物(
dist/test/**/*.js)运行。如果单独运行脚本,请确保先执行pnpm build,再执行pnpm test。
- 测试使用 Node 测试运行器,针对已编译产物(
-
发布(可选)
- 此软件包以
@discourse/mcp发布,并提供名为discourse-mcp的bin。为便捷使用,建议运行npx @discourse/mcp@latest。
- 此软件包以
-
约定
- 所有输出都仅包含 JSON,以便 Agent 可靠地进行程序化解析。
- 谨慎执行写入操作;仅在用户明确启用时才执行,并限制频率。
有关通过 Agent 框架使用此服务的更多指南,请参阅 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 基本身份验证保护的站点:
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 服务支持两种 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 密钥需要使用 OAuth 类似流程,详见 https://meta.discourse.org/t/user-api-keys-specification/48536。关键步骤:
- 生成公钥/私钥对
- 使用你的公钥、应用名称、客户端 ID 和请求的权限范围,通过
/user-api-key/new请求授权 - 用户批准请求(如有需要,先登录)
- Discourse 返回包含用户 API 密钥的加密载荷
- 使用你的私钥解密,并在配置中使用该密钥
你也可以通过 Discourse 界面手动创建用户 API 密钥(如果网站已启用此功能):
- 前往你的用户偏好设置 → 安全 → API
- 或使用实现了用户 API 密钥流程的第三方工具
常见问题
- 为什么缺少
create_post? 你处于只读模式。按上述说明启用写入功能。 - 可以禁用远程工具发现吗? 可以,使用
--tools_mode=discourse_api_only运行。 - 可以避免公开
discourse_select_site吗? 可以,使用--site <url>启动,以绑定到单个网站。 - 超时或触发速率限制? 增大
--timeout_ms,并注意内置的 429/5xx 重试和退避机制。 - 应该使用管理员 API 密钥还是用户 API 密钥? 个人使用请用用户 API 密钥(无需管理员权限)。只有在需要管理员级操作或设置系统级集成时,才使用管理员 API 密钥。
- 遇到“fetch failed”错误? 使用
--log_level debug运行,以查看详细错误信息,包括:- 请求的确切 URL
- HTTP 状态码(由于响应正文可能包含敏感内容,因此不会记录)
- 网络级错误(DNS、SSL/TLS、连接问题)
- 重试次数和时间
- 超时诊断信息
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。