Files
agent_platform_oci/docs/MCP_MULTI_ITEM_DEVELOPER_GUIDE.md

11 KiB

MCP Multi-item — Guia do Desenvolvedor

Objetivo

Use este guia quando uma única intenção do usuário precisa operar sobre mais de um item do mesmo tipo em uma chamada MCP, por exemplo:

  • cancelar vários VAS/produtos;
  • devolver ou consultar vários pedidos;
  • alterar vários ativos;
  • processar uma lista de faturas, linhas, contratos ou recursos.

O framework já possui contratos genéricos para coleta de listas, pré-validação/canonicalização, confirmação transacional, execução MCP e normalização do resultado por item. O desenvolvedor não deve criar um segundo motor de multi-item dentro do agente.

Regra principal

Não faça loop de negócio no agente. O agente declara a tool e seus parâmetros; o runtime mantém estado, confirmação e evidência. A operação em lote deve ser exposta como uma tool MCP que aceite uma coleção, ou como uma tool transacional apoiada por um workflow/MCP que processe a coleção.

Fluxo recomendado:

Usuário cita N itens
  -> Router/Agent seleciona a tool
  -> Runtime coleta/reconcilia parâmetro array/list
  -> (opcional) pre-validation canonicaliza itens
  -> Runtime pede UMA confirmação coerente para o conjunto
  -> MCP recebe a coleção
  -> MCP/workflow processa item a item
  -> MCP devolve results[] com success/ok por item
  -> Framework normaliza SUCCESS / PARTIAL_SUCCESS / FAILED
  -> Composição final relata sucessos e falhas individualmente

1. Declarar um parâmetro de lista em tools.yaml

O reconciliador transacional do framework aceita type: array ou type: list.

tools:
  cancelar_produtos:
    description: Cancela um ou mais produtos do cliente.
    mcp_server: contas
    enabled: true
    tool_type: action
    confirmation_required: true
    requires: [items]
    args_schema:
      items:
        type: array
        label: os produtos
        description: Lista de produtos que o cliente deseja cancelar.
        user_prompt: Informe quais produtos deseja cancelar.

O runtime pode reconciliar uma frase como:

quero cancelar TIM Fashion, Aya Audiobooks e Neymar Jr

para um argumento lógico semelhante a:

{
  "items": ["TIM Fashion", "Aya Audiobooks", "Neymar Jr"]
}

A semântica de domínio e a validação dos nomes continuam pertencendo ao MCP/pre-validator, não ao core.

2. Quando usar pre-validation

Use tool_policies.yaml quando a lista precisa ser resolvida/canonicalizada antes da confirmação.

tool_policies:
  cancelar_produtos:
    operation_type: transactional
    require_confirmation: true
    requires: [items]
    pre_validation:
      enabled: true
      tool: validar_produtos_cancelamento
      fail_open: false

O pre-validator pode retornar eligible: true e substituir argumentos por valores canônicos em transaction_decision.resolved_arguments:

{
  "eligible": true,
  "transaction_decision": {
    "resolved_arguments": {
      "items": [
        {"name": "TIM Fashion Mensal", "id": "1001"},
        {"name": "Aya Audiobooks Premium", "id": "1002"},
        {"name": "Neymar Jr", "id": "1003"}
      ]
    },
    "target_tool": "cancelar_produtos",
    "confirmation_message": "Você confirma o cancelamento de TIM Fashion Mensal, Aya Audiobooks Premium e Neymar Jr?"
  }
}

Importante para validação parcial

O contrato eligible da pre-validation é global. Portanto, não retorne eligible: false apenas porque um item da lista falhou, caso os demais ainda possam ser processados. Isso encerraria toda a transação.

Para permitir resultado parcial, a estratégia recomendada é:

  1. retornar eligible: true quando existe pelo menos um item acionável;
  2. preservar/canonicalizar a coleção em resolved_arguments;
  3. deixar a tool primária devolver o resultado terminal de cada item em results[].

Se nenhum item puder ser processado, então eligible: false é apropriado.

3. Contrato de entrada do MCP

Prefira uma única chamada MCP com uma coleção explícita:

{
  "tool_name": "cancelar_produtos",
  "arguments": {
    "customer_id": "123",
    "items": [
      {"name": "TIM Fashion Mensal", "id": "1001"},
      {"name": "Aya Audiobooks Premium", "id": "1002"},
      {"name": "Neymar Jr", "id": "1003"}
    ]
  }
}

O framework não deve ser usado como um loop improvisado que chama N vezes uma tool escalar a partir do agente. Se o backend só possui API unitária, o fan-out pode ficar encapsulado no MCP Server ou em um workflow de domínio, mantendo uma única tool lógica para o agente.

4. Contrato obrigatório de resultado por item

Para o framework reconhecer multi-item automaticamente, a tool primária solicitada deve expor uma lista results com dois ou mais itens, e cada item deve possuir um booleano success ou ok.

Exemplo:

{
  "ok": true,
  "result": {
    "results": [
      {"id": "1001", "subject": "TIM Fashion Mensal", "success": true},
      {"id": "1002", "subject": "Aya Audiobooks Premium", "success": false, "reason": "not_found"},
      {"id": "1003", "subject": "Neymar Jr", "success": true}
    ]
  }
}

Campos como id, subject, name, reason, error, protocol e outros são de domínio e opcionais. O sinal estrutural exigido pelo normalizador é success: true|false ou ok: true|false por item.

Workflow-backed tool

Se a tool é executada por workflow, o caminho preferencial e autoritativo é:

workflow.output[tool_name].results

Exemplo:

{
  "ok": false,
  "result": {
    "status": "COMPLETED",
    "output": {
      "cancelar_produtos": {
        "results": [
          {"subject": "A", "success": true},
          {"subject": "B", "success": false, "reason": "not_found"},
          {"subject": "C", "success": true}
        ]
      },
      "etapa_auxiliar": {
        "success": false,
        "error": "falha em pós-processamento"
      }
    }
  },
  "error": "falha em pós-processamento"
}

O runtime preserva o resultado da operação primária e não permite que uma falha auxiliar apague itens já concluídos.

5. O que o framework produz automaticamente

Com results[] válido, o runtime adiciona:

{
  "multi_item_status": "PARTIAL_SUCCESS",
  "partial_success": true,
  "metadata": {
    "multi_item": {
      "status": "PARTIAL_SUCCESS",
      "items_count": 3,
      "items_succeeded_count": 2,
      "items_failed_count": 1,
      "primary_tool": "cancelar_produtos"
    }
  }
}

Estados possíveis:

Situação multi_item_status
todos os itens concluíram SUCCESS
parte concluiu e parte falhou PARTIAL_SUCCESS
todos falharam FAILED

Se uma etapa posterior fizer o wrapper retornar ok=false, mas pelo menos um item primário tiver sucesso, o framework preserva para auditoria:

{
  "ok": true,
  "original_ok": false,
  "secondary_error": "erro da etapa posterior"
}

Isso evita transformar sucesso parcial/real em falha total na resposta ao usuário.

6. O que NÃO fazer

Evite estes padrões:

# NÃO: motor multi-item paralelo dentro do agente
for product in products:
    await tool_router.call("cancelar_produto", {"product": product})
// NÃO: apenas um approved global sem evidência por item
{
  "approved": false,
  "error": "um item falhou"
}
// NÃO: um único success agregado quando houve resultados mistos
{
  "success": false,
  "message": "operação bloqueada"
}

Esses formatos perdem a evidência terminal individual e podem fazer itens concluídos desaparecerem da composição final.

7. Exemplo completo: vários pedidos

tools.yaml

tools:
  cancelar_pedidos:
    description: Cancela um ou mais pedidos.
    mcp_server: retail
    enabled: true
    tool_type: action
    confirmation_required: true
    requires: [order_ids]
    args_schema:
      order_ids:
        type: array
        label: os pedidos
        description: Lista de identificadores de pedidos a cancelar.
        user_prompt: Quais pedidos deseja cancelar?

Retorno MCP

{
  "ok": true,
  "result": {
    "results": [
      {"order_id": "P-100", "success": true, "protocol": "ABC1"},
      {"order_id": "P-101", "success": false, "reason": "already_shipped"},
      {"order_id": "P-102", "success": true, "protocol": "ABC3"}
    ]
  }
}

O framework classificará automaticamente como PARTIAL_SUCCESS e fornecerá ao LLM a instrução de relatar os resultados item a item.

8. Read-only multi-item

A mesma estrutura serve para consultas em lote. A diferença é que normalmente não existe confirmação:

tool_policies:
  consultar_pedidos:
    operation_type: read_only
    require_confirmation: false

A tool ainda pode retornar results[] com success/ok por item para que uma consulta parcialmente indisponível não esconda as consultas bem-sucedidas.

9. Idempotência e protocolos

Em operações com efeito colateral:

  • use uma chave de idempotência de requisição e, quando necessário, uma chave por item;
  • devolva protocolo/operation id no próprio item;
  • não dependa apenas de uma mensagem textual agregada;
  • em retry, preserve os itens já concluídos e execute apenas o que a regra de domínio permitir.

O framework preserva evidência; a política de idempotência do sistema de destino continua pertencendo ao MCP/workflow de domínio.

10. Checklist para o desenvolvedor

  • A tool aceita array/list quando o caso de uso é multi-item.
  • Não existe loop MCP de negócio dentro da classe do agente.
  • Se houver canonicalização, ela está em pre-validation/MCP, não hardcoded no agente.
  • A confirmação mostra o conjunto efetivo de itens.
  • A tool primária retorna results[] com success ou ok por item.
  • Resultado parcial não é reduzido a um único approved=false ou success=false global.
  • Workflow usa output[tool_name].results para a evidência terminal da tool primária.
  • Testes cobrem todos-sucesso, parcial, todos-falha e falha auxiliar posterior.
  • A resposta final menciona sucessos e falhas quando houver PARTIAL_SUCCESS.

11. Onde o comportamento existe no framework

Referências atuais:

libs/agent_framework/src/agent_framework/runtime/transaction_parameters.py
  - coerção de type=array/list

libs/agent_framework/src/agent_framework/runtime/agent_runtime.py
  - pre-validation e resolved_arguments
  - _primary_multi_item_outcomes()
  - _normalize_multi_item_tool_result()
  - instrução de composição multi-item para o LLM

tests/test_multi_item_result_normalization.py
  - regressões de SUCCESS/PARTIAL_SUCCESS e proteção contra falha auxiliar

12. Resumo de responsabilidade

Camada Responsabilidade
Agente declarar intenção/tool e usar o runtime; não implementar motor multi-item
tools.yaml declarar parâmetro array/list e schema
tool_policies.yaml confirmação e pre-validation
Pre-validator MCP resolver/canonicalizar entidades e argumentos
Tool MCP/workflow executar a coleção e produzir resultado por item
Framework runtime manter estado, confirmação, normalizar resultado multi-item e preservar evidência
LLM/presentation transformar evidências em resposta, sem apagar sucesso/falha individual