Files
compiler-explorer/package.json
Matt Godbolt 312846230a Add built-in MCP endpoint for LLM tool integration (#8644)
Expose Compiler Explorer's compile, list, shortlink and asm-docs APIs
via a Model Context Protocol (MCP) endpoint mounted at `/mcp`. This lets
MCP-aware clients (Claude, etc.) drive CE directly as a tool.

## Tools exposed at `/mcp`

- **`compile`** — compile source and return assembly / stdout / stderr,
with optional execution.
- `compiler` is **optional**; falls back to the language's
`defaultCompiler` from `list_languages` ("compile this hello world in
C++" is one call).
- `libraries[].version` accepts **either** the version id (`"188"`) or
the human form (`"1.88.0"`) — both work.
- When `execute: true` and the build fails,
`buildResult.stdout`/`stderr` carry the real compiler diagnostics with
**ANSI codes stripped** so an LLM caller sees clean text.
- Caps: `maxAsmLines` / `maxStdoutLines` / `maxStderrLines` with
truncation flags + total counts.
- **`list_compilers`** — with `language`, `instructionSet` (closed enum
from `InstructionSetsList`), `match` (case-insensitive AND-of-tokens;
numeric/dotted-version tokens treated as version-prefix), `lean: true`,
`maxResults`, `latestPerMajor: true`, and `includeExperimental: true`.
- Hard cap of 200 entries on lean responses with a refinement hint —
prevents the unfiltered call from overflowing.
- Each entry exposes `releaseTrack` (`stable | nightly | prerelease |
experimental`) and `supportsExecute` / `supportsBinary`.
- **`list_libraries`** — with `match`, `lean`, `maxResults` (same
lean-cap behaviour).
- **`list_languages`** — minimal listing including `defaultCompiler` and
`compilerCount` per language.
- **`generate_short_url`** — returns `{url}`. Library versions are
normalised before saving.
- **`get_shortlink_info`** — returns saved sessions in the **same shape
`compile` accepts** (`{compiler, options, libraries:[{id, version}]}`)
for direct round-tripping. Multi-pane shortlinks (executors,
conformance, CMake trees) are flattened to the basic compile inputs.
- **`lookup_asm_instruction`** — `instruction_set` is a closed enum
derived from the registered providers (no hand-listed enum values; one
source of truth in `lib/asm-docs/`).

## Implementation

- New `lib/mcp/` module wiring `@modelcontextprotocol/sdk` into the
existing Express router via `StreamableHTTPServerTransport` (stateless
mode — one server per request).
- `lib/mcp/utils.ts`: tokenised `match` with version-prefix matching for
numeric/dotted-version tokens (so `"gcc 14.1"` matches `"gcc 14.1"` and
`"gcc 14.1.0"` but NOT `"gcc 14.10"` or `"gcc 14.0.1"`); `applyCap` with
both per-call lean degradation and an absolute hard cap; `truncateLines`
strips ANSI escapes via the existing `filterEscapeSequences` helper from
`lib/utils.ts`.
- `lib/mcp/library-utils.ts`: `normaliseLibraryVersion` and
`normaliseRequestLibraries` — single source of truth for "accept id or
human version" semantics, used by both `compile` and
`generate_short_url`.
- Schema descriptions are tight (LLM context cost matters) and derive
closed-set enums programmatically from `InstructionSetsList`,
`RELEASE_TRACKS`, and a new `availableAsmDocsKeys` export — no
hand-listed values that can rot.
- Refactor `StorageBase` static helpers (`encodeBuffer`, `isCleanText`,
`getSafeHash`) to module-level functions with type-checked input so MCP
tools can build shortlink hashes without instantiating a storage
backend.
- Expose `ApiHandler.compileHandler` and split out
`getAvailableLanguages()` so MCP can reuse the same code paths the REST
API uses; new `ApiHandler.getDefaultCompilerFor()` for the
compile-default-compiler resolution.
- Browser-friendly CORS on `/mcp`: OPTIONS preflight advertises
`Access-Control-Allow-Methods: POST, OPTIONS` (the shared `cors`
middleware doesn't set Methods); 405 responses on other verbs use the
same Allow header.
- `docs/API.md`: clarify that `/api/shortener` requires a JSON object
body (the prior docs implied but didn't state it).

## Tester feedback addressed

A Claude tester drove the staging deployment through several rounds;
full thread in PR comments. Round-by-round refinements:

- Compile diagnostics surfaced on execute-mode build failures (the
original "silent `Build failed` with empty stderr" bug).
- `execute: true` schema description rewritten to reflect the actual
behaviour.
- Library `version` accepts both forms; clean errors when neither
matches with a sample of available versions.
- `latestPerMajor` rebuilt on top of the `releaseTrack` field added in
#8685, with `includeExperimental` opt-in for c++ proposal forks.
- Lean mode (`lean: true`) for catalog browsing, plus a hard 200-item
cap so even unfiltered calls don't overflow the host.
- Tokenised `match` with version-prefix semantics for
numeric/dotted-version tokens. **Behaviour change** vs the old
`/api/compilers?fields=...` text matching: bare numeric tokens are now
treated as version segments — `"2024"` no longer substring-matches
inside `"v2024beta"`, and `"14.1"` no longer wrongly matches `"14.0.1"`.
Strict improvements but worth a release-note line for callers depending
on the prior loose behaviour.
- ANSI escape code stripping from compile output.
- `instructionSet` as a structured filter (instead of relying on `match`
strings).
- `supportsExecute` / `supportsBinary` on `list_compilers` so an agent
knows whether `execute: true` will work without trying.
- `compilerCount` per language so an agent can tell well-stocked vs
niche languages at a glance.
- Compiler-not-found / library-not-found errors point at the right
`list_*` tool.

## Depends on

#8685 (releaseTrack metadata on `CompilerInfo`) — merged.

## Test plan

- [x] `npm run test -- --run mcp release-track` — all pass (78 + 22)
- [x] `npm run test-min` — full minus expensive, all green
- [x] `make pre-commit` — exits 0
- [x] Multi-round driving on staging via the live MCP endpoint,
including: default-compiler hello-world (no compiler arg), human-form
library version (`"1.88.0"`), broken-compile-with-execute (verifies
buildResult), compile+run with stdin, compile+library Boost 1.90,
parallel `-O0` vs `-O3` diff, `list_compilers latestPerMajor` for
c++/rust/go/csharp, `list_libraries match boost/fmt/json`,
`lookup_asm_instruction MOV amd64`, full `generate_short_url` →
`get_shortlink_info` → re-`compile` round-trip with library
normalisation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Matt Godbolt <mattgodbolt@hudson-trading.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
2026-05-08 10:50:09 -04:00

175 lines
5.9 KiB
JSON

{
"name": "compiler-explorer",
"description": "Interactively investigate compiler output",
"type": "module",
"private": true,
"author": {
"name": "Matt Godbolt",
"email": "matt@godbolt.org",
"url": "https://xania.org"
},
"repository": {
"type": "git",
"url": "compiler-explorer/compiler-explorer"
},
"version": "0.0.3",
"engines": {
"node": ">=20.0.0"
},
"main": "./app.ts",
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.1019.0",
"@aws-sdk/client-ec2": "^3.1019.0",
"@aws-sdk/client-s3": "^3.1019.0",
"@aws-sdk/client-sqs": "^3.1019.0",
"@aws-sdk/client-ssm": "^3.1019.0",
"@aws-sdk/credential-providers": "^3.1019.0",
"@flatten-js/interval-tree": "^2.0.3",
"@modelcontextprotocol/sdk": "^1.29.0",
"@fortawesome/fontawesome-free": "^7.2.0",
"@orchidjs/sifter": "^1.1.0",
"@popperjs/core": "^2.11.8",
"@sentry/browser": "^10.46.0",
"@sentry/node": "^10.46.0",
"@types/semver": "^7.7.1",
"big-integer": "^1.6.52",
"bootstrap": "^5.3.8",
"buffer": "^6.0.3",
"chart.js": "^4.5.1",
"clipboard": "^2.0.11",
"commander": "^14.0.3",
"compression": "^1.8.1",
"copy-webpack-plugin": "^14.0.0",
"cross-env": "^10.1.0",
"enhanced-ms": "^4.3.0",
"events": "^3.3.0",
"express": "^5.2.1",
"file-saver": "^2.0.5",
"golden-layout": "^1.5.9",
"http-proxy": "^1.18.1",
"jquery": "^4.0.0",
"js-cookie": "^3.0.5",
"jszip": "^3.10.1",
"lodash.clonedeep": "^4.5.0",
"lru-cache": "^11.2.7",
"lz-string": "^1.5.0",
"marked": "^17.0.5",
"monaco-editor": "^0.55.1",
"monaco-vim": "^0.4.4",
"morgan": "^1.10.1",
"node-targz": "^0.2.0",
"p-queue": "^9.1.0",
"path-browserify": "^1.0.1",
"profanities": "^3.0.1",
"prom-client": "^15.1.3",
"pug": "^3.0.4",
"response-time": "^2.3.4",
"sanitize-filename": "^1.6.4",
"scroll-into-view-if-needed": "^3.1.0",
"semver": "^7.7.4",
"systemd-socket": "0.0.0",
"tar-stream": "^3.1.8",
"tom-select": "^2.5.2",
"tree-kill": "^1.2.2",
"triple-beam": "^1.4.1",
"tsx": "^4.21.0",
"underscore": "^1.13.8",
"url-join": "^5.0.0",
"whatwg-fetch": "^3.6.20",
"which": "^6.0.1",
"winston": "^3.19.0",
"winston-loki": "^6.1.4",
"winston-papertrail": "^1.0.5",
"winston-transport": "^4.9.0",
"ws": "^8.20.0",
"yaml": "^2.8.3",
"zod": "^3.25.0"
},
"devDependencies": {
"@biomejs/biome": "^2.4.9",
"@sentry/types": "^10.46.0",
"@smithy/util-stream": "^4.5.20",
"@types/bootstrap": "^5.2.10",
"@types/chai": "^5.2.3",
"@types/compression": "^1.8.1",
"@types/express": "^5.0.6",
"@types/file-saver": "^2.0.7",
"@types/http-proxy": "^1.17.17",
"@types/jquery": "^4.0.0",
"@types/js-cookie": "^3.0.6",
"@types/node-targz": "^0.2.4",
"@types/request": "^2.48.13",
"@types/response-time": "^2.3.9",
"@types/supertest": "^7.2.0",
"@types/temp": "^0.9.4",
"@types/underscore": "^1.13.0",
"@types/webpack-env": "^1.18.8",
"@types/which": "^3.0.4",
"@types/ws": "^8.18.1",
"@vitest/coverage-v8": "^4.1.2",
"aws-sdk-client-mock": "^4.1.0",
"cheerio": "^1.2.0",
"css-loader": "^7.1.4",
"css-minimizer-webpack-plugin": "^8.0.0",
"cypress": "^15.13.0",
"file-loader": "^6.2.0",
"happy-dom": "^20.8.9",
"husky": "^9.1.7",
"lint-staged": "^16.4.0",
"mini-css-extract-plugin": "^2.10.2",
"mock-fs": "^5.5.0",
"monaco-editor-webpack-plugin": "^7.1.1",
"nock": "^14.0.11",
"npm-run-all": "^4.1.5",
"sass": "^1.98.0",
"sass-loader": "^16.0.7",
"source-map-loader": "^5.0.0",
"supertest": "^7.2.2",
"terser-webpack-plugin": "^5.4.0",
"ts-loader": "^9.5.4",
"typescript": "^5.9.3",
"vitest": "^4.0.1",
"vitest-fetch-mock": "^0.4.5",
"webpack": "^5.105.4",
"webpack-cli": "^6.0.1",
"webpack-dev-middleware": "^7.4.5",
"webpack-manifest-plugin": "^6.0.1"
},
"optionalDependencies": {
"@rollup/rollup-darwin-arm64": "^4.60.0",
"@rollup/rollup-darwin-x64": "^4.60.0",
"@rollup/rollup-linux-x64-gnu": "^4.60.0",
"@rollup/rollup-linux-x64-musl": "^4.60.0",
"@rollup/rollup-win32-x64-msvc": "^4.60.0"
},
"scripts": {
"cypress": "cypress run",
"lint": "biome check . --write",
"lint-check": "biome check .",
"test-coverage": "vitest run --coverage",
"test": "vitest run",
"test:watch": "vitest",
"test-min": "cross-env SKIP_EXPENSIVE_TESTS=true vitest run",
"test:props": "vitest run --reporter=dot properties-validation-tests.ts",
"check": "npm run ts-check && npm run lint-check && npm run check-frontend-imports && npm run test-min -- --reporter dot",
"check-frontend-imports": "node ./etc/scripts/check-frontend-imports.js",
"dev": "cross-env NODE_ENV=DEV node --no-warnings=ExperimentalWarning --import=tsx app.ts",
"debugger": "cross-env NODE_ENV=DEV node --import=tsx --inspect-brk app.ts",
"debug": "cross-env NODE_ENV=DEV node --no-warnings=ExperimentalWarning --import=tsx app.ts --debug",
"start": "npm run webpack && cross-env NODE_ENV=LOCAL node --no-warnings=ExperimentalWarning --import=tsx app.ts",
"sentry": "npx -p @sentry/cli sentry-cli",
"update-browserslist": "npx update-browserslist-db@latest",
"prepare": "husky",
"ts-compile": "tsc",
"ts-check": "npm-run-all ts-check:*",
"ts-check:backend": "tsc -p ./tsconfig.backend.json --noEmit",
"ts-check:frontend": "tsc -p ./tsconfig.frontend.json --noEmit",
"ts-check:tests": "tsc -p ./tsconfig.tests.json --noEmit",
"ts-check:frontend-tests": "tsc -p ./tsconfig.frontend.tests.json --noEmit",
"ts-check:cypress": "tsc -p ./cypress/tsconfig.json --noEmit",
"webpack": "node --no-warnings=ExperimentalWarning --import=tsx ./node_modules/webpack-cli/bin/cli.js --node-env=production --config webpack.config.esm.ts"
},
"license": "BSD-2-Clause",
"packageManager": "npm@11.2.0"
}