public
anord
read
Ledger
Why work hard when you can work easier?
Languages
Repository composition by tracked source files.
TypeScript
86%
CSS
10%
SQL
3%
Shell
1%
HTML
0%
Trace
apps/api/src/http/routes/mcp.ts
Trace helps you understand code history line by line. See who changed each line, when it changed, and which commit introduced it.
Author
Date
Commit
Line
Code
1
import { Router } from "express";
2
import { z } from "zod";
3
import { createOrUpdatePage, getPageBySlug, getPageMetadata, listSpaces } from "../../services/pages.js";
4
import { searchPages } from "../../services/search.js";
5
import { canEditPage } from "@ledger/shared";
7
const toolsCallSchema = z.object({
8
method: z.string(),
9
params: z.record(z.any()).optional(),
10
id: z.union([z.string(), z.number()]).optional()
11
});
13
export const mcpRouter = Router();
15
mcpRouter.post("/", async (req, res) => {
16
const body = toolsCallSchema.parse(req.body);
18
if (body.method === "tools/list") {
19
return res.json({
20
id: body.id,
21
result: {
22
tools: [
23
{ name: "search_knowledge_base", description: "Search knowledge base pages visible to the current auth context and return structured page summaries." },
24
{ name: "read_page", description: "Read a page by slug when the current auth context is allowed to access it." },
25
{ name: "list_spaces", description: "List spaces visible to the current auth context." },
26
{ name: "get_page_metadata", description: "Return non-sensitive page metadata for a visible page id." },
27
{ name: "create_draft_page", description: "Create a draft page in a space when the current auth context can edit content." }
28
]
29
}
30
});
31
}
33
if (body.method === "tools/call") {
34
const name = String(body.params?.name ?? "");
35
const args = body.params?.arguments ?? {};
37
if (name === "search_knowledge_base") {
38
const result = await searchPages(String(args.query ?? ""), req.user ?? null);
39
return res.json({ id: body.id, result: { pages: result } });
40
}
42
if (name === "read_page") {
43
const result = await getPageBySlug(String(args.slug ?? ""), req.user ?? null);
44
return res.json({ id: body.id, result: result ? { page: result } : null });
45
}
47
if (name === "list_spaces") {
48
const result = await listSpaces(req.user ?? null);
49
return res.json({ id: body.id, result: { spaces: result } });
50
}
52
if (name === "get_page_metadata") {
53
const result = await getPageMetadata(String(args.pageId ?? ""), req.user ?? null);
54
return res.json({ id: body.id, result: result ? { page: result } : null });
55
}
57
if (name === "create_draft_page") {
58
if (!canEditPage(req.user ?? null)) {
59
return res.status(403).json({ error: "Editor access required" });
60
}
62
const result = await createOrUpdatePage(
63
null,
64
{
65
spaceId: String(args.spaceId),
66
title: String(args.title),
67
bodyMarkdown: String(args.bodyMarkdown),
68
visibility: "internal",
69
state: "draft"
70
},
71
req.user!.id
72
);
74
return res.json({ id: body.id, result: { page: result } });
75
}
76
}
78
return res.status(400).json({ error: "Unsupported MCP method" });
79
});