Algorand

Un servidor MCP completo para interacciones con herramientas (40+) y accesibilidad a recursos (60+), además de muchos indicadores útiles para interactuar con la cadena de bloques Algorand.

Documentación

Servidor MCP de Algorand

npm version npm downloads License: MIT

Un servidor integral del Protocolo de Contexto de Modelos (MCP) que brinda a los agentes de IA y LLMs acceso completo a la cadena de bloques de Algorand. Creado por GoPlausible.

Algorand es una cadena de bloques de Capa 1 de prueba de participación pura y carbono negativo, con finalidad instantánea, tarifas bajas y soporte integrado para contratos inteligentes (AVM), activos estándar (ASAs) y transacciones atómicas.

¿Qué es MCP?

El Protocolo de Contexto de Modelos es un estándar abierto que permite que las aplicaciones de IA se conecten a herramientas y fuentes de datos externas. Este servidor expone las operaciones de la cadena de bloques de Algorand como herramientas MCP que cualquier cliente de IA compatible puede usar: Claude Desktop, Claude Code, Cursor, Windsurf y otros.

Características

  • Billetera del agente: mnemónicos almacenados en una base de datos SQLite local, utilizados por el servidor MCP para firmar en nombre del agente (los mnemónicos nunca se devuelven en las respuestas de las herramientas)
  • Cuentas de billetera con apodos legibles por humanos
  • Creación de cuentas, gestión de claves y reasignación de claves
  • Construcción, firma y envío de transacciones (pagos, activos, aplicaciones, registro de claves)
  • Grupos de transacciones atómicas
  • Compilación y desensamblaje de TEAL
  • Acceso completo a las API de Algod e Indexer
  • Integración del servicio de nombres NFDomains (NFD)
  • Micropagos HTTP x402: descubrimiento automático y solicitudes pagadas en una sola llamada usando la billetera activa (USDC/ALGO)
  • Herramientas AP2 para Algorand
  • Integración con Tinyman AMM (pools, swaps, liquidez)
  • Agregación DEX del enrutador Haystack (swaps al mejor precio entre Tinyman, Pact, Folks)
  • Negociación en el mercado de predicciones Alpha Arcade (explorar mercados, libros de órdenes, órdenes límite/de mercado, posiciones, reclamos)
  • Generación de URI y códigos QR ARC-26
  • Base de conocimiento de Algorand con taxonomía completa de documentación para desarrolladores
  • Selección de red por llamada de herramienta (mainnet, testnet, localnet) y paginación

Requisitos

  • Node.js v20 o posterior
  • npm, pnpm o yarn

Instalación

Desde npm

npm install -g @goplausible/algorand-mcp

Desde el código fuente

git clone https://github.com/GoPlausible/algorand-mcp.git
cd algorand-mcp
npm install
npm run build

Configuración de MCP

El servidor se ejecuta sobre stdio. Hay tres formas de invocarlo: elige la que se adapte a tu configuración:

MétodoComandoCuándo usarlo
npx (recomendado)npx @goplausible/algorand-mcpSin necesidad de instalación, siempre la última versión
Instalación globalalgorand-mcpDespués de npm install -g @goplausible/algorand-mcp
Ruta absolutanode /path/to/dist/index.jsCompilado desde el código fuente o clon local

No se requieren variables de entorno para el uso estándar. La selección de red, la paginación y las URL de nodos se manejan dinámicamente en cada llamada de herramienta.


OpenClaw

No se necesita configuración manual: instala el paquete npm @goplausible/openclaw-algorand-plugin y el servidor MCP de Algorand se configura automáticamente:

npm install -g @goplausible/openclaw-algorand-plugin

Claude Desktop

Edita ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) o %APPDATA%\Claude\claude_desktop_config.json (Windows):

Usando npx:

{
  "mcpServers": {
    "algorand-mcp": {
      "command": "npx",
      "args": ["@goplausible/algorand-mcp"]
    }
  }
}

Usando instalación global:

{
  "mcpServers": {
    "algorand-mcp": {
      "command": "algorand-mcp"
    }
  }
}

Usando ruta absoluta:

{
  "mcpServers": {
    "algorand-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/algorand-mcp/dist/index.js"]
    }
  }
}

Claude Code

Crea .mcp.json en la raíz de tu proyecto (alcance del proyecto) o ~/.claude.json (alcance del usuario):

{
  "mcpServers": {
    "algorand-mcp": {
      "type": "stdio",
      "command": "npx",
      "args": ["@goplausible/algorand-mcp"]
    }
  }
}

O agrégalo de forma interactiva:

claude mcp add algorand-mcp -- npx @goplausible/algorand-mcp

Cursor

Agrégalo mediante Configuración > Servidores MCP, o edita .cursor/mcp.json en la raíz de tu proyecto:

{
  "mcpServers": {
    "algorand-mcp": {
      "command": "npx",
      "args": ["@goplausible/algorand-mcp"]
    }
  }
}

Windsurf

Agrégalo mediante Configuración > MCP, o edita ~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "algorand-mcp": {
      "command": "npx",
      "args": ["@goplausible/algorand-mcp"]
    }
  }
}

VS Code / GitHub Copilot

Edita .vscode/mcp.json en la raíz de tu espacio de trabajo, o abre Configuración > Servidores MCP:

{
  "servers": {
    "algorand-mcp": {
      "type": "stdio",
      "command": "npx",
      "args": ["@goplausible/algorand-mcp"]
    }
  }
}

Cline

Agrégalo mediante el panel Servidores MCP en la barra lateral de Cline, o edita ~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json (macOS):

{
  "mcpServers": {
    "algorand-mcp": {
      "command": "npx",
      "args": ["@goplausible/algorand-mcp"],
      "disabled": false
    }
  }
}

OpenAI Codex CLI

Crea .codex/mcp.json en la raíz de tu proyecto o ~/.codex/mcp.json para el alcance global:

{
  "mcpServers": {
    "algorand-mcp": {
      "command": "npx",
      "args": ["@goplausible/algorand-mcp"]
    }
  }
}

Open Code

Edita ~/.config/opencode/config.json:

{
  "mcp": {
    "algorand-mcp": {
      "type": "stdio",
      "command": "npx",
      "args": ["@goplausible/algorand-mcp"]
    }
  }
}

Cualquier cliente compatible con MCP

El servidor habla el protocolo estándar MCP stdio. Para cualquier cliente no listado arriba, configúralo con:

  • Comando: npx (o algorand-mcp si está instalado globalmente, o node /path/to/dist/index.js)
  • Argumentos: ["@goplausible/algorand-mcp"] (para npx)
  • Transporte: stdio

Selección de red

Cada herramienta acepta un parámetro opcional network: "mainnet" (predeterminado), "testnet" o "localnet". Las URL de Algod e Indexer están integradas para mainnet y testnet mediante AlgoNode.

Ejemplo de llamada de herramienta:

{ "name": "api_algod_get_account_info", "arguments": { "address": "ABC...", "network": "testnet" } }

Si no se proporciona network, las herramientas usan mainnet de forma predeterminada.

Paginación

Las respuestas de la API se paginan automáticamente. Cada herramienta acepta un parámetro opcional itemsPerPage (predeterminado: 10). Pasa el pageToken de una respuesta anterior para obtener la siguiente página.

Billetera del agente

Arquitectura

La billetera del agente es una base de datos SQLite local que el servidor MCP controla en nombre del agente. El servidor guarda los mnemónicos y firma transacciones por el agente; el agente nunca ve los mnemónicos en ninguna respuesta de herramienta.

CapaQué almacenaDónde
SQLite (wallet.db)Filas de cuentas (address, public_key, nickname, mnemonic, created_at) y el índice de cuenta activa~/.algorand-mcp/wallet.db (modo 0600)

Modelo de amenazas. El archivo wallet.db es el secreto. Cualquiera con acceso de lectura puede recuperar cada mnemónico almacenado en la billetera. Las mitigaciones son permisos del sistema de archivos (0600, solo propietario), mantener el directorio de datos fuera de volúmenes compartidos/legibles por todos y tratar el directorio de datos como cualquier otro almacén de secretos (haz instantáneas con cuidado, restringe las copias de seguridad, cifra el disco del host para protección en reposo). Para implementaciones con Docker, monta ~/.algorand-mcp como un volumen con nombre y restringe el acceso como lo harías con cualquier material secreto.

Cómo funciona

  Agent (LLM)                    MCP Server                          Storage
  ──────────                     ──────────                          ───────
       │                              │                                  │
       │  wallet_add_account          │                                  │
       │  { nickname: "main" }        │                                  │
       │ ──────────────────────────►  │  generate keypair                │
       │                              │  INSERT (address, public_key,    │
       │                              │           nickname, mnemonic) ──►│  wallet.db
       │  ◄─ { address, publicKey,    │                                  │
       │       nickname, index }      │                                  │
       │                              │                                  │
       │  wallet_sign_transaction     │                                  │
       │  { transaction: {...} }      │                                  │
       │ ──────────────────────────►  │  SELECT mnemonic FROM accounts ◄─│
       │                              │   WHERE address=<active>         │
       │                              │  sign in memory                  │
       │  ◄─ { txID, blob }           │  (key discarded after sign)      │
       │                              │                                  │
  1. Creación de cuentas (wallet_add_account): genera un par de claves e inserta una fila que contiene el mnemónico en accounts. Devuelve dirección, clave pública, apodo e índice. El mnemónico nunca se devuelve.
  2. Cuenta activa: una cuenta está activa a la vez. wallet_switch_account la cambia por apodo o índice. Todas las herramientas de firma y consulta operan sobre la cuenta activa.
  3. Firma de transacciones (wallet_sign_transaction): lee el mnemónico de la base de datos, firma en memoria y devuelve solo el blob firmado.
  4. Firma de datos (wallet_sign_data): firma datos hexadecimales arbitrarios usando Ed25519 puro mediante la biblioteca @noble/curves (sin prefijo del SDK de Algorand). Útil para autenticación fuera de la cadena.
  5. Opt-in de activos (wallet_optin_asset): crea, firma y envía una transacción de opt-in para la cuenta activa en un solo paso.

Compatibilidad hacia atrás (migración silenciosa desde el llavero del sistema operativo)

Las instalaciones anteriores de este MCP almacenaban mnemónicos en el llavero del sistema operativo (@napi-rs/keyring). En el primer inicio después de actualizar, el servidor ejecuta una migración silenciosa de una sola vez:

  • Para cada fila de accounts cuya columna mnemonic sea NULL o esté vacía, intenta leer el mnemónico del llavero del sistema operativo bajo el nombre de servicio algorand-mcp con clave por dirección.
  • Si lo encuentra, el mnemónico se copia en la columna de la base de datos.
  • La entrada original del llavero se deja en su lugar como copia de seguridad redundante; no se elimina nada.

Después de que esto se completa, la base de datos es la única fuente de verdad. El llavero se consulta solo como respaldo si la base de datos aún tiene un mnemónico NULL para una dirección (por ejemplo, si el llavero no estaba disponible durante el inicio y estuvo disponible más tarde). Todas las cuentas nuevas creadas después de la actualización se escriben directamente en la base de datos y nunca tocan el llavero.

Manejo de huérfanos (archivar, no eliminar). Si existe una fila de accounts pero su mnemónico no está en el llavero ni ya está en la base de datos (por ejemplo, el usuario copió wallet.db a una máquina nueva sin mover también las entradas del llavero, restauró desde una copia de seguridad parcial o instaló en Docker donde el llavero nunca existió), esa fila no se puede usar para firmar. En lugar de eliminarla, el servidor marca la fila como archivada (UPDATE accounts SET archived = 1 WHERE mnemonic IS NULL OR mnemonic = ''). Las filas archivadas:

  • están ocultas de la respuesta predeterminada de wallet_list_accounts
  • nunca se convierten en la cuenta activa (el índice de cuenta activa se limita al final de la lista activa restante, o se restablece a 0 si no quedan cuentas activas)
  • conservan su apodo original (un índice único parcial idx_active_nickname aplica la unicidad del apodo solo entre filas activas, por lo que un nuevo wallet_add_account puede reutilizar el mismo apodo para un par de claves nuevo)
  • se exponen mediante wallet_list_accounts { archived: true } para análisis forense o recuperación futura

El archivado es silencioso en la capa de herramientas MCP. El único diagnóstico es un registro de una línea en stderr por cada lectura fallida del llavero ([algorand-mcp] keychain read failed for <addr>…: <msg>), de modo que si un usuario investiga un archivado falso, pueda ver si el llavero arrojó "sin entrada" frente a "acceso denegado" frente a "sin DBus", etc.

No se requiere ninguna acción del usuario para nada de esto. Sin avisos, sin variables de entorno, sin herramientas de migración.

Versiones del esquema

El esquema de la base de datos evoluciona de forma aditiva mediante una migración idempotente que se ejecuta al inicio:

VersiónCambio
v1inicial: columnas de accounts: id, address, public_key, nickname (ÚNICO), created_at
v2se agregó la columna mnemonic TEXT
v3se agregó la columna archived INTEGER NOT NULL DEFAULT 0; se eliminó el ÚNICO a nivel de columna en nickname y se reemplazó por CREATE UNIQUE INDEX idx_active_nickname ON accounts(nickname) WHERE archived = 0 para que las filas archivadas puedan conservar sus apodos originales sin bloquear la reutilización

El paso de v2→v3 recrea la tabla accounts (SQLite no puede eliminar una restricción ÚNICA a nivel de columna mediante ALTER) y copia los datos hacia adelante con archived = 0. Las billeteras existentes siguen funcionando sin cambios.

Pagos HTTP x402

x402 es un protocolo de micropagos nativo de HTTP. Utiliza el estado 402 Payment Required reservado desde hace mucho tiempo como un protocolo de enlace real: cuando un cliente solicita un recurso pagado sin pagar, el servidor devuelve 402 con un cuerpo JSON que enumera lo que acepta (redes, activos, montos, la dirección del destinatario). El cliente construye un pago, lo adjunta como encabezado HTTP, reintenta la misma solicitud y el servidor devuelve 200 con el recurso. Sin claves de API, sin webhooks de Stripe, sin cuentas que gestionar: el pago es parte de la propia solicitud.

Este MCP implementa la variante de Algorand de x402, donde los pagos son transferencias de USDC (o ALGO nativo) en Algorand. Expone dos herramientas que reducen el flujo manual de siete pasos (sonda → analizar → verificación de opt-in → construir pagador de tarifas → construir pago → agrupar → firmar → codificar → encabezado → reintentar) a una sola llamada de herramienta.

Forma del protocolo (variante de Algorand)

  Agent (LLM)                  algorand-mcp                   Endpoint                Facilitator
  ──────────                   ────────────                   ────────                ───────────
       │                            │                            │                          │
       │  make_http_request_       │                            │                          │
       │  with_x402 { url, ... }   │                            │                          │
       │ ────────────────────────► │  HTTP request              │                          │
       │                            │ ─────────────────────────► │                          │
       │                            │  402 PaymentRequired      │                          │
       │                            │ ◄───────────────────────── │                          │
       │                            │  pick accepts[i] for      │                          │
       │                            │  Algorand network          │                          │
       │                            │  build fee-payer + payment │                          │
       │                            │  (atomic group of 2)       │                          │
       │                            │  sign payment leg          │                          │
       │                            │  via agent wallet DB       │                          │
       │                            │  encode unsigned fee-payer │                          │
       │                            │  base64 PAYMENT-SIGNATURE  │                          │
       │                            │ ─────────────────────────► │                          │
       │                            │  HTTP request +            │  forward + settle        │
       │                            │  PAYMENT-SIGNATURE         │ ───────────────────────► │
       │                            │                            │  sign fee-payer,         │
       │                            │                            │  submit atomic group     │
       │                            │  200 + resource            │ ◄─────────────────────── │
       │                            │ ◄───────────────────────── │                          │
       │ ◄─ { result, paid: {...}}│                            │                          │
       │                            │                            │                          │

Qué es diferente de la versión de Coinbase/EVM

  1. El nombre del encabezado es PAYMENT-SIGNATURE, no X-PAYMENT. El cuerpo del encabezado es JSON codificado en base64 con x402Version, scheme, network (un identificador CAIP-2 como algorand:wGHE2Pw… para mainnet), un payload y una copia textual de la entrada accepts[] que el cliente eligió.
  2. El pago es un grupo atómico de 2 transacciones. El índice 0 es una transacción de pagador de tarifas (remitente = facilitador, monto = 0, tarifa = 2000 µAlgo para todo el grupo); el índice 1 es la transferencia real del ASA USDC (remitente = billetera, tarifa = 0). La billetera firma solo el índice 1: el facilitador firma el índice 0 en el servidor al momento de la liquidación. La billetera del usuario paga solo el USDC, ni siquiera las tarifas de red.
  3. Las cadenas de red son CAIP-2 de Algorand. Este MCP reconoce mainnet (wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=) y testnet (SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=). Los endpoints que solo aceptan Base, Solana u otras redes que no son de Algorand no se pueden satisfacer aquí y la herramienta devuelve un error claro.

Compatibilidad con Coinbase Wallet MCP (superficie x402)

Las herramientas x402 — make_http_request_with_x402 y x402_discover_payment_requirements — son intencionalmente compatibles en nombre y forma con las herramientas x402 del MCP de Coinbase Wallet. Los parámetros de entrada (baseURL, path, method, queryParams, body, headers, correlationId, maxAmountPerRequest, paymentRequirements, preferredNetwork, extensions) son los mismos. El formato de salida (result, _atomicUnitsNote) es el mismo.

Qué significa esto en la práctica:

  • Sustitución directa para x402 de Algorand. Los agentes y aplicaciones MCP escritos para las herramientas x402 del MCP de Coinbase Wallet funcionan con este servidor sin necesidad de cambiar ningún prompt — simplemente acceden a los endpoints x402 de Algorand en lugar de los de Base/Solana.
  • Mismos reflejos de agente. Los modelos entrenados con trazas de llamadas a herramientas del ecosistema Coinbase usan estas herramientas correctamente desde la primera llamada. No hay nada que reaprender.
  • La compatibilidad se limita solo a la superficie x402. Las herramientas de wallet, cuenta, construcción de transacciones y DEX en este MCP son específicas de Algorand y no reflejan la API de wallet de Coinbase. Solo make_http_request_with_x402 y x402_discover_payment_requirements son compatibles como sustitución directa.

El único parámetro que necesariamente difiere: preferredNetwork acepta solo mainnet | testnet | localnet (redes de Algorand), porque la wallet solo firma transacciones de Algorand. El enum de Coinbase lista base | base-sepolia | solana | solana-devnet. Los agentes que pasen uno de esos valores reciben un error claro indicando que no existe una entrada aceptada pagable por Algorand.

Ejemplo — API meteorológica de pago

# Step 1 (optional): peek at the cost
x402_discover_payment_requirements {
  "baseURL": "https://example.x402.goplausible.xyz",
  "path": "/weather",
  "method": "GET"
}
# returns: { result: { accepts: [{ scheme: "exact", network: "algorand:SGO1...",
#                                  maxAmountRequired: "100", asset: "10458941",
#                                  payTo: "AAAA...", extra: { feePayer: "BBBB..." } }] } }

# Step 2: pay and fetch in one call
make_http_request_with_x402 {
  "baseURL": "https://example.x402.goplausible.xyz",
  "path": "/weather",
  "method": "GET",
  "maxAmountPerRequest": 10000,
  "preferredNetwork": "testnet"
}
# returns: { result: <weather payload>, paid: { network: "testnet",
#                                                asset: "10458941",
#                                                amount: "100", payTo: "AAAA..." },
#           paymentResponse: <decoded X-PAYMENT-RESPONSE> }

La cuenta de wallet activa debe estar suscrita al ASA objetivo (p. ej., USDC) y tener saldo suficiente para cubrir maxAmountRequired. Si no está suscrita, el pago falla en la liquidación — suscríbete primero con wallet_optin_asset.

Requisitos previos

  • Existe una cuenta de wallet activa (wallet_get_info para verificar)
  • Esa cuenta está suscrita al activo de pago (USDC mainnet ASA 31566704, testnet ASA 10458941)
  • La cuenta tiene suficiente del activo de pago para maxAmountRequired
  • El accepts[] del endpoint incluye al menos una entrada con una red de Algorand que el MCP reconozca

Variables de entorno opcionales

Las variables de entorno solo son necesarias para configuraciones especiales. Pásalas mediante el bloque env en tu configuración MCP.

VariableDescripciónValor predeterminadoCuándo se necesita
ALGORAND_TOKENToken API para nodos privados/autenticados""Al conectarse a un nodo Algod/Indexer privado
ALGORAND_LOCALNET_URLURL base de localnet""Al usar network: "localnet" (p. ej., http://localhost:4001)
ALPHA_API_KEYClave API de Alpha Arcade""Al acceder a datos de mercado de recompensas

Ejemplo: localnet (AlgoKit)

{
  "mcpServers": {
    "algorand-mcp": {
      "command": "node",
      "args": ["/path/to/algorand-mcp/dist/index.js"],
      "env": {
        "ALGORAND_LOCALNET_URL": "http://localhost:4001",
        "ALGORAND_TOKEN": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      }
    }
  }
}

Luego usa "network": "localnet" en tus llamadas a herramientas.

Herramientas disponibles

Herramientas de wallet (10 herramientas)

Consulta Secure Wallet para ver los detalles completos de la arquitectura.

HerramientaDescripción
wallet_add_accountCrear una nueva cuenta de Algorand con apodo (devuelve solo dirección + clave pública)
wallet_remove_accountEliminar una cuenta de la wallet por apodo o índice
wallet_list_accountsListar cuentas activas con apodos y direcciones. Pasa { archived: true } para listar cuentas archivadas en su lugar (filas cuyo mnemónico no se pudo recuperar del llavero del sistema operativo al inicio — conservadas en la base de datos para análisis forense, no se pueden firmar).
wallet_switch_accountCambiar la cuenta activa por apodo o índice
wallet_get_infoObtener información de la cuenta activa que posee este servidor MCP (respaldada por base de datos): dirección, clave pública, saldo, recuentos de suscripciones. Para cuentas arbitrarias en cadena usa api_algod_get_account_info.
wallet_get_assetsObtener todas las tenencias de ASA de la cuenta activa que posee este servidor MCP. Para cuentas arbitrarias en cadena usa api_algod_get_account_info o api_algod_get_account_asset_info.
wallet_sign_transactionFirmar una sola transacción con la cuenta activa
wallet_sign_transaction_groupFirmar un grupo de transacciones con la cuenta activa (asigna automáticamente el ID de grupo)
wallet_sign_dataFirmar datos hexadecimales arbitrarios con Ed25519 puro (noble, sin prefijo de SDK)
wallet_optin_assetSuscribir la cuenta activa a un activo (crea, firma y envía)

Herramientas de pago HTTP x402 (5 herramientas)

Consulta x402 HTTP Payments para la explicación completa del protocolo.

HerramientaDescripción
x402_discover_payment_requirementsSondear un endpoint protegido por x402 y devolver su array accepts[] (costo, activo, red, payTo) sin pagar. Solo lectura.
make_http_request_with_x402Llamar a un endpoint protegido por x402 con pago automático de USDC/ALGO desde la wallet activa. Descubre internamente si paymentRequirements no se proporciona, construye el grupo atómico de pagador de tarifas + pago, firma y reintenta con el encabezado PAYMENT-SIGNATURE.
bazaar_listExplorar el catálogo de API pagadas registradas en el directorio de descubrimiento Bazaar alojado por el facilitador configurado (facilitator.goplausible.xyz por defecto). Resumen compacto por defecto; full: true devuelve registros verbatim. Filtros: network, method, merchantId, limit, offset.
bazaar_searchBúsqueda por palabras clave sobre recursos de Bazaar (URL + descripción). En el servidor: query, network. Post-filtros en el cliente: scheme, maxUsdPrice, asset, payTo, extensions, includeTestnets.
bazaar_get_resource_detailsObtener un solo recurso de Bazaar por su URL exacta de resource. Devuelve el registro verbatim (accepts[], discoveryInfo, contadores de popularidad).

Gestión de cuentas (8 herramientas)

HerramientaDescripción
create_accountCrear una nueva cuenta de Algorand (devuelve dirección + mnemónico en claro)
rekey_accountReclavear una cuenta a una nueva dirección
mnemonic_to_mdkConvertir mnemónico a clave de derivación maestra
mdk_to_mnemonicConvertir clave de derivación maestra a mnemónico
secret_key_to_mnemonicConvertir clave secreta a mnemónico
mnemonic_to_secret_keyConvertir mnemónico a clave secreta
seed_from_mnemonicGenerar semilla a partir de mnemónico
mnemonic_from_seedGenerar mnemónico a partir de semilla

Herramientas de utilidad (13 herramientas)

HerramientaDescripción
pingVerificación de conectividad e información del servidor
validate_addressVerificar si una dirección de Algorand es válida
encode_addressCodificar una clave pública a una dirección de Algorand
decode_addressDecodificar una dirección de Algorand a una clave pública
get_application_addressObtener dirección para un ID de aplicación dado
bytes_to_bigintConvertir bytes a BigInt
bigint_to_bytesConvertir BigInt a bytes
encode_uint64Codificar uint64 a bytes
decode_uint64Decodificar bytes a uint64
verify_bytesVerificar firma contra bytes
sign_bytesFirmar bytes con una clave secreta
encode_objCodificar objeto a msgpack
decode_objDecodificar msgpack a objeto

Herramientas de transacciones (18 herramientas)

HerramientaDescripción
make_payment_txnCrear una transacción de pago
make_keyreg_txnCrear una transacción de registro de clave
make_asset_create_txnCrear una transacción de creación de activo
make_asset_config_txnCrear una transacción de configuración de activo
make_asset_destroy_txnCrear una transacción de destrucción de activo
make_asset_freeze_txnCrear una transacción de congelación de activo
make_asset_transfer_txnCrear una transacción de transferencia de activo
make_app_create_txnCrear una transacción de creación de aplicación
make_app_update_txnCrear una transacción de actualización de aplicación
make_app_delete_txnCrear una transacción de eliminación de aplicación
make_app_optin_txnCrear una transacción de suscripción a aplicación
make_app_closeout_txnCrear una transacción de cierre de aplicación
make_app_clear_txnCrear una transacción de borrado de estado de aplicación
make_app_call_txnCrear una transacción de llamada a aplicación
assign_group_idAsignar ID de grupo para transacciones atómicas
sign_transactionFirmar una transacción con una clave secreta
encode_unsigned_transactionCodificar una transacción sin firmar a bytes msgpack en base64
decode_signed_transactionDecodificar un blob de transacción firmada de vuelta a JSON con detalles de firma

Herramientas de Algod (5 herramientas)

HerramientaDescripción
compile_tealCompilar código fuente TEAL
disassemble_tealDesensamblar bytecode TEAL a código fuente
send_raw_transactionEnviar transacciones firmadas a la red
simulate_raw_transactionsSimular transacciones ya codificadas (bytes base64). Solo aprobado/fallido + registro/costo — sin traza, sin presupuesto adicional.
simulate_transactionsSimular grupos de transacciones decodificadas con configuración completa de SimulateRequest (traza, presupuesto extra de opcodes, manejo de recursos sin nombre, transacciones sin firmar).

Herramientas de API Algod (13 herramientas)

Lecturas en vivo del estado actual contra un nodo Algod. Opción predeterminada para consultas de cuenta/aplicación/activo — los endpoints indexer equivalentes se deshabilitaron intencionalmente para mantener la superficie de herramientas ágil (consulta .notes/redundant-tools-report.md). Usa la familia indexer a continuación solo cuando necesites consultas históricas o filtradas que algod no puede responder.

HerramientaDescripción
api_algod_get_account_infoObtener saldo de cuenta, activos y dirección de autorización
api_algod_get_account_application_infoObtener información de aplicación específica de cuenta
api_algod_get_account_asset_infoObtener información de activo específica de cuenta
api_algod_get_application_by_idObtener información de aplicación
api_algod_get_application_boxObtener caja de aplicación por nombre
api_algod_get_application_boxesObtener todas las cajas de aplicación
api_algod_get_asset_by_idObtener información de activo
api_algod_get_pending_transactionObtener información de transacción pendiente
api_algod_get_pending_transactions_by_addressObtener transacciones pendientes para una dirección
api_algod_get_pending_transactionsObtener todas las transacciones pendientes
api_algod_get_transaction_paramsObtener parámetros de transacción sugeridos
api_algod_get_node_statusObtener estado actual del nodo
api_algod_get_node_status_after_blockObtener estado del nodo después de una ronda específica

Herramientas de API Indexer (10 herramientas)

Consultas históricas/filtradas contra una instancia de Indexer de Algorand. Úsalas para escaneos de rangos de tiempo, búsquedas paginadas, recuperación de registros y descubrimiento de creadores/tenedores — cualquier cosa que los endpoints de estado actual de algod no puedan responder.

Siete endpoints de indexer que duplicaban equivalentes de algod (cuenta por ID, activos de cuenta, estados locales de aplicación de cuenta, aplicación por ID, caja de aplicación, cajas de aplicación, activo por ID) se deshabilitaron intencionalmente. Están comentados en src/tools/apiManager/indexer/ y se pueden re-habilitar en un solo lugar si es necesario.

HerramientaDescripción
api_indexer_lookup_account_created_applicationsObtener aplicaciones creadas por cuenta
api_indexer_search_for_accountsBuscar cuentas con filtros (tenencias de activos/aplicaciones, rangos de saldo)
api_indexer_lookup_application_logsObtener mensajes de registro de aplicación en un rango de rondas
api_indexer_search_for_applicationsBuscar aplicaciones por creador
api_indexer_lookup_asset_balancesObtener todas las cuentas que tienen un activo con sus saldos
api_indexer_lookup_asset_transactionsObtener transacciones que involucran un activo (filtros de tiempo/ronda/rol de dirección)
api_indexer_search_for_assetsBuscar activos por creador, nombre o unidad
api_indexer_lookup_transaction_by_idObtener una transacción confirmada por ID
api_indexer_lookup_account_transactionsObtener historial de transacciones de una cuenta (filtros de tiempo/ronda/tipo/activo)
api_indexer_search_for_transactionsBuscar transacciones en toda la cadena con filtros

Herramientas de NFDomains (6 herramientas)

HerramientaDescripción
api_nfd_get_nfdObtener NFD por nombre o ID de aplicación
api_nfd_get_nfds_for_addressesObtener NFDs para direcciones específicas
api_nfd_get_nfd_activityObtener actividad/cambios para NFDs
api_nfd_get_nfd_analyticsObtener datos analíticos de NFD
api_nfd_browse_nfdsExplorar NFDs con filtros
api_nfd_search_nfdsBuscar NFDs

Herramientas de Tinyman AMM (9 herramientas)

HerramientaDescripción
api_tinyman_get_poolObtener información del pool por par de activos
api_tinyman_get_pool_analyticsObtener análisis del pool
api_tinyman_get_pool_creation_quoteObtener cotización para crear un pool
api_tinyman_get_liquidity_quoteObtener cotización para añadir liquidez
api_tinyman_get_remove_liquidity_quoteObtener cotización para retirar liquidez
api_tinyman_get_swap_quoteObtener cotización para intercambiar activos
api_tinyman_get_asset_optin_quoteObtener cotización para opt-in de activos
api_tinyman_get_validator_optin_quoteObtener cotización para opt-in de validador
api_tinyman_get_validator_optout_quoteObtener cotización para opt-out de validador

Herramientas de Haystack Router (3 herramientas)

HerramientaDescripción
api_haystack_get_swap_quoteObtener cotización de intercambio optimizada con enrutamiento entre Tinyman V2, Pact, Folks y protocolos LST
api_haystack_execute_swapIntercambio todo-en-uno: cotización → firma (vía wallet) → envío → confirmación
api_haystack_needs_optinVerificar si la dirección necesita opt-in de activos antes de intercambiar

Herramientas de Pera Wallet (3 herramientas)

HerramientaDescripción
api_pera_asset_verification_statusObtener estado de verificación de un activo en mainnet (verificado, de confianza, sospechoso, desconocido)
api_pera_verified_asset_detailsObtener información detallada del activo desde Pera (nombre, unidad, logo, decimales, verificación)
api_pera_verified_asset_searchBuscar activos verificados de Pera por nombre, nombre de unidad o palabra clave

Las herramientas de Pera Wallet son solo mainnet — la API pública de Pera no soporta testnet ni localnet.

Herramientas de Alpha Arcade (14 herramientas)

Opera mercados de predicción on-chain (resultados SÍ/NO) denominados en USDC. Todos los precios y cantidades usan microunidades (1.000.000 = $1,00 o 1 acción). Las herramientas de solo lectura funcionan sin wallet; las herramientas de trading requieren una cuenta de wallet activa.

HerramientaDescripción
alpha_get_live_marketsObtener todos los mercados de predicción activos con precios, volumen y categorías
alpha_get_reward_marketsObtener mercados con recompensas de liquidez (requiere la variable de entorno ALPHA_API_KEY)
alpha_get_marketObtener detalles completos de un único mercado por ID de aplicación
alpha_get_orderbookLibro de órdenes unificado desde la perspectiva SÍ con cálculo de spread
alpha_get_open_ordersÓrdenes abiertas de una wallet en un mercado específico
alpha_get_positionsPosiciones de tokens SÍ/NO en todos los mercados
alpha_create_limit_orderColocar una orden límite a un precio específico (bloquea ~0,957 ALGO de garantía)
alpha_create_market_orderColocar una orden de mercado con emparejamiento automático y tolerancia de deslizamiento
alpha_cancel_orderCancelar una orden abierta (reembolsa USDC/tokens y garantía ALGO)
alpha_amend_orderEditar una orden existente sin ejecutar en el lugar (precio, cantidad, deslizamiento)
alpha_propose_matchProponer un emparejamiento entre una orden maker existente y tu wallet
alpha_split_sharesDividir USDC en tokens de resultado SÍ + NO iguales
alpha_merge_sharesFusionar tokens SÍ + NO iguales de vuelta a USDC
alpha_claimReclamar USDC de un mercado resuelto canjeando tokens ganadores

Variable de entorno opcional: ALPHA_API_KEY — necesaria para datos de mercados con recompensas. ALPHA_API_BASE_URL — endpoint de API personalizado (por defecto: https://platform.alphaarcade.com/api).

Herramientas de URI ARC-26 (1 herramienta)

HerramientaDescripción
generate_algorand_qrcodeGenerar URI de Algorand y código QR según la especificación ARC-26

Herramientas de Conocimiento (1 herramienta)

HerramientaDescripción
get_knowledge_docObtener contenido en markdown para documentos de conocimiento de Algorand

Recursos

El servidor expone recursos MCP para acceso directo a datos. Los recursos de wallet se describen en la sección Secure Wallet anterior.

Recursos de Conocimiento

URIDescripción
algorand://knowledge/taxonomyTaxonomía completa de conocimiento de Algorand
algorand://knowledge/taxonomy/arcsSolicitudes de Comentarios de Algorand
algorand://knowledge/taxonomy/sdksDocumentación del SDK
algorand://knowledge/taxonomy/algokitDocumentación de AlgoKit
algorand://knowledge/taxonomy/algokit-utilsDocumentación de AlgoKit Utils
algorand://knowledge/taxonomy/tealscriptDocumentación de TEALScript
algorand://knowledge/taxonomy/puyaDocumentación de Puya
algorand://knowledge/taxonomy/liquid-authDocumentación de Liquid Auth
algorand://knowledge/taxonomy/pythonDocumentación del SDK de Python
algorand://knowledge/taxonomy/developersDocumentación para desarrolladores
algorand://knowledge/taxonomy/clisDocumentación de herramientas CLI
algorand://knowledge/taxonomy/nodesDocumentación de gestión de nodos
algorand://knowledge/taxonomy/detailsDocumentación de detalles técnicos

Estructura del Proyecto

algorand-mcp/
├── src/                         # TypeScript source
│   ├── index.ts                 # Server entry point
│   ├── networkConfig.ts         # Hardcoded network URLs and client factories
│   ├── algorand-client.ts       # Re-exports from networkConfig
│   ├── env.ts                   # Legacy env shim (unused)
│   ├── types.ts                 # Shared types (Zod schemas)
│   ├── resources/               # MCP Resources
│   │   ├── knowledge/           # Documentation taxonomy
│   │   └── wallet/              # Wallet resources
│   ├── tools/                   # MCP Tools
│   │   ├── commonParams.ts      # Network + pagination schema fragments
│   │   ├── walletManager.ts     # Agent wallet (SQLite-backed)
│   │   ├── accountManager.ts    # Account operations
│   │   ├── utilityManager.ts    # Utility functions
│   │   ├── algodManager.ts      # TEAL compile, simulate, submit
│   │   ├── arc26Manager.ts      # ARC-26 URI generation
│   │   ├── knowledgeManager.ts  # Knowledge document access
│   │   ├── transactionManager/  # Transaction building
│   │   │   ├── accountTransactions.ts
│   │   │   ├── assetTransactions.ts
│   │   │   ├── appTransactions/
│   │   │   └── generalTransaction.ts
│   │   └── apiManager/          # API integrations
│   │       ├── algod/           # Algod API
│   │       ├── indexer/         # Indexer API
│   │       ├── nfd/             # NFDomains
│   │       ├── tinyman/         # Tinyman AMM
│   │       ├── hayrouter/       # Haystack Router DEX aggregator
│   │       ├── pera/            # Pera Wallet verified assets
│   │       └── alpha/           # Alpha Arcade prediction markets
│   └── utils/
│       └── responseProcessor.ts # Pagination and formatting
├── tests/                       # Test suite
│   ├── helpers/                 # Shared test utilities
│   │   ├── mockFactories.ts     # Mock algod/indexer/keychain factories
│   │   ├── testConfig.ts        # Category enable/disable logic
│   │   ├── e2eSetup.ts          # E2E account provisioning + invokeTool()
│   │   └── testConstants.ts     # Well-known testnet addresses and asset IDs
│   ├── unit/                    # 11 unit test suites (mocked, fast)
│   ├── e2e/                     # 11 E2E test suites (live testnet)
│   │   ├── globalSetup.ts       # Account provisioning + fund-check
│   │   └── globalTeardown.ts    # Cleanup
│   └── jest.config.e2e.js       # E2E-specific Jest config
├── dist/                        # Compiled output
├── jest.config.js               # Unit test Jest config
├── tsconfig.json                # Production TypeScript config
├── tsconfig.test.json           # Test TypeScript config
└── package.json

Formato de Respuesta

Todas las respuestas de herramientas siguen el formato de contenido MCP. Las respuestas de API incluyen paginación automática cuando los conjuntos de datos superan itemsPerPage (por defecto 10):

{
  "data": { ... },
  "metadata": {
    "totalItems": 100,
    "itemsPerPage": 10,
    "currentPage": 1,
    "totalPages": 10,
    "hasNextPage": true,
    "pageToken": "eyJ..."
  }
}

Pasa pageToken de una respuesta anterior para obtener la siguiente página. Establece itemsPerPage en cualquier llamada de herramienta para controlar el tamaño de página.

Desarrollo

# Install dependencies
npm install

# Type-check
npm run typecheck

# Build
npm run build

# Clean build output
npm run clean

Pruebas

El proyecto tiene un conjunto de pruebas integral de doble capa: pruebas unitarias rápidas (simuladas, sin red) y pruebas E2E reales (testnet en vivo). Ambas usan Jest 29 con ts-jest y soporte ESM.

Inicio rápido

npm test                    # Unit tests (fast, no network)
npm run test:e2e            # E2E tests (testnet, generates account + fund link)
npm run test:all            # Both

Pruebas unitarias

Las pruebas unitarias cubren las 11 categorías de herramientas con dependencias de red completamente simuladas. Se ejecutan en paralelo y terminan en ~5 segundos. No se necesitan variables de entorno ni cuentas financiadas.

npm test

Cobertura: 11 suites, más de 75 pruebas que cubren rutas de éxito, manejo de errores y casos límite para cada categoría de herramienta.

SuiteQué prueba
accountManagerCreación de cuentas, ida y vuelta de mnemónicos, validación de parámetros de rekey
utilityManagerPing, validación de direcciones, codificar/decodificar, firmar/verificar bytes, codificar/decodificar objetos
walletManagerCiclo de vida completo: añadir → listar → cambiar → obtener información → firmar datos → eliminar (keychain simulado + SQLite)
transactionManagerConstrucción de transacciones de pago, activos y aplicaciones; sign_transaction; assign_group_id
algodManagerCompilar/desensamblar TEAL, enviar raw, simular
apiAlgodLas 13 herramientas de API algod con enrutamiento de simulación correcto
apiIndexerLas 10 herramientas activas de API indexer con simulaciones de builder fluidas
apiNfdObtener/buscar/navegar NFD con fetch simulado
apiTinymanPool/intercambio Tinyman con manejo de errores
arc26ManagerGeneración de URI ARC-26 y salida SVG de código QR
knowledgeManagerRecuperación de documentos de conocimiento y manejo de errores de documento faltante

Cómo funciona la simulación

Las pruebas unitarias usan jest.unstable_mockModule() (compatible con ESM) para interceptar importaciones antes de que se carguen. El compartido tests/helpers/mockFactories.ts proporciona:

  • setupNetworkMocks() — Reemplaza algorand-client.ts con clientes algod/indexer simulados que devuelven respuestas deterministas sin ninguna llamada de red.
  • createKeychainMock() — Reemplaza @napi-rs/keyring con un Map en memoria, para que las pruebas de wallet funcionen sin un keychain del sistema operativo.
  • Simulaciones de Proxy Fluido — El SDK de Indexer de Algorand usa un patrón de builder (.searchForAssets().limit(5).do()). La fábrica de simulaciones usa objetos ES Proxy que se devuelven a sí mismos para cualquier método encadenado y se resuelven cuando se llama a .do().

Pruebas E2E

Las pruebas E2E llaman a los manejadores de herramientas directamente contra Algorand testnet (a través de nodos públicos de AlgoNode). Se ejecutan en serie para evitar limitación de velocidad.

npm run test:e2e

En la primera ejecución (sin mnemónico proporcionado), la configuración de prueba:

  1. Genera una nueva cuenta de Algorand
  2. Imprime la dirección y el mnemónico
  3. Imprime un enlace de financiación: https://lora.algokit.io/testnet/fund
  4. Ejecuta todas las pruebas (las pruebas sin financiación aún pasan)

Para ejecutar con una cuenta financiada:

E2E_MNEMONIC="word1 word2 ... word25" npm run test:e2e

Cobertura: 11 suites, más de 35 pruebas que cubren interacciones reales de red.

SuiteQué prueba
accountCreación de cuentas, cadena de ida y vuelta de mnemónico a clave
utilityPing, validación de direcciones, codificar/decodificar, firmar/verificar bytes, codificar/decodificar objetos
walletCiclo de vida completo de wallet: añadir → listar → cambiar → obtener información → obtener activos → firmar datos → eliminar
transactionConstruir pago → firmar → verificar txID; construir opt-in de activos; construir grupo con assign_group_id
algodIda y vuelta de compilar + desensamblar TEAL
algodApiInformación de cuenta, parámetros sugeridos, estado del nodo, información de activos vía algod
indexerApiConsulta de cuenta, búsqueda de activos/transacciones/cuentas vía indexer
nfdConsultar "algo.algo", buscar NFDs, navegar NFDs
tinymanObtener pool ALGO/USDC
arc26Generar URI ARC-26, verificar formato + SVG de QR
knowledgeRecuperar contenido de documento de conocimiento conocido

Activación de categorías

Las pruebas E2E se pueden habilitar selectivamente por categoría o herramienta individual mediante variables de entorno. Por defecto todas las categorías están habilitadas.

Habilitar categorías específicas

E2E_WALLET=1 npm run test:e2e            # Only wallet tests
E2E_ALGOD=1 E2E_UTILITY=1 npm run test:e2e  # Algod + utility tests

Banderas de categoría disponibles

Variable de entornoCategoría
E2E_ALL=1Todas las categorías (explícito)
E2E_WALLET=1Herramientas de wallet
E2E_ACCOUNT=1Herramientas de cuenta
E2E_UTILITY=1Herramientas de utilidad
E2E_TRANSACTION=1Herramientas de transacción
E2E_ALGOD=1Herramientas de algod
E2E_ALGOD_API=1Herramientas de API algod
E2E_INDEXER_API=1Herramientas de API indexer
E2E_NFD=1Herramientas de NFDomains
E2E_TINYMAN=1Herramientas de Tinyman
E2E_ARC26=1Herramientas de ARC-26
E2E_KNOWLEDGE=1Herramientas de conocimiento

Importante: Establecer cualquier bandera individual (p. ej. E2E_WALLET=1) deshabilita todas las demás categorías a menos que E2E_ALL=1 también esté establecida.

Habilitar herramientas específicas

E2E_TOOLS=ping,validate_address npm run test:e2e

La variable E2E_TOOLS acepta una lista separada por comas de nombres de herramientas. Solo se ejecutarán las pruebas de esas herramientas específicas.

Estructura de archivos de prueba

tests/
├── helpers/
│   ├── mockFactories.ts       # Mock algod/indexer/keychain factories
│   ├── testConfig.ts          # Category enable/disable logic
│   ├── e2eSetup.ts            # E2E account provisioning + invokeTool()
│   └── testConstants.ts       # Well-known testnet addresses and asset IDs
├── unit/                      # 11 unit test files (*.test.ts)
│   ├── accountManager.test.ts
│   ├── utilityManager.test.ts
│   ├── walletManager.test.ts
│   ├── transactionManager.test.ts
│   ├── algodManager.test.ts
│   ├── apiAlgod.test.ts
│   ├── apiIndexer.test.ts
│   ├── apiNfd.test.ts
│   ├── apiTinyman.test.ts
│   ├── arc26Manager.test.ts
│   └── knowledgeManager.test.ts
├── e2e/                       # 11 E2E test files (*.e2e.test.ts)
│   ├── globalSetup.ts         # Account provisioning + fund-check
│   ├── globalTeardown.ts      # Cleanup
│   ├── account.e2e.test.ts
│   ├── utility.e2e.test.ts
│   ├── wallet.e2e.test.ts
│   ├── transaction.e2e.test.ts
│   ├── algod.e2e.test.ts
│   ├── algodApi.e2e.test.ts
│   ├── indexerApi.e2e.test.ts
│   ├── nfd.e2e.test.ts
│   ├── tinyman.e2e.test.ts
│   ├── arc26.e2e.test.ts
│   └── knowledge.e2e.test.ts
└── jest.config.e2e.js         # E2E-specific Jest config

Configuración de Jest

ConfigPropósitoAjustes clave
jest.config.js (raíz)Pruebas unitariastestTimeout: 10s, workers en paralelo, testMatch: tests/unit/**
tests/jest.config.e2e.jsPruebas E2EtestTimeout: 60s, maxWorkers: 1 (en serie), globalSetup/globalTeardown
tsconfig.test.jsonTypeScript para pruebasrootDir: ".", incluye tanto src/ como tests/

Escribir nuevas pruebas

Plantilla de prueba unitaria:

import { jest } from '@jest/globals';
import { setupNetworkMocks } from '../helpers/mockFactories.js';

jest.unstable_mockModule('../../src/algorand-client.js', () => setupNetworkMocks());

const { YourManager } = await import('../../src/tools/yourManager.js');

describe('YourManager', () => {
  it('does something', async () => {
    const result = await YourManager.handleTool('tool_name', { arg: 'value' });
    const data = JSON.parse(result.content[0].text);
    expect(data.field).toBeDefined();
  });
});

Plantilla de prueba E2E:

import { describeIf, testConfig } from '../helpers/testConfig.js';
import { invokeTool, parseToolResponse } from '../helpers/e2eSetup.js';

describeIf(testConfig.isCategoryEnabled('your-category'))('Your Tools (E2E)', () => {
  it('does something on testnet', async () => {
    const data = parseToolResponse(
      await invokeTool('tool_name', { arg: 'value', network: 'testnet' }),
    );
    expect(data.field).toBeDefined();
  });
});

Dependencias

  • algosdk v3 — SDK de JavaScript de Algorand
  • @modelcontextprotocol/sdk — SDK de TypeScript de MCP
  • @napi-rs/keyring — Acceso nativo al keychain del sistema operativo (Keychain de macOS, libsecret de Linux, Credential Manager de Windows). Se usa como respaldo de lectura de compatibilidad hacia atrás para cuentas creadas por instalaciones anteriores a la migración de base de datos; los mnemónicos nuevos se escriben solo en wallet.db.
  • sql.js — SQLite embebido (WASM) para persistencia de metadatos de wallet
  • @noble/curves — Ed25519 en JS puro para firma de datos sin procesar (wallet_sign_data)
  • @tinymanorg/tinyman-js-sdk — SDK de AMM de Tinyman
  • @alpha-arcade/sdk — SDK de mercado de predicción de Alpha Arcade
  • zod — Validación de tipos en tiempo de ejecución
  • qrcode — Generación de códigos QR para ARC-26

Licencia

MIT