Agent building: 150 lines of TypeScript code
Building a working AI agent requires just 150 lines of TypeScript code, not complex frameworks. While the GitHub repository "awesome-ai-agents-2026" curates over 300 tools, true understanding comes from stripping away the bloat to reveal the core mechanics. Type-safe architectures are the single most critical component for preventing bugs in modern AI agent development; simplicity often outperforms bloated ecosystems.
You will learn how strong typing enforces reliability when defining tool interfaces and managing conversation memory systems. We will structure a Message interface that keeps history intact without crashing the application.
Finally, we implement a functional research assistant using Node.js 18 and the @anthropic-ai/sdk. You will see how to replace mock data with real API calls from providers like SerpAPI or Brave Search while maintaining a clean error-handling strategy. By the end, you will possess a fully operational script that handles multi-step tasks, demonstrating that the barrier to entry for building intelligent systems is lower than the industry claims.
The Role of Type-Safe Architectures in Modern AI Agent Development
Defining AI Agent Message and Tool Interfaces in TypeScript
Clear communication boundaries between an AI model and its execution environment start with strict TypeScript interfaces. Create a src/types.ts file containing two specific interfaces: Message and Tool. The Message interface holds a role (either 'user' or 'assistant') plus content as a string, guaranteeing the LLM API receives properly formatted conversation history. Changes to tool functionality break existing code paths immediately, notifying developers before deployment. This agent accepts user questions, invokes tools like web search on demand, retains conversation history, and manages errors without crashing.
The Tool interface mandates name, description, input_schema, and an execute function returning a Promise. Such a schema forces a contract where the agent maps tool definitions directly to API requests. Dynamic languages permit loose argument passing, yet this rigidity removes a category of runtime errors where missing parameters trigger silent failures. Increased initial verbosity represents the cost; developers must define JSON schemas for every capability beforehand. Immediate feedback during refactoring emerges from this upfront effort, offering a distinct advantage when orchestrating complex multi-step tasks across diverse domains or enterprise workflows. Explicit types guarantee the agent processes structured data matching the set schema.
Building a 150-Line Research Assistant Without Frameworks
Orchestrating tool use and memory without heavy frameworks requires approximately 150 lines of TypeScript to construct a functional research assistant. Architecture depends on a Tool interface defining an execute function that returns a Promise, allowing safe handling of asynchronous web search operations. Enforcing strict types on the input_schema stops runtime errors when the LLM generates malformed arguments for external APIs. This method contrasts with the trend toward complex team-based orchestration found in multi-agent systems, favoring a single, verifiable control loop instead.
Implementation manages conversation history by appending user queries and assistant responses to a local array before each API call. This pattern ensures the model retains context across multi-step tasks while keeping state management explicit. Relying on a single process for state introduces a limitation: the agent loses memory if the Node.js process restarts, unlike distributed systems that persist state to external databases. Builders asking if they should use TypeScript for AI agents find the answer in the need for type safety when integrating flexible tools. Specialized niches like Web3 or enterprise often demand distinct frameworks, yet a lightweight TypeScript implementation offers sufficient rigor for custom research tasks. Manual verbosity exchanges for total control over the execution flow.
Internal Mechanics of Tool-Calling and Conversation Memory Systems
Agent Runner Logic and Tool Use Stop Reasons
The chat method initiates requests with a max_tokens limit of 4096 to constrain output generation. A response bearing the stop_reason of tool_use triggers the handleToolUse method to process external function calls. This conditional branch parses the response content for tool blocks, executes the mapped logic, and returns results to the model for final synthesis.
Such a loop enables flexible tool-augmented chatbots by deferring logic execution to the model's discretion. Relying on stop_reason parsing introduces latency because every tool call requires a round-trip API interaction before the user receives an answer. The drawback is a measurable response delay compared to direct function invocation patterns.
Maintaining State with Conversation History Arrays
The conversationHistory array preserves context by storing every user and assistant message sequentially. This stateful mechanism allows the agent to reference prior turns when formulating responses. When the model requests external data, the handleToolUse method filters content for tool_use blocks and executes the corresponding logic. The system then appends these results back into the history before requesting a final synthesis. Flexible tool-augmented chatbots act on current information while retaining conversational continuity through this process.
Meanwhile, the TypeScript implementation enforces strict typing on the Message interface, preventing schema drift during complex multi-turn exchanges. Unbounded array growth eventually exceeds the context window of the underlying model, and latency rises with the token count at every interaction.
Memory management becomes a primary constraint on agent complexity as a direct operational consequence. Storing full history for thousands of concurrent users requires careful architectural planning due to the associated costs.
Security Risks of Eval in Calculator Tool Implementation
The reference implementation adds a calculatorTool in src/tools/calculator.ts which uses eval to perform mathematical calculations. Using JavaScript's eval function for mathematical operations introduces a remote code execution vector in production agents. The reference architecture demonstrates a searchTool in src/tools/search.ts that currently returns mock data, noting that for production, users should integrate with APIs like SerpAPI, Brave Search, or Tavily. Deployed systems relying on mock data or unsanitized inputs allow arbitrary command injection if user input is not strictly sanitized. Developers should replace mock implementations with dedicated, safe API integrations to enforce secure arithmetic and data evaluation. In production, a safe math library like mathjs belongs in that path instead of eval.
In practice, the handleToolUse method mitigates total system failure by catching exceptions and returning error strings rather than crashing the runtime. When the agent requests a tool that the registry cannot find, the logic returns a specific "Tool not found" message to the model. This error handling strategy prevents the agent from entering infinite retry loops when facing missing dependencies or typos in tool names.
Runtime validation alone leaves the application exposed to prompt injection attacks targeting the tool inputs.
Implementing a Functional Research Agent with Web Search Integration
Defining the search_web Tool Schema and Mock Execution
Construct the src/tools/search.ts module to export a searchTool object enforcing a strict input_schema requiring a query string. This definition moves the implementation beyond simple text generation toward action execution by standardizing how the agent requests external data.
- Declare the tool name as
search_webwith a description instructing usage for facts or recent events. - Define the input_schema properties to accept only a single string argument, preventing malformed requests.
- Implement the
executefunction to return mock data simulating a successful search result. - Log the search query to the console before returning the static response array.
Production environments demand integration with external search providers while maintaining the same interface signature. The rigid schema ensures that even as backend providers change, the agent's tool-calling interfaces remain stable and type-safe.
Assembling the Agent Runner with System Prompts and History
The src/agent.ts file aggregates the system prompt and conversation history to initialize the agent runner. This file imports the searchTool and configures the Agent class. The set prompt acts as a behavioral constraint, ensuring the LLM prioritizes tool usage for factual queries over internal parametric memory.
- Import the
searchToolfrom the local tools module to enable external data retrieval. - Instantiate the
Agentwith a system prompt and the available tools list. - Execute the main loop using
npx tsx src/index.tsto start the interactive session.
This assembly pattern supports tool registration and invocation within the runtime. The execution flow forces a decision at every turn: the model must either answer directly or invoke the search_web tool, creating an auditable trace of reasoning. A significant limitation arises here; without persistent storage backends, the conversation history remains volatile and resets upon process termination. The class maintains a conversationHistory array to preserve context only for the duration of the session.
Prerequisites Checklist: Node.js Version, API Keys, and Dependencies
Establishing a functional research agent requires Node.js 18 as the minimum runtime to support modern TypeScript features used in async tool execution.
- Install the core SDK
@anthropic-ai/sdkalongsidedotenvfor secure environment variable management. - Add development dependencies including
typescript,@types/node, andtsxto enable rapid iteration. - Configure an
.envfile with yourANTHROPIC_API_KEYbefore initializing the TypeScript compiler.
| Dependency Type | Package Name | Function |
|---|---|---|
| Runtime | @anthropic-ai/sdk | LLM API client |
| Runtime | dotenv | Secret injection |
| Dev | tsx | TS execution |
The required dependencies are @anthropic-ai/sdk and dotenv, while dev dependencies include typescript, @types/node, and tsx, with the entire setup estimated to take approximately five minutes. Relying on static responses during early prototyping isolates logic errors from network latency issues. Implementing strong error handling in the execute function prevents the entire agent from crashing if a single tool call fails.
Resolving Common Configuration Errors and Deployment Limitations
Diagnosing Missing API Key and Module Resolution Errors
The error ANTHROPIC_API_KEY not found surfaces when the .env file sits outside the project root, blocking the dotenv library from loading secrets before the TypeScript runtime initializes. Credentials must reside in the root directory so the agent constructor validates them immediately upon startup. A missing module error signals that Node.js cannot resolve dependencies because the local node_modules directory lacks the required packages. Running npm install populates this directory, satisfying import statements for @anthropic-ai/sdk and linked libraries.
Misconfiguration prevents the system from authenticating requests or locating tool implementations like search_web. Skipping dependency management breaks the execution chain entirely. Operational stability depends on strict adherence to file placement and package installation protocols.
Configuration oversights incur specific penalties:
- Debugging sessions expand due to silent environment variable failures.
- Agent downtime occurs when dependency trees remain unresolved.
- Context vanishes when initialization halts mid-conversation.
Validating file paths and dependency locks is necessary before deploying to platforms like Vercel or Railway.
Implementing Conversation History Limits to Prevent Context Overflow
Unbounded message arrays trigger runtime failures once the accumulated token count exceeds the model's context window. Conversation history grows too large, forcing developers to implement a limit, such as slicing the array if length exceeds 20. This mechanical truncation prevents memory exhaustion but introduces a specific failure mode where the agent loses access to earlier tool definitions. The stateful nature of these agents means that discarding old context breaks the continuity required for complex, multi-step reasoning tasks. Operators face a tension between preserving long-term context and maintaining system stability within fixed resource limits. Blindly appending messages without a slicing strategy guarantees eventual crashes in long-running sessions. Aggressive truncation risks losing critical instructions or user preferences set at the start of a session.
- Silent loss of early context cues degrades performance.
- Hallucination rates increase due to missing constraints.
- The agent fails to recognize previously set tools.
- Extended dialogues exhibit unpredictable behavior.
- Re-processing large payloads drives higher latency.
Implementing circular buffers or vector storage is often suggested for production systems requiring deep history. The cost of this safeguard is the permanent loss of early conversation turns unless external logging captures them separately. Ignoring this constraint renders the agent unusable after extended interaction periods.
Production Gaps: Missing Budget Limits and Observability Features
One piece is missing outright for production readiness: budget limits for token usage. Operational blindness compounds this financial risk when basic implementations omit logging and error tracking.
Unbounded token consumption drains budgets during infinite tool loops. Missing summarization strategies cause context windows to overflow. Silent failures occur without structured error tracking systems. Frameworks like LangGraph offer checkpoints for stateful agents, yet basic TypeScript runners do not include these safeguards by default.
Teams must address the lack of built-in safeguards for token usage and context management. Studying Anthropic's tool use guide helps understand valid safety guardrails before deploying agents that interact with external systems. Without these layers, the agent remains a fragile experiment rather than a reliable service.
About
Sofia Berg serves as Research Editor at AI Agents News, where she specializes in translating complex multi-agent research into actionable insights for engineers. Her daily work involves rigorously evaluating agentic frameworks and dissecting benchmark results from arXiv papers, giving her a unique perspective on what actually works in production versus what remains theoretical. This specific expertise makes her uniquely qualified to author a guide on building AI agents, as she constantly assesses the gap between academic concepts and practical implementation. At AI Agents News, an independent hub dedicated to autonomous systems and coding agents, Sofia focuses on stripping away hype to reveal core mechanics. By using her deep familiarity with tool use, planning algorithms, and evaluation metrics, she crafts this tutorial to demonstrate that constructing a functional agent requires understanding fundamental orchestration rather than relying on opaque, heavy frameworks.
Conclusion
A working research assistant fits in 150 lines of TypeScript: a Message interface that holds the history, a Tool interface whose execute returns a Promise, a loop that reacts to the tool_use stop reason, and an array that carries the conversation forward. The tools catalogued in awesome-ai-agents-2026 add convenience on top of that skeleton, not mechanics, and the compiler catches the interface mismatches that untyped implementations only meet at runtime.
What the script leaves out is production hardening. The calculator path still runs on eval, the search tool still returns mock data, token usage carries no budget limit, and the history array vanishes when the Node.js process restarts. Swap the mock execute for SerpAPI, Brave Search, or Tavily, put mathjs where eval sits, and pick a truncation policy before the context window picks one for you. Read the code first, then add each further layer only when you can name the failure it prevents.
Frequently Asked Questions
You need Node.js 18, basic TypeScript knowledge, and an Anthropic API key. These prerequisites allow you to run the 150 lines of code without needing complex frameworks or prior AI experience.
Strong typing flags interface mismatches during compilation rather than execution. This immediate feedback stops malformed arguments from reaching the LLM API, preventing unpredictable crashes common in untyped JavaScript implementations.
The agent loses all conversation history because state is managed locally in memory. Unlike distributed systems, this lightweight approach does not persist data to external databases when the process stops.
Yes, you can integrate real providers like SerpAPI or Brave Search while keeping the same structure. The current mock data simply returns a static array of result lines for demonstration purposes.
No, this script is designed for learning core mechanics rather than production deployment. It demonstrates functional research capabilities but lacks the reliable error handling required for critical enterprise workflows.