Build Claude Code for X with Agent Controller

Build and control long-running agent experiences with sessions, modes, permissions, subagents.

Alex BookerAlex Booker·

Jun 30, 2026

·

3 min read

Agent Controller is Mastra's control layer for building stateful, interactive agent experiences.

Use it when your agent is not just answering one request, but working with a user over time: planning, switching modes, asking for approval, delegating to subagents, resuming threads, changing models, and driving a live UI.

Coding is the familiar use case, and we use AgentController there ourselves in Mastra Code.

But the pattern is broader than coding.

Agent Controller is designed for any domain where humans and agents collaborate over time: orchestrators, engineering tools, finance workflows, research assistants, legal review systems, security agents, and other expert products.

In a recent workshop, we showed how Artifact is applying this pattern to electrical engineering. They are building toward a world where people can prompt circuit boards the way developers now prompt code. Artifact used an early version of AgentController to build that experience!

Note: AgentController was previously called Harness. The rename is explained below.

Quickstart

Here's a quick example to show how the API works.

import { Agent } from "@mastra/core/agent";
import { AgentController } from "@mastra/core/agent-controller";
import { LibSQLStore } from "@mastra/libsql";
 
const agent = new Agent({
  name: "assistant",
  instructions: "Help the user plan and complete tasks.",
  model: "__GATEWAY_OPENAI_MODEL__",
});
 
const agentController = new AgentController({
  id: "my-agent",
  agent,
  storage: new LibSQLStore({ url: "file:./data.db" }),
  modes: [
    {
      id: "plan",
      name: "Plan",
      metadata: { default: true },
      instructions: "Reason about changes before making them.",
    },
    {
      id: "build",
      name: "Build",
      instructions: "Implement the approved plan.",
    },
  ],
});
 
agentController.subscribe((event) => {
  if (event.type === "message_update") {
    console.log(event.message);
  }
});
 
await agentController.init();
await agentController.selectOrCreateThread();
await agentController.sendMessage({ content: "Hello!" });

From Harness to Agent Controller

This feature was originally called Harness. We changed the name because it made the relationship between Mastra, Agent, and this new API harder to understand than it should be.

Mastra is already an agent harness in the broader sense. It gives agents the runtime around the model, including tools, memory, workflows, storage, human-in-the-loop, and signals.

This new API is more specific. It controls the interactive experience around an agent: sessions, modes, threads, permissions, subagents, model switching, persisted state, and UI events.

We updated the name to AgentController because it's easier to reason about.

Migration

If you're already using Harness, migration is mostly a rename.

Upgrade to @mastra/core@1.47.0 or later, import from @mastra/core/agent-controller, and rename Harness to AgentController in your code.

The old @mastra/core/harness entry point remains available for now for compatibility, but new docs and examples use the new AgentController class.

Get started

Visit the AgentController docs to start building.

Share:
Alex Booker
Alex BookerHead of Developer Experience

Alex Booker is the Head of Developer Experience at Mastra, where he helps developers understand and build agent systems with Mastra. He hosts weekly workshops with members of the team and publishes videos on the Mastra YouTube channel. Outside of Mastra, he's a proud Londoner and a keen cyclist, though his legs have not yet caught up with his enthusiasm!

All articles by Alex Booker