Back to Blog

Building AI Agents with TypeScript: Complete Guide

Ultrion TeamJuly 18, 202611 min read

Building AI Agents with TypeScript: Complete Guide

How to build production AI agents using TypeScript and the modern AI stack.


TypeScript is becoming the language of choice for AI agent development. With excellent type safety, a rich ecosystem, and native MCP support, TypeScript offers the best developer experience for building AI agents in 2026.


Why TypeScript for AI Agents?

  • Type safety β€” Catch errors at compile time, not runtime
  • MCP SDK β€” First-class TypeScript SDK available
  • Vercel AI SDK β€” Powerful streaming and tool-calling utilities
  • Mastra β€” Production agent framework built for TypeScript
  • Edge runtime β€” Deploy to edge nodes globally
  • Shared types β€” Define schemas once, use everywhere

Project Setup

mkdir my-agent && cd my-agent
npm init -y

# Core dependencies
npm install @modelcontextprotocol/sdk zai ai @ai-sdk/openai @ai-sdk/anthropic

# Utilities
npm install zod dotenv pino

# Development
npm install -D typescript @types/node tsx
npx tsc --init
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "outDir": "./dist",
    "types": ["node"]
  }
}

Building Your First Agent

Basic Agent

import { Agent } from "mastra";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

const agent = new Agent({
  name: "Research Assistant",
  model: openai("gpt-4o"),
  instructions: `You are a research assistant. Find information,
    analyze it, and provide well-sourced answers.`,
  tools: {
    search: {
      description: "Search the web",
      parameters: z.object({
        query: z.string(),
      }),
      execute: async ({ query }) => {
        return await searchAPI(query);
      },
    },
    summarize: {
      description: "Summarize a long text",
      parameters: z.object({
        text: z.string(),
        maxLength: z.number().default(200),
      }),
      execute: async ({ text, maxLength }) => {
        return text.slice(0, maxLength);
      },
    },
  },
});

// Run the agent
const result = await agent.generate("What are the latest AI trends?");
console.log(result.text);

Streaming Responses

const stream = await agent.stream("Explain MCP protocol");

for await (const chunk of stream) {
  process.stdout.write(chunk);
}

Adding MCP Tools

import { McpClient } from "@modelcontextprotocol/sdk";

// Connect to MCP server
const mcpClient = new McpClient({
  serverUrl: "https://my-mcp-server.example.com/sse",
  authToken: process.env.MCP_TOKEN,
});

// Discover available tools
const tools = await mcpClient.listTools();

// Use in agent
const agent = new Agent({
  name: "MCP-Powered Agent",
  model: openai("gpt-4o"),
  tools: Object.fromEntries(
    tools.map(tool => [
      tool.name,
      {
        description: tool.description,
        parameters: tool.inputSchema,
        execute: async (params) => {
          const result = await mcpClient.callTool(tool.name, params);
          return result.content;
        },
      },
    ])
  ),
});

State Management

import { AgentState } from "mastra";

// Define state schema
const stateSchema = z.object({
  conversationHistory: z.array(z.object({
    role: z.enum(["user", "assistant", "tool"]),
    content: z.string(),
    timestamp: z.string(),
  })),
  userPreferences: z.record(z.string()),
  currentTask: z.string().nullable(),
  completedTasks: z.array(z.string()),
});

// Stateful agent
class StatefulAgent {
  private state: AgentState<z.infer<typeof stateSchema>>;

  constructor() {
    this.state = new AgentState({
      initial: {
        conversationHistory: [],
        userPreferences: {},
        currentTask: null,
        completedTasks: [],
      },
      persist: true,  // Persist to Redis
      ttl: 3600,      // 1 hour
    });
  }

  async process(message: string): Promise<string> {
    const state = await this.state.get();

    // Add to history
    state.conversationHistory.push({
      role: "user",
      content: message,
      timestamp: new Date().toISOString(),
    });

    // Process with context
    const response = await agent.generate(message, {
      context: state.conversationHistory,
    });

    // Update state
    state.conversationHistory.push({
      role: "assistant",
      content: response.text,
      timestamp: new Date().toISOString(),
    });

    // Trim history if too long
    if (state.conversationHistory.length > 20) {
      state.conversationHistory = state.conversationHistory.slice(-15);
    }

    await this.state.set(state);
    return response.text;
  }
}

Multi-Agent Systems

class MultiAgentSystem {
  private agents: Map<string, Agent>;

  constructor() {
    this.agents = new Map();
  }

  register(name: string, agent: Agent) {
    this.agents.set(name, agent);
  }

  async route(request: string): Promise<Agent> {
    // Use a classifier to pick the right agent
    const classifier = this.agents.get("classifier")!;
    const result = await classifier.generate(
      `Which agent should handle this? Options: ${[...this.agents.keys()].join(", ")}\nRequest: ${request}`
    );

    const agentName = result.text.trim();
    return this.agents.get(agentName) || this.agents.get("default")!;
  }

  async process(request: string): Promise<string> {
    const agent = await this.route(request);
    return await agent.generate(request).then(r => r.text);
  }
}

// Setup
const system = new MultiAgentSystem();
system.register("classifier", classifierAgent);
system.register("research", researchAgent);
system.register("writer", writingAgent);
system.register("analyst", analysisAgent);
system.register("default", generalAgent);

Error Handling

class ResilientAgent {
  async generate(prompt: string, retries = 3): Promise<string> {
    for (let i = 0; i < retries; i++) {
      try {
        return await this.agent.generate(prompt).then(r => r.text);
      } catch (error) {
        if (error instanceof RateLimitError) {
          await sleep(error.retryAfter * 1000);
        } else if (error instanceof ContextLengthError) {
          prompt = await this.compressContext(prompt);
        } else if (i === retries - 1) {
          throw error;
        }
      }
    }
    throw new Error("Max retries exceeded");
  }
}

Testing

import { describe, it, expect } from "vitest";

describe("Research Agent", () => {
  it("should use search tool for factual queries", async () => {
    const result = await agent.generate("What is the current EU GDP?");
    expect(result.toolCalls).toBeDefined();
    expect(result.toolCalls![0].name).toBe("search");
  });

  it("should provide sources", async () => {
    const result = await agent.generate("What are the latest AI trends?");
    expect(result.text).toContain("http");  // Contains URLs
  });
});

Deployment

Deploy to Vercel

// app/api/agent/route.ts
import { Agent } from "mastra";
import { openai } from "@ai-sdk/openai";

const agent = new Agent({
  name: "API Agent",
  model: openai("gpt-4o"),
  instructions: "You are a helpful assistant.",
});

export async function POST(request: Request) {
  const { message } = await request.json();
  const result = await agent.generate(message);
  return Response.json({ response: result.text });
}

Deploy as MCP Server

import { McpServer } from "@modelcontextprotocol/sdk";

const server = new McpServer({ name: "my-agent-tools" });

server.tool("answer_question", {
  question: z.string(),
}, async (args) => {
  const result = await agent.generate(args.question);
  return { content: [{ type: "text", text: result.text }] };
});

server.run({ transport: "http", port: 8000 });

Conclusion

TypeScript offers the best developer experience for building AI agents β€” type safety, excellent tooling, and native MCP support. With frameworks like Mastra and the Vercel AI SDK, you can build production agents quickly and safely.


Learn More

Build and publish agents on SkillExchange.

Newsletter

Enjoying this article?

Get weekly insights on building and selling AI skills, MCP tools, and creator economics. Join 2,000+ AI builders and creators.

No spam. Unsubscribe anytime.

Related Articles

Ready to try AI skills?

Browse the marketplace and discover skills for your AI agents.

Browse Skills