mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-20 19:46:16 +00:00
* feat(mcp): add durable task runtime foundation * fix(chart): sync embedded config version * fix(mcp): isolate task polls during shutdown * feat(mcp): track consecutive poll errors on mcp_tasks poll_attempt_count grows on every claim (successful polls included), so it cannot drive a failure backoff without misjudging normal long tasks. Add consecutive_poll_error_count: incremented when a claim is released after a poll error, reset to zero by any applied snapshot. The backoff/terminal policy that consumes it lands with the first concrete driver. * fix(mcp): harden durable task lifecycle * feat(mcp): add ordinary durable task driver * test(mcp): address durable task review feedback * fix(mcp): preserve submit tool descriptions * fix(mcp): bound remote task calls * fix(mcp): bound persisted task payloads * fix(mcp): preserve task tool error details * fix(mcp): enforce durable task boundaries * test(mcp): cover task config snapshot lifecycle --------- Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
58 lines
2.0 KiB
Python
58 lines
2.0 KiB
Python
"""Shared construction of MCP tool-call interceptors."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from deerflow.config.extensions_config import ExtensionsConfig
|
|
from deerflow.mcp.oauth import build_oauth_tool_interceptor
|
|
from deerflow.reflection import resolve_variable
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def build_mcp_tool_interceptors(
|
|
extensions_config: ExtensionsConfig,
|
|
*,
|
|
oauth_builder: Any = build_oauth_tool_interceptor,
|
|
resolver: Any = resolve_variable,
|
|
target_logger: logging.Logger = logger,
|
|
) -> list[Any]:
|
|
"""Build OAuth followed by configured custom MCP interceptors."""
|
|
interceptors: list[Any] = []
|
|
oauth_interceptor = oauth_builder(extensions_config)
|
|
if oauth_interceptor is not None:
|
|
interceptors.append(oauth_interceptor)
|
|
|
|
raw_paths = (extensions_config.model_extra or {}).get("mcpInterceptors")
|
|
if isinstance(raw_paths, str):
|
|
raw_paths = [raw_paths]
|
|
elif not isinstance(raw_paths, list):
|
|
if raw_paths is not None:
|
|
target_logger.warning(
|
|
"mcpInterceptors must be a list of strings, got %s; skipping",
|
|
type(raw_paths).__name__,
|
|
)
|
|
raw_paths = []
|
|
|
|
for interceptor_path in raw_paths:
|
|
try:
|
|
builder = resolver(interceptor_path)
|
|
interceptor = builder()
|
|
if callable(interceptor):
|
|
interceptors.append(interceptor)
|
|
target_logger.info("Loaded MCP interceptor: %s", interceptor_path)
|
|
elif interceptor is not None:
|
|
target_logger.warning(
|
|
"Builder %s returned non-callable %s; skipping",
|
|
interceptor_path,
|
|
type(interceptor).__name__,
|
|
)
|
|
except Exception:
|
|
target_logger.warning(
|
|
f"Failed to load MCP interceptor {interceptor_path}",
|
|
exc_info=True,
|
|
)
|
|
return interceptors
|