Skip to content

First Agent Tutorial

Alessio Rocchi edited this page Jan 27, 2026 · 1 revision

First Agent Tutorial

Interactive tutorial to create and interact with your first aistack agent.


What You'll Learn

  • How to spawn an agent using MCP tools
  • How to execute tasks with an agent
  • How to use memory to share context
  • How to coordinate multiple agents
  • Best practices for agent interaction

Prerequisites

  • aistack installed and configured
  • Claude Code with MCP integration enabled
  • API key configured (Anthropic, OpenAI, or Ollama)

See Installation and Configuration Guide if you haven't set these up.


Step 1: Spawn Your First Agent

Using MCP in Claude Code

In Claude Code, use the agent_spawn MCP tool:

Please spawn a coder agent named "my-first-coder"

Claude Code will call:

{
  "tool": "agent_spawn",
  "arguments": {
    "type": "coder",
    "name": "my-first-coder"
  }
}

Expected Response:

{
  "success": true,
  "agent": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "coder",
    "name": "my-first-coder",
    "status": "idle",
    "createdAt": "2026-01-27T10:30:00.000Z"
  },
  "prompt": "You are an expert software developer..."
}

Using CLI

npx @blackms/aistack agent spawn -t coder -n my-first-coder

Using TypeScript API

import { spawnAgent } from '@blackms/aistack';

const agent = spawnAgent('coder', {
  name: 'my-first-coder'
});

console.log('Agent spawned:', agent.id);

Verify Agent is Active

List all active agents

Response will include your agent:

{
  "agents": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "type": "coder",
      "name": "my-first-coder",
      "status": "idle"
    }
  ]
}

Step 2: Give the Agent a Simple Task

Task: Write a Hello World Function

Using agent "my-first-coder", write a TypeScript function that prints "Hello, World!"

The agent will:

  1. Understand the requirement
  2. Write clean TypeScript code
  3. Return the implementation

Expected Output:

/**
 * Prints "Hello, World!" to the console
 */
export function helloWorld(): void {
  console.log("Hello, World!");
}

// Usage example
helloWorld();

How It Works

  1. Claude Code receives your request
  2. MCP Server routes to the coder agent
  3. Coder Agent generates code using configured LLM
  4. Response is returned through MCP to Claude Code

Step 3: Store Knowledge in Memory

Store a Coding Pattern

Store in memory with key "typescript:hello-world" the pattern we just created

This calls memory_store:

{
  "tool": "memory_store",
  "arguments": {
    "key": "typescript:hello-world",
    "content": "export function helloWorld(): void { console.log('Hello, World!'); }",
    "namespace": "patterns",
    "metadata": {
      "language": "typescript",
      "category": "basics"
    }
  }
}

Search Memory

Search memory for "hello world" examples

This calls memory_search:

{
  "tool": "memory_search",
  "arguments": {
    "query": "hello world",
    "namespace": "patterns",
    "limit": 5
  }
}

Response:

{
  "count": 1,
  "results": [
    {
      "entry": {
        "key": "typescript:hello-world",
        "content": "export function helloWorld()...",
        "namespace": "patterns"
      },
      "score": 0.95,
      "matchType": "fts"
    }
  ]
}

Step 4: Coordinate Multiple Agents

Spawn a Tester Agent

Spawn a tester agent named "my-tester"

Create Tests with the Tester

Using agent "my-tester", create tests for the hello world function stored in memory

The tester agent will:

  1. Retrieve the code from memory
  2. Understand the functionality
  3. Write comprehensive tests

Expected Output:

import { describe, it, expect, vi } from 'vitest';
import { helloWorld } from './hello-world';

describe('helloWorld', () => {
  it('should print "Hello, World!" to console', () => {
    const consoleSpy = vi.spyOn(console, 'log');

    helloWorld();

    expect(consoleSpy).toHaveBeenCalledWith('Hello, World!');
    expect(consoleSpy).toHaveBeenCalledTimes(1);

    consoleSpy.mockRestore();
  });
});

Review with Reviewer Agent

Spawn a reviewer agent and have it review both the code and tests

The reviewer agent will provide feedback on:

  • Code quality
  • Test coverage
  • Best practices
  • Potential improvements

Step 5: Use Sessions for Context

Start a Session

Start a new session for my Hello World project

This calls session_start:

{
  "tool": "session_start",
  "arguments": {
    "metadata": {
      "project": "hello-world-tutorial",
      "goal": "Learn agent basics"
    }
  }
}

Associate Agents with Session

Spawn a coder agent in session {session-id}

All agents in the same session share context and can access the same memory namespace.

End Session

End session {session-id}

This marks the session as complete and persists all data.


Step 6: Real-World Example

Let's build something more practical: a simple utility function with tests.

1. Spawn Agents

Spawn a coder agent named "util-coder"
Spawn a tester agent named "util-tester"
Spawn a reviewer agent named "util-reviewer"

2. Define Requirements

Using "util-coder", create a TypeScript function that:
- Takes an array of numbers
- Returns the sum of all even numbers
- Handles empty arrays
- Has proper TypeScript types

Generated Code:

/**
 * Calculates the sum of all even numbers in an array
 * @param numbers - Array of numbers to process
 * @returns Sum of even numbers, or 0 if array is empty
 */
export function sumEvenNumbers(numbers: number[]): number {
  if (numbers.length === 0) {
    return 0;
  }

  return numbers
    .filter(num => num % 2 === 0)
    .reduce((sum, num) => sum + num, 0);
}

3. Create Tests

Using "util-tester", create comprehensive tests for the sumEvenNumbers function

Generated Tests:

import { describe, it, expect } from 'vitest';
import { sumEvenNumbers } from './sum-even-numbers';

describe('sumEvenNumbers', () => {
  it('should return 0 for empty array', () => {
    expect(sumEvenNumbers([])).toBe(0);
  });

  it('should sum even numbers correctly', () => {
    expect(sumEvenNumbers([1, 2, 3, 4, 5, 6])).toBe(12); // 2 + 4 + 6
  });

  it('should return 0 when no even numbers present', () => {
    expect(sumEvenNumbers([1, 3, 5, 7])).toBe(0);
  });

  it('should handle negative even numbers', () => {
    expect(sumEvenNumbers([-2, -1, 0, 1, 2])).toBe(0); // -2 + 0 + 2
  });

  it('should handle large arrays', () => {
    const largeArray = Array.from({ length: 1000 }, (_, i) => i);
    const expected = Array.from({ length: 500 }, (_, i) => i * 2).reduce((a, b) => a + b, 0);
    expect(sumEvenNumbers(largeArray)).toBe(expected);
  });
});

4. Code Review

Using "util-reviewer", review the implementation and tests

Reviewer Feedback:

Code Review Summary:

Correctness: ✓ PASS
- Logic correctly filters and sums even numbers
- Edge cases handled properly

Performance: ✓ GOOD
- Efficient O(n) algorithm
- Appropriate use of filter/reduce

Maintainability: ✓ EXCELLENT
- Clear function name and documentation
- Simple, readable implementation

Testing: ✓ COMPREHENSIVE
- All edge cases covered
- Good test descriptions
- Performance test included

Suggestions:
- Consider adding JSDoc examples
- Could extract even number check to a helper function for reusability

Overall: APPROVED - High quality implementation

5. Store in Memory

Store this pattern in memory with key "patterns:sum-even-numbers"

Step 7: Adversarial Testing

For critical code, use the adversarial review loop:

Start an adversarial review loop for the sumEvenNumbers function

This calls review_loop_start:

{
  "tool": "review_loop_start",
  "arguments": {
    "code": "export function sumEvenNumbers(numbers: number[]): number { ... }",
    "maxIterations": 3
  }
}

The adversarial agent will:

  1. Try to break the code
  2. Find edge cases
  3. Identify security issues
  4. Demand fixes

If issues are found:

  1. Coder agent fixes the code
  2. Adversarial agent reviews again
  3. Loop continues until approved or max iterations

Common Patterns

Pattern 1: Research → Code → Test

1. Spawn researcher agent
2. Research best practices for the task
3. Spawn coder agent
4. Implement based on research
5. Spawn tester agent
6. Create tests

Pattern 2: Architect → Code → Review

1. Spawn architect agent
2. Design the solution
3. Spawn coder agent
4. Implement the design
5. Spawn reviewer agent
6. Review implementation

Pattern 3: Code → Adversarial → Fix

1. Spawn coder agent
2. Implement feature
3. Start adversarial review loop
4. Fix issues until approved

Best Practices

Agent Naming

✓ GOOD: "user-auth-coder", "api-tester", "security-reviewer"
✗ BAD: "agent1", "test", "temp"

Use descriptive names that indicate:

  • Purpose (coder, tester, reviewer)
  • Context (user-auth, api, security)

Memory Organization

✓ GOOD:
  namespace: "user-auth"
  key: "pattern:jwt-validation"

✗ BAD:
  namespace: "default"
  key: "stuff"

Use structured namespaces and keys:

  • Namespace: Feature or module
  • Key: Pattern or concept

Session Management

✓ GOOD: Start session for each feature/task
✗ BAD: One session for everything

Sessions provide:

  • Context isolation
  • Resource cleanup
  • Progress tracking

Task Decomposition

✓ GOOD: Break into specific, focused tasks
✗ BAD: One agent does everything

Example:

Task: Build authentication system

Decomposition:
1. Architect: Design auth flow
2. Coder: Implement JWT generation
3. Tester: Test JWT validation
4. Security Auditor: Security review
5. Documentation: API docs

Troubleshooting

Agent Not Responding

Problem: Agent spawned but doesn't execute tasks

Solution:

  1. Check agent status: agent_status
  2. Verify provider configuration
  3. Check API key validity
  4. Review logs for errors

Memory Not Found

Problem: Stored data not retrievable

Solution:

  1. Verify namespace matches
  2. Check key spelling
  3. Use memory_list to see all entries
  4. Try FTS search instead of exact key

Session Errors

Problem: Agents can't access session data

Solution:

  1. Verify session is active: session_status
  2. Ensure agents spawned with correct sessionId
  3. Check session hasn't ended

Next Steps

Learn More

Try These Recipes

Explore Tools


Related:

Clone this wiki locally