Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(connectors): surface MCP-consuming agents in the "Active for" panel (#1227)#1310

Merged
kovtcharov-amd merged 3 commits into
amd:mainfrom
alexey-tyurin:feat/surface-mcp-consuming-agents
Jun 3, 2026
Merged

feat(connectors): surface MCP-consuming agents in the "Active for" panel (#1227)#1310
kovtcharov-amd merged 3 commits into
amd:mainfrom
alexey-tyurin:feat/surface-mcp-consuming-agents

Conversation

@alexey-tyurin

Copy link
Copy Markdown
Contributor

Summary

Widens the Settings → Connectors "Active for" panel so it lists agents that consume MCP servers dynamically (like the chat agent), not only agents that statically declare REQUIRED_CONNECTORS. The chat agent now appears as an activatable target for MCP-only connectors loaded from ~/.gaia/mcp_servers.json.

Why

Before this change, the "Active for" panel only listed agents whose class declared the connector in REQUIRED_CONNECTORS. The chat agent (builtin:chat) declares none for MCP servers — it loads them at runtime and already gates their tools through the activation ledger (_active_mcp_serversMCPClientManager.servers_for_agentis_agent_active). So a user could activate builtin:chat for an MCP connector via the CLI/SDK and it worked, but the Settings UI never showed the chat agent as eligible — a UI-only user had no way to toggle it. The runtime was correct; the panel just couldn't see these agents.

This PR closes that surfacing gap by adding a CONSUMES_MCP_SERVERS capability flag that flows from the agent class through the registry and API to the frontend, exactly the way REQUIRED_CONNECTORS already does. No activation, ledger, or SSE logic changes — those already work end-to-end (#1005, #1226).

Follow-up to #1219, which introduced per-agent MCP tool-visibility activations.

Linked issue

Closes #1227

Changes

  • New capability flag, surfaced like REQUIRED_CONNECTORS: Agent.CONSUMES_MCP_SERVERS ClassVar (False by default), set True on ChatAgent. The registry carries it as AgentRegistration.consumes_mcp_servers — hardcoded for the built-in chat (its lazy factory must not import the chat module at discovery time; a guard test keeps the two in sync) and read via class introspection on the custom-agent discovery path, so builder-scaffolded MCP agents surface automatically too.
  • API exposure: AgentInfo.consumes_mcp_servers (Pydantic) + the /api/agents serializer (_reg_to_info), and the matching TypeScript AgentInfo field.
  • Frontend: ConnectorsSection.tsx computes a separate, wider activatableAgents set for the "Active for" block (an agent is eligible if it declares the connector or sets consumes_mcp_servers); the "Per-agent grants" credential section is unchanged. AgentActivationCard no longer bails when an agent has no static requirement, and its one-click auto-grant falls back to the canonical MCP use scope for dynamic consumers (named constant, not a magic string).
  • Docs: docs/sdk/infrastructure/connectors.mdx documents the new CONSUMES_MCP_SERVERS flag in the agent-author guide and adds a "Which agents appear in the Active for panel" section covering both inclusion patterns.
  • Tests: base default, chat flag, class↔registration consistency guard, "no other built-in consumes MCP", custom-agent round-trip (True and default False), AgentInfo round-trip, and _reg_to_info exposure for a consumer vs a non-consumer.

Test plan

  • python util/lint.py --all — Black/isort clean, no new critical errors.
  • pytest tests/unit/test_agent_required_connectors.py tests/unit/test_agent_hub_api.py -q — 30 passed.
  • pytest tests/unit/connectors/ -q — 429 passed, 3 skipped (no regressions).
  • cd src/gaia/apps/webui && npm run buildtsc && vite build clean.
  • Manual golden path: add an MCP server to ~/.gaia/mcp_servers.json and configure it as an MCP connector → gaia chat --ui → Settings → Connectors → open the MCP tile. Confirm Chat now appears under "Active for"; toggle it ON. Verify gaia connectors activations list <connector_id> shows builtin:chat: active and the toggle survives a page reload (SSE refresh). Toggle OFF and confirm deactivation. Confirm an OAuth connector still shows no "Active for" section.

Checklist

  • I have linked a GitHub issue above (Closes #N / Fixes #N / Refs #N).
  • I have described why this change is being made, not just what changed.
  • I have run linting and tests locally (python util/lint.py --all, pytest tests/unit/).
  • I have updated documentation if user-visible behavior changed (see CONTRIBUTING.md).

@github-actions github-actions Bot added documentation Documentation changes tests Test changes agents labels May 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Clean, focused implementation that closes a genuine UX gap — the chat agent's "Active for" panel invisibility — without touching any activation, ledger, or SSE logic. The flag-follows-data pattern (ClassVar → AgentRegistration → Pydantic → TS type → frontend filter) is the right shape for this kind of capability surface, and the test suite is unusually thorough.


Issues

🟢 Minor — Multi-line comments on the new ClassVar exceed the one-line-max CLAUDE.md standard (agent.py:52–57, chat/agent.py:79–82)

The adjacent REQUIRED_CONNECTORS ClassVar in agent.py:49 is described in one line (# Empty list = no external connections required (the default for built-ins).). The new flag gets five. CLAUDE.md says "one short line max" for WHY comments.

src/gaia/agents/base/agent.py:52–57 — suggestion:

    # Registry reads this to include dynamic MCP consumers in the Settings "Active for" panel.
    CONSUMES_MCP_SERVERS: ClassVar[bool] = False

src/gaia/agents/chat/agent.py:79–82 — suggestion:

    # Dynamic MCP loader — registry exposes this for the Settings "Active for" panel.
    CONSUMES_MCP_SERVERS: ClassVar[bool] = True

Strengths

  • Guard test is the right call. test_builtin_chat_registration_matches_class explicitly checks that the hardcoded registry value matches ChatAgent.CONSUMES_MCP_SERVERS — exactly the kind of sync risk a lazy factory creates, and exactly the right place to catch drift.
  • MCP_DEFAULT_GRANT_SCOPES constant. Naming the canonical scope rather than inlining ['use'] is the correct move; the comment on it explains the "why" for dynamic consumers in one sentence.
  • activatableAgents separation from relevantAgents. The frontend neatly keeps credential-grant eligibility (static required_connections) separate from activation eligibility (wider), and the connectorType === 'mcp_server' guard is baked into activatableAgents's ternary rather than duplicated at the render site.
  • Test breadth. Base default, chat value, class↔registration sync, no-other-builtins regression guard, custom-agent round-trips (True and False), API serialization — these cover every layer the flag touches.

Verdict

Approve with suggestions. The two multi-line comment nits are the only findings; no bugs, no architectural concerns. Safe to merge after applying the one-liner suggestions (or skipping them — they're style-only).

@github-actions

Copy link
Copy Markdown
Contributor

Clean, well-scoped implementation. The design correctly separates the two inclusion paths (static REQUIRED_CONNECTORS vs. dynamic CONSUMES_MCP_SERVERS) without touching any activation, ledger, or SSE logic — exactly the right blast radius for a UI surfacing gap. One nit worth a one-line fix; everything else is solid.


Issues

🟢 Minor — Unnecessary defensive getattr in _reg_to_info (src/gaia/ui/routers/agents.py:115)

Every other field serialised by _reg_to_info uses direct attribute access (reg.min_memory_gb, reg.required_connections, reg.namespaced_agent_id). Since consumes_mcp_servers is now a proper dataclass field with a default, the getattr fallback is redundant and slightly misleading.

        consumes_mcp_servers=reg.consumes_mcp_servers,

🟢 Minor — Verbose comment block on AgentRegistration.consumes_mcp_servers (src/gaia/agents/registry.py:278–282)

GAIA convention (CLAUDE.md "Code Comments") is one short WHY line per field. The 5-line block mirrors what the field name and type already say; the neighbouring required_connections comment runs two lines. Trimming keeps the dataclass scannable.

    # Surfaced from the agent's CONSUMES_MCP_SERVERS ClassVar; True for dynamic MCP loaders.
    consumes_mcp_servers: bool = False

Same trimming applies to the parallel comment in src/gaia/ui/models.py:255–258 (4 lines; one line is enough for the same reason).


Strengths

  • test_builtin_chat_registration_matches_class is the right kind of guard test: it catches exactly the failure mode (hardcoded flag drifts from the class definition) that the lazy-factory workaround creates, without coupling the test to internal module paths.
  • Two-list design in ConnectorsSection.tsx (relevantAgents for per-credential grants, activatableAgents for the "Active for" panel) keeps the two sections independent and makes the intent immediately readable in the diff.
  • MCP_DEFAULT_GRANT_SCOPES named constant eliminates a magic ['use'] string from the activation callback; the JSDoc on AgentActivationCard is updated in the same commit to explain the fallback path.
  • TypeScript type is consumes_mcp_servers?: boolean (optional), so the frontend degrades gracefully against an older backend that doesn't send the field — the filter treats undefined as falsy and the agent simply won't appear.

Verdict

Approve. Both nits are stylistic and non-blocking. The implementation is correct, the guard test is smart, and the test matrix covers all four cases (base default, chat builtin, custom consumer, custom non-consumer) plus the serialisation round-trip.

@alexey-tyurin

Copy link
Copy Markdown
Contributor Author

@kovtcharov-amd -- My original commit
50fb140
for this PR got reported "All checks have passed" and Verdict "Approve with suggestions. The two multi-line comment nits are the only findings...".
So, I made suggested fixes in comments only, and this new commit somehow got Failed check:
Test GAIA CLI on Windows (Full Integration).
But it also got Verdict "Approve."
This failed test looks like a glitch.

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Clean, narrow feature that closes a real UX gap in the connectors panel. One nit worth a one-line fix; everything else is solid. Approve with suggestion.


Summary

The "Active for" panel previously only listed agents that declare REQUIRED_CONNECTORS, so builtin:chat — which loads MCP servers dynamically and already honours the activation ledger — was invisible in the UI despite the backend working correctly. This PR closes the gap with a CONSUMES_MCP_SERVERS capability flag that flows exactly the same path as REQUIRED_CONNECTORS: agent class → AgentRegistrationAgentInfo (Python) → /api/agents → TypeScript AgentInfo. The implementation is narrow, the logic is correct, and the guard test that keeps the hardcoded builtin registration in sync with the class attribute is a smart pattern.


Issues Found

🟢 Minor — doc snippet missing from typing import ClassVar (docs/sdk/infrastructure/connectors.mdx:196–199)

The minimal snippet uses ClassVar[bool] but doesn't import it. The preceding GmailAgent example at line 147 does include the import, but a reader copying just this snippet will get a NameError.

```python
from typing import ClassVar
from gaia.agents.base.agent import Agent


class MyAgent(Agent):
    CONSUMES_MCP_SERVERS: ClassVar[bool] = True

---

## Strengths

- **Layered propagation is exact.** The flag follows the same `ClassVar → AgentRegistration → AgentInfo → TypeScript` pipeline as `REQUIRED_CONNECTORS`, making it easy for future contributors to reason about.
- **Guard test prevents silent drift.** `test_builtin_chat_registration_matches_class` directly asserts that the hardcoded `consumes_mcp_servers=True` in the lazy-factory builtin registration matches `ChatAgent.CONSUMES_MCP_SERVERS`. This is the right way to handle the necessary decoupling for import-cycle reasons.
- **Frontend changes are surgically scoped.** `activatableAgents` is a separate computed set that widens *only* the "Active for" block; `relevantAgents` and the per-agent credential grants section are untouched. The `MCP_DEFAULT_GRANT_SCOPES` constant eliminates the magic string in the toggle path. The `connectorType === 'mcp_server'` guard is correctly absorbed into `activatableAgents` (returns `[]` for non-MCP connectors) rather than duplicated at the render site.
- **Test coverage is thorough:** base default, chat flag, class↔registration consistency, "no other builtin sets the flag," custom-agent round-trip in both directions, `AgentInfo` Pydantic round-trip, and `_reg_to_info` serialization for a consumer vs. a non-consumer.

---

## Verdict

**Approve with suggestion** — one nit (missing import in the doc snippet). Safe to merge after applying; the doc fix is one line and can be done inline.

@itomek itomek left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — this is a clean, well-scoped contribution, @alexey-tyurin. Thanks for following the issue all the way through from report to fix.

I verified the whole chain locally on HEAD (3ec32bb):

  • All four #1227 acceptance criteria hold end-to-end: builtin:chat now surfaces in the MCP Active-for panel, toggling round-trips through the existing ledger/SSE path (the PR reuses that logic rather than touching it), static REQUIRED_CONNECTORS agents render unchanged (activatableAgents is a superset of relevantAgents), and the docs are updated.
  • pytest tests/unit/test_agent_required_connectors.py tests/unit/test_agent_hub_api.py -> 30 passed, including the sync guard test_builtin_chat_registration_matches_class. pytest tests/unit/connectors/ -> 436 passed, no regressions.
  • python util/lint.py --black --isort -> clean.
  • Full CI on the fork PR is green across the board (unit 3.10-3.12, API, Security Linux+Windows, Chat Agent, CLI integration Linux+Windows, build-apps, doc cross-ref) — the earlier Windows Full Integration blip was transient and is now passing.

The capability-flag threading (CONSUMES_MCP_SERVERS through base Agent -> ChatAgent -> registry -> AgentInfo -> /api/agents -> the frontend filter) is a tidy way to model this, and keeping the hardcoded registry flag guarded by a test against the class is the right call.

One optional follow-up, not a blocker: the heart of the feature — the activatableAgents filter in ConnectorsSection.tsx — has no frontend unit test; it's currently exercised only by tsc/vite at build time (type-safety, not behavior). I'd normally suggest a small vitest case, but the webui has effectively no frontend-test culture today (a single pre-existing spec), so this is genuinely a nice-to-have rather than something I'd hold the PR on.

The two still-open bot nits (the redundant getattr at agents.py:115 and the missing from typing import ClassVar in the connectors.mdx snippet) are minor and fine to fold in or leave.

Nice work.


Generated by Claude Code

@alexey-tyurin

Copy link
Copy Markdown
Contributor Author

@kovtcharov-amd this PR is approved by @itomek. Can it be merged now?

@kovtcharov-amd kovtcharov-amd added this pull request to the merge queue Jun 3, 2026
@kovtcharov-amd

Copy link
Copy Markdown
Collaborator

@kovtcharov-amd this PR is approved by @itomek. Can it be merged now?

Yes, merged. Apologies for the delay.

Merged via the queue into amd:main with commit 1316fae Jun 3, 2026
33 checks passed
@amd amd deleted a comment from codecov-commenter Jun 3, 2026
kovtcharov-amd pushed a commit that referenced this pull request Jun 3, 2026
19 PRs merged to main after the v0.20.0 notes were drafted; merge main into
the release branch and update the notes to match.

- Multi-Device: note #1338 wired the selector end-to-end (it was a no-op
  after #1252 alone) and added loud runtime validation for unavailable devices.
- PowerPoint RAG: note #1366 unblocked .pptx in the Agent UI file picker
  (backend already accepted it; the frontend rejected it pre-upload).
- Activations: note #1310 widened the "Active for" panel to MCP-consuming
  agents (e.g. the chat agent), not just static REQUIRED_CONNECTORS.
- New bug fixes: RAG embedding reuse (#1306), overlapping-chat-turn 409
  (#1304), Memory/Settings mutual exclusivity (#1368).
- Tooling/CI: doc-vs-code realignment (#1298/#1337/#1340), gaia-testing
  skill (#1372), Claude CI + Codecov hardening.
- Full Changelog regenerated: 44 -> 63 commits.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents documentation Documentation changes tests Test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(connectors): widen "Active for" panel to surface MCP-consuming agents, not just REQUIRED_CONNECTORS declarants

3 participants