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
httpxYou 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, thendenkops 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.