mcp.ymlΒΆ

---
- name: "πŸ” Resolve {{ application_id }} 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: "{{ BASEROW_SERVICE_NAME }}"
    resolve_output_prefix: BASEROW
    resolve_compose_cid: "{{ BASEROW_CONTAINER }}"

- name: "πŸ”‘ Create the MCP endpoint, its owner and its workspace"
  ansible.builtin.command:
    argv:
      - container
      - exec
      - -i
      - -e
      - "DJANGO_SETTINGS_MODULE={{ BASEROW_DJANGO_SETTINGS_MODULE }}"
      - -e
      - "SECRET_KEY={{ BASEROW_SECRET_KEY }}"
      - -e
      - "BASEROW_SECRET_KEY={{ BASEROW_SECRET_KEY }}"
      - -e
      - "MCP_OWNER_EMAIL={{ BASEROW_MCP_OWNER_EMAIL }}"
      - -e
      - "MCP_WORKSPACE_NAME={{ BASEROW_MCP_WORKSPACE_NAME }}"
      - -e
      - "MCP_ENDPOINT_NAME={{ BASEROW_MCP_ENDPOINT_NAME }}"
      - -e
      - "MCP_ENDPOINT_KEY={{ BASEROW_MCP_ENDPOINT_KEY }}"
      - "{{ BASEROW_LOCAL_CID }}" # nocheck: container-exec-resolver
      - "{{ BASEROW_PYTHON }}"
      - "{{ BASEROW_MANAGE_PY }}"
      - shell
      - -c
      - "{{ lookup('file', 'python/provision_mcp.py') }}"
  register: baserow_mcp_provision
  retries: 30
  delay: 10
  until: baserow_mcp_provision.rc == 0
  changed_when: "'CHANGED' in baserow_mcp_provision.stdout"
  no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
  delegate_to: "{{ BASEROW_HOST_NODE }}"
  run_once: true

- name: "🩺 Probe the MCP endpoint on the internal origin"
  ansible.builtin.command:
    argv:
      - container
      - exec
      - -i
      - -e
      - "MCP_BASE_URL={{ BASEROW_INTERNAL_URL }}"
      - -e
      - "MCP_ENDPOINT_PATH={{ BASEROW_MCP_ENDPOINT_PATH }}"
      - -e
      - "MCP_ENDPOINT_KEY={{ BASEROW_MCP_ENDPOINT_KEY }}"
      - "{{ BASEROW_LOCAL_CID }}" # nocheck: container-exec-resolver
      - "{{ BASEROW_PYTHON }}"
      - "-"
    stdin: "{{ lookup('file', 'python/probe_mcp_endpoint.py') }}"
  register: baserow_mcp_probe
  retries: 30
  delay: 10
  until: baserow_mcp_probe.rc == 0 or 'REJECTED' in baserow_mcp_probe.stderr
  failed_when: false
  changed_when: false
  no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
  delegate_to: "{{ BASEROW_HOST_NODE }}"
  run_once: true

- name: "πŸ›‘ Refuse an endpoint that never serves the MCP stream"
  ansible.builtin.assert:
    that:
      - baserow_mcp_probe.rc == 0
    fail_msg: >-
      {{ BASEROW_INTERNAL_URL }}{{ BASEROW_MCP_ENDPOINT_PATH }} did not answer 401
      for an unknown key and text/event-stream for the provisioned one, so every
      MCP client would be pointed at a dead or unguarded endpoint.

- name: "πŸ” Rebuild the adapter sidecar against the current contract"
  ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/svc-ai-mcp-adapter/tasks/rebuild.yml') }}"

- name: "πŸ”„ Start the sidecar before probing it"
  ansible.builtin.meta: flush_handlers