diff --git a/README.md b/README.md index 3175a7f..ea6f7a0 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ -# ContextForge UI Client +# ContextForge Web UI Client -React-based admin UI for ContextForge MCP Gateway. +React-based user interface for mcp-context-forge. -This UI targets **ContextForge API v1.0.7**, matching [`openapi.json`](./openapi.json) committed at repo root. +This UI targets **mcp-context-forge API v1.0.7**, matching [`openapi.json`](./openapi.json) committed at repo root. ## 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 @@ -29,24 +29,26 @@ npm install The app is split into three pieces that all must run for local dev: -- **ContextForge** (`mcpgateway`) — the upstream FastAPI gateway. It owns +- **mcp-context-forge**: 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 +- **BFF** (`server/`): a Fastify app that sits between the browser and + mcp-context-forge. 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 + talks to the BFF, never directly to mcp-context-forge. +- **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`). Bring them up in this order: -1. **Start ContextForge** — the upstream `mcp-context-forge` repo. Follow +1. **Start mcp-context-forge** (terminal A, a separate clone of the + `mcp-context-forge` repo: it is not part of contextforge-web-ui). Follow its own quick-start guide: https://github.com/IBM/mcp-context-forge/issues/2503 - Note whatever port it ends up listening on for the next step. + In that repo, `make dev` boots it on `:8000` by default. Note whatever + port it ends up listening on for the next step. -2. **Configure and start the BFF** (terminal B, this repo's `server/`): +2. **Configure and start the BFF** (terminal B, contextforge-web-ui's `server/`): ```bash cd server @@ -54,16 +56,18 @@ Bring them up in this order: ``` Edit `server/.env`: - - `FASTAPI_URL` — point it at whatever host:port ContextForge is - listening on from step 1 (`.env.example`'s default is `4444`; confirm - against your ContextForge run rather than assuming). - - `COOKIE_SECURE=false` — needed for local HTTP; the default (`true`) is + - `FASTAPI_URL`: point it at whatever host:port mcp-context-forge is + listening on from step 1. `.env.example`'s default (`4444`) is + mcp-context-forge's own **production** default (`make serve`); `make dev` + (the usual path for local dev) listens on `:8000` instead: confirm + against your actual run rather than assuming either. + - `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 `server/.env.example`. + dev-safe defaults; see comments in `server/.env.example`. `REDIS_URL=memory://` (the default) is an in-process store, no Redis - process needed for local dev — state resets on restart. + process needed for local dev; state resets on restart. ```bash npm install @@ -78,29 +82,29 @@ 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 ContextForge JWT server-side and hands the browser only + which holds the mcp-context-forge 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 mcp-context-forge'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 mcp-context-forge on its own. #### 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 ContextForge token hard-expires per +- **401 mid-session** — expected; the mcp-context-forge token hard-expires per `TOKEN_EXPIRY` (default 20 min). The BFF auto-revokes the session and redirects to login. @@ -120,7 +124,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 @@ -169,9 +173,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 @@ -260,9 +264,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 @@ -326,12 +330,12 @@ client/ ├── tsconfig.app.json # TypeScript app config ├── vite.config.ts # Vite configuration (builds to server/public/) ├── package.json # Dependencies and scripts -└── server/ # BFF (Fastify): session/CSRF boundary in front of ContextForge +└── server/ # BFF (Fastify): session/CSRF boundary in front of mcp-context-forge ├── 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 mcp-context-forge), sse/ ├── public/ # Built SPA (npm run build output), served by BFF ├── .env.example # Copy to .env and configure FASTAPI_URL etc. └── package.json @@ -363,7 +367,7 @@ client/ The app supports multiple languages via React Intl: -- **English (en-US)** - Default +- **English (en-US)** (default) - **Spanish (es-ES)** - **Portuguese (pt-BR)**