Files
mcp-server/src/tools/api-keys.ts
T
Space-Banane 8083b0b8a8
CI / Build & Test (push) Successful in 17s
fix: remove stale legal:write from api-keys scope list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 23:53:58 +02:00

98 lines
3.3 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";
const SCOPES = [
"news:read",
"news:write",
"news:moderate",
"news:comment",
"sources:read",
"sources:write",
"bookmarks:write",
"tickets:read",
"tickets:write",
"tickets:moderate",
"mod:queue",
"mod:stats",
"mod:users",
"blog:write",
"user:profile",
"user:apikeys",
"privacy:read",
"files:write",
] as const;
export function registerApiKeyTools(server: McpServer, client: BetterNewsClient) {
server.tool(
"list_api_keys",
"List API keys for the authenticated user. Requires user:apikeys scope.",
{},
handleTool(async () => {
const data = await client.get<unknown>("/api/user/api-keys");
return JSON.stringify(data, null, 2);
}),
);
server.tool(
"create_api_key",
"Create a new API key. The raw key value is returned only once. Requires cookie-based session (not another API key) and user:apikeys scope.",
{
name: z.string().min(1).max(50).describe("Human-readable name for this key"),
scopes: z
.array(z.enum(SCOPES))
.optional()
.describe("Permission scopes. Empty array = full access (inherits your role)."),
expiresAt: z
.string()
.datetime()
.nullable()
.optional()
.describe("Expiry date (ISO 8601). Null = never expires."),
},
handleTool(async (body) => {
const data = await client.post<unknown>("/api/user/api-keys", body);
return JSON.stringify(data, null, 2);
}),
);
server.tool(
"update_api_key",
"Update an API key's name, scopes, or expiry. Requires user:apikeys scope.",
{
keyId: z.string().uuid().describe("API key UUID"),
name: z.string().min(1).max(50).optional().describe("New name"),
scopes: z.array(z.enum(SCOPES)).optional().describe("New scopes list"),
expiresAt: z
.string()
.datetime()
.nullable()
.optional()
.describe("New expiry date (ISO 8601). Null = never expires."),
},
handleTool(async ({ keyId, ...body }) => {
const data = await client.patch<unknown>(`/api/user/api-keys/${keyId}`, body);
return JSON.stringify(data, null, 2);
}),
);
server.tool(
"revoke_api_key",
"Revoke or permanently delete an API key. Pass hardDelete=true to permanently remove it; otherwise it is soft-revoked and stays visible in the list. Requires user:apikeys scope.",
{
keyId: z.string().uuid().describe("API key UUID"),
hardDelete: z
.boolean()
.optional()
.describe("True to permanently delete; false (default) to soft-revoke"),
},
handleTool(async ({ keyId, hardDelete }) => {
const data = await client.delete<unknown>(
`/api/user/api-keys/${keyId}${hardDelete ? "?hardDelete=true" : ""}`,
);
return JSON.stringify(data, null, 2);
}),
);
}