From 9a72bbe464a0a515641c84d114eab1c6f7ef9123 Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Wed, 7 Oct 2026 16:34:15 +1100 Subject: [PATCH] fix: verify the OpenAI domain and serve a large site icon The OpenAI plugin portal reads a public token from /.well-known/openai-apps-challenge. The token now ships as a Worker var. Claude shows a connector icon from Google's favicon service, which kept a retired skilld mark because the site declared no large icon. The site now serves /apple-touch-icon.png, and the MCP server lists its icons in serverInfo.icons. --- docs/runbooks/agent-app-directories.md | 8 ++++++++ layers/mcp/server/mcp/index.ts | 5 +++++ nuxt.config.ts | 3 +++ public/apple-touch-icon.png | Bin 0 -> 8207 bytes worker-configuration.d.ts | 5 +++-- wrangler.jsonc | 4 +++- 6 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 public/apple-touch-icon.png diff --git a/docs/runbooks/agent-app-directories.md b/docs/runbooks/agent-app-directories.md index 940550d2..0a0b1729 100644 --- a/docs/runbooks/agent-app-directories.md +++ b/docs/runbooks/agent-app-directories.md @@ -75,3 +75,11 @@ Any paid Claude plan can submit. Use these fields in the portal. Before you submit, add the server in Claude as a custom connector and call each tool once. After submission, Anthropic scans the server and lists it as a Community connector. Status and reviewer feedback appear in the portal. Escalations go to `mcp-review@anthropic.com`. + +## Icons + +Claude shows a connector's icon from Google's favicon service (`google.com/s2/favicons?domain=skilld.dev`), not from skilld.dev directly. +Google caches that icon. Until 2026-10-07 it served a retired skilld mark, because the site declared no large icon. +The site now declares `/apple-touch-icon.png`, and the MCP server lists its icons in `serverInfo.icons`. +The Claude listing sets its custom icon URL to `https://skilld.dev/logo-icon.png`, which overrides the favicon in the directory listing only. +The chat tile updates when Google refreshes its cache. diff --git a/layers/mcp/server/mcp/index.ts b/layers/mcp/server/mcp/index.ts index b83806f0..42a7358c 100644 --- a/layers/mcp/server/mcp/index.ts +++ b/layers/mcp/server/mcp/index.ts @@ -7,6 +7,11 @@ const MAX_MCP_BODY_BYTES = 64 * 1024 export default defineMcpHandler({ description: 'Search and browse skilld.dev for agent skills, and read who wrote each one.', + // MCP clients that read server icons show these instead of a cached favicon. + icons: [ + { src: 'https://skilld.dev/logo-icon.png', mimeType: 'image/png', sizes: ['512x512'] }, + { src: 'https://skilld.dev/logo-icon.svg', mimeType: 'image/svg+xml', sizes: ['any'] }, + ], instructions: 'skilld.dev is a curated registry of agent skills. Search first, inspect provenance before recommending a skill, then return the run command for the user to approve and run. Offer the install command only when the user wants the skill in every session. Without a shell, such as in a chat app, follow the markdown from get_skill for this session, and tell the user the skill name and source repository first. skilld.dev shows who wrote a skill and where its source lives. It does not review skills for safety, so never call a skill safe or verified. This server never runs or installs anything.', tools: (event) => { const deps: McpToolDeps = { diff --git a/nuxt.config.ts b/nuxt.config.ts index 96f2008b..d12f4f67 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -386,6 +386,9 @@ export default defineNuxtConfig({ // .ico is a fixed multi-size bitmap, so it declares what it contains. { rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg', sizes: 'any' }, { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico', sizes: '16x16 32x32 48x48' }, + // Google's favicon service, which Claude uses for connector icons, prefers a + // large icon. Without one it kept serving a retired mark for skilld.dev. + { rel: 'apple-touch-icon', href: '/apple-touch-icon.png', sizes: '512x512' }, ], style: [ // A browser orders cascade layers by where each name first appears. diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b85b7c82a68c866b57002790ab7678a5c12a75aa GIT binary patch literal 8207 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 = { [Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string; }; declare namespace NodeJS { - interface ProcessEnv extends StringifyValues> {} + interface ProcessEnv extends StringifyValues> {} } // Begin runtime types diff --git a/wrangler.jsonc b/wrangler.jsonc index 6f629379..dcf9ef7b 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -135,7 +135,9 @@ "ARTIFACT_PRIVATE_ACCESS_ENABLED": "false", "ARTIFACT_KEY_WRAP_KEY_ACTIVE_SLOT": "primary", "ARTIFACT_KEY_WRAP_KEY_PRIMARY_ID": "skilld-wrap-2026-08", - "ARTIFACT_KEY_WRAP_KEY_SECONDARY_ID": "" + "ARTIFACT_KEY_WRAP_KEY_SECONDARY_ID": "", + // Public token the OpenAI plugin portal reads at /.well-known/openai-apps-challenge. + "NUXT_OPENAI_APPS_CHALLENGE": "IygUmSBQJVU5v275Y6kWojVpQkVxqckQf4k8cVmBiys" }, "ai": { "binding": "AI",