mcpField
D
Discourse Mcp Server

Discourse Mcp Server

本地服务
github.com/discourse社交媒体789 周前更新

关于 Discourse Mcp Server

Discourse MCP 是一个基于 stdio 的 MCP 服务,将 Discourse 论坛的功能以工具和资源的形式提供给 AI Agent。它兼容自托管与 discourse.org 云托管站点,只需生成 API 密钥即可连接,写操作默认关闭。

主要功能

  1. 01以工具形式提供论坛搜索、主题与帖子读取
  2. 02可选的写操作,需通过 --allow_writes 启用
  3. 03按需启用的工具集,可精简暴露的工具
  4. 04支持多站点认证配置与配置文件
  5. 05支持 stdio 和回环地址上的 Streamable HTTP 传输

适用场景

  1. 01让 AI Agent 检索并阅读 Discourse 论坛的主题与帖子
  2. 02通过 Data Explorer 工具执行只读查询
  3. 03在授权后由 Agent 协助回复帖子或管理草稿

MCP 相关

该项目本身是 MCP 服务,向 MCP 客户端暴露 Discourse 的工具与资源。客户端可通过 npx 或 discourse-mcp 命令启动它。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"discourse": {
"command": "npx",
"args": [
"-y",
"@discourse/mcp"
]
}
}
}
启动命令npx -y @discourse/mcp

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@discourse/mcp0.3.1

工具

12

收录的工具列表 · 更新于 3 周前

参数类型必填说明
sitestring是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"]
}

可用的工具集如下:

工具集内置工具
sitediscourse_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 个工具:
  1. discourse_search_tag_groups 是公开的、经 Guardian 过滤的发现工具。它始终会发送明确的限制数量,并报告结果是否可能被截断。搜索结果不包含标签组 ID、父级和权限,因此不是权威清单;不区分大小写的精确标签组名称是关联的依据。q 和 names 按 AND 语义组合,上游会将 %/_ 视为 SQL LIKE 通配符。
  2. discourse_list_tag_groups 和 discourse_get_tag_group 要求已配置的 API 凭据格式正确,并且调用者在上游具有 staff 权限。本地辅助程序无法验证 staff 角色;Discourse 是权威来源,并会出于隐私保护向非 staff 用户返回 404。即使禁用了标签功能,读取操作仍可能成功。
  3. 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"}];组 ID 0 是 Discourse 内置的 everyone 组。服务端仅在 HTTP 边界处将这些条目转换为 Discourse 的数字权限映射(1 = full,3 = readonly)。parent_tag 是可选的 {id} 或 {name} 选择器:创建时若不指定父级,请省略该字段或使用 null;空白客户端占位值会被视为未提供。新的选择器名称要求 allow_tag_creation=true,因为这会创建持久标签,并运行常规索引和插件钩子。
  4. 更新要求提供最新的 expected_state_hash,会在本地合并未提供的字段,并发送完整的标签、父级、每个主题一个标签设置和权限,因为不完整的上游请求体会清除状态。移除标签/父级、替换权限,以及可能将序列化器合成的 everyone/full 旧版权限实体化,都要求明确确认(包括 acknowledge_possible_synthetic_permission_materialization)。该哈希是 MCP 乐观前置条件,不是上游原子锁;预检后仍可能发生竞态。
  5. 删除要求提供完全匹配的 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 密钥。典型流程如下:

  1. 列出节点类型,然后请求特定的 identifier,以获取其参数架构、输出端口和 $json 约定。
  2. 解析类别、标签、群组、用户、徽章、聊天频道或数据表的 ID。
  3. 从模板创建,或提交一个完整的小型图。
  4. 编辑前立即 GET 工作流。使用完整的 nodes 和 connections 替换,或使用 MCP 端的 operations[] 进行机械式编辑。省略整个图更新中的某个节点会将其删除。
  5. 添加固定数据和步骤运行数据,或手动运行当前草稿;轮询 discourse_get_workflow_execution。
  6. 测试后设置 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_tool JavaScript 预设原文: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:相同的上游响应可能表示插件/设置已停用、资源不可见或资源不存在。选择工具集不会授予可见权限或员工访问权限。

组合示例:

  1. 使用 discourse_read_topic_posts 的 latest/replies_only 模式跟进讨论串,然后让调用模型总结按顺序排列的证据。
  2. 结合 Solved 主题筛选条件、已接受回答字段和支持仪表板评估回答状态,同时说明“已解决”仅是代理指标。
  3. 使用 discourse_list_group_posts 和主题 ID 检查群组参与情况;群组作者身份是证据,但不能证明其承担组织责任。
  4. 检查可能存在的冲突时,获取确切帖子/回复链,并可选择获取上游情感分类;任何语义判断都由调用模型作出并解释。

工具

内置工具(除非另有说明,否则始终可用)。所有工具均返回严格 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
  • 依赖与锁文件策略

    • pnpm(packageManager: [email protected])是权威的开发工作流,但 pnpm-lock.yaml 和 package-lock.json 都会纳入版本控制,以兼容下游项目和 npm。依赖发生变化时,重新生成并提交两个锁文件;CI 会使用冻结的 pnpm 安装和干净的 npm ci 构建,确保两者都不会悄然偏离。
    • @modelcontextprotocol/sdk 有意固定为经过审核的 1.30.0 版本。SDK 更新属于有意进行的安全性/兼容性更改,必须通过类型检查、真实传输/输出架构测试、针对两个锁文件的生产环境高严重性审计,以及打包冒烟测试。仅限开发依赖的审计例外必须注明日期、负责人和修复计划。
  • 测试说明

    • 测试使用 Node 测试运行器,针对已编译产物(dist/test/**/*.js)运行。如果单独运行脚本,请确保先执行 pnpm build,再执行 pnpm test。
  • 发布(可选)

    • 此软件包以 @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 身份验证方式:

  1. 管理员 API 密钥(api_key + api_username)

    • 生成密钥需要管理员/版主权限
    • 通过管理面板 → API → 新建 API 密钥创建
    • 可以执行所有操作,包括创建用户/类别
    • 使用请求头:Api-Key 和 Api-Username
  2. 用户 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 的设备授权流程:

  1. 生成 RSA 密钥对并请求一个短期授权。
  2. 显示激活 URL 和一组短代码,例如 ABCD-2345。
  3. 打开该 URL,输入代码,查看权限范围,然后授权请求。
  4. 命令会轮询 Discourse,并自动获取加密的用户 API 密钥。
  5. 验证并解密响应,然后打印配置或将其保存到配置档案中。

无需将加密载荷复制回终端。对于较旧的 Discourse 网站,命令会自动回退到旧版授权 URL 和载荷提示。

手动方法

用户 API 密钥需要使用 OAuth 类似流程,详见 https://meta.discourse.org/t/user-api-keys-specification/48536。关键步骤:

  1. 生成公钥/私钥对
  2. 使用你的公钥、应用名称、客户端 ID 和请求的权限范围,通过 /user-api-key/new 请求授权
  3. 用户批准请求(如有需要,先登录)
  4. Discourse 返回包含用户 API 密钥的加密载荷
  5. 使用你的私钥解密,并在配置中使用该密钥

你也可以通过 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、连接问题)
    • 重试次数和时间
    • 超时诊断信息

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。