Skip to main content

Tessera × Mastra

@tessera-llm/mastra is a Mastra-shaped adapter for the Tessera substrate layer. Mastra Agents accept any AI SDK provider module as the model:field — Tessera's config function returns exactly that shape, so dropping it in is a three-line edit on each agent.

Same mechanic stack (auto-route, exact + semantic cache, compress, output- length ceiling, batch arbitrage) fires server-side on every agent.generate() / agent.stream() call. Tools, structured outputs, schema-constrained tool calls, and RAG retrieval workflows all unchanged.

Install

npm install @tessera-llm/mastra @mastra/core
# Plus whichever provider package you use:
npm install @ai-sdk/openai          # or @ai-sdk/anthropic / @ai-sdk/mistral / @ai-sdk/groq / @ai-sdk/cohere

Get a free Tessera API key at tesseraai.io/dev — 60M tokens/month, no card up front.

Quickstart

import { Agent } from "@mastra/core/agent";
import { createOpenAI } from "@ai-sdk/openai";
import { tesseraOpenAIConfig } from "@tessera-llm/mastra";

const openai = createOpenAI({
  apiKey: process.env.OPENAI_API_KEY!,
  ...tesseraOpenAIConfig({ apiKey: process.env.TESSERA_API_KEY! }),
});

export const supportAgent = new Agent({
  name: "Support",
  instructions: "Triage incoming support tickets in two sentences.",
  model: openai("gpt-4o"),
});

Scenario 1 — Convenience factory — skip the explicit createOpenAI import

If you don't already use createOpenAI elsewhere in your codebase, the convenience factory wraps the import and pre-merges the Tessera config so the Agent definition stays clean.

import { Agent } from "@mastra/core/agent";
import { tesseraOpenAI } from "@tessera-llm/mastra";

const openai = await tesseraOpenAI({
  openaiApiKey: process.env.OPENAI_API_KEY!,
  tesseraApiKey: process.env.TESSERA_API_KEY!,
});

export const triageAgent = new Agent({
  name: "Triage",
  instructions: "Classify support tickets into bug, feature, billing, or other.",
  model: openai("gpt-4o-mini"),
});

const result = await triageAgent.generate("Customer says their March invoice is wrong.");

Scenario 2 — Agent with tools

Auto-route gates on tool-calling capability — an agent using tools never gets routed to a non-tool-capable model. Tool dispatch loops on long-running workflows benefit from exact-match cache on identical sub-prompts; cached responses return upstream of any content-mutating mechanic.

import { Agent } from "@mastra/core/agent";
import { createTool } from "@mastra/core/tools";
import { tesseraAnthropic } from "@tessera-llm/mastra";
import { z } from "zod";

const lookupCustomer = createTool({
  id: "lookup-customer",
  description: "Look up customer billing history",
  inputSchema: z.object({ accountId: z.string() }),
  execute: async ({ context: { accountId } }) => billingDB.lookup(accountId),
});

const anthropic = await tesseraAnthropic({
  anthropicApiKey: process.env.ANTHROPIC_API_KEY!,
  tesseraApiKey: process.env.TESSERA_API_KEY!,
});

export const billingAgent = new Agent({
  name: "Billing",
  instructions: "Resolve billing questions. Use lookup-customer when needed.",
  model: anthropic("claude-sonnet-4-6"),
  tools: { lookupCustomer },
});

Scenario 3 — Multi-agent app with one Tessera key

Register multiple Agents on the same Mastra instance, each on a different model + provider, all routed through one Tessera key. Per- workload auto-route + quality canary fire independently; the verified- savings ledger rolls up totals across every Agent into one monthly reading.

// One Tessera key powers every agent — auto-route decisions and quality
// canary stay per-workload, but billing rolls up to the same ledger.

import { Agent } from "@mastra/core/agent";
import { Mastra } from "@mastra/core";
import { tesseraOpenAI, tesseraAnthropic } from "@tessera-llm/mastra";

const openai = await tesseraOpenAI({
  openaiApiKey: process.env.OPENAI_API_KEY!,
  tesseraApiKey: process.env.TESSERA_API_KEY!,
});
const anthropic = await tesseraAnthropic({
  anthropicApiKey: process.env.ANTHROPIC_API_KEY!,
  tesseraApiKey: process.env.TESSERA_API_KEY!,
});

export const mastra = new Mastra({
  agents: {
    triage: new Agent({ name: "Triage", model: openai("gpt-4o-mini"), instructions: "..." }),
    research: new Agent({ name: "Research", model: anthropic("claude-sonnet-4-6"), instructions: "..." }),
    draft: new Agent({ name: "Draft", model: openai("gpt-4o"), instructions: "..." }),
  },
});

Worked savings example

Three-agent customer-support app on Mastra. Triage on gpt-4o-mini, Research on claude-sonnet-4-6, Draft on gpt-4o. Combined 5B tokens/month, list prices.

StageCost / moSaved
Baseline — direct providers via Mastra$24,000
+ Tessera (route, cache, prompt-cache, compress, M9 ceiling, batch)$9,400$14,600
Tessera fee (20% × measured savings)$2,920
You net pay$12,320$11,680 / mo saved

Per-stack quality canary held mean-score 0.96 across all three agent workloads (floor 0.95) — the 0.95 SLA held all 30 days.

Architecture and quality contract

Adapter is a thin npm package (Apache-2.0). The Mastra core SDK and the @ai-sdk/* provider packages are peer dependencies — install only the providers you actually use.

Composition cap (max 2 content-mutators per request), per-stack 0.95 quality floor with auto-rollback + 10% credit on breach, and audit-immutable measurement (two pricing snapshots per request) all enforced on the proxy. The verified- savings ledger is at ledger.tesseraai.io.

FAQ

Does this work with Mastra's string-shorthand model ID ("openai/gpt-4o")?
Not directly. Mastra's string shorthand uses environment variables like OPENAI_API_KEY against the provider's canonical endpoint. Tessera needs a custom baseURL + custom X-Tessera-API-Key header, which the string shorthand doesn't surface. Use the AI SDK provider factory form (shown in examples) — Mastra accepts it as a first-class alternative.
Does this break Agent tools / structured outputs / streaming / RAG?
No. The Vercel AI SDK provider object that Mastra accepts is unchanged in shape — agent.generate(), agent.stream(), generateObject, schema-constrained tool calls, and RAG retrieval workflows all work unchanged.
Why are there two API surfaces (tesseraOpenAIConfig vs tesseraOpenAI)?
The config function returns the kwargs object you spread into createOpenAI(...) — explicit, easy to combine with other settings (organization, custom fetch, etc.). The convenience factory imports createOpenAI for you and pre-merges. Use whichever you find more readable.

Where to go next