diff --git a/open-sse/mcp-server/server.ts b/open-sse/mcp-server/server.ts index 9200290f3b..1fa1d65003 100644 --- a/open-sse/mcp-server/server.ts +++ b/open-sse/mcp-server/server.ts @@ -55,6 +55,8 @@ import { handleGetSessionSnapshot, handleSyncPricing, } from "./tools/advancedTools.ts"; +import { memoryTools } from "./tools/memoryTools.ts"; +import { skillTools } from "./tools/skillTools.ts"; import { normalizeQuotaResponse } from "../../src/shared/contracts/quota.ts"; // ============ Configuration ============ @@ -717,6 +719,48 @@ export function createMcpServer(): McpServer { ) ); + // ── Memory Tools ────────────────────────────── + Object.values(memoryTools).forEach((toolDef) => { + server.registerTool( + toolDef.name, + { + description: toolDef.description, + inputSchema: toolDef.inputSchema as any, + }, + withScopeEnforcement(toolDef.name, async (args) => { + try { + const parsedArgs = toolDef.inputSchema.parse(args ?? {}); + const result = await toolDef.handler(parsedArgs as any); + return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] }; + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + return { content: [{ type: "text" as const, text: `Error: ${msg}` }], isError: true }; + } + }) + ); + }); + + // ── Skill Tools ────────────────────────────── + Object.values(skillTools).forEach((toolDef) => { + server.registerTool( + toolDef.name, + { + description: toolDef.description, + inputSchema: toolDef.inputSchema as any, + }, + withScopeEnforcement(toolDef.name, async (args) => { + try { + const parsedArgs = toolDef.inputSchema.parse(args ?? {}); + const result = await toolDef.handler(parsedArgs as any); + return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] }; + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + return { content: [{ type: "text" as const, text: `Error: ${msg}` }], isError: true }; + } + }) + ); + }); + return server; } diff --git a/src/app/api/memory/[id]/route.ts b/src/app/api/memory/[id]/route.ts new file mode 100644 index 0000000000..4abbe5df32 --- /dev/null +++ b/src/app/api/memory/[id]/route.ts @@ -0,0 +1,36 @@ +import { NextResponse } from "next/server"; +import { deleteMemory, getMemory } from "@/lib/memory/store"; + +export async function DELETE( + request: Request, + props: { params: Promise<{ id: string }> } +) { + try { + const { id } = await props.params; + const success = await deleteMemory(id); + if (!success) { + return NextResponse.json({ error: "Memory not found" }, { status: 404 }); + } + return NextResponse.json({ success: true }); + } catch (err: unknown) { + const error = err instanceof Error ? err.message : String(err); + return NextResponse.json({ error }, { status: 500 }); + } +} + +export async function GET( + request: Request, + props: { params: Promise<{ id: string }> } +) { + try { + const { id } = await props.params; + const memory = await getMemory(id); + if (!memory) { + return NextResponse.json({ error: "Not found" }, { status: 404 }); + } + return NextResponse.json({ memory }); + } catch (err: unknown) { + const error = err instanceof Error ? err.message : String(err); + return NextResponse.json({ error }, { status: 500 }); + } +} diff --git a/src/app/api/memory/route.ts b/src/app/api/memory/route.ts new file mode 100644 index 0000000000..9c77cb42d0 --- /dev/null +++ b/src/app/api/memory/route.ts @@ -0,0 +1,36 @@ +import { NextResponse } from "next/server"; +import { listMemories, createMemory } from "@/lib/memory/store"; + +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + const apiKeyId = searchParams.get("apiKeyId") || undefined; + const type = searchParams.get("type") as any || undefined; + const sessionId = searchParams.get("sessionId") || undefined; + const limitParams = searchParams.get("limit"); + const offsetParams = searchParams.get("offset"); + + const memories = await listMemories({ + apiKeyId, + type, + sessionId, + limit: limitParams ? parseInt(limitParams, 10) : undefined, + offset: offsetParams ? parseInt(offsetParams, 10) : undefined, + }); + return NextResponse.json({ memories }); + } catch (err: unknown) { + const error = err instanceof Error ? err.message : String(err); + return NextResponse.json({ error }, { status: 500 }); + } +} + +export async function POST(request: Request) { + try { + const body = await request.json(); + const memoryId = await createMemory(body); + return NextResponse.json({ success: true, id: memoryId }); + } catch (err: unknown) { + const error = err instanceof Error ? err.message : String(err); + return NextResponse.json({ error }, { status: 400 }); + } +} diff --git a/src/app/api/skills/[id]/route.ts b/src/app/api/skills/[id]/route.ts new file mode 100644 index 0000000000..0386143d7d --- /dev/null +++ b/src/app/api/skills/[id]/route.ts @@ -0,0 +1,30 @@ +import { NextResponse } from "next/server"; +import { getDbInstance } from "@/lib/db/core"; +import { skillRegistry } from "@/lib/skills/registry"; + +export async function PUT( + request: Request, + props: { params: Promise<{ id: string }> } +) { + try { + const { id } = await props.params; + const body = await request.json(); + + if (typeof body.enabled !== "boolean") { + return NextResponse.json({ error: "Invalid payload, missing enabled boolean" }, { status: 400 }); + } + + const db = getDbInstance(); + db.prepare("UPDATE skills SET enabled = ? WHERE id = ?").run( + body.enabled ? 1 : 0, + id + ); + + await skillRegistry.loadFromDatabase(); + + return NextResponse.json({ success: true, enabled: body.enabled }); + } catch (err: unknown) { + const error = err instanceof Error ? err.message : String(err); + return NextResponse.json({ error }, { status: 500 }); + } +} diff --git a/src/app/api/skills/executions/route.ts b/src/app/api/skills/executions/route.ts new file mode 100644 index 0000000000..098fa5dcc6 --- /dev/null +++ b/src/app/api/skills/executions/route.ts @@ -0,0 +1,12 @@ +import { NextResponse } from "next/server"; +import { skillExecutor } from "@/lib/skills/executor"; + +export async function GET() { + try { + const executions = skillExecutor.listExecutions(); + return NextResponse.json({ executions }); + } catch (err: unknown) { + const error = err instanceof Error ? err.message : String(err); + return NextResponse.json({ error }, { status: 500 }); + } +} diff --git a/src/app/api/skills/route.ts b/src/app/api/skills/route.ts new file mode 100644 index 0000000000..ca2a8e1580 --- /dev/null +++ b/src/app/api/skills/route.ts @@ -0,0 +1,13 @@ +import { NextResponse } from "next/server"; +import { skillRegistry } from "@/lib/skills/registry"; + +export async function GET() { + try { + await skillRegistry.loadFromDatabase(); + const skills = skillRegistry.list(); + return NextResponse.json({ skills }); + } catch (err: unknown) { + const error = err instanceof Error ? err.message : String(err); + return NextResponse.json({ error }, { status: 500 }); + } +} diff --git a/src/lib/db/migrations/014_create_memories_down.sql b/src/lib/db/migrations/014_create_memories_down.sql deleted file mode 100644 index 68a218d09a..0000000000 --- a/src/lib/db/migrations/014_create_memories_down.sql +++ /dev/null @@ -1,4 +0,0 @@ --- 014_create_memories_down.sql --- DOWN Migration: Remove memories table (Rollback) - -DROP TABLE IF EXISTS memories; diff --git a/src/lib/db/migrations/014_create_memories.sql b/src/lib/db/migrations/015_create_memories.sql similarity index 100% rename from src/lib/db/migrations/014_create_memories.sql rename to src/lib/db/migrations/015_create_memories.sql diff --git a/src/lib/db/migrations/015_create_skills_down.sql b/src/lib/db/migrations/015_create_skills_down.sql deleted file mode 100644 index 9bc12d0247..0000000000 --- a/src/lib/db/migrations/015_create_skills_down.sql +++ /dev/null @@ -1,5 +0,0 @@ --- 015_create_skills_down.sql --- Rollback skills and skill_executions tables - -DROP TABLE IF EXISTS skill_executions; -DROP TABLE IF EXISTS skills; \ No newline at end of file diff --git a/src/lib/db/migrations/015_create_skills.sql b/src/lib/db/migrations/016_create_skills.sql similarity index 100% rename from src/lib/db/migrations/015_create_skills.sql rename to src/lib/db/migrations/016_create_skills.sql