Loading...

Adding Local Resources

Register local resources to provide context data to Tambo

Register resources with Tambo to provide context data, documentation, or other information to the AI. Local resources are data sources that run in your React app. Use them for providing documentation, configuration data, or dynamic content that the AI can reference when responding to user messages.

Static Resources

The simplest way to register resources is to provide a static array via the resources prop. This is useful when you have a fixed set of resources that don't change:

import { TamboRegistryProvider, ListResourceItem } from "@tambo-ai/react";

const resources: ListResourceItem[] = [
  {
    uri: "docs://api-reference",
    name: "API Reference",
    description: "Complete API documentation",
    mimeType: "text/plain",
  },
  {
    uri: "docs://faq",
    name: "Frequently Asked Questions",
    description: "Common questions and answers",
    mimeType: "text/plain",
  },
];

export function App() {
  return (
    <TamboRegistryProvider resources={resources}>
      <YourApp />
    </TamboRegistryProvider>
  );
}

With static resources, you must also provide a getResource function to retrieve the content:

import { ReadResourceResult } from "@tambo-ai/react";

const getResource = async (uri: string): Promise<ReadResourceResult> => {
  if (uri === "docs://api-reference") {
    return {
      contents: [
        {
          uri,
          mimeType: "text/plain",
          text: "API Reference: GET /api/users - Returns a list of users...",
        },
      ],
    };
  }
  if (uri === "docs://faq") {
    return {
      contents: [
        {
          uri,
          mimeType: "text/plain",
          text: "Q: How do I reset my password? A: Click the reset link...",
        },
      ],
    };
  }
  throw new Error(`Resource not found: ${uri}`);
};

export function App() {
  return (
    <TamboRegistryProvider resources={resources} getResource={getResource}>
      <YourApp />
    </TamboRegistryProvider>
  );
}

Dynamic Resources

For resources that change based on search queries or other dynamic conditions, provide listResources and getResource functions. Both functions must be provided together:

import {
  TamboRegistryProvider,
  ListResourceItem,
  ReadResourceResult,
} from "@tambo-ai/react";

const listResources = async (search?: string): Promise<ListResourceItem[]> => {
  // Fetch available resources, optionally filtered by search
  const allDocs = await fetchDocumentation();

  if (search) {
    return allDocs.filter((doc) =>
      doc.name.toLowerCase().includes(search.toLowerCase()),
    );
  }

  return allDocs.map((doc) => ({
    uri: `docs://${doc.id}`,
    name: doc.title,
    description: doc.summary,
    mimeType: "text/plain",
  }));
};

const getResource = async (uri: string): Promise<ReadResourceResult> => {
  const docId = uri.replace("docs://", "");
  const doc = await fetchDocument(docId);

  if (!doc) {
    throw new Error(`Document not found: ${uri}`);
  }

  return {
    contents: [
      {
        uri,
        mimeType: "text/plain",
        text: doc.content,
      },
    ],
  };
};

export function App() {
  return (
    <TamboRegistryProvider
      listResources={listResources}
      getResource={getResource}
    >
      <YourApp />
    </TamboRegistryProvider>
  );
}

Note: listResources and getResource must be provided together. If you provide one, you must provide the other. This validation ensures resources can be both discovered and retrieved.

Programmatic Registration

You can also register resources programmatically using the registry context. This is useful for adding resources based on user actions or application state:

import { useTamboRegistry } from "@tambo-ai/react";

function DocumentUploader() {
  const { registerResource } = useTamboRegistry();

  const handleUpload = async (file: File) => {
    const content = await file.text();

    // Register the uploaded document as a resource
    registerResource({
      uri: `user-docs://${file.name}`,
      name: file.name,
      description: `User uploaded: ${file.name}`,
      mimeType: file.type,
    });
  };

  return (
    <input
      type="file"
      onChange={(e) => {
        if (e.target.files?.[0]) {
          handleUpload(e.target.files[0]);
        }
      }}
    />
  );
}

For batch registration, use registerResources:

import { useTamboRegistry } from "@tambo-ai/react";

function DocumentLibrary() {
  const { registerResources } = useTamboRegistry();

  const loadLibrary = async () => {
    const docs = await fetchAllDocuments();

    registerResources(
      docs.map((doc) => ({
        uri: `library://${doc.id}`,
        name: doc.title,
        description: doc.summary,
        mimeType: "text/plain",
      })),
    );
  };

  // Load on mount
  useEffect(() => {
    loadLibrary();
  }, []);

  return <div>Library loaded</div>;
}

Resource Content Types

Resources can return various content types through the contents array. Each content item must include a uri and mimeType:

Text Content

const getResource = async (uri: string): Promise<ReadResourceResult> => {
  return {
    contents: [
      {
        uri,
        mimeType: "text/plain",
        text: "This is plain text content",
      },
    ],
  };
};

Binary Content (Base64)

const getResource = async (uri: string): Promise<ReadResourceResult> => {
  const imageData = await fetchImageAsBase64(uri);

  return {
    contents: [
      {
        uri,
        mimeType: "image/png",
        blob: imageData, // Base64-encoded binary data
      },
    ],
  };
};

Multiple Content Items

A single resource can return multiple content items:

const getResource = async (uri: string): Promise<ReadResourceResult> => {
  return {
    contents: [
      {
        uri: `${uri}/readme`,
        mimeType: "text/plain",
        text: "README content...",
      },
      {
        uri: `${uri}/diagram`,
        mimeType: "image/png",
        blob: "base64-encoded-image-data...",
      },
    ],
  };
};

When to Use Local Resources

Use local resources when you need to:

  • Provide documentation or reference material to the AI
  • Share configuration data or settings
  • Make user-uploaded content available for context
  • Expose application state as readable context
  • Provide dynamic content that changes based on search

For server-side integrations with databases, file systems, or external APIs, consider using MCP servers instead. MCP servers provide additional capabilities like tools, prompts, and sampling.

Resource URIs and Prefixing

Local resources registered through TamboRegistryProvider are never prefixed with a server key. This distinguishes them from MCP resources, which are always prefixed with their server key to prevent URI conflicts.

// Local resource URI (no prefix)
uri: "docs://getting-started";

// MCP resource URI (always prefixed)
uri: "filesystem:file:///path/to/document";

When using useTamboMcpResourceList(), local resources appear alongside MCP resources in the combined list, with local resources easily identifiable by their unprefixed URIs.

Validation and Error Handling

Tambo validates resource registrations to ensure data integrity:

  • Resource objects: Must have uri, name, and mimeType properties
  • Function pairing: listResources and getResource must both be provided or both omitted
  • URI uniqueness: Each resource should have a unique URI within your application
// ✅ Valid - both functions provided
<TamboRegistryProvider
  listResources={listResources}
  getResource={getResource}
>

// ✅ Valid - neither function provided (static only)
<TamboRegistryProvider resources={staticResources}>

// ❌ Invalid - only one function provided
<TamboRegistryProvider listResources={listResources}>
// Error: Both listResources and getResource must be provided together

Integration with MCP Hooks

Local resources integrate seamlessly with existing MCP hooks:

Listing All Resources

import { useTamboMcpResourceList } from "@tambo-ai/react";

function ResourceBrowser() {
  const { data: resources } = useTamboMcpResourceList();

  return (
    <ul>
      {resources?.map((entry) => (
        <li key={entry.resource.uri}>
          {entry.resource.name}
          {entry.server === null && " (local)"}
        </li>
      ))}
    </ul>
  );
}

Reading a Resource

import { useTamboMcpResource } from "@tambo-ai/react";

function ResourceViewer({ uri }: { uri: string }) {
  const { data: resource } = useTamboMcpResource(uri);

  if (!resource) return <div>Loading...</div>;

  return (
    <div>
      {resource.contents.map((content, i) => (
        <div key={i}>
          {content.text && <pre>{content.text}</pre>}
          {content.blob && (
            <img
              src={`data:${content.mimeType};base64,${content.blob}`}
              alt=""
            />
          )}
        </div>
      ))}
    </div>
  );
}

Combining Registration Methods

You can combine static resources with dynamic functions for maximum flexibility:

import { TamboRegistryProvider } from "@tambo-ai/react";

// Static resources that are always available
const staticResources = [
  {
    uri: "docs://privacy-policy",
    name: "Privacy Policy",
    mimeType: "text/plain",
  },
];

// Dynamic function to list all resources (static + dynamic)
const listResources = async (search?: string) => {
  const dynamicDocs = await fetchUserDocuments();
  const allResources = [
    ...staticResources,
    ...dynamicDocs.map((doc) => ({
      uri: `user-docs://${doc.id}`,
      name: doc.title,
      mimeType: "text/plain",
    })),
  ];

  if (search) {
    return allResources.filter((r) =>
      r.name.toLowerCase().includes(search.toLowerCase()),
    );
  }

  return allResources;
};

const getResource = async (uri: string) => {
  if (uri === "docs://privacy-policy") {
    return {
      contents: [
        { uri, mimeType: "text/plain", text: "Privacy policy text..." },
      ],
    };
  }

  // Handle dynamic resources
  const doc = await fetchDocument(uri);
  return {
    contents: [{ uri, mimeType: "text/plain", text: doc.content }],
  };
};

<TamboRegistryProvider
  resources={staticResources}
  listResources={listResources}
  getResource={getResource}
>
  <App />
</TamboRegistryProvider>;