nodesUpdatePort
Configure network port settings like speed, MTU, VLAN, and FEC mode for a specific node in Hyperfabric infrastructure using REST API calls.
Instructions
Update a specific port.
[SAFE OPERATION] This tool configures network fabric port settings (speed, MTU, VLAN, etc.) via REST API. It does NOT execute code or commands on the system.
To use this tool, pass the resource ID and the fields to update as arguments
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| fabricId | Yes | The fabric id or name. | |
| nodeId | Yes | This is a read-only field. The unique identifier of the node to which this port belongs to. | |
| portId | Yes | The port name, id or index. | |
| annotations | No | A list of name-value annotations to store user-defined data including complex data such as JSON associated with the port. | |
| breakout | No | This is a read-only field. The flag that indicates if the port is in breakout mode or not. | |
| breakoutIndex | No | This is a read-only field. The index number of the breakout in the parent port. The breakout index is not set when the port is not in breakout mode. | |
| description | No | The description is a user-defined field to store notes about the port. | |
| enabled | No | The enabled state of the port which indicates if the port is enabled or disabled. | |
| fec | No | The Forward Error Correction (FEC) mode of the port. Supported modes are "rs" and "none". The FEC must be set to "none" for 100G DR/FR/LR pluggable optics. | |
| forceCounter | No | The Force Counter can be incremented to force the port configuration to be forcefully reapplied when there are no other configuration changes such as to recover (un-shut) a port blocked by STP. | |
| id | No | This is a read-only field. The unique identifier of the port. | |
| index | No | This is a read-only field. The index number of the port on the linecard. | |
| ipv4Addresses | No | A list of up to two IPv4 host addresses with subnet mask to be configured on the port. Requires the `ROUTED_PORT` role to be configured in roles and the port to be associated with a VRF. | |
| ipv6Addresses | No | A list of up to two IPv6 host addresses with subnet mask to be configured on the port. Requires the `ROUTED_PORT` role to be configured in roles and the port to be associated with a VRF. | |
| labels | No | A list of user-defined labels that can be used for grouping and filtering ports. | |
| linecard | No | This is a read-only field. The index number of the linecard to which this port belongs to. | |
| linkDown | No | Prevent traffic from being forwarded by the port. Requires `enabled` to be set to `true` (equivalent to `Admin State` set to `Up`) and role to be one of `UNUSED_PORT`, `ROUTED_PORT` or `HOST_PORT`. | |
| maxSpeed | No | This is a read-only field. The maximum speed of the port as reported by the system (E.g. 10G). | |
| metadata | No | Metadata defines a map of attributes related to the lifecycle of the object. | |
| mtu | No | The Maximum Transmission Unit (MTU) of the port of the node. Default value is 9216 for "FABRIC_PORT" and 9100 for all other roles. The MTU value must be 9216 for "FABRIC_PORT" and between 1500 and 9100 for all other roles. | |
| name | No | The name of the port of the node. The name must have a prefix of Ethernet (E.g. Ethernet1_1) and cannot be modified. | |
| pluggable | No | The name of the model (PID) of the pluggable cable or optic expected to be used in the port. | |
| portAlias | No | The name alias of the port. | |
| roles | No | A list of roles of the port. The port roles list is mandatory, and must contain exactly one role. | |
| speed | No | The configurable speed mode of the port (E.g. 10G). The port speed cannot be set for a port in breakout mode. | |
| stp | No | The Spanning Tree Protocol (STP) interface configuration for the port. The configuration is only used when a VLAN is deployed on the port. | |
| subInfCount | No | This is a read-only field. The number of sub-interfaces configured on the port. | |
| vlanIds | No | This is a read-only field. A list of VLAN IDs deployed on the port of the node. | |
| vnis | No | This is a read-only field. A list of VNIs attached to the port of the node. | |
| vrfId | No | The unique identifier of the VRF associated with the port. The VRF identifier is required for a port with the `ROUTED_PORT` role. |
Implementation Reference
- src/main.ts:337-448 (handler)Core handler function that implements the execution logic for the 'nodesUpdatePort' tool (shared with all tools). It performs security validation specific to port configuration tools, maps the tool name to the OpenAPI operation, constructs the HTTP request using tool arguments, and executes the API call to Hyperfabric.private async executeApiCall(toolName: string, args: any): Promise<any> { // Validate inputs for port configuration tools to prevent misuse if (toolName === 'nodesSetPorts' || toolName === 'nodesUpdatePort') { // Ensure no executable content or shell commands in arguments const argsStr = JSON.stringify(args); if (/(\$\(|`|eval|exec|system|spawn|child_process)/.test(argsStr)) { throw new Error('Security: Invalid arguments detected. Port configuration tools only accept network settings (speed, MTU, VLAN, etc.)'); } } // Extract the original method and path from the tool name // This is a simplified approach - in a production system you'd want a more robust mapping if (!this.openApiSpec?.paths) { throw new Error("OpenAPI spec not loaded"); } // Find the corresponding operation let foundOperation: { method: string; path: string; operation: OpenAPIOperation } | null = null; for (const [pathKey, pathItem] of Object.entries(this.openApiSpec.paths)) { for (const [method, operation] of Object.entries(pathItem)) { if (typeof operation !== 'object' || !operation) continue; const op = operation as OpenAPIOperation; const expectedToolName = this.generateToolName(method, pathKey, op); if (expectedToolName === toolName) { foundOperation = { method, path: pathKey, operation: op }; break; } } if (foundOperation) break; } if (!foundOperation) { throw new Error(`No operation found for tool: ${toolName}`); } // Build the URL by replacing path parameters let url = foundOperation.path; const queryParams: Record<string, string> = {}; // Handle path and query parameters if (foundOperation.operation.parameters) { for (const param of foundOperation.operation.parameters) { const value = args[param.name]; if (value !== undefined) { if (param.in === 'path') { url = url.replace(`{${param.name}}`, encodeURIComponent(value)); } else if (param.in === 'query') { queryParams[param.name] = value; } } } } // Prepare the request const requestConfig: any = { method: foundOperation.method.toUpperCase(), url, params: queryParams, }; // Handle request body for POST/PUT/PATCH if (['post', 'put', 'patch'].includes(foundOperation.method.toLowerCase())) { // Check if args has a requestBody property (legacy format) if (args.requestBody) { requestConfig.data = args.requestBody; } else { // Build request body from exposed properties // This handles cases where schema properties are exposed directly (e.g., fabrics, nodes, etc.) const requestBody: Record<string, any> = {}; const pathItem = this.openApiSpec?.paths?.[foundOperation.path]; const operation = (pathItem as any)?.[foundOperation.method]; if (operation?.requestBody) { const requestBodyDef = this.resolveSchemaRef(operation.requestBody); const schema = this.deepResolveSchema(requestBodyDef.content?.['application/json']?.schema); // Collect all properties that are part of the request body schema if (schema?.properties) { for (const propName of Object.keys(schema.properties)) { if (args.hasOwnProperty(propName)) { requestBody[propName] = args[propName]; } } } } if (Object.keys(requestBody).length > 0) { requestConfig.data = requestBody; } } } logger.debug(`Making API call: ${requestConfig.method} ${url}`); try { const response = await this.httpClient.request(requestConfig); return { status: response.status, statusText: response.statusText, data: response.data }; } catch (error) { if (axios.isAxiosError(error)) { throw new Error(`API call failed: ${error.response?.status} ${error.response?.statusText} - ${JSON.stringify(error.response?.data)}`); } throw error; } }
- src/main.ts:213-290 (schema)Generates the Tool definition including input schema, description, and special security note for 'nodesUpdatePort' from the OpenAPI operation schema.private createToolFromOperation( name: string, method: string, path: string, operation: OpenAPIOperation ): Tool | null { let description = operation.summary || operation.description || `${method.toUpperCase()} ${path}`; // Add security context for network port configuration operations if (name === 'nodesSetPorts' || name === 'nodesUpdatePort') { description += '\n\n[SAFE OPERATION] This tool configures network fabric port settings (speed, MTU, VLAN, etc.) via REST API. It does NOT execute code or commands on the system.'; } // Enhance description for create/update operations if (['post', 'put', 'patch'].includes(method.toLowerCase())) { if (method.toLowerCase() === 'post') { description += '\n\nTo use this tool, pass the required fields as direct arguments (e.g., fabrics=[{name:"my-fabric", description:"...", ...}])'; } else if (method.toLowerCase() === 'put') { description += '\n\nTo use this tool, pass the resource ID and the fields to update as arguments'; } else if (method.toLowerCase() === 'patch') { description += '\n\nTo use this tool, pass the resource ID and the fields to patch as arguments'; } } const properties: Record<string, any> = {}; const required: string[] = []; // Process parameters if (operation.parameters) { for (const param of operation.parameters) { if (param.in === 'path' || param.in === 'query') { properties[param.name] = { type: param.schema?.type || 'string', description: param.description || '' }; if (param.required) { required.push(param.name); } } } } // Process request body for POST/PUT/PATCH requests if (operation.requestBody && ['post', 'put', 'patch'].includes(method.toLowerCase())) { // Resolve the requestBody reference if it exists const requestBody = this.resolveSchemaRef(operation.requestBody); const content = requestBody.content; if (content?.['application/json']?.schema) { let schema = content['application/json'].schema; // Deeply resolve schema references schema = this.deepResolveSchema(schema); if (schema.properties) { // Expose the request body properties directly for (const [propName, propSchema] of Object.entries(schema.properties)) { const propDef = propSchema as any; properties[propName] = this.deepResolveSchema(propDef, 0); if (schema.required?.includes(propName)) { required.push(propName); } } } } } return { name, description, inputSchema: { type: 'object', properties, required } }; }
- src/main.ts:134-157 (registration)Dynamically generates and registers all tools, including 'nodesUpdatePort', by iterating over OpenAPI spec paths/operations and creating Tool objects.private generateTools(): void { if (!this.openApiSpec?.paths) { logger.error("No paths found in OpenAPI spec"); return; } this.tools = []; for (const [pathKey, pathItem] of Object.entries(this.openApiSpec.paths)) { for (const [method, operation] of Object.entries(pathItem)) { if (typeof operation !== 'object' || !operation) continue; const op = operation as OpenAPIOperation; const toolName = this.generateToolName(method, pathKey, op); const tool = this.createToolFromOperation(toolName, method, pathKey, op); if (tool) { this.tools.push(tool); logger.debug(`Generated tool: ${toolName}`); } } } logger.info(`Generated ${this.tools.length} tools from OpenAPI spec`);
- src/main.ts:292-335 (registration)Registers the MCP protocol handlers: listTools (returns registered tools) and callToolRequest (dispatches to executeApiCall for tool execution).private setupHandlers(): void { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: this.tools }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request: any) => { const { name, arguments: args } = request.params; logger.info(`Calling tool: ${name}`); logger.debug(`Tool arguments: ${JSON.stringify(args, null, 2)}`); try { // Find the tool definition const tool = this.tools.find(t => t.name === name); if (!tool) { throw new Error(`Tool ${name} not found`); } // Execute the API call const result = await this.executeApiCall(name, args); return { content: [ { type: "text", text: JSON.stringify(result, null, 2) } ] }; } catch (error) { logger.error(`Error executing tool ${name}:`, error); return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` } ], isError: true }; } }); }