Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "sap-cloud-sdk"
version = "0.53.0"
version = "0.54.0"
description = "SAP Cloud SDK for Python"
readme = "README.md"
license = "Apache-2.0"
Expand Down Expand Up @@ -41,7 +41,7 @@ dependencies = [
]

[project.optional-dependencies]
extensibility = ["a2a-sdk>=0.2.0,<2"]
extensibility = ["a2a-sdk>=0.2.0,<1"]
starlette = ["starlette>=0.40.0,<1"]
fastapi = ["fastapi>=0.100.0,<1"]
aiohttp = ["aiohttp>=3.9.0,<4"]
Expand Down Expand Up @@ -75,7 +75,7 @@ dev = [
"sqlalchemy>=2.0.0",
"django>=4.0",
"flask>=3.0",
"a2a-sdk>=0.2.0",
"a2a-sdk>=0.2.0,<1",
"langchain-core>=1.2.7",
"langgraph>=0.2.0",
"langchain-community>=0.3.0",
Expand Down
30 changes: 24 additions & 6 deletions src/sap_cloud_sdk/extensibility/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,11 +236,17 @@ class N8nWorkflowConfig:
"""n8n workflow configuration embedded in a hook.

Attributes:
workflow_id: n8n workflow ID.
ord_id: ORD ID of the API resource for this workflow.
card_ord_id: MCP translation card ORD ID (per ADR-021).
tool_name: MCP tool name (apiName segment of ordId, per ADR-021).
global_tenant_id: Global tenant ID of the n8n workflow tenant (per ADR-022).
method: HTTP method for the n8n webhook call.
"""

workflow_id: str
ord_id: str
card_ord_id: str
tool_name: str
global_tenant_id: str
method: HTTPMethod

@classmethod
Expand All @@ -250,7 +256,10 @@ def from_dict(cls, obj: Dict[str, Any]) -> N8nWorkflowConfig:
Expected JSON shape::

{
"workflowId": "unique_n8n_workflow_id",
"ordId": "sap.n8nwfrt:apiResource:invoice-po-solution_my-workflow.postInvoice:v1",
"cardOrdId": "sap.n8nwfrt:apiResource:invoice-po-solution_my-workflow.postInvoice_mcp:v1",
"toolName": "postInvoice",
"globalTenantId": "tenant-abc",
"method": "POST"
}

Expand All @@ -262,7 +271,10 @@ def from_dict(cls, obj: Dict[str, Any]) -> N8nWorkflowConfig:
"""
method = _parse_http_method(obj.get("method", "POST")) or HTTPMethod.POST
return cls(
workflow_id=obj.get("workflowId", ""),
ord_id=obj.get("ordId", ""),
card_ord_id=obj.get("cardOrdId", ""),
tool_name=obj.get("toolName", ""),
global_tenant_id=obj.get("globalTenantId", ""),
method=method,
)

Expand Down Expand Up @@ -338,7 +350,10 @@ def from_dict(cls, obj: Dict[str, Any]) -> Hook:
"order": 1,
"canShortCircuit": true,
"n8nWorkflowConfig": {
"workflowId": "unique_n8n_workflow_id",
"ordId": "sap.n8nwfrt:apiResource:invoice-po-solution_my-workflow.postInvoice:v1",
"cardOrdId": "sap.n8nwfrt:apiResource:invoice-po-solution_my-workflow.postInvoice_mcp:v1",
"toolName": "postInvoice",
"globalTenantId": "tenant-abc",
"method": "POST"
}
}
Expand Down Expand Up @@ -679,7 +694,10 @@ def from_dict(cls, obj: Dict[str, Any]) -> ExtensionCapabilityImplementation:
"order": 1,
"canShortCircuit": true,
"n8nWorkflowConfig": {
"workflowId": "unique_n8n_workflow_id",
"ordId": "sap.n8nwfrt:apiResource:invoice-po-solution_my-workflow.postInvoice:v1",
"cardOrdId": "sap.n8nwfrt:apiResource:invoice-po-solution_my-workflow.postInvoice_mcp:v1",
"toolName": "postInvoice",
"globalTenantId": "tenant-abc",
"method": "POST"
}
}
Expand Down
17 changes: 12 additions & 5 deletions src/sap_cloud_sdk/extensibility/_ums_transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
additions { type mcpConfig { globalTenantId ordId toolNames } }
}
hooks { id hookId type name onFailure timeout deploymentType canShortCircuit
n8nWorkflowConfig { workflowId method }
n8nWorkflowConfig { ordId cardOrdId toolName globalTenantId method }
}
}
}
Expand Down Expand Up @@ -280,10 +280,11 @@ def _build_hook(raw: Dict[str, Any]) -> Optional[Hook]:
return None

n8n_config = raw.get("n8nWorkflowConfig") or {}
workflow_id = n8n_config.get("workflowId", "")
if not workflow_id:
tool_name = n8n_config.get("toolName", "")
card_ord_id = n8n_config.get("cardOrdId", "")
if not tool_name or not card_ord_id:
logger.warning(
"Skipping hook with missing workflowId (hookId=%s)",
"Skipping hook with missing toolName or cardOrdId (hookId=%s)",
raw.get("hookId"),
)
return None
Expand All @@ -295,7 +296,13 @@ def _build_hook(raw: Dict[str, Any]) -> Optional[Hook]:
return Hook(
id=raw.get("id", ""),
hook_id=raw.get("hookId", ""),
n8n_workflow_config=N8nWorkflowConfig(workflow_id=workflow_id, method=method),
n8n_workflow_config=N8nWorkflowConfig(
ord_id=n8n_config.get("ordId", ""),
card_ord_id=n8n_config.get("cardOrdId", ""),
tool_name=tool_name,
global_tenant_id=n8n_config.get("globalTenantId", ""),
method=method,
),
name=raw.get("name", ""),
type=hook_type,
deployment_type=deployment_type,
Expand Down
Loading
Loading