Skip to content

Commit 1302074

Browse files
committed
Resolve all favicons through integrations.sh/logo
1 parent 04ce4b4 commit 1302074

3 files changed

Lines changed: 11 additions & 31 deletions

File tree

packages/plugins/mcp/src/sdk/presets.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export const mcpPresets: readonly McpPreset[] = [
3030
summary: "Deterministic MCP fixtures for validating native text and image content.",
3131
url: "https://emulators.dev/mcp/query/mcp?token=demo-token",
3232
endpoint: "https://emulators.dev/mcp/query/mcp?token=demo-token",
33-
icon: "https://emulators.dev/favicon.ico",
33+
icon: "https://integrations.sh/logo/emulators.dev",
3434
},
3535
{
3636
id: "deepwiki",
@@ -142,7 +142,7 @@ export const mcpPresets: readonly McpPreset[] = [
142142
id: "chrome-devtools",
143143
name: "Chrome DevTools",
144144
summary: "Debug a live Chrome browser session via local stdio.",
145-
icon: "https://www.google.com/chrome/static/images/favicons/favicon-32x32.png",
145+
icon: "https://integrations.sh/logo/chrome.com",
146146
featured: true,
147147
transport: "stdio",
148148
command: "npx",

packages/react/src/components/integration-favicon.test.tsx

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { describe, expect, it } from "@effect/vitest";
22

33
import {
4-
integrationFaviconFallbackUrl,
54
integrationFaviconSrc,
65
integrationFaviconUrl,
76
integrationInferredUrl,
@@ -10,22 +9,15 @@ import {
109
} from "./integration-favicon";
1110

1211
describe("IntegrationFavicon", () => {
13-
it("uses the integrations.sh logo proxy as the primary favicon source", () => {
12+
it("resolves favicons only through the integrations.sh logo proxy", () => {
1413
expect(integrationFaviconUrl("https://api.github.com/graphql", 20)).toBe(
1514
"https://integrations.sh/logo/github.com?sz=40",
1615
);
1716
});
1817

19-
it("falls back to the Google favicon service", () => {
20-
expect(integrationFaviconFallbackUrl("https://api.github.com/graphql", 20)).toBe(
21-
"https://www.google.com/s2/favicons?domain=github.com&sz=40",
22-
);
23-
});
24-
2518
it("does not request favicons for local URLs", () => {
2619
expect(integrationFaviconUrl("http://localhost:3000/private", 20)).toBeNull();
2720
expect(integrationFaviconUrl("http://127.0.0.1:3000/private", 20)).toBeNull();
28-
expect(integrationFaviconFallbackUrl("http://localhost:3000/private", 20)).toBeNull();
2921
});
3022

3123
it("sends only the registrable domain to the logo proxy", () => {
@@ -34,13 +26,11 @@ describe("IntegrationFavicon", () => {
3426
);
3527
});
3628

37-
it("walks the cascade from the proxy to the favicon service on failure", () => {
29+
it("falls through to the placeholder when the proxy fails", () => {
3830
const url = "https://api.github.com/graphql";
3931
const primary = integrationFaviconSrc({ url, size: 20 });
4032
expect(primary).toBe("https://integrations.sh/logo/github.com?sz=40");
41-
expect(integrationFaviconSrc({ url, size: 20, failedSrcs: [primary ?? ""] })).toBe(
42-
"https://www.google.com/s2/favicons?domain=github.com&sz=40",
43-
);
33+
expect(integrationFaviconSrc({ url, size: 20, failedSrcs: [primary ?? ""] })).toBeNull();
4434
});
4535

4636
it("uses the Executor favicon for the built-in executor integration", () => {

packages/react/src/components/integration-favicon.tsx

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,15 @@ const integrationFaviconDomain = (url: string | undefined): string | null => {
1414
};
1515

1616
// integrations.sh/logo proxies context.dev's Logo Link behind an edge cache
17-
// and is executor's single logo source; Google's favicon service remains in
18-
// the cascade as the fallback the <img> onError walks to when the proxy is
19-
// unreachable or serves nothing for the domain.
17+
// and is executor's single logo source. Fallbacks (Google's favicon service,
18+
// a letter placeholder for unknown domains) live inside the proxy, so clients
19+
// never resolve favicons against a third party directly.
2020
export function integrationFaviconUrl(url: string | undefined, size: number): string | null {
2121
const domain = integrationFaviconDomain(url);
2222
if (!domain) return null;
2323
return `https://integrations.sh/logo/${domain}?sz=${size * 2}`;
2424
}
2525

26-
export function integrationFaviconFallbackUrl(
27-
url: string | undefined,
28-
size: number,
29-
): string | null {
30-
const domain = integrationFaviconDomain(url);
31-
if (!domain) return null;
32-
return `https://www.google.com/s2/favicons?domain=${domain}&sz=${size * 2}`;
33-
}
34-
3526
export function integrationLocalIconUrl(integrationId: string | undefined): string | null {
3627
if (integrationId !== "executor") return null;
3728
return "/favicon-32.png";
@@ -181,9 +172,9 @@ export function integrationPresetIconUrl(
181172
}
182173

183174
// Resolution cascade for the rendered favicon: first non-null, non-failed of an
184-
// explicit preset icon, the bundled local icon for a known integration id, the
185-
// integrations.sh logo proxy derived from the integration URL, then the Google
186-
// favicon service as a last resort. The built-in executor integration has no preset
175+
// explicit preset icon, the bundled local icon for a known integration id, then
176+
// the integrations.sh logo proxy derived from the integration URL (which owns
177+
// its own upstream fallbacks). The built-in executor integration has no preset
187178
// icon and no URL, so it resolves ONLY through the integrationId branch: callers
188179
// that drop integrationId fall through to the neutral BoxIcon placeholder.
189180
export function integrationFaviconSrc(args: {
@@ -199,7 +190,6 @@ export function integrationFaviconSrc(args: {
199190
args.icon ?? null,
200191
integrationLocalIconUrl(args.integrationId),
201192
integrationFaviconUrl(args.url, args.size),
202-
integrationFaviconFallbackUrl(args.url, args.size),
203193
].find((candidate) => candidate !== null && !failedSrcs.includes(candidate)) ?? null
204194
);
205195
}

0 commit comments

Comments
 (0)