NarraNexusNarraNexus
NarraNexus · Modules

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()Required

Returns ModuleConfig with name, priority, enabled, description, and module_type ("capability" or "task")

hook_data_gathering(ctx_data)Optional

Called before LLM execution. Enriches ContextData with module-specific data. Must return ContextData.

hook_after_event_execution(params)Optional

Called after LLM execution. Async post-processing — DB updates, summaries, memory consolidation.

get_mcp_config()Required

Returns MCPServerConfig with server_name and URL, or None if no tools needed.

create_mcp_server()Optional

Returns a FastMCP instance for tool registration.

get_instructions(ctx_data)Optional

Returns instruction text with dynamic placeholders ({awareness}, {user_id}, etc.).

ModuleConfig

The configuration object has exactly five fields:

nameMust match MODULE_MAP key and class name
priorityInstruction sort order (0 = highest). Awareness=0, Chat=1, Job=2, ...
enabledActivation flag
descriptionHuman-readable purpose
module_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. 1.Create module directory: module/your_module/
  2. 2.Implement XYZBaseModule with get_config() and get_mcp_config()
  3. 3.Register in module/__init__.py MODULE_MAP
  4. 4.Add database tables via utils/schema_registry.py using _register(TableDef(...))
  5. 5.Place Repository in repository/, Schema in schema/, private logic in _your_module_impl/
  6. 6.Assign next available MCP port (7807+)
  7. 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

7801AwarenessModule
7802SocialNetworkModule
7803JobModule
7804ChatModule
7805GeminiRAGModule
7806SkillModule
7807+Available for new modules

Adding 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. 1.Create a channel module with trigger, context builder, and MCP tools
  2. 2.Register an async sender function in ChannelSenderRegistry for reply routing
  3. 3.Implement ChannelContextBuilderBase for platform-specific prompt assembly
  4. 4.Attach a ChannelTag to every incoming message before calling AgentRuntime
  5. 5.Use the shared poller pattern — never per-agent listeners
  6. 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.