はじめに
こんにちは!! @hayatohanaoka です。
今回は、個人で LLM を使ったアプリを作るために使う Mastra というフレームワークについて、記事を書いてみました。
書いたと言っても、仕様や設計についてAIと壁打ちした内容を備忘録がてら残しているだけなんですが、これから Mastra を使おうとしている人の役に立つかもしれないので、もし良ければ見ていっていただけると嬉しいです!
Mastra とは
Mastra とは、Gatsby の創業メンバーが立ち上げた Mastra, Inc. が開発している TypeScript ベースの AI Agent 開発フレームワークです。柔軟な Agent 定義・Workflow 定義・Tool 定義に加え、Memory(会話履歴の永続化)や Observability(トレース)、MCP / A2A プロトコル対応などの機能が組み込みで提供されています。
TypeScript で書けることを活かし、React や Next.js といったフロントエンドや、Hono や Express などの Web API フレームワークとの統合が容易である点も大きな強みです。
一方、Python エコシステムに依存する ML パイプラインやモデルチューニングとの直接的な統合はやり辛さを感じるかもしれません。
この領域が絡むアプリケーションを作るのであれば Google ADK (Python) のほうが親和性が高いと言えます。
※ ADK Python v2.0.0(現在 beta)でグラフベースの Workflow が導入されるなど、両フレームワークとも急速に進化しています。上記の内容は現時点(2026年4月)でのまとめ程度にご覧ください。
Q1. MastraServer はどのようなパスを自動生成するのか?
src/index.ts で以下のように MastraServer を初期化すると、init() 時に大量の API ルートが自動登録されます。
const server = new MastraServer({ app, mastra })
await server.init()
デフォルトプレフィックス
MastraServer のコンストラクタで prefix のデフォルト値が "/api" に設定されているため、全ての自動登録ルートは /api 配下に生えます。
主要なルート一覧
Agents 系
| Method | Path | 説明 |
|---|---|---|
| GET | /api/agents |
登録エージェント一覧を取得 |
| GET | /api/agents/:agentId |
特定エージェントの情報を取得 |
| POST | /api/agents/:agentId/generate |
エージェントにテキスト生成させる |
| POST | /api/agents/:agentId/stream |
エージェントのストリーミング生成 |
| POST | /api/agents/:agentId/clone |
エージェントを複製 |
| POST | /api/agents/:agentId/tools/:toolId/execute |
エージェント配下のツールを実行 |
| GET | /api/agents/:agentId/tools/:toolId |
エージェント配下のツール情報取得 |
| GET | /api/agents/providers |
利用可能なプロバイダー一覧 |
Tools 系
| Method | Path | 説明 |
|---|---|---|
| GET | /api/tools |
全登録ツール一覧を取得 |
| GET | /api/tools/:toolId |
特定ツールの情報取得 |
| POST | /api/tools/:toolId/execute |
ツール実行(Body: { data, requestContext? }) |
Workflows 系
| Method | Path | 説明 |
|---|---|---|
| GET | /api/workflows |
全ワークフロー一覧 |
| GET | /api/workflows/:workflowId |
特定ワークフローの詳細 |
| GET | /api/workflows/:workflowId/runs |
実行履歴 |
| GET | /api/workflows/:workflowId/runs/:runId |
特定実行の詳細 |
| DELETE | /api/workflows/:workflowId/runs/:runId |
実行削除 |
| POST | /api/workflows/:workflowId/create-run |
新しい実行を作成 |
| POST | /api/workflows/:workflowId/stream |
ストリーミング実行 |
| POST | /api/workflows/:workflowId/start |
同期実行開始 |
| POST | /api/workflows/:workflowId/start-async |
非同期実行開始 |
| POST | /api/workflows/:workflowId/resume |
サスペンドされた実行を再開 |
| POST | /api/workflows/:workflowId/runs/:runId/cancel |
実行のキャンセル |
Memory 系
| Method | Path | 説明 |
|---|---|---|
| GET | /api/memory/status |
メモリステータス確認 |
| GET | /api/memory/config |
メモリ設定取得 |
| GET | /api/memory/threads |
スレッド一覧 |
| POST | /api/memory/threads |
スレッド作成 |
| GET | /api/memory/threads/:threadId |
スレッド取得 |
| PATCH | /api/memory/threads/:threadId |
スレッド更新 |
| DELETE | /api/memory/threads/:threadId |
スレッド削除 |
| GET | /api/memory/threads/:threadId/messages |
メッセージ一覧 |
| POST | /api/memory/save-messages |
メッセージ保存 |
| GET | /api/memory/search |
セマンティック検索 |
その他のルートグループ
| グループ | パスプレフィックス | 概要 |
|---|---|---|
| Auth | /api/auth/* |
認証関連 |
| A2A | /api/a2a/* |
Agent-to-Agent プロトコル |
| MCP | /api/mcp/* |
Model Context Protocol |
| Vectors | /api/vectors/* |
ベクトルストア操作 |
| Logs | /api/logs/* |
ログ取得 |
| Observability | /api/observability/* |
トレース・スパンデータ |
| System | /api/system/* |
システム情報 |
Q2. リクエストボディの JSON を元にプロンプトを組むにはどうすればよいか?
例えば {"hoge": 123} というリクエストボディを受け取り、それをプロンプトに組み込みたい場合、主に2つの方法があります。
方法1: Hono のカスタムルートで受け取り、Agent を直接呼ぶ
app.post('/my-endpoint', async (c) => {
const body = await c.req.json() // { hoge: 123 }
const agent = mastra.getAgent('weather-agent')
const result = await agent.generate(
`hogeの値は${body.hoge}です。この数値に基づいて何か提案してください。`
)
return c.json({ response: result.text })
})
方法2: 組み込み API を使い、クライアント側でプロンプトを構築する
curl -X POST http://localhost:3000/api/agents/weather-agent/generate \
-H "Content-Type: application/json" \
-d '{
"messages": [
{ "role": "user", "content": "hogeの値は123です。提案してください。" }
]
}'
Q3. 以下のような責務分担を考えている。この場合、Api からプロンプトを渡すのは歪ではないか?
質問者が想定する責務分担:
| レイヤー | 責務 |
|---|---|
| Web | UI/UX |
| Api | コアロジック・データ処理 |
| Agent | LLM へのリクエストに伴う作業 |
この分担に基づけば、Api がプロンプト文字列を組み立てて Agent に渡すのは 責務の漏れ にあたる。「プロンプトをどう組み立てるか」は LLM に関わる作業であり、Agent 側の責務だからである。
あるべき姿
Api は 構造化されたデータ を Agent に渡し、Agent が「それをどうプロンプトに落とすか」を決める。
Api → Agent: { hoge: 123 }(データをそのまま渡す)
Agent 内部: データを受け取り、プロンプトを組んで LLM に投げる
Q4. MastraServer の現在の構成でカスタムエンドポイントは追加できるのか?
結論: できる
new MastraServer({ app, mastra }) の形はそのまま維持して問題ありません。
方法1: registerApiRoute() を使う(推奨)
Mastra 公式のカスタムルート登録機能です。Mastra インスタンスの server.apiRoutes に定義します。
import { registerApiRoute } from '@mastra/core/server'
export const mastra = new Mastra({
// ...agents, workflows 等
server: {
apiRoutes: [
registerApiRoute('/my-task', {
method: 'POST',
handler: async (c) => {
const data = await c.req.json() // { hoge: 123 }
const mastra = c.get('mastra')
const agent = mastra.getAgent('weather-agent')
// プロンプト構築は Agent 側(ここ)の責務
const result = await agent.generate(
`hogeの値は${data.hoge}です。...`
)
return c.json({ response: result.text })
},
}),
],
},
})
-
MastraServerのinit()時に自動的に登録される - 認証・OpenAPI との連携もサポートされている
方法2: Hono の app に直接ルートを追加する
await server.init()
app.post('/my-task', async (c) => {
const data = await c.req.json()
const agent = mastra.getAgent('weather-agent')
const result = await agent.generate(`hogeの値は${data.hoge}です。...`)
return c.json({ response: result.text })
})
- シンプルだが Mastra の管理外になる
比較
| 方法 | 定義場所 | 特徴 |
|---|---|---|
registerApiRoute() |
Mastra 設定内 | 公式。認証・OpenAPI 連携対応 |
app.post(...) |
src/index.ts |
Hono の素の書き方。Mastra 管理外 |
責務分担の観点からは、registerApiRoute() を使って Mastra の中にカスタムルートを定義するのが、Agent レイヤーにプロンプト構築を閉じ込められて筋が良い設計です。
結論
壁打ちをした結果、
責務分担
初期の想定と変わらず
| レイヤー | 責務 |
|---|---|
| Web | UI/UX |
| Api | コアロジック・データ処理 |
| Agent | LLM へのリクエストに伴う作業 |
実装方法
Mastra 公式のカスタムルート登録機能を使った実装を選択。
Honoの方でカスタムする実装を選択すると、やりたいこと(Agent側でプロンプトを作って実行する)がシンプルに解決できなくなってしまう上、後ほど変更しづらくなったり、仕様変更を追従できなくなる可能性があるため。
import { registerApiRoute } from '@mastra/core/server'
export const mastra = new Mastra({
// ...agents, workflows 等
server: {
apiRoutes: [
registerApiRoute('/my-task', {
method: 'POST',
handler: async (c) => {
const data = await c.req.json() // { hoge: 123 }
const mastra = c.get('mastra')
const agent = mastra.getAgent('weather-agent')
// プロンプト構築は Agent 側(ここ)の責務
const result = await agent.generate(
`hogeの値は${data.hoge}です。...`
)
return c.json({ response: result.text })
},
}),
],
},
})
という結論に至りました。
参考リンク
MastraServer / Hono アダプター
- Hono Getting Started Guide - Hono で Mastra サーバーを構築する手順
-
MastraServer Reference -
MastraServerのコンストラクタオプション等の API リファレンス -
Hono Adapter Reference (v1) -
@mastra/honoアダプターのリファレンス - Creating A Mastra Server - デプロイ向けサーバー構成ガイド
Agent API
- Agents Overview - エージェントの概要と基本的な使い方
カスタム API ルート
-
Custom API Routes Guide -
registerApiRoute()を使ったカスタムルート定義のガイド -
registerApiRoute() Reference -
registerApiRoute()の API リファレンス - Server Middleware - グローバル / ルート単位のミドルウェア設定
ソースコード
- mastra-ai/mastra (GitHub) - Mastra フレームワーク本体のリポジトリ
- server-adapters/hono/src/index.ts - Hono アダプターのソースコード