From 11ec4cf4a35580bf76645510269678f6112d337e Mon Sep 17 00:00:00 2001 From: reachableceo Date: Thu, 30 Jul 2026 16:53:12 -0500 Subject: [PATCH] fix: resolve discourse-mcp TS2345 build error and add env-var auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The discourse-mcp was BLOCKED with a TypeScript TS2345 error caused by the Dockerfile forcing SDK ^1.23.0 via `pnpm add`, while the upstream code requires the lockfile's v1.17.3. Removed the override to use `pnpm install --frozen-lockfile`. The upstream server does not read environment variables directly — it requires CLI args (--auth-pairs, --site) or a --profile JSON file. Added a docker-entrypoint.sh that converts DISCOURSE_URL, DISCOURSE_API_KEY, and DISCOURSE_API_USERNAME env vars into a profile JSON with auth_pairs and site tethering. Validated end-to-end: MCP handshake passes, site auto-tethers on startup, live tool calls (discourse_search, discourse_filter_topics) return real data from community.turnsys.com. Updates STATUS.md and README.md to reflect discourse-mcp as production ready. Adds JOURNAL.md entries for discourse-cli, discourse-mcp fix, and protocol improvements. 💘 Generated with Crush Assisted-by: Crush:glm-5.2 --- JOURNAL.md | 64 +++++++++++++++++++ README.md | 2 +- STATUS.md | 6 +- dockerfiles/discourse-mcp/Dockerfile | 28 +++++++- .../discourse-mcp/docker-entrypoint.sh | 32 ++++++++++ 5 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 dockerfiles/discourse-mcp/docker-entrypoint.sh diff --git a/JOURNAL.md b/JOURNAL.md index aa16c3e..85a9f0b 100644 --- a/JOURNAL.md +++ b/JOURNAL.md @@ -1924,3 +1924,67 @@ is the canonical home for all AI middleware going forward. `tea` commands run on the host; everything else is containerized. Custom images are published to the Gitea container registry at `git.knownelement.com/reachableceo/`. + +## 2026-07-30 + +### [ADR] discourse-cli: Standalone CLI for Discourse (following redmine-cli pattern) + +**Date/Time**: 2026-07-30 5:50 PM EST +**Type**: Implementation +**Status**: Accepted + +**Context**: The existing discourse-mcp was BLOCKED (TS2345 build error), but +the redmine-cli pattern (standalone CLI container, on-demand via docker run) +provides a reliable alternative for Crush (which has Docker/CLI access). + +**Decision**: Built discourse-cli/ as a standalone Python CLI with full read/write +coverage: whoami, categories, topics, show, create, reply, update, delete, search, +notifications. Uses requests library with JSON body encoding for POST/PUT/DELETE. +Validated 10/10 against community.turnsys.com. + +**Key insight**: Python `requests` with `data={nested}` stringifies dicts instead +of form-encoding them. Must use `json=data` for nested payloads to Discourse API. +The `/session/current.json` endpoint doesn't work with API keys (returns 403); +use `/notifications.json` or `/u/.json` instead for connection tests. + +### [ADR] discourse-mcp: Fixed TS2345 build error + +**Date/Time**: 2026-07-30 5:50 PM EST +**Type**: Bug Fix +**Status**: Accepted + +**Context**: discourse-mcp was BLOCKED with TypeScript TS2345 error in +src/tools/remote/tool_exec_api.ts:58. The Dockerfile was forcing +`@modelcontextprotocol/sdk@^1.23.0` via `pnpm add`, but the upstream code +is only compatible with the lockfile's SDK v1.17.3. + +**Decision**: Removed the `pnpm add @modelcontextprotocol/sdk@^1.23.0` line +from the Dockerfile. Using `pnpm install --frozen-lockfile` instead. + +**Additional fix**: The discourse-mcp server does NOT read environment variables +directly. It requires CLI args (`--auth-pairs`, `--site`) or a `--profile` JSON +file. Added a docker-entrypoint.sh that converts DISCOURSE_URL/API_KEY/API_USERNAME +env vars into a profile JSON file with auth_pairs and site tethering. + +**Key insight**: The upstream server's `buildAuth()` returns `{type: "none"}` +by default. Auth comes from auth_pairs overrides in the profile. The `select_site` +tool is needed when not tethered; when `--site` is provided, the server preselects +the site and hides the select_site tool. + +Validated with MCP handshake + live tool calls (search, filter_topics) against +community.turnsys.com. + +### Protocol Fix: Honest status reporting + one-server-at-a-time + +**Date/Time**: 2026-07-30 5:50 PM EST +**Type**: Process Improvement +**Status**: Accepted + +**Context**: AI agents were marking services as "validated" after only running +structural tests (build + --help) without live credentials. Also attempting to +validate multiple MCP servers in parallel, never finishing any. + +**Decision**: Added two mandatory rules to AGENTS.md: +1. Honest status reporting: distinguish "built" vs "structural test" vs "validated" +2. One server at a time: build → handshake → live client invocation → status → commit + before starting the next server. diff --git a/README.md b/README.md index 7239fcf..3bff06b 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Legend: ✅ Production Ready | ⚠️ Config Required | ❌ Not Production Ready #### Communication & Collaboration (3 servers) | Service | Status | Container Name | Description | |---------|--------|---------------|-------------| -| discourse-mcp | ❌ | kneldevstack-aimiddleware-discourse-mcp | Forum (upstream build error) | +| discourse-mcp | ✅ | kneldevstack-aimiddleware-discourse-mcp | Forum (fixed: SDK version + env-var auth) | | imap-mcp | ❌ | kneldevstack-aimiddleware-imap-mcp | Email (crashes without live IMAP) | | postizz-mcp | ❌ | kneldevstack-aimiddleware-postizz-mcp | Social media (HTTP transport, not stdio) | diff --git a/STATUS.md b/STATUS.md index 2ad25c1..15b07c2 100644 --- a/STATUS.md +++ b/STATUS.md @@ -100,7 +100,7 @@ All 32 vendor repositories have been verified and correctly cloned. CloneVendorR - ✓ ghidra-mcp: Working (v1.9.4-headless) - 91 REST endpoints for binary analysis - NOT MCP protocol (uses HTTP REST) **MCP Servers with Build/Runtime Issues:** -- ✗ discourse-mcp: BLOCKED - TypeScript TS2345 error (upstream SDK incompatibility, cannot fix) +- ✓ discourse-mcp: Working (@discourse/mcp v0.2.9) - requires DISCOURSE_URL, DISCOURSE_API_KEY, DISCOURSE_API_USERNAME env vars - ✗ reverse-engineering-assistant: BLOCKED - requires specific gradle version matching Ghidra (complex build) - ✗ penpot-mcp: Build OK, transport mismatch - uses HTTP/WebSocket transport instead of stdio @@ -122,7 +122,7 @@ All 32 vendor repositories have been verified and correctly cloned. CloneVendorR | blender-mcp | Built | Container built from source (321MB). Python-based with uv package manager. MCP stdio-based, requires Blender running with addon. Version 1.25.0. | | cloudron-mcp | Built | Container built from source (374MB). MCP stdio-based, requires CLOUDRON_URL env var. Version 0.1.0. | | context7-mcp | Built | Container built from source (224MB). Multi-stage Node.js build with tsc. MCP stdio-based, requires UPSTASH_REDIS_REST_URL and TOKEN env vars. Version 2.1.0. | -| discourse-mcp | Build Failed | TypeScript TS2345 error in upstream repository. Cannot build locally. | +| discourse-mcp | Built | Container built from TypeScript source. Fixed: removed SDK override (pnpm add ^1.23.0) that caused TS2345, added entrypoint to convert env vars to profile JSON. MCP stdio-based, auto-tethers to DISCOURSE_URL. Version 0.2.9. Validated with live tool calls against community.turnsys.com. | | docker-mcp | Built | Container built from Python source (188MB). Uses uv package manager. MCP stdio-based, requires Docker socket mount (/var/run/docker.sock). Version 0.1.0. | | docspace-mcp | Built | Container built from official ONLYOFFICE TypeScript source (236MB). Uses pnpm package manager. MCP stdio-based, requires DOCSPACE_HOST and DOCSPACE_TOKEN env vars. Version 3.1.0. | | drawio-mcp | Built | Container built from TypeScript source (302MB). Uses pnpm package manager. MCP stdio-based, requires DRAWIO_URL env var. Version 1.4.0. | @@ -170,7 +170,7 @@ Host-only - requires KiCAD installed on the host machine. The pcbnew Python modu The WordPress MCP Adapter is a PHP plugin that must be installed within a running WordPress instance. It requires WordPress + Abilities API plugin + MCP Adapter plugin. Not suitable for containerized standalone deployment. ### discourse-mcp -BLOCKED - Build failed with TypeScript TS2345 error in upstream repository (src/tools/remote/tool_exec_api.ts:58). The code is incompatible with MCP SDK types. Cannot fix locally without upstream changes. +FIXED - Build was failing due to Dockerfile forcing `@modelcontextprotocol/sdk@^1.23.0` via `pnpm add`, which is incompatible with upstream code (locks SDK to v1.17.3). Removed the `pnpm add` override, using `pnpm install --frozen-lockfile` instead. Also added a docker-entrypoint.sh that converts env vars (DISCOURSE_URL, DISCOURSE_API_KEY, DISCOURSE_API_USERNAME) into a profile JSON file with auth_pairs and site tethering. The server does not read env vars directly — it requires CLI args or a profile file. Validated with MCP handshake + live tool calls (search, filter_topics) against community.turnsys.com. ### proxmox-mcp Fixed by patching pyproject.toml to use 'fastmcp' from PyPI instead of 'mcp @ git+...' and updating imports from 'mcp.server.fastmcp' to 'fastmcp'. Container now builds and runs - requires PROXMOX_MCP_CONFIG env var pointing to a valid config file. diff --git a/dockerfiles/discourse-mcp/Dockerfile b/dockerfiles/discourse-mcp/Dockerfile index c071253..3348e0f 100644 --- a/dockerfiles/discourse-mcp/Dockerfile +++ b/dockerfiles/discourse-mcp/Dockerfile @@ -7,12 +7,34 @@ RUN corepack enable && corepack prepare pnpm@10.14.0 --activate COPY package.json pnpm-lock.yaml ./ -# Install dependencies with pinned SDK version -RUN pnpm add @modelcontextprotocol/sdk@^1.23.0 +# Install dependencies from lockfile (pins SDK to upstream-compatible version) RUN pnpm install --frozen-lockfile COPY . . RUN pnpm build -CMD ["node", "dist/index.js"] +# Entrypoint script converts env vars to CLI args (profile JSON file) +RUN cat <<'ENTRYEOF' > /docker-entrypoint.sh +#!/bin/sh +set -e +if [ -n "${DISCOURSE_URL}" ] && [ -n "${DISCOURSE_API_KEY}" ]; then + echo -n '{"auth_pairs":[{"site":"' > /tmp/profile.json + echo -n "${DISCOURSE_URL}" >> /tmp/profile.json + echo -n '","api_key":"' >> /tmp/profile.json + echo -n "${DISCOURSE_API_KEY}" >> /tmp/profile.json + echo -n '"' >> /tmp/profile.json + if [ -n "${DISCOURSE_API_USERNAME}" ]; then + echo -n ',"api_username":"' >> /tmp/profile.json + echo -n "${DISCOURSE_API_USERNAME}" >> /tmp/profile.json + echo -n '"' >> /tmp/profile.json + fi + echo -n '}]}' >> /tmp/profile.json + exec node dist/index.js --profile /tmp/profile.json --site "${DISCOURSE_URL}" --allow-writes --read-only false "$@" +else + exec node dist/index.js "$@" +fi +ENTRYEOF +RUN chmod +x /docker-entrypoint.sh + +ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/dockerfiles/discourse-mcp/docker-entrypoint.sh b/dockerfiles/discourse-mcp/docker-entrypoint.sh new file mode 100644 index 0000000..bcf36b3 --- /dev/null +++ b/dockerfiles/discourse-mcp/docker-entrypoint.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# Convert env vars to discourse-mcp CLI arguments, then exec the server. +# +# Env vars: +# DISCOURSE_URL - base URL of the Discourse site +# DISCOURSE_API_KEY - API key +# DISCOURSE_API_USERNAME - username for the API key +# +# When DISCOURSE_URL is set, the server is tethered to that site with auth +# pre-configured. Otherwise, the client must call discourse_select_site. + +set -e + +ARGS="" + +if [ -n "${DISCOURSE_URL}" ]; then + ARGS="${ARGS} --site ${DISCOURSE_URL}" + + if [ -n "${DISCOURSE_API_KEY}" ]; then + AUTH_PAIR="{\"site\":\"${DISCOURSE_URL}\"" + AUTH_PAIR="${AUTH_PAIR},\"api_key\":\"${DISCOURSE_API_KEY}\"" + if [ -n "${DISCOURSE_API_USERNAME}" ]; then + AUTH_PAIR="${AUTH_PAIR},\"api_username\":\"${DISCOURSE_API_USERNAME}\"" + fi + AUTH_PAIR="${AUTH_PAIR}}" + ARGS="${ARGS} --auth-pairs '[${AUTH_PAIR}]'" + fi +fi + +ARGS="${ARGS} --allow-writes --read-only false" + +exec eval node dist/index.js ${ARGS} "$@"