Skip to content

Tool API plugins

content_deployment_tools exposes the main module's operations as Tool API plugins, for the chat project, MCP Server, ECA and anything else that runs tools. It is experimental while Tool API is in beta, and was tested against Tool API 1.0.0-beta11.

composer require drupal/tool
drush pm:install content_deployment_tools

Nothing is configured for MCP Server: exposing the tools over MCP is each site's choice, made in MCP Server's own configuration.

The tools

Each tool calls the service its Drush command calls, and its one output, result, holds what the command prints. Tool API's operation kinds say what each does; destructive asks callers to confirm first.

Tool Operation Destructive Drush command
content_spec_model explain no content-spec:model
content_spec_query read no content-spec:query
content_spec_read read no content-spec:read
content_spec_pending read no content-spec:pending
content_spec_create write no content-spec:create
content_spec_apply write no content-spec:apply
content_spec_media_ingest write no content-spec:media-ingest
content_spec_publish write no content-spec:publish
content_spec_unpublish write yes content-spec:unpublish

Inputs are the commands' options, in snake case:

Tool Inputs
content_spec_model entity_type, bundle
content_spec_query entity_type (default node), bundle, search, where (a list of conditions), published, unpublished, sort, limit, offset, with (a list of field names), count_only
content_spec_read entity (id or uuid), entity_type (default node), no_text, published, portable, structure_only
content_spec_pending conflicts
content_spec_create, content_spec_apply spec (the content spec, as an object), dry_run, publish
content_spec_media_ingest file (a file entity's uuid), alt, name, bundle
content_spec_publish entity, entity_type, langcode
content_spec_unpublish entity, entity_type

tool.plugin.<id> config schema covers each tool's inputs, for a site that stores them. A spec is free-form there; create and apply validate it when they run.

Who writes

Drush commands and changesets write as the robot account. A tool writes as the account invoking it: that account owns what it creates and authors each revision.

Access

Each tool checks access in access(), as Tool API asks, and again when it runs, since a caller can run a tool without asking. A refusal at run time is a failed result of category Access, and nothing is written.

Tool Allowed when the account
content_spec_model may create content of some bundle a spec can describe, or administer some entity type's fields
content_spec_query may run it; rows and the count are of what it may view, and a field asked for is reported where it may view the field. Accounts take administer users.
content_spec_read may view the entity; the newest revision, when it is a pending draft in any language, takes update access. Accounts take administer users.
content_spec_pending may run it; the list holds the drafts of entities it may update
content_spec_create, content_spec_apply has create access to the bundle, or update access to the entity; edit access to each field the write changes; use access to every text format the spec writes, a value without a format taking the field's default; update access to each entity a paragraph is claimed from; and, with publish, the review flow's access to publish. Accounts take administer users, since a spec can carry roles.
content_spec_media_ingest uploaded the file; has create access to the media type, or, when the bytes are already a media item, view access to it, and update access when the ingest would change its alt text
content_spec_publish may publish the draft: the workflow's transition to its published state, or update access and edit access to the published flag without moderation
content_spec_unpublish may take it offline: the workflow's transition to the archived state, or update access and edit access to the published flag; administer users to block an account

A field the write leaves as it is needs no edit access, so a spec read and applied back unchanged does not need access to its author or dates. A configurable field the spec omits is emptied, and so changed.

Media

The media ingest tool reads no paths: a path from a remote caller would let it read any file the web server can. Upload the bytes first, through an authenticated route that makes a file entity, such as core's JSON:API or REST file upload, then pass the file's uuid. The file is left as it is; the media item gets a file of its own.

Running a tool from Drush

drush tool:run content_spec_read --uid=2 --input='{"entity":"0a8ba38b-c932-40ca-bd7f-f28a87686ac8"}' --json
{
  "success": true,
  "message": "Read node \"Opening hours\".",
  "outputs": {
    "result": {
      "$schema": "https://project.pages.drupalcode.org/content_deployment/schema/content-spec/v3",
      "entity_type": "node",
      "bundle": "landing",
      "uuid": "0a8ba38b-c932-40ca-bd7f-f28a87686ac8",
      "title": "Opening hours"
    }
  }
}

--uid names the invoking account; without it the tool runs as anonymous.