Building long-running interactive MCP tools with Temporal

AUTHORS
Joshua Smith
DATE
Aug 21, 2025
CATEGORY
DURATION
0 MIN

In a previous post, I talked about Anthropic’s Model Context Protocol (MCP),how to implement MCP tools as workflows to add durability, and the ability to run for an indeterminate (or even a very long) time. One thing I didn’t talk about was how to interact with a long-running tool workflow using MCP. A lot of developers building AI systems with Claude (or even other MCP hosts) ask the same question: how do I make my tools long-running, interactive, and reliable?

This blog post will cover a way to build long running MCP tools, how to interact with them, and how to orchestrate a complex multi-step process.

(There is discussion of how to do this differently in the MCP Protocol itself, see for example here. For this blog post, we’ll work with the protocol as it is, and add Temporal for durability.)

Recently, Temporal’s Kevin Martin created an excellent application that demonstrates a long-running tool powered by Temporal Workflows, and in this post I want to explain what the application did that was so powerful.

The Invoice sample#

In Kevin’s Temporal Invoice MCP, there are multiple tools:

  1. Trigger invoice processing
  2. Check status
  3. Approve
  4. Reject
# Trigger Invoice MCP tool from https://github.com/Aslan11/temporal-invoice-mcp/blob/main/server.py
@mcp.tool()
async def trigger(invoice: Dict) -> Dict[str, str]:
    """Start the InvoiceWorkflow with the given invoice JSON."""
    client = await _client()
    handle = await client.start_workflow(
        InvoiceWorkflow.run,
        invoice,
        id=f"invoice-{uuid.uuid4()}",
        task_queue="invoice-task-queue",
    )
    return {"workflow_id": handle.id, "run_id": handle.result_run_id}

MCP host enables process You can check out this video to see this in action:

Human interaction with MCP, powered by Workflows#

What’s so powerful about this architecture is that it enables long-running tools for Agentic AI Systems. These tools can run for an unlimited time, and can be interacted with as part of an Agentic System.

Human-interaction-with-MCP-and-workflows

This enables a user to trigger a tool via MCP, and then check on the status of it and interact with it as it executes. Because Temporal handles all orchestration, your AI tool doesn’t have to worry about memory, timeout issues, or ad hoc retries. AI can handle confusing and ambiguous user input with aplomb, and can interact with the Temporal Workflow orchestration as it executes:

goose executing the Invoice Workflow Goose executing the Invoice Workflow

Agentic Systems can also query Workflows and report back to the user. Note it remembers what we’re working on and doesn’t make me input the invoice parameters or workflow ID to continue the conversation about this invoice process:

Goose checking the status of the the invoice process for me Goose checking the status of the the invoice process for me

An architecture for durable, long-running agentic AI tools#

This architecture is built from Agentic AI, MCP Tools, and Temporal Workflows:

Agentic-AI-architecture

First, start with a Temporal Workflow. Here’s some of the relevant code:

class InvoiceWorkflow:
    def __init__(self) -> None:
        self.approved: bool | None = None
        self.status: str = "INITIALIZING"

    @workflow.run
    async def run(self, invoice: dict) -> str:
        self.status = "PENDING-VALIDATION"
        await workflow.execute_activity(
            validate_against_erp,
            invoice,
            start_to_close_timeout=timedelta(seconds=30),
            retry_policy=RetryPolicy(
                initial_interval=timedelta(seconds=1),
                maximum_interval=timedelta(seconds=30),
                maximum_attempts=5,
            ),
        )

        self.status = "PENDING-APPROVAL"
        # Wait for the approval signal
        await workflow.wait_condition(
            lambda: self.approved is not None,
            timeout=timedelta(days=5),
        )

        if not self.approved:
            self.status= "REJECTED"
            return "REJECTED"

        self.status = "APPROVED"
        # Process each line item in parallel
        for line in invoice.get("lines", []):
            handle = await workflow.start_child_workflow(PayLineItem.run, line,)
        # <snip details of tracking error counts>    
        workflow.logger.info("All line items paid successfully")
        self.status = "PAID"
        return self.status

Full Workflow code is here. Any Workflow that you want to use as a tool will fit well into this system, but we’ll need to add a few things to connect it via MCP. Our Workflow is waiting for a human to approve the processing. It can wait forever, but in our case, we set it up to wait for 5 days for approval.

Let’s create some tools to define the Workflow interactions you want to expose, such as this tool to approve the invoice:

@mcp.tool()
async def approve(workflow_id: str, run_id: str) -> str:
    """Signal approval for the invoice workflow."""
    client = await _client()
    handle = client.get_workflow_handle(workflow_id=workflow_id, run_id=run_id)
    await handle.signal("ApproveInvoice")
    return "APPROVED"

This sends a Signal to the workflow that can be very simple:

    @workflow.signal
    async def ApproveInvoice(self) -> None:
        self.approved = True

To get information from the Workflow, add a status tool:

@mcp.tool()
async def status(workflow_id: str, run_id: str) -> str:
    """Return current status of the workflow."""
    client = await _client()
    handle = client.get_workflow_handle(workflow_id=workflow_id, run_id=run_id)
    desc = await handle.describe()
    status = await handle.query("GetInvoiceStatus")
    return f"Invoice with ID {workflow_id} is currently {status}. " \
           f"Workflow status: {desc.status.name}"

And a Workflow Query:

 @workflow.query
    async def GetInvoiceStatus(self) -> str:
        return self.status

These could be much more complex, passing more information, or sending much more complicated information than a simple approval. Signals, Queries, and Updates make interacting with Workflows very simple.

Now we’ll use the approve tool to approve the invoice processing and let it complete: Goose handling multiple tool calls at once - approving the invoice, then checking on the status - it’s complete! Goose handling multiple tool calls at once - approving the invoice, then checking on the status - it’s complete!

Multiple tools, one process#

Using multiple MCP tools, you can start, control, make progress on, and understand a long-running process implemented in a Workflow. Tools can initiate, follow, and control a complex, long-running process.

Enabling human-in-the-loop tools with Temporal#

Tool interactions don’t have to be short — MCP partnered with Durable Execution powers a whole new set of interactions with long-running tools.

These tools can be used by any agentic system that functions as an MCP host and client, so you can switch from Claude to Goose to pick-your-tool.

Check out the sample: Temporal Invoice MCP.

If you have questions and want to learn more, feel free to reach out:

Special thanks to Kevin Martin for the great Invoice Sample, and the Goose Team for creating such a useful app.

Temporal Cloud

Ready to see for yourself?

Sign up for Temporal Cloud today and get $1,000 in free credits.