Orquesta Agentes IA que desarrollan por ti
Verificando...
60-add-mcp-tool
Procedimiento: A├▒adir Nueva Herramienta MCP
Mantenimiento
1 plugin(s)
Editor
Preview
Tareas
0
Info
Titulo
Guía la creación de nuevas herramientas MCP. Define estructura, implementación, testing, y documentación de tools para el protocolo MCP.
Descripcion
Contenido Markdown
23449 caracteres
Guardar
# Procedimiento: A├▒adir Nueva Herramienta MCP ## Metadata - **ID**: PROC-60 - **Frecuencia**: Seg├║n necesidad (feature nueva) - **Duraci├│n estimada**: 2-4 horas (implementaci├│n + tests + docs) - **Requiere**: .NET 8 SDK, conocimiento de MCP protocol, acceso al repo - **Dependencias**: PROC-04 (tests), PROC-01 (docs) - **Bloquea**: Nada directamente ## Objetivo Implementar una nueva herramienta MCP siguiendo los est├índares del proyecto, con tests, documentaci├│n, y verificaci├│n de funcionamiento. ## Arquitectura de MCP Tools ``` ÔöîÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÉ Ôöé MCP Endpoint Ôöé Ôöé POST /mcp Ôöé ÔööÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÿ Ôöé Ôû╝ ÔöîÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÉ Ôöé McpRouter Ôöé Ôöé Valida JSON-RPC, extrae method/params Ôöé ÔööÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÿ Ôöé Ôû╝ ÔöîÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÉ Ôöé McpToolInvoker Ôöé Ôöé Valida scopes, resuelve tool, invoca Ôöé ÔööÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÿ Ôöé Ôû╝ ÔöîÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÉ Ôöé IMcpTool Ôöé Ôöé ÔöîÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÉ ÔöîÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÉ ÔöîÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÉ ÔöîÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÉ Ôöé Ôöé Ôöé products Ôöé Ôöé orders Ôöé Ôöé pricing Ôöé Ôöé TU NUEVA Ôöé Ôöé Ôöé Ôöé _search Ôöé Ôöé _create Ôöé Ôöé _get Ôöé Ôöé _tool Ôöé Ôöé Ôöé ÔööÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÿ ÔööÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÿ ÔööÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÿ ÔööÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÿ Ôöé ÔööÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÇÔöÿ ``` ## Interfaz IMcpTool ```csharp public interface IMcpTool { /// <summary>Nombre ├║nico de la tool (snake_case)</summary> string Name { get; } /// <summary>Descripci├│n para el LLM</summary> string Description { get; } /// <summary>Scope OAuth requerido</summary> string RequiredScope { get; } /// <summary>Ejecuta la tool</summary> Task<object> InvokeAsync( string apiKey, JsonElement arguments, CancellationToken ct); } ``` ## Par├ímetros de la Nueva Tool Antes de empezar, definir: | Par├ímetro | Valor | |-----------|-------| | **Nombre** (snake_case) | ___ (ej: `inventory_check`) | | **Dominio** | Catalog / Orders / Pricing / Support / Sales / Customer / Feedback | | **Scope requerido** | ___ (ej: `dmi:catalog:read`) | | **Descripci├│n corta** | ___ | | **Par├ímetros de entrada** | ___ | | **Respuesta esperada** | ___ | ## Checklist Ejecutable ### 1. Crear estructura de archivos ```bash cd src/src/BigCommerceApi # Crear archivo de la tool (en carpeta del dominio) # Ejemplo para dominio Catalog: touch Mcp/Tools/Catalog/InventoryCheckTool.cs # Crear archivo de tests touch ../../test/BigCommerceApi.Tests/Mcp/Catalog/InventoryCheckToolTests.cs ``` Estructura de carpetas: ``` Mcp/Tools/ Ôö£ÔöÇÔöÇ Catalog/ Ôöé Ôö£ÔöÇÔöÇ ProductsSearchTool.cs Ôöé Ôö£ÔöÇÔöÇ CatalogCategoriesListTool.cs Ôöé ÔööÔöÇÔöÇ InventoryCheckTool.cs ÔåÉ NUEVA Ôö£ÔöÇÔöÇ Orders/ Ôö£ÔöÇÔöÇ Pricing/ Ôö£ÔöÇÔöÇ Support/ Ôö£ÔöÇÔöÇ Sales/ Ôö£ÔöÇÔöÇ Customer/ ÔööÔöÇÔöÇ Feedback/ ``` - [ ] Archivo de tool creado - [ ] Archivo de tests creado ### 2. Implementar la Tool ```csharp // Mcp/Tools/Catalog/InventoryCheckTool.cs using System.Text.Json; using BigCommerceApi.Mcp.Infrastructure; using BigCommerceApi.Services; namespace BigCommerceApi.Mcp.Tools.Catalog; /// <summary> /// Verifica disponibilidad de inventario para una lista de productos. /// </summary> public sealed class InventoryCheckTool : IMcpTool { private readonly IInventoryService _inventoryService; private readonly ILogger<InventoryCheckTool> _logger; public InventoryCheckTool( IInventoryService inventoryService, ILogger<InventoryCheckTool> logger) { _inventoryService = inventoryService; _logger = logger; } public string Name => "inventory_check"; public string Description => """ Verifica la disponibilidad de inventario para uno o m├ís productos. Devuelve stock actual, stock reservado, y disponibilidad real. ├Ütil para validar antes de crear pedidos. """; public string RequiredScope => "dmi:catalog:read"; public async Task<object> InvokeAsync( string apiKey, JsonElement arguments, CancellationToken ct) { // 1. Parsear y validar input var request = ParseAndValidateInput(arguments); // 2. Ejecutar l├│gica de negocio var result = await _inventoryService.CheckAvailabilityAsync( request.ProductCodes, request.WarehouseId, ct); // 3. Mapear respuesta return new InventoryCheckResponse { Items = result.Select(MapToDto).ToList(), CheckedAt = DateTime.UtcNow, WarehouseId = request.WarehouseId }; } private InventoryCheckRequest ParseAndValidateInput(JsonElement arguments) { var request = new InventoryCheckRequest(); // productCodes (requerido) if (!arguments.TryGetProperty("productCodes", out var codesElement)) { throw new McpToolException(400, "El campo 'productCodes' es obligatorio."); } if (codesElement.ValueKind != JsonValueKind.Array) { throw new McpToolException(400, "El campo 'productCodes' debe ser un array de strings."); } request.ProductCodes = codesElement .EnumerateArray() .Select(x => x.GetString() ?? "") .Where(x => !string.IsNullOrWhiteSpace(x)) .ToList(); if (request.ProductCodes.Count == 0) { throw new McpToolException(400, "Debe especificar al menos un c├│digo de producto."); } if (request.ProductCodes.Count > 100) { throw new McpToolException(400, "M├íximo 100 productos por consulta."); } // warehouseId (opcional) if (arguments.TryGetProperty("warehouseId", out var warehouseElement)) { request.WarehouseId = warehouseElement.GetString(); } return request; } private static InventoryItemDto MapToDto(InventoryItem item) => new() { ProductCode = item.ProductCode, ProductName = item.ProductName, StockTotal = item.StockTotal, StockReserved = item.StockReserved, StockAvailable = item.StockAvailable, IsAvailable = item.StockAvailable > 0, NextRestockDate = item.NextRestockDate }; } // DTOs internos file record InventoryCheckRequest { public List<string> ProductCodes { get; set; } = new(); public string? WarehouseId { get; set; } } file record InventoryCheckResponse { public List<InventoryItemDto> Items { get; set; } = new(); public DateTime CheckedAt { get; set; } public string? WarehouseId { get; set; } } file record InventoryItemDto { public string ProductCode { get; set; } = ""; public string ProductName { get; set; } = ""; public int StockTotal { get; set; } public int StockReserved { get; set; } public int StockAvailable { get; set; } public bool IsAvailable { get; set; } public DateTime? NextRestockDate { get; set; } } ``` - [ ] Tool implementada - [ ] Validaci├│n de input completa - [ ] Manejo de errores con `McpToolException` - [ ] DTOs definidos ### 3. Registrar en DI ```csharp // Mcp/Infrastructure/McpServiceExtensions.cs public static class McpServiceExtensions { public static IServiceCollection AddMcpServices(this IServiceCollection services) { // ... otras tools ... // Catalog Tools services.AddScoped<IMcpTool, ProductsSearchTool>(); services.AddScoped<IMcpTool, CatalogCategoriesListTool>(); services.AddScoped<IMcpTool, InventoryCheckTool>(); // ÔåÉ A├æADIR // ... resto ... return services; } } ``` - [ ] Tool registrada en `McpServiceExtensions.cs` ### 4. Verificar compilaci├│n ```bash cd src dotnet build --no-restore # Si hay errores, resolver antes de continuar ``` - [ ] Build exitoso ### 5. Implementar Tests ```csharp // test/BigCommerceApi.Tests/Mcp/Catalog/InventoryCheckToolTests.cs using System.Text.Json; using BigCommerceApi.Mcp.Tools.Catalog; using BigCommerceApi.Services; using FluentAssertions; using Microsoft.Extensions.Logging; using Moq; using Xunit; namespace BigCommerceApi.Tests.Mcp.Catalog; public class InventoryCheckToolTests { private readonly Mock<IInventoryService> _inventoryServiceMock; private readonly Mock<ILogger<InventoryCheckTool>> _loggerMock; private readonly InventoryCheckTool _sut; public InventoryCheckToolTests() { _inventoryServiceMock = new Mock<IInventoryService>(); _loggerMock = new Mock<ILogger<InventoryCheckTool>>(); _sut = new InventoryCheckTool( _inventoryServiceMock.Object, _loggerMock.Object); } [Fact] public void Name_ShouldBeSnakeCase() { _sut.Name.Should().Be("inventory_check"); } [Fact] public void RequiredScope_ShouldBeCatalogRead() { _sut.RequiredScope.Should().Be("dmi:catalog:read"); } [Fact] public async Task InvokeAsync_WhenValidInput_ShouldReturnInventory() { // Arrange var input = JsonSerializer.Deserialize<JsonElement>(""" { "productCodes": ["SKU001", "SKU002"] } """); _inventoryServiceMock .Setup(x => x.CheckAvailabilityAsync( It.IsAny<List<string>>(), It.IsAny<string?>(), It.IsAny<CancellationToken>())) .ReturnsAsync(new List<InventoryItem> { new() { ProductCode = "SKU001", StockAvailable = 10 }, new() { ProductCode = "SKU002", StockAvailable = 0 } }); // Act var result = await _sut.InvokeAsync("api-key", input, CancellationToken.None); // Assert result.Should().NotBeNull(); } [Fact] public async Task InvokeAsync_WhenProductCodesEmpty_ShouldThrow400() { // Arrange var input = JsonSerializer.Deserialize<JsonElement>(""" { "productCodes": [] } """); // Act var act = () => _sut.InvokeAsync("api-key", input, CancellationToken.None); // Assert var exception = await act.Should().ThrowAsync<McpToolException>(); exception.Which.StatusCode.Should().Be(400); } [Fact] public async Task InvokeAsync_WhenProductCodesMissing_ShouldThrow400() { // Arrange var input = JsonSerializer.Deserialize<JsonElement>("{}"); // Act var act = () => _sut.InvokeAsync("api-key", input, CancellationToken.None); // Assert var exception = await act.Should().ThrowAsync<McpToolException>(); exception.Which.StatusCode.Should().Be(400); } [Fact] public async Task InvokeAsync_WhenTooManyProducts_ShouldThrow400() { // Arrange var codes = Enumerable.Range(1, 101).Select(i => $"SKU{i:D3}").ToList(); var json = $$"""{"productCodes": [{{string.Join(",", codes.Select(c => $"\"{c}\""))}}]}"""; var input = JsonSerializer.Deserialize<JsonElement>(json); // Act var act = () => _sut.InvokeAsync("api-key", input, CancellationToken.None); // Assert var exception = await act.Should().ThrowAsync<McpToolException>(); exception.Which.StatusCode.Should().Be(400); } } ``` - [ ] Tests implementados - [ ] Test de nombre snake_case - [ ] Test de scope correcto - [ ] Test happy path - [ ] Tests de validaci├│n (input vac├¡o, faltante, excedido) - [ ] Tests de par├ímetros opcionales ### 6. Ejecutar tests ```bash cd src # Solo tests de la nueva tool dotnet test --filter "FullyQualifiedName~InventoryCheckToolTests" # Todos los tests dotnet test ``` - [ ] Tests de la nueva tool pasan - [ ] Todos los tests pasan ### 7. Verificar que aparece en tools/list ```bash # Si tienes acceso a un entorno de desarrollo curl -X POST http://localhost:5000/mcp \ -H "Content-Type: application/json" \ -H "X-Api-Key: YOUR_DEV_KEY" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }' | jq '.result.tools[] | select(.name == "inventory_check")' ``` Output esperado: ```json { "name": "inventory_check", "description": "Verifica la disponibilidad de inventario...", "inputSchema": { "type": "object", "properties": { "productCodes": { "type": "array", "items": { "type": "string" } }, "warehouseId": { "type": "string" } }, "required": ["productCodes"] } } ``` - [ ] Tool aparece en `tools/list` - [ ] Schema de input correcto ### 8. Probar invocaci├│n ```bash curl -X POST http://localhost:5000/mcp \ -H "Content-Type: application/json" \ -H "X-Api-Key: YOUR_DEV_KEY" \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "inventory_check", "arguments": { "productCodes": ["SKU001", "SKU002"] } } }' | jq '.result' ``` - [ ] Invocaci├│n exitosa - [ ] Respuesta tiene formato esperado ### 9. Actualizar documentaci├│n Editar `docs/20-mcp-tools.md`: ```markdown ### inventory_check **Scope**: `dmi:catalog:read` Verifica la disponibilidad de inventario para uno o m├ís productos. **Par├ímetros**: | Campo | Tipo | Requerido | Descripci├│n | |-------|------|-----------|-------------| | productCodes | string[] | S├¡ | C├│digos de producto (max 100) | | warehouseId | string | No | Filtrar por almac├®n | **Ejemplo**: ```json // Request { "productCodes": ["SKU001", "SKU002"], "warehouseId": "WH-MADRID" } // Response { "items": [ { "productCode": "SKU001", "productName": "Port├ítil HP...", "stockTotal": 50, "stockReserved": 10, "stockAvailable": 40, "isAvailable": true, "nextRestockDate": null } ], "checkedAt": "2024-12-27T10:30:00Z", "warehouseId": "WH-MADRID" } ``` Actualizar contadores en `20-mcp-tools.md`: - Total de tools: 44 ÔåÆ 45 - Catalog tools: X ÔåÆ X+1 - [ ] Tool documentada en `20-mcp-tools.md` - [ ] Contadores actualizados - [ ] Ejemplo de request/response incluido ### 10. Crear commit y PR ```bash git checkout -b feature/mcp-tool-inventory-check git add . git commit -m "feat(mcp): add inventory_check tool - New tool to check product availability - Supports multiple product codes (max 100) - Optional warehouse filter - Returns stock total, reserved, and available Tests: 5 new tests added Docs: Updated 20-mcp-tools.md PROC-60" git push origin feature/mcp-tool-inventory-check ``` - [ ] PR creado - [ ] PR URL: ___ ### 11. Verificaci├│n post-merge Despu├®s de merge a `develop`: ```bash # Verificar en entorno dev curl -X POST https://bigcapi-dev.dmi.es/mcp \ -H "Content-Type: application/json" \ -H "X-Api-Key: $DEV_API_KEY" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' \ | jq '.result.tools | length' # Deber├¡a ser el n├║mero actualizado ``` - [ ] Tool disponible en dev - [ ] Smoke test pasando ## Checklist de Calidad para Nueva Tool | Criterio | Verificado | |----------|------------| | **Nombre v├ílido (CR├ìTICO)** | | | Nombre en `snake_case` | | | Descripci├│n clara para LLM | | | Scope correcto (m├¡nimo privilegio) | | | Validaci├│n de todos los inputs | | | Errores con `McpToolException` y c├│digo HTTP apropiado | | | L├¡mites razonables (max items, max length) | | | Async/await correcto | | | Logging apropiado | | | Sin hardcoded values | | | Tests unitarios completos | | | Documentaci├│n actualizada | | ### Validaci├│n del Nombre de Tool (CR├ìTICO) El nombre de la tool **DEBE** cumplir el patr├│n MCP: ``` ^[a-zA-Z0-9_-]{1,64}$ ``` **Caracteres permitidos:** - Letras: `a-z`, `A-Z` - N├║meros: `0-9` - Gui├│n bajo: `_` - Gui├│n: `-` **Caracteres NO permitidos:** - ÔØî Puntos (`.`) - **ERROR COM├ÜN** - ÔØî Espacios - ÔØî Caracteres especiales (@, #, $, etc.) **Longitud:** 1-64 caracteres **Ejemplos v├ílidos:** - Ô£à `inventory_check` - Ô£à `products_search` - Ô£à `orders-create` - Ô£à `pricing_get` **Ejemplos INV├üLIDOS:** - ÔØî `feedback.event.record` ÔåÆ usar `feedback_event_record` - ÔØî `dmi_brand_guide.query` ÔåÆ usar `dmi_brand_guide_query` - ÔØî `get stock` ÔåÆ usar `get_stock` **Test autom├ítico:** El proyecto incluye `McpToolNameValidationTests` que valida todos los nombres de tools en cada build. ## Patrones Comunes ### Normalizaci├│n de Input (legacy fields) ```csharp // Soportar nombres legacy para backward compatibility private static string GetProductCode(JsonElement element) { // Intentar nombre nuevo primero if (element.TryGetProperty("productCode", out var code)) return code.GetString() ?? ""; // Fallback a nombre legacy if (element.TryGetProperty("product", out var legacy)) return legacy.GetString() ?? ""; return ""; } ``` ### Paginaci├│n ```csharp // Par├ímetros est├índar de paginaci├│n var page = arguments.TryGetProperty("page", out var p) ? p.GetInt32() : 1; var pageSize = arguments.TryGetProperty("pageSize", out var ps) ? Math.Clamp(ps.GetInt32(), 1, 100) : 20; ``` ### Enriquecer con datos Icecat ```csharp // Si la tool devuelve productos, opcionalmente enriquecer if (arguments.TryGetProperty("includeSummary", out var incl) && incl.GetBoolean()) { var summaries = await _icecatSummaryCache.GetBatchAsync( lang, items.Select(i => i.ProductId).ToList(), ct); foreach (var item in items) { if (summaries.TryGetValue(item.ProductId, out var summary)) item.Summary = summary; } } ``` ## Troubleshooting | S├¡ntoma | Causa | Acci├│n | |---------|-------|--------| | Tool no aparece en `tools/list` | No registrada en DI | A├▒adir a `McpServiceExtensions` | | Error 403 al invocar | Scope no autorizado | Verificar scope en token/API key | | Error 500 al invocar | Excepci├│n no manejada | A├▒adir try/catch, revisar logs | | Schema incorrecto | DTOs no serializables | Revisar `JsonPropertyName` attributes | | Tests no encuentran la clase | Namespace incorrecto | Verificar `using` statements | ## Resultado - **├ëxito**: - Tool implementada y funcionando - Tests pasando (ÔëÑ 5 tests) - Documentaci├│n actualizada - PR merged - **Parcial**: - Tool funciona pero faltan edge cases - Documentaci├│n pendiente - **Fallo**: - Tests fallando - Tool no aparece en `tools/list` - Revisar registro en DI y compilaci├│n ## Advertencias - **NO** crear tools sin tests - **NO** usar scopes m├ís amplios de lo necesario - **NO** exponer datos internos (IDs de BD, stack traces) - **NO** olvidar l├¡mites en arrays/strings - **SIEMPRE** validar TODOS los inputs, nunca confiar en el LLM ## Mensaje de Finalizaci├│n **IMPORTANTE - ACCI├ôN REQUERIDA AL FINALIZAR:** Cuando hayas completado todos los pasos de este procedimiento, DEBES ejecutar el siguiente comando usando la herramienta Bash: Ejecuta: `echo "====== PROCESO TERMINADO [$(date +%H%M%S)] ======" && echo "RESULTADO: Tool [nombre] creada, X tests added, docs OK"` Sustituye [nombre] y X por los valores reales. --- ## Output Estructurado (Nexus) Al finalizar, el agente DEBE generar un bloque JSON con el siguiente formato para que Nexus pueda procesarlo automaticamente: ```json { "result": "success", "summary": "Ejecucion de PROC-60 completada. [Descripcion breve de resultados]", "metrics": { "issues_found": 0, "issues_resolved": 0, "tool_created": true, "tests_added": 5, "custom": { "procedure_specific_metric": "value" } }, "backlog_items": [ { "title": "Titulo del item de seguimiento", "description": "Descripcion detallada si se requiere accion futura", "priority": "medium", "type": "improvement", "tags": ["proc-60"] } ], "next_steps": [ "Accion recomendada 1", "Accion recomendada 2" ], "warnings": [ "Advertencias encontradas durante la ejecucion" ] } ``` **Campos requeridos:** - `result`: `"success"` | `"partial"` | `"failed"` - `summary`: Resumen ejecutivo en 1-3 lineas **Metricas especificas de este procedure:** - tool_created, tests_added, documentation_updated **Criterios de resultado:** - `success`: Procedimiento completado sin errores criticos - `partial`: Completado con algunos problemas menores o items pendientes - `failed`: Error critico o no se pudo completar ## Historial de Ejecuciones | Fecha | Tool Creada | Dominio | Scope | Tests | Docs | PR | |-------|-------------|---------|-------|-------|------|-----| | | | | | | | |
H1
H2
H3
Bold
Italic
Code
Lista
Num
Task
Code Block
Link
Nexus Platform
Reconectando
Recuperando la conexion
Se ha interrumpido la conexion con el servidor. Estamos reconectando automaticamente.
Reconectando...
Manten esta pestana abierta, volvemos enseguida.
No hemos podido reconectar
El servidor puede estar reiniciandose o tu conexion a internet es inestable.
Reintentar
La sesion ha expirado
Recarga la pagina para iniciar una nueva sesion.
Recargar
Si no vuelve en 30 segundos, recarga la pagina.