Skip to main content

Structural linter for MCP-compatible, zero-dependency Python agent tool packages

Project description

Toolint

Structural linter for MCP-compatible, zero-dependency Python agent tool packages.

toolint enforces architectural rules that ensure your Python package works correctly as:

  • A standalone library (from my_tool import MyTool)
  • A CLI tool (my-tool search "query")
  • An MCP server (my-tool serve --source spec.json)
  • An SDK middleware (OpenAI / Anthropic client patches)

Inspired by the architecture of graph-tool-call.

Why?

Building agent-compatible tools is easy to get wrong:

Mistake Consequence
core/ imports numpy without guard Users without numpy get ImportError on import my_tool
MCP server has business logic Can't use the same functionality as a library
__version__ != pyproject.toml version PyPI shows wrong version
Tool function has no docstring LLM can't understand what the tool does
Optional dep not in extras pip install my-tool[mcp] doesn't install MCP SDK

toolint catches all of these before they reach users.

Installation

pip install toolint

# or with uv
uv pip install toolint

# or as a tool
uvx toolint check .

Quick Start

# Lint current project
toolint check .

# Lint with specific rules only
toolint check . --select ATL101,ATL102

# Ignore specific rules
toolint check . --ignore ATL105

# JSON output (for CI integration)
toolint check . --format json

# Show all available rules
toolint rules

Example Output

my_tool/core/engine.py:3:0  ATL101 (error)
  Hard import of 'numpy' in core module — core must be stdlib-only.
  Use try/except ImportError guard or move to a non-core module.

my_tool/retrieval/embedding.py:5:0  ATL102 (error)
  Optional import 'sentence_transformers' missing try/except ImportError guard.

my_tool/__init__.py:1:0  ATL004 (error)
  Version mismatch: __init__.py has '0.2.0' but pyproject.toml has '0.2.1'

my_tool/mcp_server.py:42:8  ATL503 (error)
  MCP tool function 'process_data' has no docstring.
  LLMs rely on tool descriptions to select the right tool.

4 issues found (4 errors, 0 warnings)

Rules

Layer 1: Structure (ATL0xx)

Rule Severity Description
ATL001 error Package must have a single public facade class
ATL002 error __main__.py must exist and be registered in pyproject.toml scripts
ATL003 warning __init__.py must define __all__ including the facade class
ATL004 error __version__ in __init__.py must match pyproject.toml version

Layer 2: Dependency Rules (ATL1xx)

Rule Severity Description
ATL101 error No third-party imports in core/ directory (stdlib only)
ATL102 error Optional dependencies must use try/except ImportError guard
ATL103 warning Import guard must include install hint (e.g. pip install pkg[extra])
ATL104 error Optional imports must be registered in pyproject.toml extras
ATL105 warning __init__.py should not eagerly import optional-dep modules

Layer 3: Layer Separation (ATL2xx)

Rule Severity Description
ATL201 warning Interface files should not call internal business logic directly (type/enum/constant imports are allowed)
ATL202 warning CLI command handlers should invoke functionality through the facade class
ATL203 warning Interface layer should not import core/ internals directly (except types/constants)

Layer 4: pyproject.toml Consistency (ATL3xx)

Rule Severity Description
ATL301 error CLI entry point must be registered in [tool.poetry.scripts] or [project.scripts]
ATL302 error If MCP server exists, mcp extras group must be defined
ATL303 warning all extras group must include all dependencies from other extras groups

Layer 5: Tool Schema Quality (ATL5xx)

Rule Severity Description
ATL501 warning Facade public methods must have docstrings
ATL502 warning Facade public methods must have parameter + return type hints
ATL503 error MCP tool functions must have docstrings (min 10 chars)
ATL504 warning MCP tool function docstrings should describe each parameter

Layer 6: Agent Compatibility (ATL6xx)

Rule Severity Description
ATL601 warning Facade public methods should return JSON-serializable types
ATL602 error MCP tool functions must return str (MCP protocol requirement)
ATL603 warning Facade/MCP tools should not silently swallow exceptions

Configuration

Add to pyproject.toml:

[tool.toolint]
# Package root (auto-detected from pyproject.toml)
package = "my_tool"

# Facade class name (auto-detected if single prominent class exists)
facade_class = "MyTool"

# Core directory (default: "core")
core_dir = "core"

# Interface files (default: auto-detected)
interface_files = ["mcp_server.py", "mcp_proxy.py", "middleware.py", "__main__.py"]

# Extra stdlib-like packages allowed in core (escape hatch)
core_allowed_imports = []

# Rules to ignore
ignore = ["ATL105"]

Or use a standalone file .toolint.toml with the same structure (without the [tool.toolint] nesting).

The Architecture This Enforces

my_package/
├── __init__.py          # __version__, __all__, lazy imports
├── __main__.py          # CLI (argparse) — calls facade only
├── core/                # ZERO external dependencies (stdlib only)
│   ├── protocol.py      # Abstract interfaces (Protocol classes)
│   └── models.py        # Domain models (dataclasses)
├── feature_a/           # Business logic modules
│   └── ...              # May use optional deps with import guards
├── facade.py            # Single public API class
├── mcp_server.py        # MCP server — wraps facade only
└── middleware.py         # SDK patches — wraps facade only

Key principles:

  1. Core is stdlib-only — anyone can import my_tool without installing extras
  2. Facade is the single API surface — all interfaces (CLI, MCP, middleware) go through it
  3. Optional deps use import guards — graceful degradation, not crashes
  4. Interface layers are thin wrappers — no business logic in MCP server or CLI

CI Integration

GitHub Actions

- name: Lint agent tool structure
  run: uvx toolint check .

Pre-commit

repos:
  - repo: https://github.com/PlateerLab/toolint
    rev: v0.1.0
    hooks:
      - id: toolint

Technical Details

  • Python 3.10+
  • Zero dependencies — uses only ast and tomllib from stdlib
  • Fast — AST parsing, no runtime imports of the target package
  • Self-validatingtoolint follows the same architecture it enforces

License

MIT

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

toolint-0.1.0.tar.gz (22.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

toolint-0.1.0-py3-none-any.whl (27.5 kB view details)

Uploaded Python 3

File details

Details for the file toolint-0.1.0.tar.gz.

File metadata

  • Download URL: toolint-0.1.0.tar.gz
  • Upload date:
  • Size: 22.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for toolint-0.1.0.tar.gz
Algorithm Hash digest
SHA256 6db1de54ea3c9efafe8875e3a357ff68830ede56d52619f41773eaef00d8b7fd
MD5 d1cb559e11688ed73fcb8ebccba8137c
BLAKE2b-256 57339d34074e8067960f67fc50b303a3fbab8b2565b8dfc557f0ee37d3aa78b8

See more details on using hashes here.

File details

Details for the file toolint-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: toolint-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 27.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for toolint-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 322edd16b2c5f14b790fb6376429e17767e76ae65abc3f19c17f58346d1f9a15
MD5 6b69da2351cde0ef42327a33c5f3299c
BLAKE2b-256 7c24a48c8605b477d56ffcc2979e510ea8c72fb2a1a62a62f4a799c1c24e1e07

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page