From 8b32b7fd651392a2bf8321660cdddc07bda36562 Mon Sep 17 00:00:00 2001 From: Anna Effort Date: Tue, 18 Aug 2026 17:56:48 -0700 Subject: [PATCH 1/3] docs: name components by role instead of repo slug Refer to the backing service as the ContextForge API rather than "the gateway", "ContextForge MCP Gateway", or "mcp-context-forge". "Gateway" is ambiguous here: it is also a domain entity (GatewayCreate/GatewayRead, /v1/gateways, Gateways.tsx), so "point the BFF at the gateway" read as if a gateway had to be created first. The repo slug is now confined to links and clone paths. Add a component table up front so the body can use short forms, and lead with "AI gateway" as category framing rather than a referring term. Also fix two leftovers from when this lived inside mcp-context-forge: CI workflow links pointed at ../.github/, and the structure tree was rooted at client/. Signed-off-by: Anna Effort --- README.md | 74 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 8c9661b..f188964 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,20 @@ -# ContextForge UI Client +# ContextForge Web UI -React-based admin UI for ContextForge MCP Gateway. +The web interface for ContextForge, the open source AI gateway that federates +tools, agents, and APIs into one endpoint. + +The backing service is a separate process in a separate repository +([IBM/mcp-context-forge](https://github.com/IBM/mcp-context-forge)). This +repository holds the two pieces that sit in front of it: + +| Component | Lives in | Role | +| --------- | -------- | ---- | +| **ContextForge API** | separate repo | FastAPI service that owns auth and all business data | +| **BFF** | `server/` | Fastify app holding the session/CSRF boundary in front of the API | +| **Client** | `src/` | React SPA, served as static files by the BFF | + +Throughout this README, "the API", "the BFF", and "the client" refer to those +three. The browser only ever talks to the BFF, never directly to the API. This UI targets **ContextForge API v1.0.7**, matching [`openapi.json`](./openapi.json) committed at repo root. @@ -29,24 +43,25 @@ npm install ### Development -The app is split into three pieces that all must run for local dev: - -- **ContextForge** (`mcpgateway`) — the upstream FastAPI gateway. It owns - auth and all business data. -- **BFF** (`server/`) — a Fastify app that sits between the browser and - ContextForge. It holds the session cookie/CSRF boundary and keeps the - API's JWT off the browser (`server/src/index.ts`). The browser only ever - talks to the BFF, never directly to ContextForge. -- **Client** (`src/`) — this React SPA, served as static files by the BFF - (same-origin — the API client always calls relative paths, see - `src/api/client.ts`). +All three components must be running for local dev. Beyond the roles above: +the BFF keeps the API's JWT off the browser (`server/src/index.ts`), and the +client is served same-origin by the BFF, so its requests are always relative +paths (`src/api/client.ts`). Bring them up in this order: -1. **Start ContextForge** — the upstream `mcp-context-forge` repo. Follow - its own quick-start guide: +1. **Start the ContextForge API** (terminal A). It is a separate service in + its own clone, not part of this repository. Follow its own quick-start + guide for first-time setup: https://github.com/IBM/mcp-context-forge/issues/2503 - Note whatever port it ends up listening on for the next step. + + ```bash + cd /path/to/mcp-context-forge + make dev # listens on :8000, matching .env.example's default below + ``` + + (`make serve` runs it in production mode on `:4444` instead.) Note + whichever port yours ends up on; step 2 needs it. 2. **Configure and start the BFF** (terminal B, from the repo root): @@ -55,9 +70,10 @@ Bring them up in this order: ``` Edit `.env`: - - `CONTEXTFORGE_URL` — point it at whatever host:port ContextForge is - listening on from step 1 (`.env.example`'s default is `0.0.0.0:8000`; - confirm against your ContextForge run rather than assuming). + - `CONTEXTFORGE_URL` — point it at whatever host:port the API is + listening on from step 1 (`.env.example`'s default is `0.0.0.0:8000`, + which matches `make dev`; confirm against your actual run rather than + assuming). - `COOKIE_SECURE=false` — needed for local HTTP; the default (`true`) is for prod and silently drops the session cookie over plain HTTP. @@ -87,22 +103,22 @@ Bring them up in this order: 4. **Use it.** Visit `http://localhost:3000/` — redirects to `/app/login` (unauthed) or `/app/` (authed). The login form posts through the BFF, - which holds the ContextForge JWT server-side and hands the browser only + which holds the API's JWT server-side and hands the browser only an opaque session cookie. Default seeded admin: `admin@example.com` / `changeme` (first login forces a password change unless `PASSWORD_CHANGE_ENFORCEMENT_ENABLED=false` - is set in ContextForge's `.env`). + is set in the API's `.env`). > `npm run dev` (plain Vite dev server at `:5173`, no BFF in front) still > works for UI-only iteration, but `/api/*` calls need the BFF — it won't -> reach ContextForge on its own. +> reach the ContextForge API on its own. #### Troubleshooting - **`EADDRINUSE` on `:3000`** — stale `tsx watch` process: `lsof -ti:3000 | xargs kill`, then restart `npm run dev` in `server/`. -- **401 mid-session** — expected; the ContextForge token hard-expires per +- **401 mid-session** — expected; the API token hard-expires per `TOKEN_EXPIRY` (default 20 min). The BFF auto-revokes the session and redirects to login. @@ -270,7 +286,7 @@ Test-specific TypeScript configuration: End-to-end tests live in [`e2e/`](./e2e/) and are written in TypeScript with Playwright. They run against the Vite dev server and stub backend API calls -with `page.route()`, so no Python gateway is required. +with `page.route()`, so no running ContextForge API is required. ```bash npm run e2e:install # Install Playwright browsers (one-time) @@ -286,8 +302,8 @@ See [`e2e/README.md`](./e2e/README.md) for layout, fixtures, and guidelines. ### GitHub Actions -Tests and linting run automatically on pull requests via [`.github/workflows/client-lint-test.yml`](../.github/workflows/client-lint-test.yml). -E2E tests run via [`.github/workflows/client-e2e.yml`](../.github/workflows/client-e2e.yml). +Tests and linting run automatically on pull requests via [`.github/workflows/client-lint-test.yml`](./.github/workflows/client-lint-test.yml). +E2E tests run via [`.github/workflows/client-e2e.yml`](./.github/workflows/client-e2e.yml). **Workflow Steps:** @@ -304,7 +320,7 @@ E2E tests run via [`.github/workflows/client-e2e.yml`](../.github/workflows/clie ## Project Structure ``` -client/ +contextforge-web-ui/ ├── src/ │ ├── api/ # API client and types │ ├── auth/ # Authentication context and hooks @@ -331,12 +347,12 @@ client/ ├── .env.example # Shared BFF config — copy to .env (see Getting Started) ├── .env.prod.example # Production-ready template — copy to .env ├── Dockerfile / docker-compose.yml / DOCKER.md # see DOCKER.md -└── server/ # BFF (Fastify): session/CSRF boundary in front of ContextForge +└── server/ # BFF (Fastify): session/CSRF boundary in front of the API ├── src/ │ ├── index.ts # Entrypoint │ ├── config.ts # Env-driven config │ ├── plugins/ # cookie, redis, session, csrf, static - │ └── routes/ # auth/, proxy/ (catch-all to ContextForge), sse/ + │ └── routes/ # auth/, proxy/ (catch-all to the API), sse/ ├── public/ # Built SPA (npm run build output), served by BFF └── package.json ``` From 7808264652cf38fc00046ac799c7c7d1a2fceaa7 Mon Sep 17 00:00:00 2001 From: Anna Effort Date: Tue, 18 Aug 2026 18:09:41 -0700 Subject: [PATCH 2/3] docs: normalize em dashes and definition-list bullets Use colons or semicolons for label separators and clause joins instead of em dashes and hyphens, consistently across the definition lists. Signed-off-by: Anna Effort --- README.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index f188964..90e39a2 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,11 @@ This UI targets **ContextForge API v1.0.7**, matching [`openapi.json`](./openapi ## Tech Stack - **React 18** with TypeScript -- **Vite** - Build tool and dev server -- **React Router** - Client-side routing -- **React Intl** - Internationalization (i18n) -- **Tailwind CSS** - Utility-first styling -- **shadcn/ui** - Component library +- **Vite**: build tool and dev server +- **React Router**: client-side routing +- **React Intl**: internationalization (i18n) +- **Tailwind CSS**: utility-first styling +- **shadcn/ui**: component library ## Getting Started @@ -70,17 +70,17 @@ Bring them up in this order: ``` Edit `.env`: - - `CONTEXTFORGE_URL` — point it at whatever host:port the API is + - `CONTEXTFORGE_URL`: point it at whatever host:port the API is listening on from step 1 (`.env.example`'s default is `0.0.0.0:8000`, which matches `make dev`; confirm against your actual run rather than assuming). - - `COOKIE_SECURE=false` — needed for local HTTP; the default (`true`) is + - `COOKIE_SECURE=false`: needed for local HTTP; the default (`true`) is for prod and silently drops the session cookie over plain HTTP. Other values (`PORT`, `REDIS_URL`, `SESSION_TTL_SECONDS`, etc.) have - dev-safe defaults — see comments in `.env.example`. `REDIS_URL` is left + dev-safe defaults; see comments in `.env.example`. `REDIS_URL` is left unset, which falls back to an in-process store (no Redis process needed - for local dev — state resets on restart). + for local dev; state resets on restart). ```bash cd server @@ -96,12 +96,12 @@ Bring them up in this order: ``` This builds the SPA into `server/public/`, which the already-running BFF - serves directly. Re-run `npm run build` after any frontend change — + serves directly. Re-run `npm run build` after any frontend change; there's no HMR dev server wired to the BFF, so this build step is the loop for local iteration against the real backend. (`npm run build:watch` reruns it automatically on file changes.) -4. **Use it.** Visit `http://localhost:3000/` — redirects to `/app/login` +4. **Use it.** Visit `http://localhost:3000/`: redirects to `/app/login` (unauthed) or `/app/` (authed). The login form posts through the BFF, which holds the API's JWT server-side and hands the browser only an opaque session cookie. @@ -116,9 +116,9 @@ Bring them up in this order: #### Troubleshooting -- **`EADDRINUSE` on `:3000`** — stale `tsx watch` process: +- **`EADDRINUSE` on `:3000`**: stale `tsx watch` process: `lsof -ti:3000 | xargs kill`, then restart `npm run dev` in `server/`. -- **401 mid-session** — expected; the API token hard-expires per +- **401 mid-session**: expected; the API token hard-expires per `TOKEN_EXPIRY` (default 20 min). The BFF auto-revokes the session and redirects to login. @@ -138,7 +138,7 @@ npm run preview ## API Types -TypeScript types and fetch clients under `src/generated/` come from [`openapi.json`](./openapi.json) via [Orval](./orval.config.ts). That file is committed and pinned to API v1.0.7 — not re-fetched at build time. +TypeScript types and fetch clients under `src/generated/` come from [`openapi.json`](./openapi.json) via [Orval](./orval.config.ts). That file is committed and pinned to API v1.0.7, not re-fetched at build time. ```bash npm run generate # regenerate src/generated/ from ./openapi.json @@ -187,9 +187,9 @@ npm run format:check ### Test Framework -- **Vitest** - Fast unit test runner with jsdom environment -- **React Testing Library** - Component testing utilities -- **MSW (Mock Service Worker)** - API mocking +- **Vitest**: Fast unit test runner with jsdom environment +- **React Testing Library**: Component testing utilities +- **MSW (Mock Service Worker)**: API mocking ### Running Tests @@ -278,9 +278,9 @@ export const handlers = [ Test-specific TypeScript configuration: -- **[`tsconfig.app.json`](./tsconfig.app.json)** - Includes `vitest/globals` and `@testing-library/jest-dom` types -- **[`src/vitest.d.ts`](./src/vitest.d.ts)** - Global type declarations for test utilities -- **[`vitest.config.ts`](./vitest.config.ts)** - Vitest configuration with jsdom environment +- **[`tsconfig.app.json`](./tsconfig.app.json)**: includes `vitest/globals` and `@testing-library/jest-dom` types +- **[`src/vitest.d.ts`](./src/vitest.d.ts)**: global type declarations for test utilities +- **[`vitest.config.ts`](./vitest.config.ts)**: Vitest configuration with jsdom environment ## End-to-End Testing @@ -383,7 +383,7 @@ contextforge-web-ui/ The app supports multiple languages via React Intl: -- **English (en-US)** - Default +- **English (en-US)** (default) - **Spanish (es-ES)** - **Portuguese (pt-BR)** From 362fc64ccbf2c9c54ba45398db4815bc52077719 Mon Sep 17 00:00:00 2001 From: Gabriel Costa Date: Wed, 19 Aug 2026 14:54:21 +0100 Subject: [PATCH 3/3] Update README.md Co-authored-by: Marek Dano Signed-off-by: Gabriel Costa --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 90e39a2..c04ce1c 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ The web interface for ContextForge, the open source AI gateway that federates tools, agents, and APIs into one endpoint. The backing service is a separate process in a separate repository -([IBM/mcp-context-forge](https://github.com/IBM/mcp-context-forge)). This -repository holds the two pieces that sit in front of it: +([IBM/mcp-context-forge](https://github.com/IBM/mcp-context-forge)). +This repository holds the BFF and client that sit in front of it. The table below also documents the API for naming reference, though it lives in a separate repo: | Component | Lives in | Role | | --------- | -------- | ---- |