0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【TS × AI】Mastra + Hono の Agent サーバー設計をAIと壁打ちしてみた

0
Last updated at Posted at 2026-04-27

はじめに

こんにちは!! @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 })
        },
      }),
    ],
  },
})
  • MastraServerinit() 時に自動的に登録される
  • 認証・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 アダプター

Agent API

カスタム API ルート

ソースコード

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?