github mastra-ai/mastra @mastra/core@1.4.0
February 13, 2026

12 hours ago

Highlights

Datasets & Experiments (core + server + Studio UI)

Mastra now has first-class evaluation primitives: versioned Datasets (with JSON Schema validation and SCD-2 item versioning) and Experiments that run agents against datasets with configurable scorers and result tracking. This ships end-to-end across @mastra/core APIs, new /datasets REST endpoints in @mastra/server, and a full Studio UI for managing datasets, triggering experiments, and comparing results.

Workspace & Filesystem Lifecycle + Safer Filesystem Introspection

Workspace lifecycle interfaces were split into FilesystemLifecycle and SandboxLifecycle, and MastraFilesystem now supports onInit/onDestroy callbacks. Filesystem path resolution and metadata were improved (generic FilesystemInfo<TMetadata>, provider-specific metadata, safer instructions for uncontained filesystems) and filesystem info is now exposed via the workspaces API response.

Workflow foreach Progress Streaming

Workflows now emit a workflow-step-progress stream event for foreach steps (completed/total, current index, per-iteration status/output), supported by both execution engines. Studio renders real-time progress bars, and @mastra/react watch hooks now accumulate foreachProgress into step state.

Breaking Changes

  • @mastra/memory: observe() now takes a single object parameter (e.g., observe({ threadId, resourceId })) instead of positional arguments.

Changelog

@mastra/core@1.4.0

Minor Changes

  • Added Datasets and Experiments to core. Datasets let you store and version collections of test inputs with JSON Schema validation. Experiments let you run AI outputs against dataset items with configurable scorers to track quality over time. (#12747)

    New exports from @mastra/core/datasets:

    • DatasetsManager — orchestrates dataset CRUD, item versioning (SCD-2), and experiment execution
    • Dataset — single-dataset handle for adding items and running experiments

    New storage domains:

    • DatasetsStorage — abstract base class for dataset persistence (datasets, items, versions)
    • ExperimentsStorage — abstract base class for experiment lifecycle and result tracking

    Example:

    import { Mastra } from "@mastra/core";
    
    const mastra = new Mastra({
      /* ... */
    });
    
    const dataset = await mastra.datasets.create({ name: "my-eval-set" });
    await dataset.addItems([{ input: { query: "What is 2+2?" }, groundTruth: { answer: "4" } }]);
    
    const result = await dataset.runExperiment({
      targetType: "agent",
      targetId: "my-agent",
      scorerIds: ["accuracy"]
    });
  • Fix LocalFilesystem.resolvePath handling of absolute paths and improve filesystem info. (#12971)

    • Fix absolute path resolution: paths were incorrectly stripped of leading slashes and resolved relative to basePath, causing PermissionError for valid paths (e.g. skills processor accessing project-local skills directories).
    • Make FilesystemInfo generic (FilesystemInfo<TMetadata>) so providers can type their metadata.
    • Move provider-specific fields (basePath, contained) to metadata in LocalFilesystem.getInfo().
    • Update LocalFilesystem.getInstructions() for uncontained filesystems to warn agents against listing /.
    • Use FilesystemInfo type in WorkspaceInfo instead of duplicated inline shape.
  • Add workflow-step-progress stream event for foreach workflow steps. Each iteration emits a progress event with completedCount, totalCount, currentIndex, iterationStatus (success | failed | suspended), and optional iterationOutput. Both the default and evented execution engines emit these events. (#12838)

    The Mastra Studio UI now renders a progress bar with an N/total counter on foreach nodes, updating in real time as iterations complete:

    // Consuming progress events from the workflow stream
    const run = workflow.createRun();
    const result = await run.start({ inputData });
    const stream = result.stream;
    
    for await (const chunk of stream) {
      if (chunk.type === "workflow-step-progress") {
        console.log(`${chunk.payload.completedCount}/${chunk.payload.totalCount} - ${chunk.payload.iterationStatus}`);
      }
    }

    MCP Client Storage

    New storage domain for persisting MCP client configurations with CRUD operations. Each MCP client can contain multiple servers with independent tool selection:

    // Store an MCP client with multiple servers
    await storage.mcpClients.create({
      id: "my-mcp",
      name: "My MCP Client",
      servers: {
        "github-server": { url: "https://mcp.github.com/sse" },
        "slack-server": { url: "https://mcp.slack.com/sse" }
      }
    });

    LibSQL, PostgreSQL, and MongoDB storage adapters all implement the new MCP client domain.

    ToolProvider Interface

    New ToolProvider interface at @mastra/core/tool-provider enables third-party tool catalog integration (e.g., Composio, Arcade AI):

    import type { ToolProvider } from '@mastra/core/tool-provider';
    
    # Providers implement: listToolkits(), listTools(), getToolSchema(), resolveTools()

    resolveTools() receives requestContext from the current request, enabling per-user API keys and credentials in multi-tenant setups:

    const tools = await provider.resolveTools(slugs, configs, {
      requestContext: { apiKey: "user-specific-key", userId: "tenant-123" }
    });

    Tool Selection Semantics

    Both mcpClients and integrationTools on stored agents follow consistent three-state selection:

    • { tools: undefined } — provider registered, no tools selected
    • { tools: {} } — all tools from provider included
    • { tools: { 'TOOL_SLUG': { description: '...' } } } — specific tools with optional overrides
  • Added (#12764)
    Added a suppressFeedback option to hide internal completion‑check messages from the stream. This keeps the conversation history clean while leaving existing behavior unchanged by default.

    Example
    Before:

    const agent = await mastra.createAgent({
      completion: { validate: true }
    });

    After:

    const agent = await mastra.createAgent({
      completion: { validate: true, suppressFeedback: true }
    });
  • Split workspace lifecycle interfaces (#12978)

    The shared Lifecycle interface has been split into provider-specific types that match actual usage:

    • FilesystemLifecycle — two-phase: init()destroy()
    • SandboxLifecycle — three-phase: start()stop()destroy()

    The base Lifecycle type is still exported for backward compatibility.

    Added onInit / onDestroy callbacks to MastraFilesystem

    The MastraFilesystem base class now accepts optional lifecycle callbacks via MastraFilesystemOptions, matching the existing onStart / onStop / onDestroy callbacks on MastraSandbox.

    const fs = new LocalFilesystem({
      basePath: "./data",
      onInit: ({ filesystem }) => {
        console.log("Filesystem ready:", filesystem.status);
      },
      onDestroy: ({ filesystem }) => {
        console.log("Cleaning up...");
      }
    });

    onInit fires after the filesystem reaches ready status (non-fatal on failure). onDestroy fires before the filesystem is torn down.

Patch Changes

  • Update provider registry and model documentation with latest models and providers (7ef618f)

  • Fixed Anthropic API rejection errors caused by empty text content blocks in assistant messages. During streaming with web search citations, empty text parts could be persisted to the database and then rejected by Anthropic's API with 'text content blocks must be non-empty' errors. The fix filters out these empty text blocks before persistence, ensuring stored conversation history remains valid for Anthropic models. Fixes #12553. (#12711)

  • Improve error messages when processor workflows or model fallback retries fail. (#12970)

    • Include the last error message and cause when all fallback models are exhausted, instead of the generic "Exhausted all fallback models" message.
    • Extract error details from failed workflow results and individual step failures when a processor workflow fails, instead of just reporting "failed with status: failed".
  • Fixed tool-not-found errors crashing the agentic loop. When a model hallucinates a tool name (e.g., Gemini 3 Flash adding prefixes like creating:view instead of view), the error is now returned to the model as a tool result instead of throwing. This allows the model to self-correct and retry with the correct tool name on the next turn. The error message includes available tool names to help the model recover. Fixes #12895. (#12961)

  • Fixed structured output failing with Anthropic models when memory is enabled. The error "assistant message in the final position" occurred because the prompt sent to Anthropic ended with an assistant-role message, which is not supported when using output format. Resolves #12800 (#12835)

@mastra/codemod@1.0.2

Patch Changes

@mastra/deployer@1.4.0

Patch Changes

@mastra/mcp-docs-server@1.1.2

Patch Changes

@mastra/memory@1.3.0

Minor Changes

  • @mastra/opencode: Add opencode plugin for Observational Memory integration (#12925)

    Added standalone observe() API that accepts external messages directly, so integrations can trigger observation without duplicating messages into Mastra's storage.

    New exports:

    • ObserveHooks — lifecycle callbacks (onObservationStart, onObservationEnd, onReflectionStart, onReflectionEnd) for hooking into observation/reflection cycles
    • OBSERVATION_CONTEXT_PROMPT — preamble that introduces the observations block
    • OBSERVATION_CONTEXT_INSTRUCTIONS — rules for interpreting observations (placed after the <observations> block)
    • OBSERVATION_CONTINUATION_HINT — behavioral guidance that prevents models from awkwardly acknowledging the memory system
    • getOrCreateRecord() — now public, allows eager record initialization before the first observation cycle
    import { ObservationalMemory } from "@mastra/memory/processors";
    
    const om = new ObservationalMemory({ storage, model: "google/gemini-2.5-flash" });
    
    // Eagerly initialize a record
    await om.getOrCreateRecord(threadId);
    
    // Pass messages directly with lifecycle hooks
    await om.observe({
      threadId,
      messages: myMessages,
      hooks: {
        onObservationStart: () => console.log("Observing..."),
        onObservationEnd: () => console.log("Done!"),
        onReflectionStart: () => console.log("Reflecting..."),
        onReflectionEnd: () => console.log("Reflected!")
      }
    });

    Breaking: observe() now takes an object param instead of positional args. Update calls from observe(threadId, resourceId) to observe({ threadId, resourceId }).

Patch Changes

  • Fixed observational memory writing non-integer token counts to PostgreSQL, which caused invalid input syntax for type integer errors. Token counts are now correctly rounded to integers before all database writes. (#12976)

  • Fixed cloneThread not copying working memory to the cloned thread. Thread-scoped working memory is now properly carried over when cloning, and resource-scoped working memory is copied when the clone uses a different resourceId. (#12833)

  • Updated dependencies [7ef618f, b373564, 927c2af, b896b41, 6415277, 0831bbb, 63f7eda, a5b67a3, 877b02c, 7567222, af71458, eb36bd8, 3cbf121]:

    • @mastra/core@1.4.0

@mastra/playground-ui@11.0.0

Minor Changes

  • Added Datasets and Experiments UI. Includes dataset management (create, edit, delete, duplicate), item CRUD with CSV/JSON import and export, SCD-2 version browsing and comparison, experiment triggering with scorer selection, experiment results with trace visualization, and cross-experiment comparison with score deltas. Uses coreFeatures runtime flag for feature gating instead of build-time env var. (#12747)

  • Add workflow-step-progress stream event for foreach workflow steps. Each iteration emits a progress event with completedCount, totalCount, currentIndex, iterationStatus (success | failed | suspended), and optional iterationOutput. Both the default and evented execution engines emit these events. (#12838)

    The Mastra Studio UI now renders a progress bar with an N/total counter on foreach nodes, updating in real time as iterations complete:

    // Consuming progress events from the workflow stream
    const run = workflow.createRun();
    const result = await run.start({ inputData });
    const stream = result.stream;
    
    for await (const chunk of stream) {
      if (chunk.type === "workflow-step-progress") {
        console.log(`${chunk.payload.completedCount}/${chunk.payload.totalCount} - ${chunk.payload.iterationStatus}`);
      }
    }
  • Revamped agent CMS experience with dedicated route pages for each section (Identity, Instruction Blocks, Tools, Agents, Scorers, Workflows, Memory, Variables) and sidebar navigation (#13016)

  • Added ability to create sub-agents on-the-fly via a SideDialog in the Sub-Agents section of the agent editor (#12952)

Patch Changes

  • dependencies updates: (#12949)

  • Removed experiment mode from the agent prompt sidebar. The system prompt is now displayed as readonly. (#12994)

  • Aligned frontend rule engine types with backend, added support for greater_than_or_equal, less_than_or_equal, exists, and not_exists operators, and switched instruction blocks to use RuleGroup (#12864)

  • Skip awaitBufferStatus calls when observational memory is disabled. Previously the Studio sidebar would unconditionally hit /memory/observational-memory/buffer-status after every agent message, which returns a 400 when OM is not configured and halts agent execution. (#13025)

  • Fix prompt experiment localStorage persisting stale prompts: only save to localStorage when the user edits the prompt away from the code-defined value, and clear it when they match. Previously, the code-defined prompt was eagerly saved on first load, causing code changes to agent instructions to be ignored. (#12929)

  • Fixed chat briefly showing an empty conversation after sending the first message on a new thread. (#13018)

  • Fixed missing validation for the instructions field in the scorer creation form and replaced manual submission state tracking with the mutation hook's built-in pending state (#12993)

  • Default Studio file browser to basePath when filesystem containment is disabled, preventing the browser from showing the host root directory. (#12971)

  • Updated dependencies [7ef618f, b373564, 927c2af, 927c2af, 3da8a73, 927c2af, b896b41, 6415277, 4ba40dc, 0831bbb, 63f7eda, a5b67a3, 877b02c, 877b02c, 7567222, 40f224e, af71458, eb36bd8, 3cbf121]:

    • @mastra/core@1.4.0
    • @mastra/client-js@1.4.0
    • @mastra/react@0.2.3
    • @mastra/ai-sdk@1.0.4

@mastra/server@1.4.0

Minor Changes

  • Added REST API routes for Datasets and Experiments. New endpoints under /datasets for full CRUD on datasets, items, versions, experiments, and experiment results. Includes batch operations and experiment comparison. (#12747)

Patch Changes

  • Fixed the /api/tools endpoint returning an empty list even when tools are registered on the Mastra instance. Closes #12983 (#13008)

  • Fixed custom API routes registered via registerApiRoute() being silently ignored by Koa, Express, Fastify, and Hono server adapters. Routes previously appeared in the OpenAPI spec but returned 404 at runtime. Custom routes now work correctly across all server adapters. (#12960)

    Example:

    import Koa from "koa";
    import { Mastra } from "@mastra/core";
    import { registerApiRoute } from "@mastra/core/server";
    import { MastraServer } from "@mastra/koa";
    
    const mastra = new Mastra({
      server: {
        apiRoutes: [
          registerApiRoute("/hello", {
            method: "GET",
            handler: async (c) => c.json({ message: "Hello!" })
          })
        ]
      }
    });
    
    const app = new Koa();
    const server = new MastraServer({ app, mastra });
    await server.init();
    // GET /hello now returns 200 instead of 404
  • Added API routes for stored MCP clients and tool provider discovery. (#12974)

    Stored MCP Client Routes

    New REST endpoints for managing stored MCP client configurations:

    • GET /api/stored-mcp-clients — List all stored MCP clients
    • GET /api/stored-mcp-clients/:id — Get a specific MCP client
    • POST /api/stored-mcp-clients — Create a new MCP client
    • PATCH /api/stored-mcp-clients/:id — Update an existing MCP client
    • DELETE /api/stored-mcp-clients/:id — Delete an MCP client
    // Create a stored MCP client
    const response = await fetch("/api/stored-mcp-clients", {
      method: "POST",
      body: JSON.stringify({
        id: "my-mcp-client",
        name: "My MCP Client",
        servers: {
          "github-server": { url: "https://mcp.github.com/sse" }
        }
      })
    });

    Tool Provider Routes

    New REST endpoints for browsing registered tool providers and their tools:

    • GET /api/tool-providers — List all registered tool providers with metadata
    • GET /api/tool-providers/:providerId/toolkits — List toolkits for a provider
    • GET /api/tool-providers/:providerId/tools — List tools (with optional toolkit/search filtering)
    • GET /api/tool-providers/:providerId/tools/:toolSlug/schema — Get input schema for a tool
    // List all registered tool providers
    const providers = await fetch("/api/tool-providers");
    
    // Browse tools in a specific toolkit
    const tools = await fetch("/api/tool-providers/composio/tools?toolkit=github");
    
    // Get schema for a specific tool
    const schema = await fetch("/api/tool-providers/composio/tools/GITHUB_LIST_ISSUES/schema");

    Updated stored agent schemas to include mcpClients and integrationTools conditional fields, and updated agent version tracking accordingly.

  • Fixed requestContextSchema missing from the agent list API response. Agents with a requestContextSchema now correctly include it when listed via GET /agents. (#12954)

  • Expose filesystem info from getInfo() in the GET /api/workspaces/:id API response, including provider type, status, readOnly, and provider-specific metadata. (#12971)

  • Updated dependencies [7ef618f, b373564, 927c2af, b896b41, 6415277, 0831bbb, 63f7eda, a5b67a3, 877b02c, 7567222, af71458, eb36bd8, 3cbf121]:

    • @mastra/core@1.4.0

Don't miss a new mastra release

NewReleases is sending notifications on new releases.