Skip to content

feat(agents): external NAT agents — backend (register, chat, eval, health)#763

Open
marcusds wants to merge 7 commits into
mainfrom
register-external-agents-backend/mschwab
Open

feat(agents): external NAT agents — backend (register, chat, eval, health)#763
marcusds wants to merge 7 commits into
mainfrom
register-external-agents-backend/mschwab

Conversation

@marcusds

@marcusds marcusds commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Backend for external NAT agents — agents that run outside NeMo Platform, registered by URL and discovered via their A2A agent card, reachable without a NAT deployment. (Studio UI is a separate stacked PR that depends on this.)

What's included

Entity + create

  • Agent gains source (managed|external), endpoint, card.
  • POST /agents takes config XOR url; a url fetches the A2A card (timeout + size-capped; generic error to avoid a port-scan oracle) and stores an external agent. Structural URL validation.

A2A bridge (dependency-free JSON-RPC, a2a.py)

  • OpenAI chat/completionsmessage/send + message/stream (streaming), reachable via the Studio route and the CLI/SDK agent-name proxy (/-/v1/chat/completions).
  • NAT /generate/fullmessage/send, so external agents evaluate through the existing nat eval pipeline with unchanged eval configs.
  • Multi-turn: NAT's A2A executor is message-only, so the OpenAI history is folded into the message (single turn verbatim; multi-turn as a transcript).
  • Hardening: byte-capped reads, A2A failures logged, targets the vetted registered endpoint (never the card's self-reported url).

Lifecycle + gating

  • POST /agents/{name}/refresh (re-fetch card), GET /agents/{name}/reachability (liveness probe).
  • External agents cannot be deployed (400). Single is_external_agent predicate.

Testing

~100 unit tests (respx + real-socket), ruff + ty clean. Live E2E against a real nat a2a serve agent (chat + full nat eval through the bridge, accuracy 0.79).

Follow-ups (not in this PR)

Auth to external agents (would mirror IGW ModelProvider secret ref + header template); SSRF is platform-wide operator-trust (no per-feature guard); Python SDK (Stainless) regen at release; docs.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for creating and registering externally hosted agents via URL.
    • Added external-agent reachability checks and a refresh action to re-fetch agent cards.
    • Extended the gateway to proxy external agents for chat and text generation, including streaming responses.
    • Enhanced agent details with source, endpoint, and stored agent card information.
  • Bug Fixes

    • Prevented deploying external agents as managed platform agents.
    • Strengthened request validation to require exactly one creation method (configuration or URL).
    • Improved security around external connections by blocking disallowed/private targets and preventing redirect-following.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

External agent support

Layer / File(s) Summary
Agent contracts and validation
plugins/nemo-agents/src/nemo_agents_plugin/entities.py, plugins/nemo-agents/src/nemo_agents_plugin/schema.py, plugins/nemo-agents/openapi/openapi.yaml, plugins/nemo-agents/src/nemo_agents_plugin/log_utils.py, plugins/nemo-agents/tests/unit/test_entities.py, plugins/nemo-agents/tests/unit/test_log_utils.py
Agent models and requests distinguish managed and external agents, enforce config/url exclusivity, document lifecycle APIs, and sanitize logged identifiers.
A2A discovery and messaging
plugins/nemo-agents/src/nemo_agents_plugin/a2a.py, plugins/nemo-agents/tests/unit/test_a2a.py
A2A card discovery, reachability probing, JSON-RPC messaging, SSE parsing, text extraction, size limits, redirect handling, and SSRF checks are implemented and tested.
External agent lifecycle endpoints
plugins/nemo-agents/src/nemo_agents_plugin/api/v2/agents.py, plugins/nemo-agents/tests/unit/test_agents_api.py
Agent creation fetches and stores external cards; refresh updates cards; reachability returns endpoint liveness.
Deployment eligibility
plugins/nemo-agents/src/nemo_agents_plugin/api/v2/deployments.py, plugins/nemo-agents/tests/unit/test_deployments_api.py
External agents are rejected from deployment creation, while managed deployment creation remains tested.
External gateway bridge
plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py, plugins/nemo-agents/tests/unit/test_gateway.py
External agents are bridged through A2A for chat/completions and generate endpoints with JSON and SSE response formats.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Gateway
  participant AgentStore
  participant ExternalAgent
  Client->>Gateway: chat/completions request
  Gateway->>AgentStore: load Agent
  AgentStore-->>Gateway: external endpoint
  Gateway->>ExternalAgent: A2A message/send or message/stream
  ExternalAgent-->>Gateway: response text or deltas
  Gateway-->>Client: OpenAI JSON or SSE
Loading

Possibly related PRs

Suggested reviewers: tylersbray, maxdubrinsky, crookedstorm

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.31% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: backend support for external NAT agents, including registration, chat, evaluation, and health endpoints.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch register-external-agents-backend/mschwab

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/nemo-agents/openapi/openapi.yaml`:
- Around line 2785-2793: Update the CreateAgentRequest schema to encode the
mutually exclusive config/url requirement with oneOf branches, making one branch
require config and the other require url while preserving the existing name
requirement and property definitions.

In `@plugins/nemo-agents/src/nemo_agents_plugin/a2a.py`:
- Around line 142-149: Update the probe loop around the client.get call to use
httpx.AsyncClient.stream with a GET request and an async context manager,
checking only the response status_code before returning. Preserve the existing
HTTPError handling and path iteration while ensuring the response body is never
fully buffered.
- Around line 276-284: Update the response-reading loop in the async stream
handling around resp.aiter_bytes() to preserve UTF-8 characters split across
chunks: keep incoming data as bytes, use an incremental UTF-8 decoder when
producing text, and only then split and parse SSE lines. Retain the existing
total-byte limit and buffering behavior.
- Around line 92-96: Prevent SSRF for external-agent URLs by validating resolved
destinations against loopback, private, link-local, and other disallowed address
ranges before any request, and by preventing or revalidating every redirect at
the HTTP sink. Apply the shared protection to fetch_agent_card at
plugins/nemo-agents/src/nemo_agents_plugin/a2a.py lines 92-96, send_a2a_message
at lines 208-209, and stream_a2a_message at lines 268-269; update
CreateAgentRequest validation in
plugins/nemo-agents/src/nemo_agents_plugin/schema.py lines 54-66 to enforce the
same destination policy rather than syntax-only http(s) validation.

In `@plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py`:
- Around line 520-540: Update _stream_openai_from_a2a to accept the external
agent name, then log a warning with that name and the caught A2AMessageError in
its exception handler before yielding the embedded SSE error content. Update
every call site, including the additional location noted in the review, to pass
name and preserve the existing streaming response behavior.
- Around line 143-148: Sanitize entity-store failures in both lookup paths: in
plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py lines 143-148 and
563-568, log the caught exception through the existing logging mechanism and
replace the raw str(exc) HTTP 500 detail with a fixed, non-sensitive error
message. Preserve the existing 404 handling for NemoEntityNotFoundError.
- Around line 466-490: Update _conversation_prompt to retain system messages in
the conversation context and require the final relevant message to have role
"user"; return an empty string when the last relevant turn is not user. Preserve
existing single-turn handling and transcript formatting while labeling system
turns appropriately.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 43ecfbbc-2a8b-4c0c-bd45-030e2f74b37d

📥 Commits

Reviewing files that changed from the base of the PR and between 44cee38 and 1e75a83.

📒 Files selected for processing (11)
  • plugins/nemo-agents/openapi/openapi.yaml
  • plugins/nemo-agents/src/nemo_agents_plugin/a2a.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/agents.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/deployments.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py
  • plugins/nemo-agents/src/nemo_agents_plugin/entities.py
  • plugins/nemo-agents/src/nemo_agents_plugin/schema.py
  • plugins/nemo-agents/tests/unit/test_a2a.py
  • plugins/nemo-agents/tests/unit/test_agents_api.py
  • plugins/nemo-agents/tests/unit/test_deployments_api.py
  • plugins/nemo-agents/tests/unit/test_gateway.py

Comment thread plugins/nemo-agents/openapi/openapi.yaml
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py Outdated
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py Outdated
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Outdated
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Outdated
@marcusds
marcusds marked this pull request as draft July 17, 2026 17:50
…alth)

Add a first-class "external" agent that runs outside NeMo Platform: registered
by URL, discovered via its A2A card, reachable without a NAT deployment.

Entity + create:
- Agent gains source (managed|external), endpoint, card
- POST /agents takes config XOR url; url fetches the A2A card (timeout +
  size-capped, generic error to avoid a port-scan oracle) and stores an
  external agent; structural URL validation

A2A bridge (dependency-free JSON-RPC in a2a.py):
- chat/completions <-> message/send + message/stream (streaming), reached via
  the Studio route and the CLI/SDK agent-name proxy (/-/v1/chat/completions)
- NAT /generate/full <-> message/send, so external agents evaluate through the
  existing `nat eval` pipeline with unchanged eval configs
- multi-turn: NAT's A2A executor is message-only, so the OpenAI history is
  folded into the message (single turn verbatim; multi-turn as a transcript)
- byte-capped reads; A2A failures logged; targets the vetted registered
  endpoint, never the card's self-reported url

Lifecycle + gating:
- POST /agents/{name}/refresh (re-fetch card), GET /agents/{name}/reachability
- external agents cannot be deployed (400); is_external_agent predicate

~100 unit tests (respx + real-socket); ruff + ty clean.

Signed-off-by: mschwab <mschwab@nvidia.com>
marcusds added a commit that referenced this pull request Jul 17, 2026
Studio support for external agents (registered by URL). Depends on the backend
PR (#763): needs the source/endpoint/card Agent fields, config-XOR-url create,
and the refresh/reachability endpoints.

- Register modal: Connect running agent by endpoint URL (structural validation)
- Agents table: External badge; onboarding empty state + NAT docs link
- Agent panel:
  - Workflow tab — managed: config graph + YAML (credentials redacted);
    external: A2A card skills + endpoint
  - Chat playground wired to the external chat bridge (streaming, multi-turn)
  - Details: Source/Endpoint, reachability badge + Refresh card button
  - Deploy/Logs gated for external; Evaluate available (via bridge)
  - resolve the panel agent via GET /agents/{name} (not a page-1 list scan)
- shared isExternalAgent predicate + ExternalAgentNotice

Unit tests for read models + credential redaction; typecheck + lint clean.

Signed-off-by: mschwab <mschwab@nvidia.com>
@marcusds
marcusds force-pushed the register-external-agents-backend/mschwab branch from 1e75a83 to 4915ed8 Compare July 17, 2026 18:27
for path in AGENT_CARD_PATHS:
url = _card_url(base, path)
try:
async with client.stream("GET", url, headers={"Accept": "application/json"}) as resp:
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/agents.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 25704/32944 78.0% 62.7%
Integration Tests 14784/31593 46.8% 19.2%

- probe_agent_reachable streams and checks status only (no unbounded body read)
- external chat honors the card's capabilities.streaming: a streaming request to
  a non-streaming agent uses message/send wrapped in one SSE chunk
- streaming uses an incremental UTF-8 decoder so a multi-byte char split across
  network chunks isn't corrupted
- streaming primes the first delta so a failure before any token returns 502;
  a mid-stream failure is logged and ends without a normal "stop" finish
- the conversation prompt preserves system messages and anchors on the last user
  turn (drops trailing assistant)
- managed-agent proxy no longer double-fetches the Agent entity

Skipped (P2): full AgentCard schema validation — would require taking a2a-sdk as
a runtime dep; cards stay loosely validated (operator-trust) and incompatible
ones fail at invocation with a clear error.

Signed-off-by: mschwab <mschwab@nvidia.com>
@marcusds
marcusds force-pushed the register-external-agents-backend/mschwab branch from 4915ed8 to e098344 Compare July 17, 2026 19:18
…ize gateway errors

- a2a.py: follow_redirects=False on all httpx sinks so a registered external
  agent URL can't 302 the backend into loopback/metadata endpoints (SSRF).
- gateway.py: entity-store lookup failures no longer leak str(exc); log and
  return a fixed detail, matching the agents.py CRUD routes.
- Regression tests for both.

Signed-off-by: mschwab <mschwab@nvidia.com>
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
marcusds added 2 commits July 17, 2026 13:00
Signed-off-by: mschwab <mschwab@nvidia.com>
…bing

- a2a.py: block external-agent URLs that resolve to private/loopback/link-local/
  reserved addresses (e.g. 169.254.169.254 metadata) before any request, in
  fetch/send/stream/probe. Gated by NEMO_AGENTS_ALLOW_PRIVATE_AGENT_HOSTS for
  local dev. (py/full-ssrf #4269)
- log_utils.scrub(): strip CR/LF from user-controlled values before logging;
  applied across gateway/agents/a2a logger calls. (py/log-injection #4270-4279)
- Tests for the guard (blocks private, allows public) and the scrubber.

Signed-off-by: mschwab <mschwab@nvidia.com>
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py Fixed
marcusds added 2 commits July 17, 2026 13:24
…tion

_endpoint_host output feeds logger calls whose URL arg is user-controlled;
sanitize at the source so CodeQL clears py/log-injection #4280/#4281.

Signed-off-by: mschwab <mschwab@nvidia.com>
Runtime enforces exactly-one-of config/url via model_validator, but the
generated OpenAPI schema allowed both/neither. Add json_schema_extra oneOf
so generated client contracts match runtime behavior.

Signed-off-by: mschwab <mschwab@nvidia.com>
@marcusds
marcusds marked this pull request as ready for review July 17, 2026 21:58
@marcusds
marcusds requested a review from mckornfield July 17, 2026 21:58

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/nemo-agents/src/nemo_agents_plugin/a2a.py`:
- Around line 260-261: Remove the endpoint value from the A2AMessageError
messages in both HTTP transport exception handlers, including the handler around
the shown raise and the second handler at the other referenced location.
Preserve the exception type and chained cause while reporting only a generic
connection failure and the safe exception class name, so embedded credentials
cannot reach gateway responses.

In `@plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py`:
- Around line 636-641: Require the request method to be POST before dispatching
to the external agent in the handler around _external_a2a_endpoint. Guard both
the _external_openai_chat and _serve_external_generate_full branches, returning
the existing method-not-allowed response for GET, HEAD, OPTIONS, and other
non-POST methods while preserving current POST behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e90057af-c151-4739-a765-ad924d1c91a3

📥 Commits

Reviewing files that changed from the base of the PR and between 1e75a83 and 11e13f2.

⛔ Files ignored due to path filters (1)
  • sdk/python/nemo-platform/.github/workflows/ci.yml is excluded by !sdk/**
📒 Files selected for processing (14)
  • plugins/nemo-agents/openapi/openapi.yaml
  • plugins/nemo-agents/src/nemo_agents_plugin/a2a.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/agents.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/deployments.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py
  • plugins/nemo-agents/src/nemo_agents_plugin/entities.py
  • plugins/nemo-agents/src/nemo_agents_plugin/log_utils.py
  • plugins/nemo-agents/src/nemo_agents_plugin/schema.py
  • plugins/nemo-agents/tests/unit/test_a2a.py
  • plugins/nemo-agents/tests/unit/test_agents_api.py
  • plugins/nemo-agents/tests/unit/test_deployments_api.py
  • plugins/nemo-agents/tests/unit/test_entities.py
  • plugins/nemo-agents/tests/unit/test_gateway.py
  • plugins/nemo-agents/tests/unit/test_log_utils.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • plugins/nemo-agents/src/nemo_agents_plugin/schema.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/deployments.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/agents.py
  • plugins/nemo-agents/src/nemo_agents_plugin/entities.py
  • plugins/nemo-agents/openapi/openapi.yaml

Comment on lines +260 to +261
except httpx.HTTPError as exc:
raise A2AMessageError(f"could not reach agent at {endpoint} ({exc.__class__.__name__})") from exc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Remove the full endpoint from transport errors.

These exceptions flow into gateway HTTP/SSE responses. An endpoint containing embedded credentials would be disclosed to callers.

Proposed fix
-        raise A2AMessageError(f"could not reach agent at {endpoint} ({exc.__class__.__name__})") from exc
+        raise A2AMessageError(f"could not reach agent ({exc.__class__.__name__})") from exc

Apply the same change to both transport handlers.

Also applies to: 349-350

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/nemo-agents/src/nemo_agents_plugin/a2a.py` around lines 260 - 261,
Remove the endpoint value from the A2AMessageError messages in both HTTP
transport exception handlers, including the handler around the shown raise and
the second handler at the other referenced location. Preserve the exception type
and chained cause while reporting only a generic connection failure and the safe
exception class name, so embedded credentials cannot reach gateway responses.

Comment on lines +636 to +641
endpoint = _external_a2a_endpoint(agent, name)
if trailing_uri.endswith("chat/completions"):
body = await _read_json_object(request)
return await _external_openai_chat(name, endpoint, body, _card_supports_streaming(agent))
if trailing_uri.startswith("generate"):
return await _serve_external_generate_full(name, endpoint, request)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Require POST before invoking an external agent.

This handler is also reached through read-scoped GET/HEAD/OPTIONS routes. A GET carrying a JSON body can therefore invoke chat or generation without write scope.

Proposed fix
     endpoint = _external_a2a_endpoint(agent, name)
+    if request.method != "POST":
+        raise HTTPException(
+            status_code=405,
+            detail="External agent invocation requires POST.",
+            headers={"Allow": "POST"},
+        )
     if trailing_uri.endswith("chat/completions"):
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
endpoint = _external_a2a_endpoint(agent, name)
if trailing_uri.endswith("chat/completions"):
body = await _read_json_object(request)
return await _external_openai_chat(name, endpoint, body, _card_supports_streaming(agent))
if trailing_uri.startswith("generate"):
return await _serve_external_generate_full(name, endpoint, request)
endpoint = _external_a2a_endpoint(agent, name)
if request.method != "POST":
raise HTTPException(
status_code=405,
detail="External agent invocation requires POST.",
headers={"Allow": "POST"},
)
if trailing_uri.endswith("chat/completions"):
body = await _read_json_object(request)
return await _external_openai_chat(name, endpoint, body, _card_supports_streaming(agent))
if trailing_uri.startswith("generate"):
return await _serve_external_generate_full(name, endpoint, request)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py` around lines
636 - 641, Require the request method to be POST before dispatching to the
external agent in the handler around _external_a2a_endpoint. Guard both the
_external_openai_chat and _serve_external_generate_full branches, returning the
existing method-not-allowed response for GET, HEAD, OPTIONS, and other non-POST
methods while preserving current POST behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants