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>
21 lines
2.0 KiB
Bash
21 lines
2.0 KiB
Bash
{% set endpoint = lookup('config', application_id, 'mcp.endpoint') -%}
|
|
{% set upstream_key = lookup('config', application_id, 'mcp.adapter.upstream_network', '') -%}
|
|
{% set implementation = lookup('config', application_id, 'mcp.implementation') -%}
|
|
{% set entity = application_id | get_entity_name -%}
|
|
MCP_AUTH_HEADER='{{ {'id': application_id, 'auth': lookup('config', application_id, 'mcp.auth'), 'token': lookup('mcp_client_bearer', application_id), 'owner': lookup('config', application_id, 'mcp.credential.owner')} | mcp_authorization }}'
|
|
MCP_DEADLINE_SECONDS={{ lookup('timeout', 420) | int }}
|
|
MCP_DEPLOYMENT_MODE={{ compose_mode }}
|
|
MCP_EXPECTED_NETWORKS={{ ([entity] + ([entity ~ '_' ~ upstream_key] if upstream_key else [])) | join(',') }}
|
|
MCP_EXPECTED_TOOLS='{{ lookup('config', application_id, 'mcp.tools.allowlist', []) | to_json }}'
|
|
MCP_HOST_HEADER={{ lookup('domain', application_id) if endpoint.get('host_header', false) else '' }}
|
|
MCP_PATH_KEY=
|
|
MCP_READ_ARGUMENTS='{{ lookup('config', application_id, 'mcp.tools.read_probe.arguments', {}) | to_json }}'
|
|
MCP_READ_TOOL={{ lookup('config', application_id, 'mcp.tools.read_probe.tool', '') }}
|
|
MCP_SIDECAR_SERVICE={{ ((entity ~ '_' ~ endpoint.service_key) if compose_mode == 'swarm' else lookup('config', application_id, 'services.' ~ endpoint.service_key ~ '.name')) if implementation == 'adapter' else '' }}
|
|
MCP_TEST_ENABLED={{ lookup('config', application_id, 'mcp.enabled') | string | lower }}
|
|
MCP_TEST_IMAGE={{ lookup('config', 'svc-ai-mcp-adapter', 'services.mcp-adapter.image') }}:{{ lookup('config', 'svc-ai-mcp-adapter', 'services.mcp-adapter.version') }}
|
|
MCP_TEST_NETWORK={{ entity }}
|
|
MCP_TRANSPORT={{ lookup('config', application_id, 'mcp.transport') }}
|
|
MCP_UPSTREAM_SERVES='{{ ([] if implementation == 'adapter' else lookup('config', application_id, 'mcp.tools.upstream_serves', [])) | to_json }}'
|
|
MCP_URL=http://{{ endpoint.service_key }}:{{ lookup('config', application_id, 'services.' ~ endpoint.service_key ~ '.ports.internal.' ~ endpoint.port_key) }}{{ endpoint.path }}
|