React UI Base
Loading...

McpResources

Compose MCP resource picker behavior with unstyled parts for listing, searching, and selecting resources.

McpResources

McpResources in @tambo-ai/react-ui-base owns MCP resource discovery — listing available resources, search filtering, and selection handling — while keeping styling in @tambo-ai/ui-registry or your own UI layer.

Demo

import { McpResources } from "@tambo-ai/react-ui-base/mcp-resources";import { FileText, Search } from "lucide-react";export function DemoMcpResources() {  return (    <McpResources.Root onSelectResource={(uri, label) => console.log("Selected:", uri, label)}>      <div className="flex flex-col gap-3">        <McpResources.Trigger className="flex items-center gap-2 rounded-lg border border-neutral-200 px-3 py-1.5 text-sm text-neutral-700 hover:bg-neutral-100 disabled:opacity-40 dark:border-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-800">          <FileText className="h-3.5 w-3.5" />          Insert Resource        </McpResources.Trigger>        <div className="relative">          <Search className="absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-neutral-400" />          <McpResources.Search            placeholder="Search resources..."            className="w-full rounded-lg border border-neutral-200 bg-transparent py-1.5 pl-8 pr-3 text-sm placeholder:text-neutral-400 focus:outline-none focus:ring-1 focus:ring-neutral-300 dark:border-neutral-700 dark:focus:ring-neutral-600"          />        </div>        <McpResources.List          render={(props, state) => (            <div {...props} className="flex flex-col gap-1">              {state.resources.map((entry) => (                <McpResources.Item                  key={entry.resource.uri}                  uri={entry.resource.uri}                  name={entry.resource.name}                  description={entry.resource.description}                  className="w-full rounded-md px-3 py-1.5 text-left text-sm text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-700"                >                  {entry.resource.name ?? entry.resource.uri}                </McpResources.Item>              ))}            </div>          )}        />      </div>    </McpResources.Root>  );}

Anatomy

<McpResources.Root onSelectResource={handleSelect}>
  <McpResources.Trigger />
  <McpResources.Search />
  <McpResources.List>
    <McpResources.Item uri="..." />
  </McpResources.List>
</McpResources.Root>

Examples

Resource Search and Selection

The Root manages search state internally and passes it to useTamboMcpResourceList. The Search input updates the search state, and the List provides the filtered results.

<McpResources.Root
  onSelectResource={(uri, label) => {
    insertResourceReference(uri, label);
  }}
>
  <McpResources.Search placeholder="Filter resources..." />
  <McpResources.List
    render={(props, state) => (
      <div {...props}>
        {state.resources.map((entry) => (
          <McpResources.Item
            key={entry.resource.uri}
            uri={entry.resource.uri}
            name={entry.resource.name}
          >
            {entry.resource.name ?? entry.resource.uri}
          </McpResources.Item>
        ))}
      </div>
    )}
  />
</McpResources.Root>

Accessing List State via Render Props

Use the render prop on List to access filtered resources and count:

<McpResources.List
  render={(props, state) => (
    <div {...props}>
      <p>{state.resourceCount} resources</p>
      {state.resources.map((entry) => (
        <McpResources.Item
          key={entry.resource.uri}
          uri={entry.resource.uri}
          name={entry.resource.name}
          description={entry.resource.description}
          render={(itemProps, itemState) => (
            <button {...itemProps}>
              <strong>{itemState.name ?? itemState.uri}</strong>
              {itemState.description && <span>{itemState.description}</span>}
            </button>
          )}
        />
      ))}
    </div>
  )}
/>

API reference

Root

PropTypeDescription
onSelectResource(uri: string, label: string) => voidOptional. Callback invoked when a resource is selected.

Renders nothing when no MCP resources are available, not loading, and no search query is active.

Render state:

FieldTypeDescription
resourceCountnumberNumber of available resources.
isLoadingbooleanWhether the resource list is loading.
hasSearchbooleanWhether a search query is active.

Trigger

No custom props. Renders as a <button> disabled when no resources are available.

Render state:

FieldTypeDescription
hasResourcesbooleanWhether resources are available.
isLoadingbooleanWhether the resource list is loading.

No custom props. Renders as an <input> wired to the search state from context.

Render state:

FieldTypeDescription
searchstringThe current search query.

List

No custom props. Provides the filtered resource collection through render state.

Render state:

FieldTypeDescription
resourcesListResourceEntry[]Array of filtered resources.
resourceCountnumberCount of filtered resources.
hasSearchbooleanWhether a search query is active.

Item

PropTypeDescription
uristringThe resource URI.
namestringOptional display name for the resource.
descriptionstringOptional description exposed via state.

Render state:

FieldTypeDescription
uristringThe resource URI.
namestring | undefinedThe resource display name.
descriptionstring | undefinedThe resource description.
select() => voidSelects this resource.

Accessibility

  • Trigger renders as <button> and is automatically disabled when no resources are available.
  • Search renders as <input type="text"> and supports all standard input attributes.
  • Item renders as <button> for keyboard accessibility.
  • Consumers should provide appropriate aria-label attributes on the trigger and search input.

Styling Hooks

  • data-slot="mcp-resources"
  • data-slot="mcp-resources-trigger"
  • data-slot="mcp-resources-search"
  • data-slot="mcp-resources-list"
  • data-slot="mcp-resources-item"