RAG Search MCP App - #25
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new self-contained example under agents/rag-search-mcp/ demonstrating retrieval‑augmented generation (RAG) with per‑patient access control: a stateful Streamable‑HTTP MCP server provides scoped semantic search, and a React+Express UI provisions/uses a Corti orchestrator and mints short‑lived scope tokens.
Changes:
- Added
search-documents-mcp(TypeScript) MCP server: embedding-based local retrieval, disk-backed chunk store, scope-token verification, and HTTP endpoints (/mcp,/bind-context,/ingest). - Added
rag-ui-corti(React + Express) app: Corti connection + orchestrator provisioning/detection, mock clinician sign-in and patient panel, scoped chat, and document upload. - Added mock documents and READMEs to make the demo runnable end-to-end.
Reviewed changes
Copilot reviewed 52 out of 55 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| agents/rag-search-mcp/README.md | Top-level README describing the two demo components and how to run them. |
| agents/rag-search-mcp/search-documents-mcp/tsconfig.json | TypeScript compiler configuration for the MCP server. |
| agents/rag-search-mcp/search-documents-mcp/package.json | MCP server package metadata and runtime dependencies. |
| agents/rag-search-mcp/search-documents-mcp/package-lock.json | MCP server dependency lockfile. |
| agents/rag-search-mcp/search-documents-mcp/.env.example | Example env vars for MCP server (secret + port). |
| agents/rag-search-mcp/search-documents-mcp/README.md | MCP server documentation: flow, scoping model, endpoints, and setup. |
| agents/rag-search-mcp/search-documents-mcp/src/embed.ts | On-device embedding generation via @huggingface/transformers. |
| agents/rag-search-mcp/search-documents-mcp/src/store.ts | File-backed chunk store + scoped semantic search implementation. |
| agents/rag-search-mcp/search-documents-mcp/src/token.ts | Scope-token verification (HMAC + exp). |
| agents/rag-search-mcp/search-documents-mcp/src/scope.ts | Auth header parsing + contextId→scope binding store. |
| agents/rag-search-mcp/search-documents-mcp/src/server.ts | MCP tool definition (search_documents) and response formatting. |
| agents/rag-search-mcp/search-documents-mcp/src/http.ts | Express HTTP wrapper for MCP transport + /bind-context + /ingest. |
| agents/rag-search-mcp/search-documents-mcp/src/ingest.ts | CLI ingestion utility for indexing docs/. |
| agents/rag-search-mcp/search-documents-mcp/docs/clinic-policy.md | Mock shared reference document. |
| agents/rag-search-mcp/search-documents-mcp/docs/records-retention-memo.md | Mock shared reference document (retention update). |
| agents/rag-search-mcp/search-documents-mcp/docs/hypertension-guideline.md | Mock shared reference guideline document. |
| agents/rag-search-mcp/search-documents-mcp/docs/diabetes-guideline.md | Mock shared reference guideline document. |
| agents/rag-search-mcp/search-documents-mcp/docs/metformin-medication-sheet.md | Mock shared reference medication sheet. |
| agents/rag-search-mcp/search-documents-mcp/docs/discharge-summary.md | Mock patient-scoped document (patient:000-MOCK-1234). |
| agents/rag-search-mcp/search-documents-mcp/docs/discharge-summary-2.md | Mock patient-scoped document (patient:000-MOCK-5678). |
| agents/rag-search-mcp/search-documents-mcp/docs/lab-results.md | Mock patient-scoped document (patient:000-MOCK-5678). |
| agents/rag-search-mcp/search-documents-mcp/docs/clinic-note-sam.md | Mock patient-scoped document (patient:000-MOCK-3456). |
| agents/rag-search-mcp/search-documents-mcp/docs/cardiology-note-maria.md | Mock patient-scoped document (patient:000-MOCK-9012). |
| agents/rag-search-mcp/rag-ui-corti/package.json | UI app package metadata and dependencies. |
| agents/rag-search-mcp/rag-ui-corti/package-lock.json | UI app dependency lockfile. |
| agents/rag-search-mcp/rag-ui-corti/.env.example | Example env vars for Corti + MCP wiring + token secret + prompt. |
| agents/rag-search-mcp/rag-ui-corti/config.js | Env loading/validation and derived MCP endpoint URLs. |
| agents/rag-search-mcp/rag-ui-corti/corti.js | Corti SDK client lifecycle + Express guards + error shaping. |
| agents/rag-search-mcp/rag-ui-corti/session.js | In-process “active clinician” and active MCP name tracking. |
| agents/rag-search-mcp/rag-ui-corti/directory.js | Mock clinician/patient directory + profile shaping for UI. |
| agents/rag-search-mcp/rag-ui-corti/token.mjs | HMAC signing for scope tokens minted by the UI app. |
| agents/rag-search-mcp/rag-ui-corti/mcp.js | Token minting (scopes + names + audience) + agent MCP matching by URL. |
| agents/rag-search-mcp/rag-ui-corti/server.js | Express API server wiring + JSON size limit configuration. |
| agents/rag-search-mcp/rag-ui-corti/routes/auth.js | /api/auth route to establish Corti connection. |
| agents/rag-search-mcp/rag-ui-corti/routes/clinicians.js | Clinician listing + mock “sign-in” route. |
| agents/rag-search-mcp/rag-ui-corti/routes/agent.js | Agent detection-by-MCP-URL + provisioning route. |
| agents/rag-search-mcp/rag-ui-corti/routes/chat.js | Chat start (warmup + pre-bind) + message relay with token DataPart. |
| agents/rag-search-mcp/rag-ui-corti/routes/documents.js | Document upload route enforcing scope against clinician panel. |
| agents/rag-search-mcp/rag-ui-corti/index.html | Vite app entry HTML + font loading. |
| agents/rag-search-mcp/rag-ui-corti/vite.config.js | Vite dev server configuration + /api proxy. |
| agents/rag-search-mcp/rag-ui-corti/tailwind.config.js | Tailwind theme configuration (tokens + fonts). |
| agents/rag-search-mcp/rag-ui-corti/postcss.config.js | PostCSS plugins for Tailwind build. |
| agents/rag-search-mcp/rag-ui-corti/src/main.jsx | React entrypoint. |
| agents/rag-search-mcp/rag-ui-corti/src/index.css | Tailwind base styles + theme variables. |
| agents/rag-search-mcp/rag-ui-corti/src/api.js | Frontend API client + agent-task polling helpers. |
| agents/rag-search-mcp/rag-ui-corti/src/ui.jsx | Shared UI primitives (Banner, ScreenHeader, MONO). |
| agents/rag-search-mcp/rag-ui-corti/src/App.jsx | Top-level flow orchestration between views. |
| agents/rag-search-mcp/rag-ui-corti/src/AuthView.jsx | “Connect to Corti” screen. |
| agents/rag-search-mcp/rag-ui-corti/src/AgentSetupView.jsx | Detect-or-create orchestrator screen. |
| agents/rag-search-mcp/rag-ui-corti/src/ClinicianSignInView.jsx | Mock clinician picker screen. |
| agents/rag-search-mcp/rag-ui-corti/src/PatientPanelView.jsx | Patient panel + start chat/upload navigation. |
| agents/rag-search-mcp/rag-ui-corti/src/UploadView.jsx | Upload document UI (scope selection + file/text ingest). |
| agents/rag-search-mcp/rag-ui-corti/src/AgentChatView.jsx | Minimal chat UI with file attachment support and context continuity. |
| agents/rag-search-mcp/rag-ui-corti/README.md | UI app documentation: setup, token flow, and layout. |
Files not reviewed (1)
- agents/rag-search-mcp/search-documents-mcp/package-lock.json: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Natalia Markitantova (markitosha)
left a comment
There was a problem hiding this comment.
These comments were written with AI assistance and validated by a person. They may still be more robust than you expect — please ask if anything seems off or unclear.
Thanks for this — it is a genuinely well-built example. The scoped RAG design is thoughtful, both component READMEs are thorough, and the security-sensitive paths (scope tokens, ingest gating) are implemented carefully. A few repo-convention items to sort before merge. Inline comments have the per-file detail.
Blockers
- Folder layout does not match repo convention (inline). Use
{category}/{language-or-stack}/{example-name}/— e.g.agents/react/...,agents/typescript/...— notagents/rag-search-mcp/{app}. - Root
README.mdnot updated (inline). Add row(s) under### Agentsso the example is discoverable. - Not wired into CI (
.github/workflows/sdk-examples-ci.yml). Neither app is registered — checks were skipped on this PR, not passed. Addts-filter+ matchingincludeper project. - Missing linter setup in both projects (inline). Repo convention; add Biome even though CI did not run here.
Should fix
@corti/sdkpinned to^1.2.0— bump to^3/^4orlatest(inline).VITE_-prefixed Corti credentials + non-standard env names (inline).- Parent README "Note" contradicts the code and links to missing
notes.md(inline).
Nits (optional)
- Add
"private": trueto bothpackage.jsonfiles (inline). - Clarify committed
data/index.jsonvsnpm run reindexin MCP README (inline). notes.mdlink also in MCP README Layout section (inline).- UI server is plain
.jswhile MCP is TypeScript — consider aligning (inline).
Code quality
- Checked naming: module/route files use
camelCase(corti.js,auth.js), React components usePascalCase(AgentChatView.jsx) — matches repo standard. - Main naming gap is env vars (
VITE_CORTI_*vsCORTI_*) — covered inline. rag-ui-cortikeeps Express at the project root and React undersrc/; acceptable for a Vite + Express split.
Security: no committed secrets; Corti client secret is read server-side only today. The VITE_ prefix on secrets is still a footgun — fix inline.
(Left as a draft for a maintainer to review and submit.)
Natalia Markitantova (markitosha)
left a comment
There was a problem hiding this comment.
These comments were written with AI assistance and validated by a person. They may still be more robust than you expect — please ask if anything seems off or unclear.
Thanks for the follow-up — a lot of the earlier feedback landed cleanly (folder layout, root README rows, Biome config, @corti/sdk → latest, CORTI_* env naming, private: true, TypeScript server migration). Remaining items below each match an inline comment.
Re-review
- ✅ Addressed:
agents/react/search-documents-ui/+agents/typescript/search-documents-mcp/layout; root README Agents rows; Biome +lintscripts;@corti/sdklatest; noVITE_Corti secrets;private: true; Express server in TypeScript; parentnotes.md/ contradictory Note removed. ⚠️ Still open: neither project is wired into CI (carried forward).- 💬 Discussed (no change needed):
ALLOW_INGESTdefault-off + README warnings; skipping localhost bind and verbose demo logging (not required for merge).
Blockers
- CI still not registered for either new project.
npm run lintfails insearch-documents-ui(56 errors) — will break CI once wired.
Should fix
- MCP README claims a committed
data/index.jsonthat is not in the PR.
Code quality
- Naming/structure look good (
camelCasemodules,PascalCaseReact components, Corti calls go through@corti/sdkon the server). Scope-token design is solid.
| "build": "vite build", | ||
| "preview": "vite preview", | ||
| "typecheck": "tsc -p tsconfig.server.json", | ||
| "lint": "biome check ." |
There was a problem hiding this comment.
Blocker: wire both new projects into CI. Neither search-documents-ui nor search-documents-mcp appears in .github/workflows/sdk-examples-ci.yml — path filters + matrix include — so lint/build never run on this contribution.
Why it matters: without this, regressions land silently and Biome issues (see sibling comment) never surface on the PR.
How to fix: add two filters under the ts-filter step and matching include entries, mirroring react-next-agent-chat:
search-documents-ui:
- 'agents/react/search-documents-ui/**'
- '.github/workflows/sdk-examples-ci.yml'
search-documents-mcp:
- 'agents/typescript/search-documents-mcp/**'
- '.github/workflows/sdk-examples-ci.yml'and:
- project: search-documents-ui
path: agents/react/search-documents-ui
- project: search-documents-mcp
path: agents/typescript/search-documents-mcpFilter name must match the project value. Same pattern as agents/react/next-agent-chat.
| "build": "vite build", | ||
| "preview": "vite preview", | ||
| "typecheck": "tsc -p tsconfig.server.json", | ||
| "lint": "biome check ." |
There was a problem hiding this comment.
Blocker: npm run lint fails (56 errors). Biome is present (good — prior feedback addressed), but biome check . currently exits non-zero. Most hits are lint/style/useBlockStatements (e.g. server/corti.ts, server/mcp.ts), noNonNullAssertion in server/config.ts, and useButtonType in the React views.
Why it matters: once CI is wired (previous comment), this job will fail and block merge. Even locally, a red lint script means the example does not match repo convention.
How to fix: from this directory run npx biome check --write . (or fix manually), then re-run npm run lint until it is clean. Compare with sdk/typescript/express-web-api/ for the same Biome ruleset.
| npm run reindex # optional on first run — a prebuilt index is committed | ||
| ``` | ||
|
|
||
| > **First run:** `data/index.json` is committed, so you can skip `npm run reindex` |
There was a problem hiding this comment.
Should fix: README says data/index.json is committed, but it is not in the PR. Setup calls reindex "optional on first run" and the callout says you can skip straight to Running — yet there is no data/ directory in the tree. Skipping reindex leaves an empty index (store.ts treats a missing file as []), so search_documents returns nothing.
Why it matters: readers following this README alone will think the demo works out of the box; the UI README correctly requires npm run reindex, so the two halves disagree.
How to fix: either commit a generated data/index.json, or (simpler) make reindex required in Setup — drop the "optional / prebuilt index" wording and align with agents/react/search-documents-ui/README.md (npm run build && npm run reindex && npm run start:http).
f799443 to
54d0064
Compare
Logging scopes count vs. logging full patient MRN Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Potential fix for pull request finding Scope count vs. full patient MRN Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Potential fix for pull request finding Dont log sensitive data Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Potential fix for pull request finding Kebab case project name Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
dbdf7d4 to
d438e8c
Compare
Summary
Adds a self-contained example under agents/rag-search-mcp/ demonstrating retrieval-augmented generation with per-patient access control on the Corti Agentic Framework. It pairs a local MCP server that does scoped document search with a UI app that auto-provisions a Corti orchestrator wired to that MCP. A clinician signs in, sees their patient panel, and chats with the agent. Retrieval is scoped server-side to exactly the patients that clinician is allowed to see, so the agent can never surface records outside that panel, including while asking clarifying questions.
What's included
search-documents-mcp (TypeScript): A stateful Streamable-HTTP MCP server exposing one search_documents tool. Embeddings run on-device; chunks are tagged shared or patient: and filtered by a verified scope token bound to the A2A contextId. Also serves /bind-context and /ingest.
rag-ui-corti (React + Express): Connects to Corti, provisions an orchestrator wired to the MCP, and gives a clinician sign-in, patient panel, scoped chat, and document upload. Each message carries a short-lived scope token from the clinician's panel.
Packages / tools
MCP server: @modelcontextprotocol/sdk, @huggingface/transformers (all-MiniLM-L6-v2, ONNX runtime), express, zod, TypeScript.
UI app: react 18 + vite, express, @corti/sdk, lucide-react, tailwindcss, dotenv, concurrently.
Auth between the two: HMAC-signed, audience-scoped, 5-minute scope tokens (the UI signs, the MCP verifies a shared secret).
Planed updates
Medical.Document.RAG.Lookup.App.Overview.mp4