feat(mcp): Phase 2 — real DB operations for tool execution

Implement actual sync_changes reads and writes for MCP tool calls:

- sync-db.ts: Connection to mana_sync DB, RLS-scoped withUser(),
  readLatestRecords() for replaying sync state, writeRecord() for
  creating sync_changes entries
- executor.ts: 10 tool handlers implemented:
  - Reads: list_tasks, get_task_stats, list_notes, get_todays_events,
    get_contacts, get_habits
  - Writes: create_task, complete_task, create_note, create_contact
  - Remaining tools return helpful "not yet implemented" message
- server.ts: userId from auth context bound into MCP session via closure
- index.ts: typed Hono app with AuthVariables

Write pattern matches mana-ai: INSERT into sync_changes with
actor={kind:'system', source:'mcp-tool'}, client_id='mcp-server'.
Records appear on user devices on next sync cycle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-16 13:46:06 +02:00
parent 10acabfed6
commit e969324cc8
4 changed files with 391 additions and 53 deletions

View file

@ -13,6 +13,7 @@ import {
errorHandler,
notFoundHandler,
rateLimitMiddleware,
type AuthVariables,
} from '@mana/shared-hono';
// MCP server
@ -41,7 +42,7 @@ import { whoRoutes } from './modules/who/routes';
const PORT = parseInt(process.env.PORT || '3060', 10);
const CORS_ORIGINS = (process.env.CORS_ORIGINS || 'http://localhost:5173').split(',');
const app = new Hono();
const app = new Hono<{ Variables: AuthVariables }>();
// ─── Global Middleware ──────────────────────────────────────
app.onError(errorHandler);
@ -53,7 +54,7 @@ app.use('/api/*', authMiddleware());
// ─── MCP Endpoint ──────────────────────────────────────────
// Streamable HTTP transport: POST (messages), GET (SSE stream), DELETE (close)
app.all('/api/v1/mcp', (c) => handleMcpRequest(c.req.raw));
app.all('/api/v1/mcp', (c) => handleMcpRequest(c.req.raw, c.get('userId')));
// ─── Module Routes ──────────────────────────────────────────
app.route('/api/v1/calendar', calendarRoutes);