We use cookies

We use cookies and similar technologies for analytics and marketing. You can allow these cookies or continue with only essential cookies.

By clicking "Accept", you agree to our use of cookies.
Learn more.

Hatchet and MCP: Using the OpenAI Agents SDK in a Trusted Environment

This cookbook builds on the Hatchet Agent Tools guide by connecting the OpenAI Agents SDK directly to Hatchet-backed support tools. When the agent decides to use a tool, the tool handler submits a run to the Hatchet engine. A worker executes the task, and the result flows back to the agent.

What this example builds

The example uses a support scenario similar to How to Create a Support Agent Using Hatchet, but with a different architecture. That cookbook models support as a durable Hatchet workflow. This cookbook shows the agent choosing among separate Hatchet-backed tools:

  • lookup-customer: retrieve a customer profile by ID.
  • check-order-status: check shipping status and known issues for an order.
  • create-ticket: open a support ticket for the customer.

The same Hatchet-backed tool pattern also works for workflows, as shown in Hatchet Agent Tools. Use a workflow when a tool should trigger a multi-step process rather than a single operation. Later guides in this series will explore larger production agent patterns.

Architecture

The agent may call independent tools in a different order depending on the model's decisions.

Unlike the Claude Agent SDK example, there is no in-process MCP server here. Hatchet's MCP tool helper converts each task into an OpenAI-compatible tool, which is passed directly to the OpenAI Agent.

Trusted environment pattern. This cookbook uses a trusted-harness architecture. The agent process runs in your own infrastructure with direct access to Hatchet credentials. If you need to run agent turns inside untrusted sandboxes with credentials kept outside, that is a different architecture pattern covered in a later guide.

Setup

Prepare your environment

You need:

  • A working local Hatchet environment or access to Hatchet Cloud
  • A Hatchet SDK example environment (see the Quickstart)
  • An OPENAI_API_KEY environment variable set with a valid OpenAI API key

Install the OpenAI Agents SDK integration for your language:

Install the Hatchet SDK extra for OpenAI:

pip install "hatchet-sdk[openai]"

Define the models

Define input and output types for each tool. The agent uses the input schema to understand what arguments a tool accepts.

class CustomerLookupInput(BaseModel):    customer_id: strclass CustomerInfo(BaseModel):    customer_id: str    name: str    email: str    plan: str    account_status: str    default_order_id: str    support_tier: strclass OrderStatusInput(BaseModel):    order_id: strclass OrderStatus(BaseModel):    order_id: str    status: str    last_updated: str    estimated_delivery: str    known_issue: str | None    carrier: str    tracking_number: strclass CreateTicketInput(BaseModel):    customer_id: str    order_id: str    subject: str    body: str    priority: strclass TicketResult(BaseModel):    ticket_id: str    status: str    priority: str    routing_team: str    summary: str

Set up the Hatchet client

Initialize the Hatchet client. It reads credentials from environment variables or a .env file.

from __future__ import annotationsfrom typing import TYPE_CHECKINGif TYPE_CHECKING:    from agents import FunctionTool    from claude_agent_sdk import SdkMcpToolfrom pydantic import BaseModelfrom hatchet_sdk import Context, Hatchetfrom hatchet_sdk.runnables.workflow import MCPProviderhatchet = Hatchet(debug=True)

Add deterministic support data

The following fixture data keeps the example runnable without any third-party APIs.

CUSTOMERS = {    "C-100": CustomerInfo(        customer_id="C-100",        name="Alice Martin",        email="alice@example.com",        plan="business",        account_status="active",        default_order_id="ORD-9987",        support_tier="priority",    ),}ORDERS = {    "ORD-9987": OrderStatus(        order_id="ORD-9987",        status="delayed",        last_updated="2026-05-20T14:30:00Z",        estimated_delivery="2026-05-28",        known_issue="Carrier reported weather delay at regional hub",        carrier="FastShip",        tracking_number="FS-482910",    ),}

Define the Hatchet-backed tools

Each tool is a standalone Hatchet task with a description and an input validator, as covered in the Hatchet Agent Tools guide.

Lookup customer

First, define a tool that retrieves customer profile data.

@hatchet.task(    name="lookup-customer",    input_validator=CustomerLookupInput,    description="Look up a customer by ID and return their profile, plan, and support tier.",)async def lookup_customer(input: CustomerLookupInput, ctx: Context) -> CustomerInfo:    customer = CUSTOMERS.get(input.customer_id)    if customer is None:        return CustomerInfo(            customer_id=input.customer_id,            name="Unknown",            email="unknown@example.com",            plan="none",            account_status="not_found",            default_order_id="",            support_tier="standard",        )    return customer

Check order status

Next, define another tool that returns shipping status, carrier, and any known issues for an order.

@hatchet.task(    name="check-order-status",    input_validator=OrderStatusInput,    description="Check the current status, carrier, and any known issues for an order.",)async def check_order_status(input: OrderStatusInput, ctx: Context) -> OrderStatus:    order = ORDERS.get(input.order_id)    if order is None:        return OrderStatus(            order_id=input.order_id,            status="not_found",            last_updated="",            estimated_delivery="",            known_issue=None,            carrier="unknown",            tracking_number="",        )    return order

Create ticket

Finally, define a tool that creates a support ticket. Validate inputs before creating records, and consider attaching agent or user context with additional metadata to improve traceability.

@hatchet.task(    name="create-ticket",    input_validator=CreateTicketInput,    description="Create a support ticket for a customer issue and return the ticket ID and routing.",)async def create_ticket(input: CreateTicketInput, ctx: Context) -> TicketResult:    ticket_id = f"TICKET-{input.customer_id}-001"    return TicketResult(        ticket_id=ticket_id,        status="open",        priority=input.priority,        routing_team="shipping-support",        summary=f"Ticket {ticket_id} created for {input.customer_id} "        f"regarding order {input.order_id}: {input.subject}",    )

Expose the tasks as OpenAI agent tools

Convert each task into an OpenAI FunctionTool using Hatchet's tool helper. Although the helper has mcp in its name, this example passes FunctionTool objects directly to the OpenAI Agents SDK. A later guide in this series will cover using the OpenAI Agents SDK with an MCP server.

def create_lookup_customer_tool_openai() -> FunctionTool:    return lookup_customer.mcp_tool(MCPProvider.OPENAI)def create_check_order_status_tool_openai() -> FunctionTool:    return check_order_status.mcp_tool(MCPProvider.OPENAI)def create_ticket_tool_openai() -> FunctionTool:    return create_ticket.mcp_tool(MCPProvider.OPENAI)

The worker does not discover agent tools. It registers Hatchet tasks normally. When the tool handler submits a run, Hatchet dispatches it to a worker that registered the corresponding task.

Register and start the worker

Register the Hatchet tasks with a worker. Tool calls submit runs to the Hatchet engine, which dispatches them to a running worker.

from examples.support_agent_tools.tools import (    hatchet,    lookup_customer,    check_order_status,    create_ticket,)def main() -> None:    worker = hatchet.worker(        "support-tools-worker",        workflows=[lookup_customer, check_order_status, create_ticket],    )    worker.start()if __name__ == "__main__":    main()

Wire the OpenAI Agents SDK

Now we have everything we need to create the support agent. Pass each tool directly to the OpenAI Agent through the tools array. Unlike the Claude version of this cookbook, this example does not create an in-process MCP server or declare a list of pre-approved MCP tool names.

The example runs the agent once, waits for the tool calls to complete, and prints the final result.

import asynciofrom agents import Agent, Runnerfrom examples.support_agent_tools.tools import (    create_lookup_customer_tool_openai,    create_check_order_status_tool_openai,    create_ticket_tool_openai,)async def main() -> None:    lookup_customer_tool = create_lookup_customer_tool_openai()    check_order_status_tool = create_check_order_status_tool_openai()    ticket_tool = create_ticket_tool_openai()    agent = Agent(        name="support-agent",        tools=[lookup_customer_tool, check_order_status_tool, ticket_tool],    )    result = await Runner.run(        agent,        "Customer C-100 says order ORD-9987 has not arrived. "        "Look up the customer, check the order status, and create a "        "support ticket if the order has a known issue or delayed delivery. "        'If you create a ticket, use priority "high", subject '        '"Delayed order ORD-9987", and a body that summarizes the known '        "carrier delay. Then summarize what happened.",    )    print(result.final_output)if __name__ == "__main__":    asyncio.run(main())

Test it

Start the worker in one terminal and run the agent in another. The worker must be running before the agent calls any tools.

Start the worker:

cd sdks/python
poetry run python -m examples.support_agent_tools.worker

In a second terminal, run the agent:

cd sdks/python
poetry run python -m examples.support_agent_tools.agent_openai

When successful, you should see tool calls for all three agent tools. Given the fixture data used in this example, the agent looks up customer C-100, checks order ORD-9987, creates a high priority ticket for the delayed delivery, and prints a final summary to the terminal.

Each tool call appears as a task run in the Hatchet dashboard with full status, timing, and input/output visibility.

Security considerations

The agent process has direct access to Hatchet client credentials and runs in your own infrastructure. Every tool passed to the agent adds to the model's available capability set. Once a tool is available to the model, you should assume the model may attempt to call it. Do not rely on the agent prompt alone to enforce security rules.

Hatchet does not provide native code sandboxing. If you need to run each agent turn inside an untrusted sandbox, use a different architecture with external sandbox providers and credential proxying. Later guides in this series will explore sandboxed and custom-harness patterns.

Next steps

Last updated on August 13, 2026

On this page