Saltar al contenido
Firma de Manifiesto de Tools

Firma Ed25519 del manifiesto de tools MCP

Trusteed soporta la firma del manifiesto público de tools MCP con Ed25519 (JWS compacto, payload detachado). Cada tool lleva hashes SHA-256 separados de su descripción y su inputSchema, permitiendo a agentes y operadores detectar cualquier cambio silencioso antes de ejecutar una tool.

Capacidad de spec — en activación

Endpoint público

El manifiesto está disponible en una URL canónica estable con cabeceras HTTP que exponen la firma y el hash directamente, sin necesidad de parsear el cuerpo JSON para verificación rápida.

URL canónica

https://trusteed.xyz/.well-known/mcp-tools-manifest.json

Cache-Control:
public, max-age=3600, s-maxage=3600
Content-Type:
application/json

Cabeceras de respuesta

X-JWS-Signature
Formato: <kid>:<jws_compacto>. El kid identifica la clave en jwks.json. El JWS tiene payload detachado (campo vacío entre los dos puntos centrales).
X-Manifest-Version
Versión en formato YYYY-MM-DD.N (p.ej. 2026-05-02.1).
X-Manifest-Hash
SHA-256 hex del cuerpo JSON canónico completo.

Esquema de firma

La firma sigue el perfil JWS Compact Serialization (RFC 7515) con payload detachado: el JSON del manifiesto no se incluye dentro del token JWS, solo se firma como bytes en crudo.

Algoritmo

EdDSA (Ed25519) — clave de 255 bits, firma de 64 bytes, resistente a ataques de canal lateral.

alg: "EdDSA", crv: "Ed25519"

Clave pública

Publicada en el endpoint JWKS estándar. El campo kid en el header JWS identifica qué clave usar.

https://trusteed.xyz/.well-known/jwks.json

Rotación de clave

Período de gracia: la clave anterior sigue publicada en JWKS tras introducir la nueva. Ambas son válidas durante ese período para permitir transiciones sin interrupción.

Hashes por tool

Además de la firma global del manifiesto, cada entrada de tool incluye dos hashes SHA-256 independientes que cubren la descripción y el inputSchema de la tool.

description_sha256

Hash SHA-256 de la descripción de la tool. Permite detectar cambios en el texto visible al agente sin necesidad de comparar el manifiesto completo.

input_schema_sha256

Hash SHA-256 del inputSchema de la tool. Los cambios de schema (nuevos parámetros, tipos, required) siempre modifican este hash, aunque la descripción no cambie.

Estructura de una entrada de tool

{
  "name": "search_products",
  "description": "Search for products in a merchant store...",
  "hashes": {
    "description_sha256": "a3f2...c1d9",
    "input_schema_sha256": "7b04...e82a"
  }
}

Por qué importa: tool poisoning

El "tool poisoning" (MCP-08 en la taxonomía MCP-38) consiste en modificar silenciosamente la descripción o el inputSchema de una tool para inducir al agente a escalar privilegios, filtrar datos o ejecutar acciones no autorizadas. La firma del manifiesto permite tres niveles de protección:

  1. Verificación de integridad global

    La firma JWS Ed25519 sobre el manifiesto completo garantiza que ningún contenido ha sido modificado en tránsito ni en el servidor. Un fallo de firma invalida todo el manifiesto.

  2. Pinning de hashes por tool

    Un cliente puede almacenar los hashes de las tools que usa y comparar en cada invocación. Si description_sha256 o input_schema_sha256 cambian sin un bump de versión, el cliente puede rechazar la llamada.

  3. Políticas granulares

    Hashes separados para descripción e inputSchema permiten políticas distintas: un operador puede tolerar cambios cosméticos en la descripción pero rechazar cualquier mutación de schema.

Versionado del manifiesto

El campo version sigue el formato YYYY-MM-DD.N, donde N es un contador ordinal que se incrementa si se emite más de un manifiesto en el mismo día. El campo superseded_at indica cuándo una versión anterior deja de ser válida, permitiendo transiciones graduales.

Campos de versión en el manifiesto

{
  "version": "2026-05-02.1",
  "previous_version": "2026-04-15.1",
  "superseded_at": "2026-05-09T00:00:00Z",
  "tools": [ ... ]
}
version
Versión actual en formato YYYY-MM-DD.N.
previous_version
Referencia a la versión anterior para clientes que necesitan trazar el historial.
superseded_at
ISO 8601. Fecha a partir de la cual la versión anterior debe considerarse expirada.

Verificación completa (Node.js + jose)

El siguiente snippet descarga el manifiesto y el JWKS, verifica la firma JWS, y comprueba los hashes de una tool específica. Requiere el paquete jose (v5+) y Node.js 18+.

verify-manifest.mjs

import { createHash } from 'node:crypto';
import { compactVerify, createLocalJWKSet } from 'jose';

const MANIFEST_URL = 'https://trusteed.xyz/.well-known/mcp-tools-manifest.json';
const JWKS_URL     = 'https://trusteed.xyz/.well-known/jwks.json';

async function fetchJson(url) {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);
  return res.json();
}

// 1. Fetch manifest and JWKS
const manifest = await fetchJson(MANIFEST_URL);
const jwks     = await fetchJson(JWKS_URL);
const keySet   = createLocalJWKSet(jwks);

// 2. Reconstruct compact JWS with the manifest body as detached payload
//    The server provides the signature via X-JWS-Signature header (<kid>:<jws>)
//    or the manifest body includes a top-level "_jws" field as fallback.
const rawJws   = manifest._jws;            // e.g. "eyJhbGciOiJFZERTQSIsImtpZCI6Ii4uLiJ9..eyJ..."
if (!rawJws) throw new Error('No _jws field found in manifest');

// 3. Verify Ed25519 signature — jose rejects tampered payloads automatically
const manifestBytes = Buffer.from(JSON.stringify(manifest, null, 0));
const { protectedHeader } = await compactVerify(rawJws, keySet, {
  algorithms: ['EdDSA'],
});
// => Signature OK — kid: <opaque_key_id>
// => Manifest version: 2026-05-02.1

// 4. Verify per-tool hashes for a specific tool
const toolName   = 'search_products';
const tool       = manifest.tools?.find(t => t.name === toolName);
if (!tool) throw new Error(`Tool "${toolName}" not found in manifest`);

const descHash   = createHash('sha256').update(tool.description, 'utf8').digest('hex');
const schemaHash = createHash('sha256')
  .update(JSON.stringify(tool.inputSchema), 'utf8')
  .digest('hex');

if (descHash !== tool.hashes.description_sha256) {
  throw new Error('description_sha256 mismatch — possible tool poisoning!');
}
if (schemaHash !== tool.hashes.input_schema_sha256) {
  throw new Error('input_schema_sha256 mismatch — possible schema mutation!');
}

// => Tool "search_products" hashes verified OK
// =>   description_sha256 : a1b2c3...
// =>   input_schema_sha256: d4e5f6...

Para un script mínimo de curl + verificación de firma, ver también la página padre: Seguridad MCP.

Endpoint JWKS

Las claves públicas Ed25519 usadas para firmar el manifiesto se publican en el endpoint JWKS estándar RFC 7517. Usa los campos use y kid del header JWS para seleccionar la clave correcta.

Ejemplo de entrada de clave en jwks.json

{
  "keys": [
    {
      "kty": "OKP",
      "crv": "Ed25519",
      "use": "sig",
      "alg": "EdDSA",
      "kid": "<opaque_key_id>",
      "x": "<base64url_encoded_public_key>"
    }
  ]
}

https://trusteed.xyz/.well-known/jwks.json — Cache 3600s. Actualizado automáticamente durante la rotación de clave.

Firma de Manifiesto de Tools — Ed25519 JWS | Trusteed