@@ -223,34 +223,34 @@ def call_hook(
223223 hook_config : HookConfig ,
224224 ) -> Optional [Message ]:
225225 """Call a hook's MCP endpoint and poll until completion.
226-
226+
227227 Executes the workflow via ``execute-workflow``, then polls
228228 ``get-execution`` every 500 ms until the execution succeeds, fails,
229229 or ``hook.timeout`` seconds elapse.
230-
230+
231231 This method is transport-agnostic: regardless of how extension
232232 metadata was fetched (backend, local file, or no-op),
233233 the actual hook invocation is always a direct HTTP call to the
234234 URL embedded in the :class:`Hook` object.
235-
235+
236236 Args:
237237 hook: Hook configuration (workflow ID, method, timeout).
238238 hook_config: Hook invocation configuration (endpoint URL, auth token, optional payload).
239-
239+
240240 Returns:
241241 Parsed ``Message`` from the last executed workflow node, or ``None``
242242 if the hook completed successfully but produced no message.
243-
243+
244244 Raises:
245245 TransportError: On HTTP errors, terminal execution failures, or timeout.
246-
246+
247247 Example:
248248 ```python
249249 from sap_cloud_sdk.extensibility import create_client
250-
250+
251251 client = create_client("sap.ai:agent:myAgent:v1")
252252 impl = client.get_extension_capability_implementation(tenant="tenant-abc")
253-
253+
254254 if impl.hooks:
255255 hook = impl.hooks[0]
256256 result = client.call_hook(
@@ -265,13 +265,13 @@ def call_hook(
265265 """
266266 headers = {** _JSONRPC_HEADERS }
267267 inject (headers )
268-
268+
269269 message_payload : dict [str , Any ] = {}
270270 if hook_config .payload is not None :
271271 model_dump = getattr (hook_config .payload , "model_dump" , None )
272272 if callable (model_dump ):
273273 message_payload = cast (dict [str , Any ], model_dump (exclude_none = True ))
274-
274+
275275 # 1. Execute workflow
276276 execute_workflow_arguments = {
277277 "workflowId" : hook .n8n_workflow_config .workflow_id ,
@@ -285,7 +285,7 @@ def call_hook(
285285 },
286286 },
287287 }
288-
288+
289289 try :
290290 with httpx .Client (
291291 headers = {"Authorization" : f"Bearer { hook_config .auth_token } " },
@@ -304,24 +304,24 @@ def call_hook(
304304 raise TransportError (
305305 f"HTTP request to hook MCP endpoint failed: { exc } "
306306 ) from exc
307-
307+
308308 try :
309309 data = _extract_tool_result (_parse_response (tool_resp ))
310310 except TransportError :
311311 raise
312312 except Exception as exc :
313313 raise TransportError (f"Could not parse hook response: { exc } " ) from exc
314-
314+
315315 status = data .get ("status" , "" )
316-
316+
317317 # 2. Fail fast on terminal statuses from execute-workflow
318318 if status in _EXECUTE_TERMINAL_STATUSES :
319319 error_msg = data .get ("error" , "" )
320320 raise TransportError (
321321 f"Workflow execution failed with status { status !r} "
322322 + (f": { error_msg } " if error_msg else "" )
323323 )
324-
324+
325325 # 3. Return immediately if execution completed synchronously
326326 if status == "success" :
327327 try :
@@ -339,20 +339,20 @@ def call_hook(
339339 raise TransportError (
340340 f"Failed to extract response from last executed node: { exc } "
341341 ) from exc
342-
342+
343343 # 4. Poll get-execution for running/new/waiting/started
344344 execution_id = data .get ("executionId" )
345345 get_execution_arguments = {
346346 "workflowId" : hook .n8n_workflow_config .workflow_id ,
347347 "executionId" : str (execution_id ),
348348 "includeData" : True ,
349349 }
350-
350+
351351 deadline = time .monotonic () + hook .timeout
352352 last_status = status
353353 while time .monotonic () < deadline :
354354 time .sleep (_HOOK_POLL_INTERVAL )
355-
355+
356356 try :
357357 with httpx .Client (
358358 headers = {"Authorization" : f"Bearer { hook_config .auth_token } " },
@@ -371,18 +371,18 @@ def call_hook(
371371 raise TransportError (
372372 f"HTTP request to hook MCP endpoint failed: { exc } "
373373 ) from exc
374-
374+
375375 try :
376376 data = _extract_tool_result (_parse_response (tool_resp ))
377377 except TransportError :
378378 raise
379379 except Exception as exc :
380380 raise TransportError (f"Could not parse hook response: { exc } " ) from exc
381-
381+
382382 last_status = data .get ("execution" , {}).get ("status" , "" ) or data .get (
383383 "status" , ""
384384 )
385-
385+
386386 if last_status == "success" :
387387 try :
388388 result_data = data .get ("data" , {}).get ("resultData" , {})
@@ -399,16 +399,16 @@ def call_hook(
399399 raise TransportError (
400400 f"Failed to extract response from last executed node: { exc } "
401401 ) from exc
402-
402+
403403 if last_status in _EXECUTION_TERMINAL_STATUSES :
404404 error_msg = data .get ("error" , "" )
405405 raise TransportError (
406406 f"Workflow execution failed with status { last_status !r} "
407407 + (f": { error_msg } " if error_msg else "" )
408408 )
409-
409+
410410 # Continue polling for: running, waiting, new, unknown
411-
411+
412412 raise TransportError (
413413 f"Workflow execution timed out after { hook .timeout } s. "
414414 f"Last status: { last_status !r} "
0 commit comments