diff --git a/README.md b/README.md index 7c5891c..f6e017f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ WebMCP is a W3C Draft standard that lets websites expose structured, callable to | **WebMCP Core** | 30% | `navigator.modelContext` API, registered imperative tools, schema quality, annotations | | **Declarative Forms** | 25% | `toolname`, `tooldescription`, `toolparamdescription`, `toolautosubmit` attributes, form coverage ratio | | **Structured Data** | 20% | JSON-LD blocks, `potentialAction` (SearchAction, BuyAction), Product/Offer schema, Organization/WebSite schema | -| **Discovery & Crawling** | 15% | `/.well-known/webmcp` manifest, `/llms.txt`, `robots.txt` AI crawler rules, sitemaps | +| **Discovery & Crawling** | 15% | `/.well-known/webmcp` and `/.well-known/webmcp.json` manifest, `/llms.txt`, `robots.txt` AI crawler rules, sitemaps | | **Technical Foundation** | 10% | HTTPS, semantic HTML, server-side rendering, stable form IDs | | **Security & Consent** | Flags | Sensitive forms with autosubmit, missing consent patterns | diff --git a/claude-skill/webmcp-audit.md b/claude-skill/webmcp-audit.md index 02fa012..13035a6 100644 --- a/claude-skill/webmcp-audit.md +++ b/claude-skill/webmcp-audit.md @@ -38,7 +38,9 @@ For each URL, use WebFetch to retrieve: 4. **`{origin}/.well-known/webmcp`** — check if it exists and extract manifest content -Make all 4 fetch requests for each URL. +5. **`{origin}/.well-known/webmcp.json`** — check if it exists and extract manifest content (alternative proposed standard) + +Make all 5 fetch requests for each URL. ## Step 2: Score the page @@ -78,7 +80,7 @@ Apply the following scoring rubric. Each category has a weight and individual si | Signal | How to detect | Points (out of 15) | |--------|--------------|---------------------| -| `/.well-known/webmcp` manifest exists | Successful fetch with valid JSON | 5 | +| `/.well-known/webmcp` or `/.well-known/webmcp.json` manifest exists | Successful fetch with valid JSON from either endpoint | 5 | | `/llms.txt` exists | Successful fetch with markdown content | 4 | | `robots.txt` allows AI crawlers | No blocks for GPTBot, Google-Extended, ClaudeBot | 4 | | Sitemap referenced in robots.txt | `Sitemap:` directive present | 2 | @@ -132,6 +134,7 @@ Apply the following scoring rubric. Each category has a weight and individual si ## Discovery Files - {status icon} /.well-known/webmcp — {status detail} +- {status icon} /.well-known/webmcp.json — {status detail} - {status icon} /llms.txt — {status detail} - {status icon} robots.txt — {AI crawler status detail} diff --git a/extension/background.js b/extension/background.js index e5e8dc4..35c688e 100644 --- a/extension/background.js +++ b/extension/background.js @@ -5,7 +5,7 @@ * Responsibilities: * - Open the side panel when the extension icon is clicked. * - Update the badge based on scan results from the content script. - * - Fetch discovery files (robots.txt, llms.txt, .well-known/webmcp). + * - Fetch discovery files (robots.txt, llms.txt, .well-known/webmcp, .well-known/webmcp.json). * - Inject a MAIN-world script to detect navigator.modelContext. * - Route messages between the content script and the side panel. * - Auto-scan on navigation and SPA history changes. @@ -76,17 +76,18 @@ async function fetchFile(url) { } /** - * Fetch all three discovery files for a given origin. + * Fetch all discovery files for a given origin. * @param {string} origin - e.g. "https://example.com" - * @returns {Promise<{ robots: object, llms: object, webmcp: object }>} + * @returns {Promise<{ robots: object, llms: object, webmcp: object, webmcpJson: object }>} */ async function fetchDiscoveryFiles(origin) { - const [robots, llms, webmcp] = await Promise.all([ + const [robots, llms, webmcp, webmcpJson] = await Promise.all([ fetchFile(`${origin}/robots.txt`), fetchFile(`${origin}/llms.txt`), fetchFile(`${origin}/.well-known/webmcp`), + fetchFile(`${origin}/.well-known/webmcp.json`), ]); - return { robots, llms, webmcp }; + return { robots, llms, webmcp, webmcpJson }; } // --------------------------------------------------------------------------- @@ -190,7 +191,7 @@ async function handleRequestScan(message) { /** * Handle FETCH_DISCOVERY requests. - * Fetches robots.txt, llms.txt, and .well-known/webmcp for the given origin. + * Fetches robots.txt, llms.txt, .well-known/webmcp, and .well-known/webmcp.json for the given origin. * @param {object} message * @param {function} sendResponse */ diff --git a/extension/content.js b/extension/content.js index f3e6bc5..7193b7d 100644 --- a/extension/content.js +++ b/extension/content.js @@ -348,19 +348,22 @@ function buildDiscoveryPlaceholder() { /** * Fill discovery category with data returned from the background script. - * @param {{ robots: object, llms: object, webmcp: object }} discovery + * @param {{ robots: object, llms: object, webmcp: object, webmcpJson: object }} discovery * @returns {{ score: number, max: number, signals: Array }} */ function fillDiscovery(discovery) { const signals = []; let score = 0; - // webmcp manifest (5 pts) - if (discovery.webmcp?.status === 200 && discovery.webmcp.content) { + // webmcp manifest (5 pts) — check .well-known/webmcp or .well-known/webmcp.json + const hasWebmcp = discovery.webmcp?.status === 200 && discovery.webmcp.content; + const hasWebmcpJson = discovery.webmcpJson?.status === 200 && discovery.webmcpJson.content; + if (hasWebmcp || hasWebmcpJson) { + const found = [hasWebmcp && '.well-known/webmcp', hasWebmcpJson && '.well-known/webmcp.json'].filter(Boolean).join(' and '); score += 5; - signals.push(signal('webmcp manifest', 'pass', '.well-known/webmcp found', 5)); + signals.push(signal('webmcp manifest', 'pass', `${found} found`, 5)); } else { - signals.push(signal('webmcp manifest', 'fail', 'No .well-known/webmcp', 0)); + signals.push(signal('webmcp manifest', 'fail', 'No .well-known/webmcp or .well-known/webmcp.json', 0)); } // llms.txt (4 pts) @@ -675,7 +678,7 @@ function generateRecommendations(categories, forms, tools) { } const noManifest = disc.find((s) => s.name === 'webmcp manifest' && s.status === 'fail'); if (noManifest) { - recs.push('Create a /.well-known/webmcp manifest listing your agent-accessible tools.'); + recs.push('Create a /.well-known/webmcp or /.well-known/webmcp.json manifest listing your agent-accessible tools.'); } // Missing tooldescription on tools diff --git a/extension/sidepanel.js b/extension/sidepanel.js index 206922c..aed4394 100644 --- a/extension/sidepanel.js +++ b/extension/sidepanel.js @@ -87,7 +87,7 @@ const FIX_SNIPPETS = { - [Cart](https://example.com/cart): Manage shopping cart`, }, webmcp_manifest: { - desc: 'Create a /.well-known/webmcp manifest describing available tools.', + desc: 'Create a /.well-known/webmcp or /.well-known/webmcp.json manifest describing available tools.', code: `{ "schema_version": "0.1", "name_for_model": "example_site",