Skip to content

feat: Transparent TLS + ServiceBinding Envelope Support for Agent Gateway Module#220

Open
smahr wants to merge 33 commits into
SAP:mainfrom
smahr:main
Open

feat: Transparent TLS + ServiceBinding Envelope Support for Agent Gateway Module#220
smahr wants to merge 33 commits into
SAP:mainfrom
smahr:main

Conversation

@smahr

@smahr smahr commented Jul 9, 2026

Copy link
Copy Markdown

Transparent TLS Mode and ServiceBinding Support

Overview

This PR adds support for Transparent TLS mode for Agent Gateway integration, allowing agents to run in environments where mTLS is handled externally (e.g., by an API Gateway or service mesh). It also includes improvements to dependency management and credential handling.

Key Features

Transparent TLS Mode

Agents can now operate without managing mTLS certificates directly. Instead of file-based certificates, agents use environment variables for authentication:

  • INTEGRATION_CLIENT_ID - OAuth2 client ID
  • INTEGRATION_AUTH_URL - IAS token service URL
  • INTEGRATION_GATEWAY_URL - Agent Gateway base URL
  • INTEGRATION_DEPENDENCIES (optional) - JSON array of integration dependencies

Benefits:

  • Simplified agent deployment in managed environments
  • External mTLS handling by infrastructure (API Gateway, Istio, etc.)
  • Backward compatible with existing file-based credential flow (STANDARD mode)

ServiceBinding Support

Full support for the servicebinding.io specification:

  • Automatic discovery via SERVICE_BINDING_ROOT environment variable
  • Scans for bindings with type: integration-credentials
  • Supports both flat and nested credential structures

Integration Dependencies Improvements

  • Optional dependencies: integrationDependencies is now optional and defaults to an empty array
  • Resilient parsing: Gracefully handles missing or empty dependencies
  • Dual format support:
    • File credentials: {"ordId": "...", "data": {"globalTenantId": "..."}}
    • Environment: {"ordId": "...", "globalTenantId": "..."}

OpenTelemetry Dependencies Made Optional

OpenTelemetry dependencies are now optional to support environments where telemetry is not required or handled separately.

Implementation Details

Core Changes

  • _customer.py: Added transparent TLS mode detection and credential loading
  • _dependencies_resolver.py: Added missing module for integration dependency resolution
  • agw_client.py: Auto-detection of TLS mode (transparent vs standard)
  • pyproject.toml: Version bumped to 0.33.24, OpenTelemetry made optional

Mode Detection

The SDK automatically detects the appropriate mode based on environment:

  1. Transparent Mode: If INTEGRATION_CLIENT_ID, INTEGRATION_AUTH_URL, and INTEGRATION_GATEWAY_URL are set
  2. Standard Mode (file-based): If credentials file is found via:
    • AGW_CREDENTIALS_PATH environment variable
    • SERVICE_BINDING_ROOT scan for integration-credentials type
    • Legacy path /etc/ums/credentials/credentials

Environment Variables

Transparent TLS Mode:

INTEGRATION_CLIENT_ID=<your-client-id>
INTEGRATION_AUTH_URL=https://<tenant>.accounts.ondemand.com/oauth2/token
INTEGRATION_GATEWAY_URL=https://<gateway-url>
INTEGRATION_DEPENDENCIES='[{"ordId":"sap.example:api:v1","globalTenantId":"123456"}]'  # optional

ServiceBinding Discovery:

SERVICE_BINDING_ROOT=/bindings  # or defaults to /bindings

Standard Mode:

AGW_CREDENTIALS_PATH=/path/to/credentials.json

Breaking Changes

None. This is a backward-compatible addition.

Testing

  • Updated unit tests for transparent mode credential loading
  • Added tests for resilient dependency handling
  • Verified both STANDARD and TRANSPARENT mode detection logic

Commits

  • 56ec285 - chore: Bump version to 0.33.24
  • 749af00 - fix: Make integrationDependencies optional and improve resilience
  • 57f78ba - fix: Support both flat and nested formats in EnvironmentDependenciesResolver
  • 143a637 - fix: Add missing _dependencies_resolver module
  • 229af47 - feat: Make OpenTelemetry dependencies optional
  • 9d6e24b - refactor: Rename INTEGRATION_TOKEN_SERVICE_URL to INTEGRATION_AUTH_URL
  • a9ea654 - feat: Add transparent TLS mode support

Migration Guide

For Existing Agents

No changes required. Existing agents using file-based credentials will continue to work without modifications.

For New Agents Using Transparent Mode

Set environment variables instead of mounting credential files:

export INTEGRATION_CLIENT_ID="your-client-id"
export INTEGRATION_AUTH_URL="https://tenant.accounts.ondemand.com/oauth2/token"
export INTEGRATION_GATEWAY_URL="https://gateway-url"
export INTEGRATION_DEPENDENCIES='[{"ordId":"sap.example:api:v1","globalTenantId":"123456"}]'

The SDK will automatically detect and use transparent mode.

Related Issues

  • Implements transparent TLS mode for cloud-native deployments
  • Resolves ServiceBinding discovery for Kyma/Kubernetes environments
  • Improves resilience for agents without integration dependencies

smahr added 27 commits July 7, 2026 16:28
Add environment-based authentication (no mTLS) to support transparent TLS mode
where the gateway handles mTLS externally and the SDK uses standard HTTPS with
client credentials.

Changes:
- Add TlsMode enum (STANDARD, TRANSPARENT) to config.py
- Make certificate/private_key optional in CustomerCredentials model
- Add detect_transparent_credentials() to check for transparent mode env vars
- Add load_customer_credentials_from_env() for environment-based credentials
- Add _request_token_transparent() for standard HTTPS token requests
- Add automatic TLS mode detection in AgentGatewayClient
- Add comprehensive tests for transparent mode authentication flow

Environment variables for transparent mode:
- INTEGRATION_CLIENT_ID
- INTEGRATION_TOKEN_URL or INTEGRATION_TOKEN_SERVICE_URL
- INTEGRATION_GATEWAY_URL
- INTEGRATION_DEPENDENCIES (JSON array)

The SDK automatically detects the mode based on environment variable presence
and falls back to STANDARD mode (file-based mTLS) when not present.
Add support for targeted MCP server discovery, flexible tool invocation,
and enhanced credential detection to support multiple deployment scenarios.

New features:
- ORD ID filtering: Target specific MCP servers via ord_id parameter in
  list_mcp_tools() and call_mcp_tool()
- Tool-by-name invocation: Accept tool name as string in call_mcp_tool()
  for more flexible tool invocation patterns
- servicebinding.io support: 3-tier credential detection priority
  (explicit override → servicebinding.io scan → legacy default)
- Centralized MCP session management via _mcp_session.py
- Abstract IntegrationDependenciesResolver for flexible dependency sources
- AgentGatewayServerError exception for better error handling

Key implementations:
- _resolve_dependency() for ORD ID lookup in integration dependencies
- EnvironmentDependenciesResolver for INTEGRATION_DEPENDENCIES env var
- _resolve_tool_by_name() helper for string-based tool resolution
- SERVICE_BINDING_ROOT scanning for integration-credentials type

Backward compatibility:
- All existing APIs preserved (get_ias_client_id, etc.)
- ord_id parameter optional (defaults to None)
- Tool parameter accepts both MCPTool object and string

This enables deployment flexibility across Kyma, Kubernetes with service
bindings, and environment-variable-based configurations.
Fix MCP client API usage to match streamablehttp_client signature changes
in newer MCP library versions. The library no longer accepts http_client
as a parameter; headers must be passed directly.

Changes:
- Update MCP client API calls in _customer.py and _lob.py
- Remove nested httpx.AsyncClient context managers
- Pass headers directly to streamablehttp_client()
- Upgrade mcp dependency to >=1.20.0 for protocol 2025-11-25 support
- Add proper exception chaining (raise ... from e) per coding guidelines
- Add AGW_DISABLE_SSL_VERIFY environment variable for testing
- Fix indentation errors in _lob.py from previous edits
- Update test_lob.py for API changes

Breaking change in MCP library:
Old: streamablehttp_client(url, http_client=client)
New: streamablehttp_client(url, headers={...}, timeout=...)

Testing support:
Set AGW_DISABLE_SSL_VERIFY=1 to bypass SSL verification when testing
with self-signed certificates. Works in both STANDARD and TRANSPARENT modes.
Migrate from deprecated streamablehttp_client() to the new recommended
streamable_http_client() API (note the underscore). This aligns with
MCP library best practices and provides better separation of concerns
for HTTP client configuration.

Changes:
- Update imports to use streamable_http_client (with underscore)
- Wrap HTTP configuration in httpx.AsyncClient instances
- Pass pre-configured client to streamable_http_client via http_client parameter
- Fix indentation in nested async context managers
- Update test mocks to patch correct function name
- Update test data format to match flat INTEGRATION_DEPENDENCIES structure

API pattern change:
OLD (deprecated):
  async with streamablehttp_client(url, headers={...}, timeout=...) as (read, write, _):

NEW (recommended):
  async with httpx.AsyncClient(headers={...}, timeout=...) as client:
      async with streamable_http_client(url, http_client=client) as (read, write, _):

This provides cleaner architecture where HTTP concerns (headers, timeouts, auth)
are handled by httpx.AsyncClient, and MCP transport focuses on protocol handling.

All 202 tests passing.
Move OpenTelemetry packages from required dependencies to optional
[telemetry] extras group. This resolves version conflicts when the
auto-instrumentation operator injects its own OTel packages.

Problem:
- Auto-instrumentation operator provides OTel SDK 1.20.0
- SDK previously required OTel SDK 1.42.1
- Version mismatch caused import errors:
  ModuleNotFoundError: opentelemetry.exporter.otlp.proto.common._exporter_metrics

Solution:
- Move all opentelemetry-* dependencies to optional [telemetry] group
- Use relaxed version constraints (>=1.20.0) for compatibility
- SDK now works with operator-provided OTel packages
- Standalone users can install with: pip install sap-cloud-sdk[telemetry]

Changed dependencies:
- opentelemetry-api (>=1.20.0) - moved to [telemetry]
- opentelemetry-sdk (>=1.20.0) - moved to [telemetry]
- opentelemetry-exporter-otlp-proto-grpc (>=1.20.0) - moved to [telemetry]
- opentelemetry-exporter-otlp-proto-http (>=1.20.0) - moved to [telemetry]
- opentelemetry-processor-baggage (>=0.50.0) - moved to [telemetry]
- traceloop-sdk (>=0.50.0) - moved to [telemetry]
- opentelemetry-instrumentation-langchain (>=0.50.0) - moved to [telemetry]

Backward compatibility:
- No breaking changes for users who install the SDK standalone
- Auto-instrumentation scenarios now work without conflicts
- All 202 tests passing

Fixes OpenTelemetry version conflict in containerized environments
with auto-instrumentation operators.
Fix unconditional telemetry imports that caused ModuleNotFoundError when
OpenTelemetry packages were not installed (even though marked as optional).

Problem:
- Making OTel optional in pyproject.toml wasn't enough
- Code still had unconditional imports: `from sap_cloud_sdk.core.telemetry import ...`
- When agentgateway imports destination module, it fails if OTel not installed
- Error: ModuleNotFoundError: opentelemetry.exporter.otlp.proto.common._exporter_metrics

Root Cause Analysis:
- destination/client.py imports telemetry unconditionally (line 9)
- agentgateway/_lob.py imports from destination (line 16)
- Import chain fails when OTel packages missing

Solution:
1. Created _telemetry_compat.py module with conditional import pattern
2. Updated destination module files to use conditional imports:
   - destination/client.py
   - destination/fragment_client.py
   - destination/certificate_client.py
3. When telemetry unavailable, provides no-op implementations

Pattern:
  # Old (unconditional):
  from sap_cloud_sdk.core.telemetry import Module, Operation, record_metrics

  # New (conditional):
  from sap_cloud_sdk.core._telemetry_compat import Module, Operation, record_metrics

  # _telemetry_compat.py handles the try/except and provides fallbacks

Benefits:
- SDK works with or without telemetry packages installed
- Auto-instrumentation operator scenarios now work
- No-op decorators when telemetry unavailable (zero overhead)
- Standalone installations with [telemetry] extra get full functionality

Testing:
- All 202 tests passing
- Agent import chain works without OTel packages
- Backward compatible with telemetry-enabled installations

Fixes import error chain:
  tools.debugging → sap_cloud_sdk.agentgateway → destination → telemetry ❌
  tools.debugging → sap_cloud_sdk.agentgateway → destination → _telemetry_compat ✅

Related: fix(telemetry): make OpenTelemetry dependencies optional (c7dc4f9)
Changes:
- Add transparent TLS mode with INTEGRATION_AUTH_URL environment variable
- Restore servicebinding.io credential discovery from KoblerS branch
- Revert MCP API to httpx.AsyncClient wrapper pattern for minimal changes
- Add defensive null checks for MCP tool invocation results
- Remove AGW_DISABLE_SSL_VERIFY support
- Bump version to 0.33.5

Transparent TLS Mode:
- Supports INTEGRATION_CLIENT_ID, INTEGRATION_AUTH_URL, INTEGRATION_GATEWAY_URL
- No mTLS certificates needed - gateway handles TLS externally
- Uses standard HTTPS for token requests

ServiceBinding.io Support:
- Scans $SERVICE_BINDING_ROOT (default /bindings) for integration-credentials
- Falls back to legacy /etc/ums/credentials/credentials path
- Supports AGW_CREDENTIALS_PATH override

MCP Changes:
- Restored httpx.AsyncClient wrapper in _customer.py and _lob.py
- Fixed import: streamable_http_client (not streamablehttp_client)
- Added null result checks to prevent AttributeError on MCP failures

Tests: All 202 agentgateway unit tests pass
- Move OTel packages back to required dependencies (not optional)
- Use SAP upstream pinned versions (~=1.42.1, ~=0.61.0)
- Revert mcp version constraint from >=1.20.0 to >=1.1.0
- Remove optional telemetry dependency group

This matches SAP/cloud-sdk-python main branch exactly for OTel deps.
Move OTel packages to optional 'telemetry' dependency group to support
Kubernetes OpenTelemetry auto-instrumentation operators that inject
their own OTel version (e.g., 1.20.0) at runtime.

Changes:
- Remove OTel packages from required dependencies
- Add 'telemetry' optional dependency group with relaxed version constraints
- Use >=1.20.0 instead of ~=1.42.1 to avoid conflicts with operator-injected versions

Installation:
- Base: pip install sap-cloud-sdk (no OTel)
- With telemetry: pip install sap-cloud-sdk[telemetry]

This resolves version conflicts when the K8s operator injects OTel 1.20.0
while the SDK required 1.42.1.
Restore OpenTelemetry version constraints that matched the working Archive 5:
- opentelemetry-processor-baggage~=0.61b0 (was >=0.50.0)
- opentelemetry-exporter-otlp-proto-grpc~=1.42.1 (was >=1.20.0)
- opentelemetry-exporter-otlp-proto-http~=1.42.1 (was >=1.20.0)
- traceloop-sdk~=0.61.0 (was >=0.50.0)
- opentelemetry-instrumentation-langchain>=0.61.0 (was >=0.50.0)

Keep api/sdk with >=1.42.1 as in SAP upstream.
Restore exact Archive 5 / SAP v0.25.1 configuration:
- OpenTelemetry packages in REQUIRED dependencies (not optional)
- Version: 0.33.5 → 0.33.7
- OTel versions: ~=1.42.1, ~=0.61b0, >=1.42.1 (working config)

This matches the working Archive 5 configuration with:
- Transparent TLS mode support
- servicebinding.io credential discovery
- MCP API with httpx.AsyncClient wrapper
- All OTel packages always installed

All 202 tests pass.
- Fixed IndentationError at line 685 in _customer.py
- ClientSession async with block was not properly indented inside streamable_http_client context
- Rebuilt wheel with version 0.33.7 containing the fix
- Updated pyproject.toml version to 0.33.8
- Built new wheel with indentation fix from previous commit
- Fixed IndentationError at line 686 and subsequent lines
- All code inside 'async with ClientSession' block now properly indented
- Bumped version to 0.33.9 with complete fix
- Fresh build with properly indented ClientSession block
- All indentation issues resolved in _customer.py
…er.py

- Fixed indentation at line 801 in call_mcp_tool_customer function
- Both _list_server_tools and call_mcp_tool_customer now properly indented
- Bumped version to 0.33.11
- Fixed indentation at lines 284 and 398 in _lob.py
- All ClientSession blocks now properly indented in both _customer.py and _lob.py
- Ran full Python compilation check - all files pass
- Bumped version to 0.33.12
…emoved

- Function was deleted in commit 94b99b0 but still imported in agw_client.py
- Restored function from before 94b99b0 (lines 117-141)
- Function is needed by AgentGatewayClient.get_ias_client_id() for LoB flow
- Bumped version to 0.33.13
- Removed intermediate wheel versions 0.33.7 through 0.33.12
- Latest version 0.33.13 is the current release
- File was added in fork but never used or imported
- Does not exist in SAP upstream repository
- Removed to align with upstream and avoid introducing unauthorized changes
- Bumped version to 0.33.14
- Tests were removed in commit c75ccdf but function was restored
- Added TestGetIasClientIdLob test class with 4 test methods
- All tests pass: returns client ID, handles missing destination, handles missing property, handles missing env var
- Added missing ConsumptionOptions import
- Bumped version to 0.33.15
- Removed result is None check from call_mcp_tool_lob()
- Removed result.isError check from call_mcp_tool_lob()
- Removed result is None check from call_mcp_tool_customer()
- Removed result.isError check from call_mcp_tool_customer()
- These defensive checks were added in fork but don't exist in SAP upstream
- Aligned with SAP upstream simple implementation
- Bumped version to 0.33.16
…tream)

- Removed ord_id parameter from AgentGatewayClient.list_mcp_tools()
- Parameter was added in fork but doesn't exist in SAP upstream
- Other filter logic is already in the works upstream
- Breaks interface compatibility with SAP upstream
- Removed from method signature, docstring, and both call sites
- All tests pass (9 tests in TestListMcpTools)
- Bumped version to 0.33.17
…P upstream)

- Changed tool parameter from 'MCPTool | str' to just 'MCPTool'
- Removed ord_id parameter from call_mcp_tool()
- Removed _resolve_tool_by_name() helper method
- Removed all isinstance(tool, str) resolution logic from customer/transparent/LoB flows
- Simplified docstring: removed tool-by-name example and ord_id docs
- Interface now matches SAP upstream exactly
- All tests pass (10 tests in TestCallMcpTool)
- Bumped version to 0.33.18
@smahr smahr requested a review from a team as a code owner July 9, 2026 15:16
@cla-assistant

cla-assistant Bot commented Jul 9, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ KoblerS
❌ smahr
You have signed the CLA already but the status is still pending? Let us recheck it.

Comment thread dist/sap_cloud_sdk-0.33.13-py3-none-any.whl Outdated
Comment thread src/sap_cloud_sdk/agentgateway/_customer.py Outdated
Returns:
True if all required environment variables are present, False otherwise.
"""
has_client_id = bool(os.environ.get(_INTEGRATION_CLIENT_ID_ENV))

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.

Comment to SDK team: This is not a problem. We will address this directly usage of env vars once we enhance Secrets Resolver.

Comment thread src/sap_cloud_sdk/agentgateway/agw_client.py Outdated
Comment thread pyproject.toml
[project]
name = "sap-cloud-sdk"
version = "0.33.2"
version = "0.33.18"

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.

Suggested change
version = "0.33.18"
version = "0.34.0"

I kindly ask you to run skills for review and release-prep

@NicoleMGomes NicoleMGomes changed the title Transparent TLS + ServiceBinding Envelope Support feat: Transparent TLS + ServiceBinding Envelope Support for Agent Gateway Module Jul 9, 2026
@NicoleMGomes

Copy link
Copy Markdown
Contributor

Once is fully tested and the small points mentioned above are addressed, please ping me and I will release it. Thank you!

@@ -0,0 +1,27 @@
"""Conditional telemetry imports for modules that use telemetry as optional dependency.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This approach is risky because collecting telemetry is very important to us. Making telemetry optional by providing no-op fallbacks means we could silently lose telemetry data if the dependency isn't installed properly.

Instead of making telemetry optional in the code, if you want to disable telemetry for local tests, you can use the environment variable CLOUD_SDK_OTEL_DISABLED=true. This way, telemetry remains a required dependency, but can be disabled when needed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Hi -- understood. at a minimum we need to guarantee that the otel is compatible with the baggage required for otel auto-instrumentation. Feel free to remove that.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants