nodebb-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@nodebb-mcp search the forum for the login loop bug and whether it's resolved"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
nodebb-plugin-mcp
MCP for a NodeBB forum, served by the forum itself. Each person connects their AI client (Claude Code, an IDE) with their own forum account and can then search the forum, pull answers out of existing threads, investigate problem reports, and work support tickets on a Shotef triage board — always with exactly their own forum permissions.
Works on a plain NodeBB forum; the search, Q&A and Shotef plugins each unlock more (see Plugins).
Install
Like any NodeBB plugin:
cd /path/to/nodebb
npm install github:heknon/nodebb-mcp # or a local checkout's path; it builds itself on install
./nodebb activate nodebb-plugin-mcp
./nodebb build && ./nodebb restart(It is not on the npm registry yet. Installing from GitHub needs read access to the repository.)
The forum now serves MCP at https://forum.example.com/api/mcp, inside NodeBB's
own API. ACP → Plugins →
MCP shows that address, the settings, and every connected app.
Related MCP server: Discourse MCP
Connect
claude mcp add --transport http forum https://forum.example.com/api/mcpThe first time the client connects, the browser opens the forum: the person signs in the way they always do (password, SSO, two-factor — whatever the forum uses) and sees "Claude Code wants to use the forum as you". Allow sends them back to the client, which is now connected as them. Nothing else to configure: no tokens to copy, no passwords through the MCP client.
Each person's own page, /mcp/connections, lists their connected apps with a Disconnect button, and shows the command above.
Tools
What a tool may change decides when it is available:
Changes | Available | Who may use it |
nothing | always | anyone the forum lets read the content |
ticket — Shotef team state | always, when Shotef is installed | members of the team handling the ticket, and administrators (Shotef checks) |
public — what forum readers see | only with Public writes on (ACP → Plugins → MCP; | the connected person, within their NodeBB privileges |
Tool | Does | Changes | Needs |
| Who you are on the forum, which plugins are live, what is missing | nothing | — |
| Topics that already answer a question, with the answer from each (labelled accepted or merely upvoted) and its ticket stage | nothing | — |
| Existing answers, still-open reports, triage state, and next steps for a problem report | nothing | — |
| Full-text search with snippets, flagging hits that have a ticket | nothing | a search plugin and a signed-in user (otherwise a recent-titles scan) |
| A thread in full, accepted answer and triage stage marked | nothing | — |
| The answer to one topic | nothing | Q&A for an accepted answer (otherwise the top reply, labelled) |
| One post with its topic | nothing | — |
| Category tree with ids | nothing | — |
| Recent, popular, top or unread topics, forum-wide or for one category, with ticket stages | nothing | a signed-in user for |
| Questions nobody has answered | nothing | Q&A (otherwise topics with no replies) |
| A topic's stage: received, in progress, answered, closed | nothing | Shotef |
| A team's board by column, plus the status keys | nothing | Shotef, team member |
| Find tickets on every board you can see: by words, lane (active, backlog, intake, closed), status, owner, priority, help wanted, Q&A state, tag, age, reporter silence | nothing | Shotef, team member |
| A ticket with its history and internal notes | nothing | Shotef, team member |
| Leave an internal note only the team sees | ticket | Shotef, team member |
| Move a ticket, or close it as resolved or no-response | ticket | Shotef, team member |
| Claim, release, or assign a ticket. Claiming a ticket in the intake lane (a category two teams share, taken by neither) takes it for your team | ticket | Shotef, team member (manager to assign someone else) |
| Set priority and the help-wanted flag | ticket | Shotef, team member |
| Hand a ticket to another team | ticket | Shotef, team member |
| Start a topic, optionally as a Q&A question | public | Public writes |
| Post a reply | public | Public writes |
| Replace a post's text | public | Public writes |
| Upvote, downvote, or withdraw a vote | public | Public writes |
| Accept a reply as a question's answer | public | Public writes, Q&A, Shotef, team member |
Ticket changes are not entirely invisible: the reporter sees the coarse stage ("in progress", "closed") and Shotef notifies them when it changes. Notes and the ticket history stay with the team. To keep a server off the board entirely, give it a token for a user who is on no triage team and is not an administrator (Shotef treats administrators as members of every team).
Names. Teams, categories and assignees can be given by name instead of id,
and spelling need not be exact. "suport", "the billing team", "S&O" or
"generel discusion" all find what was meant. A lookup takes the closest unique
match and its output says what it matched. A change (pass_ticket,
assign_ticket, create_topic) needs the exact name, ignoring case and
punctuation. A near miss there is answered with "did you mean …?" and
nothing changes, because a wrong guess would move someone's ticket. A name that fits several
("general", with both General Discussion and E2E General Discussion) is never
guessed: the options are listed.
Prompts: answer_forum_question and investigate_report chain the read tools;
work_ticket takes a ticket from report to closed (with public writes off, it
drafts the reply into a note for a person to post).
How access works
Each connection is the person. Allowing an app creates a NodeBB API token for that person (listed in ACP → Settings → API Access as "MCP: Claude Code (alice)"). Every tool call goes through the forum's own API with that token, so NodeBB checks it like any request of theirs: category privileges, bans, Shotef's team rules.
Ending access. The person disconnects the app on /mcp/connections; an administrator revokes it in ACP → Plugins → MCP, or deletes its API token; or the client revokes its own token. Any of these cuts it off on the next request.
Standard OAuth. The forum is the authorization server: discovery (
/.well-known/oauth-authorization-server/api/mcp), dynamic client registration, PKCE, one-time codes, hourly access tokens and rotating refresh tokens, all stored hashed in the forum's database. Nothing outside the forum is involved, so it works air-gapped.Any number of forum processes. The MCP endpoint keeps no session between requests, and all state is in the database, so a multi-process forum works as-is.
Settings (ACP → Plugins → MCP):
Setting | Default | Effect |
Public writes | off | Lets connected apps post topics and replies, edit posts, vote and accept answers, as the person and within their privileges. Shotef ticket actions do not need it. Takes effect on the next request, in every forum process. |
Before going live:
HTTPS. OAuth requires the forum to be served over
https://(plain http is accepted only on localhost).Forums under a subpath (
https://example.com/forum): OAuth discovery lives at the origin root, so the reverse proxy must also pass/.well-known/oauth-authorization-server/forum/api/mcpand/.well-known/oauth-protected-resource/forum/api/mcpto NodeBB, as they are (the plugin answers them at the root; no rewriting).Rate limits. Client registration, authorization and token requests are limited per IP (the MCP SDK's defaults: 20 registrations an hour). Behind a proxy, set NodeBB's
trust_proxyso that is per person, not per proxy.Content-Security-Policy. If you turned on NodeBB's CSP, its
form-actionmust allow the clients' callback addresses (e.g.http://localhost:*for Claude Code), or the browser blocks the way back after Allow.
Running it locally instead (stdio)
Without installing anything on the forum, one person can run the same tools on
their own machine, acting as one forum account (npm install && npm run build
in a checkout first):
{
"mcpServers": {
"forum": {
"command": "node",
"args": ["/path/to/nodebb-mcp/dist/index.js"],
"env": {
"NODEBB_URL": "https://forum.example.com",
"NODEBB_USERNAME": "support-bot",
"NODEBB_PASSWORD": "…"
}
}
}
}Variable | Default | Purpose |
| required | Forum root, e.g. |
| — | The account to sign in as. Without credentials the server is a guest: public content only, and no search |
| — | Alternative to a password (ACP → Settings → API Access). Wins if both are set |
| — | User to act as. Required with a master token; ignored otherwise |
| off |
|
|
| Per-request timeout |
|
| Posts read per topic |
|
| How long plugin detection is cached |
|
| Shotef's route prefix under |
| per-user cache file | Where the signed-in session is kept ( |
|
| How often an idle session is re-checked before a Shotef or other Write API read |
Signing in with a password. The server keeps the session cookie, sends the CSRF token on writes, and signs in again by itself when the session expires or is revoked. NodeBB limits sign-ins (by default, 5 failures an hour lock the account; 20 sign-ins an hour from one IP lock the IP), so new processes reuse the cached session, a wrong password is tried once and remembered for an hour, and the password itself is never written anywhere. The session shows on the account's sessions page as nodebb-mcp 0.2.0 on Linux. Two-factor accounts need an API token instead.
Check who you are. NodeBB does not reject an invalid or revoked token; it
quietly treats the caller as a guest. forum_capabilities shows the user the
forum actually resolved, and flags credentials it refused or ignored.
Plugins
None are required. When one is missing, the tools still answer and say in their output what they could not see.
Plugin | With it | Without it |
Search: | Ranked search over titles and post bodies, for a token's user (guests lack the search privilege by default) | A scan of recent topic titles, flagged as such |
| Accepted answers, the unsolved queue, | The most-upvoted reply, labelled not marked accepted; the queue becomes topics without replies |
Triage stage, board, tickets, and the ticket tools | Triage is reported as unknown |
The ticket tools need Shotef's HTTP API (/api/v3/plugins/shotef/tickets/…, see
its README). On a build without it, forum_capabilities reports triage actions
as absent and says why, and the ticket tools refuse before sending anything.
Such builds also ignore API tokens: the board then reads as empty for a token,
but works when the server signs in with NODEBB_USERNAME / NODEBB_PASSWORD.
An unconfirmed reply is never presented as an accepted answer, and every refusal from the forum comes back with its reason and what to do about it.
Development
npm run check # build + unit tests (fake forum, no NodeBB needed)
npm run e2e # build, seed and test against a real NodeBB; provisions one if missingUnit tests (
tests/*.test.js) run againsttests/fake-forum.js, an HTTP model of NodeBB 4.16 with the three plugins.End-to-end tests (
tests/e2e/) run against a real forum seeded bye2e/seed.mjs, which runs this checkout asnodebb-plugin-mcp(symlinked, like Shotef). The plugin suites connect through the forum's real OAuth, login and Connect page — one of them in headless Chromium, ACP included; the others drive the stdio server once per forum identity. They run once with all plugins on and once with them off.e2e/forum.shstarts, stops, rebuilds and inspects that forum.
Layout: src/plugin/ is the NodeBB side (routes, OAuth provider, database
store), library.cjs + plugin.json + templates/ + public/ are what NodeBB
loads, and everything else in src/ is the MCP server both modes share.
When the two disagree, the real forum wins and the fake is fixed. CLAUDE.md
covers this container; .claude/skills/nodebb-mcp-dev/ covers NodeBB's API
quirks and how to add a tool.
Plugin-authoring docs
The server also serves a guide to writing NodeBB plugins (v3/v4) as MCP resources, distilled from the Shotef plugin:
URI | Covers |
| Mental model, plugin anatomy, the five rules |
| The |
|
|
| Page routes, |
| The |
| Client scripts, ACP modules, templates, languages, CSS |
| 17 traps as symptom → cause → fix |
|
|
| Scaffolding, dev environment, snippets |
| An annotated tour of a real plugin |
They live in resources/ as plain markdown. To add one, write the file and add
an entry to resources/index.json.
License
MIT
Available Tools
12 toolsfind_answersFind existing answersARead-only
Find topics that already answer a question, and extract the answer from each. Prefers author-accepted answers when the Q&A plugin is installed, and falls back to the strongest reply otherwise — always labelling which it used, so an unconfirmed reply is never presented as a confirmed answer. This is the first tool to reach for when someone asks a question the forum may have seen before.
Inputs: question (required), tags, depth, categories, +1 more.
forum_capabilitiesForum capabilitiesARead-only
Report which forum features this server can actually use: reachability, authentication, full-text search, the Q&A plugin (accepted answers), and the Shotef triage plugin. Call this first when a result looks thin or a tool reports a feature as unavailable — it explains what is missing and what would enable it.
Inputs: refresh.
get_postRead a single postARead-only
Read one post by its id, with the topic it belongs to. Use when a search result or a link points at a specific reply and you only need that reply.
Inputs: pid (required).
get_topicRead a topicARead-only
Read a topic in full: the original post, replies in order, and — when the relevant plugins are installed — which reply is the accepted answer and where the topic sits on the triage board. This is the tool for understanding a reported issue in the reporter's own words.
Inputs: tid (required), page, max_posts.
get_topic_answerGet a topic's answerARead-only
Get the answer to one specific topic. With the Q&A plugin this is the accepted answer, and an open question is reported as genuinely unanswered rather than guessed at. Without it, returns the most-upvoted reply, labelled as unconfirmed.
Inputs: tid (required).
get_triage_boardGet the triage boardARead-only
The team's triage board: every open ticket by workflow column, with priority, owner and age. Use it to see the current support workload, or to find which issues are stalled. Requires the Shotef plugin and a token belonging to a team member — a non-member gets an explanation rather than an error.
Inputs: team.
get_triage_statusGet triage statusARead-only
Where a topic stands in the support workflow: received, in progress, awaiting the reporter, or closed — plus which team handles it and whether it is parked. Answers "is anyone working on this, and was it ever resolved?". Requires the Shotef triage plugin; without it, reports that cleanly and suggests reading the topic instead.
Inputs: tid (required).
investigate_issueInvestigate an issueARead-only
The full sweep for a reported problem: find every related topic, extract the answers that already exist, separate them from the reports still open, fold in triage status, and end with concrete next steps. Use this when someone reports a problem and you need to know whether the forum has seen it before, whether it was solved, and whether anyone is already on it. Slower than find_answers because it opens several topics — prefer find_answers for a plain question.
Inputs: issue (required), tags, depth, categories, +2 more.
list_categoriesList categoriesARead-only
List the forum's category tree with ids, descriptions and topic counts. Use it to find the category id to pass to search_forum, list_recent_topics, or investigate_issue.
Inputs: no named parameters.
list_recent_topicsList recent topicsARead-only
Browse topic listings — recent, popular, top, or unread — optionally inside one category. Useful for getting a feel for what is being reported lately, and as the way to explore a forum that has no search plugin.
Inputs: cid, page, limit, source.
list_unanswered_questionsList unanswered questionsARead-only
List questions nobody has answered yet — the queue for someone who wants to help. With the Q&A plugin this is the forum's real unsolved list; without it, recent topics that have no replies, which approximates it but cannot tell a question from a discussion. Shows triage status per item when the Shotef plugin is installed.
Inputs: page, limit.
search_forumSearch the forumARead-only
Full-text search across topic titles and post bodies. Returns ranked matches with a snippet showing why each matched. Requires a search plugin on the forum (NodeBB core has none); when one is absent this falls back to scanning recent topics by title and says so. For answering a question, prefer find_answers, which also extracts the answers.
Inputs: query (required), in, page, tags, +5 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 12 tools
There is meaningful overlap in the search/answer cluster (search_forum, find_answers, investigate_issue, get_topic_answer), but the descriptions explicitly cross-reference and route the agent (e.g. 'prefer find_answers', 'prefer find_answers for a plain question'). get_topic vs get_post and triage status vs board are cleanly separated, so an agent can reliably pick.
Almost everything follows a verb_noun pattern: get_post, list_categories, search_forum, get_topic, find_answers, investigate_issue, get_triage_status, get_triage_board. The lone deviation is forum_capabilities (noun-only, no verb), but it stays snake_case and readable.
12 tools is well within the ideal band and each earns its place by covering a distinct read/triage action. No redundant or filler tools are apparent.
The set covers reading posts, topics, categories, listings, search, answer extraction, unanswered queues, and triage status/board — a coherent read-and-triage surface. It is read-only, so any posting/replying/updating workflow the domain might need is absent, but that appears intentional and is not a dead end for the stated purpose.
Related MCP Connectors
Q&A forum where AI agents ask each other for help: search, read, post, reply, inbox.
Search or monitor any Flarum-powered forum for discussions, replies, participants, dates, and…
Public forum where AI agents read questions and optionally post or reply with user permission.
Related MCP Servers
AlicenseAqualityCmaintenanceEnables AI agents to interact with Discourse forums through search, reading topics/posts, managing categories and users. Supports secure authentication and optional write operations with rate limiting.1411,259 npm79MIT- AlicenseAqualityDmaintenanceEnables AI agents to interact with Discourse forums through search, reading topics/posts, managing categories and tags, chat channels, and optionally creating content with safeguarded write operations.1511,259 npmMIT
- FlicenseAqualityDmaintenanceConnects AI assistants to thousands of Tapatalk-enabled forums, enabling browsing, reading, searching, and optional posting.14-
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to search and read solved threads from the n8n community forum for vetted solutions.MIT