Skip to main content
Glama

Scan a site's composer files for a core upgrade

scan_composer
Read-onlyIdempotent

Which of a site's drupal/* packages need a new release for a target core, and which have none? Send composer.lock and composer.json once; do not query one package at a time. The answer is the release scan for every package, plus extra.patches as check_patches items. Constraints are read by composer's own semver library.

  • composer_lock: the whole file, or its path to get the upload command. A real lock does not fit a call. With the path, the files go from disk to the api by curl and only the plan comes back. composer_json: adds constraints and extra.patches. Sent as text with a lock path, the command lists every patch to send. target_core: 11.4 or 11.4.5. Empty scans against the installed core (target_is_installed): what can be updated without a core upgrade.

  • installed_unknown: the release data does not have the installed version; the row offers no older release. installed_from_tag: the project's git tag answered instead. Neither is a finding about the site.

  • A sub-module has no releases of its own; drupal.org packages it as a metapackage built from its project's release. Its row says submodule_of and has that project's answer. The lock's type, require and extra.drupal.datestamp fields pair a metapackage with its project.

  • rows, problems first. no_release: no published release supports the target; latest_any and dev_branch say what exists. update: a compatible release the site does not have; latest, latest_core. unknown: not a drupal.org project in the bundle; note says where its releases are. current: installed_supports and nothing newer needed; such a row has package, installed and installed_supports only. counts covers every package even when rows are cut.

  • candidates, on every row that is not current: the newest three releases, each with version, core constraint, date and supports_target. project_profile adds installs and branches to the same three.

  • patches: one item per extra.patches entry with the installed version filled in. Pass them to check_patches after composer update; local paths need the file text. A patch on a package outside drupal/ gets no item and no row; outside_drupal lists it.

  • patches:true answers with plan instead: every patch judged against the release its package would install for target_core. plan.counts: the verdict tally. plan.package_counts: the scan tallies. plan.no_release: the packages that block the target. plan.patches: one row per patch needing a decision (conflicts, unknown, merged). plan.applying: the number that still apply. The scan rows are not in a plan; call again without patches for them.

  • A patch whose package has no release for the target is judged against the branch when the lock installs a dev version. Otherwise it is unknown, with the reason in note. plan.warnings: a package is in no_release because of something the site controls. Each warning gives the requirement to change: its own constraint, or its minimum stability. A row's project for reroll_patch is its package without the drupal/ prefix.

  • A plan never has a re-rolled diff. plan.next_step points at reroll_patch, one call per patch, when any patch no longer applies.

  • bundle_date: when the release data was published. A release after it is invisible here. Confirm a no_release with composer show or drupal.org before acting.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
patchesNoJudge the site's patches in the same call: each one against the release its package would install for target_core. Returns a verdict per patch instead of items to check yourself.
patch_filesNoThe text of every patch listed in extra.patches, keyed by the source as written: a path (patches/x.patch) or its base name, or the URL. Only needed with patches:true. Fetch a URL patch yourself and send its text under that URL. For a merge request .patch URL, send its .diff form under a second key too.
target_coreNoCore version to move to, e.g. 11.4 or 11.4.5. Leave empty to scan against the core the site already runs: which packages have a newer release for it.
composer_jsonNoThe contents of composer.json (require, require-dev, extra.patches). Send the text; this server cannot read your filesystem. Optional; without it there are no constraints and no patches.
composer_lockYesThe text of composer.lock. A real lock is too big to paste: send its path (composer.lock) instead. The answer is then the curl command that uploads the files from the site root.
installed_coreNoWhat each installed release requires of core, keyed by composer name. Read it from the site's own vendor directory: composer show -f json <pkg>, or the drupal/core entry of its composer.json. Send it and the installed release is judged by what it declares.

Schema Changelog

Changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. Changed2 schema fields changed
    • changedInput schema / properties / composer_json / description
      Previous value: -"The text of composer.json, not a path (require, require-dev, extra.patches). Optional; without it there are no constraints and no patches."New value: +"The contents of composer.json (require, require-dev, extra.patches). Send the text; this server cannot read your filesystem. Optional; without it there are no constraints and no patches."
    • changedInput schema / properties / composer_lock / description
      Previous value: -"The text of composer.lock, not a path."New value: +"The text of composer.lock. A real lock is too big to paste: send its path (composer.lock) instead. The answer is then the curl command that uploads the files from the site root."
  2. Changed1 schema field changed
    • changedInput schema / properties / patch_files / description
      Previous value: -"The text of each local patch file listed in extra.patches, keyed by the path as written (patches/x.patch) or its base name. Only needed with patches:true. URL patches need nothing."New value: +"The text of every patch listed in extra.patches, keyed by the source as written: a path (patches/x.patch) or its base name, or the URL. Only needed with patches:true. Fetch a URL patch yourself and send its text under that URL. For a merge request .patch URL, send its .diff form under a second key too."
  3. Changed1 schema field changed
    • changedInput schema / properties / composer_lock / description
      Previous value: -"The text of composer.lock, not a path, or the slim form {\"packages\":[{\"name\":…,\"version\":…}]} with only drupal/* packages."New value: +"The text of composer.lock, not a path."
  4. Changed4 schema fields changed
    • changedInput schema / properties / composer_json / description
      Previous value: -"The composer.json contents as text, not a path (require, require-dev, extra.patches). Optional: without it there are no constraints and no patches."New value: +"The text of composer.json, not a path (require, require-dev, extra.patches). Optional; without it there are no constraints and no patches."
    • changedInput schema / properties / composer_lock / description
      Previous value: -"The composer.lock contents as text (not a path), or the slim form {\"packages\":[{\"name\":…,\"version\":…}]} with only drupal/* packages (a few KB instead of hundreds)."New value: +"The text of composer.lock, not a path, or the slim form {\"packages\":[{\"name\":…,\"version\":…}]} with only drupal/* packages."
    • changedInput schema / properties / installed_core / description
      Previous value: -"What each installed release requires of core, keyed by composer name, read from the site's own vendor directory (composer show -f json <pkg>, or the drupal/core entry of its composer.json). The release data here can be months behind a project; a site cannot. Send it and the installed release is judged as it actually is."New value: +"What each installed release requires of core, keyed by composer name. Read it from the site's own vendor directory: composer show -f json <pkg>, or the drupal/core entry of its composer.json. Send it and the installed release is judged by what it declares."
    • changedInput schema / properties / patch_files / description
      Previous value: -"Text of each local patch file named in extra.patches, keyed by the path as written (patchs/x.patch) or its base name. Only needed with patches:true; URL patches need nothing."New value: +"The text of each local patch file listed in extra.patches, keyed by the path as written (patches/x.patch) or its base name. Only needed with patches:true. URL patches need nothing."
  5. Changed1 schema field changed
    • addedInput schema / properties / installed_core
      Added value: +{
      +  "additionalProperties": {
      +    "type": "string"
      +  },
      +  "description": "What each installed release requires of core, keyed by composer name, read from the site's own vendor directory (composer show -f json <pkg>, or the drupal/core entry of its composer.json). The release data here can be months behind a project; a site cannot. Send it and the installed release is judged as it actually is.",
      +  "type": "object"
      +}
  6. Changed2 schema fields changed
    • addedInput schema / properties / patch_files
      Added value: +{
      +  "additionalProperties": {
      +    "type": "string"
      +  },
      +  "description": "Text of each local patch file named in extra.patches, keyed by the path as written (patchs/x.patch) or its base name. Only needed with patches:true; URL patches need nothing.",
      +  "type": "object"
      +}
    • addedInput schema / properties / patches
      Added value: +{
      +  "description": "Judge the site's patches in the same call: each one against the release its package would install for target_core. Returns a verdict per patch instead of items to check yourself.",
      +  "type": "boolean"
      +}
  7. Changed2 schema fields changed
    • changedInput schema / properties / composer_json / description
      Previous value: -"The composer.json text (require, require-dev, extra.patches). Optional: without it there are no constraints and no patches."New value: +"The composer.json contents as text, not a path (require, require-dev, extra.patches). Optional: without it there are no constraints and no patches."
    • changedInput schema / properties / composer_lock / description
      Previous value: -"The composer.lock text, or the slim form {\"packages\":[{\"name\":…,\"version\":…}]} with only drupal/* packages (a few KB instead of hundreds)."New value: +"The composer.lock contents as text (not a path), or the slim form {\"packages\":[{\"name\":…,\"version\":…}]} with only drupal/* packages (a few KB instead of hundreds)."
  8. Added

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint, but the description adds substantial behavioral detail: it explains that the server cannot read the filesystem, how the upload command works, the meaning of installed_unknown and installed_from_tag, and the limitation that releases after bundle_date are invisible. It also discloses that a plan request excludes scan rows. This exceeds what annotations alone provide.

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?

The description is long, but every sentence adds specific, non-redundant detail. It is structured into clear thematic paragraphs (parameters, row types, candidates, patches, plan) and starts with the core purpose. It is appropriately sized for the tool's complexity, though a more concise summary could be considered for strictly minimal usage.

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?

Given the tool has nested parameters, multiple modes, and no output schema, the description must explain output behavior. It does this thoroughly: it defines row types (no_release, update, unknown, current), explains candidates and patches, describes plan output structure, and covers edge cases like dev versions and outside_drupal. Nothing an agent needs to call it correctly is missing.

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

Parameters5/5

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

Although the schema covers 100% of parameters, the description goes far beyond it. It explains how composer_lock can be either text or a path, that an empty target_core scans against installed core, and the purpose of patch_files and composer_json. It clarifies the relationship between metapackages and sub-modules, adding semantic depth that the schema alone does not convey.

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 opens with a precise, question-like statement of purpose: 'Which of a site's drupal/* packages need a new release for a target core, and which have none?' It also distinguishes itself from siblings by referencing check_patches and reroll_patch as separate steps, making the core scanning role clear.

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

Usage Guidelines4/5

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

The description explicitly instructs to send composer.lock and composer.json once, and not to query one package at a time. It also explains when to use the 'patches' mode and references check_patches for later steps, but it does not explicitly name alternatives such as project_upgrade_report or when not to use this tool. The guidance is strong but could be more explicit about exclusions.

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

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

TDQS

A4.6/5.0
Disambiguation4/5

Tools cluster into related families (change records, symbol usage, patch checks), but descriptions and cross-references clearly separate list vs detail vs diff operations. Some adjacent tools like lookup_core_symbol and list_symbol_users require careful reading, yet their purposes are distinct enough for an agent to select correctly.

Naming Consistency4/5

Most tools follow a clear snake_case verb_noun pattern: list_*, get_*, lookup_*, query_, scan_, search_, reroll_, describe_. A few names like project_profile, subsystem_coupling, and what_changed deviate from verb_noun but remain consistent in style and readable.

Tool Count4/5

At 16 tools, the set is slightly above the typical well-scoped range, but the domain is broad: patch lifecycle, composer scanning, symbol lookup, change records, project profiles, dataset queries, and code search. Each tool covers a distinct query surface, so none feels redundant.

Completeness5/5

The toolkit covers the full read-only analysis lifecycle: scanning composer constraints, checking and re-rolling patches, exploring datasets, looking up core symbols and their users, listing change records, profiling projects, and assessing upgrade readiness. No obvious dead ends or missing operations for its stated purpose.

Resources