1/** 2 * WebMCP Bridge â Frontend bridge 3 * 4 * Registration priority: 5 * 1. navigator.modelContext.provideContext() â current Chrome WebMCP spec 6 * 2. window.ai?.tools / navigator.ai?.tools â legacy Early Preview API 7 * 3. window.webmcpBridgeTools â vanilla JS public fallback 8 */ 9( function( config ) { 10 'use strict'; 11 12 if ( ! config ) return; 13 14 // ------------------------------------------------------------------------- 15 // Internal: call a tool via the REST API 16 // ------------------------------------------------------------------------- 17 async function executeTool( toolName, params ) { 18 const res = await fetch( config.restUrl + '/execute', { 19 method : 'POST', 20 headers : { 21 'Content-Type': 'application/json', 22 'X-WP-Nonce' : config.nonce, 23 }, 24 body: JSON.stringify( { tool: toolName, params: params || {} } ), 25 } ); 26 27 if ( ! res.ok ) { 28 const err = await res.json().catch( () => ( {} ) ); 29 throw new Error( err.message || `Tool execution failed (HTTP ${ res.status })` ); 30 } 31 32 return res.json(); 33 } 34 35 // ------------------------------------------------------------------------- 36 // Fetch manifest from REST endpoint 37 // ------------------------------------------------------------------------- 38 async function fetchManifest() { 39 const res = await fetch( config.restUrl + '/manifest', { cache: 'no-store' } ); 40 if ( ! res.ok ) throw new Error( 'Could not fetch WebMCP manifest.' ); 41 return res.json(); 42 } 43 44 // ------------------------------------------------------------------------- 45 // Build tool definitions â use inputSchema (not parameters) 46 // ------------------------------------------------------------------------- 47 function buildToolDefinitions( manifest ) { 48 return ( manifest.tools || [] ).map( tool => ( { 49 name : tool.name, 50 description: tool.description, 51 // inputSchema is the correct field per WebMCP spec and Anthropic tool_use format 52 inputSchema: tool.inputSchema || { type: 'object', properties: {}, additionalProperties: true }, 53 execute : ( params ) => executeTool( tool.name, params ), 54 } ) ); 55 } 56 57 // ------------------------------------------------------------------------- 58 // Register with the browser WebMCP API 59 // Priority: navigator.modelContext.provideContext() > legacy ai.tools 60 // ------------------------------------------------------------------------- 61 async function registerWithBrowserAPI( toolDefinitions ) { 62 63 // 1. Current Chrome WebMCP spec (navigator.modelContext.provideContext) 64 if ( navigator.modelContext && typeof navigator.modelContext.provideContext === 'function' ) { 65 try { 66 await navigator.modelContext.provideContext( { 67 tools: toolDefinitions, 68 } ); 69 console.info( '[WebMCP Bridge] Registered', toolDefinitions.length, 'tools via navigator.modelContext.provideContext()' ); 70 return true; 71 } catch ( e ) { 72 console.warn( '[WebMCP Bridge] modelContext.provideContext() failed:', e ); 73 } 74 } 75 76 // 2. Legacy Early Preview API (window.ai?.tools / navigator.ai?.tools) 77 const aiTools = window.ai?.tools ?? navigator.ai?.tools ?? null; 78 if ( aiTools && typeof aiTools.register === 'function' ) { 79 try { 80 await aiTools.register( { 81 name : config.siteTitle + ' (WebMCP Bridge)', 82 tools: toolDefinitions, 83 } ); 84 console.info( '[WebMCP Bridge] Registered via legacy ai.tools.register()' ); 85 return true; 86 } catch ( e ) { 87 console.warn( '[WebMCP Bridge] Legacy ai.tools registration failed:', e ); 88 } 89 } 90 91 return false; 92 } 93 94 // ------------------------------------------------------------------------- 95 // Inject <meta> tags for agent discovery 96 // ------------------------------------------------------------------------- 97 function injectMetaTags() { 98 const meta = document.createElement( 'meta' ); 99 meta.name = 'webmcp-manifest'; 100 meta.content = config.restUrl + '/manifest'; 101 document.head.appendChild( meta ); 102 103 const metaVersion = document.createElement( 'meta' ); 104 metaVersion.name = 'webmcp-version'; 105 metaVersion.content = '1.0'; 106 document.head.appendChild( metaVersion ); 107 } 108 109 // ------------------------------------------------------------------------- 110 // Expose public fallback API for non-WebMCP agents 111 // ------------------------------------------------------------------------- 112 function exposePublicAPI( toolDefinitions ) { 113 window.webmcpBridgeTools = { 114 version : '1.0.0', 115 site : config.siteTitle, 116 manifestUrl : config.restUrl + '/manifest', 117 tools : {}, 118 119 call: function( toolName, params ) { 120 return executeTool( toolName, params ); 121 }, 122 123 list: function() { 124 return toolDefinitions.map( t => t.name ); 125 }, 126 }; 127 128 toolDefinitions.forEach( tool => { 129 window.webmcpBridgeTools.tools[ tool.name ] = { 130 description : tool.description, 131 inputSchema : tool.inputSchema, 132 call : ( params ) => executeTool( tool.name, params ), 133 }; 134 } ); 135 136 console.info( '[WebMCP Bridge] Public API available at window.webmcpBridgeTools' ); 137 } 138 139 // ------------------------------------------------------------------------- 140 // Boot 141 // ------------------------------------------------------------------------- 142 async function boot() { 143 try { 144 const manifest = await fetchManifest(); 145 const toolDefinitions = buildToolDefinitions( manifest ); 146 147 injectMetaTags(); 148 exposePublicAPI( toolDefinitions ); 149 150 const registered = await registerWithBrowserAPI( toolDefinitions ); 151 if ( ! registered ) { 152 console.info( '[WebMCP Bridge] No browser WebMCP API available â fallback active.' ); 153 } 154 155 window.dispatchEvent( new CustomEvent( 'wpwebmcp:ready', { 156 detail: { tools: toolDefinitions.map( t => t.name ), manifest }, 157 } ) ); 158 159 } catch ( e ) { 160 console.error( '[WebMCP Bridge] Boot error:', e ); 161 } 162 } 163 164 if ( document.readyState === 'loading' ) { 165 document.addEventListener( 'DOMContentLoaded', boot ); 166 } else { 167 boot(); 168 } 169 170} )( window.webmcpBridge || null );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.