---
title: Per-Environment App Icons, Generated by Your Agent
description: The Favicon Tools MCP returns dev, staging, and production icon variants by default, plus a stage-aware install snippet so the right icon renders per environment. No more deploying to the wrong tab.
canonical: https://favicontools.com/blog/favicon-mcp-per-environment
---

# Per-Environment App Icons, Generated by Your Agent

When local, staging, and production all show the same tab icon, it is only a matter of time before someone runs the wrong script in the wrong tab. The Favicon Tools MCP fixes that at the source: it returns dev, staging, and production icons by default.

## What each stage gets

| Stage | Icon |
| --- | --- |
| Production | Clean brand icon, no overlay |
| Staging | Same glyph with a corner "S" badge |
| Development | Same glyph with a corner "D" badge |

## One call returns the whole set

Point your agent at the MCP server and call generate_iconset. With the defaults, a single source produces the full per-stage kit plus matching manifests.

```jsonc
// .mcp.json: add the server
{
  "mcpServers": {
    "favicontools": {
      "type": "http",
      "url": "https://favicontools.com/api/mcp"
    }
  }
}
```

```ts
// then the agent calls:
generate_iconset({ source: "noto:fox" })
// → clean production icon
// → staging-* variants (corner "S")
// → dev-* variants (corner "D")
// → manifest.json + staging-manifest.json + dev-manifest.json
// → a stage-aware <head> snippet
```

> **Want a single production-only icon?:** Pass variantType: "none". The default leans toward full coverage so the per-stage safety net is opt-out, not opt-in.

## The install snippet is already conditional

You don't wire the switching logic yourself. The returned snippet renders the right icon and manifest off your environment variable. Drop it into your root layout:

```tsx
// app/layout.tsx (Next.js)
const isStaging = process.env.NEXT_PUBLIC_APP_ENV === "staging";
const isDev = process.env.NODE_ENV === "development";

// pick the filename prefix + manifest per stage
const prefix = isDev ? "/dev-" : isStaging ? "/staging-" : "/";
const manifest = isDev
  ? "/dev-manifest.json"
  : isStaging
    ? "/staging-manifest.json"
    : "/manifest.json";

// <link rel="icon" href={`${prefix}favicon.ico`} />
// <link rel="icon" sizes="32x32" href={`${prefix}favicon-32x32.png`} />
// <link rel="manifest" href={manifest} />
```

The same pattern ships for React, Vue, Svelte, and Astro. It keys off a filename prefix (/, /staging-, /dev-) so production stays clean and the other stages get their badge automatically.

## A deploy safety net

The tab bar becomes a glanceable indicator of which environment you're looking at. A badged icon is the pause that stops a destructive command landing on production.

Because the variants and the conditional snippet come back in the same response, you get multi-environment hygiene for free: no second generation pass, no hand-edited link tags.

## Before you ship

1. **Check the files are there** — Confirm staging-* and dev-* files (and their manifests) are in your static dir.
2. **Set the env var per deployment** — The snippet reads NEXT_PUBLIC_APP_ENV or your equivalent. Set it on every environment.
3. **Suffix each manifest name** — For example "Acme", "Acme (Staging)", "Acme (Dev)".
4. **Verify each environment** — Run /verify against each environment URL to confirm the right icon is live.

> **Never deploy to the wrong tab again:** Generate a per-stage icon set now, or read the deeper playbook on environment-specific favicons.
