> ## Documentation Index
> Fetch the complete documentation index at: https://mcp-b-sync-npm-packages-docs-bf03420.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Live WebMCP Resource Examples

> Interactive examples of WebMCP resources that AI agents can read in real-time. Working demonstrations of static and template-based resources.

export const PolyfillSetup = () => {
  const {useState, useEffect} = React;
  const [isLoaded, setIsLoaded] = useState(false);
  useEffect(() => {
    const checkPolyfill = () => {
      if (window.navigator?.modelContext) {
        setIsLoaded(true);
      }
    };
    checkPolyfill();
    window.addEventListener('load', checkPolyfill);
    window.addEventListener('webmcp-loaded', checkPolyfill);
    return () => {
      window.removeEventListener('load', checkPolyfill);
      window.removeEventListener('webmcp-loaded', checkPolyfill);
    };
  }, []);
  const copyScript = () => {
    const scriptTag = '<script src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"></script>';
    navigator.clipboard.writeText(scriptTag);
  };
  return <div className="not-prose border dark:border-white/10 rounded-xl p-6 space-y-4">
      <h3 className="text-lg font-semibold text-zinc-950 dark:text-white mb-4">
        WebMCP Polyfill Status
      </h3>

      <div className="space-y-3">
        <div className="flex items-center justify-between p-4 rounded-lg bg-zinc-50 dark:bg-zinc-900">
          <div className="flex items-center gap-3">
            <div className={`w-3 h-3 rounded-full ${isLoaded ? 'bg-green-500' : 'bg-zinc-400'}`} />
            <div>
              <p className="text-sm font-medium text-zinc-900 dark:text-zinc-100">
                navigator.modelContext API
              </p>
              <p className="text-xs text-zinc-600 dark:text-zinc-400">
                {isLoaded ? 'Loaded and ready' : 'Not detected'}
              </p>
            </div>
          </div>
          {isLoaded && <span className="text-xs px-2 py-1 rounded bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400">
              Active
            </span>}
        </div>
      </div>

      {!isLoaded && <div className="p-4 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800">
          <p className="text-sm font-semibold text-amber-900 dark:text-amber-200 mb-2">
            Polyfill Not Detected
          </p>
          <p className="text-sm text-amber-800 dark:text-amber-300 mb-3">
            Add the WebMCP polyfill to enable the API. Add this script tag to your HTML:
          </p>
          <div className="relative">
            <pre className="text-xs bg-white dark:bg-zinc-950 p-3 rounded border border-amber-300 dark:border-amber-700 overflow-x-auto">
              <code className="text-amber-900 dark:text-amber-100">
                {`<script src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"></script>`}
              </code>
            </pre>
            <button onClick={copyScript} className="absolute top-2 right-2 px-2 py-1 text-xs bg-amber-200 dark:bg-amber-800 hover:bg-amber-300 dark:hover:bg-amber-700 text-amber-900 dark:text-amber-100 rounded transition-colors">
              Copy
            </button>
          </div>
        </div>}

      <div className="p-4 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800">
        <p className="text-sm font-semibold text-blue-900 dark:text-blue-200 mb-2">
          Installation Options
        </p>
        <div className="space-y-2 text-sm text-blue-800 dark:text-blue-300">
          <div>
            <strong>Via CDN (Easiest):</strong>
            <pre className="text-xs mt-1 bg-white dark:bg-blue-950 p-2 rounded border border-blue-200 dark:border-blue-700 overflow-x-auto">
              <code>{`<script src="https://unpkg.com/@mcp-b/global@latest/dist/index.iife.js"></script>`}</code>
            </pre>
          </div>
          <div>
            <strong>Via NPM:</strong>
            <pre className="text-xs mt-1 bg-white dark:bg-blue-950 p-2 rounded border border-blue-200 dark:border-blue-700 overflow-x-auto">
              <code>{`npm install @mcp-b/global
import '@mcp-b/global';`}</code>
            </pre>
          </div>
        </div>
      </div>
    </div>;
};

export const FileResource = () => {
  const [isRegistered, setIsRegistered] = useState(false);
  const [resourceReads, setResourceReads] = useState([]);
  const [executionPhase, setExecutionPhase] = useState(null);
  const [lastContent, setLastContent] = useState(null);
  const [lastPath, setLastPath] = useState(null);
  const containerRef = useRef(null);
  const virtualFiles = {
    'readme.txt': 'Welcome to WebMCP!\n\nThis is a virtual file system demo.',
    'config.json': '{\n  "version": "1.0.0",\n  "name": "webmcp-demo"\n}',
    'notes.md': '# Notes\n\n- Learn WebMCP\n- Build awesome tools\n- Ship it!',
    'data.csv': 'name,value\nalpha,100\nbeta,200\ngamma,300'
  };
  const showPageEffect = (color = '#10B981') => {
    const overlay = document.createElement('div');
    overlay.id = 'webmcp-file-effect';
    overlay.style.cssText = `
      position: fixed;
      inset: 0;
      background: ${color};
      opacity: 0;
      pointer-events: none;
      z-index: 9999;
      transition: opacity 0.3s ease;
    `;
    document.body.appendChild(overlay);
    requestAnimationFrame(() => {
      overlay.style.opacity = '0.08';
    });
    return overlay;
  };
  const hidePageEffect = () => {
    const overlay = document.getElementById('webmcp-file-effect');
    if (overlay) {
      overlay.style.opacity = '0';
      setTimeout(() => overlay.remove(), 300);
    }
  };
  const startExecution = async onExecute => {
    setExecutionPhase('executing');
    showPageEffect('#10B981');
    if (containerRef.current) {
      containerRef.current.scrollIntoView({
        behavior: 'smooth',
        block: 'center'
      });
    }
    await new Promise(resolve => setTimeout(resolve, 1000));
    const result = await onExecute();
    setExecutionPhase('complete');
    hidePageEffect();
    await new Promise(resolve => setTimeout(resolve, 2000));
    setExecutionPhase(null);
    return result;
  };
  useEffect(() => {
    const registerResource = async () => {
      if (typeof window === 'undefined' || !window.navigator?.modelContext) {
        return;
      }
      try {
        await window.navigator.modelContext.registerResource({
          uri: 'file://{path}',
          name: 'Virtual File',
          description: 'Read files from the virtual filesystem. Available files: readme.txt, config.json, notes.md, data.csv',
          mimeType: 'text/plain',
          async read(uri, params) {
            return startExecution(async () => {
              const path = params?.path || 'unknown';
              const fileContent = virtualFiles[path] || `File not found: ${path}`;
              const found = !!virtualFiles[path];
              const content = {
                uri: uri.href,
                text: fileContent,
                mimeType: path.endsWith('.json') ? 'application/json' : 'text/plain'
              };
              setLastContent(content);
              setLastPath(path);
              setResourceReads(prev => [...prev, {
                time: new Date().toISOString(),
                uri: `file://${path}`,
                status: found ? 'success' : 'not_found',
                content
              }]);
              return {
                contents: [content]
              };
            });
          }
        });
        setIsRegistered(true);
      } catch (error) {
        console.error('Failed to register file resource:', error);
      }
    };
    registerResource();
    window.addEventListener('webmcp-loaded', registerResource);
    return () => {
      window.removeEventListener('webmcp-loaded', registerResource);
      if (window.navigator?.modelContext?.unregisterResource) {
        window.navigator.modelContext.unregisterResource('file://{path}');
      }
    };
  }, []);
  const isActive = executionPhase !== null;
  return <div ref={containerRef} className={`not-prose border rounded-xl p-6 space-y-4 transition-all duration-300 relative ${isActive ? 'border-[#10B981] shadow-lg shadow-[#10B981]/10 ring-2 ring-[#10B981]/20' : 'border-zinc-200 dark:border-white/10'}`}>
      {isActive && <div className="absolute top-0 left-0 right-0 h-1 bg-zinc-100 dark:bg-zinc-800 rounded-t-xl overflow-hidden">
          <div className={`h-full bg-[#10B981] transition-all duration-500 ${executionPhase === 'executing' ? 'w-2/3 animate-pulse' : 'w-full'}`} />
        </div>}

      <div className="flex items-center justify-between">
        <div className="flex items-center gap-3">
          <h3 className="text-lg font-semibold text-zinc-950 dark:text-white">
            File Reader Resource
          </h3>
          {isActive && <span className="inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium rounded-md bg-[#10B981]/10 text-[#10B981] dark:bg-[#10B981]/20 dark:text-[#34D399]">
              {executionPhase === 'executing' && <>
                  <svg className="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
                    <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="3" />
                    <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
                  </svg>
                  Reading file...
                </>}
              {executionPhase === 'complete' && <>
                  <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
                    <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
                  </svg>
                  Complete
                </>}
            </span>}
        </div>
        {isRegistered && !isActive && <span className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-md bg-emerald-50 dark:bg-emerald-900/20 text-emerald-700 dark:text-emerald-400 border border-emerald-200 dark:border-emerald-800">
            <span className="w-1.5 h-1.5 rounded-full bg-emerald-500" />
            Ready
          </span>}
      </div>

      <div className="flex items-center gap-2 text-xs text-zinc-500 dark:text-zinc-400">
        <span className="px-2 py-0.5 rounded bg-zinc-100 dark:bg-zinc-800 font-mono">
          URI Template
        </span>
        <span>
          Resource with parameterized URI{' '}
          <code className="text-emerald-600 dark:text-emerald-400">
            file://{'{'}
            <span className="text-amber-600 dark:text-amber-400">path</span>
            {'}'}
          </code>
        </span>
      </div>

      {!isRegistered && !isActive && <div className="p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800">
          <p className="text-sm text-amber-800 dark:text-amber-200">
            WebMCP not detected. Install the MCP-B extension to enable AI agent integration.
          </p>
        </div>}

      <div className="space-y-3">
        {}
        <div className="p-4 rounded-lg bg-zinc-50 dark:bg-zinc-800/50 border border-zinc-200 dark:border-zinc-700">
          <p className="text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-2 uppercase tracking-wide">
            Available Files
          </p>
          <div className="grid grid-cols-2 sm:grid-cols-4 gap-2">
            {Object.keys(virtualFiles).map(file => <div key={file} className="px-3 py-2 text-sm rounded-lg border border-zinc-200 dark:border-zinc-700 text-zinc-600 dark:text-zinc-400 font-mono">
                {file}
              </div>)}
          </div>
          <p className="text-xs text-zinc-500 dark:text-zinc-400 mt-2">
            AI agents can read any of these files using{' '}
            <code className="text-emerald-600 dark:text-emerald-400">
              readResource("file://filename")
            </code>
          </p>
        </div>

        {}
        {lastContent && lastPath && (executionPhase === 'complete' || resourceReads.length > 0) && <div className="p-4 rounded-lg bg-[#10B981]/5 dark:bg-[#10B981]/10 border border-[#10B981]/20">
            <div className="flex items-center justify-between mb-2">
              <p className="text-xs font-medium text-[#10B981] dark:text-[#34D399] uppercase tracking-wide">
                Last Read: {lastPath}
              </p>
              <span className="text-xs text-zinc-500 dark:text-zinc-400 font-mono">
                {lastContent.mimeType}
              </span>
            </div>
            <pre className="text-xs text-zinc-900 dark:text-zinc-100 whitespace-pre-wrap bg-zinc-100 dark:bg-zinc-800 p-3 rounded-lg overflow-x-auto font-mono">
              {lastContent.text}
            </pre>
          </div>}
      </div>

      {resourceReads.length > 0 && <div className="mt-6 pt-4 border-t border-zinc-200 dark:border-zinc-800">
          <h4 className="text-xs font-semibold text-zinc-500 dark:text-zinc-400 mb-3 uppercase tracking-wide">
            Recent Reads
          </h4>
          <div className="space-y-2 max-h-40 overflow-y-auto">
            {resourceReads.slice(-3).reverse().map((read, idx) => <div key={idx} className={`p-3 rounded-lg text-sm border ${read.status === 'success' ? 'bg-zinc-50 dark:bg-zinc-800/50 border-zinc-200 dark:border-zinc-700' : 'bg-amber-50 dark:bg-amber-900/10 border-amber-200 dark:border-amber-800'}`}>
                  <div className="flex items-center justify-between">
                    <code className="text-zinc-700 dark:text-zinc-300 font-mono text-sm">
                      readResource("{read.uri}")
                    </code>
                    {read.status === 'success' ? <svg className="w-3.5 h-3.5 text-emerald-600 dark:text-emerald-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
                        <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
                      </svg> : <svg className="w-3.5 h-3.5 text-amber-600 dark:text-amber-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
                        <path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
                      </svg>}
                  </div>
                </div>)}
          </div>
        </div>}
    </div>;
};

export const ConfigResource = () => {
  const [isRegistered, setIsRegistered] = useState(false);
  const [resourceReads, setResourceReads] = useState([]);
  const [executionPhase, setExecutionPhase] = useState(null);
  const [lastContent, setLastContent] = useState(null);
  const [config, setConfig] = useState({
    theme: 'dark',
    language: 'en',
    notifications: true,
    apiVersion: 'v2'
  });
  const containerRef = useRef(null);
  const showPageEffect = (color = '#10B981') => {
    const overlay = document.createElement('div');
    overlay.id = 'webmcp-resource-effect';
    overlay.style.cssText = `
      position: fixed;
      inset: 0;
      background: ${color};
      opacity: 0;
      pointer-events: none;
      z-index: 9999;
      transition: opacity 0.3s ease;
    `;
    document.body.appendChild(overlay);
    requestAnimationFrame(() => {
      overlay.style.opacity = '0.08';
    });
    return overlay;
  };
  const hidePageEffect = () => {
    const overlay = document.getElementById('webmcp-resource-effect');
    if (overlay) {
      overlay.style.opacity = '0';
      setTimeout(() => overlay.remove(), 300);
    }
  };
  const startExecution = async onExecute => {
    setExecutionPhase('executing');
    showPageEffect('#10B981');
    if (containerRef.current) {
      containerRef.current.scrollIntoView({
        behavior: 'smooth',
        block: 'center'
      });
    }
    await new Promise(resolve => setTimeout(resolve, 1000));
    const result = await onExecute();
    setExecutionPhase('complete');
    hidePageEffect();
    await new Promise(resolve => setTimeout(resolve, 2000));
    setExecutionPhase(null);
    return result;
  };
  const configRef = useRef(config);
  configRef.current = config;
  useEffect(() => {
    const registerResource = async () => {
      if (typeof window === 'undefined' || !window.navigator?.modelContext) {
        return;
      }
      try {
        await window.navigator.modelContext.registerResource({
          uri: 'config://app-settings',
          name: 'App Settings',
          description: 'Current application configuration settings',
          mimeType: 'application/json',
          async read() {
            return startExecution(async () => {
              const content = {
                uri: 'config://app-settings',
                text: JSON.stringify(configRef.current, null, 2),
                mimeType: 'application/json'
              };
              setLastContent(content);
              setResourceReads(prev => [...prev, {
                time: new Date().toISOString(),
                uri: 'config://app-settings',
                status: 'success',
                content
              }]);
              return {
                contents: [content]
              };
            });
          }
        });
        setIsRegistered(true);
      } catch (error) {
        console.error('Failed to register config resource:', error);
      }
    };
    registerResource();
    window.addEventListener('webmcp-loaded', registerResource);
    return () => {
      window.removeEventListener('webmcp-loaded', registerResource);
      if (window.navigator?.modelContext?.unregisterResource) {
        window.navigator.modelContext.unregisterResource('config://app-settings');
      }
    };
  }, []);
  const updateConfig = (key, value) => {
    setConfig(prev => ({
      ...prev,
      [key]: value
    }));
  };
  const isActive = executionPhase !== null;
  return <div ref={containerRef} className={`not-prose border rounded-xl p-6 space-y-4 transition-all duration-300 relative ${isActive ? 'border-[#10B981] shadow-lg shadow-[#10B981]/10 ring-2 ring-[#10B981]/20' : 'border-zinc-200 dark:border-white/10'}`}>
      {isActive && <div className="absolute top-0 left-0 right-0 h-1 bg-zinc-100 dark:bg-zinc-800 rounded-t-xl overflow-hidden">
          <div className={`h-full bg-[#10B981] transition-all duration-500 ${executionPhase === 'executing' ? 'w-2/3 animate-pulse' : 'w-full'}`} />
        </div>}

      <div className="flex items-center justify-between">
        <div className="flex items-center gap-3">
          <h3 className="text-lg font-semibold text-zinc-950 dark:text-white">
            App Settings Resource
          </h3>
          {isActive && <span className="inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium rounded-md bg-[#10B981]/10 text-[#10B981] dark:bg-[#10B981]/20 dark:text-[#34D399]">
              {executionPhase === 'executing' && <>
                  <svg className="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
                    <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="3" />
                    <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
                  </svg>
                  Reading resource...
                </>}
              {executionPhase === 'complete' && <>
                  <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
                    <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
                  </svg>
                  Complete
                </>}
            </span>}
        </div>
        {isRegistered && !isActive && <span className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-md bg-emerald-50 dark:bg-emerald-900/20 text-emerald-700 dark:text-emerald-400 border border-emerald-200 dark:border-emerald-800">
            <span className="w-1.5 h-1.5 rounded-full bg-emerald-500" />
            Ready
          </span>}
      </div>

      <div className="flex items-center gap-2 text-xs text-zinc-500 dark:text-zinc-400">
        <span className="px-2 py-0.5 rounded bg-zinc-100 dark:bg-zinc-800 font-mono">
          registerResource()
        </span>
        <span>
          Static resource with URI{' '}
          <code className="text-emerald-600 dark:text-emerald-400">config://app-settings</code>
        </span>
      </div>

      {!isRegistered && !isActive && <div className="p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800">
          <p className="text-sm text-amber-800 dark:text-amber-200">
            WebMCP not detected. Install the MCP-B extension to enable AI agent integration.
          </p>
        </div>}

      <div className="space-y-3">
        {}
        <div className="p-4 rounded-lg bg-zinc-50 dark:bg-zinc-800/50 border border-zinc-200 dark:border-zinc-700">
          <p className="text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-3 uppercase tracking-wide">
            Edit Settings (updates resource content)
          </p>
          <div className="grid grid-cols-2 gap-3">
            <div>
              <label className="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">
                Theme
              </label>
              <select value={config.theme} onChange={e => updateConfig('theme', e.target.value)} className="w-full px-3 py-1.5 text-sm rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100">
                <option value="light">Light</option>
                <option value="dark">Dark</option>
                <option value="system">System</option>
              </select>
            </div>
            <div>
              <label className="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">
                Language
              </label>
              <select value={config.language} onChange={e => updateConfig('language', e.target.value)} className="w-full px-3 py-1.5 text-sm rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100">
                <option value="en">English</option>
                <option value="es">Spanish</option>
                <option value="fr">French</option>
              </select>
            </div>
          </div>

          <div className="flex items-center gap-3 mt-3">
            <label className="flex items-center gap-2 text-sm text-zinc-700 dark:text-zinc-300">
              <input type="checkbox" checked={config.notifications} onChange={e => updateConfig('notifications', e.target.checked)} className="rounded border-zinc-300 dark:border-zinc-700" />
              Notifications
            </label>
          </div>
        </div>

        {}
        <div className="p-4 rounded-lg bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700">
          <p className="text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-2 uppercase tracking-wide">
            Current Resource Content
          </p>
          <pre className="text-xs text-zinc-900 dark:text-zinc-100 whitespace-pre-wrap font-mono">
            {JSON.stringify(config, null, 2)}
          </pre>
        </div>

        {}
        {lastContent && (executionPhase === 'complete' || resourceReads.length > 0) && <div className="p-4 rounded-lg bg-[#10B981]/5 dark:bg-[#10B981]/10 border border-[#10B981]/20">
            <p className="text-xs font-medium text-[#10B981] dark:text-[#34D399] mb-2 uppercase tracking-wide">
              Last Read Content
            </p>
            <pre className="text-xs text-zinc-900 dark:text-zinc-100 whitespace-pre-wrap bg-zinc-100 dark:bg-zinc-800 p-3 rounded-lg overflow-x-auto font-mono">
              {lastContent.text}
            </pre>
          </div>}
      </div>

      {resourceReads.length > 0 && <div className="mt-6 pt-4 border-t border-zinc-200 dark:border-zinc-800">
          <h4 className="text-xs font-semibold text-zinc-500 dark:text-zinc-400 mb-3 uppercase tracking-wide">
            Recent Reads
          </h4>
          <div className="space-y-2 max-h-40 overflow-y-auto">
            {resourceReads.slice(-3).reverse().map((read, idx) => <div key={idx} className="p-3 rounded-lg text-sm bg-zinc-50 dark:bg-zinc-800/50 border border-zinc-200 dark:border-zinc-700">
                  <div className="flex items-center justify-between">
                    <code className="text-zinc-700 dark:text-zinc-300 font-mono text-sm">
                      readResource("{read.uri}")
                    </code>
                    <svg className="w-3.5 h-3.5 text-emerald-600 dark:text-emerald-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
                      <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
                    </svg>
                  </div>
                </div>)}
          </div>
        </div>}
    </div>;
};

This page demonstrates **live WebMCP resources** that register themselves with the browser and can be read by AI agents in real-time. Resources expose data that AI models can access asynchronously.

<Note>
  **These resources are live and ready!** The WebMCP polyfill is included with this documentation site. Install the [MCP-B browser extension](https://chromewebstore.google.com/detail/mcp-b-extension/daohopfhkdelnpemnhlekblhnikhdhfa) to enable AI agents (like Claude) to discover and read these resources directly.
</Note>

<CardGroup cols={3}>
  <Card title="Resource Registration" icon="list-check" href="/concepts/resources">
    Understanding resource registration
  </Card>

  <Card title="Live Tools" icon="wand-magic-sparkles" href="/live-tool-examples">
    Interactive tool demonstrations
  </Card>

  <Card title="Live Prompts" icon="message-lines" href="/live-prompt-examples">
    Interactive prompt demonstrations
  </Card>
</CardGroup>

***

## WebMCP Status

Check if the WebMCP polyfill is loaded and ready:

<PolyfillSetup />

***

## What are Resources?

Resources in MCP are **data endpoints** that AI agents can read. Unlike tools (which execute actions) or prompts (which generate messages), resources provide access to dynamic or static content like configuration, files, or API data.

| Feature       | Description                                            |
| ------------- | ------------------------------------------------------ |
| **Purpose**   | Expose readable data to AI agents                      |
| **URI**       | Unique identifier (static or template)                 |
| **MIME Type** | Content type hint (application/json, text/plain, etc.) |
| **Output**    | Array of content objects with text or binary data      |

***

## App Settings Resource

**Demonstrates:** Static resource with fixed URI

This resource shows the fundamentals of `registerResource()` - a static resource with a fixed URI that returns configuration data. The resource content updates dynamically based on the current settings.

<ConfigResource />

<Tip>
  **Try it with your AI assistant:** Ask Claude to "read the app-settings resource" and watch it retrieve the current configuration!
</Tip>

<CodeGroup>
  ```jsx "Static Resource Implementation" theme={null}
  // Register a static resource with fixed URI
  navigator.modelContext.registerResource({
    uri: 'config://app-settings',
    name: 'App Settings',
    description: 'Current application configuration',
    mimeType: 'application/json',
    async read() {
      return {
        contents: [{
          uri: 'config://app-settings',
          text: JSON.stringify({
            theme: 'dark',
            language: 'en',
            notifications: true,
          }, null, 2),
          mimeType: 'application/json',
        }],
      };
    },
  });
  ```
</CodeGroup>

***

## File Reader Resource

**Demonstrates:** Resource template with URI parameters

This resource shows advanced usage with **URI templates** - the `{path}` placeholder allows AI agents to read different files by specifying the path parameter. Templates enable flexible, parameterized data access.

<FileResource />

<Tip>
  **Try it with your AI assistant:** Ask Claude to "read the file readme.txt" and watch it access the virtual filesystem!
</Tip>

<CodeGroup>
  ```jsx "Template Resource Implementation" theme={null}
  // Register a resource with URI template
  navigator.modelContext.registerResource({
    uri: 'file://{path}',  // {path} is extracted from the request
    name: 'Virtual File',
    description: 'Read files from the virtual filesystem',
    mimeType: 'text/plain',
    async read(uri, params) {
      // params.path contains the extracted path
      const path = params?.path || 'unknown';

      // Fetch file content (from filesystem, API, etc.)
      const content = await fetchFileContent(path);

      return {
        contents: [{
          uri: uri.href,
          text: content,
          mimeType: 'text/plain',
        }],
      };
    },
  });
  ```
</CodeGroup>

***

## Resource API Reference

### `registerResource(descriptor)`

Registers a new resource with the browser.

```typescript theme={null}
interface ResourceDescriptor {
  uri: string;              // Static URI or template with {params}
  name: string;             // Human-readable name
  description?: string;     // Description for AI agents
  mimeType?: string;        // Content type hint
  read: (uri: URL, params?: Record<string, string>) => Promise<{
    contents: ResourceContents[];
  }>;
}

interface ResourceContents {
  uri: string;              // The resolved URI
  text?: string;            // Text content
  blob?: string;            // Base64 binary content
  mimeType?: string;        // Content type
}
```

### `listResources()`

Returns all registered static resources.

```typescript theme={null}
const resources = navigator.modelContext.listResources();
// [{ uri: 'config://app-settings', name: 'App Settings', ... }]
```

### `listResourceTemplates()`

Returns all registered resource templates.

```typescript theme={null}
const templates = navigator.modelContext.listResourceTemplates();
// [{ uriTemplate: 'file://{path}', name: 'Virtual File', ... }]
```

<Note>
  The `readResource()` method is called by AI agents through the MCP protocol, not directly from your application code. When an AI agent calls `readResource('config://app-settings')`, your registered `read()` handler is invoked and the result is returned to the agent.
</Note>

***

## Static vs Template Resources

<Tabs>
  <Tab title="Static Resources">
    Static resources have fixed URIs and provide single data endpoints.

    ```typescript theme={null}
    // Fixed URI - always returns the same logical resource
    registerResource({
      uri: 'config://app-settings',
      // ...
    });

    // Read with exact URI
    readResource('config://app-settings');
    ```

    **Use cases:**

    * Application configuration
    * User preferences
    * System status
    * Single data endpoints
  </Tab>

  <Tab title="Template Resources">
    Template resources use URI parameters to access dynamic data.

    ```typescript theme={null}
    // Template with {param} placeholders
    registerResource({
      uri: 'file://{path}',
      // params.path available in read()
    });

    // Read with resolved URI
    readResource('file://readme.txt');  // path = "readme.txt"
    readResource('file://config.json'); // path = "config.json"
    ```

    **Use cases:**

    * File systems
    * Database records
    * API endpoints
    * Any parameterized data
  </Tab>
</Tabs>

***

## Best Practices

<AccordionGroup>
  <Accordion title="Choose meaningful URIs">
    Use descriptive URI schemes that indicate the data type: `config://`, `file://`, `user://`, `api://`. This helps AI agents understand what they're accessing.
  </Accordion>

  <Accordion title="Specify MIME types">
    Always provide `mimeType` to help AI agents parse content correctly. Use standard MIME types like `application/json` or `text/plain`.
  </Accordion>

  <Accordion title="Handle errors gracefully">
    Return helpful error messages in the content when resources can't be read. Include context about what went wrong.
  </Accordion>

  <Accordion title="Use templates for collections">
    When exposing multiple similar items (files, users, records), use URI templates instead of registering each item separately.
  </Accordion>

  <Accordion title="Keep content focused">
    Return only the relevant data. Avoid dumping entire databases - let AI agents request specific resources as needed.
  </Accordion>
</AccordionGroup>

***

## URI Scheme Examples

| Scheme       | Example                 | Use Case                  |
| ------------ | ----------------------- | ------------------------- |
| `config://`  | `config://app-settings` | Application configuration |
| `file://`    | `file://{path}`         | Virtual filesystem        |
| `user://`    | `user://{id}/profile`   | User data                 |
| `api://`     | `api://weather/{city}`  | External API data         |
| `db://`      | `db://products/{sku}`   | Database records          |
| `session://` | `session://current`     | Session state             |

***

## Related Documentation

<CardGroup cols={2}>
  <Card title="Concepts: Resources" icon="lightbulb" href="/concepts/resources">
    Deep dive into resource architecture and patterns
  </Card>

  <Card title="Live Tool Examples" icon="wand-magic-sparkles" href="/live-tool-examples">
    Interactive tool demonstrations
  </Card>

  <Card title="Live Prompt Examples" icon="message-lines" href="/live-prompt-examples">
    Interactive prompt demonstrations
  </Card>

  <Card title="Best Practices" icon="star" href="/best-practices">
    Guidelines for effective WebMCP usage
  </Card>
</CardGroup>
