Skip to main content
Glama

Duck Council

duck_council
Read-only

Submit a question to a council of AI ducks and receive multiple perspectives for debugging or problem-solving.

Instructions

Get responses from all configured ducks (like a panel discussion)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
promptYesThe question for the duck council
modelNoSpecific model to use for all ducks (optional)
imagesNoOptional images to include with the prompt (for vision-capable models)

Implementation Reference

  • Main handler for the duck_council tool. Gathers responses from all configured providers (ducks), formats them into a panel discussion, and returns the result. Uses duckCouncil() or compareDucksWithProgress() depending on whether a ProgressReporter is provided.
    export async function duckCouncilTool(
      providerManager: ProviderManager,
      args: Record<string, unknown>,
      progress?: ProgressReporter
    ) {
      const { prompt, model, images } = args as {
        prompt?: string;
        model?: string;
        images?: ImageInput[];
      };
    
      if (!prompt) {
        throw new Error('Prompt is required for the duck council');
      }
    
      logger.info('Convening the duck council...');
    
      // Get all available ducks
      const allProviders = providerManager.getProviderNames();
    
      if (allProviders.length === 0) {
        throw new Error('No ducks available for the council!');
      }
    
      const content = buildContent(prompt, images);
    
      // Get responses from all ducks, reporting progress as each completes
      const responses = progress
        ? await providerManager.compareDucksWithProgress(
            content,
            undefined,
            { model },
            (providerName, completed, total) => {
              void progress.report(
                completed,
                total,
                `${providerName} responded (${completed}/${total})`
              );
            }
          )
        : await providerManager.duckCouncil(content, { model });
    
      // Build council response with a panel discussion format
      let response = `${duckArt.panel}\n\n`;
      response += `πŸŽ™οΈ **Duck Council Topic:** "${prompt}"\n`;
      response += `πŸ‘₯ **${allProviders.length} ducks in attendance**\n\n`;
      response += `═══════════════════════════════════════\n\n`;
    
      // Present each duck's perspective
      for (let i = 0; i < responses.length; i++) {
        const duckResponse = responses[i];
        const duckNumber = i + 1;
    
        response += `**Duck #${duckNumber}: ${duckResponse.nickname}**\n`;
        response += `─────────────────────────────────────\n`;
    
        if (duckResponse.content.startsWith('Error:')) {
          response += `πŸ¦†πŸ’¬ *[Duck had to leave early: ${duckResponse.content}]*\n`;
        } else {
          response += `πŸ¦†πŸ’¬ "${duckResponse.content}"\n`;
    
          // Add metadata in a subtle way
          response += `\n`;
          response += `*[${duckResponse.model}`;
          if (duckResponse.latency > 0) {
            response += ` β€’ ${duckResponse.latency}ms`;
          }
          if (duckResponse.usage) {
            response += ` β€’ ${duckResponse.usage.total_tokens} tokens`;
          }
          response += `]*\n`;
        }
    
        response += `\n`;
      }
    
      // Add council summary
      const successCount = responses.filter((r) => !r.content.startsWith('Error:')).length;
      response += `═══════════════════════════════════════\n`;
      response += `πŸ›οΈ **Council Summary**\n`;
      response += `β€’ ${successCount}/${responses.length} ducks provided their wisdom\n`;
    
      if (successCount === responses.length) {
        response += `β€’ ${getRandomDuckMessage('success')}\n`;
      } else if (successCount > 0) {
        response += `β€’ Partial council - some ducks were unavailable\n`;
      } else {
        response += `β€’ ${getRandomDuckMessage('error')}\n`;
      }
    
      logger.info(`Duck council completed: ${successCount}/${responses.length} responses`);
    
      return {
        content: [
          {
            type: 'text',
            text: response,
          },
        ],
      };
    }
  • Alternative handler when MCP is enabled. Calls enhancedProviderManager.duckCouncilWithMCP() or compareDucksWithProgressMCP() and formats results with header using formatEnhancedDuckResponse().
    private async handleDuckCouncilWithMCP(
      args: Record<string, unknown>,
      progress?: import('./services/progress.js').ProgressReporter
    ) {
      if (!this.enhancedProviderManager) {
        throw new Error('Enhanced provider manager not available');
      }
    
      const { prompt, model, images } = args as {
        prompt: string;
        model?: string;
        images?: ImageInput[];
      };
    
      const content = buildContent(prompt, images);
      const responses = progress
        ? await this.enhancedProviderManager.compareDucksWithProgressMCP(
            content,
            undefined,
            { model },
            (providerName, completed, total) => {
              void progress.report(
                completed,
                total,
                `${providerName} responded (${completed}/${total})`
              );
            }
          )
        : await this.enhancedProviderManager.duckCouncilWithMCP(content, { model });
    
      const header = 'πŸ¦† Duck Council in Session πŸ¦†\n=============================';
      const formattedResponse = responses
        .map((response) => this.formatEnhancedDuckResponse(response))
        .join('\n\n═══════════════════════════════════════\n\n');
    
      return {
        content: [
          {
            type: 'text' as const,
            text: `${header}\n\n${formattedResponse}`,
          },
        ],
      };
  • Registration of duck_council tool with its input schema (prompt, optional model, optional images array with ImageInputSchema). Registered via this.server.registerTool().
    this.server.registerTool(
      'duck_council',
      {
        title: 'Duck Council',
        description: 'Get responses from all configured ducks (like a panel discussion)',
        inputSchema: {
          prompt: z.string().describe('The question for the duck council'),
          model: z.string().optional().describe('Specific model to use for all ducks (optional)'),
          images: z
            .array(ImageInputSchema)
            .optional()
            .describe('Optional images to include with the prompt (for vision-capable models)'),
        },
        annotations: {
          readOnlyHint: true,
          openWorldHint: true,
        },
      },
  • src/server.ts:470-507 (registration)
    Registration block for duck_council tool. Dispatches to either handleDuckCouncilWithMCP() or duckCouncilTool() depending on whether MCP is enabled.
    // duck_council
    this.server.registerTool(
      'duck_council',
      {
        title: 'Duck Council',
        description: 'Get responses from all configured ducks (like a panel discussion)',
        inputSchema: {
          prompt: z.string().describe('The question for the duck council'),
          model: z.string().optional().describe('Specific model to use for all ducks (optional)'),
          images: z
            .array(ImageInputSchema)
            .optional()
            .describe('Optional images to include with the prompt (for vision-capable models)'),
        },
        annotations: {
          readOnlyHint: true,
          openWorldHint: true,
        },
      },
      async (args, extra) => {
        try {
          const progress = createProgressReporter(
            extra._meta?.progressToken,
            extra.sendNotification
          );
          if (this.mcpEnabled && this.enhancedProviderManager) {
            return this.toolResult(
              await this.handleDuckCouncilWithMCP(args as Record<string, unknown>, progress)
            );
          }
          return this.toolResult(
            await duckCouncilTool(this.providerManager, args as Record<string, unknown>, progress)
          );
        } catch (error) {
          return this.toolErrorResult(error);
        }
      }
    );
  • Base duckCouncil() method in ProviderManager - delegates to compareDucks() which queries all providers in parallel.
    async duckCouncil(
      prompt: MessageContent,
      options?: Partial<ChatOptions>
    ): Promise<DuckResponse[]> {
      return this.compareDucks(prompt, undefined, options);
    }
Behavior3/5

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

Annotations already declare readOnlyHint=true and openWorldHint=true. The description adds the panel discussion analogy but no additional behavioral traits beyond that. It does not disclose any side effects or expectations.

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 a single sentence (9 words) that is front-loaded with the verb and resource. Every word earns its place; there is no waste.

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

Completeness2/5

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

With no output schema, the description should hint at the return format (e.g., combined responses, ordering) but does not. It also fails to explicitly state that all ducks respond simultaneously or how the responses are presented. Gaps exist for a multi-duck tool.

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 coverage is 100% (all three parameters have descriptions). The description does not add meaning beyond the schemaβ€”no mention of prompt format, model purpose, or image usage.

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 'Get responses from all configured ducks', specifying both the verb and the resource. It distinguishes from siblings like 'ask_duck' (single duck) and 'duck_debate' (back-and-forth) by emphasizing a collective panel discussion.

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

Usage Guidelines3/5

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

The 'panel discussion' analogy implies usage for collective opinions, but no explicit when-to-use or when-not-to-use guidance is provided. It does not list alternatives or prerequisites.

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/nesquikm/mcp-rubber-duck'

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