All skills
jwynia avatar

/mastra-hono

@99a8797
by J Wyniajwynia/agent-skills160 stars
20

Develop AI agents, tools, and workflows with Mastra v1 Beta and Hono servers. This skill should be used when creating Mastra agents, defining tools with Zod schemas, building workflows with step data flow, setting up Hono API servers with Mastra adapters, or implementing agent networks. Keywords: mastra, hono, agent, tool, workflow, AI, LLM, typescript, API, MCP.

Use this Skill: https://skilld.dev/gh/jwynia/agent-skills/mastra-hono

This session only. Nothing lands on disk.

referenceshono-server-patterns.md

≈2.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Hono Server Patterns

Complete guide to setting up and configuring Hono servers with Mastra.

Basic Setup

Minimal Server

// src/index.ts
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { MastraServer } from "@mastra/hono";
import { mastra } from "./mastra/index.js";

const app = new Hono();
const server = new MastraServer({ app, mastra });
await server.init();  // Don't forget await!

app.get("/", (c) => c.text("Mastra + Hono Server"));

serve({ fetch: app.fetch, port: 3000 });

Auto-Generated Endpoints

After server.init(), these endpoints are available:

Endpoint Method Description
/api/agents/{name}/generate POST Generate response from agent
/api/agents/{name}/stream POST Stream response from agent
/api/workflows/{id}/start POST Start workflow run
/api/workflows/{id}/{runId}/status GET Get workflow run status

Request Format

# Agent generate
curl -X POST http://localhost:3000/api/agents/weather-agent/generate \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      { "role": "user", "content": "What is the weather in Tokyo?" }
    ]
  }'

# Agent stream
curl -X POST http://localhost:3000/api/agents/weather-agent/stream \
  -H "Content-Type: application/json" \
  -d '{"messages": [{"role": "user", "content": "Tell me about Seattle"}]}'

# Workflow start
curl -X POST http://localhost:3000/api/workflows/data-pipeline/start \
  -H "Content-Type: application/json" \
  -d '{"inputData": {"value": 42}}'

Server Configuration

Custom Route Prefix

const server = new MastraServer({
  app,
  mastra,
  prefix: "/v1/ai",  // Routes at /v1/ai/agents/...
});

// Now endpoints are:
// /v1/ai/agents/{name}/generate
// /v1/ai/workflows/{id}/start

Mastra Instance Configuration

// src/mastra/index.ts
import { Mastra } from "@mastra/core/mastra";
import { LibSQLStore } from "@mastra/libsql";

export const mastra = new Mastra({
  agents: { weatherAgent, assistantAgent },
  workflows: { dataPipeline, reportGenerator },
  storage: new LibSQLStore({
    url: "file:./mastra.db",
  }),
  server: {
    port: 3000,      // Defaults to 4111
    timeout: 30000,  // Request timeout (default: 3 minutes)
  },
  cors: {
    origin: ["https://example.com"],
    allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
    allowHeaders: ["Content-Type", "Authorization"],
    credentials: false,
  },
});

Custom API Routes

Basic Custom Route

import { registerApiRoute } from "@mastra/core/server";

registerApiRoute("/health", {
  method: "GET",
  handler: async (c) => {
    return c.json({ status: "healthy", timestamp: Date.now() });
  },
});

Accessing Mastra in Routes

registerApiRoute("/custom-agent-call", {
  method: "POST",
  handler: async (c) => {
    const mastra = c.get("mastra");
    const body = await c.req.json();

    const agent = mastra.getAgent("my-agent");
    if (!agent) {
      return c.json({ error: "Agent not found" }, 404);
    }

    const result = await agent.generate(body.message);
    return c.json({ response: result.text });
  },
});

Running Workflows from Routes

registerApiRoute("/process-data", {
  method: "POST",
  handler: async (c) => {
    const mastra = c.get("mastra");
    const body = await c.req.json();

    const workflow = mastra.getWorkflow("data-pipeline");
    const run = workflow.createRun();

    const result = await run.start({
      inputData: { data: body.data },
    });

    return c.json({
      status: result.status,
      output: result.steps?.["final-step"]?.output,
    });
  },
});

Route with Middleware

import { bearerAuth } from "hono/bearer-auth";

registerApiRoute("/protected-route", {
  method: "POST",
  middleware: [
    bearerAuth({ token: process.env.API_TOKEN! }),
  ],
  handler: async (c) => {
    const mastra = c.get("mastra");
    // ... protected logic
    return c.json({ success: true });
  },
});

Middleware Patterns

Global Middleware

const app = new Hono();

// Add middleware before MastraServer.init()
app.use("*", async (c, next) => {
  console.log(`${c.req.method} ${c.req.url}`);
  await next();
});

// CORS middleware
import { cors } from "hono/cors";
app.use("*", cors({
  origin: ["https://example.com"],
  allowMethods: ["GET", "POST", "OPTIONS"],
}));

// Then initialize Mastra
const server = new MastraServer({ app, mastra });
await server.init();

Authentication Middleware

import { bearerAuth } from "hono/bearer-auth";

// Protect all /api routes
app.use("/api/*", bearerAuth({ token: process.env.API_TOKEN! }));

// Or use JWT
import { jwt } from "hono/jwt";
app.use("/api/*", jwt({ secret: process.env.JWT_SECRET! }));

Rate Limiting

// Custom rate limiter middleware
const rateLimiter = new Map<string, { count: number; resetAt: number }>();

app.use("/api/*", async (c, next) => {
  const ip = c.req.header("x-forwarded-for") || "unknown";
  const now = Date.now();
  const limit = rateLimiter.get(ip);

  if (limit && now < limit.resetAt) {
    if (limit.count >= 100) {
      return c.json({ error: "Rate limit exceeded" }, 429);
    }
    limit.count++;
  } else {
    rateLimiter.set(ip, { count: 1, resetAt: now + 60000 });
  }

  await next();
});

Request Logging

import { logger } from "hono/logger";

app.use("*", logger());

RuntimeContext Pattern

Setting Context at Request Level

import { RuntimeContext } from "@mastra/core";

registerApiRoute("/user-query", {
  method: "POST",
  handler: async (c) => {
    const mastra = c.get("mastra");
    const body = await c.req.json();
    const userId = c.req.header("x-user-id");

    // Create runtime context with request-specific values
    const runtimeContext = new RuntimeContext();
    runtimeContext.set("user-id", userId);
    runtimeContext.set("user-tier", body.tier || "free");
    runtimeContext.set("request-id", crypto.randomUUID());

    const agent = mastra.getAgent("my-agent");
    const result = await agent.generate(body.message, {
      runtimeContext,
    });

    return c.json({ response: result.text });
  },
});

Accessing Context in Tools

export const premiumTool = createTool({
  id: "premium-feature",
  execute: async (inputData, context) => {
    const { runtimeContext } = context;
    const tier = runtimeContext.get("user-tier");

    if (tier !== "premium") {
      throw new Error("Premium subscription required");
    }

    // Premium logic...
    return { result: "Premium result" };
  },
});

OpenAPI Documentation

Enabling OpenAPI

const server = new MastraServer({
  app,
  mastra,
  openapiPath: "/openapi.json",  // Enable OpenAPI spec
});
await server.init();

// Access spec at http://localhost:3000/openapi.json

Adding Swagger UI

import { swaggerUI } from "@hono/swagger-ui";

app.get("/docs", swaggerUI({ url: "/openapi.json" }));

Error Handling

Global Error Handler

app.onError((err, c) => {
  console.error("Server error:", err);

  if (err.message.includes("not found")) {
    return c.json({ error: "Resource not found" }, 404);
  }

  if (err.message.includes("unauthorized")) {
    return c.json({ error: "Unauthorized" }, 401);
  }

  return c.json({ error: "Internal server error" }, 500);
});

Not Found Handler

app.notFound((c) => {
  return c.json({ error: "Route not found" }, 404);
});

Deployment Patterns

Node.js Server

import { serve } from "@hono/node-server";

serve({
  fetch: app.fetch,
  port: parseInt(process.env.PORT || "3000"),
});

Bun Server

export default {
  fetch: app.fetch,
  port: parseInt(process.env.PORT || "3000"),
};

Cloudflare Workers

export default app;

Docker Configuration

FROM node:22-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

EXPOSE 3000
CMD ["node", "dist/index.js"]

Health Checks

registerApiRoute("/health", {
  method: "GET",
  handler: async (c) => {
    const mastra = c.get("mastra");

    // Check storage connection
    try {
      await mastra.storage?.getThreads({ limit: 1 });
    } catch (err) {
      return c.json({ status: "unhealthy", error: "Storage unavailable" }, 503);
    }

    return c.json({
      status: "healthy",
      timestamp: new Date().toISOString(),
      version: process.env.npm_package_version,
    });
  },
});

registerApiRoute("/ready", {
  method: "GET",
  handler: async (c) => {
    return c.json({ ready: true });
  },
});

Streaming Responses

Server-Sent Events (SSE)

import { streamSSE } from "hono/streaming";

registerApiRoute("/stream-updates", {
  method: "GET",
  handler: async (c) => {
    return streamSSE(c, async (stream) => {
      for (let i = 0; i < 10; i++) {
        await stream.writeSSE({
          data: JSON.stringify({ count: i }),
          event: "update",
        });
        await new Promise((r) => setTimeout(r, 1000));
      }
    });
  },
});

Streaming Agent Response

registerApiRoute("/chat-stream", {
  method: "POST",
  handler: async (c) => {
    const mastra = c.get("mastra");
    const body = await c.req.json();

    const agent = mastra.getAgent("chat-agent");
    const stream = await agent.stream(body.message);

    return new Response(stream.textStream, {
      headers: { "Content-Type": "text/event-stream" },
    });
  },
});

Source: SKILL.md on GitHub

2 warnings16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill provides a comprehensive developer toolkit for building AI agents and workflows with Mastra v1 Beta and Hono servers. It includes scaffolding scripts, code templates, and detailed documentation on best practices for agent orchestration and data flow. All external dependencies are from well-known technology organizations and the code patterns align with standard software development practices.

  • Socket16d

    1 alert: gptAnomaly

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    22/22 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 99a8797. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Dormantupdated 8 months ago
compatibility
Node.js 22.13.0+ required for v1 Beta
Other metadata
metadata
{
  "author": "agent-skills",
  "version": "1.0",
  "type": "utility",
  "mode": "assistive",
  "domain": "development"
}

README badge

README badge for jwynia/agent-skills/mastra-hono