---
- name: "π Resolve the Nextcloud hosting node + container id"
ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/swarm/resolve_host_cid.yml') }}"
vars:
resolve_service_key: "{{ NEXTCLOUD_SERVICE }}"
resolve_output_prefix: NEXTCLOUD
resolve_compose_cid: "{{ NEXTCLOUD_CONTAINER }}"
- name: "π Resolve the proxy hosting node + container id"
ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/swarm/resolve_host_cid.yml') }}"
vars:
resolve_service_key: "{{ NEXTCLOUD_PROXY_SERVICE }}"
resolve_output_prefix: NEXTCLOUD_PROXY
resolve_compose_cid: "{{ NEXTCLOUD_PROXY_CONTAINER }}"
- name: "π Resolve the Context Agent hosting node + container id"
ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/swarm/resolve_host_cid.yml') }}"
vars:
resolve_service_key: "{{ NEXTCLOUD_CONTEXT_AGENT_SERVICE }}"
resolve_output_prefix: NEXTCLOUD_CONTEXT_AGENT
resolve_compose_cid: "{{ NEXTCLOUD_CONTEXT_AGENT_CONTAINER }}"
- name: "β³ Wait until the Context Agent answers its heartbeat"
ansible.builtin.command:
argv:
- container
- exec
- "{{ NEXTCLOUD_CONTEXT_AGENT_LOCAL_CID }}" # nocheck: container-exec-resolver
- python3
- -c
- "import urllib.request; urllib.request.urlopen('http://127.0.0.1:{{ NEXTCLOUD_CONTEXT_AGENT_PORT }}/heartbeat', timeout=5)"
register: nextcloud_context_agent_heartbeat
retries: 60
delay: 5
until: nextcloud_context_agent_heartbeat.rc == 0
changed_when: false
delegate_to: "{{ NEXTCLOUD_CONTEXT_AGENT_HOST_NODE }}"
run_once: true
- name: "π Register the Context Agent as an AppAPI ExApp"
ansible.builtin.include_tasks:
file: "{{ role_path }}/tasks/utils/mcp/register.yml"
apply:
delegate_to: "{{ NEXTCLOUD_HOST_NODE }}"
run_once: true
vars:
nextcloud_exapp_renew: false
- name: "π Read the MCP app password this deployment already issued"
ansible.builtin.set_fact:
nextcloud_mcp_token: "{{ lookup('mcp_credential', application_id) }}"
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
- name: "π©Ί Check the stored app password against the MCP endpoint"
ansible.builtin.include_tasks: "{{ role_path }}/tasks/utils/mcp/probe.yml"
when: nextcloud_mcp_token | length > 0
- name: "π Mint and persist a fresh MCP app password"
ansible.builtin.include_tasks: "{{ role_path }}/tasks/utils/mcp/token.yml"
when: nextcloud_mcp_token | length == 0 or nextcloud_mcp_probe.stdout | default('') != '200'
- name: "π©Ί Re-check the freshly minted app password"
ansible.builtin.include_tasks: "{{ role_path }}/tasks/utils/mcp/probe.yml"
when: nextcloud_mcp_probe is not defined or nextcloud_mcp_probe.stdout != '200'
- name: "β»οΈ Re-register the ExApp a rotated app secret left behind"
ansible.builtin.include_tasks:
file: "{{ role_path }}/tasks/utils/mcp/register.yml"
apply:
delegate_to: "{{ NEXTCLOUD_HOST_NODE }}"
run_once: true
vars:
nextcloud_exapp_renew: true
when: nextcloud_mcp_probe.stdout != '200'
- name: "π©Ί Re-check the endpoint behind the re-registered ExApp"
ansible.builtin.include_tasks: "{{ role_path }}/tasks/utils/mcp/probe.yml"
when: nextcloud_mcp_probe.stdout != '200'
- name: "π Refuse an MCP endpoint that rejects the stored app password"
ansible.builtin.assert:
that:
- nextcloud_mcp_probe.stdout == '200'
fail_msg: >-
The MCP endpoint {{ NEXTCLOUD_MCP_PATH }} answered
{{ nextcloud_mcp_probe.stdout | default('no status') }} for the app password stored
for {{ application_id }}, so every MCP client would be configured with a
bearer the endpoint rejects.
- name: "π Hand the minted app password to the adapter"
ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/svc-ai-mcp-adapter/tasks/upstream_key.yml') }}"
vars:
mcp_upstream_key_path: "{{ NEXTCLOUD_MCP_ADAPTER_UPSTREAM_ENV }}"
mcp_upstream_key: "{{ lookup('mcp_credential', application_id) }}"
- name: "π§± Rebuild the MCP adapter of {{ application_id }} now the credential exists"
ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/svc-ai-mcp-adapter/tasks/rebuild.yml') }}"
- name: "πΏ Flush compose handlers for {{ application_id }}"
ansible.builtin.meta: flush_handlers