---
- name: "π Mint the long-lived token and create the MCP config entry"
ansible.builtin.command:
argv:
- container
- exec
- -i
- -e
- "HA_PORT={{ HOMEASSISTANT_PORT_INTERNAL }}"
- -e
- "HA_USERNAME={{ HOMEASSISTANT_ADMIN_USERNAME }}"
- -e
- HA_PASSWORD
- -e
- "HA_TOKEN_CLIENT_NAME={{ HOMEASSISTANT_TOKEN_CLIENT_NAME }}"
- -e
- "HA_SERVICE_USERNAME={{ HOMEASSISTANT_MCP_ACCOUNT_USERNAME }}"
- -e
- HA_SERVICE_PASSWORD
- -e
- "HA_SERVICE_NAME={{ HOMEASSISTANT_MCP_ACCOUNT_NAME }}"
- -e
- "HA_SERVICE_GROUP={{ HOMEASSISTANT_MCP_ACCOUNT_GROUP }}"
- "{{ HOMEASSISTANT_LOCAL_CID }}" # nocheck: container-exec-resolver
- python3
- "-"
stdin: "{{ lookup('file', 'python/provision.py') }}"
environment:
HA_PASSWORD: "{{ HOMEASSISTANT_ADMIN_PASSWORD }}"
HA_SERVICE_PASSWORD: "{{ HOMEASSISTANT_MCP_ACCOUNT_PASSWORD }}"
register: homeassistant_provision
retries: 30
delay: 10
until: homeassistant_provision.rc == 0
changed_when: "'CHANGED' in homeassistant_provision.stdout"
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
delegate_to: "{{ HOMEASSISTANT_HOST_NODE }}"
run_once: true
- name: "π§ Take the minted token from the provisioning output"
ansible.builtin.set_fact:
homeassistant_mcp_token: "{{ homeassistant_provision.stdout_lines | last | trim }}"
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
run_once: true
- name: "π Refuse a hub that issued no usable token"
ansible.builtin.assert:
that:
- homeassistant_mcp_token | length >= 50
fail_msg: >-
The hub did not return a long-lived access token, so every MCP client
would be configured with an empty bearer and fail at first use.
- name: "πΎ Persist the token so MCP clients can read it"
ansible.builtin.include_role:
name: sys-token-store
tasks_from: write.yml
vars:
sys_token_store_user_key: "{{ lookup('config', application_id, 'mcp.credential.owner') }}"
sys_token_store_app: "{{ application_id }}"
sys_token_store_token: "{{ homeassistant_mcp_token }}"
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"