Python API¶
Who this page is for: someone who wants to call mcpolish from Python code rather than the command line.
What you will learn¶
- How to import mcpolish and call its public functions.
- The shape of the report objects you get back.
- How to iterate over diagnostics in code.
- How to use mcpolish inside your own tooling or CI scripts.
Background¶
mcpolish ships as a normal Python package. The same engine that powers the CLI is available as a library. There are two top-level functions:
mcpolish.lint(path)returns a full report.mcpolish.score(path)returns just the score.
Both accept a string path or a pathlib.Path.
The public surface¶
import mcpolish
mcpolish.__version__
mcpolish.lint(path)
mcpolish.score(path)
mcpolish.Category
mcpolish.Severity
mcpolish.Diagnostic
mcpolish.ToolDecl
mcpolish.ToolRegistry
That is the whole public API. Power users can also reach into specific rule classes:
But the rules module is treated as semi-public; small changes between releases are possible.
Step by step¶
1. Install¶
2. Call lint¶
import mcpolish
report = mcpolish.lint("examples/smelly_server.py")
print(report.score) # 72
print(report.error_count) # 5
print(report.warning_count) # 7
print(report.note_count) # 14
The report is a LintReport dataclass with five fields:
| Field | Type | What |
|---|---|---|
diagnostics |
tuple[Diagnostic, ...] |
Every finding, sorted by file then line. |
registry |
ToolRegistry |
The IR mcpolish built. Has server_name, namespace, tools. |
score |
int |
0-100, same as mcpolish.score(). |
error_count |
int |
Number of diagnostics with severity error. |
warning_count |
int |
Number of diagnostics with severity warning. |
note_count |
int |
Number of diagnostics with severity note. |
There is also a helper:
3. Walk diagnostics¶
Diagnostic is a frozen Pydantic model. Useful fields:
| Field | Type | Notes |
|---|---|---|
rule_id |
str |
MP010 etc. |
rule_name |
str |
generic-tool-name. |
category |
Category |
Enum value. |
severity |
Severity |
Enum value. |
message |
str |
One-line summary. |
file |
str |
Absolute path. |
line |
int |
1-indexed. |
col |
int |
1-indexed. |
tool_name |
str | None |
Which tool. |
docs_url |
str |
Link to the rule's page. |
hint |
str | None |
Plain-language fix hint. |
fix |
Fix | None |
Present when an autofix is available. |
d.location() returns the file:line:col string.
4. Filter diagnostics in code¶
errors = [d for d in report.diagnostics if d.severity == mcpolish.Severity.ERROR]
naming = [d for d in report.diagnostics if d.category == mcpolish.Category.NAMING]
5. Just the score¶
Same number you would get from mcpolish score path/to/server.py.
Common variations¶
Build the registry yourself¶
If you want the IR without running any rules:
from mcpolish.discover.ir import build_registry
reg = build_registry("path/to/server.py")
for tool in reg.tools:
print(tool.name, tool.description[:50])
This is useful for writing your own checks on top of the discovery layer.
Pass a config in code¶
import mcpolish
from mcpolish.config.loader import MCPolishConfig
cfg = MCPolishConfig(ignore=["MP025"])
report = mcpolish.lint("path/", config=cfg)
Enable LLM rules programmatically¶
from mcpolish.llm.client import build_client
client = build_client("openai:gpt-4o")
report = mcpolish.lint("path/", llm=client)
You still need the right API key in your environment.
Use case: a custom CI gate¶
import sys
import mcpolish
report = mcpolish.lint(".")
if report.has_errors():
print(f"mcpolish: {report.error_count} errors, exiting", file=sys.stderr)
sys.exit(1)
if report.score < 80:
print(f"mcpolish: score {report.score} below gate of 80", file=sys.stderr)
sys.exit(1)
print(f"mcpolish: score {report.score}, build green")
This is more flexible than mcpolish lint --fail-on error because you can combine error counts and the score in one rule.
Troubleshooting¶
AttributeError: module 'mcpolish' has no attribute 'lint'. Older builds may not have wired the public API yet. Run pip install -U mcpolish. Confirm with mcpolish --version.
pydantic.ValidationError when constructing a Diagnostic. The library treats Diagnostic as immutable. Build the Diagnostic once with the fields you have. Do not mutate it.