scriptling.runtime.http
Overview
The scriptling.runtime.http library lets a setup script register routes, middleware, and static file serving, then build JSON/HTML/text/redirect responses from handlers. Reach for it when you want a Scriptling script to act as (or contribute routes to) an HTTP server.
Available Functions
| Function | Description |
|---|---|
get(path, handler) |
Register a GET route |
post(path, handler) |
Register a POST route |
put(path, handler) |
Register a PUT route |
patch(path, handler) |
Register a PATCH route |
delete(path, handler) |
Register a DELETE route |
route(path, handler, methods=["GET", "POST", "PUT", "DELETE"]) |
Register a route for multiple methods |
middleware(handler) |
Register global middleware |
not_found(handler) |
Register a custom 404 handler |
static(path, directory) |
Register static file serving |
websocket(path, handler) |
Register a WebSocket route |
json(status_code, data) |
Create a JSON response |
html(status_code, content) |
Create an HTML response |
text(status_code, content) |
Create a plain text response |
redirect(location, status=302) |
Create a redirect response |
parse_query(query_string) |
Parse a URL query string |
Route paths support wildcards: {name} matches one path segment and {name...} matches the rest of the path. Values are read in the handler with request.path_param(name) — see Request Object below. Handler references may use dotted module paths: a handler in routes/me.py is referenced as "routes.me.get_user" — the module part is everything before the last dot.
Functions
get(path, handler)
Registers a GET route.
Parameters:
path(str): URL path (e.g."/api/users"). Supports{name}and{name...}wildcards.handler(str): Handler function as"library.function".
Returns: None
import scriptling.runtime as runtime
runtime.http.get("/users", "handlers.list_users")
runtime.http.get("/users/{id}", "handlers.get_user")post(path, handler)
Registers a POST route.
Parameters:
path(str): URL path.handler(str): Handler function as"library.function".
Returns: None
import scriptling.runtime as runtime
runtime.http.post("/users", "handlers.create_user")put(path, handler)
Registers a PUT route.
Parameters:
path(str): URL path.handler(str): Handler function as"library.function".
Returns: None
import scriptling.runtime as runtime
runtime.http.put("/users/{id}", "handlers.update_user")patch(path, handler)
Registers a PATCH route.
Parameters:
path(str): URL path.handler(str): Handler function as"library.function".
Returns: None
import scriptling.runtime as runtime
runtime.http.patch("/users/{id}", "handlers.patch_user")delete(path, handler)
Registers a DELETE route.
Parameters:
path(str): URL path.handler(str): Handler function as"library.function".
Returns: None
import scriptling.runtime as runtime
runtime.http.delete("/users/{id}", "handlers.delete_user")route(path, handler, methods=["GET", "POST", "PUT", "DELETE"])
Registers a route for multiple HTTP methods.
Parameters:
path(str): URL path.handler(str): Handler function as"library.function".methods(list, optional): HTTP methods to match. Default:["GET", "POST", "PUT", "DELETE"](all of the standard methods).
Returns: None
import scriptling.runtime as runtime
runtime.http.route("/users/{id}", "handlers.user_resource", methods=["GET", "PUT", "DELETE"])middleware(handler)
Registers global middleware that runs before every route handler — and, when the protocol endpoints are enabled, before the /mcp, /json-rpc and WebSocket handlers too. The middleware receives the request and should return None to continue to the handler, or a response dict to short-circuit the request.
The middleware can pass data to the handler by writing to request.context, a dict that starts empty on every request. HTTP route handlers read it straight off their request object; MCP tools and JSON-RPC methods read it with request_context() / get_request() (see the MCP tool and JSON-RPC pages). Keep plain data in it: request_context() copies dicts and lists deeply so concurrent handlers cannot race through them, but instances pass by reference (they may hold resources), so a shared instance is still shared. It can also register MCP entries for the life of the request — per-user tools, resources and prompts — with the request-scoped registration functions.
Parameters:
handler(str): Middleware function as"library.function".
Returns: None
import scriptling.runtime as runtime
def auth(request):
if "authorization" not in request.headers:
return runtime.http.json(401, {"error": "Unauthorized"})
request.context["user"] = "alice" # Readable by the handler
return None # Continue to handler
runtime.http.middleware("handlers.auth")not_found(handler)
Registers a custom 404 Not Found handler. The handler receives the request object and should return a response. It is called when no route matches the request path, or when the --web-root directory is configured but the requested file is not found.
Parameters:
handler(str): Handler function as"library.function".
Returns: None
import scriptling.runtime as runtime
def not_found(request):
return runtime.http.html(404, f"<h1>404 - {request.path} not found</h1>")
runtime.http.not_found("handlers.not_found")static(path, directory)
Registers static file serving under a URL path prefix.
Parameters:
path(str): URL path prefix (e.g."/assets").directory(str): Local directory to serve.
Returns: None
import scriptling.runtime as runtime
runtime.http.static("/assets", "./public/assets")websocket(path, handler)
Registers a WebSocket route. The handler receives a WebSocketClient object and runs for the lifetime of the connection: it should loop while client.connected() and use client.receive() / client.send().
Parameters:
path(str): URL path for the WebSocket endpoint (e.g."/ws").handler(str): Handler function as a"library.function"string.
Returns: None
import scriptling.runtime as runtime
runtime.http.websocket("/chat", "handlers.chat_handler")
# In handlers.py:
def chat_handler(client):
client.send("Welcome!")
while client.connected():
msg = client.receive(timeout=60)
if msg:
client.send(f"Echo: {msg}")WebSocketClient Object
The object passed to a websocket() handler:
| Member | Description |
|---|---|
connected() |
Returns True while the connection is open. |
receive(timeout=30) |
Receives the next message, blocking up to timeout seconds. |
send(text) |
Sends a text message. |
send_binary(data) |
Sends a list of byte values (0-255) as a binary message. |
close() |
Closes the connection. |
remote_addr |
The client’s remote address as a str. |
This is the server-side counterpart to the client documented on the scriptling.net.websocket page: the two expose the same connected()/receive()/send()/send_binary()/close()/remote_addr surface. Use scriptling.net.websocket.is_text() / is_binary() to inspect a received message’s type.
json(status_code, data)
Creates a JSON response.
Parameters:
status_code(int): HTTP status code.data(any): Data to serialize as JSON.
Returns: dict: response dict compatible with the server.
import scriptling.runtime as runtime
def list_users(request):
users = runtime.kv.default.get("users", default=[])
return runtime.http.json(200, {"users": users})html(status_code, content)
Creates an HTML response.
Parameters:
status_code(int): HTTP status code.content(str): HTML content.
Returns: dict: response dict compatible with the server.
import scriptling.runtime as runtime
runtime.http.html(200, "<h1>Hello</h1>")text(status_code, content)
Creates a plain text response.
Parameters:
status_code(int): HTTP status code.content(str): Text content.
Returns: dict: response dict compatible with the server.
import scriptling.runtime as runtime
runtime.http.text(200, "pong")redirect(location, status=302)
Creates a redirect response.
Parameters:
location(str): URL to redirect to.status(int, optional): HTTP status code. Default:302.
Returns: dict: response dict compatible with the server.
import scriptling.runtime as runtime
runtime.http.redirect("/login")parse_query(query_string)
Parses a URL query string.
Parameters:
query_string(str): Query string to parse.
Returns: dict: key-value pairs.
import scriptling.runtime as runtime
params = runtime.http.parse_query("page=2&limit=10")
print(params["page"]) # "2"Request Object
Handlers receive a Request object with these fields:
method(str): HTTP method.path(str): Request path.body(str): Request body.headers(dict): Request headers (lowercase keys).query(dict): Query parameters.path_params(dict): Path parameters captured from route wildcards.remote_addr(str): Remote address of the client.context(dict): Starts empty on every request; middleware can write to it (e.g.request.context["user"] = nameafter authenticating) and the handler reads it back. Per-request only — not related to the persistent KV store.
Methods:
path_param(name, default=None): get a path parameter captured from a route wildcard ("/api/users/{id}"capturesid), percent-decoded.query_param(name, default=None): get the first value of a query parameter.header(name, default=None): get a request header; names are case-insensitive.json(): parse the body as JSON.
def get_user(request):
user_id = request.path_param("id") # from "/api/users/{id}"
page = request.query_param("page", "1")
token = request.header("Authorization")
data = request.json()
return runtime.http.json(200, {"user_id": user_id})Examples
Basic routes with a 404 handler
# setup.py
import scriptling.runtime as runtime
runtime.http.get("/users", "handlers.list_users")
runtime.http.post("/users", "handlers.create_user")
runtime.http.middleware("handlers.auth")
runtime.http.not_found("handlers.not_found")# handlers.py
import scriptling.runtime as runtime
def list_users(request):
users = runtime.kv.get("users", default=[])
return runtime.http.json(200, {"users": users})
def create_user(request):
data = request.json()
users = runtime.kv.get("users", default=[])
users.append(data)
runtime.kv.set("users", users)
return runtime.http.json(201, {"user": data})Routes are registered during setup script execution. Use --web-root <dir> to serve static files from the CLI; unmatched requests fall through to the not_found handler.
Security Considerations
This is an extended library, requiring registration in Go, see Library Registration.
scriptling.runtime.http does not make outbound requests: it registers routes and handlers that turn the embedding process into (or adds to) an HTTP server, exposing a network listener. The risk shape is therefore about what gets exposed: every registered route, middleware, and static directory becomes reachable by anyone who can reach the listening address. Review handler logic for authorization, and be deliberate about what static() directories you expose. For a full risk breakdown across all libraries, see the Security Guide.
See Also
- scriptling.runtime: background tasks,
start_server(), and the rest of the runtime namespace - scriptling.runtime.jsonrpc: JSON-RPC 2.0 server sharing the same handler model
- scriptling.runtime.kv: share state across HTTP handlers
- Security Guide