Drop-in Flask API for LLM agent access to SQLModel web apps
Project description
flask-fenrir
Drop-in Flask API for LLM agent access to SQLModel web apps. One install, two lines of code, one env var.
Install
uv add flask-fenrir
Usage
from flask_fenrir import create_fenrir_bp
app.register_blueprint(create_fenrir_bp(engine))
from flask_fenrir import secure_app
secure_app(app) # optional — adds basic auth to the whole app
dokku config:set myapp FENRIR_API_KEY=$(openssl rand -hex 32)
That's it. One env var for everything — bearer token for LLM agents, basic auth password for browser access.
What it does
Registers a /fenrir/ blueprint with four endpoints, all behind bearer token auth:
| Endpoint | Method | Description |
|---|---|---|
/fenrir/ |
GET | App info, FENRIR.md contents, table list with row counts |
/fenrir/schema |
GET | Full schema introspection (columns, types, PKs, FKs, indexes) |
/fenrir/query |
POST | Read-only SQL (SELECT / WITH ... SELECT), returns rows as JSON |
Authentication
FENRIR_API_KEY is the single secret for your app.
Fenrir endpoints (/fenrir/*) require Authorization: Bearer <key>.
secure_app(app) (optional) adds basic auth to all other routes — any username, password must match FENRIR_API_KEY. This replaces Dokku's http-auth so everything is managed in one place.
Both are skipped when FLASK_DEBUG is on. If the env var isn't set, everything returns 401/503 (fail closed).
# Options:
secure_app(app) # basic auth on all routes
secure_app(app, skip_paths=["/health", "/webhook"]) # skip specific paths
secure_app(app, api_key_auth={ # also accept a separate API key
"header": "X-API-Key",
"secret": os.getenv("API_KEY"),
})
FENRIR.md
Write a FENRIR.md at your project root with domain context for the LLM:
# My App
A brief description of what this app does.
## Domain concepts
- **Widget** — the core thing users create. Has a `status` field: draft, active, archived.
- **Team** — groups of users. The `owner_id` is always a user, not another team.
## Useful queries
- Active widgets by team: `SELECT t.name, COUNT(*) FROM widget w JOIN team t ON ...`
The /fenrir/ endpoint serves this content so the LLM can understand your app before querying it.
Generating FENRIR.md with an LLM
If the app is running locally with flask-fenrir enabled, you can have an LLM generate the file. Run this prompt from the app's project root:
This app uses flask-fenrir — a small Flask blueprint that exposes the
database to LLM agents via REST (schema introspection, read/write SQL).
It serves a FENRIR.md file at GET /fenrir/ to give the LLM domain
context it can't infer from the schema alone. That's the file you're
writing now.
The API is at /fenrir/.
Read the codebase to understand the app's models, business logic, and
domain. Then hit GET /fenrir/schema and POST /fenrir/query to see what
the database actually looks like from Fenrir POV.
Compare what you learned from the code with what the schema and data
show. Write a FENRIR.md that bridges the gap — focus on things an LLM
querying the database blind would NOT be able to figure out from the
schema alone:
- What the app actually does (one-liner)
- What each table/field means in business terms
- Non-obvious values: enums, flags, status codes, soft deletes, fields
whose names are misleading
- How tables relate to each other (especially implicit relationships
not captured by foreign keys)
- Common useful queries for an operator
- Gotchas and edge cases
Don't repeat what's already obvious from column names and types.
An LLM can read a schema — FENRIR.md should add the context it can't
infer.
Ask me questions if anything in the code or data is unclear. Don't
guess at business logic — it's better to ask than to document something
wrong.
This file will need to be maintained alongside the codebase. Keep it concise
and structured so it's easy to update when models change. If a section
would go stale quickly, leave it out or note what to watch for.
Row limit
By default, /fenrir/query caps results at 1000 rows. Override it:
app.register_blueprint(create_fenrir_bp(engine, row_limit=500))
Dependencies
Just Flask (≥3.0) and SQLAlchemy (≥2.0). Works with any Flask + SQLAlchemy app — SQLModel not required at runtime.
Release
# 1. Bump version
uv version --bump minor # or --bump patch / --bump major
# 2. Clean old artifacts and build
rm -rf dist/*
uv build
# 3. Publish to PyPI
uv publish dist/* --token "$PYPI_TOKEN"
License
MIT
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
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_fenrir-0.4.0.tar.gz.
File metadata
- Download URL: flask_fenrir-0.4.0.tar.gz
- Upload date:
- Size: 9.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8dc9ce1eeaae06da511a73a1437de7ea9b37db49425e0108bc0b5e8074c81352
|
|
| MD5 |
906a2b4d2f4321d0c1f4801ed26653be
|
|
| BLAKE2b-256 |
2c9f3f49f09d7e19c67f814bcc41f7ec50d1b9d67ba4d1e0c3c8f851e5a06327
|
File details
Details for the file flask_fenrir-0.4.0-py3-none-any.whl.
File metadata
- Download URL: flask_fenrir-0.4.0-py3-none-any.whl
- Upload date:
- Size: 7.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d076bd9248191f8181e663f11c2f1c24c43c2cfcdaf41a0c478253894a2a8d52
|
|
| MD5 |
94a645bea769c79d3035977f1d339cff
|
|
| BLAKE2b-256 |
280fb58bb0108515a8440158c415849175fcedc1d5a409635f0d4d5c8bc05fc3
|