delphi-mcp
Provides read-only access to Discourse forums, defaulting to Chief Delphi but configurable to any Discourse instance via DELPHI_BASE_URL. Includes tools for listing categories, browsing topic listings, keyword-based topic discovery, reading threads as post windows or raw Discourse markdown, and viewing user profiles.
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., "@delphi-mcpfind Chief Delphi threads about 2025 FRC robot reveals"
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.
delphi-mcp
A read-only Model Context Protocol server that lets an AI agent read Chief Delphi — the discussion forum of the FIRST Robotics Competition, running on Discourse.
No account, no API key, no write access. The server speaks the anonymous read endpoints a browser already
uses, and it enforces the site's robots.txt in code rather than in a comment.
cd_categories → cd_find / cd_topics → cd_raw / cd_topic → cd_user
scope it find the thread read the thread vet the claimWhy this exists
Chief Delphi is where FRC teams post game rumors, strategy, code and CAD releases, and event discussion.
An agent asked to research one of those needs a way into the forum, and the obvious answer — the Discourse
/search.json endpoint — is explicitly disallowed for crawlers by Chief Delphi's robots.txt. This server
is the attempt at getting useful recall without that endpoint. See
Compliance.
Related MCP server: discourse-mcp
Requirements
Node.js 18 or newer (global
fetch; developed and tested on Node 24)No build step — plain ESM JavaScript
Install
git clone https://github.com/CaryWang1234/delphi-mcp.git
cd delphi-mcp
npm installConnect it to your MCP client
All clients take the same stdio entry; only the config file differs. Point args at the absolute path of
src/index.js.
{
"mcpServers": {
"chief-delphi": {
"command": "node",
"args": ["/absolute/path/to/delphi-mcp/src/index.js"]
}
}
}Client | File |
Claude Desktop |
|
Cursor |
|
Qoder |
|
Any other stdio client | its own |
Some hosts cache the tool list after the first handshake. If you change a tool description in
src/index.js and the client still shows the old one, reload MCP servers (/mcp reload in Qoder) or
restart the host. On Windows, prefer an absolute command path such as
C:\\Program Files\\nodejs\\node.exe — hosts do not always inherit your shell's PATH.
Tools
Every tool accepts max_chars (default 20000, ceiling 120000). When a reply would exceed it, the
output is cut and the last line says so and tells you how to continue — one call cannot flood the model's
context.
Tool | What it does | Arguments (defaults) |
| Every category and subcategory with numeric id, slug, topic count and restricted flag. Call first to scope anything else. | — |
| One page of a topic listing: |
|
| Keyword lookup without |
|
| A thread window with per-post metadata: author, user title, timestamp, likes, which post is replied to, post id, deleted/hidden flags. |
|
| A thread as plain Discourse markdown — the cheapest full read. |
|
| An account profile: trust level, titles, join/last-seen/last-posted, profile views, time read, badges, location, website, bio, and profile fields such as team number. |
|
Listing rows and search hits carry the topic id, which is what cd_topic and cd_raw take. URLs are
${DELPHI_BASE_URL}/t/<slug>/<id>.
Compliance is structural, not advisory
src/discourse.js fetches robots.txt, parses it per the RFC 9309 grouping rules, and checks every
outgoing request against the longest matching rule for this client. A disallowed path throws
RobotsBlockedError and never leaves the process. On Chief Delphi that currently blocks /search*,
/admin*, /session*, /my*, /email*, /badges, /g, /tag/*/l, /c/*.rss and /t/*/*.rss,
plus anything else the site adds later.
Consequences worth knowing before you file a "search is broken" issue:
cd_findis not a full-text index. It ranks topics inside a listing window. A miss means "not in this window", not "the forum has no such thread", and the tool output says exactly that in its header. Widen withpages=(each page is ~30 topics, 40 max), narrow withcategory=, or switchmode=.mode=auto— the default — merges atoppool (most engaged) with alatestpool (most recent), because pooling onlytopsystematically misses last week's thread and pooling onlylatestsystematically misses the decade-old classic.cd_usercannot list a user's posts. That needs/search?q=user:..., which is disallowed. It also cannot report post counts or likes: the anonymous/u/<name>.jsonpayload does not carry them.RSS is not a shortcut around this. The feeds that would actually help are disallowed:
/t/<slug>/<id>.rssand/c/<slug>/<id>.rssboth throwRobotsBlockedError. The global/latest.rssand/top.rssare not forbidden byrobots.txt, but they are strictly worse than the/latest.jsonand/top.jsonpages the server already uses — one anonymous feed page is ~113 KB of XML for the same 30 topics, so nothing here reaches for them.
Being polite to the host is the other half of the same section: all requests are serialized through one
queue with a 350 ms floor (DELPHI_MIN_INTERVAL_MS) because Chief Delphi sits behind Cloudflare and
parallel or rapid-fire requests get challenged, and every response is cached on disk so a repeat call costs
nothing.
Cache | Default TTL |
topic listings ( | 10 min |
thread bodies ( | 3 h |
| 24 h |
category and field metadata ( | 6 h |
Changing this stance — adding /search.json, dropping the throttle, deleting the guard — is a code change
to src/discourse.js, not a config flag. If that is what you want, that is your call to make, but it is
made against the site's stated policy, so this project would rather not ship it.
Using it against another Discourse forum
The server is not Chief Delphi-specific: set DELPHI_BASE_URL to any Discourse forum and the same six
tools read it. Check that forum's robots.txt first — the guard will adapt automatically and may simply
refuse the endpoints you wanted.
Configuration
Variable | Default | Notes |
|
| Any Discourse forum |
|
| Raise it if you see 403 / 429 / 503 |
|
| Per request |
|
| The token |
|
| Delete the directory to force a refetch; there is no cache-off switch |
Tests
npm testtest/smoke.js spawns the real server, speaks stdio to it over the official client, and runs 20
assertions against the live forum: every tool, the pagination contract, unknown-category / unknown-topic /
unknown-user error paths, the max_chars budget, and four assertions dedicated to the guard —
/search.json blocked, /admin/logs.json blocked, /c/technical/9.rss blocked, and a control proving
/latest.json still passes. An assertion that only shows blocking works is not evidence; the guard has to
be shown not to block everything. Cold run ~21 s, warm run ~3.5 s.
Upstream facts worth knowing before you patch this
Verified against the live site, and the reason several first drafts of these tools were wrong:
In a Discourse topic URL,
/t/<id>/<n>.json,<n>is a post number, not a page. The returned window starts five posts before the anchor, socd_topicasks forat_post + 5to land where you meant.?page=on/t/<id>/posts.jsondoes nothing for anonymous requests.A window is padded to
chunk_sizelive posts, so in a thread containing deleted posts consecutivepage=requests can repeat one boundary post.at_postis the gap-safe way to continue, and that is what the "next window" hint in the output emits./u/<name>.jsonwraps the profile in auserkey, and numbereduser_fieldsonly become human-readable ("FRC Team Number") viauser_fieldsin/site.json./c/<slug>.json301s; the canonical path is/c/<slug>/<id>.json, and category-scoped feeds live under/c/<slug>/<id>/l/<feed>.json.
Troubleshooting
Symptom | Cause and fix |
| You asked for a path the site disallows. Not a bug to fix by editing the guard — pick an allowed endpoint. |
| Too fast. Raise |
| The window is too small. |
Tools missing in the client | Host has not loaded the connector: |
Stale tool description after an edit | Same — hosts cache |
Client reports a stdio initialization timeout | This server does no network I/O at startup, so check the |
Disclaimer
Not affiliated with FIRST or Chief Delphi. Forum posts are user-generated content owned by their authors; this server retrieves it for you and does not redistribute it. Whatever you build on top of it — summaries, published research, scraped corpora — is yours to keep within Chief Delphi's terms and that content's rights.
License
MIT — see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Read-only MCP server exposing a user ORANO library to their own AI agent.
Remote MCP server for SeenThis AI Hub. Supports browsing, searching, and posting to AI boards.
Read-only MCP for AI usage profiles, leaderboards, stats, and docs; no writes or private data.
One MCP server for all web data. Scrape, search, crawl, and AI research in one install.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceRead-only MCP server that enables Cursor or Grok Bot to query Reddit data via OAuth bearer auth, providing user profile, history, subscriptions, subreddit feeds, and search.8 npmMIT
- FlicenseAqualityCmaintenanceEnables an AI agent to read and act on a single Discourse forum as one user, including posting, private messages, likes, reactions, bookmarks, watch/track/mute, and reading its own notifications via a stdio MCP server.8-
- AlicenseAqualityAmaintenanceA local-first MCP server that lets AI agents read any webpage as clean Markdown, crawl whole sites within configured limits, search without API keys, and solve supported captchas locally — all without cloud services or third-party keys.3020AGPL 3.0
- AlicenseAqualityAmaintenanceA read-only MCP server so AI agents can query the corpus or the live site.1234 PyPI16MIT