Files
mcp-server/src/tools/comments.ts
T
2026-06-20 19:12:27 +02:00

88 lines
3.8 KiB
TypeScript

import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import type { BetterNewsClient } from "../client.js";
import { handleTool } from "../utils.js";
export function registerCommentTools(server: McpServer, client: BetterNewsClient) {
server.tool(
"list_comments",
"List comments on a news item. Moderators also see hidden comments. Requires news:read scope.",
{
newsItemId: z.string().uuid().describe("News item UUID"),
page: z.number().int().positive().optional().describe("Page number (default 1)"),
limit: z.number().int().min(1).max(50).optional().describe("Items per page (default 20, max 50)"),
sort: z.enum(["top", "most_disliked", "new", "oldest"]).optional().describe("Sort order: top = most liked (default), most_disliked, new = newest first, oldest"),
},
handleTool(async ({ newsItemId, page, limit, sort }) => {
const data = await client.get<unknown>(`/api/news/${newsItemId}/comments`, { page, limit, sort });
return JSON.stringify(data, null, 2);
}),
);
server.tool(
"create_comment",
"Post a comment on a published news item. Requires news:comment scope.",
{
newsItemId: z.string().uuid().describe("News item UUID"),
content: z.string().min(1).max(2000).describe("Comment text"),
},
handleTool(async ({ newsItemId, content }) => {
const data = await client.post<unknown>(`/api/news/${newsItemId}/comments`, { content });
return JSON.stringify(data, null, 2);
}),
);
server.tool(
"delete_comment",
"Delete a comment. Owners can delete their own; Mod/Admin can delete any. Requires news:comment scope.",
{
newsItemId: z.string().uuid().describe("News item UUID"),
commentId: z.string().uuid().describe("Comment UUID"),
},
handleTool(async ({ newsItemId, commentId }) => {
const data = await client.delete<unknown>(`/api/news/${newsItemId}/comments/${commentId}`);
return JSON.stringify(data, null, 2);
}),
);
server.tool(
"like_comment",
"Toggle a like on a comment. Requires news:comment scope.",
{
newsItemId: z.string().uuid().describe("News item UUID"),
commentId: z.string().uuid().describe("Comment UUID"),
},
handleTool(async ({ newsItemId, commentId }) => {
const data = await client.post<unknown>(`/api/news/${newsItemId}/comments/${commentId}/like`);
return JSON.stringify(data, null, 2);
}),
);
server.tool(
"dislike_comment",
"Toggle a dislike on a comment. Requires news:comment scope.",
{
newsItemId: z.string().uuid().describe("News item UUID"),
commentId: z.string().uuid().describe("Comment UUID"),
},
handleTool(async ({ newsItemId, commentId }) => {
const data = await client.post<unknown>(`/api/news/${newsItemId}/comments/${commentId}/dislike`);
return JSON.stringify(data, null, 2);
}),
);
server.tool(
"hide_comment",
"Hide or unhide a comment. Hidden comments are invisible to regular users but still visible to Mod/Admin. Requires Mod/Admin role and news:moderate scope.",
{
newsItemId: z.string().uuid().describe("News item UUID"),
commentId: z.string().uuid().describe("Comment UUID"),
hidden: z.boolean().describe("True to hide, false to unhide"),
},
handleTool(async ({ newsItemId, commentId, hidden }) => {
const data = await client.patch<unknown>(`/api/news/${newsItemId}/comments/${commentId}`, { hidden });
return JSON.stringify(data, null, 2);
}),
);
}