Write a handler
Most deployments use the handler preset. Choose one runtime and place its required entry file at
the top level of the deployment directory. To run your own process instead of a handle function,
see command deployments.
Python 3.12
Section titled “Python 3.12”Create handler.py with a callable named handle.
def handle(input: dict): return {"received": input}TypeScript on Node.js 22
Section titled “TypeScript on Node.js 22”Create handler.ts and export a named synchronous or asynchronous handle function.
import { log, secret } from '@siloga/magmell-agent'
export async function handle(input: Record<string, unknown>) { log('creating greeting', { name: input.name }) return { message: `Hello, ${String(input.name ?? 'world')}!`, hasToken: secret('MODEL_API_KEY') !== undefined, }}@siloga/magmell-agent supplies in-handler helpers and TypeScript types. Declare the exact version
reported by the gateway as a devDependency; Magmell supplies its protected runtime copy during
the build. See Agent SDK for the helper API and availability details.
{ "engines": { "node": ">=22 <23" }, "devDependencies": { "@siloga/magmell-agent": "0.1.0" }}Commit the npm lockfile v3 as package-lock.json. A handler with no third-party packages can omit
both package files. Node deployments do not accept Python requirements.
When creation_enabled is true, deploy it explicitly:
magmell deploy ./greeter \ --name greeter \ --version 1 \ --runtime node-22 \ --waitInput and output
Section titled “Input and output”The handler receives the run’s input object. If a run omits input, the runtime passes an empty
dictionary. Return a JSON value: Python uses json.dumps, and Node uses JSON serialization after
awaiting the handler. Circular values, BigInt, non-finite numbers, and other non-JSON values fail
the run.
def handle(input: dict) -> dict: numbers = input.get("numbers", []) return {"sum": sum(numbers), "count": len(numbers)}An uncaught exception fails the run. Magmell captures its type and message for the run record while protecting secrets from gateway logs.
Python package requirements
Section titled “Python package requirements”Add a conventional requirements file beside the handler:
httpx==0.28.1pydantic==2.11.7Then deploy the directory normally:
magmell deploy ./summarizer \ --name summarizer \ --version 1 \ --waitPin exact versions for repeatable production builds. Standard-library-only handlers do not need
requirements.txt. For system packages and other build commands, see
setup and dependencies.
Initialize reusable resources once
Section titled “Initialize reusable resources once”The runtime imports the handler module once when it starts. Put reusable client initialization at
module scope and invocation-specific work inside handle.
import httpx
client = httpx.Client(timeout=20)
def handle(input: dict) -> dict: response = client.get(input["url"]) return {"status": response.status_code}Do not keep invocation-specific secrets or mutable request state in module globals.
Emit structured telemetry
Section titled “Emit structured telemetry”import siloga_agent
def handle(input: dict) -> dict: siloga_agent.log("starting analysis", document_id=input.get("id")) with siloga_agent.span("analyze"): result = analyze(input["text"]) siloga_agent.metric("input.characters", len(input["text"])) siloga_agent.event("analysis.complete", confidence=result["confidence"]) return resultEvents emitted before an exception are retained, which makes them useful for diagnosing failed runs. See events and logs.
The equivalent TypeScript helpers come from @siloga/magmell-agent:
import { event, log, metric, span } from '@siloga/magmell-agent'
export async function handle(input: { text?: string }) { log('starting analysis') return span('analyze', async () => { const result = await analyze(input.text ?? '') metric('input.characters', (input.text ?? '').length) event('analysis.complete', { confidence: result.confidence }) return result })}Read secrets explicitly
Section titled “Read secrets explicitly”import siloga_agent
def handle(input: dict) -> dict: api_key = siloga_agent.secret("MODEL_API_KEY") if not api_key: raise RuntimeError("MODEL_API_KEY is not configured") return call_model(api_key, input)Secrets are scoped to the current invocation. They are not environment variables and should never be returned or included in emitted event data.
On Node.js 22, secret('MODEL_API_KEY') returns string | undefined. The runtime uses
AsyncLocalStorage so concurrent asynchronous invocations do not share secret or event context.