Skip to content

registerTool: resource content type requires 'as any' cast when passing Resource metadata #1131

@domdomegg

Description

@domdomegg

Issue

When using registerTool() with type: "resource" in tool response content, passing a Resource object (which includes metadata like name, description, title, etc.) requires an as any cast to satisfy TypeScript's type checker.

Example

const resource = ALL_RESOURCES[resourceIndex]; // Type: Resource (includes name, description, etc.)

return {
  content: [
    {
      type: "resource" as const,
      resource: resource as any,  // ← 'as any' required
    },
  ],
};

Root Cause

The registerTool() type signature expects resource content in tool responses to be resource content (only uri, text/blob, and mimeType), not resource metadata (which includes additional fields like name, description, title, icons).

However, the type: "resource" content type in prompts (via GetPromptRequestSchema) accepts full Resource metadata objects without casting.

TypeScript Error Without Cast

Type '{ [x: string]: unknown; uri: string; name: string; _meta?: { [x: string]: unknown; } | undefined; title?: string | undefined; icons?: { ...; }[] | undefined; mimeType?: string | undefined; description?: string | undefined; }' is not assignable to type '{ [x: string]: unknown; uri: string; text: string; _meta?: { [x: string]: unknown; } | undefined; mimeType?: string | undefined; } | { [x: string]: unknown; uri: string; blob: string; _meta?: { ...; } | undefined; mimeType?: string | undefined; }'.
  Property 'blob' is missing in type '{ [x: string]: unknown; uri: string; name: string; ... }' but required in type '{ [x: string]: unknown; uri: string; blob: string; ... }'.

Workaround

Extract only content fields:

const resourceContent: { uri: string; text: string; mimeType?: string } | { uri: string; blob: string; mimeType?: string } =
  'text' in resource && resource.text
    ? { uri: resource.uri, text: resource.text as string, mimeType: resource.mimeType }
    : { uri: resource.uri, blob: resource.blob as string, mimeType: resource.mimeType };

return {
  content: [{
    type: "resource" as const,
    resource: resourceContent,
  }],
};

However, this is verbose and loses the metadata that might be useful to include in tool responses.

Questions

  1. Should tool response resource content accept full Resource metadata objects? This would match prompt behavior.
  2. Or should there be a clearer separation? Perhaps different types like ResourceContent vs ResourceMetadata.
  3. Is the as any cast acceptable for now? It works at runtime but hides potential type issues.

Context

Found while migrating the "everything" example server to use modern registerTool() APIs. See: modelcontextprotocol/servers#3017

Reproduction

See src/everything/everything.ts in the servers repo, specifically the GET_RESOURCE_REFERENCE tool around line 714.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions