Skip to main content
Glama
gavxm
by gavxm

anilist_shared_planning

Read-only

Compare two users' anime or manga planning lists to find overlapping titles and unique entries. See what both plan to watch or read.

Instructions

Find titles on both users' planning lists. Use when two users want to see what they're both planning to watch or read. Shows overlap and unique titles.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
user1YesFirst AniList username
user2YesSecond AniList username
typeNoCompare anime or manga planning listsANIME
limitNoMax entries to show (default 25, max 50)

Implementation Reference

  • Execute function for anilist_shared_planning tool. Fetches planning lists for two users from AniList, computes overlap (shared titles sorted by score), and returns a formatted summary with unique counts.
    execute: async (args) => {
      try {
        const [list1, list2] = await Promise.all([
          anilistClient.fetchList(args.user1, args.type, "PLANNING"),
          anilistClient.fetchList(args.user2, args.type, "PLANNING"),
        ]);
    
        const ids1 = new Set(list1.map((e) => e.media.id));
        const ids2 = new Set(list2.map((e) => e.media.id));
        const entryMap = new Map<number, AniListMediaListEntry>();
        for (const e of [...list1, ...list2]) entryMap.set(e.media.id, e);
    
        // Shared titles
        const sharedIds = [...ids1].filter((id) => ids2.has(id));
        const shared = sharedIds
          .map((id) => entryMap.get(id))
          .filter((e): e is AniListMediaListEntry => e !== undefined)
          .sort((a, b) => (b.media.meanScore ?? 0) - (a.media.meanScore ?? 0));
    
        const lines = [
          `# Shared Planning: ${args.user1} & ${args.user2}`,
          `${args.user1}: ${list1.length} | ${args.user2}: ${list2.length} | Overlap: ${shared.length}`,
          "",
        ];
    
        if (shared.length === 0) {
          lines.push("No titles in common on both planning lists.");
        } else {
          lines.push("Both planning to watch:");
          const show = shared.slice(0, args.limit);
          for (let i = 0; i < show.length; i++) {
            const e = show[i];
            const title = getTitle(e.media.title);
            const score = e.media.meanScore
              ? ` (${(e.media.meanScore / 10).toFixed(1)}/10)`
              : "";
            lines.push(`${i + 1}. ${title}${score}`);
          }
    
          if (shared.length > args.limit) {
            lines.push(`...and ${shared.length - args.limit} more`);
          }
        }
    
        // Unique counts
        const only1 = list1.filter((e) => !ids2.has(e.media.id)).length;
        const only2 = list2.filter((e) => !ids1.has(e.media.id)).length;
        if (only1 > 0 || only2 > 0) {
          lines.push("");
          if (only1 > 0) lines.push(`Only ${args.user1}: ${only1} titles`);
          if (only2 > 0) lines.push(`Only ${args.user2}: ${only2} titles`);
        }
    
        return lines.join("\n");
      } catch (error) {
        return throwToolError(error, "comparing planning lists");
      }
  • Registration of the 'anilist_shared_planning' tool via server.addTool() inside registerSocialTools().
    // === Shared Planning ===
    
    server.addTool({
      name: "anilist_shared_planning",
      description:
        "Find titles on both users' planning lists. " +
        "Use when two users want to see what they're both planning to watch or read. " +
        "Shows overlap and unique titles.",
      parameters: SharedPlanningInputSchema,
      annotations: {
        title: "Shared Planning",
        readOnlyHint: true,
        destructiveHint: false,
        openWorldHint: true,
      },
      execute: async (args) => {
        try {
          const [list1, list2] = await Promise.all([
            anilistClient.fetchList(args.user1, args.type, "PLANNING"),
            anilistClient.fetchList(args.user2, args.type, "PLANNING"),
          ]);
    
          const ids1 = new Set(list1.map((e) => e.media.id));
          const ids2 = new Set(list2.map((e) => e.media.id));
          const entryMap = new Map<number, AniListMediaListEntry>();
          for (const e of [...list1, ...list2]) entryMap.set(e.media.id, e);
    
          // Shared titles
          const sharedIds = [...ids1].filter((id) => ids2.has(id));
          const shared = sharedIds
            .map((id) => entryMap.get(id))
            .filter((e): e is AniListMediaListEntry => e !== undefined)
            .sort((a, b) => (b.media.meanScore ?? 0) - (a.media.meanScore ?? 0));
    
          const lines = [
            `# Shared Planning: ${args.user1} & ${args.user2}`,
            `${args.user1}: ${list1.length} | ${args.user2}: ${list2.length} | Overlap: ${shared.length}`,
            "",
          ];
    
          if (shared.length === 0) {
            lines.push("No titles in common on both planning lists.");
          } else {
            lines.push("Both planning to watch:");
            const show = shared.slice(0, args.limit);
            for (let i = 0; i < show.length; i++) {
              const e = show[i];
              const title = getTitle(e.media.title);
              const score = e.media.meanScore
                ? ` (${(e.media.meanScore / 10).toFixed(1)}/10)`
                : "";
              lines.push(`${i + 1}. ${title}${score}`);
            }
    
            if (shared.length > args.limit) {
              lines.push(`...and ${shared.length - args.limit} more`);
            }
          }
    
          // Unique counts
          const only1 = list1.filter((e) => !ids2.has(e.media.id)).length;
          const only2 = list2.filter((e) => !ids1.has(e.media.id)).length;
          if (only1 > 0 || only2 > 0) {
            lines.push("");
            if (only1 > 0) lines.push(`Only ${args.user1}: ${only1} titles`);
            if (only2 > 0) lines.push(`Only ${args.user2}: ${only2} titles`);
          }
    
          return lines.join("\n");
        } catch (error) {
          return throwToolError(error, "comparing planning lists");
        }
      },
    });
  • Zod schema SharedPlanningInputSchema defining inputs: user1, user2, type (ANIME/MANGA default ANIME), limit (1-50 default 25).
    export const SharedPlanningInputSchema = z.object({
      user1: usernameSchema.describe("First AniList username"),
      user2: usernameSchema.describe("Second AniList username"),
      type: z
        .enum(["ANIME", "MANGA"])
        .default("ANIME")
        .describe("Compare anime or manga planning lists"),
      limit: z
        .number()
        .int()
        .min(1)
        .max(50)
        .default(25)
        .describe("Max entries to show (default 25, max 50)"),
    });
  • src/index.ts:61-61 (registration)
    Registration call registerSocialTools(server) invoked from the main server setup.
    registerSocialTools(server);
  • registerSocialTools function declaration that registers this and other social tools.
    export function registerSocialTools(server: FastMCP): void {
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide readOnlyHint=true, destructiveHint=false, and openWorldHint=true. The description adds that it shows overlap and unique titles, but does not elaborate on behavioral specifics such as output format or any limitations. It does not contradict annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences, front-loaded with the main purpose, and contains no redundant or extraneous information. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with four parameters and no output schema, the description provides a clear idea of the purpose and output (overlap and unique titles). It is mostly complete but could benefit from a brief note on output format or additional details on how to interpret results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the description does not add any additional meaning beyond what the schema already provides for the four parameters (user1, user2, type, limit). Baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool finds titles on both users' planning lists, using the verb 'find' and specifying the resource as 'titles on both users' planning lists.' This distinguishes it from siblings like anilist_compare and anilist_list, which target different list types.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description includes explicit usage context: 'Use when two users want to see what they're both planning to watch or read.' It also indicates output includes overlap and unique titles, but does not mention when not to use it or provide explicit alternatives to sibling tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/gavxm/ani-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server