Skip to main content
Glama
README.md
# nexus-mcp

TypeScript **MCP server** for the [Plugin Federation](https://github.com/plugin-federation)
**Nexus** control plane. Coding agents and humans can inspect catalogs, judge
opinions, plugins, proposals, and run governance writes — without pasting
long-lived Nexus API tokens.

## MCP protocol

| Item | Value |
|------|--------|
| Spec revision | **`2026-07-28`** ([stateless modern core](https://modelcontextprotocol.io/specification/2026-07-28)) |
| TypeScript SDK | **`@modelcontextprotocol/server@^2`** (MCP SDK v2) |
| Schema validation | Zod v4 (`zod/v4`) |
| Transport | stdio (primary); protocol versions advertised include modern + 2025-era fallbacks |

Configured in [`src/mcp-protocol.ts`](./src/mcp-protocol.ts) via
`supportedProtocolVersions` (modern `2026-07-28` first).

## Security model

| Identity | How it authenticates | Nexus credential |
|---|---|---|
| **Human** (Cursor, Claude Desktop, local agent) | **GitHub SSO** — device flow (`GITHUB_OAUTH_CLIENT_ID`) or existing `gh auth login` session | Short-lived Nexus JWT via `provider: github_user` (memory only) |
| **GitHub Actions / coding agents in GitHub** | **GitHub OIDC** (`permissions: id-token: write`) + tenant OIDC trust policy | Short-lived Nexus JWT via `provider: github_actions` (memory only) |

**Not supported (by design):**

- `NEXUS_ACCESS_TOKEN` / `NEXUS_BEARER_TOKEN` (static control-plane secrets)
- Gateway enrollment / workload / bootstrap credential tools
- Recording tool catalogs or analyses from chat (CI + OIDC Actions own evidence)

Nexus remains the **only** issuer of API JWTs (ADR 0008).

## Tools (v0.1)

Orientation: `get_session`, `get_tenant`, `list_pipeline_checks`

Plugins: `list_plugins`, `create_plugin`, `get_plugin_draft`, `replace_plugin_draft`,
`compose_plugin_draft`, `list_plugin_variants`, `list_plugin_revisions`

Quality: `get_tool_catalog`, `list_tool_analyses`, `list_tool_judges`,
`get_tool_judge`, `create_tool_judge`, `update_tool_judge`,
`get_latest_schema_verification`, `summarize_source_quality`,
`find_fix_suggestions_for_tool`

Governance: `get_proposal`, `create_proposal`,
`create_proposal_from_schema_verification`, `decide_proposal` (confirm),
`release_plugin_revision` (confirm), `promote_plugin_revision` (confirm)

Exports: `get_compatibility_report`, `create_export`, `get_export_job`

Audit: `list_audit_events`

## Setup

```bash
npm install
npm run build
```

### Environment

| Variable | Required | Purpose |
|---|---|---|
| `NEXUS_URL` | yes | e.g. `https://api.nonprod.plugin-federation.com` |
| `TENANT_ID` | Actions OIDC | Tenant UUID matching OIDC trust policy; also preferred tenant for multi-membership humans |
| `NEXUS_OIDC_AUDIENCE` | Actions | Must match Nexus `NEXUS_OIDC_GITHUB_AUDIENCE` (defaults to `NEXUS_URL`) |
| `GITHUB_OAUTH_CLIENT_ID` | humans (device flow) | Public OAuth App client id for device flow |
| `NEXUS_MCP_ALLOW_GH_CLI` | no | Default `true` — use `gh auth token` when device flow client id unset |
| `NEXUS_MCP_GITHUB_USER_TOKEN` | no | Optional GitHub **user** OAuth token injected by a host (still exchanged; never a Nexus JWT) |

### Human (local)

```bash
# Option A: GitHub CLI already SSO'd
gh auth login
export NEXUS_URL=https://api.nonprod.plugin-federation.com
export TENANT_ID=<optional-preferred-tenant>
npx tsx src/index.ts

# Option B: device flow
export GITHUB_OAUTH_CLIENT_ID=<public-oauth-app-client-id>
npx tsx src/index.ts
# Follow the verification URL + user code on stderr
```

### GitHub Actions / coding agent

```yaml
permissions:
  id-token: write
  contents: read
steps:
  - uses: actions/checkout@v4
  - uses: actions/setup-node@v4
    with:
      node-version: "22"
  - run: npm ci && npm run build
    working-directory: path/to/nexus-mcp
  - name: Run agent with Nexus MCP
    env:
      NEXUS_URL: ${{ vars.NEXUS_URL }}
      TENANT_ID: ${{ vars.TENANT_ID }}
      NEXUS_OIDC_AUDIENCE: ${{ vars.NEXUS_OIDC_AUDIENCE }}
    run: node path/to/nexus-mcp/dist/index.js
```

Register `owner/repo` (immutable ids) on a Nexus **OIDC trust policy** with the
roles the agent needs (contributor / approver / etc.).

### MCP client config (stdio example)

```json
{
  "mcpServers": {
    "nexus": {
      "command": "node",
      "args": ["/absolute/path/to/nexus-mcp/dist/index.js"],
      "env": {
        "NEXUS_URL": "https://api.nonprod.plugin-federation.com",
        "TENANT_ID": "11111111-1111-4111-8111-111111111111",
        "GITHUB_OAUTH_CLIENT_ID": "Ov23li…"
      }
    }
  }
}
```

## Development

```bash
npm run typecheck
npm run dev
```

## Related

- [ADR 0008 Human GitHub SSO](https://github.com/plugin-federation/plugin-federation/blob/main/docs/architecture/decisions/0008-human-github-sso.md)
- [meteo-static](https://github.com/plugin-federation/meteo-static) / [units-static](https://github.com/plugin-federation/units-static) dogfood MCPs
- Console uses the same `github_user` exchange path