Skip to content
Open
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
84 changes: 44 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -29,41 +29,45 @@ 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.
Comment on lines +48 to +49

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add something like

 cd /path/to/mcp-context-forge                                                                                                                                                                                                   
 make dev  # Listens on :8000 by default


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
cp .env.example .env
```

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.
Comment on lines +59 to +63

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be good if we had a table here?

Command Port Use Case
make dev :8000 Local development (recommended)
make serve :4444 Production mode

- `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
Expand All @@ -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.

Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)**

Expand Down
Loading