Files
computer-playbook/roles/web-app-moodle/tasks/utils/mcp/plugin.yml
kevinveenbirkenbachandClaude Opus 5 a423bde6cc refactor(mcp): unify client entrypoints and read provider tokens through one lookup
Every MCP role keeps its task tree under tasks/utils/ without numeric
prefixes, so a caller can reach any of them through include_role
tasks_from. utils/roles/mapping.py holds that path as the single source.

Ansible resolves a relative include against the including file's own
directory when that file was reached by path, but against tasks/ when it
was reached through tasks_from. The same file therefore loaded from its
own role and failed from a caller using tasks_from. Every nested include
now names {{ role_path }}/tasks/utils/..., which holds under both bases,
and a lint walks each tasks_from entrypoint transitively to keep it so.
The exec-gating lint learns the same form, otherwise it loses the include
chain and reports inherited IS_STACK_HOST gates as missing.

The expression resolving a provider's stored token appeared eighteen
times. plugins/lookup/mcp_credential.py replaces it, and mcp_probe_owner
disappears from eight roles: it was only ever a step towards the bearer
and no probe read it.

The Hermes and OpenClaw probes stop aborting the play while it is still
running. Roles deploy in dependency order, so a client probing during its
own role can run before a provider exists at all, and failing there
prevents that provider from ever deploying. The verdict moves to the
reconciliation pass, gated on MCP_RECONCILE_STRICT, which the reconciler
sets and group_vars declares so the readers need no default.

The sys-svc-mcp-reconcile role is added here; wiring it into a stage is a
separate change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 21:06:31 +02:00

35 lines
1.6 KiB
YAML

---
- name: "🔎 Check the MCP plugin is baked into the image"
ansible.builtin.shell: | # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node
container exec --user root {{ MOODLE_MCP_LOCAL_CID | quote }} \
test -d {{ MOODLE_MCP_PLUGIN_SOURCE_DIR | quote }}
register: moodle_mcp_plugin_source
changed_when: false
failed_when: false
delegate_to: "{{ MOODLE_MCP_HOST_NODE }}"
run_once: true
- name: "🛑 Refuse a Moodle image built without the MCP plugin"
ansible.builtin.assert:
that:
- moodle_mcp_plugin_source.rc == 0
fail_msg: >-
{{ MOODLE_MCP_PLUGIN_SOURCE_DIR }} is missing, so the image carries no
webservice_mcp plugin and the MCP endpoint cannot exist.
- name: "📋 Sync the MCP plugin into the bootstrapped code volume"
ansible.builtin.shell: | # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node
container exec --user {{ MOODLE_RUNTIME_USER }} {{ MOODLE_MCP_LOCAL_CID | quote }} \
cp -aT {{ MOODLE_MCP_PLUGIN_SOURCE_DIR | quote }} {{ MOODLE_MCP_PLUGIN_DIR | quote }}
changed_when: false
delegate_to: "{{ MOODLE_MCP_HOST_NODE }}"
run_once: true
- name: "▶️ Install the MCP plugin database records"
ansible.builtin.shell: | # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node
container exec --user {{ MOODLE_RUNTIME_USER }} {{ MOODLE_MCP_LOCAL_CID | quote }} \
php {{ MOODLE_CLI_DIR }}/upgrade.php --non-interactive
changed_when: false
delegate_to: "{{ MOODLE_MCP_HOST_NODE }}"
run_once: true