Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

Expand Down
7 changes: 5 additions & 2 deletions claude-skill/webmcp-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 |
Expand Down Expand Up @@ -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}

Expand Down
13 changes: 7 additions & 6 deletions extension/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 };
}

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -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
*/
Expand Down
15 changes: 9 additions & 6 deletions extension/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion extension/sidepanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down