Custom Modules
How to build and register your own modules — from the base class interface to MCP tool exposure and lifecycle hooks.
XYZBaseModule Interface
Every module inherits from XYZBaseModule and implements these core methods:
get_config()RequiredReturns ModuleConfig with name, priority, enabled, description, and module_type ("capability" or "task")
hook_data_gathering(ctx_data)OptionalCalled before LLM execution. Enriches ContextData with module-specific data. Must return ContextData.
hook_after_event_execution(params)OptionalCalled after LLM execution. Async post-processing — DB updates, summaries, memory consolidation.
get_mcp_config()RequiredReturns MCPServerConfig with server_name and URL, or None if no tools needed.
create_mcp_server()OptionalReturns a FastMCP instance for tool registration.
get_instructions(ctx_data)OptionalReturns instruction text with dynamic placeholders ({awareness}, {user_id}, etc.).
ModuleConfig
The configuration object has exactly five fields:
nameMust match MODULE_MAP key and class namepriorityInstruction sort order (0 = highest). Awareness=0, Chat=1, Job=2, ...enabledActivation flagdescriptionHuman-readable purposemodule_type"capability" (auto-loaded) or "task" (LLM-decided creation)Instance ID prefixes are derived automatically from the class name (e.g., ChatModule → chat_). No manual specification needed.
Registration Checklist
- 1.Create module directory: module/your_module/
- 2.Implement XYZBaseModule with get_config() and get_mcp_config()
- 3.Register in module/__init__.py MODULE_MAP
- 4.Add database tables via utils/schema_registry.py using _register(TableDef(...))
- 5.Place Repository in repository/, Schema in schema/, private logic in _your_module_impl/
- 6.Assign next available MCP port (7807+)
- 7.Add MCP tools via create_mcp_server() if needed
MCP Server Architecture
Key design decisions for MCP tool exposure:
- One MCP Server per module type (not per Agent) — all Agents share the same server instance
- Agent identification via explicit tool parameters (agent_id, instance_id)
- Data isolation through WHERE clause filtering, not connection-level isolation
- Stateless — no per-Agent state stored in the MCP process
- Database access via get_mcp_db_client() class method (lazy initialization)
Port Allocation
7801AwarenessModule7802SocialNetworkModule7803JobModule7804ChatModule7805GeminiRAGModule7806SkillModule7807+Available for new modulesAdding a New IM Integration
New messaging platform integrations (Telegram, Lark, Slack, etc.) follow a standard checklist. The channel system uses a shared poller pattern — one background process routes messages to agents, never one listener per agent.
- 1.Create a channel module with trigger, context builder, and MCP tools
- 2.Register an async sender function in ChannelSenderRegistry for reply routing
- 3.Implement ChannelContextBuilderBase for platform-specific prompt assembly
- 4.Attach a ChannelTag to every incoming message before calling AgentRuntime
- 5.Use the shared poller pattern — never per-agent listeners
- 6.Register the module in MODULE_MAP and assign a port
Contact info for entities is stored as nested JSON in SocialNetworkModule. Adding new channels requires zero changes to the social network schema — just deep-merge the new channel data.