Add MCP memo listing tool
This commit is contained in:
1 parent
c45c8c7a9e
commit
74b623c63a
1 file changed
+83
@@ -1693,6 +1693,20 @@ const MCP_TOOLS = [
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list_memos",
|
||||
description: "List active EdgeEver memos with pagination. Use includeContent when full Markdown is needed.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
notebookId: { type: "string" },
|
||||
limit: { type: "integer", minimum: 1, maximum: 100 },
|
||||
offset: { type: "integer", minimum: 0 },
|
||||
includeContent: { type: "boolean" },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "get_memo",
|
||||
description: "Read a memo with Markdown content.",
|
||||
@@ -1851,6 +1865,15 @@ const callMcpTool = async (
|
||||
}),
|
||||
};
|
||||
}
|
||||
case "list_memos": {
|
||||
assertScope(auth, "read:memos");
|
||||
return await listMemosForMcp(c.env.DB, {
|
||||
notebookId: getOptionalString(args.notebookId),
|
||||
limit: clampNumber(Number(args.limit ?? 50), 1, 100),
|
||||
offset: clampNumber(Number(args.offset ?? 0), 0, 100_000),
|
||||
includeContent: args.includeContent === true,
|
||||
});
|
||||
}
|
||||
case "get_memo": {
|
||||
assertScope(auth, "read:memos");
|
||||
const memoId = getRequiredString(args.memoId, "memoId");
|
||||
@@ -3084,6 +3107,66 @@ const searchMemoSummaries = async (
|
||||
return rows.results.map(mapMemoSummary);
|
||||
};
|
||||
|
||||
const listMemosForMcp = async (
|
||||
db: D1Database,
|
||||
options: { notebookId?: string | null; limit: number; offset: number; includeContent: boolean }
|
||||
) => {
|
||||
const notebookId = options.notebookId?.trim() || null;
|
||||
const limit = clampNumber(options.limit, 1, 100);
|
||||
const offset = clampNumber(options.offset, 0, 100_000);
|
||||
const pageSize = limit + 1;
|
||||
|
||||
if (options.includeContent) {
|
||||
const rows = await db
|
||||
.prepare(
|
||||
`SELECT m.id, m.notebook_id, m.title, m.excerpt, m.tags_json, m.is_pinned,
|
||||
m.is_archived, m.is_deleted, m.created_at, m.updated_at, m.deleted_at, c.revision,
|
||||
c.content_json, c.content_markdown, c.content_text, c.content_hash,
|
||||
m.source_memo_ids, m.merge_source_count, m.merged_into_memo_id
|
||||
FROM memos m
|
||||
INNER JOIN memo_contents c ON c.memo_id = m.id
|
||||
WHERE m.is_deleted = 0
|
||||
AND (? IS NULL OR m.notebook_id = ?)
|
||||
ORDER BY m.updated_at DESC, m.id ASC
|
||||
LIMIT ? OFFSET ?`
|
||||
)
|
||||
.bind(notebookId, notebookId, pageSize, offset)
|
||||
.all<MemoDetailRow>();
|
||||
const page = rows.results.slice(0, limit).map(mapMemoDetail);
|
||||
|
||||
return {
|
||||
memos: page,
|
||||
limit,
|
||||
offset,
|
||||
nextOffset: rows.results.length > limit ? offset + limit : null,
|
||||
hasMore: rows.results.length > limit,
|
||||
};
|
||||
}
|
||||
|
||||
const rows = await db
|
||||
.prepare(
|
||||
`SELECT m.id, m.notebook_id, m.title, m.excerpt, m.tags_json, m.is_pinned,
|
||||
m.is_archived, m.is_deleted, m.created_at, m.updated_at, m.deleted_at, c.revision
|
||||
FROM memos m
|
||||
INNER JOIN memo_contents c ON c.memo_id = m.id
|
||||
WHERE m.is_deleted = 0
|
||||
AND (? IS NULL OR m.notebook_id = ?)
|
||||
ORDER BY m.updated_at DESC, m.id ASC
|
||||
LIMIT ? OFFSET ?`
|
||||
)
|
||||
.bind(notebookId, notebookId, pageSize, offset)
|
||||
.all<MemoSummaryRow>();
|
||||
const page = rows.results.slice(0, limit).map(mapMemoSummary);
|
||||
|
||||
return {
|
||||
memos: page,
|
||||
limit,
|
||||
offset,
|
||||
nextOffset: rows.results.length > limit ? offset + limit : null,
|
||||
hasMore: rows.results.length > limit,
|
||||
};
|
||||
};
|
||||
|
||||
const getNotebook = async (db: D1Database, id: string): Promise<Notebook | null> => {
|
||||
const row = await db
|
||||
.prepare(
|
||||
|
||||
Reference in new issue
Block a user