Menu

  1. Docs
  2. What To Build
  3. AI Astrology Chatbot

Build an AI Astrology Chatbot

In this tutorial you will build a chatbot that answers questions about birth charts, tarot, numerology, and more. Users type natural-language questions and the AI calls the right Roxy endpoints automatically using MCP (Model Context Protocol).

This is the most advanced tutorial. You should be comfortable running commands in a terminal and have a basic understanding of JavaScript before starting. If you are new to APIs, start with the Horoscope Widget first.

What you will build

  • A chat interface where users ask questions like "What is my zodiac compatibility with someone born March 22, 1992?"
  • MCP integration that auto-discovers all 110+ Roxy endpoints as tools
  • The AI picks the right endpoint, calls it, and explains the results conversationally

How MCP works

Without MCP, you would hardcode which endpoint to call for each question. With MCP, the AI does that automatically:

  1. Your chatbot connects to the Roxy MCP server
  2. The server tells the AI about all available tools (endpoints) and what they do
  3. When a user asks a question, the AI decides which tool to call, formats the request, calls it, and explains the response

You do not need to map questions to endpoints yourself. The AI handles it.

Prerequisites

Option A: Clone the starter (fastest)

The quickest path is to clone the ready-made chatbot starter:

git clone https://github.com/RoxyAPI/astrology-ai-chatbot.git
cd astrology-ai-chatbot
npm install

Create a .env file:

ROXY_API_KEY=your_roxy_api_key
OPENAI_API_KEY=your_openai_key

Run it:

npm run dev

Open http://localhost:3000 and start chatting. The starter includes a full web UI, conversation history, and multi-provider support (OpenAI, Claude, Gemini).

Option B: Build from scratch

If you want to understand how it works, here is a minimal chatbot in ~50 lines. Create a new directory and set it up:

mkdir astro-chat && cd astro-chat
npm init -y
npm install @anthropic-ai/sdk

Create chat.js:

import Anthropic from '@anthropic-ai/sdk';
import { readFileSync } from 'fs';
import { createInterface } from 'readline';

const client = new Anthropic();
const ROXY_KEY = process.env.ROXY_API_KEY;

// Define Roxy tools the AI can call
const tools = [
  {
    name: 'daily_horoscope',
    description: 'Get today\'s horoscope for a zodiac sign',
    input_schema: {
      type: 'object',
      properties: { sign: { type: 'string', description: 'Zodiac sign (e.g. aries, taurus)' } },
      required: ['sign']
    }
  },
  {
    name: 'natal_chart',
    description: 'Generate a birth chart from birth date, time, and location',
    input_schema: {
      type: 'object',
      properties: {
        date: { type: 'string', description: 'Birth date YYYY-MM-DD' },
        time: { type: 'string', description: 'Birth time HH:MM:SS' },
        latitude: { type: 'number' },
        longitude: { type: 'number' },
        timezone: { type: 'number' }
      },
      required: ['date', 'time', 'latitude', 'longitude', 'timezone']
    }
  },
  {
    name: 'tarot_daily',
    description: 'Draw a daily tarot card with interpretation',
    input_schema: { type: 'object', properties: {} }
  },
  {
    name: 'life_path',
    description: 'Calculate numerology Life Path number from birth date',
    input_schema: {
      type: 'object',
      properties: {
        year: { type: 'number' }, month: { type: 'number' }, day: { type: 'number' }
      },
      required: ['year', 'month', 'day']
    }
  }
];

// Map tool names to API endpoints
const endpoints = {
  daily_horoscope: (input) => ({
    url: `https://roxyapi.com/api/v2/astrology/horoscope/${input.sign}/daily`,
    method: 'GET'
  }),
  natal_chart: (input) => ({
    url: 'https://roxyapi.com/api/v2/astrology/natal-chart',
    method: 'POST',
    body: input
  }),
  tarot_daily: () => ({
    url: 'https://roxyapi.com/api/v2/tarot/daily',
    method: 'POST',
    body: {}
  }),
  life_path: (input) => ({
    url: 'https://roxyapi.com/api/v2/numerology/life-path',
    method: 'POST',
    body: input
  })
};

async function callTool(name, input) {
  const ep = endpoints[name](input);
  const options = {
    method: ep.method,
    headers: { 'X-API-Key': ROXY_KEY, 'Content-Type': 'application/json' }
  };
  if (ep.body) options.body = JSON.stringify(ep.body);
  const res = await fetch(ep.url, options);
  return await res.json();
}

async function chat(userMessage, history) {
  history.push({ role: 'user', content: userMessage });

  // Ask Claude, providing the available tools
  let response = await client.messages.create({
    model: 'claude-sonnet-4-20250514',
    max_tokens: 1024,
    system: 'You are a friendly astrology assistant. Use the available tools to answer questions about horoscopes, birth charts, tarot, and numerology. Always call a tool when relevant rather than guessing.',
    tools: tools,
    messages: history
  });

  // If Claude wants to call a tool, execute it and send the result back
  while (response.stop_reason === 'tool_use') {
    const toolBlock = response.content.find(b => b.type === 'tool_use');
    const result = await callTool(toolBlock.name, toolBlock.input);

    history.push({ role: 'assistant', content: response.content });
    history.push({
      role: 'user',
      content: [{ type: 'tool_result', tool_use_id: toolBlock.id, content: JSON.stringify(result) }]
    });

    response = await client.messages.create({
      model: 'claude-sonnet-4-20250514',
      max_tokens: 1024,
      system: 'You are a friendly astrology assistant. Use the available tools to answer questions about horoscopes, birth charts, tarot, and numerology. Always call a tool when relevant rather than guessing.',
      tools: tools,
      messages: history
    });
  }

  const text = response.content.find(b => b.type === 'text')?.text || '';
  history.push({ role: 'assistant', content: response.content });
  return text;
}

// Simple terminal chat loop
const rl = createInterface({ input: process.stdin, output: process.stdout });
const history = [];

console.log('Astrology Chatbot (type "quit" to exit)\n');
function ask() {
  rl.question('You: ', async (input) => {
    if (input.toLowerCase() === 'quit') { rl.close(); return; }
    const reply = await chat(input, history);
    console.log('\nBot: ' + reply + '\n');
    ask();
  });
}
ask();

Run it:

ROXY_API_KEY=your_key ANTHROPIC_API_KEY=your_key node chat.js

Try asking:

  • "What is the horoscope for Leo today?"
  • "I was born July 15, 1990 at 2:30 PM in New York. What is my birth chart?"
  • "Draw me a tarot card"
  • "What is my Life Path number? Birthday is July 15, 1990"

How it works

  1. Define tools — Each tool maps to a Roxy endpoint. The description tells the AI when to use it. The input_schema tells the AI what parameters to extract from the user's question.

  2. Send the message to Claude — Claude sees the user's question plus all available tools. If the question needs an API call, Claude responds with a tool_use block instead of text.

  3. Execute the tool call — The callTool() function maps the tool name to an endpoint URL, calls Roxy, and returns the JSON result.

  4. Send the result back — The API result goes back to Claude as a tool_result. Claude reads the data and writes a conversational response explaining what it means.

  5. Loop — If Claude needs to call multiple tools (e.g., two birth charts for compatibility), the while loop handles consecutive tool calls.

This pattern works with any LLM that supports tool calling (OpenAI, Gemini, Claude). The tools array format differs slightly between providers but the concept is identical.

Scale it up with MCP

The example above manually defines 4 tools. The Roxy MCP server auto-discovers all 110+ endpoints as tools, so the AI can answer questions across all 8 domains without you writing tool definitions.

To use MCP instead of manual tools, see the MCP Setup guide. It shows how to register the Roxy MCP server with Claude Desktop, VS Code, and custom applications.

Next steps

  • MCP Setup — connect the full Roxy MCP server to your AI agent
  • AI Prompts — copy-paste prompts for Cursor, Claude, and Copilot
  • Starter Apps — production-ready chatbot starter with web UI