nodesDeleteFabricNode
Remove a specific node from a Hyperfabric network fabric by providing fabric and node identifiers to manage infrastructure components.
Instructions
Delete a specific node.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| fabricId | Yes | The fabric id or name. | |
| nodeId | Yes | A node id or name. |
Implementation Reference
- src/main.ts:337-448 (handler)Generic handler that executes all dynamically generated MCP tools, including 'nodesDeleteFabricNode'. It matches the tool name to an OpenAPI operation, constructs the HTTP request from tool arguments, and calls the Hyperfabric API.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:134-158 (registration)Dynamically generates and registers MCP Tool objects by iterating over OpenAPI spec paths and operations. The tool 'nodesDeleteFabricNode' is created here from the corresponding DELETE operation on nodes/fabric nodes.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:213-290 (schema)Generates the inputSchema for each tool, including 'nodesDeleteFabricNode', by extracting and resolving parameters and requestBody schemas from the OpenAPI operation.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:160-169 (helper)Generates tool names from OpenAPI operations using operationId or method_path template. Used both for tool creation and matching during execution.private generateToolName(method: string, path: string, operation: OpenAPIOperation): string { if (operation.operationId) { return operation.operationId; } // Generate a name from the method and path const pathParts = path.split('/').filter(part => part && !part.startsWith('{')); const nameBase = pathParts.join('_').replace(/[^a-zA-Z0-9_]/g, '_'); return `${method}_${nameBase}`; }
- src/main.ts:297-335 (handler)MCP CallTool request handler that locates the tool by name and invokes the executeApiCall method.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 }; } }); }