Hosted remote MCP server for vas3k.club, running on Cloudflare Workers. Provides read-only and read+write MCP endpoints authenticated via OAuth 2.1 against vas3k.club.
vas3k-mcp demonstrates strong definition quality overall. Tool naming is consistently verb-focused (get_, list_, search_, upvote_, retract_, toggle_, subscribe_, mute_). Descriptions are clear and actionable, averaging ~120 characters with context about WHEN to use each tool. All 24 tools have full JSON Schema input definitions with typed parameters and descriptions. Output schemas are documented in descriptions or implicitly clear from tool purpose. Risk annotations (READ_ONLY, WRITE, REVERSIBLE) provide clarity on side effects. However, several tools have descriptions under the 10-char minimum for optimal LLM parsing, and some parameter constraints (e.g., prefix length in search_users: '3-15 characters') appear only in text descriptions rather than JSON Schema constraints. Error handling is implicit (not documented in tool definitions), and dependency relationships between tools (e.g., get_feed returns post slugs that feed into get_post) are not explicitly documented. Tool composition is excellent, tools are highly granular and well-chainable.
Add this post to the user's bookmarks, or remove it if it was already bookmarked. Same call performs both — there is no separate `unbookmark_post`. Response.status is 'created' if it's now bookmarked, 'deleted' if the bookmark was just removed.
Search for a user by their linked Telegram account. Requires the numeric Telegram id (not the @username). Useful when you've extracted a numeric id from a Telegram profile URL or message metadata.
Fetch a page of the global feed or a filtered feed. Supports sorting by activity, newest, or upvote window (top_week, top_month, top_year, hot). Default ordering is 'activity' (most recent comments first). Posts are paginated; the response includes a `count` field and hints about whether the next page exists.
Profile of the authenticated member
Fetch a single post by slug and type. Returns the full post object: title, body, markdown, upvote count, author, tags, comments, etc. post_type must match the post's actual type (calling /question/<slug> on a post of type 'project' returns 302).
Parameter constraints specified only in descriptions, not in JSON Schema. E.g., search_users prefix '3-15 characters' and search_tags group enum are documented in text but lack formal min/max or pattern validation in the schema.
No explicit error handling documentation in tool definitions. Tools lack descriptions of failure modes (e.g., what happens if post_type doesn't match post's actual type, or if a user is not found). LLM must infer recovery paths.
Tool dependency documentation missing. E.g., get_feed returns post_slug but does not explicitly state this slug feeds into get_post and get_post_markdown. Tool chains require agent to infer relationships.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | A | 89 | 2025-06-18+ | v2 |
Fetch the raw markdown body of a post (not the rendered HTML, not the summary). Useful for agents that want to parse or re-render the markup themselves.
Fetch a single user profile by slug. Returns the public-facing card: name, bio, tags, badges, joindate.
Fetch achievements for a user. Achievements are platform milestones (first post, first vote, etc.) or role-based (moderator, god).
Fetch badges awarded to a user by other members. Each badge has a count (how many times given) and the list of givers.
Fetch the profile tags (self-declared interests) of a user. Tags are grouped by category (tech, hobbies, personal, etc.).
Fetch top-level comments on a post. Nested replies are not returned; the response includes a `replies` count for each top-level comment. If you need replies, make a separate call for each comment's UUID.
Mute a room (hide it from the user's feed). The user is still subscribed; they can unmute to see posts again. Response.status='created' = muted, 'deleted' = unmuted.
Retract the user's upvote on a comment. Idempotent — `success:false` if there was no vote to retract.
Retract the user's upvote on a post. Idempotent — `success:false` in the response means there was no vote to retract; `success:true` means a vote was removed and the upvote count decremented.
Search for profile tags by name prefix. Returns matching tags grouped by category (club, tech, hobbies, personal, collectible, other). Tags also include a membership count and usage frequency.
Search for users by name prefix. Returns a paginated list of matching members. Prefix must be 3–15 characters; shorter queries are rejected.
Subscribe to a room (add it to the user's feed). Idempotent if already subscribed.
For posts of type `event`: mark the authenticated user as participating, or unmark them if already RSVPed. Also auto-subscribes them to the event's comments. Response.status='created' = now attending. Calling on a non-event post may fail.
Send a friend request to another user, or revoke a prior request if it's still pending. Response.status='created' = request sent, 'deleted' = request cancelled.
Subscribe to email + Telegram notifications for new top-level comments on this post, or unsubscribe if already subscribed. Response.status='created' means now subscribed.
Add or remove a profile tag from the authenticated user's profile. Response.status='created' = tag added, 'deleted' = tag removed.
Upvote a comment by its UUID. Idempotent (re-calls don't double-count). Get the comment_id from `list_post_comments`.
Upvote a post. Idempotent — calling twice does NOT downvote (use retract_post_vote for that). Returns the new total upvote count plus the upvote timestamp.
Prompt template for guided 'summarize this week's top posts and activity' runs. Automatically discovers the week's top posts by trending (the `hot` ordering).
weekly_digest tool description is vague ('Prompt template for guided...') and does not clarify that it is a PROMPT tool, not a data-fetching tool. Description lacks actionable context.
toggle_friend description does not explain the semantics of toggling a pending request vs. accepting/rejecting an existing request. LLM cannot determine if the tool handles all relationship states.