Newer
Older
TelosDB / src / mcp-handlers.js
@楽曲作りまくりおじさん 楽曲作りまくりおじさん 8 days ago 3 KB Add comprehensive test suite (35 tests) and update journal
import {
    CallToolRequestSchema,
    ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { db, EMBEDDING_DIM, knexDb } from "./db.js";
import { llamaCompletion, llamaEmbedding } from "./llama-client.js";
import { getToolDefinitions } from "./mcp-tools.js";

function assertEmbeddingDim(vector) {
  if (!Array.isArray(vector) || vector.length !== EMBEDDING_DIM) {
    throw new Error(`Embedding dimension mismatch. Expected ${EMBEDDING_DIM}`);
  }
}

export function registerMcpHandlers(server) {
  server.setRequestHandler(ListToolsRequestSchema, async () => {
    return {
      tools: getToolDefinitions(),
    };
  });

  server.setRequestHandler(CallToolRequestSchema, async (request) => {
    switch (request.params.name) {
      case "add_item_text": {
        const { content, path } = request.params.arguments;
        const embedding = await llamaEmbedding(content);
        assertEmbeddingDim(embedding);
        const insertIds = await knexDb("items").insert({ content, path });
        const id = Array.isArray(insertIds) ? insertIds[0] : insertIds;
        db.prepare("INSERT INTO vec_items(id, embedding) VALUES (?, ?)")
          .run(id, new Float32Array(embedding));
        return { content: [{ type: "text", text: `Added item with id ${id}` }] };
      }
      case "add_item": {
        const { content, path, vector } = request.params.arguments;
        assertEmbeddingDim(vector);
        const insertIds = await knexDb("items").insert({ content, path });
        const id = Array.isArray(insertIds) ? insertIds[0] : insertIds;
        db.prepare("INSERT INTO vec_items(id, embedding) VALUES (?, ?)")
          .run(id, new Float32Array(vector));
        return { content: [{ type: "text", text: `Added item with id ${id}` }] };
      }
      case "search_text": {
        const { content } = request.params.arguments;
        const embedding = await llamaEmbedding(content);
        assertEmbeddingDim(embedding);
        const results = db.prepare(`
          SELECT
            i.content,
            i.path,
            i.created_at,
            i.updated_at,
            v.distance
          FROM vec_items v
          JOIN items i ON v.id = i.id
          WHERE embedding MATCH ?
          ORDER BY distance
          LIMIT 5
        `).all(new Float32Array(embedding));

        return {
          content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
        };
      }
      case "search_vector": {
        const { vector } = request.params.arguments;
        assertEmbeddingDim(vector);
        const results = db.prepare(`
          SELECT
            i.content,
            i.path,
            i.created_at,
            i.updated_at,
            v.distance
          FROM vec_items v
          JOIN items i ON v.id = i.id
          WHERE embedding MATCH ?
          ORDER BY distance
          LIMIT 5
        `).all(new Float32Array(vector));

        return {
          content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
        };
      }
      case "llm_generate": {
        const { prompt, n_predict, temperature } = request.params.arguments;
        const text = await llamaCompletion(prompt, { n_predict, temperature });
        return { content: [{ type: "text", text }] };
      }
      default:
        throw new Error("Unknown tool");
    }
  });
}