Skip to content

Repository files navigation

tool-lint

Built by The Foundry, an autonomous build pipeline I run. A Haiku scout finds a developer pain point, a Sonnet agent writes the spec, and aider driving Sonnet builds it overnight.

This repo was produced end to end by that pipeline. I commissioned the system, approved each phase of it and reviewed what it shipped.

A linter for the tool definitions you hand to an LLM. It reads MCP, OpenAI function-calling and Anthropic tool-use schemas, applies twelve rules, and scores each tool out of 100. The rules cover the things that make an agent pick the wrong tool or call it wrongly: a description too short to disambiguate, a parameter with no description, a parameter with no type.

Built on 2 March 2026. Twelve rules, three formats, three output formats, 23 tests.

Running it

It isn't published to npm, so run it from source:

git clone https://github.com/solstice035/tool-lint.git
cd tool-lint
npm install
npm run build
node dist/bin/tool-lint.js check examples/mcp-tools.json

npm link puts it on your path as tool-lint.

What it looks like

$ tool-lint check bad-tools.json

bad-tools.json

  get_data [61/100]
    ⚠ Tool "get_data" has a very short description (9 chars)
      bad-tools.json > get_data > description
      → Expand the description to at least 20 characters with specific details about what the tool does
    ⚠ Parameter "x" in tool "get_data" is missing a description
      bad-tools.json > get_data > parameters.properties.x
      → Add a description explaining what "x" is used for and what values are expected
    ✖ Parameter at "parameters.properties.flag" is missing a type definition
      bad-tools.json > get_data > parameters.properties.flag
      → Add a "type" field (e.g., "string", "number", "boolean", "object", "array")

Summary
──────────────────────────────────────────────────
Files checked: 1
Tools analyzed: 1
Total violations: 4
Average score: 61/100

Commands

tool-lint check [files...]     lint files, directories, globs, or "-" for stdin
  --format <text|json|sarif>   output format (default: text)
  --min-score <n>              fail if any tool scores below n
  --severity <level>           minimum severity to report (error|warning|info)
  --ignore <rules>             comma-separated rule IDs to skip
  --config <path>              path to a config file
  -q, --quiet                  summary only
  -v, --verbose                include passing checks

tool-lint rules                list the rules
tool-lint init                 write a .toollintrc.json

SARIF output is there so GitHub can show findings in the Security tab.

The rules

Rule Severity
missing-tool-name error
missing-tool-description error
missing-required-field error
no-type-specified error
duplicate-param-names error
vague-tool-description warning
missing-param-description warning
vague-param-name warning
too-many-params warning
deeply-nested-schema warning
ambiguous-enum info
oversized-description info

Every tool starts at 100 and loses 15 per error, 8 per warning and 3 per info. The score is a rough signal, not a measurement: it says how many rules a definition tripped, weighted by how much each one matters.

Formats it reads

The same tool in each of the three shapes it understands, which differ mainly in where the schema hangs:

{ "name": "get_weather", "description": "Retrieves current weather for a location",
  "inputSchema": { "type": "object", "properties": { "location": { "type": "string", "description": "City name or ZIP code" } }, "required": ["location"] } }

MCP uses inputSchema, Anthropic uses input_schema, and OpenAI uses parameters. A file can hold a single tool or an array of them.

Known issues

  • A .toollintrc.json in the project root is not picked up on its own. You have to pass --config .toollintrc.json. tool-lint init writes the file but nothing reads it by default.
  • Only ignore is read from that config. The minScore, rules and format keys are parsed and then unused, so rule severity can't be changed from the file. --ignore and --min-score on the command line do work.
  • init writes its rules as {"enabled": true} objects, which is a third shape again, and also unused.
  • Reading from stdin writes to a fixed path, /tmp/tool-lint-stdin.json, so two runs at once will collide.

Development

npm install
npm run build
npm test        # 23 tests

Licence

MIT. See LICENSE.

About

CLI linter for AI tool definitions — validates MCP, OpenAI, and Anthropic schemas

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages