Skip to content

Decorators & registration

LangStitch describes an application as a set of decorated functions and classes. Each decorator records a lightweight spec on a process-global registry at import time; nothing heavy is instantiated until a node actually needs it.

Every decorator works bare or parameterized:

from langstitch import skill

@skill
def echo(text: str) -> str:
    return text

@skill(name="search", tools=["web"], tags=["retrieval"])
def web_search(query: str) -> list[str]:
    ...

The decorator catalog

Decorator Purpose
@graph_node Register a node handler (state -> dict).
@graph Register a graph builder (entrypoint=True for the root, parent=... for subgraphs).
@skill Register a reusable capability.
@input_guardrail / @output_guardrail Validate inbound requests / outbound responses.
@business_policy Register an organizational rule (evaluated by priority).
@persona Register an agent identity / system prompt.
@configuration Bind a section of application.yaml to a dataclass.
@langstitch_graph_server Turn a class into a runnable graph API server (protocol, port, name, properties).
@tool Register a callable an LLM can invoke (roles, tags, input_schema).
@worker_agent Register a delegatable sub-agent (role, tools, persona).
@langstitch_mcp_server Mark the MCP server class + transport (protocol, properties).
@mcp_tool Expose a callable as an MCP tool (name, roles, description).
@mcp_resource Expose a readable MCP resource (name, uri, mime_type).
@mcp_prompt Expose a reusable MCP prompt (name, description, arguments).

Worked example

from langstitch import (
    graph, graph_node, skill, persona,
    input_guardrail, output_guardrail, business_policy,
    tool, worker_agent, configuration,
    langstitch_graph_server, GraphBuilder, END,
)

@persona(role="assistant", tone="helpful, concise")
def assistant() -> str:
    return "You are a helpful LangStitch support assistant."

@tool(tags=["billing"], roles=["agent"])
def lookup_invoice(invoice_id: str) -> dict:
    """Fetch an invoice by id."""
    ...

@input_guardrail(description="Reject empty/oversized input.", action="block")
def non_empty(text: str) -> bool:
    return bool(text and 0 < len(text) <= 8000)

@business_policy(priority=100, description="Deny refunds over policy limit.")
def refund_limit(context: dict) -> dict:
    amount = context.get("amount", 0)
    return {"decision": "deny" if amount > 1000 else "allow"}

@graph_node(description="Answer the latest message.")
def respond(state: dict) -> dict:
    ...
    return {"response": "...", "messages": [...]}

@graph(name="main", entrypoint=True)
def main_graph() -> GraphBuilder:
    g = GraphBuilder("main")
    g.add_node("respond", respond)
    g.set_entry_point("respond")
    g.add_edge("respond", END)
    return g

@langstitch_graph_server(name="my-agent", protocol="http", port=8000)
class Server:
    """Graph API server."""

Registration is import-time

A decorator runs when its module is imported, so make sure each module is reachable from app/__init__.py. If a component is missing from langstitch info, its module almost certainly wasn't imported.