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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flask_production_mcp-0.1.0.tar.gz.
File metadata
- Download URL: flask_production_mcp-0.1.0.tar.gz
- Upload date:
- Size: 77.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f91eec49ff5a85894a9461c7bd7c8693d068c7da87d4a4e8664a2c0fbe0085cb
|
|
| MD5 |
2c5f18cd0dd34c0ac234e6cdc7867ce7
|
|
| BLAKE2b-256 |
df320229ac06eeb18bfb81c590df7a1ec01f417cb8c4287a7ebace8ba942067c
|
Provenance
The following attestation bundles were made for flask_production_mcp-0.1.0.tar.gz:
Publisher:
publish.yml on yemibalogun/flask-production-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flask_production_mcp-0.1.0.tar.gz -
Subject digest:
f91eec49ff5a85894a9461c7bd7c8693d068c7da87d4a4e8664a2c0fbe0085cb - Sigstore transparency entry: 2732667448
- Sigstore integration time:
-
Permalink:
yemibalogun/flask-production-mcp@7f7f034bcb9538ab371031457f4c85a66fb3a9be -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/yemibalogun
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7f7f034bcb9538ab371031457f4c85a66fb3a9be -
Trigger Event:
push
-
Statement type:
File details
Details for the file flask_production_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: flask_production_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 95.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d13ab58fbd5b2ddb3e293a1355ca3eb32857c35e8f231e307abd57582c2984d
|
|
| MD5 |
08bb7e8704a850df52a67932b663a732
|
|
| BLAKE2b-256 |
2e2eda046f448dc4e2663e2c325b5cc8bf1250e5ebb20fb6a0bbcb099c65038a
|
Provenance
The following attestation bundles were made for flask_production_mcp-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on yemibalogun/flask-production-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flask_production_mcp-0.1.0-py3-none-any.whl -
Subject digest:
1d13ab58fbd5b2ddb3e293a1355ca3eb32857c35e8f231e307abd57582c2984d - Sigstore transparency entry: 2732667482
- Sigstore integration time:
-
Permalink:
yemibalogun/flask-production-mcp@7f7f034bcb9538ab371031457f4c85a66fb3a9be -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/yemibalogun
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7f7f034bcb9538ab371031457f4c85a66fb3a9be -
Trigger Event:
push
-
Statement type: