CLAUDE.md — BetterNews MCP Server
Stdio MCP server (@modelcontextprotocol/sdk) that exposes the BetterNews API as tools.
Auth: BETTERNEWS_API_KEY env var, sent as Authorization: Bearer on every request.
Source Files
| File |
Purpose |
src/index.ts |
Entry point. Imports all register functions and calls them against server and client. |
src/client.ts |
BetterNewsClient — get/post/patch/delete wrappers with auth header and error handling. |
src/utils.ts |
handleTool(fn) — wraps a handler, catches errors, returns { content: [{ type:"text", text }] }. |
src/tools/news.ts |
24 news tools |
src/tools/comments.ts |
6 comment tools |
src/tools/sources.ts |
7 source tools |
src/tools/user.ts |
9 user/profile tools |
src/tools/moderation.ts |
14 moderation tools |
src/tools/tickets.ts |
6 support ticket tools |
src/tools/blog.ts |
7 blog tools |
src/tools/api-keys.ts |
4 API key tools |
src/tools/sessions.ts |
3 session tools |
src/tools/discussions.ts |
13 discussion channel tools |
src/tools/polls.ts |
13 poll tools |
Complete Tool Index
Format: tool_name → file → METHOD /api/route → minimum auth
News — tools/news.ts
| Tool |
Route |
Auth |
get_top_news |
GET /api/news/top |
public |
list_news |
GET /api/news |
optional |
get_news_item |
GET /api/news/{id} |
optional |
search_news |
GET /api/news/search |
optional |
get_similar_news |
GET /api/news/{id}/similar |
optional |
list_topics |
GET /api/news/topics |
public |
get_news_item_stats |
GET /api/news/{id}/stats |
owner or Mod/Admin |
check_duplicates |
GET /api/news/{id}/check-duplicates |
news:moderate |
create_news_item |
POST /api/news |
news:write |
update_news_item |
PATCH /api/news/{id} |
news:write |
delete_news_item |
DELETE /api/news/{id} |
owner or Admin |
submit_news_item |
POST /api/news/{id}/submit |
news:write (owner) |
review_news_item |
POST /api/news/{id}/review |
news:moderate |
like_news_item |
POST /api/news/{id}/like |
news:write |
dislike_news_item |
POST /api/news/{id}/dislike |
news:write |
bookmark_news_item |
POST /api/news/{id}/bookmark |
bookmarks:write |
publish_news_item |
POST /api/news/{id}/publish |
news:moderate |
unpublish_news_item |
POST /api/news/{id}/unpublish |
news:moderate |
republish_news_item |
POST /api/news/{id}/republish |
news:moderate |
archive_news_item |
POST /api/news/{id}/archive |
news:moderate (owner) |
direct_publish_news_item |
POST /api/news/{id}/direct-publish |
news:moderate |
direct_create_publish_news_item |
POST /api/news/direct |
news:moderate |
get_deep_dive |
GET /api/news/{id}/deep-dive |
optional |
regenerate_deep_dive |
POST /api/news/{id}/deep-dive/regenerate |
news:moderate Mod/Admin |
| Tool |
Route |
Auth |
list_comments |
GET /api/news/{id}/comments |
optional; sort: top/most_disliked/new/oldest |
create_comment |
POST /api/news/{id}/comments |
news:comment + email verified |
delete_comment |
DELETE /api/news/{id}/comments/{commentId} |
owner or news:comment Mod/Admin |
like_comment |
POST /api/news/{id}/comments/{commentId}/like |
news:comment |
dislike_comment |
POST /api/news/{id}/comments/{commentId}/dislike |
news:comment |
hide_comment |
PATCH /api/news/{id}/comments/{commentId} |
news:moderate Mod/Admin |
Sources — tools/sources.ts
| Tool |
Route |
Auth |
list_sources |
GET /api/sources |
sources:read |
get_source |
GET /api/sources/{id} |
sources:read |
create_source |
POST /api/sources |
sources:write Mod/Admin |
update_source |
PATCH /api/sources/{id} |
sources:write Mod/Admin |
list_source_submissions |
GET /api/news/{id}/sources |
news:read |
submit_source |
POST /api/news/{id}/sources |
news:write |
review_source_submission |
POST /api/news/{id}/sources/{submissionId}/review |
news:moderate |
User — tools/user.ts
| Tool |
Route |
Auth |
get_current_user |
GET /api/user/whoami |
user:profile |
get_user_profile |
GET /api/users/{username} |
public |
list_my_news |
GET /api/user/news |
user:profile |
list_my_source_submissions |
GET /api/user/source-submissions |
user:profile |
list_bookmarks |
GET /api/user/bookmarks |
bookmarks:write |
update_profile |
PATCH /api/user/profile |
user:profile |
update_privacy_settings |
PATCH /api/user/privacy |
user:profile |
list_my_polls |
GET /api/user/polls |
user:profile |
list_my_poll_votes |
GET /api/user/poll-votes |
user:profile |
Moderation — tools/moderation.ts
| Tool |
Route |
Auth |
get_mod_queue |
GET /api/mod/queue |
mod:queue Mod/Admin |
get_mod_stats |
GET /api/mod/stats |
mod:stats Mod/Admin |
list_mod_users |
GET /api/mod/users |
mod:users Admin |
get_mod_user |
GET /api/mod/users/{userId} |
mod:users Admin |
change_user_role |
PATCH /api/mod/users/{userId}/role |
mod:users Admin |
ban_user |
POST /api/mod/users/{userId}/ban |
mod:users Admin |
unban_user |
DELETE /api/mod/users/{userId}/ban |
mod:users Admin |
shadowban_user |
PATCH /api/mod/users/{userId}/shadowban |
mod:users Admin |
create_mod_user |
POST /api/mod/users |
mod:users Admin |
list_all_tickets |
GET /api/mod/tickets |
mod:queue Mod/Admin |
list_mod_comments |
GET /api/mod/comments |
mod:queue Mod/Admin |
list_mod_user_api_keys |
GET /api/mod/users/{userId}/api-keys |
mod:users Admin |
update_mod_user_api_key |
PATCH /api/mod/users/{userId}/api-keys/{keyId} |
mod:users Admin |
revoke_mod_user_api_key |
DELETE /api/mod/users/{userId}/api-keys/{keyId} |
mod:users Admin |
Tickets — tools/tickets.ts
| Tool |
Route |
Auth |
list_my_tickets |
GET /api/tickets |
tickets:read |
get_ticket |
GET /api/tickets/{id} |
tickets:read owner or Mod/Admin |
create_ticket |
POST /api/tickets |
tickets:write |
reply_to_ticket |
POST /api/tickets/{id}/reply |
tickets:write |
create_ticket_message |
POST /api/tickets/{id}/messages |
tickets:write / tickets:moderate |
update_ticket_status |
PATCH /api/tickets/{id}/status |
tickets:moderate Mod/Admin |
Blog — tools/blog.ts
| Tool |
Route |
Auth |
list_blog_posts |
GET /api/blog |
optional; Mod/Admin see drafts |
get_blog_post |
GET /api/blog/{slug} |
optional; Mod/Admin see drafts |
create_blog_post |
POST /api/blog |
blog:write Mod/Admin |
update_blog_post |
PATCH /api/blog/{id} |
blog:write Mod/Admin |
delete_blog_post |
DELETE /api/blog/{id} |
blog:write Mod/Admin |
publish_blog_post |
POST /api/blog/{id}/publish |
blog:write Mod/Admin |
unpublish_blog_post |
POST /api/blog/{id}/unpublish |
blog:write Mod/Admin |
API Keys — tools/api-keys.ts
| Tool |
Route |
Auth |
list_api_keys |
GET /api/user/api-keys |
user:apikeys |
create_api_key |
POST /api/user/api-keys |
cookie session only |
update_api_key |
PATCH /api/user/api-keys/{keyId} |
user:apikeys |
revoke_api_key |
DELETE /api/user/api-keys/{keyId} |
user:apikeys |
Sessions — tools/sessions.ts
| Tool |
Route |
Auth |
list_sessions |
GET /api/user/sessions |
cookie session only |
revoke_all_sessions |
DELETE /api/user/sessions |
cookie session only |
revoke_session |
DELETE /api/user/sessions/{sessionId} |
cookie session only |
Discussions — tools/discussions.ts
| Tool |
Route |
Auth |
list_discussions |
GET /api/discussions |
optional; Mod/Admin see all statuses |
propose_discussion |
POST /api/discussions |
User+ (3/hour rate limit) |
get_discussion |
GET /api/discussions/{id} |
optional; Mod/Admin see pending/rejected |
update_discussion |
PATCH /api/discussions/{id} |
Mod/Admin |
delete_discussion |
DELETE /api/discussions/{id} |
Admin |
list_discussion_messages |
GET /api/discussions/{id}/messages |
optional; cursor via before ISO timestamp |
delete_discussion_message |
DELETE /api/discussions/{id}/messages/{messageId} |
owner or Mod/Admin |
pin_discussion_message |
POST /api/discussions/{id}/messages/{messageId}/pin |
Mod/Admin |
list_discussion_bans |
GET /api/discussions/{id}/bans |
Mod/Admin |
ban_discussion_user |
POST /api/discussions/{id}/bans |
Mod/Admin |
unban_discussion_user |
DELETE /api/discussions/{id}/bans/{userId} |
Mod/Admin |
list_mod_discussions |
GET /api/mod/discussions |
Mod/Admin |
review_discussion |
POST /api/mod/discussions/{id}/review |
Mod/Admin |
Polls — tools/polls.ts
| Tool |
Route |
Auth |
list_polls |
GET /api/polls |
optional; non-published statuses require Mod/Admin |
create_poll |
POST /api/polls |
polls:write + email verified |
get_poll |
GET /api/polls/{id} |
optional; non-published only for author or Mod/Admin |
update_poll |
PATCH /api/polls/{id} |
polls:write (owner) or polls:moderate (Mod/Admin) |
delete_poll |
DELETE /api/polls/{id} |
polls:write (owner pending_review only) or Mod/Admin |
vote_poll |
POST /api/polls/{id}/vote |
polls:write |
like_poll |
POST /api/polls/{id}/like |
polls:write |
dislike_poll |
POST /api/polls/{id}/dislike |
polls:write |
review_poll |
POST /api/polls/{id}/review |
polls:moderate Mod/Admin |
list_poll_comments |
GET /api/polls/{id}/comments |
optional |
create_poll_comment |
POST /api/polls/{id}/comments |
polls:comment + email verified |
hide_poll_comment |
PATCH /api/polls/{id}/comments/{commentId} |
polls:moderate Mod/Admin |
delete_poll_comment |
DELETE /api/polls/{id}/comments/{commentId} |
owner or polls:comment Mod/Admin |
Adding a New Tool
- Find or create the right
src/tools/<category>.ts file.
- Add a
server.tool(name, description, zodSchema, handleTool(async (args) => { ... })) call inside the register function.
- If creating a new file, export
function registerXxxTools(server, client) and call it in src/index.ts.
- Run
pnpm build — a clean exit means no type errors.
Pattern (copy from any existing tool):
Query params: pass as second arg to client.get(url, params) — undefined values are stripped.
Body params: pass as second arg to client.post/patch(url, body).
Build & Validate
No pre-push script; just pnpm build is the gate.