koala73/worldmonitor · error · SafeWebMcpError

list_dashboard_panels accepts only variant, category, enable

Error message

list_dashboard_panels accepts only variant, category, enabled, available, cursor, and limit.

What it means

The WebMCP tool list_dashboard_panels is registered with a strict input schema (additionalProperties: false). Before executing the tool, the handler checks the exact own-key set of the arguments object via hasOnlyOwnKeys and throws SafeWebMcpError when any unrecognized key is present, keeping the tool surface minimal and predictable for MCP clients.

Solutions

  1. Remove all arguments except variant, category, enabled, available, cursor, and limit from the tool call.
  2. Check the tool's inputSchema (properties block above) for exact names and types of the six allowed keys.
  3. Refresh the MCP client's tool listing so the schema it uses matches the server's current registration.

Example fix

// before
const res = await mcp.callTool('list_dashboard_panels', { category: 'military', search: 'radar' });
// after
const res = await mcp.callTool('list_dashboard_panels', { category: 'military', limit: 20 });
Defensive patterns

Strategy: validation

Validate before calling

const ALLOWED = ['variant','category','enabled','available','cursor','limit'];
const extra = Object.keys(args ?? {}).filter(k => !ALLOWED.includes(k));
if (extra.length) throw new Error(`Unknown list_dashboard_panels args: ${extra.join(', ')}`);

Type guard

const isPanelQuery = (a: unknown): a is Record<string, string|boolean|number> =>
  a !== null && typeof a === 'object' &&
  Object.keys(a).every(k => ['variant','category','enabled','available','cursor','limit'].includes(k));

Try / catch

try {
  const res = await mcp.callTool('list_dashboard_panels', query);
} catch (e) {
  if (e instanceof Error && e.message.includes('accepts only')) {
    console.error('Bad arguments, allowed keys: variant, category, enabled, available, cursor, limit', e.message);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling list_dashboard_panels with any property outside variant, category, enabled, available, cursor, limit — e.g. misspelled names like variants, category, startCursor, page, or extra keys such as search or id.

Common situations: Developers guessing parameter names instead of reading the tool's JSON schema; clients auto-generating arguments from stale tool metadata; copying query params from other catalog tools (e.g. pagination keys named differently); SDK version drift where an older schema allowed more fields.

Understand the failure class

Background: "Invalid query parameter" / "Failed to parse value of ...": fixing bad query string parameters across APIs — this error's family across 36 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15). Data as JSON: /api/errors/806da0c9c6e53deb. Report an issue: GitHub.

Appendix: source

Thrown at src/services/webmcp.ts:2032

            description: 'Catalog cursor from the previous page nextCursor.',
            minLength: 1,
            maxLength: DASHBOARD_PANEL_ID_MAX_CHARS,
            pattern: DASHBOARD_PANEL_ID_PATTERN,
          },
          limit: {
            type: 'integer',
            description: 'Maximum panels in this page, from 1 to 8.',
            minimum: 1,
            maximum: DASHBOARD_PANEL_CATALOG_MAX_LIMIT,
            default: DASHBOARD_PANEL_CATALOG_DEFAULT_LIMIT,
          },
        },
        additionalProperties: false,
      },
      annotations: { readOnlyHint: true },
      execute: withBindings(WEBMCP_SPA_TOOL.listDashboardPanels, async (args, extra) => {
        if (!hasOnlyOwnKeys(args, ['variant', 'category', 'enabled', 'available', 'cursor', 'limit'])) {
          throw new SafeWebMcpError(
            'list_dashboard_panels accepts only variant, category, enabled, available, cursor, and limit.',
            'validation',
          );
        }
        const query: DashboardPanelCatalogQuery = {};
        if (args.variant !== undefined) query.variant = args.variant as string;
        if (args.category !== undefined) query.category = args.category as string;
        if (args.enabled !== undefined) query.enabled = args.enabled as boolean;
        if (args.available !== undefined) query.available = args.available as boolean;
        if (args.cursor !== undefined) query.cursor = args.cursor as string;
        if (args.limit !== undefined) query.limit = args.limit as number;
        return boundDashboardPanelCatalog(await app.listDashboardPanels(query, extra));
      }, trackEvent, {
        successMetadata: (_args, value) => {
          const result = value as DashboardPanelCatalogPage;
          return {
            resultCount: result.panels.length,
            hasMore: result.hasMore === true,

View on GitHub (pinned to 7d06c8633d)