mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
The contract check ran as an Ansible task in the middle of every provider deploy, and the two client checks ran the same way. A test that is part of the deploy cannot be run without deploying, cannot be re-run on its own, and reports its verdict as a deploy failure. Twenty-seven task files lose 491 lines to this.
It is now what it always was. Each provider ships files/test/test.sh, templates/test.env.j2 and meta/tests.yml, and test-e2e-cli stages one shared harness beside it: run.sh drives contract.sh in a throwaway container on the provider's own network, then isolation.sh for adapter-fronted providers. The read probe each provider offers moved from the calling task into meta/mcp.yml next to the allowlist it must satisfy, and the schema now rejects a read probe naming a tool outside that allowlist.
The bearer the test presents is the one a client resolves through mcp.credential, via the new mcp_client_bearer lookup over resolve_credential, which moved to utils.roles.applications.mcp so it is the same function mcp_servers renders every client connection with. The probe presented whatever the calling role passed, which for the fifteen adapter-fronted providers is a different secret than any client holds.
Sidecar isolation is read from the engine rather than measured inside the container, so it no longer needs a delegation to the node holding the task: attachment is compared by network name, which drops the gwbridge special case, and a swarm worker says it cannot measure instead of skipping silently.
Retrying is bounded by wall clock against MCP_DEADLINE_SECONDS, which scales through the same lookup('timeout') as the harness budget it has to stay under. A flag that is neither true nor false aborts rather than reading as off.
mcp-provider-probe becomes mcp-provider-cli-test: an enabled provider must ship the test rather than include the task. mcp-probe-bearer becomes mcp-adapter-bearer, tying ADAPTER_BEARER to the declared credential key now that no probe include carries it. A new integration test renders the shared env template for every provider through Ansible's own templar, so a later change to it breaks visibly instead of poisoning twenty deploys quietly.
Hermes and OpenClaw keep their client-side proof as CLI tests of their own.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
57 lines
2.7 KiB
YAML
57 lines
2.7 KiB
YAML
---
|
|
- name: "🔍 Read the MCP token this instance already issued"
|
|
ansible.builtin.set_fact:
|
|
gitlab_mcp_token: "{{ lookup('mcp_credential', application_id) }}"
|
|
|
|
- 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: "{{ GITLAB_WEBSERVICE_SERVICE_NAME }}"
|
|
resolve_output_prefix: GITLAB_MCP
|
|
resolve_compose_cid: "{{ lookup('container_address', application_id, GITLAB_WEBSERVICE_SERVICE_NAME) }}"
|
|
|
|
- name: "🩺 Check the stored token against the running instance"
|
|
ansible.builtin.include_tasks: "{{ role_path }}/tasks/utils/mcp/probe.yml"
|
|
when: gitlab_mcp_token | length > 0
|
|
|
|
- name: "🔑 Mint the MCP token and persist it"
|
|
ansible.builtin.include_tasks: "{{ role_path }}/tasks/utils/mcp/provision.yml"
|
|
when: gitlab_mcp_token | length == 0 or gitlab_mcp_probe.rc | default(1) != 0
|
|
|
|
- name: "🩺 Re-check the freshly minted token"
|
|
ansible.builtin.include_tasks: "{{ role_path }}/tasks/utils/mcp/probe.yml"
|
|
when: gitlab_mcp_probe is not defined or gitlab_mcp_probe.rc != 0
|
|
|
|
- name: "🛑 Refuse an instance whose MCP token every client would be rejected with"
|
|
ansible.builtin.assert:
|
|
that:
|
|
- gitlab_mcp_probe.rc == 0
|
|
fail_msg: >-
|
|
The instance rejected the token stored for {{ application_id }}, so every
|
|
MCP client would be configured with a bearer {{ GITLAB_MCP_PATH }} answers
|
|
401 to.
|
|
|
|
- name: "🛑 Refuse an instance that authenticates but advertises no MCP tool"
|
|
ansible.builtin.assert:
|
|
that:
|
|
- "'tools=' in (gitlab_mcp_probe.stdout | default(''))"
|
|
- "'tools=0' not in (gitlab_mcp_probe.stdout | default(''))"
|
|
fail_msg: >-
|
|
{{ GITLAB_MCP_PATH }} accepted the token but advertised no tool. The
|
|
endpoint needs no licence tier: CE gates it on the instance setting
|
|
'mcp_server_enabled', which defaults to true. An empty tool list
|
|
therefore means that setting was turned off on this instance, or the
|
|
token carries no 'mcp' scope.
|
|
|
|
- name: "🔑 Hand the minted upstream token to the adapter"
|
|
ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/svc-ai-mcp-adapter/tasks/upstream_key.yml') }}"
|
|
vars:
|
|
mcp_upstream_key_path: "{{ [GITLAB_MCP_INSTANCE_DIR, 'upstream.env'] | path_join }}"
|
|
mcp_upstream_key: "{{ lookup('mcp_credential', application_id) }}"
|
|
|
|
- name: "🧱 Rebuild the MCP adapter of {{ application_id }} now the bearer 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
|