# Contributing First off, thank you for considering contributing to MCPJam Inspector! It's people like you that make the open source community such a great place. ## Finding an issue to work on 1. You can find things to work on in our [issues tab](https://github.com/MCPJam/inspector/issues). 2. Look for issues labelled `good first issue` and `very easy`. These are great starter tasks that are low commitment. 3. Once you find an issue you like to work on, comment on the issue and tag @chelojimenez. Then assign yourself the issue. This helps avoid multiple contributors working on the same issue. ## Getting Started Before you get started, please consider giving the project a star. It helps grow the project and gives your contributions more recognition. Also join our [Discord channel](https://discord.com/invite/JEnDtz8X6z). That's where the community and other open source contributors communicate. ### Prerequisites - [Node.js](https://nodejs.org/) — use the version in [`mcpjam-inspector/.nvmrc`](./mcpjam-inspector/.nvmrc) (`nvm use` picks it up). v22 is the floor for running the app; **Node 25 and newer cannot package the desktop app** — `electron-forge make` exits 0 and writes no `out/`, so the failure looks like success. Watch out for Homebrew here: its `node@24` and `node@25` formulae have both symlinked to Node 26, so check `node -v` rather than trusting the formula name. - [npm](https://www.npmjs.com/) (comes with Node.js) ### Fork, Clone, and Branch 1. **Fork** the repository on GitHub. 2. **Clone** your fork locally: ```bash git clone https://github.com/YOUR_USERNAME/inspector.git cd inspector ``` 3. Create a new **branch** for your changes: ```bash git checkout -b my-feature-branch ``` ### Project Structure This is an **npm workspaces monorepo**. The main packages are: | Workspace | Package | Description | | ------------------- | ----------------------- | ---------------------------------------- | | `mcpjam-inspector/` | `@mcpjam/inspector` | Inspector app (client, server, Electron) | | `sdk/` | `@mcpjam/sdk` | MCP SDK for testing and evals | | `cli/` | `@mcpjam/cli` | CLI tool | | `design-system/` | `@mcpjam/design-system` | Shared UI components | | `soundcheck/` | `@mcpjam/soundcheck` | Soundcheck app | | `mcp/` | `@mcpjam/mcp` | MCP worker | Most contributions target the `mcpjam-inspector/` workspace. ### Setup Install dependencies for all workspaces from the repo root: ```bash npm install ``` ## Development Copy the env file inside the inspector workspace: ```bash cp mcpjam-inspector/.env.local mcpjam-inspector/.env.development ``` Then start the inspector in dev mode: ```bash npm run dev -w @mcpjam/inspector ``` This runs: - **Client**: Vite dev server on `:5173` - **Server**: Hono dev server on `:6274` - **Platform MCP worker**: `mcp/` via `wrangler dev --env dev` on `:8787` Open `http://localhost:5173` in your browser. The client proxies API requests to the server. The platform MCP worker backs the Home/MCPJam agent's workspace tools (`list_projects`, `show_servers`, eval/scenario tools). It starts automatically with `npm run dev`, and the agent connects to it on `:8787` — no env var to set. If you only need the UI/server and want to skip the worker (and its one-time UI build), use `npm run dev:app` instead. ### Dev Convex configuration (for the Home agent's platform tools) The platform worker forwards your dev AuthKit token through `/api/v1` to the dev **Convex** deployment (the one your `.env.development` `CONVEX_HTTP_URL` points at). That deployment must trust the dev WorkOS app, or `list_projects` returns a 401. Set these once on the dev Convex deployment (in the `mcpjam-backend` repo / Convex dashboard — this is backend/infra config, not in this repo): ```bash npx convex env set WORKOS_CLIENT_ID client_01KTN2EWHHJCKRB8RSR307X4SG npx convex env set AUTHKIT_DOMAIN deep-vanilla-68-test.authkit.app npx convex env list # verify WORKOS_CLIENT_ID / AUTHKIT_DOMAIN are the dev values ``` Do not point `GUEST_JWKS_URL` anywhere: a development deployment verifies guest tokens with its own `/guest/jwks` by default. (The shared OSS deployment's pairing with the hosted guest authority is managed by operators.) ### Guest sessions The Inspector never writes configuration to a backend while it runs. Guests come from ONE guest authority per instance: - **Standard profile** (the committed `.env.local`, and packaged releases): the hosted Inspector at `app.mcpjam.com`. Nothing to set up. - **Your own development deployment**: initialize it once, explicitly: ```bash npm run dev:setup-guest-auth -w @mcpjam/inspector -- --deployment dev: \ --env-file mcpjam-inspector/.env.development.local ``` This accepts only a fully qualified `dev:` selector, stops unless Convex itself reports that name as a development deployment, runs the Convex CLI from an isolated directory with your shell's `CONVEX_*` settings removed, initializes only missing values (it never overwrites, never writes `GUEST_JWKS_URL`), and adds the deployment's addresses and guest credentials to the profile you name. ### Running several instances (worktrees) ```bash npm run dev:worktree -w @mcpjam/inspector -- [local|staging|preview ] [--env-file ] ``` Instance `N` gets client `5173+N`, server `6274+N`, platform worker `8787+N` and worker debugger `9229+N` (`--client-port`, `--server-port`, `--worker-port`, `--debugger-port` override them; `--no-worker` skips the worker). The launcher resolves ONE profile for the instance and passes it to the server, Vite and the worker, none of which then reads `.env` files on its own: - `--env-file ` is the whole profile and wins over everything, except with `preview`: there the two URLs name the backend and the file supplies its other settings (for example its guest credentials). A relative path is resolved from the directory you ran npm in. - `local` (default): the committed `.env.local`, overlaid by this worktree's `.env.development.local` — or, only when you named no target, the main worktree's, read in place (secrets are never copied between worktrees). - `staging` / `preview` never inherit the local profile. Both default to the hosted guest authority at `staging.mcpjam.com`. Backend settings (Convex addresses, guest authority, service tokens, `COMPUTERS_*`, `DEPLOYMENT_SESSION_JWT_*`) and sign-in settings (WorkOS, CLI/Slack/Discord auth) are each taken as a group from one layer, so a profile that points at another backend never picks up the standard profile's credentials. Values for those settings exported in your shell are ignored (the launcher lists them). Callback origins (`CLI_AUTH_PUBLIC_ORIGIN` and the Slack/Discord link origins) follow the instance's port. Each instance keeps its own sign-in and guest cookies, so instances never sign each other in or out. After upgrading from an older Inspector, each instance asks you to sign in once; guest sessions carry over. ### Electron Development One command, from a fresh clone: ```bash npm run electron:dev -w @mcpjam/inspector ``` It runs next to `npm run dev`. The launcher (`scripts/electron-dev.mjs`) picks a free server port once (6274, or the next free one when another Inspector holds it) and hands it to both the renderer's `/api` proxy and the main process, so the window always talks to its own embedded server and keeps its own sign-in: the desktop app and the web app on the same machine never share a WorkOS or guest cookie. `--server-port N` pins the port (`npm run electron:dev -w @mcpjam/inspector -- --server-port 7000`). Its `pre` step builds the SDK if `../sdk/dist` is stale and regenerates the gitignored bundles (`PluginShim.bundled.ts` and friends) and telemetry catalogs (`*telemetry-catalog.generated.json` and friends), so there is nothing to run first. Then it starts: - the Electron main process, with the embedded Hono server on the chosen port - Vite's dev server for the renderer, which is what the window loads - file watchers for the main and preload bundles - the telemetry catalog watcher **Telemetry catalogs** are build outputs too. The names the strict telemetry builders may forward (log messages, error classes, routes, source files) are read from source by `scripts/generate-telemetry-catalogs.mjs`, which `npm install`, every SDK and Inspector build, test and typecheck, and the dev loops (`dev`, `dev:app`, `dev:server`, `dev:client`, SDK `dev`, `electron:dev`, `test:watch`) run for you. A new logger message, route or error class is catalogued on save; there is no file to commit. Error names that dependencies set are not in our source: add them to `DEPENDENCY_ERRORS` in that script, or their errors reach Sentry and Axiom as `UnknownError`. **Does my field arrive?** Telemetry forwards only fields that have a rule, so a new diagnostic field can be dropped without any error. Check it in the test that adds it: `expectTelemetryFields(sample, ["statusCode"])` from `mcpjam-inspector/shared/__tests__/explain-telemetry.ts` runs the sample through the real log-row or server-analytics builder and fails with each missing field's drop reason (for example, "no field rule: declare it in server/utils/log-events.ts"). `explainTelemetry(sample)` returns the same `{ forwarded, dropped }` for a look before you write the assertion. An org, project or server ID arrives only from the server's request context, never from a payload; see `mcpjam-inspector/server/utils/LOGGING.md`. **Reload semantics**, which differ by process: | You edit | What happens | | ----------------------------------- | ---------------------------------------------------- | | `client/` (renderer) | Vite HMR, no restart | | `src/` (main, preload) or `server/` | bundle rebuilds, then the app restarts automatically | A restart takes a couple of seconds and prints `[hot-restart] restart #N`. A build error prints the error and does **not** restart, so you keep the last working app until you fix it. You never need to type `rs`. Four environment variables, for when you need them: | Variable | Effect | | ----------------------------------- | ----------------------------------------------------------------------- | | `MCPJAM_ELECTRON_DEV_NO_RESTART=1` | never auto-restart (holding a breakpoint in main) | | `MCPJAM_ELECTRON_DEV_SOURCEMAP=1` | emit main-process sourcemaps (off in dev; ~47MB per rebuild) | | `MCPJAM_ELECTRON_DEV_BUNDLE_DEPS=1` | bundle dependencies as packaging does, instead of leaving them external | | `MCPJAM_ELECTRON_DEV_TIMING=1` | print per-build graph vs render timings | In dev, `node_modules` is right there, so dependencies are left external rather than bundled — that is what keeps a rebuild at a few seconds instead of a minute. Packaging still bundles everything, because the packaged app ships no `node_modules`. #### Packaging `npm run electron:package` and `npm run electron:make` check their preconditions first and refuse to start if something would fail silently — the Node version, a missing `dist/client`, or (on macOS) the DMG maker's native addons. Each failure prints the exact command that fixes it. If the addons are the problem: ```bash npm run electron:fix:dmg-deps -w @mcpjam/inspector ``` ### Building the Project To build everything (SDK, CLI, and Inspector): ```bash npm run build ``` To build individual workspaces: - `npm run build -w @mcpjam/sdk` - Build the SDK - `npm run build -w @mcpjam/cli` - Build the CLI - `npm run build -w @mcpjam/inspector` - Build the Inspector To start the production build locally: ```bash npm run start -w @mcpjam/inspector ``` ### Running Tests Run all tests and type checks: ```bash npm run verify ``` Or run tests for a specific workspace: ```bash npm run test -w @mcpjam/inspector npm run test -w @mcpjam/sdk npm run test -w @mcpjam/cli ``` #### WorkOS contract tests The suites named `server/**/*.emulator.test.ts` boot [`@workos/emulate`](https://github.com/workos/emulate) — WorkOS's own in-memory API server — in-process and drive the real code paths against it, rather than stubbing `fetch` and mocking token verification. That is what lets them assert things only WorkOS can settle: that a spent refresh token is refused, that a revoked API key stops validating immediately, that a forged JWT fails against the issuer's real JWKS. ```bash npm run test -w @mcpjam/inspector -- server/routes/web/__tests__/api-keys.emulator.test.ts ``` Each file starts its own emulator on port 0 in `beforeAll`, so nothing special is needed in CI and the six shards cannot collide. The helper (`server/test/support/workos-emulator.ts`) sets `WORKOS_API_BASE_URL` and the other env itself — do not export that variable from your shell, and see `mcpjam-inspector/HOSTED_DEPLOYMENT.md` for why it is loopback-only. Requires **Node >= 22.11** (the emulator's floor; CI runs 24.x). The helper says so explicitly rather than failing with a stack trace on an older runtime. The pre-existing mocked suites (`api-keys.test.ts`, `bearer-auth.test.ts`, `workos-authkit.test.ts`) are still the right place for logic that does not need a server — they are faster, and they are also the regression guard proving the default path still points at `api.workos.com`. ## Code Style We use [Prettier](https://prettier.io/) to maintain a consistent code style. Before you commit your changes, please format your code by running: ```bash npm run prettier-fix -w @mcpjam/inspector ``` ## Commit Messages We follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification. This helps us keep the commit history clean and readable. Your commit messages should be structured as follows: ``` [optional scope]: [optional body] [optional footer(s)] ``` **Example:** `feat(client): add new button to the main component` `fix(server): resolve issue with API endpoint` ## Getting Help - [Discord](https://discord.com/invite/JEnDtz8X6z) - [Docs](https://docs.mcpjam.com) Thank you for your contribution!