Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -288,10 +288,13 @@ TESSERACT_CMD=C:\Program Files\Tesseract-OCR\tesseract.exe

# --- Agent tooling only (not read by Next.js / env.ts) ---
# Context7 MCP — versioned library docs for agents (Tailwind 4, Zod 4, Playwright,
# Vitest, …). Wired in `.cursor/mcp.json` as https://mcp.context7.com/mcp with
# header CONTEXT7_API_KEY from this env var. Works without a key at lower rate
# Vitest, React 19, @supabase/supabase-js, …). Wired in `.cursor/mcp.json` as
# https://mcp.context7.com/mcp (unversioned remote) with header
# CONTEXT7_API_KEY: ${env:CONTEXT7_API_KEY}. Works without a key at lower rate
# limits; get a free key (prefix ctx7sk) at https://context7.com/dashboard.
# Set it as a Windows/user env var or Cursor MCP env so Cursor can expand
# ${env:CONTEXT7_API_KEY} — putting it only in .env.local does not feed MCP.
# Set it as a Windows/user env var or Cursor Settings → MCP so Cursor can expand
# ${env:CONTEXT7_API_KEY}. Does NOT feed project MCP: .env.local alone, or Cursor
# Cloud Agent Secrets (those inject into the Next app env only). Host-injected
# Cloud Context7 is a separate connector path — see docs/agents-guide.md.
# Never commit the real value. Next.js 16 docs stay local: node_modules/next/dist/docs/
#CONTEXT7_API_KEY=
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,7 @@ Use `docs/codex-cloud.md` as the environment contract:

Durable notes for Cloud Agents. Standard commands live in `README.md` and `package.json`; only non-obvious caveats are captured here.

- Context7 peer-library docs habit (and the Next 16 local-docs carve-out) lives in `docs/agents-guide.md`; Cloud Agent Secrets feed the Next app env, not `.cursor/mcp.json` `${env:}` headers.
- Runtime: the app hard-requires Node 24.x / npm 11.x (`engine-strict`, and `scripts/dev-free-port.mjs` exits on any other major). Node 24 is installed via nvm and symlinked into `/usr/local/cargo/bin` (first entry in `PATH`) so `node`/`npm` resolve to v24 in every shell. If a shell ever resolves `/exec-daemon/node` (v22) instead, prepend the installed nvm Node 24 bin to `PATH` (for example `"$HOME/.nvm/versions/node/v24.18.1/bin"`; run `ls "$HOME/.nvm/versions/node"` to confirm the exact patch version).
- Live vs demo mode: the app auto-detects. When the Supabase + OpenAI env vars below are present (set them as Cloud Agent **Secrets** so they inject into `.env.local`/`process.env`), `isDemoMode()` (`src/lib/env.ts`) is false and the app runs against the live `Clinical KB Database` project (~2000 indexed docs) with OpenAI answer generation. When they are absent, dev auto-falls back to demo mode using the synthetic corpus in `src/lib/demo-data.ts` / `public/demo-documents/`. Required for live mode: `NEXT_PUBLIC_SUPABASE_URL`, `SUPABASE_PROJECT_REF`, `SUPABASE_PROJECT_NAME`, `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY` (`sb_publishable_…`), `SUPABASE_SERVICE_ROLE_KEY` (accepts the `sb_secret_…` secret key), `OPENAI_API_KEY`. Keep `RAG_PROVIDER_MODE=auto` so OpenAI is used with graceful source-only fallback. `E2E_USER_EMAIL`/`E2E_USER_PASSWORD` power CI env-check and Playwright.
- Live-mode caveat: `RAG_PROVIDER_MODE=auto` attempts OpenAI (fast → strong route); if generation fails the built-in quality gates it silently degrades to a deterministic "Source-only" answer that still cites real documents — this is expected, not a failure. The header sign-in UI exposes magic-link + OAuth only (no password field), but the `/api/answer` + retrieval flow works server-side without a browser session.
Expand Down
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,15 +187,18 @@ hosted Supabase MCP server uses OAuth, not repo secrets.

### Context7

Workspace config in `.cursor/mcp.json` points at `https://mcp.context7.com/mcp`;
`.cursor/settings.json` enables the `context7-plugin`. Use Context7 for
versioned library docs — **Tailwind 4, Zod 4, Playwright, Vitest** — not for
Next.js 16: read `node_modules/next/dist/docs/` locally and do not invent App
Router APIs from Context7 or training data.
Workspace config in `.cursor/mcp.json` points at `https://mcp.context7.com/mcp`
(unversioned remote URL); `.cursor/settings.json` enables the `context7-plugin`.
Use Context7 for versioned library docs — **Tailwind 4, Zod 4, Playwright, Vitest,
React 19, `@supabase/supabase-js`** (peers; not exhaustive) — not for Next.js 16:
read `node_modules/next/dist/docs/` locally and do not invent App Router APIs from
Context7 or training data.

Optional `CONTEXT7_API_KEY` (`ctx7sk…`) from [context7.com/dashboard](https://context7.com/dashboard)
raises rate limits. Set it as a user/OS env var or in Cursor MCP env (`.env.local`
alone does not feed MCP). Context7 works without a key at lower limits. Full setup
raises rate limits. Set it as a user/OS env var or in Cursor **Settings → MCP** so
`${env:CONTEXT7_API_KEY}` in `.cursor/mcp.json` expands. `.env.local` alone and
Cursor Cloud Agent Secrets do **not** feed that project MCP header (Secrets feed
the Next app env only). Context7 works without a key at lower limits. Full setup
notes: `docs/agents-guide.md`. Never commit the API key.

## Documentation
Expand Down
32 changes: 21 additions & 11 deletions docs/agents-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,32 @@ servers** per session (tool-schema token bloat degrades agents).
Use registered MCPs before opening dashboards when the task is read-only inspection.
Writes, secret rotations, and hosted mutations stay confirmation-gated per `AGENTS.md`.

| Server | Config | Use for | Do not |
| --------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| **Supabase** (read-only) | `.cursor/mcp.json` — pinned `project_ref=sjrfecxgysukkwxsowpy`, `read_only=true` | `search_docs`, advisors, read SQL, schema inspection | Print secret values; raw-edit retrieval RPCs via `execute_sql`; Auth DB connection-cap (`#011`) — **dashboard only** |
| **Railway** | Root `.mcp.json` | Deploy status, service logs, env **names**/presence | Confuse `RAILWAY_API_TOKEN` (personal) with CI `RAILWAY_TOKEN`; mutate without approval |
| **Context7** | `.cursor/mcp.json` → `https://mcp.context7.com/mcp` (+ Cursor `context7-plugin`) | Versioned docs for **Tailwind 4, Zod 4, Playwright, Vitest** (and similar peers). Optional higher limits: set `CONTEXT7_API_KEY` (see below) | Next.js 16 — always use `node_modules/next/dist/docs/` (AGENTS.md). Do not invent App Router APIs from training data; never commit the API key |
| **Chrome DevTools** | `.cursor/mcp.json` → `npx -y chrome-devtools-mcp@1.6.0` | CLS/LCP/console/network while implementing redesigns (`#147`, `#162`–`#164`, Therapy Compass) | Don't leave it always-on with Browse + Playwright MCP (token bloat). Use for perf/debug passes |
| **GitHub Checks / Actions** | Operator approval pending | PR check visibility when `gh pr checks` returns empty totals | Bot `update-branch`; broaden scopes beyond Checks/Actions read |
| Server | Config | Use for | Do not |
| --------------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| **Supabase** (read-only) | `.cursor/mcp.json` — pinned `project_ref=sjrfecxgysukkwxsowpy`, `read_only=true` | `search_docs`, advisors, read SQL, schema inspection | Print secret values; raw-edit retrieval RPCs via `execute_sql`; Auth DB connection-cap (`#011`) — **dashboard only** |
| **Railway** | Root `.mcp.json` | Deploy status, service logs, env **names**/presence | Confuse `RAILWAY_API_TOKEN` (personal) with CI `RAILWAY_TOKEN`; mutate without approval |
| **Context7** | `.cursor/mcp.json` → `https://mcp.context7.com/mcp` (+ Cursor `context7-plugin`) | Versioned docs for **Tailwind 4, Zod 4, Playwright, Vitest, React 19, `@supabase/supabase-js`** (peers; not exhaustive). Optional higher limits: set `CONTEXT7_API_KEY` (see below) | Next.js 16 — always use `node_modules/next/dist/docs/` (AGENTS.md). Do not invent App Router APIs from training data; never commit the API key |
| **Chrome DevTools** | `.cursor/mcp.json` → `npx -y chrome-devtools-mcp@1.6.0` | CLS/LCP/console/network while implementing redesigns (`#147`, `#162`–`#164`, Therapy Compass) | Don't leave it always-on with Browse + Playwright MCP (token bloat). Use for perf/debug passes |
| **GitHub Checks / Actions** | Operator approval pending | PR check visibility when `gh pr checks` returns empty totals | Bot `update-branch`; broaden scopes beyond Checks/Actions read |

### Context7 API key (optional)

1. Create a free key at [context7.com/dashboard](https://context7.com/dashboard) (`ctx7sk…`).
2. Set `CONTEXT7_API_KEY` as a **user/OS env var** or in Cursor **Settings → MCP → context7**
env so `${env:CONTEXT7_API_KEY}` in `.cursor/mcp.json` resolves. `.env.local` alone does
not feed Cursor MCP.
3. Reload MCP servers in Cursor. Without a key, Context7 still works at lower rate limits.
4. Prefer the Cursor `context7-plugin` / `resolve-library-id` + `query-docs` tools over raw
env so `${env:CONTEXT7_API_KEY}` in `.cursor/mcp.json` resolves. That expansion is what
raises rate limits for the **project** MCP entry (header form
`CONTEXT7_API_KEY: ${env:CONTEXT7_API_KEY}` — leave it; do not switch the committed file to
empty `Authorization: Bearer`).
3. **Does not feed project MCP `${env:}`:** `.env.local` alone, or Cursor Cloud Agent
**Secrets** (Secrets inject into the Next app’s `.env.local` / `process.env`, not into
`.cursor/mcp.json` header interpolation).
4. **Project MCP vs host connector:** the checked-in `.cursor/mcp.json` entry is the desktop
project path. Cursor Cloud may also expose a **host-injected** Context7 connector — same
product, different injection. Cloud quota is not fixed by putting the key only in app
Secrets.
5. The remote URL is **unversioned** (cannot pin like `chrome-devtools-mcp@1.6.0`). Reload MCP
servers after key changes. Without a key, Context7 still works at lower rate limits.
6. Prefer the Cursor `context7-plugin` / `resolve-library-id` + `query-docs` tools over raw
`curl` to `https://context7.com/api/v2/...` unless you are debugging the HTTP API.

Never paste credential values into chat, issues, or commits. Prefer presence/length checks
Expand Down
4 changes: 3 additions & 1 deletion docs/codex-cloud.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,9 @@ setup copies the audited Railway and constrained Supabase URLs into its managed
Hosted ChatGPT still requires the matching installed plugin/connector. In either host, start a fresh
task after consent and verify the actual callable inventory.
The root `.mcp.json` is a cross-client template and static allowlist only. It does not prove hosted
Cloud availability unless a plugin manifest or host explicitly imports it.
Cloud availability unless a plugin manifest or host explicitly imports it. Context7 / library-docs
MCP is Cursor-side (`.cursor/mcp.json` or a host-injected connector), not part of this Codex Cloud
Railway + Supabase allowlist.

Production Supabase stays project-scoped and `read_only=true`, with
`default_tools_approval_mode = "prompt"` so every production metadata/read call requires
Expand Down
1 change: 1 addition & 0 deletions docs/plans/tooling-activation-implementation-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ Return: checklist of done/blocked + evidence lines.
You are executing WS-B of docs/plans/tooling-activation-implementation-plan.md.
B3: done 2026-08-01 — Railway + Supabase MCP habit in docs/agents-guide.md; do not redo unless stale.
B1: done 2026-08-01 — remote Context7 in .cursor/mcp.json (https://mcp.context7.com/mcp); context7-plugin enabled; agents-guide + .env.example; Next 16 stays in node_modules/next/dist/docs/. Do not redo.
B1 follow-up: done 2026-08-04 — contract test + peer-list docs; auth form intentionally unchanged. Pending operator proof: desktop reload + `resolve-library-id` smoke after `CONTEXT7_API_KEY` is configured.
B2: only if user approved — wire GitHub MCP (PRs+Actions toolsets) and/or Checks:Read; verify listing PR checks works.
Keep active MCP count small.
Do not mutate GitHub repo settings without approval. Return: diff summary + verify steps run.
Expand Down
42 changes: 42 additions & 0 deletions tests/cursor-mcp-contract.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";

const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const cursorMcpPath = path.join(repoRoot, ".cursor", "mcp.json");

type CursorMcpConfig = {
mcpServers?: Record<
string,
{
url?: string;
headers?: Record<string, string>;
command?: string;
args?: string[];
}
>;
};

describe("Cursor project MCP contract", () => {
const raw = readFileSync(cursorMcpPath, "utf8");
const config = JSON.parse(raw) as CursorMcpConfig;
const servers = config.mcpServers ?? {};

it("keeps Context7 on the remote URL with env-interpolated CONTEXT7_API_KEY header", () => {
expect(servers.context7?.url).toBe("https://mcp.context7.com/mcp");
expect(servers.context7?.headers?.CONTEXT7_API_KEY).toBe("${env:CONTEXT7_API_KEY}");
expect(servers.context7?.headers?.Authorization).toBeUndefined();
});

it("does not embed Context7 API key literals", () => {
expect(raw).not.toMatch(/ctx7sk/i);
});

it("keeps the sanctioned co-resident MCP set with chrome-devtools pinned", () => {
expect(Object.keys(servers).sort()).toEqual(["chrome-devtools", "context7", "supabase"]);
expect(servers["chrome-devtools"]?.command).toBe("npx");
expect(servers["chrome-devtools"]?.args).toEqual(["-y", "chrome-devtools-mcp@1.6.0"]);
expect(servers.supabase?.url).toBe("https://mcp.supabase.com/mcp?project_ref=sjrfecxgysukkwxsowpy&read_only=true");
});
});
Loading