
Um protocolo de comunicação unificado e voltado para a segurança, para acesso a ferramentas e coordenação de agentes. O UAP elimina a CVE-2025-49596 e as vulnerabilidades de envenenamento de ferramentas MCP usando CapabilityCards assinados com Ed25519, mTLS obrigatório, autenticação via IdP Keycloak e sandboxing Docker efêmero por chamada.
██╗ ██╗ █████╗ ██████╗
██║ ██║██╔══██╗██╔══██╗
██║ ██║███████║██████╔╝
██║ ██║██╔══██║██╔═══╝
╚██████╔╝██║ ██║██║
╚═════╝ ╚═╝ ╚═╝╚═╝
Universal Agent Protocol
Um protocolo de comunicação unificado, que prioriza a segurança, para acesso a ferramentas, coordenação de agentes e RPC estruturado. Construído para fechar a CVE-2025-49596 e a classe de vulnerabilidades de envenenamento de ferramentas e escape de sandbox que o MCP deixou estruturalmente abertas.
git clone https://github.com/RajSidwadkar/UAP-protocol
cd UAP-protocol
npm install
npx tsx scripts/generate-keypair.ts
docker compose -f docker-compose.dev.yml up -d
curl http://localhost:3000/health
# {"status":"ok","version":"1.0.0"}
Isso inicia o Keycloak na :8080 e o UAP Gateway na :3000. O gateway aplica mTLS, verifica CapabilityCards assinados com Ed25519 e executa cada chamada de ferramenta em um contêiner Docker efêmero. Nenhuma configuração é necessária além do par de chaves gerado.
graph TD
A[Client / Agent SDK] -->|mTLS + JWT + card_sig| B[UAP Gateway<br/>Fastify · port 3000]
B --> C{8-Stage Pipeline}
C --> C1[1 · Frame parse<br/>UapMessageFactory]
C1 --> C2[2 · Schema validate<br/>AJV against schema_ref]
C2 --> C3[3 · Token verify<br/>Keycloak JWKS · max 15 min]
C3 --> C4[4 · Scope enforce<br/>PermissionEnforcer]
C4 --> C5[5 · Card verify<br/>Ed25519 signature check]
C5 --> C6[6 · Sandbox execute<br/>Docker · CapDrop ALL · 128 MB]
C6 --> C7[7 · Audit emit<br/>AuditEventBus · non-blocking]
C7 --> C8[8 · Response<br/>UapResponseEnvelope]
B --> R[(Service Registry<br/>InMemory · Redis)]
B --> KC[(Keycloak IdP<br/>OAuth 2.1 · PKCE)]
B --> OT[(OpenTelemetry<br/>W3C TraceContext)]
B --> AU[(Audit Log<br/>append-only NDJSON)]Camadas hexagonais — o domínio tem zero dependências de I/O. Toda preocupação de infraestrutura fica atrás de uma interface de porta tipada. Troque Docker por WASM, Keycloak por Auth0, Pino por um exportador de SIEM — zero impacto no domínio.
CVE-2025-49596 (envenenamento de ferramentas por meio de metadados não assinados). As descrições de ferramentas do MCP são mutáveis após a publicação. Um atacante pode injetar instruções maliciosas em nomes ou descrições de ferramentas após a implantação — o cliente não tem como detectar adulteração. O UAP coloca todos os metadados de ferramentas dentro do payload do CapabilityCard assinado com Ed25519. Qualquer mutação após a assinatura invalida a assinatura e o gateway rejeita o cartão antes de executar qualquer coisa.
CWE-284 / classe de escape de sandbox. O MCP executa ferramentas no nível do processo do servidor. Um path traversal ou injeção de processo em qualquer ferramenta alcança o sistema de arquivos e a rede do host. O UAP cria um contêiner Docker novo por chamada — rootfs somente leitura, CapDrop: ALL, limite de 128 MB de RAM, rede desabilitada por padrão. O contêiner é destruído após a resposta. Não há superfície de ataque persistente entre chamadas.
CWE-287 / autenticação de representante confuso (confused-deputy). O MCP atua como seu próprio provedor OAuth, tornando-o tanto o servidor de recursos quanto o servidor de autorização. Esse é o clássico padrão confused-deputy. O UAP separa esses papéis: o gateway é um servidor de recursos puro. O Keycloak (ou qualquer IdP externo) é a única autoridade. Os JWTs são verificados contra um endpoint JWKS remoto e têm limite rígido de 15 minutos.
Toda mensagem UAP usa o mesmo envelope. O bloco auth carrega um JWT com escopo e uma referência ao CapabilityCard assinado do agente. O gateway verifica ambos antes que a requisição chegue a qualquer código de aplicação.
{
"uap": {
"version": "1.0",
"type": "tool_call",
"id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
"trace": {
"traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
},
"auth": {
"token": "eyJhbGciOiJSUzI1NiJ9...",
"scope": ["tool:read"],
"card_sig": "ed25519:a1b2c3d4..."
}
},
"method": "tools/invoke",
"schema_ref": "uap:tool.invoke/v1",
"params": {
"tool_id": "db:query",
"input": { "sql": "SELECT 1" }
},
"ack": true
}
UAP-protocol/
├── packages/
│ ├── gateway/ @uap/gateway — Fastify-based UAP Gateway
│ ├── sdk-ts/ @uap/sdk-ts — TypeScript client + server SDK
│ └── sdk-py/ uap-sdk — Python async SDK (httpx + FastAPI)
├── scripts/
│ ├── generate-keypair.ts — Ed25519 keypair for local dev
│ └── keycloak-bootstrap.ts — Idempotent Keycloak realm setup
└── docker/
└── sandbox/ — Base image for zero-trust tool execution
import { UapClient, ClientCredentialsTokenProvider } from "@uap/sdk-ts";
const client = new UapClient({
gatewayUrl: "https://gateway.example.com",
tokenProvider: new ClientCredentialsTokenProvider({
tokenUrl: process.env.TOKEN_URL!,
clientId: process.env.CLIENT_ID!,
clientSecret: process.env.CLIENT_SECRET!,
}),
});
const result = await client.invokeTool(
"db:query",
{ sql: "SELECT * FROM users LIMIT 10" },
["tool:read"]
);
const task = await client.delegateTask(
"summarizer",
{ text: "..." },
["task:submit"]
);
from uap_sdk.client import UapClient, UapClientOptions
from uap_sdk.token import ClientCredentialsTokenProvider
async with UapClient(UapClientOptions(
gateway_url="https://gateway.example.com",
token_provider=ClientCredentialsTokenProvider(
token_url=os.environ["TOKEN_URL"],
client_id=os.environ["CLIENT_ID"],
client_secret=os.environ["CLIENT_SECRET"],
)
)) as client:
result = await client.invoke_tool("db:query", {"sql": "SELECT 1"}, ["tool:read"])
Middleware FastAPI
from uap_sdk.middleware import uap_auth
@app.post("/summarize")
@uap_auth(scope=["task:submit"])
async def summarize(request: Request):
claims = request.state.uap_claims # typed AuthClaims
...
O CLI uap-migrate envolve qualquer servidor MCP como um CapabilityCard UAP assinado em menos de 5 minutos. Idempotente — executá-lo novamente em um servidor já migrado é um no-op.
node dist/cli/migrate.js mcp http://localhost:3001 \
--issuer did:uap:my-org \
--key config/dev.privkey.hex \
--out ./uap-cards
# → ./uap-cards/did:uap:my-org.card.json
Nomes de ferramentas, descrições e esquemas de parâmetros fazem parte do payload assinado com Ed25519. Mutar qualquer campo após a assinatura invalida a assinatura — derrotando estruturalmente o envenenamento de ferramentas.
const signed = await signer.sign({
issuer: "did:uap:my-agent",
version: "1.0.0",
tools: [{
id: "db:query",
description: "Run a read-only SQL query",
inputSchema: { type: "object", required: ["sql"] },
scopes: ["tool:read"]
}],
scopes: ["tool:read"],
issuedAt: Date.now(),
expiresAt: Date.now() + 86_400_000 * 30
});
// signed.signature === "ed25519:a1b2c3..."
Nomenclatura de branches: feat/<sprint>-<task>-<slug> — ex.: feat/s2-2.1-mtls-keycloak
Toda alteração segue: branch → npx tsc --noEmit + npx vitest run localmente → commit → PR → squash merge → exclusão da branch. Sem dependência de CI para desenvolvimento local.
# Run all tests before committing
cd packages/gateway && npx vitest run
cd packages/sdk-ts && npx vitest run
cd packages/sdk-py && python -m pytest tests/ -v
UAP · Universal Agent Protocol · 2026
SPDX-License-Identifier: Apache-2.0
| Camada | Conteúdo | Dependências |
|---|
| Domínio | UapEnvelope, CapabilityCard, AuditEvent, Task | Nenhuma — apenas stdlib |
| Aplicação | InvokeToolUseCase, DelegateTaskUseCase, ValidateCardUseCase | Apenas portas de domínio |
| Infraestrutura | KeycloakAuthAdapter, DockerSandboxAdapter, Ed25519SignerAdapter, PinoAuditAdapter | Portas da aplicação + bibliotecas externas |
| Transporte | Parser de frames UAP-RPC, gateway Fastify, handlers SSE/WebSocket | Infraestrutura + serializadores de domínio |
| Recurso | UAP | MCP | A2A | JSON-RPC 2.0 |
|---|
| Autenticação obrigatória | ✅ mTLS + JWT sempre | ❌ Opcional | ⚠️ Apenas chave de API | ❌ Nenhuma |
| Integridade dos metadados das ferramentas | ✅ Payload assinado com Ed25519 | ❌ Mutação pós-publicação possível | ❌ Sem assinatura | ❌ Sem assinatura |
| Modelo de sandbox | ✅ Docker efêmero por chamada | ⚠️ Apenas no nível do servidor | ❌ Nenhum | ❌ Nenhum |
| Descoberta de agentes | ✅ Hub-and-spoke · N conexões | ❌ HTTP direto N² | ⚠️ Baseado em DNS | ❌ Nenhuma |
| Trilha de auditoria | ✅ Log append-only nativo do protocolo | ❌ Nenhum | ❌ Nenhum | ❌ Nenhum |
| Rastreamento distribuído | ✅ W3C TraceContext obrigatório | ❌ Nenhum | ❌ Nenhum | ❌ Nenhum |
| Validação de esquema | ✅ AJV na camada de transporte | ⚠️ Zod opcional | ❌ Nenhuma | ❌ Nenhuma |
| Limite de validade do token | ✅ 15 min obrigatórios | ❌ Não aplicado | ❌ Não aplicado | ❌ N/A |
| Semântica de lotes | ✅ serial · paralela · transacional | ❌ Indefinida | ❌ Nenhuma | ⚠️ Ambígua |
| Payloads binários | ✅ Frames nativos | ❌ Apenas Base64 | ❌ Apenas Base64 | ❌ Apenas Base64 |
| Modelo de IdP | ✅ Apenas IdP externo | ❌ O servidor é seu próprio provedor OAuth | ⚠️ Varia | ❌ Nenhum |
| Adaptadores de ponte | ✅ Pontes MCP + A2A chegam na Fase 2 | ❌ Sem ponte | ❌ Sem ponte | ❌ Sem ponte |
| Escala horizontal | ✅ Stateless · qualquer balanceador de carga | ❌ Sessões sticky | ⚠️ Varia | ❌ Nenhuma |
| Variável | Descrição | Padrão |
|---|
KEYCLOAK_URL | URL base do Keycloak | http://localhost:8080 |
KEYCLOAK_REALM | Nome do realm | uap |
UAP_AUDIENCE | Claim de audience do JWT | uap-gateway |
UAP_SIGNING_KEY_PATH | Caminho para a chave privada Ed25519 em hex | config/dev.privkey.hex |
UAP_DOCKER_SOCKET | Caminho do socket Docker | /var/run/docker.sock |
UAP_AUDIT_LOG_DIR | Diretório do log de auditoria NDJSON append-only | /var/log/uap |
UAP_GATEWAY_PORT | Porta de escuta do gateway | 3000 |
UAP_MTLS_CERT | Certificado TLS do servidor (PEM) | certs/server.crt |
UAP_MTLS_KEY | Chave privada TLS do servidor (PEM) | certs/server.key |
UAP_MTLS_CA | Certificado CA para verificação do certificado do cliente | certs/ca.crt |
OTEL_EXPORTER_OTLP_ENDPOINT | Endpoint do coletor OpenTelemetry | http://localhost:4318 |
REDIS_URL | Redis para registro multi-instância (opcional) | — |
| Pacote | Versão | Finalidade |
|---|
fastify | ^4.27 | Servidor HTTP do gateway |
zod | ^3.23 | Esquema do envelope e inferência de tipos |
@noble/curves | ^1.4 | Assinatura Ed25519 |
jose | ^5.4 | Verificação de JWT, cliente JWKS |
ajv | ^8.16 | Validação de parâmetros no nível de transporte |
dockerode | ^4.0 | Adaptador de sandbox zero-trust |
pino | ^9.2 | Log de auditoria estruturado append-only |
ulid | ^2.3 | IDs exclusivos ordenáveis |
@opentelemetry/sdk-node | ^0.52 | Rastreamento distribuído |
ioredis | ^5.3 | Registro de serviços multi-instância |
@modelcontextprotocol/sdk | ^1.0 | Adaptador de ponte MCP |
httpx (Python) | ^0.27 | Cliente HTTP assíncrono para o SDK Python |
pydantic (Python) | ^2.7 | Validação de tipos Python |