Skip to content

feat: Add MCP server #19592

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Apr 30, 2025
Merged

feat: Add MCP server #19592

merged 20 commits into from
Apr 30, 2025

Conversation

nzakas
Copy link
Member

@nzakas nzakas commented Apr 1, 2025

Prerequisites checklist

What is the purpose of this pull request? (put an "X" next to an item)

[ ] Documentation update
[ ] Bug fix (template)
[ ] New rule (template)
[ ] Changes an existing rule (template)
[ ] Add autofix to a rule
[x] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

What changes did you make? (Give an overview)

This change adds an MCP server that is triggered by --mcp. Because no other options are valid with this, I implemented it similar to --init.

The TypeScript SDK is a bit rough, so I had to work around a couple of things. Overall, everything works. You can test it out locally using the MCP inspector:
https://github.com/modelcontextprotocol/inspector

fixes #19588

Is there anything you'd like reviewers to focus on?

@nzakas nzakas requested a review from a team as a code owner April 1, 2025 18:23
@github-project-automation github-project-automation bot moved this to Needs Triage in Triage Apr 1, 2025
@eslint-github-bot eslint-github-bot bot added the feature This change adds a new feature to ESLint label Apr 1, 2025
@github-actions github-actions bot added the cli Relates to ESLint's command-line interface label Apr 1, 2025
Copy link

netlify bot commented Apr 1, 2025

Deploy Preview for docs-eslint canceled.

Name Link
🔨 Latest commit a59b7c3
🔍 Latest deploy log https://app.netlify.com/sites/docs-eslint/deploys/680f947e2387000008e4a076

Copy link

Hi everyone, it looks like we lost track of this pull request. Please review and see what the next steps are. This pull request will auto-close in 7 days without an update.

@github-actions github-actions bot added the Stale label Apr 11, 2025
@nzakas
Copy link
Member Author

nzakas commented Apr 14, 2025

Still iterating on this.

@nzakas
Copy link
Member Author

nzakas commented Apr 16, 2025

I've got this working and it's kind of fun. 😄 Also added setup docs.

@fasttime fasttime moved this from Needs Triage to Triaging in Triage Apr 18, 2025
@fasttime fasttime added the accepted There is consensus among the team that this change meets the criteria for inclusion label Apr 18, 2025
@fasttime fasttime moved this from Triaging to Implementing in Triage Apr 18, 2025
Copy link
Member

@fasttime fasttime left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After looking into this pull request I'm excited to see the new MCP server feature come to life. I left some comments.

@nzakas
Copy link
Member Author

nzakas commented Apr 23, 2025

Okay, I removed the "lint and fix" tool. I also removed the prompts as it's unclear how this is intended to be used and I'd rather start small and add things later as we find their helpful.

Comment on lines +39 to +44
const eslint = new ESLint({
// enable lookup from file rather than from cwd
flags: ["unstable_config_lookup_from_file"],
});

const results = await eslint.lintFiles(filePaths);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In VS Code (I haven't checked other IDEs yet) the MCP server runs with cwd set to my user's home folder. This is reflected in the cwd constructor option and it could affect how plugins work when files are linted. Are we comfortable with this? Alternatively we could set the current directory explicitly with process.chdir to avoid surprises.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure there's anything we can do about this. I don't think we have a logical directory to pass to process.chdr().

As a note, this is why we need the unstable_config_lookup_from_file flag passed to correctly find a config file for any files being linted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that maybe we could set the current directory explicitly to os.homedir() or to / in case the server is started from a different location.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I this calling process.chdir() is a bit dangerous. It's possible that they're still trying to figure out what the cwd for MCP servers and I don't think we want to override that default.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like Cursor sets the cwd to the location of the Cursor executable. I'd say let's hold off on trying to change the directory. Maybe there's a way we can get the editor to give us a relevant directory.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Windsurf also passes its own cwd to the MCP server process. We can start with the editor default then.

Comment on lines +98 to +104
### 1. Access Windsurf Settings

Navigate to Windsurf - Settings > Advanced Settings, or open the Command Palette and select "Open Windsurf Settings Page".

### 2. Add ESLint MCP Server

Scroll down to the Cascade section and click the "Add Server" button. Then select "Add custom server +".
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably the UI is different for different operating systems or releases. For me in Windows the menu selection was File > Preferences > Windsurf Settings, then I clicked on the "Cascade" tab and the page scrolled down to that section directly.

Comment on lines 26 to 35
const filePathsSchema = {
filePaths: z
.array(
z
.string()
.min(1)
.describe("Full filesystem path of a file to be analyzed"),
)
.nonempty(),
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After removing describe() I'm still seeing the error "Invalid type for parameter 'filePaths' in tool lint-files" in Cursor, but now it's showing a button where I can start lint-files anyway, and it seems to be working. So probably it's okay to keep it like this.

Comment on lines +39 to +44
const eslint = new ESLint({
// enable lookup from file rather than from cwd
flags: ["unstable_config_lookup_from_file"],
});

const results = await eslint.lintFiles(filePaths);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Windsurf also passes its own cwd to the MCP server process. We can start with the editor default then.

@fasttime fasttime requested a review from Copilot April 28, 2025 10:32
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request adds an MCP server to ESLint that is triggered using the new CLI option "--mcp". The changes include new tests for the MCP server, updates to the CLI option parsing, and comprehensive documentation on configuring and using the MCP server.

  • Added tests in tests/lib/mcp/mcp-server.js to validate MCP server behavior.
  • Introduced a new CLI flag in lib/options.js and integrated the MCP server implementation in lib/mcp/mcp-server.js.
  • Updated documentation in docs/src/use/mcp.md and docs/src/use/command-line-interface.md to guide users in configuring and using the MCP server.

Reviewed Changes

Copilot reviewed 8 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/lib/mcp/mcp-server.js New tests to verify the behavior of the MCP server and ESLint tool integration.
tests/lib/cli.js Removed extraneous commented separator lines.
lib/options.js Added conditional inclusion of the "--mcp" CLI flag when using flat config.
lib/mcp/mcp-server.js Implemented MCP server logic and tool registration for linting files.
docs/src/use/mcp.md Documentation for setting up and using the MCP server across various editors.
docs/src/use/command-line-interface.md Updated CLI documentation to include the new "--mcp" option.
Files not reviewed (2)
  • .temp-eslintsuppressions: Language not supported
  • package.json: Language not supported
Comments suppressed due to low confidence (2)

tests/lib/mcp/mcp-server.js:60

  • [nitpick] Consider adding tests to verify that the MCP server handles invalid or empty 'filePaths' inputs properly, ensuring robust validation of the provided schema.
describe("Tools", () => {

lib/options.js:216

  • [nitpick] The '--mcp' flag is conditionally added only when using flat config. Confirm whether this behavior is intentional or if the flag should be available regardless of the config type to avoid potential inconsistencies in CLI usage.
let mcpFlag;

fasttime
fasttime previously approved these changes Apr 28, 2025
Copy link
Member

@fasttime fasttime left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks! This is good enough for a start. I left some comments but I don't have any further suggestions at this time. I would like another review from a team member, if possible.

@fasttime
Copy link
Member

Oh, and we should probably add .temp-eslintsuppressions and .cursor/ to gitignore.

@fasttime fasttime moved this from Implementing to Second Review Needed in Triage Apr 28, 2025
@nzakas
Copy link
Member Author

nzakas commented Apr 28, 2025

I opted to fix the test so that .temp-eslintsuppressions is deleted after completion.

@nzakas nzakas requested a review from Copilot April 29, 2025 20:22
@nzakas
Copy link
Member Author

nzakas commented Apr 29, 2025

Ping @mdjermanovic.

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request introduces a new CLI option (--mcp) to start an MCP server for ESLint and adds the necessary integration for both internal tests and external documentation.

  • Adds a new MCP server implementation and related tests
  • Updates CLI options and documentation for MCP server usage across multiple editors
  • Removes extraneous comment lines in the CLI tests to improve readability

Reviewed Changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/lib/mcp/mcp-server.js New tests for validating MCP server behavior
tests/lib/cli.js Removed redundant separator comment
lib/options.js Added conditional definition of the mcpFlag option
lib/mcp/mcp-server.js New MCP server implementation exposing the "lint-files" tool
docs/src/use/mcp.md Added documentation for setting up the ESLint MCP server
docs/src/use/command-line-interface.md Updated CLI documentation for the new --mcp option
Files not reviewed (1)
  • package.json: Language not supported
Comments suppressed due to low confidence (2)

tests/lib/mcp/mcp-server.js:83

  • [nitpick] Consider adding an inline comment to explain the rationale behind slicing the rawResults array to omit the header and footer, to enhance future maintainability.
const results = rawResults.slice(1, rawResults.length - 1)

lib/options.js:218

  • [nitpick] Consider reviewing the conditional inclusion of the mcpFlag; verify if the MCP server option should also be available in non-flat config environments for consistency.
if (usingFlatConfig) {

Copy link
Member

@mdjermanovic mdjermanovic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@mdjermanovic mdjermanovic merged commit dcbdcc9 into main Apr 30, 2025
32 checks passed
@mdjermanovic mdjermanovic deleted the issue19588 branch April 30, 2025 10:46
@github-project-automation github-project-automation bot moved this from Second Review Needed to Complete in Triage Apr 30, 2025
@christian-kreuzberger-dtx

While I think this change is awesome to have, it just introduced a ton of new dependencies for eslint.

Please consider reverting this PR and invest into #19682 .

@RobinTail
Copy link
Contributor

RobinTail commented May 5, 2025

@nzakas

If one doesn't use AI to read ESLint messages, why do they have to install all that?
Could you make it an opt-in feature, an optional peer dependency?

image

image

image

@qupig
Copy link

qupig commented May 9, 2025

Watch the dependencies changed by this PR more intuitively, and almost doubles in both the number and size:

eslint_dependencies

@SimenB
Copy link
Contributor

SimenB commented May 9, 2025

There is a PR open for extracting it: #19699

@RobinTail

This comment was marked as off-topic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion cli Relates to ESLint's command-line interface feature This change adds a new feature to ESLint
Projects
Status: Complete
Development

Successfully merging this pull request may close these issues.

Change Request: MCP Server
8 participants