Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.mcpjungle.com/llms.txt

Use this file to discover all available pages before exploring further.

MCPJungle dashboard frontend development

1. What the dashboard is

The Dashboard UI is served by the MCPJungle Go server at / in development mode.

2. Frontend architecture

Frontend source

  • Frontend app root: web/dashboard
  • Main React entry: web/dashboard/src/main.tsx
  • Main app component: web/dashboard/src/App.tsx
  • Shared components: web/dashboard/src/components
  • API helpers: web/dashboard/src/lib/api.ts
  • Frontend types: web/dashboard/src/lib/types.ts
  • Global styles: web/dashboard/src/styles.css

Stack

  • Framework: React 18 + TypeScript
  • Build tool: Vite
  • React plugin: @vitejs/plugin-react
Frontend scripts live in web/dashboard/package.json:
{
  "scripts": {
    "dev": "vite",
    "build": "tsc --noEmit && vite build",
    "typecheck": "tsc --noEmit",
    "preview": "vite preview"
  }
}

Built assets

  • Vite build output: web/dashboard/dist
  • Go-served dashboard asset directory: internal/dashboardui/dist
  • Embed wiring: internal/dashboardui/embed.go
internal/dashboardui/embed.go embeds dist at Go compile time:
//go:embed dist dist/*
var embeddedFiles embed.FS
That means the final Go-served dashboard comes from the built files copied into internal/dashboardui/dist and then embedded into the Go binary.

How the Go server serves the frontend

Dashboard route registration is in internal/api/server.go. When DashboardService is available, the server registers:
  • GET /
  • GET /index.html
  • GET /assets/*filepath
These routes are backed by dashboardui.FileServer() from internal/dashboardui/embed.go.

Are assets embedded or served from disk?

For the real MCPJungle server, assets are embedded into the Go binary. The Go server does not read web/dashboard/src directly at runtime. During frontend-only development, you can also run the Vite dev server at http://localhost:5173/.

Backend dashboard handlers

Dashboard API handlers live in:
  • internal/api/dashboard.go
  • internal/api/dashboard_mutations.go
  • internal/api/dashboard_oauth.go
  • internal/api/dashboard_tool_groups.go
Most read-side dashboard response shaping lives in:
  • internal/service/dashboard/service.go
The frontend calls dashboard APIs with fetch("/api/dashboard/...") through the helpers in web/dashboard/src/lib/api.ts.

Routing model in the frontend

The dashboard is a single React app with section-state navigation. It does not use React Router today.
  • Sidebar items are defined in web/dashboard/src/components/NavSidebar.tsx
  • Active page state is controlled in web/dashboard/src/App.tsx
So for the Go server, the important UI entrypoint is /.

3. Local development workflow

Recommended workflow while changing frontend code:

Install frontend dependencies

cd web/dashboard
npm install
web/dashboard/package-lock.json exists, so npm is the expected package manager here.

Run the Go backend

From the repo root:
go run . start
This starts MCPJungle in development mode by default on http://localhost:8080. If you are using the installed CLI instead of go run, the equivalent command is:
mcpjungle start

Run the frontend dev server

In another terminal:
cd web/dashboard
npm run dev
This starts Vite on http://localhost:5173.

Which URL to open during active frontend development

Open:
http://localhost:5173/
for the fastest feedback loop. Why:
  • Vite hot reload works there
  • CSS/React changes update immediately
  • /api/* requests are proxied to the Go backend on http://localhost:8080
The proxy is defined in web/dashboard/vite.config.ts:
server: {
  port: 5173,
  proxy: {
    "/api": "http://localhost:8080",
  },
}

Do API calls go through CORS?

No special CORS setup is needed for normal frontend development because Vite proxies /api requests to the Go backend.

When to open http://localhost:8080/

Open http://localhost:8080/ when you want to verify the real Go-served embedded dashboard, not just the Vite dev server.

4. Build workflow

This is the most important workflow.

After I change frontend code, what do I run so it shows up at http://localhost:8080/?

From the repo root:
  1. Run the shared dashboard build script.
  2. Rebuild or rerun the Go server so it embeds the updated files.
  3. Refresh http://localhost:8080/.
Exact commands:
bash ./scripts/build-dashboard.sh
go run . start
If you already have a running Go server, stop it first and start it again after the copy step.

What the build script does

scripts/build-dashboard.sh is the canonical dashboard bundle refresh step for both local development and releases. It does:
  1. cd web/dashboard
  2. npm ci if node_modules/ is missing
  3. npm run build
  4. rm -rf internal/dashboardui/dist
  5. cp -R web/dashboard/dist internal/dashboardui/dist

Why is the restart required?

Because dashboard assets are embedded into the Go binary at build/run time. Updating web/dashboard/dist or internal/dashboardui/dist alone does not update an already-running server process.

Do generated assets need to be committed?

No. dist/ is ignored in .gitignore, and internal/dashboardui/dist is also treated as a generated artifact.

What is the final served UI source?

The UI served from http://localhost:8080/ comes from:
  1. Vite build output in web/dashboard/dist
  2. copied into internal/dashboardui/dist
  3. embedded by internal/dashboardui/embed.go
  4. served by internal/api/server.go

What happens if the frontend build is stale?

Common symptom:
  • http://localhost:5173/ shows your new UI
  • http://localhost:8080/ still shows the old UI
That means one of these is stale:
  • you did not run bash ./scripts/build-dashboard.sh
  • the Go server is still running an old embedded build

5. Go server integration

Where the dashboard routes are registered

Go route registration is in internal/api/server.go. Dashboard UI routes:
  • GET /
  • GET /index.html
  • GET /assets/*filepath
Dashboard API routes are under:
  • /api/dashboard/...

How / serves the dashboard

The router uses dashboardui.FileServer() and wraps it with:
  • s.requireInitialized()
  • requireDashboardMode

How local/dev mode is checked

Dashboard mode gating is in internal/api/middleware.go. requireDashboardMode() checks the current server mode from request context and only allows the dashboard when:
  • mode == model.ModeDev
Otherwise it returns:
  • 404 Not Found
This is intentional so the dashboard is local/dev only.

What happens in enterprise mode

If MCPJungle is started with:
mcpjungle start --enterprise
or SERVER_MODE=enterprise, dashboard UI routes and dashboard API routes return 404.

How static assets are wired

  • Embedded source: internal/dashboardui/embed.go
  • File server registration: internal/api/server.go

Frontend fallback routing

There is no multi-route SPA fallback setup today because the dashboard currently uses a single entrypoint and in-app section switching instead of browser URL routing.

6. Dashboard backend APIs

Where routes are defined

Dashboard routes are registered in internal/api/server.go. Handlers live in:
  • internal/api/dashboard.go
  • internal/api/dashboard_mutations.go
  • internal/api/dashboard_oauth.go
  • internal/api/dashboard_tool_groups.go

Current route groups

Read endpoints

  • GET /api/dashboard/overview
  • GET /api/dashboard/servers
  • GET /api/dashboard/tools
  • GET /api/dashboard/tool-groups
  • GET /api/dashboard/tool-groups/:name
  • GET /api/dashboard/prompts
  • GET /api/dashboard/resources
  • GET /api/dashboard/diagnostics

Mutation endpoints

  • POST /api/dashboard/servers
  • DELETE /api/dashboard/servers/:name
  • PATCH /api/dashboard/servers/:name/enabled
  • PATCH /api/dashboard/tools/:name/enabled
  • PATCH /api/dashboard/prompts/:name/enabled
  • POST /api/dashboard/tool-groups
  • DELETE /api/dashboard/tool-groups/:name

OAuth flow endpoints

  • GET /api/dashboard/oauth/callback
  • GET /api/dashboard/oauth/session/:id

Common patterns

  • Read handlers usually delegate to dashboard.Service
  • Mutation handlers usually reuse mcp.MCPService or toolgroup.ToolGroupService
  • Frontend uses relative fetch("/api/dashboard/...")

How to add a new dashboard API endpoint

Typical steps:
  1. Add the route in internal/api/server.go.
  2. Add the handler in the appropriate dashboard API file under internal/api/.
  3. Reuse existing service logic where possible instead of reimplementing CLI behavior.
  4. Add/extend response types in pkg/types or local dashboard response structs if the API is dashboard-specific.
  5. Add frontend types in web/dashboard/src/lib/types.ts.
  6. Add a frontend helper in web/dashboard/src/lib/api.ts.
  7. Update the UI in web/dashboard/src/App.tsx.
  8. Add tests in internal/api or internal/e2e.

Error handling

Handlers generally:
  • return 400 for bad request / binding errors
  • call handleServiceError(...) for service-layer errors
See:
  • internal/api/errors.go
Frontend API helpers in web/dashboard/src/lib/api.ts expect JSON errors in the form:
{ "error": "message" }

Secret handling

Dashboard APIs must not expose:
  • tokens
  • OAuth client secrets
  • env var values
  • authorization headers
  • raw sensitive config
Current dashboard UI/API only exposes sanitized summaries such as header keys or env keys, not values. Be careful not to reuse admin APIs like /api/v0/server_configs for the dashboard without sanitizing them first.

7. OAuth registration flow

Dashboard registration supports MCP servers that require upstream OAuth.

Why dashboard OAuth differs from CLI OAuth

The CLI can create its own localhost callback server. See cmd/register.go. The browser frontend cannot do that, so the dashboard uses the MCPJungle Go server as the OAuth callback receiver.

Current dashboard OAuth flow

  1. User submits + Add Server in the dashboard.
  2. Backend tries normal registration in internal/api/dashboard_mutations.go.
  3. If upstream OAuth is required, backend retries registration with:
<base-url>/api/dashboard/oauth/callback
  1. If the upstream server returns AuthorizationRequired, the backend returns 202 Accepted with:
    • authorization_required.session_id
    • authorization_required.authorization_url
    • authorization_required.expires_at
  2. Frontend opens the authorization URL in a new tab.
  3. Frontend polls:
/api/dashboard/oauth/session/:id
  1. Upstream OAuth redirects back to:
/api/dashboard/oauth/callback
  1. Backend completes the upstream OAuth session.
  2. Frontend sees completion via polling, closes the modal flow, and refreshes dashboard data.

Important implementation files

  • CLI OAuth flow reference: cmd/register.go
  • Dashboard registration entrypoint: internal/api/dashboard_mutations.go
  • Dashboard callback + polling: internal/api/dashboard_oauth.go
  • Upstream OAuth service logic: internal/service/mcp/upstream_oauth.go
  • Frontend registration modal: web/dashboard/src/App.tsx
  • Frontend API helpers: web/dashboard/src/lib/api.ts

Important constraints

  • Do not expose OAuth codes, tokens, or secrets in the UI.
  • The callback HTML page is intentionally simple.
  • Pending dashboard OAuth completion state is tracked server-side and cleaned up after expiry/retention.

8. Common frontend tasks

Add a new sidebar page

  • Add the section key/type in web/dashboard/src/lib/types.ts
  • Add the sidebar item in web/dashboard/src/components/NavSidebar.tsx
  • Add the section title/meta and render branch in web/dashboard/src/App.tsx
  • If the page needs backend data, add an API helper in web/dashboard/src/lib/api.ts

Add a new dashboard API call

  • Backend route: internal/api/server.go
  • Backend handler: internal/api/dashboard*.go
  • Frontend helper: web/dashboard/src/lib/api.ts
  • Frontend response type: web/dashboard/src/lib/types.ts

Add a new table column

  • UI rendering usually lives directly in web/dashboard/src/App.tsx
  • Styling usually goes in web/dashboard/src/styles.css
  • If the data is not already in the response, add it to the dashboard API response first

Add a new button or action

  • UI button: web/dashboard/src/App.tsx
  • API call: web/dashboard/src/lib/api.ts
  • Backend mutation handler: internal/api/dashboard_mutations.go or internal/api/dashboard_tool_groups.go

Add a new expanded row/details panel

Patterns already exist on:
  • Tools
  • Tool Groups
  • Prompts
  • Servers
Look in web/dashboard/src/App.tsx for the existing row expansion state and markup.

Add a copy button

Reuse:
  • web/dashboard/src/components/CopyButton.tsx
Keep the copy button close to the value it copies and add:
  • aria-label
  • title

Add a filter/search field

Current filtering patterns live in web/dashboard/src/App.tsx using local React state plus derived filtered arrays.

Add a new form/modal

Current examples:
  • server registration modal
  • tool group creation modal
Both live in web/dashboard/src/App.tsx. Typical steps:
  1. add local form state
  2. add validation
  3. call dashboard API helper
  4. refresh dashboard data after success
  5. show inline mutation error/success feedback

9. Styling and design notes

Current v1 visual direction:
  • GitHub-like developer dashboard
  • white / very light gray backgrounds
  • subtle gray borders
  • black / dark gray text
  • green for primary actions and enabled/healthy states
  • compact rows
Use monospace for:
  • endpoints
  • canonical names
  • transports
  • URIs
  • command strings
UI guidance:
  • Avoid raw JSON as the default UI
  • Prefer expandable rows for detail inspection
  • Keep copy buttons close to the values they copy
  • Keep detail panels compact and scan-friendly
Tools and Prompts specifically:
  • show human-readable schema/argument fields by default
  • raw JSON, if present, should be fallback/debug-only
  • raw JSON fallback should be collapsed by default

10. Testing and validation

Frontend checks

From web/dashboard:
npm run typecheck
npm run build
There is currently no dedicated frontend lint script in web/dashboard/package.json.

Backend/dashboard test commands

From the repo root:
bash ./scripts/build-dashboard.sh
go test ./internal/e2e ./internal/api
go test ./internal/service/mcp
go test ./...
Useful test files include:
  • internal/e2e/dashboard_test.go
  • internal/e2e/tool_groups_test.go

Manual smoke-test checklist

  1. Start MCPJungle in dev mode and open http://localhost:8080/.
  2. Confirm the default page is Servers.
  3. Add a server.
  4. If possible, test an OAuth-required registration.
  5. Enable/disable a server.
  6. Delete a server.
  7. Enable/disable a tool.
  8. Enable/disable a prompt.
  9. Create and delete a tool group.
  10. Copy:
    • endpoint
    • canonical tool name
    • canonical prompt name
    • resource URI
    • tool group endpoints
  11. Expand rows on:
    • Tools
    • Prompts
    • Tool Groups
  12. Open System Info.
  13. Confirm the dashboard does not expose secrets.

11. Troubleshooting

Frontend changes do not show up at localhost:8080

Likely causes:
  • you changed code while looking at the Vite dev server and forgot the Go-served UI uses embedded built assets
  • you forgot npm run build
  • you forgot to copy web/dashboard/dist into internal/dashboardui/dist
  • the Go server is still running an older embedded build
  • browser cache
  • you are checking http://localhost:8080/ instead of http://localhost:5173/ during active frontend work
Use this sequence:
bash ./scripts/build-dashboard.sh
go run . start

Frontend dev server works but Go-served UI does not

That usually means the Vite app is correct but the embedded Go-served bundle is stale. Remember:
  • http://localhost:5173/ uses live source + Vite
  • http://localhost:8080/ uses built/copied/embedded assets

Dashboard returns 404

Check:
  • MCPJungle is running in dev mode, not enterprise mode
  • you started the real server
  • the server is initialized
The dashboard intentionally returns 404 outside development mode. See internal/api/middleware.go.

API calls fail

Check:
  • the Go backend is running on localhost:8080
  • you are calling the right /api/dashboard/... path
  • if using npm run dev, the Vite proxy is active
If 5173 works but API calls fail, inspect:
  • web/dashboard/vite.config.ts

OAuth registration gets stuck

Check:
  • the backend returned an authorization URL
  • the auth tab actually opened
  • the upstream provider redirected back to /api/dashboard/oauth/callback
  • polling to /api/dashboard/oauth/session/:id is succeeding
  • the session did not expire
If needed, inspect server logs and these files:
  • internal/api/dashboard_oauth.go
  • internal/api/dashboard_mutations.go
  • internal/service/mcp/upstream_oauth.go

Copy buttons do nothing

Copy buttons use the browser clipboard API. If copy fails:
  • check the browser console
  • make sure the page is running in a normal browser context
  • retry from the real UI page rather than an unusual embedded browser/webview

12. Release and build notes

Before cutting a release or shipping a frontend change, maintainers should:
  1. Run frontend checks:
cd web/dashboard
npm run typecheck
  1. Refresh the embedded dashboard bundle:
bash ./scripts/build-dashboard.sh
  1. Run backend/dashboard tests:
go test ./internal/e2e ./internal/api
go test ./internal/service/mcp
go test ./...
  1. Rebuild and run MCPJungle.
  2. Verify the dashboard from the Go server at http://localhost:8080/, not only from http://localhost:5173/.
GoReleaser also runs bash ./scripts/build-dashboard.sh in its before.hooks, so release artifacts automatically include a fresh embedded dashboard bundle.

13. Keep this doc maintainable

When updating this document:
  • use real repo paths
  • use real commands from this repo
  • update page names if UI nav changes
  • update API route lists if dashboard endpoints change
  • update the build section whenever the embed/copy workflow changes
  • prefer short, practical repo-specific guidance over frontend theory