mcp

MCP serverDev tools

Lets your agent work with a Discourse forum, such as reading and managing topics and posts.

Unavailable. This server has no hosted endpoint yet, so ahel can't serve it.

Add to setup to save this item as a reference. ahel cannot run it, and signing in will not install it.

About this server

Discourse MCP CLI server (stdio) exposing Discourse tools via MCP

Getting started

  1. Save this item in Your setup as a reference.
  2. Read the source or reference documentation for its setup requirements. Saving it here does not connect it to your AI.
  3. Check this page for availability before trying to install it through ahel.

From the project's README

As published by discourse/discourse-mcp in README.md.

Discourse MCP

A Model Context Protocol (MCP) stdio server that exposes Discourse forum capabilities as tools and resources for AI agents.

  • Entry point: src/index.ts → compiled to dist/index.js (binary name: discourse-mcp)
  • SDK: @modelcontextprotocol/sdk
  • Node: >= 24
  • Version: 0.3.1 (simplifies write opt-in so --allow_writes is sufficient and deprecates read_only=false; 0.3.0 added operator-selectable toolsets, structured directory output, and expanded opt-in administration capabilities; 0.2.x introduced breaking changes from 0.1.x, including JSON-only tool output; category/group resources remain deprecated compatibility surfaces alongside canonical list tools)

Quick start (release)

  • Run (read‑only, recommended to start)
npx -y @discourse/mcp@latest

Then, in your MCP client, either:

  • Call the discourse_select_site tool with { "site": "https://try.discourse.org" } to choose a site, or

  • Start the server tethered to a site using --site https://try.discourse.org (in which case discourse_select_site is hidden).

  • Enable writes (opt‑in, safe‑guarded)

npx -y @discourse/mcp@latest --allow_writes --auth_pairs '[{"site":"https://try.discourse.org","api_key":"'$DISCOURSE_API_KEY'","api_username":"system"}]'
  • Run with only Data Explorer built-in tools
npx -y @discourse/mcp@latest --toolsets data_explorer --tools_mode discourse_api_only

This exposes discourse_select_site plus the read-only Data Explorer tools. Add --site, authentication, and the write flags as needed; see Built-in toolsets.

  • Use in an MCP client (example: Claude Desktop) — via npx
{
  "mcpServers": {
    "discourse": {
      "command": "npx",
      "args": ["-y", "@discourse/mcp@latest"],
      "env": {}
    }
  }
}

Alternative: if you prefer a global binary after install, the package exposes discourse-mcp.

{
  "mcpServers": {
    "discourse": { "command": "discourse-mcp", "args": [] }
  }
}

Configuration

The server registers tools under the MCP server name @discourse/mcp. Choose a target Discourse site either by:

  • Using the discourse_select_site tool at runtime (validates via /about.json), or

  • Supplying --site <url> to tether the server to a single site at startup (validates via /about.json and hides discourse_select_site).

  • Auth

    • None by default.
    • Admin API Keys (require admin permissions): --auth_pairs '[{"site":"https://example.com","api_key":"...","api_username":"system"}]'
    • User API Keys (any user can generate): --auth_pairs '[{"site":"https://example.com","user_api_key":"...","user_api_client_id":"..."}]'
    • HTTP Basic Auth (for sites behind a reverse proxy): Add http_basic_user and http_basic_pass to any auth_pairs entry. This is useful for Discourse sites protected by HTTP Basic Authentication at the reverse proxy level.
    • You can include multiple entries in auth_pairs; the matching entry is used for the selected site. If both user_api_key and api_key are provided for the same site, user_api_key takes precedence.
  • Write safety

    • Writes are disabled by default.
    • Built-in write tools are only registered when --allow_writes is enabled. This includes post, topic, private-message, category, user, upload, draft, and saved Data Explorer query mutations.
    • Private-message listing and reading also require a matching authenticated site because PM data is never public.
    • Toolset selection does not bypass write safety. A selected write tool remains absent unless writes are enabled.
    • Write tools require a matching auth_pairs entry for the selected site; otherwise they return an error.
    • A ~1 req/sec rate limit is enforced for write actions.
  • Flags & defaults

    • --help, -h, or positional help: print current CLI help and exit successfully before loading profiles or starting a transport.

    • --version, -v, or positional version: print one package-version line and exit successfully. -v means version; logging verbosity uses --log_level.

    • --allow_writes (default: false): enable mutation tools. This single explicit opt-in is sufficient.

    • --read_only <boolean>: deprecated compatibility setting. true is an explicit read-only override and conflicts with --allow_writes; false has no effect and should be removed from commands and profiles.

    • --timeout_ms <number> (default: 15000)

    • --concurrency <number> (default: 4)

    • --log_level <silent|error|info|debug> (default: info)

      • debug: Shows HTTP request URLs, statuses, and detailed network/retry information (response bodies are never logged because admin APIs may echo sensitive content)
      • info: Shows retry attempts and general operational messages
      • error: Shows only errors
      • silent: No logging output
    • --show_emails (default: false). includes emails in user tools. Requires admin access

    • --tools_mode <auto|discourse_api_only|tool_exec_api> (default: auto)

    • --toolsets <name[,name...]>: Expose selected built-in domains. Omit for the compact default catalog (all non-opt-in domains); use --toolsets all to include opt-in category/group/tag-group, moderation, workflow, and AI administration domains. See Built-in toolsets.

    • --site <url>: Tether MCP to a single site and hide discourse_select_site.

    • --default-search <prefix>: Unconditionally prefix every search query (e.g., tag:ai order:latest).

    • --max-read-length <number>: Maximum characters returned for post content (default 50000). Applies to discourse_read_post and per-post content in discourse_read_topic and discourse_read_private_message. The tools prefer raw content by requesting include_raw=true.

    • --allowed_upload_paths <paths>: Comma-separated list or JSON array of directories allowed for local file uploads. Required to enable local file uploads in discourse_upload_file. Example: --allowed_upload_paths "/home/user/images,/tmp/uploads" or --allowed_upload_paths '["/home/user/images"]'. These security-sensitive paths do not receive ~ expansion.

    • --transport <stdio|http> (default: stdio): Use standard input/output by default, or loopback-only Streamable HTTP with JSON responses. HTTP explicitly supports one stateful MCP client/session per process. Every post-initialize request must carry the returned Mcp-Session-Id; a second initialize is rejected. After session DELETE/close, restart the process before connecting another client. /health returns 503 restart_required in that closed state. Request bodies are bounded to 4 MiB.

    • --port <number> (default: 3000): Port to listen on when using HTTP transport.

    • --cache_dir <path> (reserved)

    • --profile <path.json> (see below)

  • Profile file (keep secrets off the command line)

{
  "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"]
}

Run with:

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

Flags still override values from the profile. A leading current-user ~, ~/, or ~\ in the profile path expands to the current home directory; ~otheruser, shell-style expansion elsewhere, and upload-allowlist expansion are intentionally unsupported.

Built-in toolsets

Toolsets let an operator expose only the built-in domains needed by an MCP client. They are optional: when --toolsets and the profile field are both omitted, the server registers the default catalog (including search, discourse_search, and discourse_filter_topics). Administrative and specialized domains marked (opt-in) below—including themes—must be selected explicitly. Use --toolsets all only when every built-in domain is deliberately required.

Pass one name or a comma-separated union:

# 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

Profiles use an array (a comma-separated string is also accepted):

{
  "toolsets": ["users", "uploads"]
}

Available toolsets are:

ToolsetBuilt-in tools
sitediscourse_select_site (also retained implicitly as bootstrap for any untethered subset)
searchTopic-level search/filtering plus post-level keyword evidence
topicsCore topic/post reads, exact stream selection, post search, user-post activity, and mutations
usersUser lookup/listing, user-post activity, and user mutations
chatChat message retrieval
draftsDraft retrieval, save, and deletion
uploadsFile upload
data_explorerQuery retrieval, execution, creation, update, and deletion
private_messagesAuthenticated personal/group PM listing and reading, plus write-gated creation, replies, and participant invitations
activity (opt-in)Reply relationships, site-wide post activity, topic view history, user activity summaries and timelines, and directory/cohort metrics
administration (opt-in)Category discovery, admin-visible site settings, and explicitly confirmed user activation/approval state changes
site_settings (opt-in, admin-sensitive)Masked site-setting inspection plus write-gated, preflighted updates of ordinary non-secret settings
webhooks (opt-in, admin-sensitive)Safe webhook and delivery-history inspection plus write-gated lifecycle, ping, and single-event redelivery operations
themes (opt-in, admin-sensitive)Theme/component inspection plus write-gated local creation, editing, installation, remote synchronization, asset upload, and guarded deletion
groups (opt-in)Exhaustive empty-input group directory listing, explicit page/filter compatibility mode, complete group CRUD and membership operations, and fixed-page group-authored post evidence
tag_groups (opt-in, staff-sensitive)Public visibility-filtered search plus staff inventory/detail and write-gated, preflighted create/update/delete lifecycle
moderation (opt-in)Authenticated review queue triage, user behavioral counters, bounded post revisions, and one freshly preflighted reviewable action
workflows (opt-in)Admin-only workflow discovery, graph authoring, expression evaluation, pin-data, draft runs, step runs, executions, and version management
ai_agents (opt-in)Admin-only AI agent discovery, typed lifecycle, bot-user creation, and portable import/export
ai_custom_tools (opt-in)Admin-only database-backed scripted custom-tool guide, lifecycle, actual execution testing, and import/export
ai_features (opt-in)Admin-only AI feature discovery and exact-area, non-secret feature-setting updates; also includes agent discovery
analytics (opt-in)Staff-visible Discourse report discovery/execution and the Discourse Solved support dashboard
ai_insights (opt-in)Read-oriented Discourse AI cached summaries, semantic search, and staff sentiment classifications
all (sentinel)Expands to every built-in toolset, including opt-in domains; absorbs other selections

Toolset membership is intentionally separate from safety and authorization:

  • Selected toolsets form a union. A tool in multiple selected sets is registered once, in the canonical built-in order. all expands to every real domain and is never tool metadata.
  • Omitted selection excludes opt-in-only tools. Tool definitions may not mix opt-in and default memberships, which prevents accidental default exposure.
  • discourse_select_site is automatically retained as a bootstrap capability for every untethered subset. With --site, it remains hidden as usual.
  • Read-only mode still removes tools that require write enablement. For example, --toolsets data_explorer exposes query retrieval and execution by default; add --allow_writes to expose saved-query mutations.
  • Existing call-time authentication and admin checks are unchanged. Data Explorer tools still require admin access when called; group operations retain Discourse's own staff, owner, visibility, and self-service authorization rules.
  • Toolsets filter built-in tools only; they are not an authorization or complete capability boundary. MCP resources and prompts remain available, and all existing call-time access checks remain authoritative. Remote Tool Execution API discovery is controlled independently by --tools_mode; use --tools_mode discourse_api_only when the MCP tool list must contain only the selected built-in domains. The server logs an informational notice when selected toolsets are combined with remote discovery.
  • A selected domain can contribute no tools under the current safety configuration—for example, uploads in read-only mode. The server logs an informational notice when this occurs.
  • Unknown or empty toolset selections are configuration errors. Values are de-duplicated after trimming whitespace.
Category, group, and tag-group directories

Directory capabilities are deliberately opt-in, so omitting --toolsets adds zero category/group/tag-group tools:

  • Select administration for discourse_list_categories. Empty input performs bounded exhaustive traversal through the 1-based category-search endpoint when the deployment permits it. If anonymous POST is rejected, bounded paginated nested category-index GETs (and only on their rejection, legacy /site.json) are returned with explicit anonymous_fallback/legacy_site_json incomplete metadata—never as exhaustive. Optional term, max_pages, max_requests, max_results, and deadline_ms bound focused discovery; fallback term matching is applied locally because category index does not implement search terms. Category records retain URL/hierarchy fields; parent_category_id is canonical and nullable, while pid is a legacy alias retained for compatibility. The existing rich no-input projection is intentional: the reproducible 300-record fixture in src/test/directory_tools.test.ts measures about 45 KB, so this release preserves its useful counts/access fields rather than adding a second fields contract.
  • Select groups for discourse_list_groups. {} is the canonical exhaustive, deduplicating operation. Supplying any explicit existing key—including { "page": 0 } or { "asc": false }—preserves the historical one-page/filter query behavior. Both modes return { groups, meta, extras?, total_rows_groups?, load_more_groups? }; filtered mode is intentionally complete: false.
  • Directory and tag-group successes advertise MCP outputSchema and return structuredContent. The JSON text content is the same normalized value for clients that do not consume structured output. Malformed upstream records return ordinary isError: true tool results rather than protocol output-validation failures.

Select the dedicated tag_groups domain for six tools:

  1. discourse_search_tag_groups is public, Guardian-filtered discovery. It always sends an explicit limit and reports possible truncation. Search omits tag-group IDs, parents, and permissions, so it is not authoritative inventory; case-insensitive exact group names are the correlation key. q and names combine with AND semantics, and upstream treats %/_ as SQL LIKE wildcards.
  2. discourse_list_tag_groups and discourse_get_tag_group require configured API credential shape plus upstream staff authority. The local helper cannot prove a staff role; Discourse is authoritative and privacy-preservingly returns 404 to non-staff. Reads can work when tagging is disabled.
  3. discourse_create_tag_group, discourse_update_tag_group, and discourse_delete_tag_group additionally require effective write mode and upstream tagging_enabled. MCP inputs and normalized outputs represent permissions as explicit entries, for example [{"group_id":0,"access":"full"},{"group_id":9,"access":"readonly"}]; group ID 0 is Discourse's built-in everyone group. The server converts these entries to Discourse's numeric permission map (1 = full, 3 = readonly) only at the HTTP boundary. parent_tag is an optional {id} or {name} selector: omit it or use null when creating without a parent; blank client placeholders are treated as omitted. New selector names require allow_tag_creation=true because persistent tags are created and normal indexing/plugin hooks run.
  4. Updates require a fresh expected_state_hash, merge omitted fields locally, and send complete tags/parent/one-per-topic/permissions because partial upstream bodies clear state. Tag/parent removals, permission replacement, and possible materialization of serializer-synthesized everyone/full legacy permissions require explicit confirmations (including acknowledge_possible_synthetic_permission_materialization). The hash is an MCP optimistic precondition, not an upstream atomic lock; races can still occur after preflight.
  5. Deletes require exact ID/name/hash plus explicit cascade and unresolved-plugin acknowledgements. Deletion cascades memberships, permissions, and category allowed/required relationships, but does not delete tags or topic-tag rows. Plugin dependency discovery is not exhaustive. Discourse's scoped API-key action map may not authorize delete. A 200 acknowledgement is not success until a post-delete GET proves absence; uncertain post-dispatch outcomes are non-retryable outcome_unknown errors.

These toolsets control discovery only. Staff role, Guardian visibility, scoped-key authority, write mode, and site settings remain call-time/upstream decisions.

Webhooks and site settings

The webhooks and site_settings toolsets are opt-in and admin-sensitive. Selecting either toolset controls discovery only—it is not authorization. Every call requires matching selected-site admin-style authentication, Discourse remains the final authorization and validation authority, and mutations additionally require --allow_writes. The existing site-setting read remains available through administration, but site-setting mutation is discoverable only through an explicit site_settings selection.

# 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 delivery, ping, and redelivery make requests to external systems; enqueue or HTTP success does not prove that the destination processed an event correctly. Webhook secrets are never returned, URL userinfo is removed, query values are masked, and raw event headers are never passed through. Event payload/body previews require explicit sensitive-content confirmation and are bounded and credential-redacted. Bulk redelivery is intentionally unsupported.

Site settings affect the entire forum. Reads mask both upstream-secret and credential-like names; pass overridden_only: true to list only settings whose current value differs from the default. Updates support only one freshly visible ordinary setting at a time, require an expected current value and confirmation, and verify the result with an exact re-read. Secret/credential, upload, uploaded-image-list, and structured object settings, bulk updates, and existing-user backfills are intentionally unsupported.

Theme administration

The opt-in themes toolset is admin-sensitive and is never included in the default catalog. Read-only selection registers only discourse_list_themes and discourse_get_theme; every mutation additionally requires --allow_writes. Toolset selection does not grant admin access: configure matching site authentication and Discourse remains authoritative for admin, repository-allowlist, dependency, compiler, import, and migration checks.

Theme HTML, JavaScript, SCSS, settings migrations, assets, and third-party repositories can execute or deploy code for every visitor. The tools require operation-specific confirmations, but they do not sandbox, validate, or declare third-party code safe. Local archives and assets are accepted only from bounded base64 input or regular files beneath symlink-resolved --allowed_upload_paths roots.

Use deliberate operator configuration rather than enabling every toolset:

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

Local themes and ZIP-imported themes can be edited directly (although ZIP source values are omitted by Discourse's detail serializer); Git-backed themes must be changed in their repository and synchronized. Components cannot be default/user-selectable or own color schemes. Text fields and upload fields are separate schema variants—never send placeholder upload IDs with SCSS/HTML/JavaScript:

{
  "fields": [{
    "name": "scss",
    "target": "common",
    "operation": "replace",
    "type": "scss",
    "value": "body { background: #241914; }"
  }]
}

Installation likewise uses one nested source variant. A repository install needs no archive placeholders:

{
  "source": {
    "kind": "repository",
    "remote_url": "https://github.com/example/discourse-theme.git",
    "branch": "main"
  },
  "confirm_external_code": true
}

This release intentionally excludes private-repository key management, repository repointing, export, bulk deletion, arbitrary themeable site-setting mutation, and generic controller parameter pass-through.

Group management

Shortened here. Read the whole README on GitHub.

Signals

GitHub stars
76
Forks
44
Last commit
Aug 2026
Weekly_downloads
9k weekly_downloads
Advanced
Delivery
mcp MCP server → your ahel connector (mcp.ahel.ai) → your AI.
Item type
mcp-server
Key
io-github-discourse-mcp
Source
github.com/discourse/discourse-mcp