anilist_feed
Retrieve recent activity from an AniList user's feed, including text posts and anime/manga list updates. Filter by activity type and paginate results.
Instructions
Get recent activity from a user's AniList feed. Shows text posts and list updates (anime/manga status changes). Returns numbered entries with author, date, and content. Supports pagination and type filtering.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| username | No | AniList username. Falls back to configured default if not provided. | |
| type | No | Filter by activity type | ALL |
| limit | No | Number of activities to return (default 10, max 25) | |
| page | No | Page number for pagination (default 1) |
Implementation Reference
- src/tools/social.ts:49-114 (registration)Registration of the 'anilist_feed' tool via server.addTool() in the registerSocialTools function, including its name, description, parameters (FeedInputSchema), annotations, and the execute handler.
export function registerSocialTools(server: FastMCP): void { // === Activity Feed === server.addTool({ name: "anilist_feed", description: "Get recent activity from a user's AniList feed. " + "Shows text posts and list updates (anime/manga status changes). " + "Returns numbered entries with author, date, and content. Supports pagination and type filtering.", parameters: FeedInputSchema, annotations: { title: "Activity Feed", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, execute: async (args) => { try { const username = getDefaultUsername(args.username); // Resolve username to numeric ID for the activity query const userData = await anilistClient.query<UserStatsResponse>( USER_STATS_QUERY, { name: username }, { cache: "stats" }, ); const userId = userData.User.id; const variables: Record<string, unknown> = { userId, page: args.page, perPage: args.limit, }; if (args.type !== "ALL") variables.type = args.type; const data = await anilistClient.query<ActivityFeedResponse>( ACTIVITY_FEED_QUERY, variables, { cache: "search" }, ); const { activities, pageInfo } = data.Page; if (!activities.length) { return `No recent activity for ${username}.`; } const header = `Activity feed for ${username}`; const lines = activities.map((a, i) => formatActivity(a, i + 1)); const footer = paginationFooter( args.page, args.limit, pageInfo.total, pageInfo.hasNextPage, ); return ( [header, "", ...lines].join("\n") + (footer ? `\n\n${footer}` : "") ); } catch (error) { return throwToolError(error, "fetching activity feed"); } }, }); - src/tools/social.ts:66-113 (handler)The execute handler for anilist_feed: resolves username to user ID via an AniList query, fetches activity feed with pagination/type filtering, formats results using formatActivity(), and returns a formatted string with header, entries, and pagination footer.
execute: async (args) => { try { const username = getDefaultUsername(args.username); // Resolve username to numeric ID for the activity query const userData = await anilistClient.query<UserStatsResponse>( USER_STATS_QUERY, { name: username }, { cache: "stats" }, ); const userId = userData.User.id; const variables: Record<string, unknown> = { userId, page: args.page, perPage: args.limit, }; if (args.type !== "ALL") variables.type = args.type; const data = await anilistClient.query<ActivityFeedResponse>( ACTIVITY_FEED_QUERY, variables, { cache: "search" }, ); const { activities, pageInfo } = data.Page; if (!activities.length) { return `No recent activity for ${username}.`; } const header = `Activity feed for ${username}`; const lines = activities.map((a, i) => formatActivity(a, i + 1)); const footer = paginationFooter( args.page, args.limit, pageInfo.total, pageInfo.hasNextPage, ); return ( [header, "", ...lines].join("\n") + (footer ? `\n\n${footer}` : "") ); } catch (error) { return throwToolError(error, "fetching activity feed"); } }, - src/schemas.ts:844-862 (schema)FeedInputSchema defines the input schema for anilist_feed: optional username, activity type filter (TEXT/ANIME_LIST/MANGA_LIST/ALL), limit (1-25, default 10), and page number.
export const FeedInputSchema = z.object({ username: usernameSchema .optional() .describe( "AniList username. Falls back to configured default if not provided.", ), type: z .enum(["TEXT", "ANIME_LIST", "MANGA_LIST", "ALL"]) .default("ALL") .describe("Filter by activity type"), limit: z .number() .int() .min(1) .max(25) .default(10) .describe("Number of activities to return (default 10, max 25)"), page: pageParam, }); - src/tools/social.ts:714-732 (helper)formatActivity() helper that formats a single activity entry for display. Handles TextActivity (truncates text to 200 chars) and ListActivity (shows status/progress/media title).
function formatActivity(activity: Activity, index: number): string { const date = new Date(activity.createdAt * 1000).toLocaleDateString("en-US", { month: "short", day: "numeric", }); if (activity.__typename === "TextActivity") { const text = activity.text.length > 200 ? activity.text.slice(0, 200) + "..." : activity.text; return `${index}. ${activity.user.name} posted (${date}):\n ${text}`; } // List activity const title = getTitle(activity.media.title); const progress = activity.progress ? ` ${activity.progress}` : ""; return `${index}. ${activity.user.name} ${activity.status}${progress} ${title} (${date})`; } - src/api/queries.ts:655-683 (helper)ACTIVITY_FEED_QUERY GraphQL query used by the tool to fetch activity feed from AniList API, supporting TextActivity and ListActivity types with pagination.
export const ACTIVITY_FEED_QUERY = ` query ActivityFeed($userId: Int, $type: ActivityType, $page: Int, $perPage: Int) { Page(page: $page, perPage: $perPage) { pageInfo { total currentPage hasNextPage } activities(userId: $userId, type: $type, sort: ID_DESC) { ... on TextActivity { __typename id text createdAt user { name } } ... on ListActivity { __typename id status progress createdAt user { name } media { id title { romaji english native } type } } } } } `;