# Mastra

[Mastra](https://mastra.ai) is a TypeScript agent framework with tools,
workflows, memory and evals. It has an object config for custom
OpenAI-compatible endpoints, which is the shortest path to Hyphen.

```bash
npm install @mastra/core zod
export HYPHEN_API_KEY="sk-..."
```

*Setup guide: this config comes from Mastra's official documentation and has
not been run end to end against the gateway. Corrections to [support@hyphen-solution.com](mailto:support@hyphen-solution.com).*

## The model config

```typescript
import { Agent } from "@mastra/core/agent";

export const assistant = new Agent({
  id: "assistant",
  name: "Assistant",
  instructions: "You are terse and technical. No preamble.",
  model: {
    id: "hyphen/minimax-m3",
    url: "https://api.hyphen-solution.com/v1",
    apiKey: process.env.HYPHEN_API_KEY,
  },
});

const res = await assistant.generate("Explain a Bloom filter in three sentences.");
console.log(res.text);
```

Two things to get right:

- **`url` is the base URL.** Mastra's docs are emphatic about this. Give it
  `https://api.hyphen-solution.com/v1`, never
  `https://api.hyphen-solution.com/v1/chat/completions`.
- **`id` is `provider/model`.** The part before the slash is a label; the part
  after is what gets sent upstream. `hyphen/minimax-m3` sends `minimax-m3`,
  which is what the gateway expects.

## With a tool

```typescript
import { Agent } from "@mastra/core/agent";
import { createTool } from "@mastra/core/tools";
import { z } from "zod";

export const weatherTool = createTool({
  id: "weather-tool",
  description: "Fetches current weather for a location",
  inputSchema: z.object({ location: z.string() }),
  outputSchema: z.object({
    location: z.string(),
    temperatureCelsius: z.number(),
    conditions: z.string(),
  }),
  execute: async ({ location }, { abortSignal }) => {
    const res = await fetch(`https://wttr.in/${location}?format=j1`, {
      signal: abortSignal,
    });
    const data = await res.json();
    return {
      location,
      temperatureCelsius: Number(data.current_condition[0].temp_C),
      conditions: data.current_condition[0].weatherDesc[0].value,
    };
  },
});

export const weatherAgent = new Agent({
  id: "weather-agent",
  name: "Weather Agent",
  instructions:
    "You are a weather assistant. Use weatherTool to fetch real data. Never guess.",
  model: {
    id: "hyphen/minimax-m3",
    url: "https://api.hyphen-solution.com/v1",
    apiKey: process.env.HYPHEN_API_KEY,
  },
  tools: { weatherTool },
});

const res = await weatherAgent.generate("What is the weather in Kuala Lumpur?");
console.log(res.text);
```

## Custom headers

The object config also takes `headers`, which is useful behind a corporate
proxy:

```typescript
model: {
  id: "hyphen/minimax-m3",
  url: "https://api.hyphen-solution.com/v1",
  apiKey: process.env.HYPHEN_API_KEY,
  headers: { "X-Team": "platform" },
},
```

## Handing Mastra an AI SDK provider instead

If you already build AI SDK provider instances, Mastra accepts one directly:

```typescript
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { Agent } from "@mastra/core/agent";

const hyphen = createOpenAICompatible({
  name: "hyphen",
  baseURL: "https://api.hyphen-solution.com/v1",
  apiKey: process.env.HYPHEN_API_KEY!,
});

export const assistant = new Agent({
  id: "assistant",
  name: "Assistant",
  instructions: "You are terse.",
  model: hyphen("minimax-m3"),
});
```

This works, but Mastra vendors several AI SDK majors side by side, so your
provider package's major has to match the AI SDK version Mastra expects. The
object config skips that problem entirely, which is why it is the recommended
form on this page. See [Vercel AI SDK](/build/vercel-ai-sdk) for the provider
itself.

## Per-agent models

Mastra agents each carry their own model, so mix them the way you would in any
multi-agent system. Flagship for the planner, fast model for the workers.

```typescript
const base = {
  url: "https://api.hyphen-solution.com/v1",
  apiKey: process.env.HYPHEN_API_KEY,
};

const planner = new Agent({
  id: "planner",
  name: "Planner",
  instructions: "Break the task into steps.",
  model: { id: "hyphen/minimax-m3", ...base },
});

const summariser = new Agent({
  id: "summariser",
  name: "Summariser",
  instructions: "Summarise in five bullets.",
  model: { id: "hyphen/minimax-m2.5", ...base },
});
```

Both meter at the same per-token rate. `minimax-m2.5` is the speed-and-volume
tier, so the saving is in tokens, not on the rate card. See
[Choosing a model](/choosing-a-model).

## Gotchas

- **`url` is the base URL,** not the chat endpoint.
- **`id` needs the `provider/model` shape.**
- **Give it output headroom.** The M-series reason before answering. Empty
  agent replies usually mean the output budget was spent on thinking. See
  [Choosing a model](/choosing-a-model#the-max_tokens-gotcha).
- **No embedding models,** so Mastra's vector memory needs an embedder from
  elsewhere.
- **`429`** means the monthly budget is spent. Long workflows reach it faster
  than single calls. See [Handling the 429 cap](/recipes/handling-429).

## Related

- [Mastra models](https://mastra.ai/models)
- [Mastra agents](https://mastra.ai/docs/agents/overview)
- [Vercel AI SDK](/build/vercel-ai-sdk): the layer Mastra sits on.
