#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const LOGO_DEV_API_BASE = "https://api.logo.dev";
const LOGO_DEV_IMG_BASE = "https://img.logo.dev";
export const configSchema = z.object({
apiKey: z.string().describe("Your Logo.dev API key. Get one at https://logo.dev"),
});
type Config = z.infer<typeof configSchema>;
function createLogoDevServer(config: Config) {
const server = new McpServer(
{
name: "mcp-logo-dev",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
server.tool(
"search_logos",
"Search for company logos by brand name or company name. Returns a list of matching companies with their domains and logo URLs.",
{
query: z.string().describe("The brand or company name to search for (e.g., 'Google', 'Apple', 'Microsoft')"),
},
async ({ query }) => {
try {
const response = await fetch(
`${LOGO_DEV_API_BASE}/search?q=${encodeURIComponent(query)}`,
{
headers: {
Authorization: `Bearer ${config.apiKey}`,
},
}
);
if (!response.ok) {
throw new Error(`Logo.dev API error: ${response.status} ${response.statusText}`);
}
const data = await response.json();
const results = Array.isArray(data) ? data : [];
if (results.length === 0) {
return {
content: [
{
type: "text" as const,
text: `No logos found for "${query}". Try a different search term.`,
},
],
};
}
const formattedResults = results.map((company: any) => ({
name: company.name || "Unknown",
domain: company.domain || "Unknown",
logoUrl: company.domain
? `${LOGO_DEV_IMG_BASE}/${company.domain}?token=${config.apiKey}`
: null,
}));
return {
content: [
{
type: "text" as const,
text: JSON.stringify(
{
query,
count: formattedResults.length,
results: formattedResults,
},
null,
2
),
},
],
};
} catch (error) {
return {
content: [
{
type: "text" as const,
text: `Error searching logos: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
}
);
server.tool(
"get_logo_url",
"Get a direct logo image URL for a specific domain. Supports customization options like size, format, theme, and more.",
{
domain: z.string().describe("The company domain (e.g., 'google.com', 'apple.com')"),
size: z.string().optional().describe("Logo size/dimensions (optional)"),
format: z.enum(["png", "jpg", "webp"]).optional().describe("Image format (default: png)"),
theme: z.enum(["light", "dark"]).optional().describe("Logo theme variant (optional)"),
greyscale: z.boolean().optional().describe("Convert logo to greyscale (default: false)"),
},
async ({ domain, size, format, theme, greyscale }) => {
try {
const params = new URLSearchParams({
token: config.apiKey,
});
if (size) params.append("size", size);
if (format) params.append("format", format);
if (theme) params.append("theme", theme);
if (greyscale) params.append("greyscale", "true");
const logoUrl = `${LOGO_DEV_IMG_BASE}/${domain}?${params.toString()}`;
return {
content: [
{
type: "text" as const,
text: JSON.stringify(
{
domain,
logoUrl,
options: {
size: size || "default",
format: format || "png",
theme: theme || "default",
greyscale: greyscale || false,
},
},
null,
2
),
},
],
};
} catch (error) {
return {
content: [
{
type: "text" as const,
text: `Error generating logo URL: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
}
);
return server;
}
export default function createServer({ config }: { config: Config }) {
const server = createLogoDevServer(config);
return server.server;
}
async function runStdio() {
const apiKey = process.env.LOGO_DEV_API_KEY;
if (!apiKey) {
console.error("Error: LOGO_DEV_API_KEY environment variable is required for STDIO mode");
process.exit(1);
}
const server = createLogoDevServer({ apiKey });
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Logo.dev MCP server running on stdio");
}
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
if (isMainModule) {
runStdio().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});
}