{
  "markdown": "# Logo.dev MCP\n\n<!-- mcp-name: io.github.pvliesdonk/logodev-mcp -->\n\n[![CI](https://github.com/pvliesdonk/logodev-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/pvliesdonk/logodev-mcp/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/pvliesdonk/logodev-mcp/graph/badge.svg)](https://codecov.io/gh/pvliesdonk/logodev-mcp) [![PyPI](https://img.shields.io/pypi/v/logodev-mcp)](https://pypi.org/project/logodev-mcp/) [![Python](https://img.shields.io/pypi/pyversions/logodev-mcp)](https://pypi.org/project/logodev-mcp/) [![License](https://img.shields.io/github/license/pvliesdonk/logodev-mcp)](LICENSE) [![Docker](https://img.shields.io/github/v/release/pvliesdonk/logodev-mcp?label=ghcr.io&logo=docker)](https://github.com/pvliesdonk/logodev-mcp/pkgs/container/logodev-mcp) [![Docs](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://pvliesdonk.github.io/logodev-mcp/) [![llms.txt](https://img.shields.io/badge/llms.txt-available-brightgreen)](https://pvliesdonk.github.io/logodev-mcp/llms.txt) [![Template](https://img.shields.io/badge/dynamic/yaml?url=https://raw.githubusercontent.com/pvliesdonk/logodev-mcp/main/.copier-answers.yml&query=%24._commit&label=template)](https://github.com/pvliesdonk/fastmcp-server-template)\n\nLook up company logos and brand data via the logo.dev API.\n\n**[Documentation](https://pvliesdonk.github.io/logodev-mcp/)** | **[Config wizard](https://pvliesdonk.github.io/logodev-mcp/latest/configuration-generator/)** | **[PyPI](https://pypi.org/project/logodev-mcp/)** | **[Docker](https://github.com/pvliesdonk/logodev-mcp/pkgs/container/logodev-mcp)**\n\n## Features\n\n<!-- DOMAIN-START -->\n- **Logo retrieval** (`get_logo`) — fetch a company logo as an image plus URL by domain, ticker, ISIN, crypto symbol, or brand name; supports size, format, theme, greyscale, retina, and fallback options. Requires a publishable key (`pk_…`).\n- **Brand search** (`search_brands`) — resolve a brand or company name to candidate domains and logo URLs via typeahead or exact-match. Requires a secret key (`sk_…`).\n- **Company description** (`describe_company`) — return structured company data (name, description, brand colours, social links) for a domain. Requires a secret key (`sk_…`).\n- **Full brand profile** (`get_brand`) — return the complete brand profile (logo, brandmark, banners, colours, description) for a domain — a richer superset of `describe_company`. Requires a secret key (`sk_…`).\n- **Graceful degradation** — tools are registered only when the corresponding API key is present; missing-key tools are hidden from the MCP client rather than registered and failing at call time.\n<!-- DOMAIN-END -->\n\n## What you can do with it\n\n<!-- DOMAIN-START -->\nWith this server mounted in an MCP client (Claude, etc.), you can:\n\n- **Fetch a logo** — \"Get the logo for stripe.com.\" Uses `get_logo` (publishable key required).\n- **Identify a brand's domain** — \"What domain is behind the brand 'Stripe'?\" Uses `search_brands` (secret key required).\n- **Look up company info** — \"What colours does Stripe use in their branding?\" Uses `describe_company` (secret key required).\n- **Get the full brand kit** — \"Show me the complete brand profile for shopify.com.\" Uses `get_brand` (secret key required).\n- **Logo by ticker** — \"Fetch the logo for AAPL.\" Uses `get_logo` with `identifier_type=ticker` (publishable key required).\n<!-- DOMAIN-END -->\n\n<!-- ===== TEMPLATE-OWNED SECTIONS BELOW — DO NOT EDIT; CHANGES WILL BE OVERWRITTEN ON COPIER UPDATE ===== -->\n\n## Installation\n\n### From PyPI\n\n```bash\npip install logodev-mcp\n```\n\nIf you add optional extras via the `PROJECT-EXTRAS-START` / `PROJECT-EXTRAS-END` sentinels in `pyproject.toml`, document them below:\n\n<!-- DOMAIN-START -->\n<!-- List optional extras and their purpose here (e.g. `pip install logodev-mcp[embeddings]`). Kept across copier update. -->\n<!-- DOMAIN-END -->\n\n### From source\n\n```bash\ngit clone https://github.com/pvliesdonk/logodev-mcp.git\ncd logodev-mcp\nuv sync --all-extras --all-groups\n```\n\n### Docker\n\n```bash\ndocker pull ghcr.io/pvliesdonk/logodev-mcp:latest\n```\n\nA `compose.yml` ships at the repo root as a starting point — copy `.env.example` to `.env`, edit, and `docker compose up -d`.\n\nTo attach a remote Python debugger (development only — the protocol is unauthenticated), see [Remote debugging](docs/deployment/docker.md#remote-debugging).\n\n### Linux packages (.deb / .rpm)\n\nDownload `.deb` or `.rpm` packages from the [GitHub Releases](https://github.com/pvliesdonk/logodev-mcp/releases) page. Both install a hardened systemd unit; env configuration is sourced from `/etc/logodev-mcp/env` (copy from the shipped `/etc/logodev-mcp/env.example`).\n\n### Claude Desktop (.mcpb bundle)\n\nDownload the `.mcpb` bundle from the [GitHub Releases](https://github.com/pvliesdonk/logodev-mcp/releases) page and double-click to install, or run:\n\n```bash\nmcpb install logodev-mcp-<version>.mcpb\n```\n\nClaude Desktop prompts for required env vars via a GUI wizard — no manual JSON editing needed.\n\nFor manual Claude Desktop configuration and setup options, see [Claude Desktop deployment](docs/deployment/claude-desktop.md).\n\n## Quick start\n\n```bash\nlogodev-mcp serve                                # stdio transport\nlogodev-mcp serve --transport http --port 8000   # streamable HTTP\n```\n\nFor library usage (embedding the domain logic without the MCP transport), import from the `logodev_mcp` package directly — see the project's domain modules under `src/logodev_mcp/` for entry points.\n\n### Server info\n\nThe server registers a built-in `get_server_info` tool (via `fastmcp_pvl_core.register_server_info_tool`) so operators can confirm the deployed version with a single MCP call. The default response carries `server_name`, `server_version`, and `core_version`. Servers that talk to a remote upstream wire upstream version reporting inside the `DOMAIN-UPSTREAM-START` / `DOMAIN-UPSTREAM-END` sentinel in `src/logodev_mcp/server.py` — see [`CLAUDE.md`](CLAUDE.md#server-info-tool-get_server_info) for the wiring pattern.\n\n## Configuration\n\nCore environment variables shared across all `fastmcp-pvl-core`-based services:\n\n| Variable | Default | Description |\n|---|---|---|\n| `FASTMCP_LOG_LEVEL` | `INFO` | Log level for FastMCP internals and app loggers (`DEBUG` / `INFO` / `WARNING` / `ERROR`). The `-v` CLI flag overrides to `DEBUG`. |\n| `FASTMCP_ENABLE_RICH_LOGGING` | `true` | Set to `false` for plain / structured JSON log output. |\n| `LOGODEV_MCP_KV_STORE_URL` | `file:///data/state` | Persistent-state backend URL for pvl-core subsystems — `file:///path` (survives restarts), `memory://` (dev/ephemeral). |\n\nDomain-specific variables go below under [Domain configuration](#domain-configuration).\n\n## Authentication\n\nCallers authenticate via a bearer token or OIDC (mutually exclusive). See the [Authentication guide](docs/guides/authentication.md) for setup, mapped multi-subject tokens, OIDC, and troubleshooting.\n\n## Post-scaffold checklist\n\nAfter `copier copy` and `gh repo create --push`:\n\n1. **Fill in the DOMAIN blocks** in this README (Features, What you can do with it, Domain configuration, Key design decisions) and in `CLAUDE.md`.\n2. Configure GitHub secrets — see below.\n3. Install dev + docs tooling: `uv sync --all-extras --all-groups`.\n4. Install pre-commit hooks: `uv run pre-commit install`.\n5. Run the gate locally: `uv run pytest -x -q && uv run ruff check --fix . && uv run ruff format . && uv run mypy src/ tests/`.\n6. Push the first commit — CI should be green.\n\n## GitHub secrets\n\nCI workflows reference three repository secrets. Configure them via **Settings → Secrets and variables → Actions** or with `gh secret set`:\n\n| Secret | Used by | How to generate |\n|---|---|---|\n| `RELEASE_TOKEN` | `release.yml`, `copier-update.yml` | Fine-grained PAT at <https://github.com/settings/personal-access-tokens/new> with `contents: write` and `pull_requests: write` (the `copier-update` cron opens PRs). Scoped to this repo. |\n| `CODECOV_TOKEN` | `ci.yml` | <https://codecov.io> — sign in with GitHub, add the repo, copy the upload token from the repo settings page. |\n| `CLAUDE_CODE_OAUTH_TOKEN` | `claude.yml`, `claude-code-review.yml` | Run `claude setup-token` locally and paste the result. |\n\n```bash\ngh secret set RELEASE_TOKEN\ngh secret set CODECOV_TOKEN\ngh secret set CLAUDE_CODE_OAUTH_TOKEN\n```\n\n`GITHUB_TOKEN` is auto-provided — no action needed.\n\n## Local development\n\nThe PR gate (matches CI):\n\n```bash\nuv run pytest -x -q                                  # tests\nuv run ruff check --fix . && uv run ruff format .    # lint + format\nuv run mypy src/ tests/                              # type-check\n```\n\nPre-commit runs a subset of the gate on each commit; see `.pre-commit-config.yaml` for details, or [`CLAUDE.md`](CLAUDE.md) for the full Hard PR Acceptance Gates.\n\n## Troubleshooting\n\n### Moving a scaffolded project\n\n`uv sync` creates `.venv/bin/*` scripts with absolute shebangs pointing at the venv Python. If you move the repo after scaffolding (`mv /old/path /new/path`), `uv run pytest` fails with `ModuleNotFoundError: No module named 'fastmcp'` because the stale shebang resolves to a different interpreter than the venv's site-packages.\n\n**Fix:**\n\n```bash\nrm -rf .venv\nuv sync --all-extras --all-groups\n```\n\n`uv run python -m pytest` also works as a one-shot workaround (bypasses the stale entry-script shim).\n\n### `uv.lock` refresh after `copier update`\n\nWhen `copier update` introduces new dependencies (e.g. a new extra added to `pyproject.toml.jinja`), CI runs `uv sync --frozen` which fails against a stale lockfile. Run `uv lock` locally and commit the refreshed `uv.lock` alongside accepting the copier-update PR.\n\n## Links\n\n- [Documentation](https://pvliesdonk.github.io/logodev-mcp/)\n- [llms.txt](https://pvliesdonk.github.io/logodev-mcp/llms.txt)\n- [FastMCP](https://gofastmcp.com)\n- [fastmcp-pvl-core](https://pypi.org/project/fastmcp-pvl-core/)\n\n<!-- ===== TEMPLATE-OWNED SECTIONS END ===== -->\n\n## Domain configuration\n\n<!-- DOMAIN-START -->\nDomain environment variables use the `LOGODEV_MCP_` prefix:\n\n| Variable | Default | Required | Description |\n|---|---|---|---|\n| `LOGODEV_MCP_PUBLISHABLE_KEY` | — | Conditional | logo.dev publishable key (`pk_…`). Enables the `get_logo` tool. Omit to hide that tool. |\n| `LOGODEV_MCP_SECRET_KEY` | — | Conditional | logo.dev secret key (`sk_…`). Enables `search_brands`, `describe_company`, and `get_brand`. Omit to hide those tools. |\n| `LOGODEV_MCP_DETECT_PLAN` | `true` | No | Probe `describe`/`brand` entitlement on startup and hide the tools the plan does not allow. Set `false` to skip probing and register every secret-key tool. |\n| `LOGODEV_MCP_STATE_DIR` | `/data/state` | No | Directory for the cached plan-detection verdict (`entitlements.json`). |\n\nAt least one key must be set for any API tool to be registered. Both keys may be set simultaneously to enable all four tools.\n\nWhich tool each logo.dev plan unlocks (`get_logo`/`search_brands` on free, `describe_company` on any paid plan, `get_brand` on Pro/Enterprise) is documented in the [plans and capabilities matrix](docs/configuration.md#plans-and-capabilities).\n<!-- DOMAIN-END -->\n\n## Key design decisions\n\n<!-- DOMAIN-START -->\n- **Two-key gating** — `LOGODEV_MCP_PUBLISHABLE_KEY` controls `get_logo`; `LOGODEV_MCP_SECRET_KEY` controls `search_brands`, `describe_company`, and `get_brand`. Tools for a missing key are never registered, not merely guarded at call time.\n- **Plan detection hides unentitled tools** — `describe_company` needs a paid plan and `get_brand` needs Pro/Enterprise. logo.dev has no plan endpoint, so on startup the server probes each on `logo.dev` and disables the tool on a `401`/`403`. `search_brands` is never gated; any ambiguous probe result (timeout, `5xx`, `404`, `429`) fails open and keeps the tool enabled. The verdict is cached under `LOGODEV_MCP_STATE_DIR` for 7 days (re-probed on key rotation). Disable with `LOGODEV_MCP_DETECT_PLAN=false`.\n- **Domain logic stays FastMCP-free** — `src/logodev_mcp/domain.py` contains the `Service` class and `LogoDevError` with no FastMCP imports; `src/logodev_mcp/tools.py` is the sole FastMCP layer.\n- **Errors surface as strings** — `LogoDevError` raised in domain code is caught in each tool wrapper and returned as a plain text message so the MCP client sees a readable error, not a server exception.\n- **Logo tool returns URL + image** — `get_logo` returns both the CDN URL (text) and the image bytes (image content block) unless `url_only=True`, in which case only the URL string is returned.\n<!-- DOMAIN-END -->\n",
  "bytes": 12584,
  "sha": "4272a8150c75fed90c193fb7f1a71f4d75b45030f037d006f935fe6ce815a643",
  "repo_slug": "pvliesdonk/logodev-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_pvliesdonk_logodev_mcp_39425a51/readme"
}