fix(mcp): send X-Agent-Token + X-Agent-Team from flow/do servers

flow_server._build_headers and do_server._build_headers constructed
only X-Agent-ID/Role/Correlation-ID, omitting X-Agent-Token and
X-Agent-Team (unlike ApiClient._get_agent_headers used by the other
MCP servers). Latent since the gateway refactor — surfaced when
ROBOCO_AGENT_AUTH_REQUIRED=true was armed on the NAS, 401-ing every
flow/do verb with 'Missing X-Agent-Token header'. Add both headers
(mirroring ApiClient) so the HMAC gate passes. Tests assert the
headers are now injected.
This commit is contained in:
Renn F
2026-07-06 03:42:40 +02:00
parent 51b92bed45
commit 6ed4e1391b
4 changed files with 84 additions and 2 deletions
+30
View File
@@ -89,6 +89,36 @@ def test_note_with_scope_reflect(do_module: Any) -> None:
assert kwargs["json"]["scope"] == "reflect"
def test_build_headers_carries_auth_token_and_team(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""do verbs must carry X-Agent-Token + X-Agent-Team or the API's
ROBOCO_AGENT_AUTH_REQUIRED gate 401s with "Missing X-Agent-Token"
regression: the manual header dict omitted both, latent until auth was
armed on the NAS deploy."""
import importlib
be_dev_1 = "00000000-0000-0000-0001-000000000001" # role=developer, team=backend
manifest = Path(tempfile.mkdtemp()) / "tool-manifest.json"
manifest.write_text(json.dumps({**_DO_TEST_MANIFEST, "agent_id": be_dev_1}))
monkeypatch.setenv("ROBOCO_AGENT_ID", be_dev_1)
monkeypatch.setenv("ROBOCO_AGENT_ROLE", "developer")
monkeypatch.setenv("ROBOCO_AGENT_TOKEN", "test-hmac-token")
monkeypatch.setenv("ROBOCO_ORCHESTRATOR_URL", "http://test-orchestrator:8000")
monkeypatch.setenv("ROBOCO_TOOL_MANIFEST_PATH", str(manifest))
import roboco.mcp.do_server as srv
importlib.reload(srv)
headers = srv._build_headers()
assert headers["X-Agent-ID"] == be_dev_1
assert headers["X-Agent-Role"] == "developer"
assert headers["X-Agent-Team"] == "backend"
assert headers["X-Agent-Token"] == "test-hmac-token"
assert "X-Correlation-ID" in headers
def test_dm_posts_all_fields(do_module: Any) -> None:
fake_client = MagicMock()
fake_client.__enter__.return_value = fake_client
@@ -134,6 +134,34 @@ def test_give_me_work_posts_to_orchestrator(flow_module: types.ModuleType) -> No
assert kwargs["headers"]["X-Agent-Role"] == "developer"
def test_build_headers_carries_auth_token_and_team(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
"""flow verbs must carry X-Agent-Token + X-Agent-Team or the API's
ROBOCO_AGENT_AUTH_REQUIRED gate 401s with "Missing X-Agent-Token"
regression: the manual header dict omitted both, latent until auth was
armed on the NAS deploy."""
be_dev_1 = "00000000-0000-0000-0001-000000000001" # role=developer, team=backend
manifest = tmp_path / "tool-manifest.json"
manifest.write_text(json.dumps({**_FULL_MANIFEST, "agent_id": be_dev_1}))
monkeypatch.setenv("ROBOCO_AGENT_ID", be_dev_1)
monkeypatch.setenv("ROBOCO_AGENT_ROLE", "developer")
monkeypatch.setenv("ROBOCO_AGENT_TOKEN", "test-hmac-token")
monkeypatch.setenv("ROBOCO_ORCHESTRATOR_URL", "http://test-orchestrator:8000")
monkeypatch.setenv("ROBOCO_TOOL_MANIFEST_PATH", str(manifest))
import roboco.mcp.flow_server as srv
importlib.reload(srv)
headers = srv._build_headers()
assert headers["X-Agent-ID"] == be_dev_1
assert headers["X-Agent-Role"] == "developer"
assert headers["X-Agent-Team"] == "backend"
assert headers["X-Agent-Token"] == "test-hmac-token"
assert "X-Correlation-ID" in headers
def test_i_will_work_on_passes_plan(flow_module: types.ModuleType) -> None:
fake_client = _make_fake_client({"status": "in_progress"})