Python Code Validator

ai.statemind由 statemind.ai 发布官方发布者v1.22.0更新于 Oct 24, 2026

Proves AI-generated Python does what you asked: lint, types, security, sandbox run, exact fixes.

已验证Streamable HTTP可网页运行开发者工具安全与监控

概览

AI 生成的概览

托管式 MCP 服务,对助手生成的 Python 代码做静态检查、修复,并在沙箱中运行,无需本地执行。

可以做什么
  • 静态校验 Python 源码:语法、ruff 检查、mypy 类型、AST 安全策略、bandit 与凭据扫描,并给出统一评分。
  • 确定性修复代码,在 fixed_code 中返回修正后的源码;无法证明安全时保留原代码。
  • 在无网络、只读文件系统的临时容器中运行代码,返回退出码、标准输出与标准错误。
  • 按意图校验:通过 options.examples 传入 assert 或 doctest 示例,示例不符即报错。
适用场景
适合在助手生成或修改 Python 文件、写入磁盘之前逐个检查,或需要直接拿到修复结果而非诊断信息时使用。沙箱运行与示例校验适合确认函数是否真的实现了预期行为。
运行要求
远程 HTTP 端点 api.statemind.ai,无需安装或自托管。必须在 Authorization 请求头中提供 bearer 密钥,可免账号获取免费密钥。修复与沙箱执行需要带额度的付费密钥;免费额度为每个 UTC 日 25 次静态检查。
安装前请注意
密钥属于机密,通过 Authorization 请求头发送。修复与执行模式为付费:额度需花钱购买,可用银行卡或通过 Gnosis 钱包以 xDAI 支付。execute_python 会运行提交的代码,但处于无网络、只读的容器中。说明称提交的代码不会被记录,但代码会发送给第三方服务。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Python Code Validator,将其添加到工作区。
  2. 连接 Authorization token 凭据。它们会被私密存储,绝不会在公开页面上展示。
  3. 为需要使用其工具的对话启用该服务。

Web executable,通过 Streamable HTTP。 远程服务在工作区中配置后即可从网页运行时运行。

交给智能体安装

把这段提示词粘贴到 Claude Code、Codex、Cursor 等智能体,或 SourceWeft 对话中。智能体会读取该服务器的安装指南,先向你展示发布者、端点和登录方式,经你同意后再把它添加到你的客户端。密钥不会写进任何命令。

阅读 https://sourceweft.com/mcp/ai.statemind%2Fpython-code-validator/install.md,并按其中说明把这个 MCP 服务器接入我的客户端。修改任何配置之前,先告诉我它的发布者、端点和登录方式,等我确认。不要把 API 密钥写进命令或我会提交的文件;问我密钥应从哪里获取。

查看智能体所依据的安装指南

其他 MCP 客户端

把它添加到你客户端的 mcpServers 配置中。

{
  "mcpServers": {
    "python-code-validator": {
      "type": "http",
      "url": "https://api.statemind.ai/mcp",
      "headers": {
        "Authorization": "Bearer <YOUR_TOKEN>"
      }
    }
  }
}

README

Python Code Validator

An MCP server that validates, repairs and runs Python against the examples it is supposed to satisfy — validate_python, repair_python and execute_python over HTTP at https://api.statemind.ai/mcp, with a free key and no account.

A hosted service that proves AI-generated Python does what you asked. State the intent — assertions or doctest lines — and the code is run against it inside a container with no network and a read-only filesystem; a fix comes back only when every example passes. On the QuixBugs defects that is 41% repaired and 77% refused as not doing what they say, with no false alarms on the corrected programs — where ruff and mypy flag the defect in none of them (the numbers).

The checks that need no intent come with it: syntax and lint diagnostics, an AST security policy that also catches calls hidden behind dynamic imports and runtime attribute lookups, a bandit pass, a credential scan and deterministic repair — one verdict with a score. Asking the same question twice inside ten minutes is answered from the first answer and costs nothing (x-msvc-repeat: 1).

This repository holds the client side: the MCP configuration, the CI script and the pre-commit hook. The service itself runs at https://api.statemind.ai, so there is nothing to install or host.

A key, without an account

bash
curl -s -X POST https://api.statemind.ai/v1/keys# {"api_key": "msvc_free_…", "tier": "free", "calls_per_day": 25, "modes": ["static"]}

25 static checks a day, metered per UTC day, and a few keys per address: enough to try it and to run it over a small project, not a supply. Every answer carries the state of the allowance (x-quota-remaining, x-quota-reset), so a client can back off before it is cut off.

MCP

Registered in the official MCP registry as ai.statemind/python-code-validator, a name verified against the domain that serves it rather than a GitHub account. Any MCP client adds it with one block:

json
{  "mcpServers": {    "python-code-validator": {      "type": "http",      "url": "https://api.statemind.ai/mcp",      "headers": { "Authorization": "Bearer msvc_free_…" }    }  }}
  • Claude Code: claude mcp add --transport http python-code-validator https://api.statemind.ai/mcp --header "Authorization: Bearer msvc_free_…"
  • Cursor: ~/.cursor/mcp.json, same block.
  • VS Code / Copilot: .vscode/mcp.json under "servers".

A client that only launches a command uses the stdio bridge in this repository instead, which forwards the same tool over HTTPS:

json
{  "mcpServers": {    "python-code-validator": {      "command": "python3",      "args": ["/path/to/python-code-validator/mcp_stdio.py"]    }  }}

Or as a container, which the Dockerfile here builds:

bash
docker build -t python-code-validator .docker run -i --rm -e VALIDATOR_API_KEY python-code-validator

Gemini CLI installs the same bridge as an extension, with the instruction file that makes it get used:

bash
gemini extensions install jkanselaar/python-code-validator

Three tools, named after what they do to the code:

toolruns the codekey
validate_pythonnofree
repair_python — also returns fixed_codenopaid
execute_python — also runs it in a sandboxyespaid

The old single python_code_validator tool, with its mode argument, still answers for clients that already configured it, but is no longer listed.

Saying what the code was supposed to do

Every check above passes on a function that computes the wrong answer. The one thing that catches it is the intent, and the agent that asked for the code is the only one who has it — so pass it along:

json
{"code": "def bitcount(n): …", "mode": "execute", "options": {"examples": "assert bitcount(127) == 7"}}

Doctest lines (>>> bitcount(127) then 7) work the same way, as do >>> examples already written in the source. execute_python runs them in the sandbox: one that does not hold is a python:example-mismatch error, and the repair search returns a fix only when every example passes. On the QuixBugs defect set — real bugs, hidden test inputs deciding correctness — that repairs 41% and refuses 77% as not doing what they say, with no false alarms on the corrected programs.

Repeating a call costs nothing: the same key asking the same question — same mode, same code, same examples — is answered from the answer it already got, marked x-msvc-repeat: 1, so an agent that checks its work at every step is not billed for verdicts that cannot have changed.

Claude Code plugin

An instruction can be ignored; a hook cannot. The plugin checks every Python file Claude Code writes or edits, in the turn it was written, and hands the errors back to the model instead of to you:

/plugin marketplace add jkanselaar/python-code-validator/plugin install python-code-validator@statemind

Nothing to configure: it mints and keeps its own free key on first use. A file that comes back accepted is silent, a rejected one stops the turn with the offending lines named, and an identical file is not asked about twice. It never ends a session over its own trouble — an unreachable service or a spent allowance lets the turn continue, and the allowance says how to raise it.

Set VALIDATOR_API_KEY to use a paid key instead of the free tier, and VALIDATOR_URL to point at your own deployment. The plugin also carries the validate-python skill, for the part a hook cannot do: stating the intent as examples and running the code against them.

Cursor hook

The same script, wired to Cursor's postToolUse, where the verdict comes back as context on the conversation instead of as an exit code:

bash
mkdir -p .cursor/hooksbase=https://raw.githubusercontent.com/jkanselaar/python-code-validator/maincurl -sf $base/plugin/hooks/validate_written.py -o .cursor/hooks/validate_written.pycurl -sf $base/cursor/hooks.json -o .cursor/hooks.json

Project hooks run from the project root, which is why the command in cursor/hooks.json is a path relative to it. For a hook that applies to every project instead, put the script in ~/.cursor/hooks/ and the same block in ~/.cursor/hooks.json with the command python3 ./hooks/validate_written.py --cursor.

Making the agent use it

Configuring the server is not what gets it called: the instruction file is. AGENTS.md in this repository is that text, written to be dropped into any project under whichever name the client reads:

bash
mkdir -p .githubcurl -sf https://raw.githubusercontent.com/jkanselaar/python-code-validator/main/AGENTS.md \  | tee AGENTS.md CLAUDE.md GEMINI.md .github/copilot-instructions.md >/dev/null

Cursor reads rules with front matter instead, so that one is a separate file — copy .cursor/rules/python-code-validator.mdc into .cursor/rules/ of the project.

The short version, if you would rather add a line to instructions you already have:

Write what the code should do as assert examples before writing the code, and pass them in options.examples. Call validate_python after every edit and execute_python once a function is finished, not again until what it does has changed. When a call returns fixed_code, take it — the service ran it against your examples. Do not present code that came back valid: false.

CI

The service hands out the client, so a workflow needs no checkout of this repository and no secret:

yaml
- run: |    curl -sf https://api.statemind.ai/v1/client -o validate.py    python3 validate.py --changed-against "origin/${{ github.base_ref }}"

Or as an action, from the Marketplace:

yaml
permissions:  contents: read  pull-requests: write   # so the run can comment its result on the pull requeststeps:  - uses: jkanselaar/[email protected]    with:      api-key: ${{ secrets.VALIDATOR_API_KEY }}   # optional; free tier without it

The changed Python is validated and offending lines are annotated on the diff, failing the job on syntax errors and unsafe patterns. Files the service refuses outright (over its 200 kB limit) are skipped with a warning rather than failing the run.

The run also leaves one comment on the pull request, edited in place on later pushes rather than repeated: what was accepted, what was repaired and how much of the day's allowance is left. Without pull-requests: write nothing is written and the job is unaffected; comment: "false" turns it off.

On the free tier the action keeps its key in the workflow cache, one per repository per day, so the allowance belongs to the repository rather than to the run. With api-key set the cache is skipped.

Pre-commit

yaml
repos:  - repo: https://github.com/jkanselaar/python-code-validator    rev: v1.22.0    hooks:      - id: python-code-validator

The client itself

validate.py is standard library only, so it also works as python validate.py file.py in a Makefile, a git hook or a container:

$ python3 validate.py service.py::error file=service.py,line=88,title=SyntaxError::invalid syntaxFAIL service.py score=0.66
0/1 files accepted

VALIDATOR_API_KEY is used when set; otherwise the client mints a free key — keeping it in VALIDATOR_KEY_FILE when that names a path, which is how a series of runs shares one allowance. VALIDATOR_URL points it at another deployment. VALIDATOR_SOURCE names the caller, which is only ever counted: a run inside a workflow says github-action by itself.

The badge

A repository whose Python is checked on every pull request can say so:

markdown
[![Python validated](https://img.shields.io/badge/python-validated-2ea44f?logo=python&logoColor=white)](https://api.statemind.ai/?src=badge)

[Python validated]

HTTP

bash
curl -s https://api.statemind.ai/v1/validate \  -H "Authorization: Bearer $VALIDATOR_API_KEY" \  -H 'content-type: application/json' \  -d '{"code": "def f(:\n    pass\n", "mode": "static"}'

mode is static, repair or execute; repair and execute need a configured key. Submitted code is not logged.

A refused call says what to do about it, so a caller with no operator to ask can resolve it itself:

json
{"error": "payment_required", "remedy": {"action": "upgrade_key", "hint": "A free key covers static only. …"}}

Paying for calls

A free key covers 25 static checks a day, and one address gets a few keys a day, so the allowance is a trial rather than a supply. Beyond it a key carries credits: a static check costs 1, a repair 3 and a sandboxed run 10, and an identical call repeated within ten minutes is answered from the first one for free.

Credits are bought with a card, without an invoice or anyone to ask:

bash
curl -s -X POST https://api.statemind.ai/v1/keys/checkout \  -H 'content-type: application/json' \  -d '{"api_key": "'"$VALIDATOR_API_KEY"'", "credits": 500}'

That answers with a Stripe Checkout page; the credits are on the key seconds after the card clears (500 credits is €10). An agent with a Gnosis wallet can instead pay in xDAI without a browser — GET /v1/pricing states both routes.

Examples

examples/ holds three files and the client to send them with: one that passes every check and still returns the wrong number, one the security policy refuses, and one that comes back accepted from the sandbox.

Licence

MIT.

来源:README.md,提交 6cff782

工具

3

validate_python

Validate Python

Check Python source without running it: parse, lint (ruff), type-check (mypy), AST security policy, credential scan. Safe on code you do not trust. Use it on every Python file you generated or edited, before writing it to disk. Alternatives: repair_python to get the corrected source instead of the diagnosis; execute_python to prove the code runs. Auth: a key is required. A free key covers this call, 25 per day, then HTTP 429; get one with POST /v1/keys. Credits are bought without an account, 1 per call: GET /v1/pricing says where to send the xDAI. Or pay for this one call with no key at all: call it without one and the result carries x402 payment requirements ($0.01 in USD Coin on eip155:8453); sign them and repeat the call with the payment in _meta['x402/payment']. Arguments: code: the whole file, 1..200000 bytes of UTF-8 measured after encoding (empty is refused with 400, larger with 413); a fragment is fine, but line and column numbers in the answer count from 1 in what you sent. language: must be 'python'; anything else is 400, and the field may be omitted. Of options only transpile_to (e.g. 'javascript', which returns a translated copy in transpiled) acts here; timeout_s, max_iterations, optimize, examples and expected_output need a pass that rewrites or runs the code, so send code alone. Ignored options are not refused, so a call that sets them looks like it worked; and code that does not parse is answered rather than refused: valid=false with the syntax error located, which is the point. Returns valid, score 0..1, diagnostics (rule, message, line, column), security findings, fixes, fixed_code and runtime; see outputSchema. The code and its verdict are retained to improve the service.

输入参数code必填optionslanguage

repair_python

Repair Python

Everything validation does, plus deterministic fixes: the corrected source comes back in fixed_code, and the original is kept whenever the fix cannot be proven safe. The code is still never run. Use it when validation failed and you want the fix rather than the diagnosis. Alternatives: validate_python when the diagnosis is enough; execute_python when the fix has to be proven to run. Auth: a key is required. This call needs a paid key and answers HTTP 402 without one. Credits are bought without an account, 3 per call: GET /v1/pricing says where to send the xDAI. Or pay for this one call with no key at all: call it without one and the result carries x402 payment requirements ($0.03 in USD Coin on eip155:8453); sign them and repeat the call with the payment in _meta['x402/payment']. Arguments: code: the whole file, 1..200000 bytes of UTF-8 measured after encoding (empty is refused with 400, larger with 413); a fragment is fine, but line and column numbers in the answer count from 1 in what you sent. language: must be 'python'; anything else is 400, and the field may be omitted. options.max_iterations (1..10, default 3) caps the fix/verify rounds: raise it for a file with several independent faults, leave it for a snippet. options.optimize (default false) additionally folds constants and drops dead code, and is only worth setting when you asked for a rewrite anyway. options.transpile_to (e.g. 'javascript') returns a translation of the *repaired* source in transpiled, not of what you sent. fixed_code is null when nothing could be proven safe to change, so treat null as 'no fix', not as an error. options.timeout_s, options.examples and options.expected_output do nothing here: nothing is run, so there is no clock, no stdout, and no way to check an example. Returns valid, score 0..1, diagnostics (rule, message, line, column), security findings, fixes, fixed_code and runtime; see outputSchema. The code and its verdict are retained to improve the service.

输入参数code必填optionslanguage

execute_python

Execute Python

Everything repair does, and then RUNS the code in a throwaway container — no network, read-only filesystem, killed at options.timeout_s — reporting exit code, stdout and stderr. Any '>>>' examples in the code are run too, and one that does not print what it says is an error the other tools cannot see. This is a side effect: do not submit code you do not want executed. Use it when you need proof that the code runs, or that it does what it says. Alternatives: validate_python for the diagnosis and repair_python for the fix, neither of which runs anything. Auth: a key is required. This call needs a paid key and answers HTTP 402 without one. Credits are bought without an account, 10 per call: GET /v1/pricing says where to send the xDAI. Or pay for this one call with no key at all: call it without one and the result carries x402 payment requirements ($0.1 in USD Coin on eip155:8453); sign them and repeat the call with the payment in _meta['x402/payment']. Arguments: code: the whole file, 1..200000 bytes of UTF-8 measured after encoding (empty is refused with 400, larger with 413); a fragment is fine, but line and column numbers in the answer count from 1 in what you sent. language: must be 'python'; anything else is 400, and the field may be omitted. options.max_iterations (1..10, default 3) caps the fix/verify rounds: raise it for a file with several independent faults, leave it for a snippet. options.optimize (default false) additionally folds constants and drops dead code, and is only worth setting when you asked for a rewrite anyway. options.transpile_to (e.g. 'javascript') returns a translation of the *repaired* source in transpiled, not of what you sent. fixed_code is null when nothing could be proven safe to change, so treat null as 'no fix', not as an error. options.timeout_s (seconds, default 5) is the wall clock for the run; the schema allows up to 60 but this deployment caps it at 30 and refuses a larger value with 400. options.expected_output compares stdout b

输入参数code必填optionslanguage

版本历史

1
  1. v1.22.0最新Sep 16, 2026

评分

已满足适用检查项的 100%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通必需25 分
  • 已声明认证方式必需10 分
  • 已列出工具必需15 分
  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分