Skip to main content
Glama

openlint-mcp

An MCP server for OpenLint. It lets AI tools such as Claude, Codex and OpenCode check OpenLint rulesets, and, as the remaining milestones land, lint OpenAPI, AsyncAPI and Arazzo documents and look up rules.

The server runs locally over stdio and builds only on the published @openlint/* npm packages. Design and milestones: docs/v1-plan.md.

Status: early development. The package isn't on npm yet, so install it from source (below). Once it's published, node /abs/path/to/openlint-mcp/dist/index.js in the examples becomes npx -y @openlint/openlint-mcp.

Tools

All tools are read-only.

Tool

What it does

Status

list_rulesets

Lists the rulesets the server is configured with

Available

validate_ruleset

Checks a YAML or JSON ruleset without running any code, and warns about anything lint_document would refuse inline

Available

lint_document

Lints an OpenAPI, AsyncAPI or Arazzo document

Planned (milestone 3)

list_rules, explain_rule

Look up the rules in a ruleset

Planned (milestone 4)

Related MCP server: Spotlight MCP

Install from source

Requires Node.js ^22.22.1 || >=24.

git clone https://github.com/miqui/openlint-mcp.git
cd openlint-mcp
npm ci
npm run build
npm run smoke   # optional: starts the built server over stdio and calls its tools

The server's command is node /abs/path/to/openlint-mcp/dist/index.js. Run pwd in the clone to get the absolute path.

Configure rulesets

Option

Meaning

--ruleset <path> (or -r)

The default ruleset: a YAML, JSON or JS ruleset file

--ruleset <name>=<path>

An extra named ruleset. Repeat the option to add more. oas, asyncapi and arazzo are reserved names.

OPENLINT_RULESET=<path>

The default ruleset, when there is no unnamed --ruleset

  • Which one is the default: an unnamed --ruleset, then OPENLINT_RULESET, then the first named ruleset.

  • Filenames that contain =: --ruleset team=prod.yaml means a ruleset named team, loaded from prod.yaml. To load a file called team=prod.yaml, start the path with ./ or /, as in --ruleset ./team=prod.yaml.

  • No ruleset configured: the server uses the oas, asyncapi and arazzo rulesets from @openlint/openlint-rulesets (recommended rules).

  • Use absolute paths. AI tools usually start MCP servers from a different working directory than your project, so relative paths may not resolve.

  • Custom functions are supported in configured rulesets (functions, functionsDir, and JS rulesets).

  • Errors at startup: if a ruleset fails to load, the server prints the error to stderr and exits. Your AI tool will show the server as failed; its MCP logs have the message.

Rulesets that an AI tool passes inline in a tool call are untrusted, so they are restricted:

  • no custom functions

  • extends only names a configured ruleset, oas, asyncapi, arazzo, or one of the aliases spectral:oas, spectral:asyncapi, spectral:arazzo, openlint:openapi, openlint:asyncapi and openlint:arazzo

  • formats only names formats exported by @openlint/openlint-formats

validate_ruleset explains what was refused.

Use with AI tools

In every example, replace /abs/path/to/openlint-mcp with the absolute path of your clone, and /abs/path/to/.openlint.yaml with your ruleset. The --ruleset … part is optional. Without it, the server uses the default rulesets above.

Claude Code

Add the server with the claude CLI. Everything after -- is the server's own command line:

claude mcp add openlint -- node /abs/path/to/openlint-mcp/dist/index.js --ruleset /abs/path/to/.openlint.yaml

Choose where the setting is stored with --scope:

  • local (default): only you, only this project.

  • project: everyone working on the project. It is written to .mcp.json in the project root, for committing. Claude Code asks each person to approve the server the first time.

  • user: only you, in all your projects.

A project-scoped .mcp.json looks like this:

{
  "mcpServers": {
    "openlint": {
      "type": "stdio",
      "command": "node",
      "args": ["/abs/path/to/openlint-mcp/dist/index.js", "--ruleset", "/abs/path/to/.openlint.yaml"],
      "env": {}
    }
  }
}

The paths in a committed .mcp.json must work on every teammate's machine. If your checkouts live in different places, have each person run claude mcp add with the default local scope instead.

To set the ruleset through the environment instead, use -e OPENLINT_RULESET=/abs/path/to/.openlint.yaml before --.

Check it's connected: run claude mcp list, or /mcp inside a Claude Code session.

Docs: Claude Code MCP.

Claude Desktop

Open Settings → Developer → Edit Config. That opens claude_desktop_config.json:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Add the server:

{
  "mcpServers": {
    "openlint": {
      "command": "/abs/path/to/node",
      "args": ["/abs/path/to/openlint-mcp/dist/index.js", "--ruleset", "/abs/path/to/.openlint.yaml"]
    }
  }
}
  • Use the absolute path to node (run which node, or where node on Windows). Desktop apps often don't see the PATH from your shell, especially with version managers like nvm or asdf.

  • On Windows, escape backslashes in JSON paths: "C:\\Users\\you\\openlint-mcp\\dist\\index.js".

  • Restart: quit Claude Desktop completely and start it again.

  • If it doesn't connect, check the logs. On macOS they're at ~/Library/Logs/Claude/mcp-server-openlint.log.

Docs: Connect to local MCP servers.

Codex

Add the server with the codex CLI. The CLI, the IDE extension and the ChatGPT desktop app share this configuration:

codex mcp add openlint -- node /abs/path/to/openlint-mcp/dist/index.js --ruleset /abs/path/to/.openlint.yaml

This writes to ~/.codex/config.toml. You can also edit that file directly:

[mcp_servers.openlint]
command = "node"
args = ["/abs/path/to/openlint-mcp/dist/index.js", "--ruleset", "/abs/path/to/.openlint.yaml"]

# Optional, instead of --ruleset:
# [mcp_servers.openlint.env]
# OPENLINT_RULESET = "/abs/path/to/.openlint.yaml"
  • Environment variable from the CLI: codex mcp add openlint --env OPENLINT_RULESET=/abs/path/to/.openlint.yaml -- node /abs/path/to/openlint-mcp/dist/index.js.

  • Per project: a .codex/config.toml in a trusted project can hold the same table.

Check it's connected: run codex mcp list, or /mcp inside the Codex TUI.

Docs: Codex MCP.

OpenCode

Add the server with the opencode CLI. Without --global, it goes into the project's configuration:

opencode mcp add openlint -- node /abs/path/to/openlint-mcp/dist/index.js --ruleset /abs/path/to/.openlint.yaml

In some older versions, opencode mcp add doesn't take a name and command, and asks for them instead. Enter openlint as the name, choose a local server, and give the same command.

Or edit opencode.json (or opencode.jsonc) in the project root, or ~/.config/opencode/opencode.json for all projects. The format in OpenCode's current docs (V2) puts servers under mcp.servers:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "servers": {
      "openlint": {
        "type": "local",
        "command": ["node", "/abs/path/to/openlint-mcp/dist/index.js", "--ruleset", "/abs/path/to/.openlint.yaml"],
        // Optional, instead of --ruleset:
        // "environment": { "OPENLINT_RULESET": "/abs/path/to/.openlint.yaml" }
      },
    },
  },
}

Older versions (V1) put servers directly under mcp, and use enabled:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "openlint": {
      "type": "local",
      "command": ["node", "/abs/path/to/openlint-mcp/dist/index.js", "--ruleset", "/abs/path/to/.openlint.yaml"],
      "enabled": true,
    },
  },
}

If you're not sure which format your version uses, opencode mcp add writes the right one.

Check it's connected: run opencode mcp list, or /mcps inside OpenCode.

Docs: OpenCode MCP servers.

Try it

Once the server is connected, ask your AI tool things like:

  • "Which OpenLint rulesets are configured?"

  • "Write an OpenLint ruleset that requires info.contact in OpenAPI 3.1 documents, and validate it with validate_ruleset."

Development

npm ci
npm run lint    # Prettier, ESLint and type-check
npm test        # Vitest
npm run build   # TypeScript 7 → dist/
npm run smoke   # the built server over stdio

Commit messages and PR titles follow conventional commits, as in OpenLint's CONTRIBUTING.md.

License

Apache-2.0

Available Tools

2 tools
list_rulesetsList rulesetsA
Read-only

Lists the rulesets this server is configured with. Pass a name as the ruleset argument of other tools; when ruleset is omitted, the default ruleset is used. ruleCount counts the rules that are enabled.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
rulesetsYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so the safety profile is covered. The description adds genuinely useful behavioral context beyond that: the names returned are valid `ruleset` arguments elsewhere, and omission falls back to a default ruleset, which implies a hidden default configuration state an agent should know about.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, each carrying distinct information: what is listed, how to use the result, and what `ruleCount` means. Nothing is redundant and the core action is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter read-only tool with an output schema present, the description need not explain return values, and it already clarifies the one ambiguous output field (`ruleCount` counts enabled rules). Nothing an agent needs to call this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4 and there is no parameter semantics to compensate for. The description correctly spends no space on inputs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

"Lists the rulesets this server is configured with" names a specific verb and resource and scopes it to this server, which cleanly separates it from the sibling validate_ruleset. An agent knows exactly what it gets back without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains what to do with the result (pass a listed name as the `ruleset` argument of other tools) and states the fallback behavior when `ruleset` is omitted (the default ruleset is used). It gives clear context for use but does not state explicit exclusions or contrast with validate_ruleset.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

validate_rulesetValidate rulesetA
Read-only

Checks a YAML or JSON OpenLint ruleset without running any code. valid means the ruleset is well formed. warnings list what lint_document would refuse when the ruleset is passed inline (custom functions, or extending files, URLs or packages); such rulesets can still be configured on the server with --ruleset. JavaScript rulesets are not supported.

NameRequiredDescriptionDefault
contentYesThe ruleset as YAML or JSON text

Output Schema

NameRequiredDescription
validYes
errorsYes
warningsYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint/openWorldHint annotations, it adds real context: no code is executed, JavaScript rulesets are unsupported, and what `valid` and `warnings` signify. However, it doesn't state what happens on malformed YAML/JSON input (throw vs valid=false), which is a meaningful behavioral gap for a validation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences, front-loaded with the core action and followed by result semantics. The closing notes on server-side --ruleset and JavaScript are useful rather than padding, though the sentence about warnings is dense and packs several conditions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return-format detail isn't required, yet the description still explains the two key fields. For a one-parameter validation tool this is essentially complete; only error behavior on unparseable input is left unstated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With a single required parameter and 100% schema coverage, the schema already carries the load. The description's mention of 'YAML or JSON' reinforces the accepted format but adds nothing about string encoding, size limits, or file-vs-inline handling.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb+resource: 'Checks a YAML or JSON OpenLint ruleset without running any code', which tells an agent exactly what the tool operates on and that it is static analysis. It implicitly separates itself from list_rulesets by taking ruleset content rather than enumerating stored rulesets, but never names the sibling explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains the decision-relevant context: warnings indicate what 'lint_document would refuse when the ruleset is passed inline', and notes such rulesets 'can still be configured on the server with --ruleset'. That is clear guidance on when this tool's output matters and what to do with each outcome, though there is no explicit statement of when to call this versus list_rulesets.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.0.0
    • First observedlist_rulesets
    • First observedvalidate_ruleset

TDQS

A3.9/5.0

Scored across 2 tools

Disambiguation5/5

The two tools perform clearly distinct actions: listing configured rulesets versus validating a ruleset definition. Although both concern rulesets, the descriptions make the boundary unambiguous, so an agent can easily choose the right one.

Naming Consistency5/5

Both names follow a consistent snake_case verb_noun pattern (list_rulesets, validate_ruleset). The convention is predictable and readable.

Tool Count3/5

Two tools is borderline thin for a server whose apparent purpose is linting. The set covers only ruleset introspection, leaving the core linting action absent.

Completeness2/5

The description of validate_ruleset references lint_document, which is not exposed as a tool, so the central operation of an OpenLint server is missing. There is also no way to inspect individual rules within a ruleset, leaving significant gaps for agent workflows.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues

Related MCP Connectors

Related MCP Servers