Blog · 2026-08-06

Build a Python agent backend on DenkOps

Ship a FastAPI backend your agent calls over HTTP, with auth by default, a Python KV store, write-only secrets, and traces.

To build a Python agent backend on DenkOps, write a FastAPI app with the endpoints your agent calls, use the Python denkops store for state, keep secrets in write-only env vars, and deploy with one command. You supply main.py and requirements.txt; the platform wraps it with a public /health, auth by default, and request traces. This is the paved path for HTTP APIs, webhooks, and workers, not for MCP connectors.

Should I write an MCP connector or an HTTP backend?

Be clear on which one you need, because the answer changes the language.

If you want a tool an assistant installs and calls directly, that is an MCP connector, and the paved path for that is the Bun defineMcp SDK. There is no Python defineMcp. Do not reach for Python expecting one.

Python shines for the other half: FastAPI services your agent calls over HTTP, webhook receivers, and background workers. Those get the same platform defaults as any Bun app: auth by default, the egress allowlist, write-only secrets, and traces with a code versus I/O split. So use Bun for the connector surface, and Python where FastAPI, its data libraries, or your existing code already live.

What does the app look like?

A single main.py exporting a FastAPI app. Here is an endpoint an agent calls to enrich a company, cached so repeat calls are cheap:

# main.py
import os
import httpx
from fastapi import FastAPI
from pydantic import BaseModel
from denkops import store

app = FastAPI()


class Lookup(BaseModel):
    company: str


@app.post("/enrich")
async def enrich(req: Lookup):
    cached = store.get(f"enrich:{req.company}")
    if cached is not None:
        return {"source": "cache", "data": cached}
    async with httpx.AsyncClient() as client:
        resp = await client.get(
            "https://api.example.com/company",
            params={"name": req.company},
            headers={"authorization": f"Bearer {os.environ['ENRICH_API_KEY']}"},
        )
    store.set(f"enrich:{req.company}", resp.text, ttl_seconds=3600)
    return {"source": "live", "data": resp.text}

Your requirements.txt lists what you import beyond FastAPI:

fastapi
httpx

You do not write a /health route, an auth check, or server boilerplate. The wrapper mounts your app, adds /health, and enforces the Bearer key on everything else.

How do I store state?

Use the Python denkops store, a key-value store that survives redeploys. The surface is small: get, set, delete, and list.

from denkops import store

store.set("job:42", "queued")
store.set("otp:alice", "839201", ttl_seconds=300)   # expires on its own
store.get("job:42")                                   # "queued" or None
store.list("job:")                                    # keys under a prefix
store.delete("job:42")

Values are strings, so serialize structured data with json.dumps. Pass ttl_seconds when a value should expire, such as a cache entry or a one-time code. Keys are yours to namespace; a prefix like enrich: keeps one feature's keys separate and makes list useful.

How do I keep secrets and outbound calls safe?

Two defaults do the work. First, secrets go in env vars that are write-only after you save them: your code reads os.environ["ENRICH_API_KEY"] at run time, and nobody, including an agent operating the project, can read the value back out of the dashboard.

Second, outbound calls are allowlisted. Your app only reaches the hosts you approve, so a mistaken or coerced call to somewhere else is refused. Add api.example.com to the allowlist and the httpx call above works; leave an unexpected host off it and the call does not leave the box.

Both pair with auth by default: every route needs the app's API key except /health and any path you deliberately make public.

How do I deploy it?

The app runs in an always-on slot, so there are no cold starts between agent calls. Two ways to ship:

  • Claude Code: install the DenkOps plugin, say "log in to DenkOps" and approve once in the browser, then "deploy on DenkOps" from the project directory.
  • CLI: denkops login, then denkops deploy.

Either returns a live HTTPS URL and an API key. Point your agent at the URL, send the key as a Bearer token, and read the code versus I/O traces to see where each request spends its time. See API hosting and the docs for the full surface.

FAQ

Is there a Python SDK for building MCP connectors?

No. `defineMcp` is Bun and TypeScript only. Python is for FastAPI HTTP backends, webhooks, and workers your agent calls over HTTP. Those get the same auth, egress allowlist, store, and traces, but the connector tool surface itself is written in Bun.

What can the Python denkops store do?

It is a key-value store with `get`, `set`, `delete`, and `list(prefix)`. Values are strings, and `set` accepts `ttl_seconds` for entries that should expire. It persists on a durable disk and survives redeploys.

Do I need to write a health check or auth middleware?

No. The template adds a public `/health` and enforces the app's API key on every other route by default. You write only your endpoints; mark a path public when an external caller needs it without the key.

Ship it yourself: bunx denkops deploy or say "deploy on DenkOps" from your coding agent.

Start on DenkOps →

← Give your support team a connector to your helpdesk · A CRM connector your sales team can talk to →