Skip to content

feat: add azure-ai-agentserver-activity package (Activity Protocol)#47489

Merged
Hameedkunkanoor merged 65 commits into
mainfrom
hameed-kunkanoor/agentserver-activity-protocol
Jul 21, 2026
Merged

feat: add azure-ai-agentserver-activity package (Activity Protocol)#47489
Hameedkunkanoor merged 65 commits into
mainfrom
hameed-kunkanoor/agentserver-activity-protocol

Conversation

@Hameedkunkanoor

Copy link
Copy Markdown
Contributor

Initial release of the Activity Protocol host package for Azure AI Foundry hosted agents. Provides Starlette-based HTTP endpoints for Activity Protocol traffic with M365 Agents SDK integration.

Package (azure-ai-agentserver-activity 1.0.0b1):

  • ActivityAgentServerHost with POST /activity/messages and /api/messages
  • Decorator API: @app.activity(type), @app.error for zero-config usage
  • Custom handler support for full M365 SDK control
  • Auto-initialization of M365 SDK from environment variables
  • MSAL auth patches for Foundry container MAIB auth (apply_msal_patches)
  • Activity/session ID sanitization for header injection defense
  • OpenTelemetry distributed tracing and W3C Baggage propagation
  • Error-source classification (x-platform-error-source)

Samples:

  • simple_activity_agent (echo bot)
  • streaming_activity_agent (Azure OpenAI streaming)
  • cards_activity_agent (Adaptive/Hero/Thumbnail/Receipt cards)
  • auto_signin_activity_agent (OAuth with Graph/GitHub)
  • semantic_kernel_activity_agent (SK agent with tools, multi-turn)
  • suggested_actions_activity_agent (quick-reply buttons)

Tests: 26 tests covering routes, session resolution, error classification, tracing, decorator pattern, and ID sanitization edge cases.

Initial release of the Activity Protocol host package for Azure AI
Foundry hosted agents. Provides Starlette-based HTTP endpoints for
Activity Protocol traffic with M365 Agents SDK integration.

Package (azure-ai-agentserver-activity 1.0.0b1):
- ActivityAgentServerHost with POST /activity/messages and /api/messages
- Decorator API: @app.activity(type), @app.error for zero-config usage
- Custom handler support for full M365 SDK control
- Auto-initialization of M365 SDK from environment variables
- MSAL auth patches for Foundry container MAIB auth (apply_msal_patches)
- Session ID resolution (query param, header, config, UUID fallback)
- Activity/session ID sanitization for header injection defense
- OpenTelemetry distributed tracing and W3C Baggage propagation
- Error-source classification (x-platform-error-source)

Samples:
- simple_activity_agent (echo bot)
- streaming_activity_agent (Azure OpenAI streaming)
- cards_activity_agent (Adaptive/Hero/Thumbnail/Receipt cards)
- auto_signin_activity_agent (OAuth with Graph/GitHub)
- semantic_kernel_activity_agent (SK agent with tools, multi-turn)
- suggested_actions_activity_agent (quick-reply buttons)

Tests: 26 tests covering routes, session resolution, error classification,
tracing, decorator pattern, and ID sanitization edge cases.
Copilot AI review requested due to automatic review settings June 15, 2026 01:13
@Hameedkunkanoor Hameedkunkanoor changed the title feat: add azure-ai-agentserver-activity package (Activity Protocol host) feat: add azure-ai-agentserver-activity package (Activity Protocol) Jun 15, 2026
@github-actions github-actions Bot added the Hosted Agents sdk/agentserver/* label Jun 15, 2026

Copilot AI 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.

Pull request overview

This PR introduces a new preview package, azure-ai-agentserver-activity (Activity Protocol host), adding Starlette endpoints intended for Azure AI Foundry hosted agents and integrating with the Microsoft 365 Agents SDK (decorator mode + custom-handler mode). It also wires the new package into the agentserver CI/test targeting so it is built and validated with the rest of the service directory.

Changes:

  • Added azure-ai-agentserver-activity package with ActivityAgentServerHost, M365 bridge + MSAL patches, ID sanitization, tracing/baggage, and error-source classification.
  • Added unit tests and multiple runnable samples (simple, streaming, cards, suggested actions, semantic-kernel, auto sign-in).
  • Updated sdk/agentserver/ci.yml and sdk/agentserver/tests.yml to include the new package in artifact/test targeting.

Reviewed changes

Copilot reviewed 39 out of 40 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
sdk/agentserver/tests.yml Adds activity package to test targeting string.
sdk/agentserver/ci.yml Adds activity package to pipeline artifacts.
sdk/agentserver/azure-ai-agentserver-activity/tests/test_tracing.py Adds tracing + baggage propagation tests.
sdk/agentserver/azure-ai-agentserver-activity/tests/test_session_headers.py Adds session resolution/response header tests.
sdk/agentserver/azure-ai-agentserver-activity/tests/test_server_routes.py Adds route/endpoint behavior tests.
sdk/agentserver/azure-ai-agentserver-activity/tests/test_sanitize_id.py Adds _sanitize_id edge-case tests.
sdk/agentserver/azure-ai-agentserver-activity/tests/test_id_sanitization.py Adds end-to-end activity-id sanitization tests.
sdk/agentserver/azure-ai-agentserver-activity/tests/test_error_source_classification.py Adds tests for x-platform-error-source classification.
sdk/agentserver/azure-ai-agentserver-activity/tests/test_decorator_pattern.py Adds decorator wiring tests for bridge handler.
sdk/agentserver/azure-ai-agentserver-activity/tests/conftest.py Adds shared fixtures + marker declaration.
sdk/agentserver/azure-ai-agentserver-activity/samples/README.md Documents available samples and usage patterns.
sdk/agentserver/azure-ai-agentserver-activity/samples/simple_activity_agent/simple_activity_agent.py Simple decorator-based echo agent sample.
sdk/agentserver/azure-ai-agentserver-activity/samples/simple_activity_agent/requirements.txt Sample dependencies.
sdk/agentserver/azure-ai-agentserver-activity/samples/streaming_activity_agent/streaming_activity_agent.py Streaming Azure OpenAI sample using streaming_response.
sdk/agentserver/azure-ai-agentserver-activity/samples/streaming_activity_agent/requirements.txt Sample dependencies.
sdk/agentserver/azure-ai-agentserver-activity/samples/cards_activity_agent/cards_activity_agent.py Rich cards sample (adaptive/hero/thumbnail/receipt).
sdk/agentserver/azure-ai-agentserver-activity/samples/cards_activity_agent/requirements.txt Sample dependencies.
sdk/agentserver/azure-ai-agentserver-activity/samples/suggested_actions_activity_agent/suggested_actions_activity_agent.py Suggested actions / quick-reply buttons sample.
sdk/agentserver/azure-ai-agentserver-activity/samples/suggested_actions_activity_agent/requirements.txt Sample dependencies.
sdk/agentserver/azure-ai-agentserver-activity/samples/semantic_kernel_activity_agent/semantic_kernel_activity_agent.py Semantic Kernel multi-turn + streaming sample.
sdk/agentserver/azure-ai-agentserver-activity/samples/semantic_kernel_activity_agent/requirements.txt Sample dependencies.
sdk/agentserver/azure-ai-agentserver-activity/samples/auto_signin_activity_agent/auto_signin_activity_agent.py OAuth auto sign-in sample using handler pattern + MSAL patches.
sdk/agentserver/azure-ai-agentserver-activity/samples/auto_signin_activity_agent/requirements.txt Sample dependencies.
sdk/agentserver/azure-ai-agentserver-activity/README.md Package README describing host usage and API surface.
sdk/agentserver/azure-ai-agentserver-activity/pyrightconfig.json Pyright config excluding samples.
sdk/agentserver/azure-ai-agentserver-activity/pyproject.toml Package metadata, deps, and Azure SDK build config.
sdk/agentserver/azure-ai-agentserver-activity/MANIFEST.in sdist packaging manifest.
sdk/agentserver/azure-ai-agentserver-activity/LICENSE MIT license file.
sdk/agentserver/azure-ai-agentserver-activity/dev_requirements.txt Dev/test requirements.
sdk/agentserver/azure-ai-agentserver-activity/cspell.json Spellchecker configuration.
sdk/agentserver/azure-ai-agentserver-activity/CHANGELOG.md Initial preview release changelog.
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/py.typed Marks package as typed.
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_version.py Defines package version.
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_m365_bridge.py Implements M365 SDK lazy-init + bridge handler + MSAL patching.
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_constants.py Defines Activity protocol constants and header names.
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py Implements ActivityAgentServerHost endpoint, sanitization, tracing, and error handling.
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/init.py Exposes public API (ActivityAgentServerHost, apply_msal_patches).
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/init.py Namespace package init.
sdk/agentserver/azure-ai-agentserver-activity/azure/ai/init.py Namespace package init.
sdk/agentserver/azure-ai-agentserver-activity/azure/init.py Namespace package init.

Comment thread sdk/agentserver/azure-ai-agentserver-activity/tests/test_decorator_pattern.py Outdated
Comment thread sdk/agentserver/azure-ai-agentserver-activity/README.md
Comment thread sdk/agentserver/azure-ai-agentserver-activity/README.md
…al pattern

- Remove AGENTAPPLICATION fallback defaults from _initialize_default_env_vars
  (STARTTYPINGTIMER, REMOVERECIPIENTMENTION, NORMALIZEMENTIONS, USERAUTHORIZATION__AUTOSIGNIN)
- Derive CONNECTIONS__ vars from Foundry-native env vars (FOUNDRY_AGENT_BLUEPRINT_CLIENT_ID,
  FOUNDRY_AGENT_TENANT_ID) using helper functions _get_nonempty / _set_if_missing
- Replace old archive-style samples (auto_signin, cards, semantic_kernel, simple,
  streaming, suggested_actions) with minimal invocations-pattern samples:
  - echo-agent: Tier 1 zero-config, auto-init from env vars
  - multi-protocol-agent: Tier 2 Activity + Invocations on one host
  - self-hosted-agent: Tier 3 full M365 SDK control
…ging

- Add _BaggageSpanProcessor (subclass of SDK SpanProcessor) to promote all W3C baggage entries onto every child span
- Replace single-logger filter with global setLogRecordFactory so SessionId/UserIsolationKey/ChatIsolationKey/Protocol appear on all loggers' records
- Resolve correlation ids and bind contextvars at the top of the endpoint so boundary logs are populated
- Add structured request/response logging plus activity.request/activity.response/activity.error span events with full values
…cing

The Foundry gateway already emits the turn-level request span and core's TraceContextMiddleware + _FoundryEnrichmentSpanProcessor propagate trace context and stamp identity/session/conversation/project onto every child span. The activity package now only sets baggage (its source-of-truth correlation keys) and emits structured request/response/error logs, avoiding a redundant wrapper span and duplicate/conflicting gen_ai.* + microsoft.foundry.project.id attributes. Verified live (groot-v2 v6): no handle_activity spans, child spans still carry all correlation keys + identity, reminder/proactive flow fully traced, zero errors.
- Add aiohttp constraint to core package (>=3.10.0,<4.0.0a1) to prevent pre-release 4.0.0a1 which fails to compile on Python 3.11 due to missing longintrepr.h
- Import Token from opentelemetry.context for proper type hinting
- Type baggage_token as Optional[Token[Any]] instead of Optional[object]
- Add explicit Response type annotation for handler response with type: ignore for JSONResponse variance
- Remove unused JSONResponse import
- Fix opentelemetry import grouping (separate from starlette)
- Add missing docstring parameters: 'record' to _enrich_record
- Add complete docstrings with param/return types for:
  - _sanitize_id: document value param and return
  - _classify_error: document exc param and return tuple
  - activity() decorator: add return type
  - error() decorator: add return type and fn param type
  - _response_body_preview: document response, limit, and return
  - _create_activity_endpoint: document request param and return

Copilot AI 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.

Pull request overview

Copilot reviewed 52 out of 53 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (5)

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_m365_bridge.py:271

  • The bridge creates a trusted ClaimsIdentity without validating the inbound Authorization header. HttpAdapterBase.process_request treats get_claims_identity() as the identity already attached by authentication middleware, so missing or arbitrary bearer tokens reach the agent as authenticated requests. Validate the JWT with the M365 Starlette middleware (or equivalent) and pass only its validated claims; outbound credential selection must not replace the inbound caller identity.
        # Synthesize the outbound claims for this turn and attach them to the
        # request so the framework adapter reads them via get_claims_identity
        # (mirrors how the SDK adapters read claims set by auth middleware).
        request.state.claims_identity = _build_outbound_claims(
            ClaimsIdentity,
            digital_worker=digital_worker,
            is_hosted=is_hosted,
            bot_app_id=bot_app_id,
        )

sdk/agentserver/azure-ai-agentserver-activity/samples/04-custom-handler/main.py:63

  • This sends a managed-identity bearer token to a URL derived directly from the untrusted activity serviceUrl. The warning does not mitigate the executable SSRF/token-exfiltration path: anyone who can call this sample can choose the token recipient. Remove the credentialed outbound call or authenticate the activity and enforce a strict Bot Connector endpoint allowlist before minting or attaching the token.
    url = f"{service_url.rstrip('/')}/v3/conversations/{conversation_id}/activities"
    payload = {"type": "message", "text": text}
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}

    async with aiohttp.ClientSession() as session:
        async with session.post(url, json=payload, headers=headers) as resp:

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:138

  • This global record factory labels every subsequent log record as activity, regardless of which route is executing. In the included MultiProtocolHost, invocation-handler logs will therefore be exported with Protocol=activity. Track the active protocol in request-scoped context and set/reset it in the activity endpoint instead of stamping a process-wide constant.
    if not hasattr(record, LogRecordFields.PROTOCOL):
        setattr(record, LogRecordFields.PROTOCOL, ActivityConstants.PROTOCOL)

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:750

  • All handler-returned errors default to upstream, including the M365 adapter's 400 response for a missing activity type or conversation.id. Those are malformed caller payloads and should be classified as user; otherwise platform routing attributes client errors to the agent. Preserve an origin-specific classification from the adapter (or classify its validation responses before this fallback).
            # The package contract guarantees an error-source on every error
            # response. A handler (or the M365 adapter) can return a 4xx/5xx
            # directly without raising, which skips the exception path below, so
            # classify any unclassified error status as ``upstream`` here.
            status_code = getattr(response, "status_code", 0)
            if status_code >= 400 and ERROR_SOURCE not in response.headers:
                response.headers[ERROR_SOURCE] = ErrorSource.UPSTREAM

sdk/agentserver/azure-ai-agentserver-activity/README.md:124

  • The PR description advertises streaming, cards, auto-sign-in, Semantic Kernel, and suggested-actions samples, but the package's sample set contains only echo, component override, self-hosted, custom-handler, and multi-protocol scenarios. Either add the advertised scenarios or update the PR description so reviewers and users are not promised absent examples.
See the [samples directory](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/agentserver/azure-ai-agentserver-activity/samples) for runnable scenarios, ordered as a learning path:

- `01-echo` — the simplest agent: the host builds the M365 stack and you register handlers directly on it to echo the user's message back.
- `02-custom-components` — override one or more M365 components (storage, connection manager, adapter, authorization, config) while the host builds the rest.
- `03-self-hosted-app` — build the M365 `AgentApplication` yourself and host it by passing `agent_app=`.
- `04-custom-handler` — own the request pipeline with `request_handler=` (the M365 SDK is not initialized).
- `05-multi-protocol` — compose the Activity and Invocations protocols on a single server.

@github-actions

This comment has been minimized.

Copilot AI review requested due to automatic review settings July 21, 2026 08:58
@Hameedkunkanoor
Hameedkunkanoor force-pushed the hameed-kunkanoor/agentserver-activity-protocol branch from f0c8ccc to 754b594 Compare July 21, 2026 08:58

Copilot AI 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.

Pull request overview

Copilot reviewed 51 out of 52 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (5)

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_m365_bridge.py:270

  • The bridge never validates the inbound Authorization header; instead it creates a trusted ClaimsIdentity for every request. HttpAdapterBase.process_request trusts get_claims_identity() (the standard M365 hosts populate it only after JWT validation), so requests with a missing or invalid token can invoke the agent and trigger authenticated outbound replies. Validate the inbound bearer token before assigning claims, and only construct the outbound identity after authentication succeeds.
        request.state.claims_identity = _build_outbound_claims(
            ClaimsIdentity,
            digital_worker=digital_worker,
            is_hosted=is_hosted,
            bot_app_id=bot_app_id,

sdk/agentserver/azure-ai-agentserver-activity/samples/04-custom-handler/main.py:60

  • This runnable sample sends a managed-identity bearer token to an attacker-controlled serviceUrl. The warning does not prevent exploitation: any caller that reaches the unauthenticated custom-handler endpoint can supply its own URL and receive the Bot Connector token or induce SSRF. Authenticate the activity and enforce a trusted Connector endpoint allowlist before minting or attaching credentials; otherwise remove this outbound call from the sample.
    url = f"{service_url.rstrip('/')}/v3/conversations/{conversation_id}/activities"
    payload = {"type": "message", "text": text}
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:530

  • The zero-configuration hosted path also reaches this fallback, so every hosted instance uses process-local MemoryStorage. M365 TurnState and OAuth state will be lost on restart and diverge across replicas, making multi-turn hosted agents unreliable. Select durable Foundry storage when self.config.is_hosted, or fail fast and require callers to provide a durable storage backend in hosted environments.
        # TODO: use a durable hosted store (FoundryStorage) when running in a
        # Foundry-hosted container; MemoryStorage is the local-testing default.
        # pylint: disable=import-error,no-name-in-module
        from microsoft_agents.hosting.core import MemoryStorage

        return MemoryStorage()

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_cloud_adapter.py:80

  • Only the top-level JSON-object check runs before this call. The M365 HttpAdapterBase.process_request performs Activity.model_validate(body) without catching validation errors, so malformed nested activity data (for example, a string conversation) escapes here and is converted by _run_handler into a 500/upstream response instead of a 400/user response. Catch the model-validation failure or validate the Activity before delegation and return the invalid-request envelope.
        # Process using the shared base implementation
        http_response = await self._adapter.process_request(adapted_request, agent)

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:750

  • This fallback misclassifies M365 adapter validation responses. HttpAdapterBase returns 400 for a malformed Activity and 401 for authorization failures; _to_starlette_response does not add an error source, so both are stamped upstream here even though they are caller/user errors. Preserve an adapter classification or classify these known M365 4xx responses as user before applying the upstream fallback.
            if status_code >= 400 and ERROR_SOURCE not in response.headers:
                response.headers[ERROR_SOURCE] = ErrorSource.UPSTREAM

@github-actions

This comment has been minimized.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2ee2e202-5f6a-4262-896b-4dae76544d0d
Copilot AI review requested due to automatic review settings July 21, 2026 09:55

Copilot AI 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.

Pull request overview

Copilot reviewed 52 out of 53 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (4)

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:750

  • StarletteCloudAdapter converts M365 validation failures to a 400 invalid_request response without setting an error source, so this fallback labels them upstream. A malformed activity object (for example, one missing its required type) is a caller error and should be classified as user; stamp adapter-generated 4xx responses accordingly before this fallback.
            status_code = getattr(response, "status_code", 0)
            if status_code >= 400 and ERROR_SOURCE not in response.headers:
                response.headers[ERROR_SOURCE] = ErrorSource.UPSTREAM

sdk/agentserver/azure-ai-agentserver-activity/samples/04-custom-handler/main.py:60

  • This runnable sample sends a managed-identity bearer token to the request-controlled serviceUrl without inbound JWT validation or a trusted-host allowlist. The warning does not prevent deployment; an attacker reaching this endpoint can trigger SSRF and send the token to an arbitrary host. Authenticate the activity and validate the connector endpoint before minting or attaching credentials, or remove the authenticated outbound call.
    url = f"{service_url.rstrip('/')}/v3/conversations/{conversation_id}/activities"
    payload = {"type": "message", "text": text}
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}

sdk/agentserver/azure-ai-agentserver-activity/README.md:124

  • The PR description promises streaming, cards, auto-sign-in, Semantic Kernel, and suggested-actions samples, but the added sample tree contains only these five different scenarios. Add the advertised samples or update the PR description so reviewers and users are not told functionality is included when it is absent.
- `01-echo` — the simplest agent: the host builds the M365 stack and you register handlers directly on it to echo the user's message back.
- `02-custom-components` — override one or more M365 components (storage, connection manager, adapter, authorization, config) while the host builds the rest.
- `03-self-hosted-app` — build the M365 `AgentApplication` yourself and host it by passing `agent_app=`.
- `04-custom-handler` — own the request pipeline with `request_handler=` (the M365 SDK is not initialized).
- `05-multi-protocol` — compose the Activity and Invocations protocols on a single server.

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:530

  • The default hosted path unconditionally falls back to MemoryStorage. Turn state will be lost on restart and diverge across replicas, breaking multi-turn and OAuth flows even though this host targets Foundry containers. Use a durable hosted store by default, or require callers to provide storage when self.config.is_hosted is true.
        # Foundry-hosted container; MemoryStorage is the local-testing default.
        # pylint: disable=import-error,no-name-in-module
        from microsoft_agents.hosting.core import MemoryStorage

        return MemoryStorage()

Comment thread sdk/agentserver/azure-ai-agentserver-activity/pyproject.toml
Comment thread sdk/agentserver/azure-ai-agentserver-activity/api.metadata.yml Outdated
…bumped apistub pin; API surface unchanged)
Copilot AI review requested due to automatic review settings July 21, 2026 10:04

Copilot AI 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.

Pull request overview

Copilot reviewed 54 out of 55 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_m365_bridge.py:270

  • The bridge never validates the inbound Authorization header. These claims are synthesized from local configuration, and HttpAdapterBase.process_request() trusts get_claims_identity() without validating a token, so requests with a missing or invalid bearer token can execute the agent as authenticated and trigger credentialed outbound actions. Authenticate the inbound JWT with the M365 token validator/ASGI middleware and pass those validated inbound claims here; outbound credential selection must remain separate.
        request.state.claims_identity = _build_outbound_claims(
            ClaimsIdentity,
            digital_worker=digital_worker,
            is_hosted=is_hosted,
            bot_app_id=bot_app_id,

sdk/agentserver/azure-ai-agentserver-activity/samples/04-custom-handler/main.py:63

  • This sample sends a managed-identity bearer token to service_url, which is taken directly from an unauthenticated request. An attacker can supply their own URL and exfiltrate the token or use the container for SSRF; the warning above does not make the runnable sample safe. Authenticate the inbound Bot Framework JWT and enforce an HTTPS allowlist before acquiring or attaching credentials, or remove this outbound call from the sample.
    url = f"{service_url.rstrip('/')}/v3/conversations/{conversation_id}/activities"
    payload = {"type": "message", "text": text}
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}

    async with aiohttp.ClientSession() as session:
        async with session.post(url, json=payload, headers=headers) as resp:

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:530

  • The default constructor uses MemoryStorage even in a Foundry-hosted container. Turn state is therefore isolated per replica and lost on restart, which breaks multi-turn state and authorization flows under normal hosted scaling. Keep this fallback only for local execution; hosted mode should resolve durable storage or fail construction unless the caller supplies one.
        # TODO: use a durable hosted store (FoundryStorage) when running in a
        # Foundry-hosted container; MemoryStorage is the local-testing default.
        # pylint: disable=import-error,no-name-in-module
        from microsoft_agents.hosting.core import MemoryStorage

        return MemoryStorage()

sdk/agentserver/azure-ai-agentserver-activity/README.md:124

  • The PR description advertises six streaming/cards/OAuth/Semantic Kernel/suggested-actions samples, but this package lists five different samples and none of those advertised scenarios are present. Either add the promised samples or update the PR description so reviewers and release notes reflect what is actually shipping.
- `01-echo` — the simplest agent: the host builds the M365 stack and you register handlers directly on it to echo the user's message back.
- `02-custom-components` — override one or more M365 components (storage, connection manager, adapter, authorization, config) while the host builds the rest.
- `03-self-hosted-app` — build the M365 `AgentApplication` yourself and host it by passing `agent_app=`.
- `04-custom-handler` — own the request pipeline with `request_handler=` (the M365 SDK is not initialized).
- `05-multi-protocol` — compose the Activity and Invocations protocols on a single server.

Copilot AI review requested due to automatic review settings July 21, 2026 10:11

Copilot AI 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.

Pull request overview

Copilot reviewed 54 out of 55 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (3)

sdk/agentserver/azure-ai-agentserver-activity/samples/04-custom-handler/main.py:63

  • This sample sends a managed-identity Bot Connector bearer token to the caller-controlled serviceUrl. The warning documents the SSRF/token-exfiltration path but does not prevent it, so copying or running the sample in a hosted container exposes a production credential. Mandatory: authenticate the inbound activity and validate the parsed URL against the supported Bot Connector endpoint allowlist before acquiring or attaching the token.
        async with session.post(url, json=payload, headers=headers) as resp:

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:615

  • Malformed-JSON and non-object requests return through _build_bad_request, but this response only receives the session ID; unlike successful and handler-error paths, it omits the required x-agent-activity-id response header. Generate a sanitized activity ID for this early error path as well so the header contract is consistent on every response.
            headers=_apply_error_source_headers({}, ErrorSource.USER),

sdk/agentserver/azure-ai-agentserver-activity/README.md:124

  • The PR description promises six different samples (streaming, cards, auto-sign-in, Semantic Kernel, suggested actions, and echo), while the package adds these five samples covering echo, components, self-hosting, a custom handler, and multi-protocol composition. Either add the advertised scenarios or update the PR description so reviewers and release notes reflect the shipped sample set.
- `01-echo` — the simplest agent: the host builds the M365 stack and you register handlers directly on it to echo the user's message back.
- `02-custom-components` — override one or more M365 components (storage, connection manager, adapter, authorization, config) while the host builds the rest.
- `03-self-hosted-app` — build the M365 `AgentApplication` yourself and host it by passing `agent_app=`.
- `04-custom-handler` — own the request pipeline with `request_handler=` (the M365 SDK is not initialized).
- `05-multi-protocol` — compose the Activity and Invocations protocols on a single server.

@github-actions

Copy link
Copy Markdown
Contributor
[Pilot] PR Pipeline Failure Analysis

A CI pipeline failed on this pull request. Here is an automated analysis of what went wrong and how to get the build green.

What failed

The Mindependency (minimum dependency) validation step failed (build #6593821). The check found dependencies declared in the new packages that are not listed in the repo's shared_requirements.txt file, and also found version constraints (>=) that conflict with or exceed what shared_requirements.txt allows:

  • opentelemetry-api >=1.43.0 — required by azure-ai-agentserver-activity and azure-ai-agentserver-core
  • opentelemetry-sdk >=1.43.0 — required by azure-ai-agentserver-core
  • pyyaml >=6.0 — required by azure-ai-agentserver-optimization
  • starlette >=0.45.0 — required by azure-ai-agentserver-core
  • Unknown dependencies (not present in shared_requirements.txt at all): microsoft-agents-authentication-msal, microsoft-agents-hosting-core, microsoft-agents-activity

Recommended next steps

  1. Add missing packages to shared_requirements.txt: Add microsoft-agents-authentication-msal, microsoft-agents-hosting-core, and microsoft-agents-activity (with appropriate minimum versions) to the repo's shared_requirements.txt.
  2. Update or align version pins: Ensure the minimum version constraints for opentelemetry-api, opentelemetry-sdk, pyyaml, and starlette in your package's setup.py/pyproject.toml match what is in shared_requirements.txt, or update shared_requirements.txt to reflect the required minimums (coordinate with SDK maintainers).
  3. See the CI troubleshooting guide: https://aka.ms/ci-fix
  4. Push new commits to address the failures; this comment updates automatically on the next failing run.
Raw pipeline analysis (azsdk ci analyze)
Analyzing pipeline https://github.com/Azure/azure-sdk-for-python/pull/47489...
--------------------------------------------------------------------------------
Failed Tasks
--------------------------------------------------------------------------------
### Errors:
--> /tmp/log-analysis-cbacc88c9188498aa4ea6373b5c81951.txt:184
opentelemetry-api
>=1.43.0
  * azure-ai-agentserver-activity
  * azure-ai-agentserver-core

opentelemetry-sdk
>=1.43.0
  * azure-ai-agentserver-core

pyyaml
>=6.0
  * azure-ai-agentserver-optimization

starlette
>=0.45.0
  * azure-ai-agentserver-core

Unknown dependencies found that are not present in shared_requirements.txt. Use --verbose for details.
Unknown dependencies: ['microsoft-agents-authentication-msal', 'microsoft-agents-hosting-core', 'microsoft-agents-activity']

##[error]PowerShell exited with code '1'.

### Pipeline: https://dev.azure.com/azure-sdk/public/_build/results?buildId=6593821

Copilot detected the failing pipeline and generated the analysis above. To have it attempt a fix automatically, reply with @copilot please fix the failing pipeline on this PR.

Generated by Pipeline Analysis - Next Steps · 20.2 AIC · ⌖ 6.2 AIC · ⊞ 6.6K ·

Copilot AI review requested due to automatic review settings July 21, 2026 10:50
@Hameedkunkanoor
Hameedkunkanoor requested a review from a team as a code owner July 21, 2026 10:50

Copilot AI 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.

Pull request overview

Copilot reviewed 55 out of 56 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (5)

sdk/agentserver/azure-ai-agentserver-activity/samples/04-custom-handler/main.py:63

  • This sample sends a managed-identity bearer token to the payload-controlled serviceUrl. The warning does not prevent a runnable sample from enabling SSRF and token exfiltration when copied or deployed. Authenticate the inbound activity and validate the destination against the Bot Connector allowlist before acquiring or attaching the token; otherwise keep this sample credential-free.
    url = f"{service_url.rstrip('/')}/v3/conversations/{conversation_id}/activities"
    payload = {"type": "message", "text": text}
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}

    async with aiohttp.ClientSession() as session:
        async with session.post(url, json=payload, headers=headers) as resp:

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_cloud_adapter.py:80

  • Malformed activity objects can become 500/upstream responses here. The M365 HttpAdapterBase.process_request calls Activity.model_validate(body) outside its own error handling, so payloads with invalid field types raise a validation exception; _run_handler then catches it as an internal handler failure. Convert M365 activity-validation failures to a 400 response classified as user.
        http_response = await self._adapter.process_request(adapted_request, agent)

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:750

  • This blanket fallback misclassifies M365-generated client errors as upstream. For example, a JSON object missing type or conversation.id is returned by HttpAdapterBase as 400, reaches this branch without a source header, and is labeled upstream even though the package contract classifies request validation failures as user. Have the bridge annotate M365 4xx validation/auth responses as user before this generic handler fallback.
            # The package contract guarantees an error-source on every error
            # response. A handler (or the M365 adapter) can return a 4xx/5xx
            # directly without raising, which skips the exception path below, so
            # classify any unclassified error status as ``upstream`` here.
            status_code = getattr(response, "status_code", 0)
            if status_code >= 400 and ERROR_SOURCE not in response.headers:
                response.headers[ERROR_SOURCE] = ErrorSource.UPSTREAM

sdk/agentserver/azure-ai-agentserver-activity/azure/ai/agentserver/activity/_activity.py:530

  • The default hosted-agent path also reaches this unconditional MemoryStorage, so turn/OAuth state is lost on restart and split across replicas. That makes the advertised zero-config hosted path unreliable for multi-turn agents. Until a durable Foundry store is available, require callers running with self.config.is_hosted to supply storage rather than silently selecting a local-only backend.
        # TODO: use a durable hosted store (FoundryStorage) when running in a
        # Foundry-hosted container; MemoryStorage is the local-testing default.
        # pylint: disable=import-error,no-name-in-module
        from microsoft_agents.hosting.core import MemoryStorage

        return MemoryStorage()

sdk/agentserver/azure-ai-agentserver-activity/README.md:124

  • The PR description promises streaming, cards, auto-sign-in, Semantic Kernel, and suggested-actions samples, but this package includes a different five-sample set and none of those scenarios. Either add the described samples or update the PR description so the advertised deliverables match the change.
- `01-echo` — the simplest agent: the host builds the M365 stack and you register handlers directly on it to echo the user's message back.
- `02-custom-components` — override one or more M365 components (storage, connection manager, adapter, authorization, config) while the host builds the rest.
- `03-self-hosted-app` — build the M365 `AgentApplication` yourself and host it by passing `agent_app=`.
- `04-custom-handler` — own the request pipeline with `request_handler=` (the M365 SDK is not initialized).
- `05-multi-protocol` — compose the Activity and Invocations protocols on a single server.

Comment thread sdk/agentserver/azure-ai-agentserver-invocations/pyproject.toml
@Hameedkunkanoor
Hameedkunkanoor enabled auto-merge (squash) July 21, 2026 11:29
@Hameedkunkanoor
Hameedkunkanoor merged commit 1dc6baa into main Jul 21, 2026
28 checks passed
@Hameedkunkanoor
Hameedkunkanoor deleted the hameed-kunkanoor/agentserver-activity-protocol branch July 21, 2026 12:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Hosted Agents sdk/agentserver/*

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants