feat: initialize BetterNews MCP server with core functionality
- Add .gitignore to exclude node_modules, dist, and .env files - Create README.md with setup instructions and API key information - Add package.json with dependencies, scripts, and project metadata - Implement BetterNewsClient for API interactions - Set up index.ts to initialize MCP server and register tools - Create tools for managing news, comments, sources, users, moderation, and tickets - Add TypeScript configuration for project compilation
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { z } from "zod";
|
||||
import type { BetterNewsClient } from "../client.js";
|
||||
|
||||
export function registerModerationTools(server: McpServer, client: BetterNewsClient) {
|
||||
server.tool(
|
||||
"get_mod_queue",
|
||||
"Get the moderation queue of pending news items and source submissions. Requires Mod/Admin role and mod:queue scope.",
|
||||
{
|
||||
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)"),
|
||||
},
|
||||
async (args) => {
|
||||
const data = await client.get<unknown>("/api/mod/queue", args);
|
||||
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"get_mod_stats",
|
||||
"Get aggregate moderation statistics. Requires Mod/Admin role and mod:stats scope.",
|
||||
{},
|
||||
async () => {
|
||||
const data = await client.get<unknown>("/api/mod/stats");
|
||||
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"list_mod_users",
|
||||
"List all users with moderation details. Requires Admin role and mod:users scope.",
|
||||
{
|
||||
page: z.number().int().positive().optional().describe("Page number (default 1)"),
|
||||
limit: z.number().int().min(1).max(100).optional().describe("Items per page (default 20)"),
|
||||
search: z.string().optional().describe("Search by username or email"),
|
||||
role: z.enum(["User", "Moderator", "Admin"]).optional().describe("Filter by role"),
|
||||
banned: z.boolean().optional().describe("Filter by ban status"),
|
||||
},
|
||||
async (args) => {
|
||||
const data = await client.get<unknown>("/api/mod/users", args);
|
||||
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"get_mod_user",
|
||||
"Get detailed moderation view of a specific user. Requires Admin role and mod:users scope.",
|
||||
{
|
||||
userId: z.string().uuid().describe("User UUID"),
|
||||
},
|
||||
async ({ userId }) => {
|
||||
const data = await client.get<unknown>(`/api/mod/users/${userId}`);
|
||||
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"change_user_role",
|
||||
"Change a user's role. Requires Admin role and mod:users scope.",
|
||||
{
|
||||
userId: z.string().uuid().describe("User UUID"),
|
||||
role: z.enum(["User", "Moderator", "Admin"]).describe("New role"),
|
||||
},
|
||||
async ({ userId, role }) => {
|
||||
const data = await client.patch<unknown>(`/api/mod/users/${userId}/role`, { role });
|
||||
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"ban_user",
|
||||
"Ban a user. Requires Admin role and mod:users scope.",
|
||||
{
|
||||
userId: z.string().uuid().describe("User UUID"),
|
||||
reason: z.string().max(500).optional().describe("Ban reason"),
|
||||
bannedUntil: z.string().datetime().optional().describe("Temporary ban expiry (ISO 8601). Omit for permanent."),
|
||||
permanent: z.boolean().optional().describe("Whether the ban is permanent"),
|
||||
},
|
||||
async ({ userId, ...body }) => {
|
||||
const data = await client.post<unknown>(`/api/mod/users/${userId}/ban`, body);
|
||||
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"unban_user",
|
||||
"Lift a ban from a user. Requires Admin role and mod:users scope.",
|
||||
{
|
||||
userId: z.string().uuid().describe("User UUID"),
|
||||
},
|
||||
async ({ userId }) => {
|
||||
const data = await client.post<unknown>(`/api/mod/users/${userId}/unban`);
|
||||
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"list_all_tickets",
|
||||
"List all support tickets (mod view). Requires Mod/Admin role and mod:queue scope.",
|
||||
{
|
||||
page: z.number().int().positive().optional(),
|
||||
limit: z.number().int().min(1).max(100).optional(),
|
||||
status: z.enum(["open", "in_progress", "resolved", "closed"]).optional(),
|
||||
},
|
||||
async (args) => {
|
||||
const data = await client.get<unknown>("/api/mod/tickets", args);
|
||||
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user