refactor: scaffold module registries and default-module layout
Additive change — existing code paths still run via inline fallbacks. Prepares core for per-module extractions in PR #3 onward. Four registries added with empty defaults: - delivery action handlers (delivery.ts) - router inbound gate (router.ts) - response dispatcher (index.ts) - MCP tool self-registration (container/agent-runner/src/mcp-tools/server.ts) Default modules moved to src/modules/ for signaling: - src/modules/typing/ (extracted from delivery.ts) - src/modules/mount-security/ (moved from src/mount-security.ts) Both are imported directly by core — no hook, no registry. Removal requires editing core imports. Migrator now keys applied rows by name (uniqueness) so module migrations can pick arbitrary version numbers. Stored version column is auto-assigned as an applied-order sequence. sqlite_master guards added around core calls into module-owned tables (user_roles, agent_destinations, pending_questions). No-ops today; load-bearing after the owning modules are extracted. MODULE-HOOK markers placed at scheduling's two skill-edit sites (host-sweep.ts recurrence call, poll-loop.ts pre-task gate). PR #4 replaces the marked blocks when scheduling moves to its module. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
* (see mcp-tools/index.ts). The host re-checks permission on receive.
|
||||
*/
|
||||
import { writeMessageOut } from '../db/messages-out.js';
|
||||
import { registerTools } from './server.js';
|
||||
import type { McpToolDefinition } from './types.js';
|
||||
|
||||
function log(msg: string): void {
|
||||
@@ -62,4 +63,4 @@ export const createAgent: McpToolDefinition = {
|
||||
},
|
||||
};
|
||||
|
||||
export const agentTools: McpToolDefinition[] = [createAgent];
|
||||
registerTools([createAgent]);
|
||||
|
||||
@@ -12,6 +12,7 @@ import path from 'path';
|
||||
import { findByName, getAllDestinations } from '../destinations.js';
|
||||
import { getMessageIdBySeq, getRoutingBySeq, writeMessageOut } from '../db/messages-out.js';
|
||||
import { getSessionRouting } from '../db/session-routing.js';
|
||||
import { registerTools } from './server.js';
|
||||
import type { McpToolDefinition } from './types.js';
|
||||
|
||||
function log(msg: string): void {
|
||||
@@ -258,4 +259,4 @@ export const addReaction: McpToolDefinition = {
|
||||
},
|
||||
};
|
||||
|
||||
export const coreTools: McpToolDefinition[] = [sendMessage, sendFile, editMessage, addReaction];
|
||||
registerTools([sendMessage, sendFile, editMessage, addReaction]);
|
||||
|
||||
@@ -1,59 +1,21 @@
|
||||
/**
|
||||
* MCP tools barrel — collects all tool modules and starts the server.
|
||||
* MCP tools barrel — imports each tool module for its side-effect
|
||||
* `registerTools([...])` call, then starts the MCP server.
|
||||
*
|
||||
* Each module exports a McpToolDefinition[] array. This file registers
|
||||
* them all with the MCP server. Adding a new tool module requires only
|
||||
* importing it here and spreading its tools array.
|
||||
* Adding a new tool module: create the file, call `registerTools([...])`
|
||||
* at module scope, and append the import here. No central list.
|
||||
*/
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
||||
|
||||
import type { McpToolDefinition } from './types.js';
|
||||
import { coreTools } from './core.js';
|
||||
import { schedulingTools } from './scheduling.js';
|
||||
import { interactiveTools } from './interactive.js';
|
||||
import { agentTools } from './agents.js';
|
||||
import { selfModTools } from './self-mod.js';
|
||||
import './core.js';
|
||||
import './scheduling.js';
|
||||
import './interactive.js';
|
||||
import './agents.js';
|
||||
import './self-mod.js';
|
||||
import { startMcpServer } from './server.js';
|
||||
|
||||
function log(msg: string): void {
|
||||
console.error(`[mcp-tools] ${msg}`);
|
||||
}
|
||||
|
||||
const allTools: McpToolDefinition[] = [
|
||||
...coreTools,
|
||||
...schedulingTools,
|
||||
...interactiveTools,
|
||||
...agentTools,
|
||||
...selfModTools,
|
||||
];
|
||||
|
||||
const toolMap = new Map<string, McpToolDefinition>();
|
||||
for (const t of allTools) {
|
||||
toolMap.set(t.tool.name, t);
|
||||
}
|
||||
|
||||
async function startMcpServer(): Promise<void> {
|
||||
const server = new Server({ name: 'nanoclaw', version: '2.0.0' }, { capabilities: { tools: {} } });
|
||||
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
tools: allTools.map((t) => t.tool),
|
||||
}));
|
||||
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const { name, arguments: args } = request.params;
|
||||
const tool = toolMap.get(name);
|
||||
if (!tool) {
|
||||
return { content: [{ type: 'text', text: `Unknown tool: ${name}` }] };
|
||||
}
|
||||
return tool.handler(args ?? {});
|
||||
});
|
||||
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
log(`MCP server started with ${allTools.length} tools: ${allTools.map((t) => t.tool.name).join(', ')}`);
|
||||
}
|
||||
|
||||
startMcpServer().catch((err) => {
|
||||
log(`MCP server error: ${err instanceof Error ? err.message : String(err)}`);
|
||||
process.exit(1);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { findQuestionResponse, markCompleted } from '../db/messages-in.js';
|
||||
import { writeMessageOut } from '../db/messages-out.js';
|
||||
import { getSessionRouting } from '../db/session-routing.js';
|
||||
import { registerTools } from './server.js';
|
||||
import type { McpToolDefinition } from './types.js';
|
||||
|
||||
function log(msg: string): void {
|
||||
@@ -165,4 +166,4 @@ export const sendCard: McpToolDefinition = {
|
||||
},
|
||||
};
|
||||
|
||||
export const interactiveTools: McpToolDefinition[] = [askUserQuestion, sendCard];
|
||||
registerTools([askUserQuestion, sendCard]);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import { getInboundDb } from '../db/connection.js';
|
||||
import { writeMessageOut } from '../db/messages-out.js';
|
||||
import { getSessionRouting } from '../db/session-routing.js';
|
||||
import { registerTools } from './server.js';
|
||||
import type { McpToolDefinition } from './types.js';
|
||||
|
||||
function log(msg: string): void {
|
||||
@@ -265,4 +266,4 @@ export const updateTask: McpToolDefinition = {
|
||||
},
|
||||
};
|
||||
|
||||
export const schedulingTools: McpToolDefinition[] = [scheduleTask, listTasks, updateTask, cancelTask, pauseTask, resumeTask];
|
||||
registerTools([scheduleTask, listTasks, updateTask, cancelTask, pauseTask, resumeTask]);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* the host side (defense in depth).
|
||||
*/
|
||||
import { writeMessageOut } from '../db/messages-out.js';
|
||||
import { registerTools } from './server.js';
|
||||
import type { McpToolDefinition } from './types.js';
|
||||
|
||||
function log(msg: string): void {
|
||||
@@ -140,4 +141,4 @@ export const requestRebuild: McpToolDefinition = {
|
||||
},
|
||||
};
|
||||
|
||||
export const selfModTools: McpToolDefinition[] = [installPackages, addMcpServer, requestRebuild];
|
||||
registerTools([installPackages, addMcpServer, requestRebuild]);
|
||||
|
||||
54
container/agent-runner/src/mcp-tools/server.ts
Normal file
54
container/agent-runner/src/mcp-tools/server.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* MCP server bootstrap + tool self-registration.
|
||||
*
|
||||
* Each tool module calls `registerTools([...])` at import time. The
|
||||
* barrel (`index.ts`) imports every tool module for side effects, then
|
||||
* calls `startMcpServer()` which uses whatever was registered.
|
||||
*
|
||||
* Default when only `core.ts` is imported: the core `send_message` /
|
||||
* `send_file` / `edit_message` / `add_reaction` tools are available.
|
||||
*/
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
||||
|
||||
import type { McpToolDefinition } from './types.js';
|
||||
|
||||
function log(msg: string): void {
|
||||
console.error(`[mcp-tools] ${msg}`);
|
||||
}
|
||||
|
||||
const allTools: McpToolDefinition[] = [];
|
||||
const toolMap = new Map<string, McpToolDefinition>();
|
||||
|
||||
export function registerTools(tools: McpToolDefinition[]): void {
|
||||
for (const t of tools) {
|
||||
if (toolMap.has(t.tool.name)) {
|
||||
log(`Warning: tool "${t.tool.name}" already registered, skipping duplicate`);
|
||||
continue;
|
||||
}
|
||||
allTools.push(t);
|
||||
toolMap.set(t.tool.name, t);
|
||||
}
|
||||
}
|
||||
|
||||
export async function startMcpServer(): Promise<void> {
|
||||
const server = new Server({ name: 'nanoclaw', version: '2.0.0' }, { capabilities: { tools: {} } });
|
||||
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
tools: allTools.map((t) => t.tool),
|
||||
}));
|
||||
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const { name, arguments: args } = request.params;
|
||||
const tool = toolMap.get(name);
|
||||
if (!tool) {
|
||||
return { content: [{ type: 'text', text: `Unknown tool: ${name}` }] };
|
||||
}
|
||||
return tool.handler(args ?? {});
|
||||
});
|
||||
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
log(`MCP server started with ${allTools.length} tools: ${allTools.map((t) => t.tool.name).join(', ')}`);
|
||||
}
|
||||
Reference in New Issue
Block a user