DEV Community

Ursula Okafo
Ursula Okafo

Posted on

Building an AI News Digest Agent with Mastra and Telex.im

How I built a real-time news aggregation agent, integrated it with Telex.im, and learned about AI agent architecture along the way.

Introduction

For the HNG Stage 3 backend challenge, I set out to build something practical: an AI agent that delivers fresh news headlines on demand. The result? Daily Headline Digest Agent β€” an intelligent system that fetches trending news, summarizes it with AI, and integrates seamlessly with Telex.im.

This blog post walks through my journey: why I chose Mastra, how I built the agent, the integration challenges I faced, and what I learned about building AI systems at scale.

What I Built

Daily Headline Digest Agent is an AI-powered news assistant that:

  • Fetches real-time news from the GNews API across multiple categories (tech, business, sports, health, entertainment, science)
  • Supports country-specific news (Nigeria, USA, UK, Canada, Australia, India, and more)
  • Uses Google's Gemini 2.0 Flash model for intelligent summarization
  • Integrates with Telex.im for seamless workplace collaboration
  • Provides formatted, emoji-enhanced news digests with source attribution

Example interaction:

User: "Give me today's tech news"
Agent: "πŸ—žοΈ Tech Headlines - Friday, November 7, 2025
1️⃣ OnePlus 15 launching in India on November 13...
2️⃣ iOS 26 Marks a New Divide in the iPhone Lineup..."
Enter fullscreen mode Exit fullscreen mode

Why Mastra?

When I started this project, I had three options:

  1. Build from scratch with raw APIs
  2. Use LangChain/LlamaIndex
  3. Use Mastra

I chose Mastra for several reasons:

1. Built for Telex Integration
Mastra has first-class support for Telex.im through the A2A protocol. This wasn't a hack β€” it was designed for this use case.

2. Simpler Agent Definition
With Mastra, defining an agent is straightforward:

export const newsDigestAgent = new Agent({
  name: 'newsDigestAgent',
  instructions: '...',
  model: google('gemini-2.0-flash'),
  tools: { fetchNews },
});
Enter fullscreen mode Exit fullscreen mode

No complex prompt engineering needed. Clear, declarative syntax.

3. Type Safety
Being a TypeScript-first framework, Mastra gave me confidence with full type checking. My tools had validated schemas using Zod:

const newsToolSchema = z.object({
  topic: z.string().default('world'),
  maxArticles: z.number().min(3).max(10).default(5),
});
Enter fullscreen mode Exit fullscreen mode

4. Built-in Server/Routing
Mastra handles HTTP routing, JSON-RPC validation, and API structure automatically. I didn't have to write boilerplate.

Architecture

Here's how the system is structured:

src/
β”œβ”€β”€ mastra/
β”‚   β”œβ”€β”€ agents/
β”‚   β”‚   └── news-agent.ts          # Main AI agent
β”‚   β”œβ”€β”€ tools/
β”‚   β”‚   └── news-tools.ts          # GNews API integration
β”‚   └── index.ts                   # Mastra instance
β”œβ”€β”€ routes/
β”‚   └── a2a-agent-route.ts         # Telex A2A protocol handler
β”œβ”€β”€ index.ts                       # Express server
└── types/
    └── a2a.types.ts              # Protocol types
Enter fullscreen mode Exit fullscreen mode

Key Components:

  1. newsDigestAgent - The AI agent that understands natural language and decides when to use tools
  2. fetchNews - A tool that fetches news from GNews API with smart topic mapping
  3. a2aAgentRoute - Handles JSON-RPC 2.0 requests from Telex.im
  4. Express Server - Hosts everything on Railway

The Integration Journey

Phase 1: Local Development

I started by building the agent locally:

npm install @mastra/core @ai-sdk/google dotenv
npm run dev
Enter fullscreen mode Exit fullscreen mode

The Mastra dev server gave me a Studio interface to test the agent instantly. This was powerful β€” I could iterate on instructions and see results in real-time.

Phase 2: Deployment Challenge

This is where I hit my first major hurdle: Node.js version compatibility.

My dependencies required Node 20+, but Railway was deploying Node 18. The error was cryptic at first:

ERR_MODULE_NOT_FOUND: Cannot find module '/app/dist/mastra/agents/news-agent'
Enter fullscreen mode Exit fullscreen mode

Solution: Create a .node-version file to specify Node.js 20:

echo "20" > .node-version
Enter fullscreen mode Exit fullscreen mode

Lesson: Always verify your runtime environment. A simple version mismatch can cause confusing module resolution errors with ES modules.

Phase 3: ES Module Path Issues

Another challenge: ES modules require explicit file extensions in imports.

Before (broken):

import { newsDigestAgent } from './agents/news-agent';
Enter fullscreen mode Exit fullscreen mode

After (working):

import { newsDigestAgent } from './agents/news-agent.js';
Enter fullscreen mode Exit fullscreen mode

This is a Node.js ES module requirement, not specific to Mastra, but it's easy to miss.

Phase 4: Environment Variables

My biggest debugging headache was environment variables not being passed to the deployed container.

Initially, I set them via Railway CLI:

railway variables --set GOOGLE_GENERATIVE_AI_API_KEY=...
Enter fullscreen mode Exit fullscreen mode

But they didn't persist properly. The fix was to redeploy and verify they appeared in the logs.

Lesson: Always check railway logs after setting environment variables.

Phase 5: A2A Protocol Implementation

The real integration work came with implementing the A2A (Agent-to-Agent) protocol for Telex.

Telex expects JSON-RPC 2.0 format:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "message/send",
  "params": {
    "message": {
      "parts": [{"text": "your message"}]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

I built a dedicated route handler:

export const a2aAgentRoute = registerApiRoute('/a2a/agent/:agentId', {
  method: 'POST',
  handler: async (c) => {
    // Validate JSON-RPC 2.0
    // Extract message
    // Call agent
    // Return formatted response
  },
});
Enter fullscreen mode Exit fullscreen mode

Key challenge: Handling multiple message formats. Telex might send different structures depending on context. My solution:

let message = 'Hello';
if (params?.message?.parts?.[0]?.text) {
  message = params.message.parts[0].text;
} else if (typeof params?.message === 'string') {
  message = params.message;
} else if (params?.content) {
  message = params.content;
}
Enter fullscreen mode Exit fullscreen mode

What Worked Well

βœ… Mastra's Structure - The framework enforces good patterns without feeling restrictive

βœ… Tool System - Defining tools with Zod schemas is clean and type-safe

βœ… Model Flexibility - Easy to switch between Google, OpenAI, Anthropic models

βœ… Railway Deployment - Once environment issues were solved, deployments were smooth

βœ… GNews API - Free, reliable news data with good coverage across categories and countries

What Was Tricky

❌ Telex Integration Documentation - Limited docs on custom A2A implementations (Telex seems designed primarily for Mastra Cloud)

❌ Module Resolution - ES modules require explicit extensions and proper tsconfig settings

❌ Environment Variables - Railroad between CLI setup and deployment wasn't intuitive

❌ Debugging A2A Failures - Hard to know exactly what Telex was sending without better error messages

Lessons Learned

1. Start with Local Testing
Always validate your agent works locally before deploying. The Mastra Studio is invaluable.

2. Read the Logs
Railway logs showed exactly what was failing. "No service linked" β†’ "Missing environment variables" β†’ "Module not found". The logs told the story.

3. Type Safety Saves Time
TypeScript + Zod caught errors before runtime. Schema validation prevented bad data from reaching the API.

4. Keep the Agent Instructions Clear
I spent time crafting detailed agent instructions with examples. This actually improved the agent's behavior significantly. Good instructions > complex prompts.

5. Handle Multiple Input Formats
Real-world integrations are messy. Building flexibility into message parsing was essential.

The Code

You can find the complete implementation here:

What's Next

Possible improvements:

  • Add caching to reduce API calls
  • Implement sentiment analysis on headlines
  • Add multi-language support
  • Create a web UI for the agent
  • Build a scheduled digest for Slack integration

Final Thoughts

Building the Daily Headline Digest Agent taught me that modern AI agent frameworks like Mastra handle a lot of complexity beautifully. The hardest parts weren't about AI β€” they were about infrastructure, environment setup, and protocol integration.

If you're building AI agents, I'd recommend:

  • Use Mastra if you want structure and Telex integration
  • Invest time in good instructions β€” they matter more than you think
  • Test locally first β€” catch issues before deploying
  • Read the logs β€” they tell you everything
  • Build for flexibility β€” real integrations are messier than documentation suggests

The Daily Headline Digest Agent is live, integrated with Telex.im, and delivering real news to users. Not bad for a stage 3 backend challenge! πŸš€


Built with: TypeScript, Mastra, Google Gemini 2.0 Flash, GNews API, Express, Railway

Deployed: November 7, 2025

GitHub: https://github.com/Ursulaonyi/news-digest-agent

Top comments (0)