- Docs
- What To Build
- AI Astrology Chatbot
Build an AI astrology chatbot
Ship a chatbot that answers "what is my Life Path", "draw me a tarot card", "what is my Human Design type", "what is my kundli" using real Roxy data. Time to ship: 30 to 45 minutes.
The flagship Roxy build. A user types a natural-language question. An LLM picks the right Roxy tool, calls it, and replies in plain English. The LLM does endpoint selection, field extraction, and interpretation synthesis. You write no question-to-endpoint mapping.
MCP (Model Context Protocol) is the fast path. Register Roxy once, every Roxy endpoint becomes a tool the model can auto-discover. The slower path is raw function calling, where you hand-define each tool. Both are covered below.
What you can build
- Daily horoscope bot ("what is the Leo horoscope today")
- Natal chart bot ("I was born July 15 1990 at 2:30 PM in New York, what is my chart")
- Human Design coach ("what is my type, strategy, authority")
- Daily forecast tracker ("what transits do I have this week")
- Tarot reader ("pull me a card", "do a three-card spread")
- Numerology assistant ("what is my Life Path for July 15 1990")
- Vedic kundli bot ("generate the kundli for someone born July 15 1990 in Mumbai")
- KP horary oracle ("when will my house sell, ruling planets now")
- Multi-domain wellness companion covering all twelve Roxy domains
Prerequisites
- A Roxy API key from /account.
- An LLM key from Anthropic, OpenAI, or Google AI Studio.
- Node.js 18+ or Bun (for the from-scratch path) or any MCP-compatible client (for the MCP path).
Option A: clone the flagship starter
The fastest path. The MIT-licensed reference integration the Roxy team maintains, browse it at /starters/astrology-ai-chatbot.
git clone https://github.com/RoxyAPI/astrology-ai-chatbot.git
cd astrology-ai-chatbot
npm install
Create .env.local:
ROXY_API_KEY=your_roxy_key
ANTHROPIC_API_KEY=your_claude_key
# or OPENAI_API_KEY / GOOGLE_GENERATIVE_AI_API_KEY
Run:
npm run dev
Open http://localhost:3000. The starter ships with a web UI, conversation history, multi-provider LLM support, and auto-discovery of every Roxy MCP server (astrology, vedic-astrology, numerology, tarot, biorhythm, iching, crystals, dreams, angel-numbers, human-design, forecast, location). You do not define tools. The Vercel AI SDK picks them up from the remote MCP manifest.
Option B: build it from scratch with function calling
If you want to see the machinery, here is a single-file chatbot that hand-defines four tools and hand-calls Roxy via the SDK.
Setup:
mkdir astro-chat && cd astro-chat
npm init -y
npm install @anthropic-ai/sdk @roxyapi/sdk
Create chat.ts:
import Anthropic from '@anthropic-ai/sdk';
import { createRoxy } from '@roxyapi/sdk';
import { createInterface } from 'node:readline';
const client = new Anthropic();
const roxy = createRoxy(process.env.ROXY_API_KEY!);
const tools = [
{
name: 'get_daily_horoscope',
description: 'Daily horoscope for a zodiac sign. Returns overview, love, career, health, finance.',
input_schema: {
type: 'object',
properties: { sign: { type: 'string', description: 'Lowercase sign, aries through pisces' } },
required: ['sign'],
},
},
{
name: 'generate_natal_chart',
description: 'Western natal chart from birth data. Returns planets with interpretation, houses, aspects.',
input_schema: {
type: 'object',
properties: {
date: { type: 'string', description: 'YYYY-MM-DD' },
time: { type: 'string', description: 'HH:MM:SS' },
latitude: { type: 'number' },
longitude: { type: 'number' },
timezone: { type: 'string', description: 'IANA id, e.g. America/New_York' },
},
required: ['date', 'time', 'latitude', 'longitude', 'timezone'],
},
},
{
name: 'generate_bodygraph',
description: 'Human Design bodygraph. Returns type (Manifestor, Generator, Manifesting Generator, Projector, Reflector), strategy, authority, profile, defined centers, channels, gates, incarnation cross.',
input_schema: {
type: 'object',
properties: {
date: { type: 'string' },
time: { type: 'string' },
timezone: { type: 'string' },
latitude: { type: 'number' },
longitude: { type: 'number' },
},
required: ['date', 'time', 'timezone'],
},
},
{
name: 'generate_forecast_timeline',
description: 'Forecast timeline of significant astrological events (transit aspects, sign ingresses, retrograde stations, dasha changes, biorhythm critical days) between two dates for one user.',
input_schema: {
type: 'object',
properties: {
birthData: {
type: 'object',
properties: {
date: { type: 'string' },
time: { type: 'string' },
timezone: { type: 'string' },
},
required: ['date', 'time', 'timezone'],
},
startDate: { type: 'string', description: 'YYYY-MM-DD' },
endDate: { type: 'string', description: 'YYYY-MM-DD' },
},
required: ['birthData'],
},
},
{
name: 'calculate_life_path',
description: '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'],
},
},
{
name: 'get_daily_tarot_card',
description: 'Draw a single tarot card with interpretation.',
input_schema: { type: 'object', properties: { seed: { type: 'string' } } },
},
];
async function callRoxy(name: string, input: any) {
if (name === 'get_daily_horoscope') {
const { data } = await roxy.astrology.getDailyHoroscope({ path: { sign: input.sign } });
return data;
}
if (name === 'generate_natal_chart') {
const { data } = await roxy.astrology.generateNatalChart({ body: input });
return data;
}
if (name === 'generate_bodygraph') {
const { data } = await roxy.humanDesign.generateBodygraph({ body: input });
return data;
}
if (name === 'generate_forecast_timeline') {
const { data } = await roxy.forecast.generateTimeline({ body: input });
return data;
}
if (name === 'calculate_life_path') {
const { data } = await roxy.numerology.calculateLifePath({ body: input });
return data;
}
if (name === 'get_daily_tarot_card') {
const { data } = await roxy.tarot.getDailyCard({ body: input });
return data;
}
throw new Error('unknown tool');
}
async function chat(history: any[]) {
let response = await client.messages.create({
model: 'claude-sonnet-4-5-20250929',
max_tokens: 1024,
system: 'You are a friendly spiritual assistant. Always call a tool when the user asks for horoscope, birth chart, Human Design, forecast, tarot, or numerology data. Never guess. For any chart question, geocode the city first if you only have a name.',
tools,
messages: history,
});
while (response.stop_reason === 'tool_use') {
const tu = response.content.find((b: any) => b.type === 'tool_use') as any;
const result = await callRoxy(tu.name, tu.input);
history.push({ role: 'assistant', content: response.content });
history.push({
role: 'user',
content: [{ type: 'tool_result', tool_use_id: tu.id, content: JSON.stringify(result) }],
});
response = await client.messages.create({
model: 'claude-sonnet-4-5-20250929',
max_tokens: 1024,
tools,
messages: history,
});
}
const text = (response.content.find((b: any) => b.type === 'text') as any)?.text ?? '';
history.push({ role: 'assistant', content: response.content });
return text;
}
const rl = createInterface({ input: process.stdin, output: process.stdout });
const history: any[] = [];
console.log('Roxy chatbot. Type quit to exit.\n');
const loop = () => rl.question('You: ', async (line) => {
if (line.trim().toLowerCase() === 'quit') return rl.close();
history.push({ role: 'user', content: line });
const reply = await chat(history);
console.log('\nBot: ' + reply + '\n');
loop();
});
loop();
Run:
ROXY_API_KEY=your_roxy_key ANTHROPIC_API_KEY=your_claude_key bun chat.ts
Try:
- "What is the Leo horoscope today?"
- "I was born July 15 1990 at 2:30 PM in New York, what is my Human Design type?"
- "Show me my forecast for the next 30 days."
- "Draw me a tarot card."
- "What is my Life Path? Birthday July 15 1990."
# chat.py, same idea with the Python SDK
import os
import anthropic
from roxy_sdk import create_roxy
client = anthropic.Anthropic()
roxy = create_roxy(os.environ['ROXY_API_KEY'])
def call_roxy(name, args):
if name == 'get_daily_horoscope':
return roxy.astrology.get_daily_horoscope(sign=args['sign'])
if name == 'generate_natal_chart':
return roxy.astrology.generate_natal_chart(**args)
if name == 'generate_bodygraph':
return roxy.human_design.generate_bodygraph(**args)
if name == 'generate_timeline':
return roxy.forecast.generate_timeline(**args)
if name == 'calculate_life_path':
return roxy.numerology.calculate_life_path(**args)
if name == 'get_daily_tarot_card':
return roxy.tarot.get_daily_card(**args)
raise ValueError('unknown tool')
<?php
use function RoxyAPI\Sdk\createRoxy;
$roxy = createRoxy(getenv('ROXY_API_KEY'));
function callRoxy($roxy, $name, $args) {
if ($name === 'get_daily_horoscope') {
return $roxy->astrology->getDailyHoroscope(sign: $args['sign']);
}
if ($name === 'generate_natal_chart') {
return $roxy->astrology->generateNatalChart(...$args);
}
if ($name === 'generate_bodygraph') {
return $roxy->humanDesign->generateBodygraph(...$args);
}
if ($name === 'generate_timeline') {
return $roxy->forecast->generateTimeline(...$args);
}
throw new Exception('unknown tool');
}
Option C: skip tool definitions with MCP
Neither of the above scales. The moment you want "all twelve domains, every endpoint" you stop hand-writing tool schemas and point the LLM at MCP.
Register all Roxy MCP servers with Claude Code in one go:
claude mcp add-json --scope user roxy-astrology '{"type":"http","url":"https://roxyapi.com/mcp/astrology","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-vedic '{"type":"http","url":"https://roxyapi.com/mcp/vedic-astrology","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-numerology '{"type":"http","url":"https://roxyapi.com/mcp/numerology","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-tarot '{"type":"http","url":"https://roxyapi.com/mcp/tarot","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-iching '{"type":"http","url":"https://roxyapi.com/mcp/iching","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-biorhythm '{"type":"http","url":"https://roxyapi.com/mcp/biorhythm","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-dreams '{"type":"http","url":"https://roxyapi.com/mcp/dreams","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-crystals '{"type":"http","url":"https://roxyapi.com/mcp/crystals","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-angel '{"type":"http","url":"https://roxyapi.com/mcp/angel-numbers","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-human-design '{"type":"http","url":"https://roxyapi.com/mcp/human-design","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-forecast '{"type":"http","url":"https://roxyapi.com/mcp/forecast","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-location '{"type":"http","url":"https://roxyapi.com/mcp/location","headers":{"X-API-Key":"YOUR_KEY"}}'
Same pattern for Claude Desktop, Cursor, Antigravity, and Vercel AI SDK. See the MCP setup docs for full client instructions.
Render the result (optional)
Combine the chatbot with <roxy-*> web components for a hybrid chat + visual UI. After the LLM replies with a natal-chart tool result, render the wheel:
<script src="https://cdn.jsdelivr.net/npm/@roxyapi/ui@0/dist/cdn/roxy-ui.js" defer></script>
<roxy-natal-chart id="chart"></roxy-natal-chart>
<roxy-bodygraph id="bg"></roxy-bodygraph>
<script type="module">
document.getElementById('chart').data = lastNatalChartToolResult;
document.getElementById('bg').data = lastBodygraphToolResult;
</script>
See the UI components page for the full catalog.
Ready-made starter
Browse /starters/astrology-ai-chatbot for the deploy-ready Next.js build. Same idea as Option A above with the full UI, conversation persistence, and multi-MCP discovery.
Gotchas
- Do not embed the Roxy API key in the browser. The chatbot backend holds the secret key and proxies calls. Anyone can view page source.
- Timezone accepts IANA strings. The LLM naturally fills
"America/New_York"from "New York"; let it. The server resolves DST for the birth date automatically. Decimal offsets like-5also work but do not DST-correct. - Tell the model to geocode for chart calls. Add a line to the system prompt: "For any birth chart or Human Design question, call location search first to get latitude, longitude, and timezone." Without that, the LLM sometimes invents coordinates.
- Seeded endpoints are deterministic. Pass
seed: userIdto daily tarot and the same user gets the same card all day. That is a feature, not a cache staleness bug. - Forecast endpoints accept
birthDataas a nested object. Not flat. Easy to flatten by mistake. - Gemini tool schemas use Type enums. See the function-calling guide for the Gemini variant.
What to build next
- The MCP setup docs cover Claude Desktop, Cursor, Antigravity, and custom Streamable HTTP clients.
- The function-calling guide shows the raw tool-calling pattern for OpenAI, Anthropic, and Gemini.
- The Human Design guide and forecast guide cover the new May 2026 domains your bot can answer for.
- The personalized tracker tutorial shows the full server-side build for HD + forecast + numerology in a single dashboard.
- The starters page lists the full catalog of clone-and-ship apps.