AI & Development

Designing an AI Agentic Module for Your TypeScript, React or Python Project

The four patterns that make an agent run, four frameworks compared, then integration into an existing project: isolation, testing, scorers, production.

Published on February 11, 202620 min read

In 2025, chatbots generated text. In 2026, AI agents make decisions, execute actions and collaborate with each other. The difference is not cosmetic: it changes the way we build software.

If you work on a TypeScript, React, Node.js or Python project, you have probably considered adding an AI layer. But there is a wide gap between a simple LLM API call and a real agentic module. This article gives you the keys to cross that gap: the core patterns, the available frameworks and the architectural choices you need to make.

What is an AI agent (and what it is not)

A chatbot takes a question and returns an answer. An AI agent takes a goal and figures out how to reach it on its own.

In practice, an agent:

  • Reasons about the task before acting
  • Uses tools (APIs, databases, file systems) to collect information or execute actions
  • Iterates in a loop until the result is satisfactory
  • Manages context that persists across steps

A "smart" contact form that rephrases an email is not an agent. A system that analyzes a support ticket, searches the technical documentation, identifies the likely cause and proposes a fix in a pull request is.

Core architectural patterns

Before picking a framework, you need to understand the mechanisms that power an agent. Four patterns come up consistently.

The agentic loop

This is the heart of every agent. The model receives an instruction, reasons, decides whether to use a tool, observes the result, then repeats until the goal is reached. This is commonly referred to as the ReAct pattern (Reasoning + Acting).

// Pseudo-code of an agentic loop
while (!taskCompleted) {
  const reasoning = await llm.think(context)
  const action = reasoning.nextAction

  if (action.type === 'tool_call') {
    const result = await executeTool(action.tool, action.params)
    context.addObservation(result)
  } else if (action.type === 'final_answer') {
    taskCompleted = true
    return action.answer
  }
}

In practice, you will not write this loop by hand. Modern frameworks implement it for you. But understanding this mechanism is essential when debugging an agent that does not behave as expected.

Tool calling

Tool calling lets an LLM "break out of its bubble" and interact with the outside world. You declare tools with a typed input schema, and the model decides when and how to call them.

const tools = {
  searchDatabase: {
    description: 'Search the customer database',
    parameters: z.object({
      query: z.string(),
      limit: z.number().default(10),
    }),
    execute: async ({ query, limit }) => {
      return await db.clients.search(query, { limit })
    },
  },
}

All major LLMs support tool calling natively: Claude (Anthropic), GPT-5.6 (OpenAI), Gemini (Google). It has become the standard interface between a model and your code.

MCP: the protocol that standardizes everything

The Model Context Protocol (MCP) is an open standard created by Anthropic that defines how an AI agent connects to external data sources and tools. Think of it as the "USB-C of AI agents": a universal interface.

Before MCP, every integration required custom code. With MCP, a server exposes tools that any compatible client can use. The ecosystem has exploded in 2026:

  • Zapier MCP connects your agents to over 7,000 applications
  • Supabase MCP exposes your Postgres data directly to agents
  • GitHub, Slack, Notion, Jira all have official MCP servers
  • Azure MCP Server ships natively in Visual Studio 2026

The choice is straightforward: if you are building a tool that an agent needs to use, expose it via MCP. You make it compatible with the entire ecosystem at once.

Memory and context

An agent without memory forgets everything between interactions. Three types of memory coexist:

TypeDurationUsage
Working memoryDuration of a taskInstructions, intermediate results, current state of the agentic loop
Conversation memoryDuration of a sessionChat history with the user, expressed preferences
Long-term memoryPersistentLearned facts, user profiles, knowledge accumulated over time

For long-term memory, the most common technique is RAG (Retrieval-Augmented Generation): your documents are indexed in a vector database, and the agent retrieves relevant context before responding.

Framework landscape in 2026

The agentic framework ecosystem has matured significantly. Here are the most solid options based on your tech stack.

Claude Agent SDK (Anthropic)

Anthropic's official SDK gives you access to the same tools that power Claude Code. Available in TypeScript and Python.

Strengths:

  • Native MCP support (local servers, HTTP, or embedded in your app)
  • Subagents with isolated contexts and automatic coordination
  • Agent Teams (new in Opus 4.6, February 2026): decompose a task into specialized roles (planner, researcher, coder, tester) that work in parallel
  • Dynamic tool search to avoid saturating the context window
import { Agent, tool } from '@anthropic-ai/agent'
import { z } from 'zod'

const analyzeCode = tool({
  name: 'analyze_code',
  description: 'Static analysis of source code',
  inputSchema: z.object({
    filePath: z.string(),
    checks: z.array(z.enum(['security', 'performance', 'style'])),
  }),
  handler: async ({ filePath, checks }) => {
    const results = await runStaticAnalysis(filePath, checks)
    return { content: [{ type: 'text', text: JSON.stringify(results) }] }
  },
})

const agent = new Agent({
  model: 'claude-sonnet-5',
  instructions: 'You are a code review assistant.',
  tools: [analyzeCode],
})

Best for: projects built on Claude, use cases requiring multi-agent orchestration, integration with the MCP ecosystem.

Vercel AI SDK 6

Version 6 of Vercel's AI SDK makes the agent a first-class abstraction with the ToolLoopAgent class. Integration with Next.js and React is native.

Strengths:

  • ToolLoopAgent: turnkey agentic loop with automatic tool call handling (up to 20 steps by default)
  • Extensible Agent interface to build your own abstractions
  • Native streaming for reactive React interfaces
  • Model-agnostic (OpenAI, Anthropic, Google, Mistral)
import { ToolLoopAgent } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'

const agent = new ToolLoopAgent({
  model: anthropic('claude-sonnet-5'),
  instructions: 'You are a project planning assistant.',
  tools: {
    createTask: {
      description: 'Create a task in the project manager',
      parameters: z.object({
        title: z.string(),
        assignee: z.string(),
        priority: z.enum(['low', 'medium', 'high']),
      }),
      execute: async (params) => await projectManager.createTask(params),
    },
  },
  maxSteps: 15,
})

const result = await agent.generate({
  prompt: 'Plan next week sprint.',
})

Best for: existing Next.js / React projects, teams that want to stay LLM-agnostic, rapid prototyping.

Google Agent Development Kit (ADK)

ADK is Google's open-source framework, available in TypeScript and Python. It is optimized for Gemini but compatible with other models.

Strengths:

  • Native multi-agent architecture with supervisor and sub-agent patterns
  • End-to-end TypeScript typing
  • Built-in CLI and Developer UI for debugging
  • Bidirectional audio/video streaming
  • Deploy anywhere: containers, serverless, Google Cloud Run

Best for: projects tied to the Google Cloud ecosystem, applications requiring multimedia streaming, teams that want advanced debugging tools.

Mastra

An open-source TypeScript framework that stands out with its "batteries included" approach. It combines agents, workflows and observability in a single package.

Strengths:

  • Declarative workflows as state machines (branching, pause, resume)
  • Built-in memory system (short-term and long-term)
  • Native RAG with a unified vector store API
  • Built-in observability and scoring to measure response quality
  • Documentation embedded in the npm package to facilitate work with AI tools

Best for: TypeScript/Node.js projects that want an all-in-one framework, teams that value agent observability.

How to choose?

CriteriaClaude Agent SDKVercel AI SDK 6Google ADKMastra
LanguageTypeScript, PythonTypeScriptTypeScript, PythonTypeScript
Default LLMClaudeAgnosticGeminiAgnostic
Multi-agentAgent TeamsVia compositionNative supervisorsNative supervisors
MCPNativeVia pluginsNativeNative
ObservabilityBasicThird-partyDeveloper UIBuilt-in
MaturityHighHighMediumMedium
Use caseComplex agentsReact/Next appsGoogle ecosystemAll-in-one Node.js

The choice comes down to three factors: your existing stack, the LLM you are targeting and the orchestration complexity you need. For a React/Next.js project, Vercel AI SDK 6 offers the smoothest integration. For advanced multi-agent orchestration, the Claude Agent SDK or Google ADK will be a better fit.

Integrating an agent into an existing project

Adding an agentic module to an existing project should not require a rewrite. Here are the principles that work in practice.

Start with a narrow scope

Do not build a general-purpose agent. Identify a repetitive, well-defined task in your application, then automate it. Examples:

  • An agent that analyzes customer feedback and categorizes tickets
  • An agent that generates weekly reports from structured data
  • An agent that assists developers during code review

Isolate the agentic module

Your agent should be a self-contained module, decoupled from the rest of the application. In TypeScript:

// src/agents/support-agent/index.ts
export class SupportAgent {
  private agent: ToolLoopAgent

  constructor(config: SupportAgentConfig) {
    this.agent = new ToolLoopAgent({
      model: config.model,
      instructions: SUPPORT_AGENT_PROMPT,
      tools: this.buildTools(config),
      maxSteps: config.maxSteps ?? 10,
    })
  }

  async analyzeTicket(ticket: SupportTicket): Promise<TicketAnalysis> {
    const result = await this.agent.generate({
      prompt: `Analyze this support ticket: ${JSON.stringify(ticket)}`,
    })
    return parseAnalysis(result.text)
  }

  private buildTools(config: SupportAgentConfig) {
    return {
      searchDocs: createDocSearchTool(config.docsIndex),
      getClientHistory: createClientHistoryTool(config.db),
      createFollowUp: createFollowUpTool(config.ticketSystem),
    }
  }
}

This encapsulation allows you to:

  • Test the agent independently from the rest of the application
  • Swap the framework or model without touching the calling code
  • Control permissions and accessible tools precisely

Handle errors and limits

An agent can fail in several ways: the LLM hallucinates, a tool returns an error, the loop runs forever. Plan for:

  1. A maximum number of iterations to prevent infinite loops
  2. A global timeout on agent execution
  3. Output validation before using results in your application
  4. A fallback to deterministic behavior if the agent fails
const result = await Promise.race([
  agent.analyzeTicket(ticket),
  timeout(30_000, 'Agent timeout'),
])

if (!isValidAnalysis(result)) {
  return fallbackAnalysis(ticket)
}

Testing an agent

Testing an agent is different from testing deterministic code. Three levels of tests are useful:

  • Unit tests on tools: each tool must work correctly with valid and invalid inputs
  • Integration tests with the LLM: verify that the agent calls the right tools in the right order for known scenarios
  • Regression tests on outputs: store expected input/output pairs and measure deviation
describe('SupportAgent', () => {
  it('should consult documentation for a technical bug', async () => {
    const agent = new SupportAgent(testConfig)
    const result = await agent.analyzeTicket(technicalBugTicket)

    expect(result.category).toBe('technical_bug')
    expect(result.toolsCalled).toContain('searchDocs')
    expect(result.suggestedAction).toBeDefined()
  })
})

Scoring your agent's outputs

Tests tell you whether the agent works. Scorers tell you how well it works. The distinction matters: an agent can call the right tools in the right order and still produce a response that is technically correct but useless, vague or off-topic.

A scorer is a function that takes your agent's output and returns a score between 0 and 1. Two approaches coexist:

  • Deterministic scorers: hard-coded rules (keyword presence, valid JSON format, response length, Levenshtein distance). Fast, free, reproducible.
  • LLM-as-a-judge: an LLM evaluates another LLM's output against natural language criteria (relevance, accuracy, tone). More nuanced, but slower and more expensive.
import { Factuality } from 'autoevals'

// Deterministic scorer: checks response structure
const formatScorer = ({ output }: { output: string }) => {
  const parsed = JSON.parse(output)
  const hasRequiredFields = 'category' in parsed && 'priority' in parsed
  return { name: 'Format', score: hasRequiredFields ? 1 : 0 }
}

// LLM-as-a-judge: evaluates factual accuracy
const factualityScore = await Factuality({
  input: 'What is the standard delivery time?',
  output: agent.lastResponse,
  expected: 'Standard delivery takes 3 to 5 business days.',
})
// factualityScore.score → 0.0 to 1.0

In practice, combine both. Deterministic scorers act as fast guardrails (format, safety, length). LLM-as-a-judge scorers measure subjective dimensions (relevance, clarity, tone).

Libraries like autoevals (Braintrust) provide ready-made scorers: factuality, semantic similarity, moderation detection, summary quality. Mastra integrates a scorer system directly into its framework with the createScorer function. For the Vercel AI SDK, combining it with Vitest lets you integrate evals into your CI pipeline.

The goal is not to achieve a perfect score, but to detect regressions. Every new prompt, every tool or model change can degrade quality without a traditional test catching it. Scorers make that degradation visible and measurable.

What changes in production

Taking an AI agent to production introduces specific constraints.

Cost. Each iteration of the agentic loop consumes tokens. A misconfigured agent running in a loop can generate an unexpected bill. Monitor token count per request and set budgets per agent.

Latency. An agent chaining 5 tool calls with a remote LLM takes time. Design your UX accordingly: stream intermediate results, show progress indicators, and let the user interrupt execution.

Security. An agent with access to your tools is as powerful as it is dangerous. Apply the principle of least privilege: each agent should only have access to the tools strictly necessary for its task. Validate tool call parameters server-side.

Observability. Log every step of the agentic loop: the model's reasoning, the tools called, the results obtained. Without these logs, debugging unexpected behavior is nearly impossible.

Conclusion

Agentic AI is no longer a research concept. Frameworks are mature, patterns are documented, and the MCP ecosystem provides a standardized integration layer. The real challenge is no longer technical, it is strategic: identifying the right tasks to automate and designing agents that integrate cleanly into your existing architecture.

Key takeaways:

  • An AI agent is not a chatbot: it reasons, uses tools and iterates
  • Four core patterns: agentic loop, tool calling, MCP, memory
  • Choose your framework based on your stack and orchestration needs
  • Isolate your agentic module to keep your codebase maintainable
  • In production, monitor cost, latency and security

Integrating an agentic module into an existing project is a measured investment that can radically transform the value of your product. The most important thing is to start small, with a concrete task, and iterate. This is exactly the scope I frame and ship on an AI-first web products engagement.