Skip to main content

langchain-runner

Zero-configuration way to expose your LangChain/LangGraph agents as autonomous services.

Simply wrap your agent with Runner and it becomes a web service with webhook endpoints, cron schedules, and HTTP triggers—no infrastructure code needed.

PyPI version License: MIT Python 3.10+

Installation

pip install langchain-runner

Quick Start

from langchain_runner import Runner
from langchain.agents import create_agent

# Your LangChain agent (LangChain v1+)
agent = create_agent(
    model="anthropic:claude-sonnet-4-5-20250929",  # or "openai:gpt-4.1"
    tools=[],
    system_prompt="You are a helpful assistant.",
)

# Wrap it with Runner
runner = Runner(agent)

@runner.cron("0 9 * * *")
async def morning_report():
    return "Generate the daily sales report"

@runner.webhook("/stripe")
async def on_payment(payload: dict):
    return f"Process payment: {payload['type']}"

@runner.trigger("/ask")
async def ask(question: str):
    return question

runner.serve()

Features

  • Webhook triggers - Receive external webhooks (GitHub, Stripe, Slack, etc.)
  • Cron triggers - Schedule agent runs with cron expressions
  • HTTP triggers - Simple POST endpoints to invoke your agent
  • Background execution - Agent runs don't block HTTP responses
  • Run tracking - Monitor agent run status and results
  • Zero config - Works out of the box with sensible defaults

Endpoints

Once your runner is serving, you get these endpoints automatically:

Endpoint Method Description
/health GET Health check
/triggers GET List registered triggers
/trigger/{name} POST Invoke a registered trigger
/webhook/{name} POST Receive webhook payload
/runs GET List recent runs
/runs/{id} GET Get run status and result

Triggers

HTTP Trigger

Simple endpoint to invoke your agent with a custom message:

@runner.trigger("/ask")
async def ask(question: str):
    return question
curl -X POST http://localhost:8000/trigger/ask \
  -H "Content-Type: application/json" \
  -d '{"question": "What is the weather today?"}'

Response:

{"run_id": "abc123", "status": "pending", "message": "Run started"}

Webhook Trigger

Receive webhooks from external services (GitHub, Stripe, Clerk, etc.) and transform them into agent inputs:

@runner.webhook("/clerk")
async def on_clerk_user(payload: dict):
    event_type = payload.get("type")
    user = payload.get("data", {})
    return f"Handle Clerk event '{event_type}' for user: {user.get('email_addresses', [{}])[0].get('email_address')}"

Your webhook URL will be:

https://<your-domain>/webhook/<name>

For the example above: https://your-server.com/webhook/clerk

Setting Up Webhooks

1. For local development, expose your server with ngrok:

# Start your runner
python my_agent.py  # Starts server on port 8000

# In another terminal, expose it
ngrok http 8000
# Output: https://abc123.ngrok.io -> http://localhost:8000

Your webhook URL is now: https://abc123.ngrok.io/webhook/clerk

2. Configure the external service (example: Clerk):

  1. Go to your Clerk Dashboard → Webhooks
  2. Click "Add Endpoint"
  3. Enter your webhook URL: https://abc123.ngrok.io/webhook/clerk
  4. Select events to subscribe to: user.created, user.updated, etc.
  5. Save the endpoint

3. For production, deploy your runner to a cloud provider and use that URL:

https://my-agent.railway.app/webhook/clerk

Cron Trigger

Schedule agent runs with cron expressions:

@runner.cron("0 9 * * *")  # Every day at 9am
async def daily_summary():
    return "Generate daily standup summary"

@runner.cron("0 9 * * 1-5")  # Weekdays at 9am
async def weekday_task():
    return "Process weekday reports"

@runner.cron("*/15 * * * *")  # Every 15 minutes
async def check_alerts():
    return "Check for new alerts"

Creating Your Agent

The recommended way to create agents is using LangChain v1+'s create_agent function:

from langchain.agents import create_agent

# Using model string (simple)
agent = create_agent(
    model="anthropic:claude-sonnet-4-5-20250929",  # or "openai:gpt-4.1"
    tools=[my_tool],
    system_prompt="You are a helpful assistant.",
)
runner = Runner(agent)

You can also use a model instance for more control:

from langchain.agents import create_agent
from langchain_anthropic import ChatAnthropic

model = ChatAnthropic(
    model="claude-sonnet-4-5-20250929",
    temperature=0,
    max_tokens=4096,
)
agent = create_agent(
    model=model,
    tools=[my_tool],
    system_prompt="You are a helpful assistant.",
)
runner = Runner(agent)

Custom Agents (Advanced)

For advanced use cases, you can pass any callable that accepts and returns the LangGraph message format:

# Async callable
async def my_agent(input: dict) -> dict:
    messages = input["messages"]
    # Your custom logic...
    return {"messages": [..., {"role": "assistant", "content": "response"}]}

runner = Runner(my_agent)

# Sync callable (runs in thread pool automatically)
def my_sync_agent(input: dict) -> dict:
    return {"messages": [{"role": "assistant", "content": "Hello!"}]}

runner = Runner(my_sync_agent)

Configuration

runner = Runner(
    agent,
    name="my-agent",  # Optional name (shown in /health)
    max_runs=1000,    # Max runs to keep in memory
)

runner.serve(
    host="0.0.0.0",
    port=8000,
)

Environment variables:

  • LANGCHAIN_RUNNER_HOST - Host to bind to (default: 0.0.0.0)
  • LANGCHAIN_RUNNER_PORT - Port to bind to (default: 8000)

CLI

# Run your agent file
langchain-runner serve my_agent.py

# Or use Python module
python -m langchain_runner serve my_agent.py

# With custom host/port
langchain-runner serve my_agent.py --host 127.0.0.1 --port 3000

Run Tracking

Every agent invocation returns a run_id immediately. Use it to track progress:

import requests

# Invoke trigger
response = requests.post(
    "http://localhost:8000/trigger/ask",
    json={"question": "Hello"}
)
run_id = response.json()["run_id"]

# Check status
status = requests.get(f"http://localhost:8000/runs/{run_id}")
print(status.json())

Response:

{
  "run_id": "abc123",
  "status": "completed",
  "trigger_type": "http",
  "trigger_name": "ask",
  "input": "Hello",
  "result": {"messages": [...]},
  "final_message": "Here's the answer...",
  "created_at": "2025-01-01T00:00:00Z",
  "completed_at": "2025-01-01T00:00:05Z"
}

Using MCP Tools

The recommended way to add tools to your agent is via Model Context Protocol (MCP). MCP provides a standard way to connect AI agents to external tools and data sources.

First, install the MCP adapter:

pip install langchain-runner[mcp]

Then connect to MCP servers and use their tools:

import asyncio
from langchain_runner import Runner
from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient

async def create_mcp_agent():
    # Connect to MCP servers
    client = MultiServerMCPClient({
        "tools": {
            "transport": "streamable_http",
            "url": "https://your-mcp-server.com/mcp",
        },
        # You can connect to multiple servers
        "local_tools": {
            "transport": "stdio",
            "command": "python",
            "args": ["./my_mcp_server.py"],
        },
    })
    
    # Get tools from all connected servers
    tools = await client.get_tools()
    print(f"Loaded {len(tools)} tools from MCP servers")
    
    # Create agent with MCP tools
    agent = create_agent(
        model="anthropic:claude-sonnet-4-5-20250929",
        tools=tools,
        system_prompt="You are a helpful assistant with access to external tools.",
    )
    
    return agent

# Create agent at startup
agent = asyncio.get_event_loop().run_until_complete(create_mcp_agent())
runner = Runner(agent, name="mcp-agent")

@runner.webhook("/process")
async def on_process(payload: dict):
    return f"Process this request: {payload}"

if __name__ == "__main__":
    runner.serve()

Example: Full Setup

import asyncio
from langchain_runner import Runner
from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient

async def setup_agent():
    # Connect to your MCP server with tools (Notion, Slack, etc.)
    client = MultiServerMCPClient({
        "tools": {
            "transport": "streamable_http",
            "url": "https://your-mcp-server.com/mcp",
        }
    })
    tools = await client.get_tools()
    
    return create_agent(
        model="anthropic:claude-sonnet-4-5-20250929",
        tools=tools,
        system_prompt="You are a helpful assistant.",
    )

agent = asyncio.get_event_loop().run_until_complete(setup_agent())
runner = Runner(agent, name="my-assistant")

# HTTP trigger
@runner.trigger("/chat")
async def chat(message: str):
    return message

# Webhook triggers
@runner.webhook("/slack")
async def on_slack(payload: dict):
    event = payload.get("event", {})
    return f"Respond to: {event.get('text', '')}"

@runner.webhook("/github")
async def on_github(payload: dict):
    pr = payload.get("pull_request", {})
    return f"Review PR: {pr.get('title', 'Unknown')}"

# Cron triggers
@runner.cron("0 9 * * 1-5")  # Weekdays at 9am
async def daily_standup():
    return "Generate daily standup summary"

@runner.cron("0 18 * * 5")  # Friday at 6pm
async def weekly_report():
    return "Generate weekly report"

if __name__ == "__main__":
    runner.serve()

License

MIT

Metadata

Release files for langchain-runner 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for langchain-runner 0.1.0
File Size Uploaded Provenance
langchain_runner-0.1.0.tar.gz 17.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for langchain-runner 0.1.0
File Interpreter ABI Platform
langchain_runner-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 33.2 kB

Release files / langchain_runner-0.1.0.tar.gz

Download URL langchain_runner-0.1.0.tar.gz
Size 17.0 kB
Tags Source
SHA-256 checksum
How to use checksums
9813df5917de57ea2d63bceb13bce630760bfcfc45ebed56061a94d32b204532
BLAKE2b-256 checksum
How to use checksums
60f4369966bcc4e6c1d1ae3e5e984a4c3bc23d0dce44d34418989f7aa599c0ba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release files / langchain_runner-0.1.0-py3-none-any.whl

Download URL langchain_runner-0.1.0-py3-none-any.whl
Size 16.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4423b4eb643d8b194ec984b50fd07f7dc17f7c95cda1caa2142c6c9e3ada26d3
BLAKE2b-256 checksum
How to use checksums
f759975c0ff391f83f1cd34d4c34d0eeb3ccb1fb22dfa78bb8a32b90b1ff8e4c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Security for langchain-runner 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page