Skip to content

PATTERN Cited by 1 source

Expose resource as tool for agent discoverability

Definition

Expose resource as tool for agent discoverability is the MCP- server integration pattern of registering a companion tool that returns a resource_link or resource content type pointing to an already-registered MCP resource — specifically to work around the fact that not every MCP client implements direct resource discovery via resources/list.

The resource is still registered as a first-class MCP resource. The tool is a discovery handle layered on top so clients that only enumerate tools can still reach the resource (Source: sources/2026-01-08-wix-mcp-resources-all-you-need-to-know).

Why this pattern exists

Verbatim from the Wix tutorial:

Not all AI agents know how to discover and use resources directly. However, all agents understand tools. By exposing resource link as a tool, you:

  • Make resources discoverable in all AI agent flows through tool listing
  • Provide explicit resource access patterns
  • Allow parameter validation before resource access

In practice this means: resource discovery is fragmented across MCP clients, but tool discovery is universal. A tool-wrapper brings the resource into every client's reach without forcing server authors to drop the resource primitive (and its caching / update-notification properties).

Implementation: static resource

server.registerResource(name, uri, { description, mimeType }, handler);

server.tool(
  `${resourceName}-tool`,         // tool name (can differ from resource name)
  description,                    // tool description — THIS is what most clients surface
  {},                             // no params for a static resource
  async () => ({
    content: [{
      type: 'resource_link',      // ← client treats this as a fetchable resource
      name,
      uri,                        // MUST match the resource's registered URI
      mimeType: 'text/plain',
    }],
  }),
);

Implementation: template resource

server.registerResource(name, resourceTemplate, { description, mimeType }, handler);

server.tool(
  `${name}-tool`,
  description,
  {
    userId: z.string().describe('User ID to fetch profile for'),
  },
  async ({ userId }) => ({
    content: [{
      type: 'resource_link',
      name,
      uri: `resource://user-profile/${userId}`,   // exact URI, no placeholders
      mimeType: 'application/json',
    }],
  }),
);

Two implementation invariants from Wix:

  1. The tool's description is the main discovery mechanism. Verbatim: "description that you provide when registering a resource might be ignored by some clients, this tool description is the main mechanism for clients to find your resource." Write it carefully.
  2. The tool returns an exact URI, never a template. Verbatim: "in tool response return exact URI without placeholders. It means that when client receives response with resource_link type it cannot know if it's resource or template. But usually it doesn't need to know it because given exact URI it can both fetch resource content and receive update notifications."

Embedded variant

If the client never benefits from deferred fetching (small content, one-shot, no subscribe semantics), the tool can return type: 'resource' with the content inlined directly — see concepts/resource-link-vs-embedded-resource for the token-budget trade-off.

Tension with patterns/tool-surface-minimization

This pattern increases the tool inventory — each resource gets a companion tool. That runs counter to patterns/tool-surface-minimization (keep exposed tool count low because tool definitions consume concepts/agent-context-window). Resolution: the pattern is most valuable when the resource would be useless without discovery (a live-logs resource nobody can find doesn't help) and least valuable when the resource is already discoverable through other channels (e.g. the client is known to support resources/list directly). Apply judiciously; do not wrap every resource in a tool if your target clients already discover resources natively.

Seen in

Last updated · 476 distilled / 1,218 read