Portal Community

Node Configuration

{
  "nodeType": "MCPToolCall",
  "name": "analyzeDocument",
  "config": {
    "serverId": "bfai-document-ai",
    "toolName": "analyze-document",
    "inputMap": {
      "documentUrl": "$output.fetchDocument.url",
      "documentType": "$json.documentType",
      "extractionSchema": {
        "fields": ["invoiceNumber", "totalAmount", "currency", "vendorName"]
      }
    },
    "timeoutSeconds": 60,
    "retryCount": 2
  }
}

Configuration Fields

FieldTypeRequiredDescription
serverIdstringYesRegistered MCP server identifier from IMCPServerRegistry
toolNamestringYesTool name as declared in the server's tool catalog
inputMapobjectYesParameter map — keys are tool parameter names, values are expressions or literals
timeoutSecondsintNoPer-call timeout in seconds (default: server-level timeout or 30s)
retryCountintNoNumber of retries on transient failure (default: 1; 0 = no retry)

MCPToolCallExecutor — Implementation Pattern

public class MCPToolCallExecutor : BaseNodeExecutor<MCPToolCallSettings>
{
    private readonly IMCPServerRegistry _registry;
    private readonly IMCPClientFactory _clientFactory;
    private readonly IExpressionEvaluator _expr;

    public MCPToolCallExecutor(
        IMCPServerRegistry registry,
        IMCPClientFactory clientFactory,
        IExpressionEvaluator expr)
    {
        _registry = registry;
        _clientFactory = clientFactory;
        _expr = expr;
    }

    protected override async Task<NodeExecutionResult> ExecuteAsync(
        NodeExecutionContext ctx,
        MCPToolCallSettings settings,
        CancellationToken ct)
    {
        // 1. Resolve server definition
        if (!_registry.TryGetServer(settings.ServerId, out var serverDef))
            return NodeExecutionResult.Failure(
                $"MCP server '{settings.ServerId}' is not registered.");

        // 2. Evaluate inputMap expressions against current context
        var toolParameters = new Dictionary<string, object?>();
        foreach (var (paramName, expression) in settings.InputMap)
        {
            toolParameters[paramName] = expression is string s
                ? await _expr.EvaluateAsync(s, ctx, ct)
                : expression;   // literal object/array
        }

        // 3. Create authenticated MCP client (credentials resolved inside factory)
        await using var client = await _clientFactory.CreateClientAsync(serverDef, ct);

        // 4. Execute tool call with timeout
        using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
        cts.CancelAfter(TimeSpan.FromSeconds(settings.TimeoutSeconds ?? serverDef.TimeoutSeconds));

        var callId = Guid.NewGuid().ToString("N");
        var callResult = await client.CallToolAsync(
            settings.ToolName,
            toolParameters,
            cts.Token);

        // 5. Return structured output
        return NodeExecutionResult.Success(new
        {
            result      = callResult.Content,
            toolName    = settings.ToolName,
            serverId    = settings.ServerId,
            callId      = callId,
            executedAt  = DateTimeOffset.UtcNow
        });
    }
}

Retry Behaviour

Retries apply only to transient failures: network errors, 429 Too Many Requests (with Retry-After respected), and 503/504 responses from the MCP server. Tool-level errors returned in the MCP response body (e.g., isError: true) are not retried — they surface immediately as node failure.

Tool name validation at design time: When a flow is saved in Flow Studio, the canvas fetches the tool catalog from IMCPServerRegistry.GetToolsAsync(serverId) and validates that toolName exists. If the server is unreachable at design time, the canvas shows a warning but still allows saving. Runtime resolution is authoritative.
Timeout scope: timeoutSeconds covers the entire round-trip from client request to tool response. It does not include the time to resolve credentials or establish the transport connection. Set generous timeouts for AI inference tools that may take 10–30 seconds.