Skip to main content
Glama
fabkho
by fabkho

Discover i18n Setup

discover
Read-only

Analyze project structure to locate locale directories, layer graphs, and translation modes before managing translations. Use it to determine where new translation keys belong.

Instructions

Describe the project: detected config, locale directories per layer with file counts and top-level namespaces, the layer graph, and the hand-maintained locales. Call this first to understand the project before reading or writing translations. The result also names the active translation mode ("provider" when the server has an env-configured LLM provider, "agent" otherwise). layerGraph answers where a new key belongs: a key used by more than one app belongs in a layer those apps share, and layerGraph.shared names those layers.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectDirNoAbsolute path to the project root. Defaults to I18N_PROJECT_DIR, then server cwd. Example: "/home/user/my-app".
includeTranslationGuidanceNoKeep the translation prose in projectConfig — glossary, translationPrompt, localeNotes, examples and context. Default: true at a terminal, false for a tool call, which omits those five fields and sets projectConfig.translationGuidanceOmitted instead. Every structural field (layerRules, protectedLocales, declaredNamespaces, orphanScan, translationMemory) is returned either way.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
appsYesApps and the layers each consumes — the consumer graph orphan scoping reads.
layersYesOne entry per locale directory, with file counts and key namespaces.
localesYesEvery locale of the project.
rootDirYesAbsolute path to the project root.
frameworkNoDetected framework, e.g. "nuxt", "laravel". Absent when nothing was detected.
layerGraphYesWhich layers are shared and which apps consume which layer — what answers where a new key belongs.
localeDirsYesEvery locale directory, one per layer, alias layers included.
defaultLocaleYesDefault locale code — the source locale every translate call falls back to.
layerRootDirsYesAbsolute root directories of every layer, which is what source scanning walks.
projectConfigNoThe declared config from i18n-kit.config.ts or .i18n-mcp.json, as written — or without its translation prose, flagged. Absent when the project has none.
fallbackLocaleYesThe framework's fallback chain. Empty when the framework declares none.
translationModeNoAdded by the server: whether it has an LLM provider configured ("provider") or hands back contexts to translate inline ("agent"). Check this before calling a translating tool.
localeFileFormatNoFormat of the locale files. Absent means the default, "json".
protectedLocalesYesCanonical codes of the locales the translate operations leave alone. Empty when none are protected.
translationModelNoAdded by the server: the configured model name. Absent in agent mode.
translationProviderNoAdded by the server: the configured provider name. Absent in agent mode.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed3 schema fields changedv1.0.6
    • addedInput schema / properties / includeTranslationGuidance
      Added value: +{
      +  "description": "Keep the translation prose in projectConfig — glossary, translationPrompt, localeNotes, examples and context. Default: true at a terminal, false for a tool call, which omits those five fields and sets projectConfig.translationGuidanceOmitted instead. Every structural field (layerRules, protectedLocales, declaredNamespaces, orphanScan, translationMemory) is returned either way.",
      +  "type": "boolean"
      +}
    • changedInput schema / properties / projectDir / description
      Previous value: -"Absolute path to the project root. Defaults to I18N_PROJECT_DIR, then server cwd."New value: +"Absolute path to the project root. Defaults to I18N_PROJECT_DIR, then server cwd. Example: \"/home/user/my-app\"."
    • changedOutput schema / (root)
      Previous value: -nullNew value: +{
      +  "$schema": "https://json-schema.org/draft/2020-12/schema",
      +  "additionalProperties": false,
      +  "properties": {
      +    "apps": {
      +      "description": "Apps and the layers each consumes — the consumer graph orphan scoping reads.",
      +      "items": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "layers": {
      +            "description": "Layer names this app consumes — its own plus every shared layer it can render.",
      +            "items": {
      +              "type": "string"
      +            },
      +            "type": "array"
      +          },
      +          "name": {
      +            "description": "App name, as orphan and status reports name it.",
      +            "type": "string"
      +          },
      +          "rootDir": {
      +            "description": "Absolute path to the app's root directory.",
      +            "type": "string"
      +          },
      +          "source": {
      +            "description": "Where the consumption edges came from when not the framework adapter: \"workspace\" from package.json inference, \"declared\" from the config. Absent means the adapter.",
      +            "enum": [
      +              "workspace",
      +              "declared"
      +            ],
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "name",
      +          "rootDir",
      +          "layers"
      +        ],
      +        "type": "object"
      +      },
      +      "type": "array"
      +    },
      +    "defaultLocale": {
      +      "description": "Default locale code — the source locale every translate call falls back to.",
      +      "type": "string"
      +    },
      +    "fallbackLocale": {
      +      "additionalProperties": {
      +        "description": "Locale codes to fall back to, in order.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "description": "The framework's fallback chain. Empty when the framework declares none.",
      +      "propertyNames": {
      +        "description": "Locale code, or \"default\".",
      +        "type": "string"
      +      },
      +      "type": "object"
      +    },
      +    "framework": {
      +      "description": "Detected framework, e.g. \"nuxt\", \"laravel\". Absent when nothing was detected.",
      +      "type": "string"
      +    },
      +    "layerGraph": {
      +      "additionalProperties": false,
      +      "description": "Which layers are shared and which apps consume which layer — what answers where a new key belongs.",
      +      "properties": {
      +        "aliases": {
      +          "additionalProperties": {
      +            "description": "The canonical layer whose locale directory it points at.",
      +            "type": "string"
      +          },
      +          "description": "Alias layer → canonical layer. Empty when no layer aliases another.",
      +          "propertyNames": {
      +            "description": "Alias layer name.",
      +            "type": "string"
      +          },
      +          "type": "object"
      +        },
      +        "canonical": {
      +          "description": "Alias-free layer names, in config order.",
      +          "items": {
      +            "type": "string"
      +          },
      +          "type": "array"
      +        },
      +        "consumers": {
      +          "additionalProperties": {
      +            "description": "Apps that consume it; empty when nothing does.",
      +            "items": {
      +              "type": "string"
      +            },
      +            "type": "array"
      +          },
      +          "description": "Every canonical layer is a key, so \"no consumers\" and \"not computed\" cannot be confused.",
      +          "propertyNames": {
      +            "description": "Canonical layer name.",
      +            "type": "string"
      +          },
      +          "type": "object"
      +        },
      +        "shared": {
      +          "description": "Canonical layers more than one app consumes — where a key used by several apps belongs.",
      +          "items": {
      +            "type": "string"
      +          },
      +          "type": "array"
      +        }
      +      },
      +      "required": [
      +        "canonical",
      +        "shared",
      +        "aliases",
      +        "consumers"
      +      ],
      +      "type": "object"
      +    },
      +    "layerRootDirs": {
      +      "description": "Absolute root directories of every layer, which is what source scanning walks.",
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    },
      +    "layers": {
      +      "description": "One entry per locale directory, with file counts and key namespaces.",
      +      "items": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "aliasOf": {
      +            "description": "The layer this one aliases. Absent for a layer of its own.",
      +            "type": "string"
      +          },
      +          "fileCount": {
      +            "description": "Number of locale files in the directory. 0 for an alias layer.",
      +            "maximum": 9007199254740991,
      +            "minimum": -9007199254740991,
      +            "type": "integer"
      +          },
      +          "layer": {
      +            "description": "Layer name. Pass this as the layer argument of any other call.",
      +            "type": "string"
      +          },
      +          "namespaces": {
      +            "description": "Namespace file names of one locale directory, for a namespaced layout such as Laravel. Absent for a flat layout.",
      +            "items": {
      +              "type": "string"
      +            },
      +            "type": "array"
      +          },
      +          "path": {
      +            "description": "Absolute path to the locale directory.",
      +            "type": "string"
      +          },
      +          "topLevelKeys": {
      +            "description": "Top-level keys of one locale file, for a flat layout. Absent for a namespaced layout.",
      +            "items": {
      +              "type": "string"
      +            },
      +            "type": "array"
      +          }
      +        },
      +        "required": [
      +          "layer",
      +          "path",
      +          "fileCount"
      +        ],
      +        "type": "object"
      +      },
      +      "type": "array"
      +    },
      +    "localeDirs": {
      +      "description": "Every locale directory, one per layer, alias layers included.",
      +      "items": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "aliasOf": {
      +            "description": "The layer this one points at, when the directory is an alias of another layer's. Absent for a layer of its own.",
      +            "type": "string"
      +          },
      +          "layer": {
      +            "description": "Layer name, e.g. \"root\", \"app-admin\".",
      +            "type": "string"
      +          },
      +          "layerRootDir": {
      +            "description": "Absolute path to that layer's root directory.",
      +            "type": "string"
      +          },
      +          "path": {
      +            "description": "Absolute path to the locale directory.",
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "path",
      +          "layer",
      +          "layerRootDir"
      +        ],
      +        "type": "object"
      +      },
      +      "type": "array"
      +    },
      +    "localeFileFormat": {
      +      "description": "Format of the locale files. Absent means the default, \"json\".",
      +      "enum": [
      +        "json",
      +        "php-array",
      +        "yaml"
      +      ],
      +      "type": "string"
      +    },
      +    "locales": {
      +      "description": "Every locale of the project.",
      +      "items": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "code": {
      +            "description": "Locale code used in URLs and as the identifier everywhere else, e.g. \"de\".",
      +            "type": "string"
      +          },
      +          "file": {
      +            "description": "Locale file name, e.g. \"de-DE.json\". Absent for directory-per-locale layouts.",
      +            "type": "string"
      +          },
      +          "language": {
      +            "description": "BCP-47 language tag, e.g. \"de-DE\".",
      +            "type": "string"
      +          },
      +          "name": {
      +            "description": "Human-readable language name. Absent unless the framework config names one.",
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "code",
      +          "language"
      +        ],
      +        "type": "object"
      +      },
      +      "type": "array"
      +    },
      +    "projectConfig": {
      +      "anyOf": [
      +        {
      +          "additionalProperties": false,
      +          "properties": {
      +            "$schema": {
      +              "description": "Path or URL to the JSON schema for IDE autocompletion.",
      +              "type": "string"
      +            },
      +            "apps": {
      +              "description": "Which app consumes which layers — the consumer graph behind app-scoped orphan detection, misplaced-usage reports and unconsumed-layer warnings. Declaring it overrides both framework discovery and workspace inference.",
      +              "items": {
      +                "additionalProperties": false,
      +                "properties": {
      +                  "layers": {
      +                    "description": "Layer names this app can render — its own layer plus every shared layer it consumes.",
      +                    "items": {
      +                      "minLength": 1,
      +                      "type": "string"
      +                    },
      +                    "type": "array"
      +                  },
      +                  "name": {
      +                    "description": "App name, as it appears in orphan and status reports (e.g., 'shop', '@acme/admin').",
      +                    "minLength": 1,
      +                    "type": "string"
      +                  }
      +                },
      +                "required": [
      +                  "name",
      +                  "layers"
      +                ],
      +                "type": "object"
      +              },
      +              "type": "array"
      +            },
      +            "consumerGraph": {
      +              "description": "Whether to infer the consumer graph from the package manager workspace and package.json dependencies when the framework provides no app information: 'auto' (default) infers it, 'off' keeps every layer in one app.",
      +              "enum": [
      +                "auto",
      +                "off"
      +              ],
      +              "type": "string"
      +            },
      +            "context": {
      +              "description": "Free-form project background for the agent — business domain, user base, brand voice, anything that helps the agent understand the project.",
      +              "type": "string"
      +            },
      +            "declaredNamespaces": {
      +              "description": "Namespaces whose keys exist by contract rather than by a call site — sent by a backend, keyed by runtime data, built from a registry. Their keys are never reported as orphans and never written by check --write, in every layer. Each declaration is reported with the keys it matches, so one that matches nothing is visible as stale.",
      +              "items": {
      +                "additionalProperties": false,
      +                "properties": {
      +                  "pattern": {
      +                    "description": "Key pattern the declaration covers (e.g., \"views.defaults.**\"). Use * to match a single key segment, ** to match any depth.",
      +                    "minLength": 1,
      +                    "type": "string"
      +                  },
      +                  "reason": {
      +                    "description": "What keeps these keys alive, named so the report can say why they are protected (e.g., \"sent by bookings-api as name_key\").",
      +                    "minLength": 1,
      +                    "type": "string"
      +                  }
      +                },
      +                "required": [
      +                  "pattern",
      +                  "reason"
      +                ],
      +                "type": "object"
      +              },
      +              "type": "array"
      +            },
      +            "defaultLocale": {
      +              "description": "Default locale code. Required for generic adapter activation.",
      +              "minLength": 1,
      +              "type": "string"
      +            },
      +            "examples": {
      +              "description": "Few-shot translation examples that demonstrate the project's style. The agent uses these as reference when generating translations.",
      +              "items": {
      +                "additionalProperties": {
      +                  "type": "string"
      +                },
      +                "description": "A single translation example. 'key' holds the dot-path translation key (e.g., 'common.actions.save'), 'note' an optional style comment, and every other property is a locale code (e.g., 'de-DE', 'en-US') mapped to its translated value.",
      +                "properties": {
      +                  "key": {
      +                    "description": "The dot-path translation key this example demonstrates (e.g., 'common.actions.save').",
      +                    "type": "string"
      +                  },
      +                  "note": {
      +                    "description": "Optional style comment shown alongside the example.",
      +                    "type": "string"
      +                  }
      +                },
      +                "required": [
      +                  "key"
      +                ],
      +                "type": "object"
      +              },
      +              "type": "array"
      +            },
      +            "framework": {
      +              "description": "Force framework detection instead of auto-detecting from project structure. Any registered adapter name is accepted — the suggestions are the adapters that ship today, not the only permitted values. 'vue' is accepted as an alias for 'generic', which resolves Vue projects: declare localeDirs when the locale files are not in a conventional directory.",
      +              "type": "string"
      +            },
      +            "glossary": {
      +              "additionalProperties": {
      +                "type": "string"
      +              },
      +              "description": "Term dictionary for consistent translations. Keys are source terms, values describe the required translation or usage note.",
      +              "propertyNames": {
      +                "type": "string"
      +              },
      +              "type": "object"
      +            },
      +            "layerRules": {
      +              "description": "Rules that help the agent decide which layer a new translation key belongs to. The agent interprets the natural-language 'when' field.",
      +              "items": {
      +                "additionalProperties": false,
      +                "properties": {
      +                  "description": {
      +                    "description": "What this layer contains — key namespaces, domain scope, etc.",
      +                    "type": "string"
      +                  },
      +                  "layer": {
      +                    "description": "Layer name (e.g., 'root', 'app-admin', 'app-shop', 'lang').",
      +                    "type": "string"
      +                  },
      +                  "when": {
      +                    "description": "Natural-language rule describing when a key should go in this layer.",
      +                    "type": "string"
      +                  }
      +                },
      +                "required": [
      +                  "layer",
      +                  "description",
      +                  "when"
      +                ],
      +                "type": "object"
      +              },
      +              "type": "array"
      +            },
      +            "localeDirs": {
      +              "description": "Locale directories for the generic adapter. Each entry is a path string (layer defaults to 'default') or an object with 'path' and 'layer' properties.",
      +              "items": {
      +                "anyOf": [
      +                  {
      +                    "description": "Relative path to a locale directory. Layer name defaults to 'default'.",
      +                    "minLength": 1,
      +                    "type": "string"
      +                  },
      +                  {
      +                    "additionalProperties": false,
      +                    "properties": {
      +                      "layer": {
      +                        "description": "Layer name for this locale directory.",
      +                        "minLength": 1,
      +                        "type": "string"
      +                      },
      +                      "path": {
      +                        "description": "Relative path to a locale directory.",
      +                        "minLength": 1,
      +                        "type": "string"
      +                      }
      +                    },
      +                    "required": [
      +                      "path",
      +                      "layer"
      +                    ],
      +                    "type": "object"
      +                  }
      +                ]
      +              },
      +              "type": "array"
      +            },
      +            "localeFileFormat": {
      +              "description": "Override the auto-detected locale file format. 'yaml' covers both .yaml and .yml files. Useful when several formats exist in one project or auto-detection picks wrong.",
      +              "enum": [
      +                "json",
      +                "php-array",
      +                "yaml"
      +              ],
      +              "type": "string"
      +            },
      +            "localeNotes": {
      +              "additionalProperties": {
      +                "type": "string"
      +              },
      +              "description": "Per-locale context included in translation prompts. Each key is matched against a locale's code, language tag, or file name (with or without extension) — whichever the project uses, e.g. 'de', 'de-DE' or 'de-DE.json'. Values describe register, regional conventions, or other locale-specific guidance. A key that matches no locale is reported and ignored.",
      +              "propertyNames": {
      +                "type": "string"
      +              },
      +              "type": "object"
      +            },
      +            "locales": {
      +              "description": "Explicit list of locale codes to operate on. If absent, locales are auto-discovered from files on disk.",
      +              "items": {
      +                "minLength": 1,
      +                "type": "string"
      +              },
      +              "type": "array"
      +            },
      +            "orphanScan": {
      +              "additionalProperties": {
      +                "additionalProperties": {},
      +                "properties": {
      +                  "ignorePatterns": {
      +                    "description": "Glob patterns for translation keys to exclude from orphan detection in this layer (e.g., \"common.datetime.months.*\"). Use * to match a single key segment, ** to match any depth. For keys that exist by contract elsewhere, declare them under declaredNamespaces instead, which records why and reports what each pattern protects.",
      +                    "items": {
      +                      "type": "string"
      +                    },
      +                    "type": "array"
      +                  }
      +                },
      +                "type": "object"
      +              },
      +              "description": "Per-layer configuration for orphan key detection. Map each layer name to its settings. Scan directories are automatically determined from each layer's root directory.",
      +              "propertyNames": {
      +                "type": "string"
      +              },
      +              "type": "object"
      +            },
      +            "protectedLocales": {
      +              "description": "Human-maintained locales excluded from automatic translation. Entries may be any locale ref (code, language tag, or file name); entries that do not match a known locale are ignored with a warning. Explicitly naming a protected locale in targetLocales overrides the protection (with a warning).",
      +              "items": {
      +                "minLength": 1,
      +                "type": "string"
      +              },
      +              "type": "array"
      +            },
      +            "providerBaseUrl": {
      +              "description": "Base URL for the LLM provider — gateways, self-hosted model servers and corporate proxies that speak the provider's own protocol. Overrides the endpoint only, not the request shape or auth header. Overridden by the I18N_BASE_URL environment variable and by --baseUrl.",
      +              "minLength": 1,
      +              "type": "string"
      +            },
      +            "reportOutput": {
      +              "anyOf": [
      +                {
      +                  "const": true,
      +                  "description": "Set to true to write reports to the default '.i18n-reports/' directory.",
      +                  "type": "boolean"
      +                },
      +                {
      +                  "description": "Custom directory path (relative to project root) where diagnostic tool reports are written.",
      +                  "minLength": 1,
      +                  "type": "string"
      +                }
      +              ],
      +              "description": "Enable file output for diagnostic tools (get_missing_translations, search_translations, find_orphan_keys, find_undefined_keys). When set, each tool writes its full JSON report to <reportOutput>/<toolName>.json and returns only a summary in the MCP response. Set to true for the default '.i18n-reports/' directory, or a string for a custom path."
      +            },
      +            "samplingPreferences": {
      +              "deprecated": true,
      +              "description": "Deprecated and ignored — MCP sampling was removed. Still accepted so existing config files keep validating. Configure a provider instead (e.g., I18N_PROVIDER/I18N_MODEL)."
      +            },
      +            "translationMemory": {
      +              "description": "Write a translation memory to '.i18n-kit.lock.json' at the project root, recording per layer, key and target locale a hash of the source text each translation was made from, so later runs can tell targets that are still current from ones whose source has changed since. On by default: the first translate run writes the file, which belongs in version control like any other lockfile. Set to false to disable it — nothing is then read or written, and an existing file is left untouched.",
      +              "type": "boolean"
      +            },
      +            "translationPrompt": {
      +              "description": "System prompt prepended to all translation requests (provider mode and agent-mode fallback contexts). Sets tone, style, and constraints.",
      +              "type": "string"
      +            }
      +          },
      +          "type": "object"
      +        },
      +        {
      +          "additionalProperties": false,
      +          "properties": {
      +            "$schema": {
      +              "description": "Path or URL to the JSON schema for IDE autocompletion.",
      +              "type": "string"
      +            },
      +            "apps": {
      +              "description": "Which app consumes which layers — the consumer graph behind app-scoped orphan detection, misplaced-usage reports and unconsumed-layer warnings. Declaring it overrides both framework discovery and workspace inference.",
      +              "items": {
      +                "additionalProperties": false,
      +                "properties": {
      +                  "layers": {
      +                    "description": "Layer names this app can render — its own layer plus every shared layer it consumes.",
      +                    "items": {
      +                      "minLength": 1,
      +                      "type": "string"
      +                    },
      +                    "type": "array"
      +                  },
      +                  "name": {
      +                    "description": "App name, as it appears in orphan and status reports (e.g., 'shop', '@acme/admin').",
      +                    "minLength": 1,
      +                    "type": "string"
      +                  }
      +                },
      +                "required": [
      +                  "name",
      +                  "layers"
      +                ],
      +                "type": "object"
      +              },
      +              "type": "array"
      +            },
      +            "consumerGraph": {
      +              "description": "Whether to infer the consumer graph from the package manager workspace and package.json dependencies when the framework provides no app information: 'auto' (default) infers it, 'off' keeps every layer in one app.",
      +              "enum": [
      +                "auto",
      +                "off"
      +              ],
      +              "type": "string"
      +            },
      +            "declaredNamespaces": {
      +              "description": "Namespaces whose keys exist by contract rather than by a call site — sent by a backend, keyed by runtime data, built from a registry. Their keys are never reported as orphans and never written by check --write, in every layer. Each declaration is reported with the keys it matches, so one that matches nothing is visible as stale.",
      +              "items": {
      +                "additionalProperties": false,
      +                "properties": {
      +                  "pattern": {
      +                    "description": "Key pattern the declaration covers (e.g., \"views.defaults.**\"). Use * to match a single key segment, ** to match any depth.",
      +                    "minLength": 1,
      +                    "type": "string"
      +                  },
      +                  "reason": {
      +                    "description": "What keeps these keys alive, named so the report can say why they are protected (e.g., \"sent by bookings-api as name_key\").",
      +                    "minLength": 1,
      +                    "type": "string"
      +                  }
      +                },
      +                "required": [
      +                  "pattern",
      +                  "reason"
      +                ],
      +                "type": "object"
      +              },
      +              "type": "array"
      +            },
      +            "defaultLocale": {
      +              "description": "Default locale code. Required for generic adapter activation.",
      +              "minLength": 1,
      +              "type": "string"
      +            },
      +            "framework": {
      +              "description": "Force framework detection instead of auto-detecting from project structure. Any registered adapter name is accepted — the suggestions are the adapters that ship today, not the only permitted values. 'vue' is accepted as an alias for 'generic', which resolves Vue projects: declare localeDirs when the locale files are not in a conventional directory.",
      +              "type": "string"
      +            },
      +            "layerRules": {
      +              "description": "Rules that help the agent decide which layer a new translation key belongs to. The agent interprets the natural-language 'when' field.",
      +              "items": {
      +                "additionalProperties": false,
      +                "properties": {
      +                  "description": {
      +                    "description": "What this layer contains — key namespaces, domain scope, etc.",
      +                    "type": "string"
      +                  },
      +                  "layer": {
      +                    "description": "Layer name (e.g., 'root', 'app-admin', 'app-shop', 'lang').",
      +                    "type": "string"
      +                  },
      +                  "when": {
      +                    "description": "Natural-language rule describing when a key should go in this layer.",
      +                    "type": "string"
      +                  }
      +                },
      +                "required": [
      +                  "layer",
      +                  "description",
      +                  "when"
      +                ],
      +                "type": "object"
      +              },
      +              "type": "array"
      +            },
      +            "localeDirs": {
      +              "description": "Locale directories for the generic adapter. Each entry is a path string (layer defaults to 'default') or an object with 'path' and 'layer' properties.",
      +              "items": {
      +                "anyOf": [
      +                  {
      +                    "description": "Relative path to a locale directory. Layer name defaults to 'default'.",
      +                    "minLength": 1,
      +                    "type": "string"
      +                  },
      +                  {
      +                    "additionalProperties": false,
      +                    "properties": {
      +                      "layer": {
      +                        "description": "Layer name for this locale directory.",
      +                        "minLength": 1,
      +                        "type": "string"
      +                      },
      +                      "path": {
      +                        "description": "Relative path to a locale directory.",
      +                        "minLength": 1,
      +                        "type": "string"
      +                      }
      +                    },
      +                    "required": [
      +                      "path",
      +                      "layer"
      +                    ],
      +                    "type": "object"
      +                  }
      +                ]
      +              },
      +              "type": "array"
      +            },
      +            "localeFileFormat": {
      +              "description": "Override the auto-detected locale file format. 'yaml' covers both .yaml and .yml files. Useful when several formats exist in one project or auto-detection picks wrong.",
      +              "enum": [
      +                "json",
      +                "php-array",
      +                "yaml"
      +              ],
      +              "type": "string"
      +            },
      +            "locales": {
      +              "description": "Explicit list of locale codes to operate on. If absent, locales are auto-discovered from files on disk.",
      +              "items": {
      +                "minLength": 1,
      +                "type": "string"
      +              },
      +              "type": "array"
      +            },
      +            "orphanScan": {
      +              "additionalProperties": {
      +                "additionalProperties": {},
      +                "properties": {
      +                  "ignorePatterns": {
      +                    "description": "Glob patterns for translation keys to exclude from orphan detection in this layer (e.g., \"common.datetime.months.*\"). Use * to match a single key segment, ** to match any depth. For keys that exist by contract elsewhere, declare them under declaredNamespaces instead, which records why and reports what each pattern protects.",
      +                    "items": {
      +                      "type": "string"
      +                    },
      +                    "type": "array"
      +                  }
      +                },
      +                "type": "object"
      +              },
      +              "description": "Per-layer configuration for orphan key detection. Map each layer name to its settings. Scan directories are automatically determined from each layer's root directory.",
      +              "propertyNames": {
      +                "type": "string"
      +              },
      +              "type": "object"
      +            },
      +            "protectedLocales": {
      +              "description": "Human-maintained locales excluded from automatic translation. Entries may be any locale ref (code, language tag, or file name); entries that do not match a known locale are ignored with a warning. Explicitly naming a protected locale in targetLocales overrides the protection (with a warning).",
      +              "items": {
      +                "minLength": 1,
      +                "type": "string"
      +              },
      +              "type": "array"
      +            },
      +            "providerBaseUrl": {
      +              "description": "Base URL for the LLM provider — gateways, self-hosted model servers and corporate proxies that speak the provider's own protocol. Overrides the endpoint only, not the request shape or auth header. Overridden by the I18N_BASE_URL environment variable and by --baseUrl.",
      +              "minLength": 1,
      +              "type": "string"
      +            },
      +            "reportOutput": {
      +              "anyOf": [
      +                {
      +                  "const": true,
      +                  "description": "Set to true to write reports to the default '.i18n-reports/' directory.",
      +                  "type": "boolean"
      +                },
      +                {
      +                  "description": "Custom directory path (relative to project root) where diagnostic tool reports are written.",
      +                  "minLength": 1,
      +                  "type": "string"
      +                }
      +              ],
      +              "description": "Enable file output for diagnostic tools (get_missing_translations, search_translations, find_orphan_keys, find_undefined_keys). When set, each tool writes its full JSON report to <reportOutput>/<toolName>.json and returns only a summary in the MCP response. Set to true for the default '.i18n-reports/' directory, or a string for a custom path."
      +            },
      +            "samplingPreferences": {
      +              "deprecated": true,
      +              "description": "Deprecated and ignored — MCP sampling was removed. Still accepted so existing config files keep validating. Configure a provider instead (e.g., I18N_PROVIDER/I18N_MODEL)."
      +            },
      +            "translationGuidanceOmitted": {
      +              "const": true,
      +              "description": "The translation prose (context, glossary, translationPrompt, localeNotes, examples) exists but was left out. Ask with includeTranslationGuidance for it.",
      +              "type": "boolean"
      +            },
      +            "translationMemory": {
      +              "description": "Write a translation memory to '.i18n-kit.lock.json' at the project root, recording per layer, key and target locale a hash of the source text each translation was made from, so later runs can tell targets that are still current from ones whose source has changed since. On by default: the first translate run writes the file, which belongs in version control like any other lockfile. Set to false to disable it — nothing is then read or written, and an existing file is left untouched.",
      +              "type": "boolean"
      +            }
      +          },
      +          "required": [
      +            "translationGuidanceOmitted"
      +          ],
      +          "type": "object"
      +        }
      +      ],
      +      "description": "The declared config from i18n-kit.config.ts or .i18n-mcp.json, as written — or without its translation prose, flagged. Absent when the project has none."
      +    },
      +    "protectedLocales": {
      +      "description": "Canonical codes of the locales the translate operations leave alone. Empty when none are protected.",
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    },
      +    "rootDir": {
      +      "description": "Absolute path to the project root.",
      +      "type": "string"
      +    },
      +    "translationMode": {
      +      "description": "Added by the server: whether it has an LLM provider configured (\"provider\") or hands back contexts to translate inline (\"agent\"). Check this before calling a translating tool.",
      +      "enum": [
      +        "provider",
      +        "agent"
      +      ],
      +      "type": "string"
      +    },
      +    "translationModel": {
      +      "description": "Added by the server: the configured model name. Absent in agent mode.",
      +      "type": "string"
      +    },
      +    "translationProvider": {
      +      "description": "Added by the server: the configured provider name. Absent in agent mode.",
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "rootDir",
      +    "defaultLocale",
      +    "fallbackLocale",
      +    "locales",
      +    "localeDirs",
      +    "layerRootDirs",
      +    "apps",
      +    "protectedLocales",
      +    "layers",
      +    "layerGraph"
      +  ],
      +  "type": "object"
      +}
  2. Changed2 schema fields changedv1.0.3
    • changedInput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • changedInput schema / properties / projectDir / description
      Previous value: -"Absolute path to the project root. Defaults to server cwd."New value: +"Absolute path to the project root. Defaults to I18N_PROJECT_DIR, then server cwd."
  3. First observedv1.0.0

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered. The description adds genuine behavioral context beyond that: the returned translation mode ("provider" vs "agent") and the meaning of layerGraph.shared. It does not, however, disclose anything about cost, latency, or side effects of scanning a project.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the resource description, then usage, then the layerGraph semantics. The final layerGraph sentence is dense and slightly redundant with the earlier layer-graph mention, but every sentence carries information an agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present and annotations covering the read-only profile, the description only needs to orient the agent and route it — which it does by marking this as the entry-point call and explaining the layer semantics. Nothing needed to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and both parameters (projectDir default resolution, includeTranslationGuidance terminal-vs-tool-call behavior) are already documented in the schema. The description adds no parameter-level detail beyond that, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (describe/discover) and enumerates exactly what is returned: detected config, locale directories per layer with file counts and namespaces, layer graph, and hand-maintained locales. This clearly distinguishes it from the read/write siblings like get_translations or list_namespaces.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"Call this first to understand the project before reading or writing translations" gives an explicit ordering directive relative to all sibling tools. It further explains the concrete decision layerGraph supports (where a new key belongs, shared layers for multi-app keys), which is actionable when-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.