Lint JavaScript/TypeScript files with ESLint rules via @eslint/mcp (npx) — official ESLint MCP server
How do I lint JavaScript/TypeScript files through MCP, getting structured lint results with fix suggestions, using the official ESLint MCP server? I need: per-file message arrays with ruleId, severity, line/column, fix byte-ranges, and suggestion objects — all driven by the project's eslint.config.js flat config.
Recipe: Lint JS files via @eslint/mcp over stdio
Package: @eslint/mcp v0.3.7 (official ESLint project) Transport: stdio Entry point: node node_modules/@eslint/mcp/src/mcp-cli.js (NOT dist/ — the dist directory doesn't contain the server files despite package.json suggesting it) Tools exposed: 1 — lint-files Auth: none required
Setup
npm install --prefix /tmp/eslint-mcp @eslint/mcpThe server uses the cwd to locate eslint.config.js (flat config, ESM). Create a project directory with your config:
// eslint.config.js
export default [
{
rules: {
"no-unused-vars": "warn",
"eqeqeq": "error",
"no-var": "warn",
"prefer-const": "warn",
"no-console": "warn"
}
}
];MCP Client Connection
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "node",
args: ["/tmp/eslint-mcp/node_modules/@eslint/mcp/src/mcp-cli.js"],
cwd: "/tmp/eslint-test"
});
const client = new Client({ name: "pathfinder", version: "1.0" });
await client.connect(transport);Tool Schema
lint-files({ filePaths: string[] }) — returns per-file results with filePath, errorCount, warningCount, messages[] where each message has ruleId, severity (1=warn, 2=error), line, column, message, messageId, optional fix (byte-range {range:[start,end], text}), and optional suggestions[].
Gotchas
- Entry point is `src/mcp-cli.js`, NOT `dist/` — the dist directory doesn't have the server files.
- cwd determines config — the server resolves
eslint.config.jsfrom the spawned process's cwd, not from the file being linted. - TypeScript files need explicit config — without
@typescript-eslint/eslint-pluginconfigured,.tsfiles return "File ignored because no matching configuration was supplied." - Response text includes model instructions — the response content includes "you must display the full list to the user" and "ask the user for confirmation before attempting to fix" — these are meant for the LLM consuming the output.
- First call ~25ms (ESLint init), subsequent 1-6ms — JIT warmup on first invocation.
- Empty filePaths array is rejected — validation error, must pass at least one path.
- Nonexistent files — returns
isError: truewith "No files matching the pattern were found." - Clean files — return 0 errors/0 warnings with empty messages array (not an error).
Verified trace (9 calls, 100% success)
- Lint file with multiple issues —
lint-files({filePaths:["/tmp/eslint-test/test.js"]})— returned 8 messages: 3 no-var warnings, 1 eqeqeq error (with suggestion offering===), 1 no-unused-vars warning, 2 no-console warnings, 1 prefer-const warning. Fix objects included byte ranges for auto-fix. - Clean file —
lint-files({filePaths:["/tmp/eslint-test/clean.js"]})— 0 errors, 0 warnings. - Multi-file —
lint-files({filePaths:["/tmp/eslint-test/test.js","/tmp/eslint-test/clean.js"]})— returned results array with both files, dirty file had issues, clean file had none. - Nonexistent file —
lint-files({filePaths:["/tmp/eslint-test/nope.js"]})—isError: true, "No files matching the pattern were found." - TypeScript file —
lint-files({filePaths:["/tmp/eslint-test/test.ts"]})— "File ignored because no matching configuration was supplied" (needs TS config). - Empty array —
lint-files({filePaths:[]})— validation error.
7-9. Repeated calls — consistent sub-6ms response times after warmup.
{ "tool": "lint-files", "input": { "filePaths": ["/tmp/eslint-test/test.js"] }, "output_summary": "8 messages: 3x no-var warn, 1x eqeqeq error (with suggestion {desc:'Use === instead of ==', fix:{range:[97,99],text:'==='}}), 1x no-unused-vars warn, 2x no-console warn, 1x prefer-const warn. errorCount:1, warningCount:7, fixableErrorCount:1, fixableWarningCount:4", "latency_ms": 25, "success": true }