task: #3586562 Add a configure-ai-settings agent skill for general AI settings...
Description
Closes #3586562
Adds a configure-ai-settings agent skill that lets an AI agent (or developer) read and write the AI module's general operational settings on a running Drupal site without using the admin UI, following the same pattern as the setup-guardrails skill.
The skill is driven through dedicated Tool-module plugins invoked via drush tool:run. Generic tool_belt entity tools cannot read or write simple config, so seven purpose-built plugins are added under src/Plugin/tool/Tool/:
| Plugin ID | What it does |
|---|---|
ai_settings:get_default_models |
Reads default_providers and default_vdb_provider from ai.settings |
ai_settings:set_default_model |
Writes one operation-type default; handles VDB provider as a special string-only case |
ai_settings:list_providers |
Queries ai.provider plugin manager; optional filter by operation type |
ai_settings:get_request_timeout |
Reads request_timeout |
ai_settings:set_request_timeout |
Validates range 1–3600 then writes request_timeout |
ai_settings:get_trusted_domains |
Reads allowed_hosts |
ai_settings:remove_trusted_domain |
Strict-match removes one entry from allowed_hosts |
Adding a trusted domain is intentionally out of scope for automated writes (SSRF risk); the skill returns clear UI instructions instead.
Because the Tool module is not a dependency of the AI module, the skill enables it with drush pm:enable when not already enabled and uninstalls it afterward only if the skill enabled it — leaving the site's module state unchanged.
Testing instructions
Testing Setup:
mkdir my-drupal-site && cd my-drupal-site
ddev config --project-type=drupal11 --docroot=web
ddev composer create-project drupal/cms
ddev drush site:install --account-name=admin --account-pass=admin -y
# Install the AI module and configure at least one provider
ddev composer require drupal/ai
ddev drush pm:enable ai -y
# Install the Tool module (but do NOT enable it — the skill will manage this)
ddev composer require drupal/tool
# Launch the site and add an API key for at least one AI provider
ddev launch $(ddev drush uli /admin/config/ai/settings)
## Testing instructions
<!--
Testing Setup (optional — to verify on a clean install):
mkdir my-drupal-site && cd my-drupal-site
ddev config --project-type=drupal11 --docroot=web
ddev composer create-project drupal/cms
ddev drush site:install --account-name=admin --account-pass=admin -y
# Enable AI Dashboard and ai_api_explorer
# Launch the site and open the AI dashboard to add an OpenAI or Anthropic key:
ddev launch $(ddev drush uli /admin/config/ai)
-->
Confirm Tool module is disabled before running the skill:
ddev drush php:eval "echo \Drupal::moduleHandler()->moduleExists('tool') ? 'enabled' : 'disabled';"
# expected: disabled
Enable Tool and verify each plugin is discoverable:
ddev drush pm:enable tool -y && ddev drush cr
ddev drush tool:run ai_settings:get_default_models --json
ddev drush tool:run ai_settings:list_providers --json
ddev drush tool:run ai_settings:get_request_timeout --json
ddev drush tool:run ai_settings:get_trusted_domains --json
Test setting a default model (substitute real values from list_providers output):
ddev drush tool:run ai_settings:set_default_model \
--input='{"operation_type":"chat","provider_id":"openai","model_id":"gpt-4o"}' --json
ddev drush config:get ai.settings default_providers
# expected: chat entry shows provider_id: openai, model_id: gpt-4o
Test timeout validation:
ddev drush tool:run ai_settings:set_request_timeout --input='{"seconds":120}' --json
# expected: success
ddev drush tool:run ai_settings:set_request_timeout --input='{"seconds":0}' --json
# expected: failure — out of range
ddev drush tool:run ai_settings:set_request_timeout --input='{"seconds":3601}' --json
# expected: failure — out of range
Test trusted domain removal:
# Add a test domain manually
ddev drush php:eval "
\$c = \Drupal::service('config.factory')->getEditable('ai.settings');
\$h = \$c->get('allowed_hosts') ?? [];
\$h[] = 'test.example.com';
\$c->set('allowed_hosts', \$h)->save();
"
ddev drush tool:run ai_settings:remove_trusted_domain --input='{"domain":"test.example.com"}' --json
# expected: success; domain removed
ddev drush tool:run ai_settings:remove_trusted_domain --input='{"domain":"test.example.com"}' --json
# expected: failure — domain not found
Test VDB provider validation:
ddev drush tool:run ai_settings:set_default_model \
--input='{"operation_type":"default_vdb_provider","provider_id":"typo_provider","model_id":""}' --json
# expected: failure — VDB provider not installed
Verify module-state restore — uninstall Tool then run the skill end-to-end via Claude Code. After the skill completes:
ddev drush php:eval "echo \Drupal::moduleHandler()->moduleExists('tool') ? 'enabled' : 'disabled';"
# expected: disabled (restored to original state)
## Checklist
<!-- CI runs phpcs and phpstan automatically. Fix any failures before requesting review. -->
- [X] I have linked the related issue in the MR title or description
- [X] I have performed a self-review of my own code
- [X] I have added or updated tests, or explained in the description why this change is not covered by tests
- [ ] I have updated documentation for any new or changed functionality
- [X] I have written testing instructions and verified them locally
- [X] I have noted any required post-merge steps (config imports, cache rebuilds, manual changes)
- [X] This MR contains no breaking API or hook changes, or they are explicitly documented in the description
## AI Compliance
<!--
Disclose significant AI usage — generating entire functions, classes, or architectural scaffolding.
Minor autocomplete or syntax corrections do not need to be disclosed.
You are fully responsible for all code in this MR and must be able to explain every decision.
See: https://www.drupal.org/docs/develop/issues/issue-procedures-and-etiquette/policy-on-the-use-of-ai-when-contributing-to-drupal
-->
> [!note]
> Check the one that best describes your usage, or leave all unchecked if AI was not significantly used.
>
> * [ ] AI **Assisted Code** \
> _Mainly written by a human; AI used for autocomplete or partial generation under full human supervision._
>
> * [X] **AI Generated Code** \
> _Mainly generated by AI, reviewed and approved by a human before this MR was created._
>
> * [ ] **Vibe Coded** \
> _Generated by AI and only functionally reviewed before this MR was created._
Closes #3586562