T21: MCP install instructions (/assistants) #20

Merged
dzenda merged 42 commits from t21-mcp-setup into t20-connectors 2026-11-14 17:49:55 +01:00
Owner

T21: MCP install instructions per client (/assistants)

Stacked on #19 (t20-connectors) → … → #1. Merge those first.

What

  • /assistants page ("AI assistants" in the nav, plus a card on the overview). It's public, so people can read it before signing in. It isn't at /mcp, because /mcp is the OAuth-protected MCP endpoint.
    • The MCP server URL with a copy button, generated from PUBLIC_URL. A localhost warning explains that ChatGPT and claude.ai need public HTTPS, while local desktop clients work.
    • ChatGPT: chatgpt.com/plugins → + → Add custom MCP server → URL, Authentication OAuth → Create as a plugin → install, then sign in and approve. (OpenAI docs)
    • Claude (web/desktop): Customize → Connectors → + Add → Add custom connector → URL → Continue ×2 → Sign in now + OAuth client "Register automatically" (DCR) → Add. Covers Team/Enterprise, enabling it in a chat, and the public HTTPS port 443 requirement. (Claude docs)
    • Claude Code: claude mcp add --transport http fit-os '<url>', then /mcp to sign in. The URL is shell-quoted, and tests cover IPv6 brackets.
    • Cursor: a ~/.cursor/mcp.json snippet (mcpServers.fit-os.url).
    • VS Code (Copilot): a .vscode/mcp.json snippet (servers.fit-os.type = http).
    • Other clients: transport, OAuth 2.1 + DCR + PKCE S256, both metadata URLs, and the MCP Inspector command.
    • After adding: the consent page, managing access under Connections, read-only. Plus three example questions.
  • src/mcp/setup.ts is pure and tested: URLs, local detection, JSON snippets, shellQuote.
  • Accessibility: copy feedback via aria-live. Clipped snippets sit in focusable labelled sections with a focus ring, so keyboard users can scroll them.
  • Full en/cs strings.

Verified

  • bun run lint && bun run typecheck && bun run test: 244 tests, including the new setup tests (local detection, valid JSON for Cursor and VS Code, a quoted Claude Code command with IPv6).
  • bun run dev + curl: /assistants renders (en and cs) with the generated URL, the command, the localhost warning, the ChatGPT plugins flow, "Register automatically", and 7 focusable snippet regions. /mcp still returns 401 (OAuth).
  • zsh check: the generated IPv6 command passes the URL as a single argument.

Astra review (gpt-6-astra)

It confirmed that the config shapes and route separation are sound.

Fixed (I checked each against the vendors' current docs first):

  1. (P2) The ChatGPT flow was outdated (Apps & Connectors) → the plugins flow, including install.
  2. (P2) The Claude flow was outdated → the Customize/Connectors wizard, with "Register automatically" (DCR) recommended. It's guaranteed to work with this server, whereas the "published identity" option relies on CIMD.
  3. (P2) An IPv6 URL broke the Claude Code command in zsh → single-quoted.
  4. (P2) Overflowing snippets weren't keyboard-scrollable → focusable labelled scroll regions.

🤖 Generated with Claude Code

## T21: MCP install instructions per client (`/assistants`) **Stacked on #19 (`t20-connectors`) → … → #1.** Merge those first. ### What - **`/assistants` page** ("AI assistants" in the nav, plus a card on the overview). It's public, so people can read it before signing in. It isn't at `/mcp`, because `/mcp` is the OAuth-protected MCP endpoint. - **The MCP server URL** with a copy button, generated from `PUBLIC_URL`. A **localhost warning** explains that ChatGPT and claude.ai need public HTTPS, while local desktop clients work. - **ChatGPT:** `chatgpt.com/plugins` → **+** → *Add custom MCP server* → URL, Authentication *OAuth* → *Create as a plugin* → install, then sign in and approve. ([OpenAI docs](https://developers.openai.com/api/docs/guides/custom-mcp-server)) - **Claude (web/desktop):** *Customize → Connectors* → **+ Add** → *Add custom connector* → URL → *Continue* ×2 → *Sign in now* + OAuth client **"Register automatically"** (DCR) → *Add*. Covers Team/Enterprise, enabling it in a chat, and the public HTTPS port 443 requirement. ([Claude docs](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp)) - **Claude Code:** `claude mcp add --transport http fit-os '<url>'`, then `/mcp` to sign in. The URL is shell-quoted, and tests cover IPv6 brackets. - **Cursor:** a `~/.cursor/mcp.json` snippet (`mcpServers.fit-os.url`). - **VS Code (Copilot):** a `.vscode/mcp.json` snippet (`servers.fit-os.type = http`). - **Other clients:** transport, OAuth 2.1 + DCR + PKCE S256, both metadata URLs, and the MCP Inspector command. - After adding: the consent page, managing access under Connections, read-only. Plus three example questions. - `src/mcp/setup.ts` is pure and tested: URLs, local detection, JSON snippets, `shellQuote`. - **Accessibility:** copy feedback via `aria-live`. Clipped snippets sit in focusable labelled `section`s with a focus ring, so keyboard users can scroll them. - Full en/cs strings. ### Verified - `bun run lint && bun run typecheck && bun run test`: 244 tests, including the new setup tests (local detection, valid JSON for Cursor and VS Code, a quoted Claude Code command with IPv6). - `bun run dev` + curl: `/assistants` renders (en and cs) with the generated URL, the command, the localhost warning, the ChatGPT plugins flow, "Register automatically", and 7 focusable snippet regions. `/mcp` still returns 401 (OAuth). - zsh check: the generated IPv6 command passes the URL as a single argument. ### Astra review (gpt-6-astra) It confirmed that the config shapes and route separation are sound. **Fixed (I checked each against the vendors' current docs first):** 1. *(P2)* The ChatGPT flow was outdated (Apps & Connectors) → the plugins flow, including install. 2. *(P2)* The Claude flow was outdated → the Customize/Connectors wizard, with **"Register automatically"** (DCR) recommended. It's guaranteed to work with this server, whereas the "published identity" option relies on CIMD. 3. *(P2)* An IPv6 URL broke the Claude Code command in zsh → single-quoted. 4. *(P2)* Overflowing snippets weren't keyboard-scrollable → focusable labelled scroll regions. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also fixes a production bug found while smoke-testing: createFnContext stored the global
fetch on the context object, so ctx.fetch(...) threw "Illegal invocation" in workerd. That
broke every registry call in the Worker (including all MCP tool calls); Node tests could
not see it. Regression test emulates workerd's this-check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ALLOWED_USERS enforced for MCP too (shared policy); excluded users' grants revoked on use
- telemetry: streaming 512-byte cap before parsing
- POST /oauth/register rate-limited per IP and instance-wide (Workers rate-limit bindings)
- FIT API bodies capped at 2 MB (streaming); Grades tree walk iterative and bounded
- readBounded moved to src/lib/bounded.ts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Logos: FIT service marks vendored in public/services (see README there); assistant marks
from @lobehub/icons (MIT), simple-icons (CC0) and devicon (MIT).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- service_settings table (migration 0002); MCP calls to a switched-off service fail with
  service_disabled, aggregates skip it with a warning, fit.status lists it
- setServiceEnabledFn: same-origin + session only

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- fit.sirius.courseEvents: every parallel of a course (room, teachers, capacity/occupied),
  shared paging helper with fit.sirius.events; respects the Sirius MCP switch
- getCourseWeek server fn for the calendar overlay; upcoming deadlines (60 days) beside the week

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- layoutLanes/pragueMinutes in src/lib/calendar.ts (tested)
- shared EventDetails popover; agenda list stays on mobile
- agentSetupPrompt in src/mcp/setup.ts (tested)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Referrer-Policy: same-origin (no-referrer made browsers send Origin: null on our form POSTs,
  so every management form was rejected with 403); shared isSameOriginRequest accepts
  Origin: null only with Sec-Fetch-Site: same-origin
- No COOP on OAuth/consent/login routes (popup-based web MCP clients keep window.opener)
- fit.sirius.icalUrl is websiteOnly: excluded from the MCP registry (URL embeds a credential)
- docs: telemetry wording (operator-enabled, consent advice), CSRF/COOP/credential notes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewed-on: #25
Reviewed-on: #24
Reviewed-on: #23
Reviewed-on: #22
Reviewed-on: #21
dzenda merged commit 425faee565 into t20-connectors 2026-11-14 17:49:55 +01:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
dzenda/os!20
No description provided.