This document provides comprehensive guidance for Gemini and Antigravity to work on the Vica Discord bot project.
- Project Overview
- Architecture Overview
- Key Features
- MCP Integration
- Database Schema
- Memory System
- Configuration
- Code Patterns
- Technical Details
- Development Notes
- File Structure
Vica is a Discord bot developed in Node.js that combines AI chatbot functionality with a local question bank system.
- Language: Node.js (JavaScript)
- Platform: Discord.js library
- Deployment: Runs on Raspberry Pi 3b+ via systemd
- AI Integration: Uses OpenAI-compatible APIs (Requesty) for AI features
- MCP Support: Model Context Protocol for extending bot capabilities
- Primary Language: Portuguese (codebase and user-facing content)
Vica serves as an intelligent Discord assistant that can:
- Engage in AI-powered conversations with context awareness
- Manage user XP and ranking systems
- Store and retrieve long-term memories using semantic search
- Provide interactive configuration for server administrators
- Handle various administrative tasks through slash commands
| Component | File | Purpose |
|---|---|---|
| Database Interface | core/database.js |
SQLite database operations with prepared statements |
| OpenAI Interface | core/oai_interface.js |
AI API integration for chat and embeddings |
| MCP Client | core/mcp_client.js |
Model Context Protocol client for external tools |
| Tag Parser | core/tagParser.js |
Parses special tags in messages ([salvar_memoria], [meta], [imagem]) |
| Tool Loader | core/tool_loader.js |
Loads OpenAI function calling tools and MCP servers |
| Static Data | core/static_data.js |
Manages static configuration data |
| Transcriber | core/transcriber.js |
Handles audio/image transcription |
| Audit Cache | core/auditCache.js |
Caches audit logs for performance |
| Command | File | Purpose |
|---|---|---|
/canal_noticia |
commands/canal_noticia.js |
Configure news channel |
/chat_blacklist |
commands/chat_blacklist.js |
Manage chatbot blacklist |
/comment |
commands/comment.js |
Add comments to messages |
/deslurkar |
commands/deslurkar.js |
Remove user from blacklist |
/enex |
commands/enex.js |
Enable/disable features |
/gmemories |
commands/gmemories.js |
Manage guild memories |
/mmemories |
commands/mmemories.js |
Manage user memories |
/noticia |
commands/noticia.js |
Post news with OpenGraph metadata |
/perguntar |
commands/perguntar.js |
Ask questions to the AI |
/rank |
commands/rank.js |
View user rank/XP |
/rank_blacklist |
commands/rank_blacklist.js |
Manage XP blacklist |
/rank_reset |
commands/rank_reset.js |
Reset user XP |
/rank_rolexp |
commands/rank_rolexp.js |
Set role XP multipliers |
/rank_setxp |
commands/rank_setxp.js |
Set user XP manually |
/reaction_emoji |
commands/reaction_emoji.js |
Configure reaction emojis |
/reaction_thread |
commands/reaction_thread.js |
Configure thread reactions |
/system_channel |
commands/system_channel.js |
Configure system channel |
/trigger |
commands/trigger.js |
Set message triggers |
/up_role |
commands/up_role.js |
Configure role congratulations |
| Event | File | Purpose |
|---|---|---|
guildBanAdd |
events/guildBanAdd.js |
Handle user bans |
guildMemberAdd |
events/guildMemberAdd.js |
Welcome new members |
guildMemberRemove |
events/guildMemberRemove.js |
Handle member leaves |
guildMemberUpdate |
events/guildMemberUpdate.js |
Track role changes |
interactionCreate |
events/interactionCreate.js |
Handle slash commands |
messageCreate |
events/messageCreate.js |
Process messages for XP and chatbot |
messageReactionAdd |
events/messageReactionAdd.js |
Handle reaction-based features |
| Tool | File | Purpose |
|---|---|---|
calculate |
tools/calculate.js |
Perform mathematical calculations |
get_current_time |
tools/get_current_time.js |
Get current date/time |
| Server | Transport | Status | Tools |
|---|---|---|---|
| Memory | stdio | ✅ Working | 9 tools (knowledge graph operations) |
| Sequential Thinking | http | - | |
| Time | http | - |
| Helper | File | Purpose |
|---|---|---|
| Embedding Helper | helpers/embeddingHelper.js |
Semantic search using embeddings |
- Maintains conversation history (last 6 messages)
- Supports multimodal input (text and images)
- Uses OpenAI-compatible API (Requesty)
- Context-aware responses
- Rate limiting: 5 seconds per user
- Random XP gain per message (configurable min/max)
- Level-based progression
- Role-based XP multipliers
- Blacklist support for channels
- Manual XP adjustment commands
- Role congratulations on level up
- User-specific memories
- Guild-wide memories
- Semantic search using embeddings
- Tag-based memory creation:
[salvar_memoria],[meta],[imagem] - Cosine similarity for memory retrieval
- Dedicated slash commands for server settings
- Ephemeral responses for admin commands
- Server-specific settings
- Chatbot blacklist (channels where AI won't respond)
- XP blacklist (channels where XP is not awarded)
- User blacklist (users banned from chatbot)
- Image analysis via AI
- Audio transcription
- OpenGraph metadata extraction for news
- Post news with rich embeds
- OpenGraph metadata extraction
- Configurable news channel
- Reaction-based thread creation
- Automatic congratulations on role assignment
- Customizable messages per role
- Server-specific configuration
- Customizable welcome messages
- Customizable leave messages
- Configurable system channel
The Vica bot supports Model Context Protocol (MCP), a standardized protocol that enables AI assistants to communicate with external services and tools. MCP allows extending the bot's capabilities without modifying the core codebase.
The core/mcp_client.js module provides the interface for MCP server communication:
Key Functions:
startServer(config)- Initialize and start an MCP serverstopServer(server)- Gracefully stop a running MCP serverinitializeServer(server)- Perform MCP handshake and initializationlistTools(server)- Retrieve available tools from an MCP servercallTool(server, toolName, args)- Execute a tool on an MCP servergetServer(name)- Retrieve a running server instance by namegetAllServers()- Get all active MCP server instancesstopAllServers()- Stop all running MCP servers
MCP servers are configured in data/tools.json with the following structure:
stdio Transport (Local Servers):
{
"name": "memory",
"type": "mcp",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory@latest"],
"env": {
"MEMORY_FILE_PATH": "./data/memory.json"
}
}http Transport (Hosted Servers):
{
"name": "example_server",
"type": "mcp",
"transport": "http",
"url": "https://example.com/mcp-endpoint"
}MCP servers can receive environment variables through the env field. These are merged with the process environment:
"env": {
"MEMORY_FILE_PATH": "d:/Projetos/Discord/vica/data/memory.json",
"CUSTOM_VAR": "value"
}The Memory MCP server provides knowledge graph operations:
Available Tools:
create_entities- Create multiple entities in the knowledge graphcreate_relations- Create relationships between entitiesadd_observations- Add observations to existing entitiesdelete_entities- Remove entities and their relationsdelete_observations- Remove specific observationsdelete_relations- Remove relationships from the graphread_graph- Read the entire knowledge graphsearch_nodes- Search for nodes based on queriesopen_nodes- Open specific nodes by name
MCP servers are managed in bot.js:
- Startup Phase: All configured MCP servers are initialized when the bot starts
- Runtime Phase: MCP tools are available for AI function calling
- Shutdown Phase: All MCP servers are gracefully stopped on bot exit
The core/tool_loader.js module automatically loads MCP tools and makes them available to the AI:
const tool_loader = require('./core/tool_loader');
// Load all tools including MCP tools
const tools = tool_loader.loadTools();
// Use in OpenAI chat completion
const response = await oai.chatCompletion(messages, tools);Use the test script to verify MCP server connectivity:
node test_mcp.jsThis script:
- Starts all configured MCP servers
- Lists available tools from each server
- Performs a simple tool call test
- Stops all servers gracefully
HTTP-based servers returning HTML:
The mcp.so URLs may not be correct MCP HTTP endpoints. Possible issues:
- Different protocol required (SSE, WebSocket)
- Missing authentication headers
- Incorrect endpoint format
Windows Compatibility:
- Use
npm.cmd execinstead ofnpxon Windows - The
mcp_client.jsautomatically addsshell: truefor Windows
Server Startup Failures:
- Verify the command is available in system PATH
- Check environment variables are correctly configured
- Ensure required dependencies are installed
- Review server logs for detailed error messages
- Add server configuration to
data/tools.json - Select appropriate transport type (stdio or http)
- Configure required environment variables
- Test with
node test_mcp.js - Restart the bot to load the new server
- Verify tools are available for AI function calling
- Prefer stdio transport for local servers (more reliable and easier to debug)
- Use absolute paths for file-based server configurations
- Test thoroughly before deploying to production
- Monitor server health through logs
- Implement fallback logic when MCP tools fail
When the vibe_check tool is available, it must be used for reflection before major actions.
ALWAYS include the full user prompt in every vibe_check call to preserve context awareness.
As an autonomous agent:
- Treat
vibe_checkas a high-priority collaborative pattern interrupt for course correction, not as optional output - Use
vibe_checkat strategic points: after planning and before implementation, when complexity increases, and before significant system changes - Always provide the current phase in the call (
planning,implementation, orreview) for context-appropriate feedback - Adapt the approach based on
vibe_checkfeedback unless it is clearly irrelevant; if ignored, document why - Chain
vibe_checkwithvibe_learnafter resolving mistakes to record recurring patterns and successful corrections - If
vibe_checkis temporarily unavailable, continue with explicit self-review and log the limitation
Use VS Code symbol tools to reduce context consumption:
get_document_symbols_codefor file structure overview instead of reading entire filessearch_symbols_codeto find symbols by name across the projectget_symbol_definition_codefor type info and docs without full file context- Workflow: get outline → search symbols → get definitions → read implementation only when needed
The bot uses SQLite with the following tables:
Stores message history for conversation context.
| Column | Type | Purpose |
|---|---|---|
| id | INTEGER PRIMARY KEY | Unique message ID |
| guild_id | TEXT | Discord server ID |
| channel_id | TEXT | Discord channel ID |
| user_id | TEXT | Discord user ID |
| content | TEXT | Message content |
| timestamp | INTEGER | Unix timestamp |
Stores user XP and level information.
| Column | Type | Purpose |
|---|---|---|
| guild_id | TEXT | Discord server ID |
| user_id | TEXT | Discord user ID |
| xp | INTEGER | Current XP |
| level | INTEGER | Current level |
| PRIMARY KEY | (guild_id, user_id) | Composite key |
Channels where the chatbot is disabled.
| Column | Type | Purpose |
|---|---|---|
| guild_id | TEXT | Discord server ID |
| channel_id | TEXT | Discord channel ID |
| PRIMARY KEY | (guild_id, channel_id) | Composite key |
Channels where XP is not awarded.
| Column | Type | Purpose |
|---|---|---|
| guild_id | TEXT | Discord server ID |
| channel_id | TEXT | Discord channel ID |
| PRIMARY KEY | (guild_id, channel_id) | Composite key |
Role-based XP multipliers.
| Column | Type | Purpose |
|---|---|---|
| guild_id | TEXT | Discord server ID |
| role_id | TEXT | Discord role ID |
| multiplier | REAL | XP multiplier (e.g., 1.5 for 50% bonus) |
| PRIMARY KEY | (guild_id, role_id) | Composite key |
Server-specific configuration.
| Column | Type | Purpose |
|---|---|---|
| guild_id | TEXT PRIMARY KEY | Discord server ID |
| setting_name | TEXT | Setting identifier |
| setting_value | TEXT | Setting value (JSON string for complex values) |
User-specific memories with embeddings.
| Column | Type | Purpose |
|---|---|---|
| id | INTEGER PRIMARY KEY | Unique memory ID |
| guild_id | TEXT | Discord server ID |
| user_id | TEXT | Discord user ID |
| content | TEXT | Memory content |
| embedding | TEXT | Embedding vector (JSON array) |
| tags | TEXT | Memory tags (JSON array) |
| timestamp | INTEGER | Unix timestamp |
Server-wide memories with embeddings.
| Column | Type | Purpose |
|---|---|---|
| id | INTEGER PRIMARY KEY | Unique memory ID |
| guild_id | TEXT | Discord server ID |
| content | TEXT | Memory content |
| embedding | TEXT | Embedding vector (JSON array) |
| tags | TEXT | Memory tags (JSON array) |
| timestamp | INTEGER | Unix timestamp |
Role congratulations messages.
| Column | Type | Purpose |
|---|---|---|
| guild_id | TEXT | Discord server ID |
| role_id | TEXT | Discord role ID |
| message | TEXT | Congratulations message |
| PRIMARY KEY | (guild_id, role_id) | Composite key |
Reaction emojis per server.
| Column | Type | Purpose |
|---|---|---|
| guild_id | TEXT | Discord server ID |
| emoji | TEXT | Emoji (Unicode or custom) |
| PRIMARY KEY | (guild_id, emoji) | Composite key |
The bot uses special tags in messages to trigger memory operations:
| Tag | Purpose | Example |
|---|---|---|
[salvar_memoria] |
Save content as a memory | [salvar_memoria] O usuário gosta de café |
[meta] |
Add metadata to memory | [meta] preferência: café |
[imagem] |
Include image in memory | [imagem] (with image attachment) |
- Uses OpenAI-compatible API for text embeddings
- Stores embeddings as JSON arrays in database
- Implements cosine similarity for semantic search
- Retrieves relevant memories based on query similarity
-
User Memories: Specific to individual users
- Stored in
user_memoriestable - Associated with
user_id - Personal preferences, facts about user
- Stored in
-
Guild Memories: Server-wide knowledge
- Stored in
guild_memoriestable - Associated with
guild_id - Server rules, common knowledge, shared information
- Stored in
When processing a message:
- Generate embedding for the message
- Search for similar memories using cosine similarity
- Retrieve top N most relevant memories
- Include memories in AI context for better responses
[discord]
token = "YOUR_DISCORD_BOT_TOKEN"
client_id = "YOUR_CLIENT_ID"
[models.default]
base_url = "https://your-openai-compatible-api/v1"
api_key = "YOUR_API_KEY"
model = "gpt-4o"
[models.vision]
base_url = "https://your-openai-compatible-api/v1"
api_key = "YOUR_API_KEY"
model = "gpt-4o"
[models.transcriptions]
base_url = "https://your-openai-compatible-api/v1"
api_key = "YOUR_API_KEY"
model = "whisper-1"
[ai_settings]
send_system_prompt = true
retries = 3
initial_delay_ms = 1000
historyLimit = 6
historyMaxAge = 60
budgetTokenLimit = 3000
rate_limit_ms = 5000
[ai_tools]
enabled = true
tools_file = "data/tools.json"Capability-specific sections such as [models.vision] and [models.transcriptions] can omit fields and inherit missing values from [models.default].
The bot is deployed on Raspberry Pi 3b+ using systemd:
-
Update code:
cd <path/to/vica> git pull
-
Restart service:
systemctl restart vica
-
Check status:
systemctl status vica
The bot uses config.toml for configuration. Ensure sensitive data (API keys, tokens) are properly secured and not committed to version control.
All source files should include a header with:
- File path
- Creation date
- Author
- Collaboration notes
Example:
// core/database.js
// 2024-01-15
// Author: Your Name
// Collaboration: AI AssistantUse consistent logging format: [MODULE][LEVEL] message
console.log('[DATABASE][INFO] Connected to database');
console.error('[OAI][ERROR] API request failed:', error);
console.warn('[CHATBOT][WARN] Rate limit exceeded for user:', userId);Always use prepared statements for database queries:
const stmt = db.prepare('SELECT * FROM table WHERE id = ?');
const result = stmt.get(id);For multiple operations, use transactions:
db.transaction(() => {
const insert = db.prepare('INSERT INTO table (col) VALUES (?)');
insert.run(value1);
insert.run(value2);
})();Use async/await for asynchronous operations:
async function processMessage(message) {
try {
const response = await fetchAPI(message);
return response;
} catch (error) {
console.error('[MODULE][ERROR]', error);
throw error;
}
}Always wrap potentially failing operations in try/catch blocks:
try {
await riskyOperation();
} catch (error) {
console.error('[MODULE][ERROR]', error);
// Handle error appropriately
}Always check permissions before Discord API calls:
if (!message.member.permissions.has(Permissions.FLAGS.ADMINISTRATOR)) {
return message.reply('You do not have permission to use this command.');
}Use ephemeral responses for admin commands to avoid clutter:
await interaction.reply({
content: 'Configuration updated successfully',
ephemeral: true
});Use component collectors with timeouts for interactive elements:
const collector = message.createMessageComponentCollector({
time: 60000 // 60 seconds
});
collector.on('collect', async (i) => {
await i.update({ content: 'Selected!', components: [] });
});
collector.on('end', (collected, reason) => {
if (reason === 'time') {
// Handle timeout
}
});Use modals for complex configuration input:
const modal = new ModalBuilder()
.setCustomId('configModal')
.setTitle('Configuration');
const input = new TextInputBuilder()
.setCustomId('settingInput')
.setLabel('Setting Value')
.setStyle(TextInputStyle.Short);
const row = new ActionRowBuilder().addComponents(input);
modal.addComponents(row);
await interaction.showModal(modal);XP is awarded randomly within a configurable range:
const xp = Math.floor(Math.random() * (max - min + 1)) + min;Default range: 15-25 XP per message
- Per-user rate limit: 5 seconds
- Prevents spam and API abuse
- Implemented using timestamp tracking
- Context window: Last 6 messages
- Includes both user and bot messages
- Maintains conversation flow
- Stored in
mensagenstable
Discord has a 2000 character limit per message. The bot splits long responses:
const MAX_LENGTH = 2000;
const chunks = [];
for (let i = 0; i < message.length; i += MAX_LENGTH) {
chunks.push(message.slice(i, i + MAX_LENGTH));
}Cosine similarity is used for semantic search:
function cosineSimilarity(a, b) {
const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
return dotProduct / (magnitudeA * magnitudeB);
}Tools are loaded dynamically and passed to the AI:
const tools = tool_loader.loadTools();
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: conversation,
tools: tools
});- Primary language: Portuguese
- All user-facing text should be in Portuguese
- Code comments and variable names can be in English or Portuguese
- Maintain consistency within files
- Always handle errors gracefully
- Provide user-friendly error messages
- Log errors with context for debugging
- Never expose sensitive information in error messages
- Keep files focused on single responsibilities
- Use descriptive file and function names
- Follow the existing directory structure
- Maintain separation of concerns (commands, events, core, helpers, tools)
- Test commands in a development server first
- Verify database operations don't cause locks
- Check rate limiting works correctly
- Test error scenarios
- Use prepared statements for database queries
- Implement caching where appropriate (auditCache)
- Avoid blocking the event loop
- Use async/await for I/O operations
- Never commit API keys or tokens
- Use environment variables or config files
- Validate user input
- Check permissions before executing privileged operations
- Sanitize database inputs
Before deploying:
- Update
config.tomlwith production values - Test all critical commands
- Verify database migrations
- Check systemd service configuration
- Monitor logs after deployment
- Database locks: Ensure transactions are properly committed
- Rate limits: Implement exponential backoff for API calls
- Memory leaks: Clean up event listeners and collectors
- Permission errors: Check bot permissions in Discord server
vica/
├── bot.js # Main bot entry point
├── config.example.toml # Example configuration
├── delete-commands.js # Delete slash commands
├── deploy-commands.js # Deploy slash commands
├── package.json # Node.js dependencies
├── test_mcp.js # MCP integration test script
├── README.MD # Project documentation
├── GEMINI.MD # This file
│
├── commands/ # Slash commands
│ ├── canal_noticia.js
│ ├── chat_blacklist.js
│ ├── comment.js
│ ├── config.js
│ ├── deslurkar.js
│ ├── enex.js
│ ├── gmemories.js
│ ├── mmemories.js
│ ├── noticia.js
│ ├── perguntar.js
│ ├── rank.js
│ ├── rank_blacklist.js
│ ├── rank_reset.js
│ ├── rank_rolexp.js
│ ├── rank_setxp.js
│ ├── reaction_emoji.js
│ ├── reaction_thread.js
│ ├── system_channel.js
│ ├── trigger.js
│ └── up_role.js
│
├── core/ # Core functionality
│ ├── auditCache.js
│ ├── database.js
│ ├── mcp_client.js # MCP client for external tools
│ ├── oai_interface.js
│ ├── static_data.js
│ ├── tagParser.js
│ ├── tool_loader.js # Loads tools and MCP servers
│ └── transcriber.js
│
├── events/ # Discord event handlers
│ ├── guildBanAdd.js
│ ├── guildMemberAdd.js
│ ├── guildMemberRemove.js
│ ├── guildMemberUpdate.js
│ ├── interactionCreate.js
│ ├── messageCreate.js
│ └── messageReactionAdd.js
│
├── helpers/ # Helper utilities
│ └── embeddingHelper.js
│
└── tools/ # OpenAI function tools
├── calculate.js
└── get_current_time.js
When contributing to this project:
- Follow the existing code patterns and style
- Add appropriate logging with the
[MODULE][LEVEL]format - Include file headers with path, date, author, and collaboration notes
- Test changes thoroughly before committing
- Update this documentation if adding new features or changing architecture
See LICENSE file for details.
Last updated: 2026-03-09 Document version: 1.2