Flask Production MCP
A static production-readiness auditor for Flask applications, usable three ways:
- a CLI (
flask-production-mcp audit) for local runs and scripts - a pre-commit hook and a GitHub Action so it runs at the right moments
- an MCP server so an AI coding agent can call it while building
It answers "is this Flask app ready to deploy?" — not just "does it have security bugs?" — across nine categories:
| Category | What it checks |
|---|---|
| flask | app-factory / blueprint discovery, route inventory, duplicate routes, debug mode enabled |
| architecture | extensions bound at import time, module-level Flask() beside a factory, hardcoded SECRET_KEY fallback, db.create_all() instead of migrations, unguarded app.run(), unregistered blueprints |
| templates | POST forms with no CSRF field, | safe / {% autoescape false %} on dynamic data, url_for() to an endpoint that doesn't exist |
| deployment | Dockerfile running as root, dev server as the container command, base image on :latest, debug mode via env, secrets baked into an image/compose file, DB port published to the host, Gunicorn reload = True, dev server in a Procfile/entrypoint, committed .env, Nginx (server_tokens, missing forwarded headers, client_max_body_size, HTTP-only edge) |
| testing | no test suite at all, a low line-rate in an existing coverage.xml, a test count that looks thin next to the number of routes |
| security | eval/exec, pickle loads, hardcoded secrets, missing auth on sensitive routes, missing rate limiting, debug config — plus Bandit, folded in and de-duplicated |
| database | SQLAlchemy models / relationships / indexes, raw SQL, missing indexes on filtered columns, likely N+1 access |
| dependencies | known CVEs via pip-audit (PyPI + OSV) — requirements*.txt, or uv.lock / poetry.lock / Pipfile.lock, or the exact-pinned deps in a bare pyproject.toml |
| code_quality | bare/broad except, print(), breakpoint(), assert in app code, TODO/FIXME (test modules excluded) |
All analysis is static. The target application is never imported or executed; virtual-environments, caches and build dirs are skipped.
Findings: blockers vs. advisories vs. notes
Every finding has a severity and a confidence. The audit sorts them:
| Tier | Rule | Gates a release? |
|---|---|---|
| blockers | high-confidence critical/high | yes |
| advisories | other critical/high/medium — needs a judgement call | no (configurable) |
| notes | low / info cleanups | no |
The overall_score is confidence-weighted: a confirmed critical bites
hard, a long tail of "consider an index" guesses barely moves it. A project
is production_ready when it has no blockers and no single category has
collapsed below the score floor (default 50).
Install
pip install flask-production-mcp
# with the dependency-CVE and Bandit scanners:
pip install "flask-production-mcp[scanners]"
# or, from a checkout:
uv sync --extra scanners
Python 3.11+. The base install is MCP + the static analyzers. The
scanners extra adds pip-audit (needs network) and bandit; without
it the audit reports those scans as skipped rather than failing.
CLI
flask-production-mcp audit path/to/your/flask/app
Flask Production Audit -- /path/to/app
------------------------------------------------------------
Overall score 71/100 NOT READY
flask 100 clean
architecture 96 1 finding
templates 55 1 finding (1 blocker)
deployment 100 clean
testing 92 1 finding
security 100 clean
database 76 6 findings
dependencies 100 clean
code_quality 79 7 findings
BLOCKERS (1)
x [TMPL-CSRF-001] templates app/templates/admin/products.html:242
POST form has no CSRF token field
-> Add a hidden CSRF field inside the form ...
Result: NOT production ready
- 1 blocker(s) in templates
| Flag | Effect |
|---|---|
--json |
emit the full JSON result |
--github |
emit GitHub Actions ::error / ::warning annotations + job summary |
--fail-on {blockers,advisories,any,never} |
what makes exit code 1 |
--skip-deps |
skip the CVE scan (no network) |
--skip-bandit |
skip the Bandit scan |
--config FILE |
use a specific config TOML |
-q |
print blockers only |
Exit codes: 0 pass · 1 fail (per --fail-on) · 2 bad usage.
Configuration
flask-production.toml in the project root, or [tool.flask-production]
in pyproject.toml. See flask-production.toml.example.
fail_on = "blockers"
category_floor = 50
scan_dependencies = true
run_bandit = true
ignore = ["DB-PERF-001"] # drop rules by id
select = [] # if set, run ONLY these
[severity]
"ARCH-SEC-001" = "high" # re-grade a rule for this project
pre-commit
# .pre-commit-config.yaml
repos:
- repo: https://github.com/yemibalogun/flask-production-mcp
rev: v0.1.0
hooks:
- id: flask-production-audit
The hook runs offline (--skip-deps --skip-bandit -q) and fails the commit
on blockers only. Override args: to change that.
GitHub Action
# .github/workflows/audit.yml
jobs:
flask-production-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: yemibalogun/flask-production-mcp@v0.1.0
with:
path: .
fail-on: blockers # or advisories / any / never
Findings appear as inline annotations on the PR and in the job summary.
MCP server
flask-production-mcp serve # stdio
Register with an MCP client (Claude Code / Desktop):
{
"mcpServers": {
"flask-production": {
"command": "flask-production-mcp",
"args": ["serve"]
}
}
}
Then: "Run a production audit on /path/to/my_flask_app and list the blockers."
Tools
| Tool | Purpose |
|---|---|
audit_flask_production |
Start here. Unified audit: overall_score, per-category score, production_ready + blocking_reasons, blockers / advisories / notes. |
inspect_flask_project |
Structure only: file counts, blueprints, routes, indicators. |
audit_flask |
Flask route/debug findings. |
audit_architecture |
Flask-architecture rules. |
audit_templates |
Jinja/HTML template findings. |
audit_deployment |
Dockerfile / compose / Gunicorn / Nginx / Procfile findings. |
audit_testing |
Test-suite presence and rough adequacy. |
audit_security |
Security audit (incl. Bandit). |
audit_database |
Database architecture + performance findings. |
audit_dependencies |
Dependency CVE scan. |
audit_code_quality |
Production code-quality findings. |
Every audit_* tool returns the same envelope (success, project_path,
score, summary, findings, recommendations, errors).
Development
uv run pytest
uv run ruff check src tests
uv run mypy src
src/flask_production_mcp/
├── cli.py # audit / serve subcommands
├── config.py # flask-production.toml loader + finding policy
├── server.py # MCP server, registers the tools
├── models/findings.py # Finding / AuditSummary / AuditResult
├── analyzers/
│ ├── base.py # scoring (weighted), classification
│ ├── exclusions.py # shared file-walk + exclusion rules
│ ├── flask.py # discovery + analyze_flask
│ ├── architecture.py # Flask-architecture rules
│ ├── templates.py # Jinja/HTML rules
│ ├── deployment.py # Dockerfile / compose / Gunicorn / Nginx rules
│ ├── testing.py # test-suite health
│ ├── security.py # analyze_security
│ ├── bandit_scan.py # Bandit integration
│ ├── database.py # analyze_database
│ ├── dependencies.py # pip-audit (requirements / lock / pyproject pins)
│ ├── code_quality.py # analyze_code_quality_file
│ └── production.py # analyze_production (unified, parallel)
└── tools/ # one thin MCP wrapper per analyzer
Scope
Everything is static — the app is never run. So it does not do
performance profiling or dynamic/runtime security testing (both need a
live app + database), and the testing category reads an existing
coverage.xml rather than producing one. Unpinned dependencies in a bare
pyproject.toml can't be resolved to exact versions without installing —
add a lock file for full CVE coverage.
Metadata
Release files for flask-production-mcp 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| flask_production_mcp-0.1.0.tar.gz | 77.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| flask_production_mcp-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 173.2 kB
Release files / flask_production_mcp-0.1.0.tar.gz
| Download URL | flask_production_mcp-0.1.0.tar.gz |
|---|---|
| Size | 77.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f91eec49ff5a85894a9461c7bd7c8693d068c7da87d4a4e8664a2c0fbe0085cb
|
|
BLAKE2b-256 checksum How to use checksums |
df320229ac06eeb18bfb81c590df7a1ec01f417cb8c4287a7ebace8ba942067c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency logRelease files / flask_production_mcp-0.1.0-py3-none-any.whl
| Download URL | flask_production_mcp-0.1.0-py3-none-any.whl |
|---|---|
| Size | 95.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1d13ab58fbd5b2ddb3e293a1355ca3eb32857c35e8f231e307abd57582c2984d
|
|
BLAKE2b-256 checksum How to use checksums |
2e2eda046f448dc4e2663e2c325b5cc8bf1250e5ebb20fb6a0bbcb099c65038a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency logSecurity for flask-production-mcp 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for flask_production_mcp-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.