Agent frameworks

Caveman vs Mastra: token compression for Mastra agents

Keep Mastra and add Caveman Middleware: withCavemanMastra wraps your agent, shrinks large tool results before each model call, and keeps originals recoverable.

By CavemanReviewed 6 min readRead Markdown ↗
Where Caveman fits
  1. 01Keep your Mastra agent
  2. 02Wrap it once
  3. 03Shrink tool results
  4. 04Recover originals on demand

Mastra is the framework your TypeScript agent runs on. Caveman Middleware is an adapter that wraps a Mastra agent and shrinks large tool results before each model call. Keep Mastra for agents, workflows and memory, and add Caveman where tools return more text than the next step needs.

What is Mastra?

Mastra is an open-source TypeScript framework for AI agents and applications. Its homepage calls it "the leading TypeScript agent framework", and the repo has about 28k GitHub stars. The core is Apache 2.0. Enterprise features live in ee/ folders under a source-available Mastra Enterprise License that is free for development and testing.

It covers a lot of ground in one package:

  • Agents with instructions, tools built with createTool(), and models picked with a provider/model string.
  • Workflows, a graph-based engine with suspend and resume for human-in-the-loop steps.
  • Memory, including conversation history, RAG retrieval and observational memory.
  • Processors that run before and after the model, at each step of the loop.
  • Evals and observability, plus Mastra Studio for building and testing agents locally.
  • MCP server authoring.

The framework is free. Mastra's hosted platform has a $0 Starter tier (100K observability events and 24 CPU hours a month), a Teams tier at $250 a month, and custom Enterprise and self-hosted plans. Its homepage lists case studies from teams including Replit, Sanity, WorkOS and MongoDB.

Does Caveman work with Mastra?

Yes. @caveman-ai/middleware/mastra is a native adapter for @mastra/core >=1.65 <2. You pass your existing agent to withCavemanMastra and call generate or stream on the result, same as before. This is the docs example:

javascript
import { withCavemanMastra } from '@caveman-ai/middleware/mastra';
export async function run({ agent, messages, runtime, scope }) {
if (runtime.mode !== "off") await runtime.ready(); // ready() throws in off mode
const wrapped = withCavemanMastra(agent, { runtime, scope });
return wrapped.generate(messages, { maxSteps: 4 });
}

Under the hood it is built on Mastra's own processor system. On each call the wrapper adds a Caveman input processor in front of the processors you configured, keeps your prepareStep, and registers a caveman_retrieve tool built with Mastra's createTool. Your Agent config is not mutated. Right before the provider call, eligible tool-result text in the outbound prompt is swapped for a shorter version from a local Caveman runtime. Your memory and message history keep the originals. If the model needs a detail that got cut, it calls caveman_retrieve and gets the exact original back.

Tool results that failed or were denied are left alone, as are user messages, system prompts and images.

For a feel of the size change: the Caveman docs send a 400-line worker log as a tool result, and the runtime counted 8,399 tokens before and 177 to 187 after across sixteen runs. Those are inferred o200k_base counts on one synthetic log, not a bill.

Mastra vs Caveman: feature comparison

MastraCaveman Middleware
What it isTypeScript framework for agents and workflowsAdapter that shrinks tool results before model calls
LicenseApache 2.0 core, enterprise features source-availableMIT client and adapters, BSL local runtime
Calls the modelYes, through its model routerNo, Mastra still makes the call
Agents, workflows, memoryYesUses yours
Context trimmingTokenLimiter drops older messages, ToolCallFilter drops tool callsLeaves history as is
Shrinks large tool resultsNot built inYes, on the outbound copy
Getting cut content backDropped content is not sentcaveman_retrieve returns the original
ObservabilityBuilt-in traces, evals, StudioPer-call decision reports
Hosted optionMastra platformNone needed, runtime runs locally
MaturityEstablishedAlpha, 0.1.0-alpha.2

When should you use Mastra alone?

Use Mastra alone when your tools return short results. There is nothing to compress in a status code or a three-field JSON object, and Caveman will report no_candidate or not_smaller and send the original.

It is also the right call when TokenLimiter and ToolCallFilter already keep your context in check and answers stay good. Dropping old tool calls is cheap and simple. You only need something smarter when the tool results you keep are the expensive part.

One Mastra-specific note: when a call uses structured output, forces a specific tool, or limits activeTools so caveman_retrieve is excluded, the adapter does not offer the recovery tool for that call. Without recovery the runtime shortens nothing, so that call goes out unchanged. Plan your trial on a normal text-output agent first.

When does adding Caveman pay off?

It pays off when a Mastra agent reads files, calls APIs that return big JSON, runs shell commands or pulls documents. Each of those results is resent on every later step. A 20k-token API response fetched at step 2 of a 10-step run rides along on eight more model calls.

Caveman shortens it on each resend. After the first cut it resends the exact same shortened bytes, reported as reused, so the provider's cached prefix stays byte identical.

It stacks with Mastra's processors. TokenLimiter decides how much history goes out. Caveman shrinks the tool results inside what is left, at the last moment before the provider call.

If the Caveman runtime is unreachable, the call goes through with the original input. After three straight failures the adapter skips the runtime for 30 seconds. The agent keeps running either way.

How to add Caveman to a Mastra agent

  1. npm install @caveman-ai/middleware @caveman-ai/sdk
  2. Start the runtime with CAVEMAN_MODE=compress caveman start.
  3. Wrap the agent: withCavemanMastra(agent, { runtime, scope }).
  4. Log onReport and look for applied and reused.

The Mastra switch guide walks through each step with the runtime setup, scope and rollback. The supported frameworks page has the version gates, and how to measure agent cost shows how to judge the result on finished tasks.

FAQ

Does Caveman replace Mastra? No. Mastra still runs your agents, workflows, memory and model calls. Caveman only shortens tool-result text in the copy of the request sent to the provider.

Does it work with agent.stream()? Yes. withCavemanMastra wraps both generate and stream.

Will it change what Mastra stores in memory? No. The shortened text exists only in the outbound request. Your stored messages and tool results keep their original content.

Where does the scope come from? You pass one, or a function that builds it from Mastra's requestContext. Derive it from your authenticated user and conversation, never from model input.

Does Caveman need Mastra's hosted platform? No. The adapter and runtime run in your own environment, next to Mastra wherever you deploy it.

Sources

Try it on work you know

Keep your stack. Add the adapter.

Wrap the client or framework you already call, run one agent task you can check, and compare the tokens sent.

Open the middleware docs Explore comparisons ↗