{"record":{"id":"05433067ea1d7498","repo":"iflytek/astron-agent","slug":"mcpserverid-and-mcpserverurl-cannot-both-be-empty","errorCode":null,"errorMessage":"mcpServerId and mcpServerUrl cannot both be empty","messagePattern":"mcpServerId and mcpServerUrl cannot both be empty","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"core/workflow/engine/nodes/mcp/mcp_node.py","lineNumber":40,"sourceCode":"\nclass MCPNode(BaseNode):\n    \"\"\"\n    MCP (Model Context Protocol) execution node for workflow execution.\n\n    This node enables calling MCP tools from external MCP servers within workflows,\n    supporting dynamic tool execution with configurable parameters and server endpoints.\n    \"\"\"\n\n    _private_config = PrivateConfig()\n    mcpServerId: str = Field(default=\"\", description=\"MCP server unique identifier\")\n    mcpServerUrl: str = Field(default=\"\", description=\"MCP server endpoint URL\")\n    toolName: str = Field(..., description=\"Name of the MCP tool to execute\")\n\n    @model_validator(mode=\"after\")\n    def validate_fields(self) -> \"MCPNode\":\n        \"\"\"Validate field constraints.\"\"\"\n        if not self.mcpServerId and not self.mcpServerUrl:\n            raise ValueError(\"mcpServerId and mcpServerUrl cannot both be empty\")\n        if not self.toolName:\n            raise ValueError(\"toolName cannot be empty\")\n        return self\n\n    async def execute(\n        self,\n        variable_pool: VariablePool,\n        span: Span,\n        event_log_node_trace: NodeLog | None = None,\n    ) -> NodeRunResult:\n        \"\"\"\n        Execute the MCP tool call operation.\n\n        Retrieves input variables, constructs the MCP tool call request,\n        sends it to the MCP server, and returns the results.\n\n        :param variable_pool: Pool containing workflow variables\n        :param span: Span object for tracing and logging","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/workflow/engine/nodes/mcp/mcp_node.py#L22-L58","documentation":"MCPNode's pydantic model_validator (mode='after') enforces that at least one of mcpServerId or mcpServerUrl is provided, since the node needs a way to locate the MCP server. If both are empty, ValueError is raised during model construction, failing node validation.","triggerScenarios":"Creating/instantiating an MCP node from workflow config where neither mcpServerId nor mcpServerUrl is set — e.g. an MCP node dragged into the flow but no server chosen.","commonSituations":"Workflow JSON hand-edited or imported with the server fields blank; UI failed to persist the selected MCP server; a copied node lost its server binding.","solutions":["Set mcpServerId to the registered MCP server's ID","Or set mcpServerUrl to the MCP server's direct URL (when bypassing the registry)","Re-open the node in the workflow editor and select the MCP server, then re-save the workflow"],"exampleFix":"// before\n{\"type\": \"mcp\", \"mcpServerId\": \"\", \"mcpServerUrl\": \"\", \"toolName\": \"search\"}\n// after\n{\"type\": \"mcp\", \"mcpServerId\": \"srv-123\", \"mcpServerUrl\": \"\", \"toolName\": \"search\"}","handlingStrategy":"validation","validationCode":"cfg = node_config.get(\"mcp\", {})\nif not cfg.get(\"mcpServerId\") and not cfg.get(\"mcpServerUrl\"):\n    raise ValueError(\"Provide either mcpServerId or mcpServerUrl for the MCP node\")","typeGuard":"def has_mcp_target(cfg: dict) -> bool:\n    return bool(cfg.get(\"mcpServerId\") or cfg.get(\"mcpServerUrl\"))","tryCatchPattern":"from pydantic import ValidationError\ntry:\n    node = MCPNode(**node_config)\nexcept ValidationError as e:\n    # inspect e.errors() for the validate_fields failure\n    ...","preventionTips":["Always bind an MCP server in the node editor before saving","Validate workflow JSON imports against the MCPNode pydantic model","Don't blank out server fields when copying nodes"],"tags":["python","pydantic","validation","workflow"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}