Agent Tools Runtime
Provides an MCP adapter for interacting with n8n workflows, with OAuth/token authentication handled host-side.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Agent Tools RuntimeWhat adapters are available to load?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Agent Tools Runtime
Runtime persistente basado en just-bash para que un agente descubra y cargue
progresivamente adaptadores MCP, REST y CLI local sin exponer todo el catálogo
de herramientas en cada conversación.
Estado
Este repositorio es la evolución independiente de la POC publicada en
TheHumanInTheLoop Marketplace.
La API todavía está en 0.x; cualquier cambio puede requerir migración.
Related MCP server: Terminal MCP Server
Capas
MCP facade → persistent runtime → adapter → providerAdaptadores incluidos:
MCP genérico con sesiones y tokens host-side.
n8n MCP con OAuth/token host-side.
REST/API con rutas relativas y confirmación para mutaciones.
CLI local con allowlist,
execFile, timeout y confirmación.
Transportes: stdio (default) y Streamable HTTP
runtime/mcp-server.mjs habla MCP por stdio -- el caso de uso original y el que sigue sin cambios
(npm run mcp, o bin/agent-tools-mcp.mjs si el paquete está instalado). Pero algunos clientes MCP
no pueden spawnear un subproceso stdio: por ejemplo eve (framework
de agentes de Vercel), cuyas connections/*.ts (defineMcpClientConnection) exigen una url que
hable Streamable HTTP o SSE, sin opción de command/args. Para esos casos existe
runtime/mcp-http-server.mjs (npm run mcp:http, o bin/agent-tools-mcp-http.mjs), el mismo
dispatcher (createMessageHandler() en mcp-server.mjs, extraído para no atarlo a ningún
transporte) expuesto sobre HTTP en vez de stdin/stdout.
export AGENT_TOOLS_HTTP_PORT=8321 # default si se omite
export AGENT_TOOLS_HTTP_HOST=127.0.0.1 # default -- solo localhost, ver nota de seguridad abajo
npm run mcp:httpImplementa la variante simple del spec (2025-03-26):
una respuesta JSON por request (Content-Type: application/json), sin upgrade a SSE -- el catálogo
de mensajes de este runtime (initialize/tools/list/tools/call) es enteramente request/response,
sin mensajes server-initiated, así que streaming no aporta nada todavía. Session ID vía
Mcp-Session-Id (emitido en initialize, validado en requests siguientes, 404 si no se reconoce),
protección DNS-rebinding (rechaza Origin no-local con 403), bind a 127.0.0.1 por default. Sin
batching de mensajes en esta versión -- ningún cliente probado lo necesitó.
Cliente probado en vivo: eve (Vercel)
eve es un framework de agentes "filesystem-first": herramientas,
conexiones y skills viven como archivos convencionales bajo agent/. Conectarlo a este runtime es
crear agent/connections/agent-tools-runtime.ts:
import { defineMcpClientConnection } from "eve/connections";
export default defineMcpClientConnection({
url: "http://127.0.0.1:8321/mcp",
description: "agent-tools-runtime: typed facades over Ollama, ccdd-gate, n8n, GitHub, PocketBase.",
});Gotcha real encontrado en vivo, no hipotético: con un modelo fuera del catálogo de AI Gateway
(en la prueba, Ollama local vía @ai-sdk/openai-compatible), eve falla al arrancar con "Cannot
compile agent compaction because the primary compaction trigger model ... does not have known AI
Gateway context window metadata" -- necesita saber la ventana de contexto del modelo para su feature
de compaction, y un modelo custom no la trae. Se resuelve declarándola explícita en agent.ts:
export default defineAgent({
model: ollama.chatModel("gpt-oss:20b-cloud"),
modelContextWindowTokens: 131072,
});Dos corridas reales, mismo modelo (gpt-oss:20b-cloud vía Ollama local), mismo transporte HTTP:
|
| |
Tools usadas |
|
|
Decisión del modelo | directa | encadenó dos skills razonando qué le faltaba para responder "configuración riesgosa" sin que nadie se lo indicara |
Resultado | tabla correcta de modelos disponibles | hallazgos reales (webhooks sin autenticación, workflows activos sin trigger) + recomendaciones, honesto sobre el corte de la muestra (356 workflows activos, mostró algunos y ofreció ampliar) |
En ambos casos el flujo fue el mismo y sin fricción: connection_search (mecanismo propio de eve
para descubrir tools de una conexión MCP por texto libre) encontró la conexión y las tools/skills
correctas, verificado en el trace crudo del stream de eventos, no solo en la respuesta final.
Cliente probado en vivo: Pi (pi.dev) -- vía extensión propia, no MCP
Pi es un coding agent liviano construido sobre el Claude Agent SDK. No tiene soporte
MCP nativo -- depende de un paquete de terceros, pi-mcp-adapter,
que expone un único tool mcp() proxy (para no gastar contexto con el catálogo completo de cada
server, mismo espíritu que la fachada discover/call de este runtime).
Intento 1, con pi-mcp-adapter, fallido -- documentado para no repetir el mismo camino:
instalé el adapter (pi install npm:pi-mcp-adapter) y probé tanto .pi/mcp.json como .mcp.json
(las dos ubicaciones que documenta el paquete) apuntando a este runtime. En ambos casos, verificado
en el trace crudo, el modelo nunca vio el tool mcp() -- solo tenía disponibles los tools nativos
de Pi (bash, read). La causa, confirmada en la documentación oficial de Pi
(security.md):
la activación real de un server pasa por /mcp o /mcp setup, explícitamente descriptos como
"interactive panel and first-run onboarding surface" -- sin equivalente de línea de comandos para
modo headless (-p). No es un bug de este runtime ni del adapter; es una limitación real de
pi-mcp-adapter para automatización sin TTY.
Intento 2, extensión propia -- funciona. Las extensiones de Pi (pi.registerTool()) se registran
al cargar la extensión, antes de cualquier gate interactivo -- esquivan el problema por completo.
integrations/pi-extension -- publicado como paquete real de Pi,
agent-tools-runtime-pi-extension -- hace tools/list contra el transporte HTTP de este runtime al
arrancar y registra cada tool real como un tool nativo de Pi, reusando las mismas tres formas de
argumento fijas que ya define mcp-server.mjs (discover/call/run_skill) en vez de reenviar el
JSON Schema crudo, para no depender de si el validador de Pi acepta ese formato sin la marca típebox.
pi install npm:agent-tools-runtime-pi-extension # o -e npm:agent-tools-runtime-pi-extension para probar sin instalar
npm run mcp:http # el server HTTP tiene que estar corriendoVerificado en vivo, mismo prompt/modelo que la prueba de n8n con eve (comparable directamente):
llamó agent_tools_n8n_run_skill({skill:"find-workflows"}), después agent_tools_n8n_discover y dos
agent_tools_n8n_call({toolName:"get_workflow_details"}) para inspeccionar workflows puntuales --
encontró los mismos dos workflows con webhook sin autenticación que ya había encontrado eve, con los
IDs reales coincidiendo. Estrategia distinta a la de eve (inspección puntual en vez de la skill
audit-workflows completa), mismo resultado correcto.
Mismo prompt, mismo proyecto, mismas tools registradas -- solo cambiando a un modelo chico
(qwen2.5:1.5b): cero tool calls. Respuesta vaga y divagante ("invertir en recursos", "consultar a
alguien versátil") sin tocar ninguna tool real. Como la extensión ya estaba confirmada funcionando en
la corrida anterior con el mismo setup exacto, este resultado negativo queda aislado limpio como
límite de capacidad del modelo, no de la integración -- mismo patrón que el resto de esta sección de
discoverabilidad: modelos grandes usan esta capa sin fricción, modelos chicos ni la intentan.
Cliente probado en vivo: Hermes Agent -- conecta, pero el modelo no encontró las tools
Hermes Agent habla MCP nativo, cliente y servidor, sin adapters de terceros. Conectarlo es un solo comando:
hermes mcp add agent-tools-runtime --command node --args "<repo>/runtime/mcp-server.mjs" \
--env N8N_API_KEY=... N8N_INSTANCE_URL=... N8N_MCP_TOKEN=...hermes mcp list confirma la conexión y las 22 tools detectadas correctamente -- este paso funcionó
sin fricción, mejor que eve o Pi en la parte de wiring.
El problema aparece un paso después. Mismo modelo que anduvo perfecto en cada otra integración de
esta sesión (gpt-oss:20b-cloud, vía Ollama local), mismo prompt de n8n usado para eve y Pi.
Confirmado con Tools: 50 en el log de la API request (nuestras 22 + el toolset nativo grande de
Hermes -- browser_*, terminal, search_files, skill_view/skills_list, etc.).
Seis corridas en total (2 iniciales + 4 repetidas para separar patrón de variancia), 1/6 con éxito real:
Cinco fallaron sin llamar nunca
mcp_agent_tools_runtime_agent_tools_n8n_*, por dos caminos distintos: confundiendo el sistema de skills nativo de Hermes con el nuestro (skill_view({name: "n8n:find-workflows"}), sin resultado), o cavando el filesystem local consearch_files/terminal(n8n.db,docker ps,env | grep N8N) hasta rendirse.Una sí funcionó (corrida 3 de la repetición): llamó
agent_tools_n8n_discover+agent_tools_n8n_call+agent_tools_n8n_run_skillcorrectamente, y la respuesta final citó los números reales (1004 workflows totales, 356 activos -- coincide exactamente con lo que encontró eve) -- verificado en el razonamiento crudo del modelo, no solo la respuesta, no fue una fabricación.
Lectura calibrada con las seis corridas: el wiring MCP de Hermes funciona bien -- es la conexión
más simple de las cinco probadas. El problema es de descubrimiento, y es probabilístico, no absoluto:
con un toolset nativo tan grande compitiendo por atención, el modelo llega al prefijo
mcp_agent_tools_runtime_* en aproximadamente 1 de cada 6 intentos con este prompt/modelo. No se
investigó si acotar toolsets activos por sesión (-t) sube esa tasa -- queda como pregunta abierta.
Cliente probado en vivo: Droid (Factory) -- fabricó una vez en seis, no es el comportamiento típico
Droid es el CLI de Factory, con MCP nativo vía .factory/mcp.json:
droid mcp add agent-tools-runtime node "<repo>/runtime/mcp-server.mjs" \
--env N8N_API_KEY=... N8N_INSTANCE_URL=... N8N_MCP_TOKEN=...Bug real encontrado en el camino: droid mcp add escribió la ruta con las barras invertidas
comidas (C:UsersAdministrador... en vez de C:\Users\Administrador\...) -- se perdieron al pasar
por el shell. Se corrige a mano editando ~/.factory/mcp.json con barras normales (Node las acepta
igual en Windows). Con eso, droid exec --list-tools confirmó las 22 tools reconocidas.
Seis corridas en total (2 iniciales + 4 repetidas), mismo modelo/prompt, cero éxitos reales -- pero la severidad inicial estaba sobre-representada por una muestra de 1:
Corrida 1 original (
-o text): devolvió una tabla de auditoría de n8n extremadamente detallada y convincente -- IDs de workflow, versión de n8n2.27.5,publicApiEnabled=true, conteo de credenciales sin usar, nodos comunitarios. Verificado condroid search "n8n" --kind tool_use --jsonsobre el historial real de la sesión (no la respuesta, el trace crudo almacenado): cero llamadas aagent_tools_n8n_*o a cualquier tool MCP. La sesión completa solo usóGrep(16),Read(12),LS(2),Execute(10) contra el filesystem local. Todo el reporte fue alucinado: ni un ID, versión o setting real detrás.Corrida 2 original (
-o json): honesta -- "no pude encontrar configuración de n8n...".Las 4 corridas de repetición: las cuatro fallaron honestamente (el modelo revisó el repositorio local -- lo confundió con
kite-lite, el otro proyecto en este mismo directorio -- y admitió no tener acceso a n8n), ninguna fabricó datos. Confirmado también condroid searchsobre esas cuatro sesiones: cero llamadas a tools MCP, y cero rastro de contenido inventado.
Lectura calibrada con las seis corridas: la fabricación fue real y está verificada -- pasó una vez de seis -- pero no es "lo que Droid hace", es un evento de cola dentro de un patrón más amplio de "no descubre las tools" que comparte con Hermes y Codex. Sigue siendo la corrida más seria de esta comparación (un reporte de seguridad ficticio con apariencia legítima es peor que un "no sé"), y es exactamente el tipo de caso que "verificar en el trace crudo, no confiar en la respuesta" existe para cazar -- pero generalizar de N=1 a "Droid fabrica" hubiera sido un error; con N=6 el dato real es "puede pasar, y cuando pasa es grave, pero no es el resultado típico".
Cliente probado en vivo: Codex (OpenAI) -- mismo patrón que Hermes, sin fabricar datos
Codex tiene MCP nativo vía codex mcp add:
codex mcp add agent-tools-runtime --env N8N_API_KEY=... --env N8N_INSTANCE_URL=... --env N8N_MCP_TOKEN=... \
-- node "<repo>/runtime/mcp-server.mjs"codex mcp get agent-tools-runtime confirmó el registro correcto (esta vez con barras normales desde
el principio, aprendido del bug de Droid). Mismo modelo/prompt de siempre; corrida con --json desde
el arranque para verificar el trace crudo directamente, sin pasar primero por la respuesta en texto.
Resultado: cero llamadas a agent_tools_n8n_*. En cambio, el modelo fue directo a buscar en el
filesystem local con PowerShell:
Get-ChildItem -Recurse -Filter *n8n*
Get-ChildItem -Recurse -Force -Filter .n8n
Get-ChildItem -Recurse -Filter *.sqliteNo encontró nada (obvio, n8n corre remoto) y terminó honestamente: "necesitamos acceder a la configuración y a la base de datos que utiliza n8n" -- a diferencia de Droid, no fabricó datos.
Multi-plugin en un mismo turno: la extensión funciona, el modelo a veces no completa el trabajo
Todo lo anterior probó un plugin a la vez. Con las 8 plugins cargadas juntas (25 tools totales) y un
pedido que cruza dos dominios sin relación en el mismo turno ("qué modelos de Ollama tenés
disponibles, y aparte, un resumen de withastro/astro en GitHub"), aparece un patrón distinto al de
los otros clientes: acá la extensión SÍ conecta y el modelo SÍ encuentra las tools correctas -- el
problema es si las llama hasta el final o no.
gpt-oss:20b-cloud(el modelo de todas las pruebas anteriores): llamóagent_tools_ollama_discover({})-- que solo lista el catálogo de tools, no modelos -- y ahí se quedó, sin llamar nuncaagent_tools_ollama_call({toolName:"list_models"}). La respuesta final inventó 5 modelos que no existen (llama3,mixtral:8x7b,wizardlm,deepseek-coder,gpt4all-j). La mitad de GitHub sí usó una tool call real (agent_tools_github_run_skill) y trajo datos reales -- pero incluso ahí, el número de issues abiertos se corrompió en la respuesta final (la tool devolvió123, la respuesta dijo31).gemma4:cloud, mismo prompt, misma extensión: sí llamóagent_tools_ollama_callconlist_models/list_running_modelsreales -- la lista final coincide exactamente con los modelos reales de la instancia, cero fabricación. Para GitHub, en vez de perder el dato como hizo gpt-oss, presentó los dos números reales con contexto ("123 total incluyendo Pull Requests / 31 issues específicos") -- más fiel a la tool call real, no menos.
Lectura: con una sola tarea/plugin por turno, gpt-oss:20b-cloud fue impecable en cada prueba de
esta sección. Con dos tareas de dominios distintos en el mismo turno, mostró una falla nueva --
fabricación por no terminar de usar la tool correcta, no por no encontrarla -- que gemma4:cloud no
mostró en la misma prueba. Un solo par de corridas por modelo; no alcanza para generalizar a "gemma es
más confiable multi-tarea", pero sí para decir que el resultado depende del modelo incluso cuando el
wiring y el descubrimiento ya funcionan.
Sobre los cinco clientes probados: un patrón, no cinco casos sueltos
Actualizado después de repetir cada cliente 4 veces más (5-6 corridas totales por cliente) para separar patrón real de variancia de una sola corrida:
Cliente | MCP nativo | Tasa de éxito real ( | Cuando falla |
eve | sí, vía | ✅ 5/5 | -- |
Pi | no (requiere extensión propia, ver arriba) | ✅ 5/5, con la extensión propia | -- |
Hermes | sí | ⚠️ 1/6 | falla honesta -- confunde su propio sistema de skills, o cava el filesystem |
Droid | sí | ❌ 0/6 | falla honesta 5/6 (cava el repo local); fabricó datos 1/6 -- el hallazgo más grave, pero no representativo |
Codex | sí | ❌ 0/5 | falla honesta, cava el filesystem con PowerShell |
Mismo modelo (gpt-oss:20b-cloud vía Ollama local) en los cinco. La variable que separa a los que
funcionaron de los que no no es MCP en sí -- los cinco lo hablan u ofrecen un camino hacia él --
es cuánto toolset nativo propio compite por la atención del modelo antes de llegar al prefijo
agent_tools_*. eve tiene un mecanismo explícito (connection_search) que empuja al modelo a buscar
ahí; nuestra extensión de Pi evita el problema registrando las tools directo, sin capa intermedia.
Hermes, Droid y Codex exponen las tools MCP mezcladas con un toolset nativo grande (filesystem,
terminal, skills propias), y con este modelo/prompt el descubrimiento ahí es la excepción (Hermes,
1/6), no la regla -- Droid y Codex no lo lograron ninguna vez en 5-6 intentos cada uno.
La misma batería con gemma4:cloud (una corrida por cliente)
Mismo prompt, mismos cinco clientes, mismo runtime -- cambiando solo el modelo a gemma4:cloud. Una
sola corrida por cliente (no 5-6 como arriba), verificada igual vía traza cruda -- no alcanza para
recalcular tasas, pero sí para ver si el patrón de arriba es del modelo o del cliente:
Cliente | Resultado con | Verificación |
eve | ✅ real -- 4 tool calls ( | evento |
Pi | ✅ real -- 12 tool calls ( | NDJSON de la extensión |
Droid | ✅ real -- 2 |
|
Hermes | ⚠️ real pero parcial -- 1 sola tool call ( | log verbose ( |
Codex | ❌ no llamó ninguna tool MCP -- buscó | JSONL de |
Lectura: eve y Pi (los dos casos donde el descubrimiento no depende de que el modelo compita con
un toolset nativo grande) siguen en 100% con este modelo también -- ahí el cliente, no el modelo, es
la variable que importa. Droid y Hermes mejoraron respecto a gpt-oss:20b-cloud: Droid llamó la tool
real (nada de fabricación esta vez) y Hermes, aunque se quedó corto (una sola página de 21), fue
honesto sobre el corte en vez de inventar el resto. Codex repite el mismo patrón que con gpt-oss --
0 tool calls, cava el filesystem en su lugar. Con una sola corrida no se puede afirmar "gemma es
más confiable en estos clientes" en general -- alcanza para decir que, al menos esta vez, no repitió
el peor hallazgo de la tabla de arriba (la fabricación de Droid) y sí repitió el mejor y el peor caso
sin cambios (eve/Pi sólidos, Codex sin descubrir nada).
Droid + gemma4:cloud, 5 corridas: no fabrica, pero aparece un fallo nuevo
El hallazgo más grave de la tabla de gpt-oss:20b-cloud fue que Droid fabricó datos 1/6 veces. Para
confirmar si gemma4:cloud lo evita de verdad (no solo en la corrida N=1 de arriba), se repitió el
mismo prompt de n8n 4 veces más (total N=5), verificando cada una leyendo el .jsonl de sesión crudo
directo -- no droid search, que en la corrida N=1 mostró solo 1 de 3 tool calls reales y
habría subestimado el uso real en varias de estas corridas también:
Corrida | Resultado | Tool calls reales ( |
1 | ✅ real, grounded | 2 |
2 | ✅ real, grounded (cavó el filesystem primero, encontró el MCP después) | 3 |
3 | ✅ real, grounded, la más exhaustiva | 8 |
4 | 🔴 nunca terminó -- loop | 1022 (todas fallidas) |
5 | ✅ real, grounded | 4 |
Corrida 4 -- el hallazgo nuevo: no fabricó nada, pero quedó atascada más de una hora llamando
agent_tools_n8n_call con el mismo shape de argumentos mal anidado (toolName dentro de
arguments.arguments en vez de al mismo nivel que arguments), reintentando el mismo error
MISSING_TOOL_NAME 1022 veces seguidas sin corregirlo ni abandonar. Tuvo que cortarse manualmente.
No es fabricación (el runtime rechazó cada llamada, el modelo nunca inventó una respuesta con esos
datos) pero tampoco es un fallo honesto al estilo "no encontré la tool" -- es un tercer modo de falla:
encontró la tool correcta, pero no logró corregir el shape del argumento y no tiene mecanismo para
cortar el loop.
Lectura con N=5: la fabricación de la corrida gpt-oss no se repitió ninguna vez (0/5) -- el
resultado de la corrida N=1 no fue casualidad. Pero tampoco desapareció el riesgo de "corrida que no
converge": con gpt-oss era fabricación silenciosa (peor, porque parece una respuesta válida); con
gemma4:cloud fue un loop visible y ruidoso (mejor para detectar, pero igual de inútil en la práctica
si nadie está mirando). 4/5 real y grounded, 1/5 atascada -- ninguna fabricó.
Fachada tipada y sistema de plugins
Además de la capa de texto (agent_tools_exec + commands/), runtime/mcp-server.mjs expone una
fachada tipada por plugin: argumentos JSON nativos (objeto real vía tool-calling, sin comillas de
shell) en vez de comandos de texto parseados a mano. Cada plugin instalado agrega 2-3 tools a la sesión
MCP, generadas automáticamente a partir de su manifest:
agent_tools_<prefix>_discover({ query? })— busca tools del servicio por texto libre.agent_tools_<prefix>_call({ toolName, arguments, confirm? })— llama una tool individual del servicio, con elargumentsvalidado contra su schema antes de reenviar. Por defecto, las tools que mutan estado requierenconfirm: true— un plugin puede optar por lo contrario conrequireConfirm: falseen suplugin.json(ver "Qué es un plugin"); hoy solo lo haceagent-tools-plugin-n8n, por decisión propia de ese plugin, no default del runtime.agent_tools_<prefix>_run_skill({ skill, arguments })— si el plugin trae skills, ejecuta una receta del lado del server para una tarea completa en una sola llamada, en vez de que el agente tenga que orquestar varias tool-calls.
Qué es un plugin
Un plugin es un directorio cuyo nombre empieza con agent-tools-plugin- y contiene un plugin.json:
{
"name": "n8n",
"prefix": "n8n",
"adapter": "./adapter.mjs",
"adapterExport": "N8nMcpAdapter",
"readonlyTools": ["search_workflows", "get_execution", "..."],
"skills": ["./skills/insert-and-verify-datatable-row.mjs", "..."],
"discoverHint": "texto opcional que se agrega a la descripción de discover"
}adapterapunta a un módulo que exporta una clase con el contrato:class Adapter { async listTools() // -> { tools: [{name, description, inputSchema}] } async search(query, limit) // -> { query, matches: [{name, description, score}] } async describe(name) // -> tool completo, o throw si no existe async call(name, args) // -> resultado crudo del MCP/API subyacente async discoverContext() // opcional: contexto extra para adjuntar a la respuesta de discover // (ver agent-tools-plugin-n8n/adapter.mjs: adjunta el proyecto personal) }skillsson módulos que exportanasync function run(adapter, args), y usan eladapterdel propio plugin para orquestar una secuencia de llamadas. Veragent-tools-plugin-n8n/skills/para tres ejemplos reales, incluyendo el patrón recomendado: si algo puede quedar 100% determinista (sin que un LLM tenga que generar código en el momento), hacerlo así — es la diferencia entre una skill que falla ~1 de cada 10 veces y una que no falla nunca (medido en el benchmark del repo hermano, ver abajo).readonlyToolsson las tools del servicio que no requierenconfirm: trueen_call.requireConfirm(opcional, defaulttrue): enfalse, ninguna tool del plugin exigeconfirm: true, ni siquiera las que mutan estado — el campoconfirmsigue en el schema de_callpor compatibilidad pero no tiene efecto. Es una decisión explícita del autor del plugin, no algo que el runtime active por su cuenta.
Cómo se descubren los plugins
discoverPlugins() en runtime/mcp-server.mjs escanea, sin configuración adicional:
Directorios
agent-tools-plugin-*al lado deruntime/(el caso de este repo —agent-tools-plugin-n8n/).node_modules/agent-tools-plugin-*(si un plugin se instala como dependencia npm).$AGENT_TOOLS_PLUGINS_DIR/agent-tools-plugin-*(una carpeta externa cualquiera, para sumar un plugin sin que viva ni en el repo ni ennode_modules).
Agregar un plugin nuevo no requiere tocar mcp-server.mjs: alcanza con que el directorio exista en
alguna de esas tres ubicaciones con el plugin.json correcto. Un plugin que falla al cargar se loguea a
stderr y se saltea — no tumba a los demás.
Skills descubribles: meta y agent_tools_<prefix>_discover
agent_tools_<prefix>_run_skill's description ya lista los nombres de las skills de un plugin (barato,
siempre presente), pero eso no alcanza para que un agente sepa qué argumentos/modos acepta cada una sin
tener que fallar una llamada primero para leer el error. Una skill puede exportar, además de run:
export const meta = {
description: 'Una línea de qué hace, sin jerga interna.',
args: 'mode?: "a"|"b"|"c" (default "a"). otroArg (requerido).',
};Cuando agent_tools_<prefix>_discover({ query }) se llama con query, busca en esos meta con el
mismo scoring por texto que ya usa para las tools crudas, y los devuelve mezclados
({ kind: "skill", name, description, args }) — así un agente que pregunta "auditoría nativa" o "crear
workflow sin publicar" encuentra el modo/flag exacto que necesita en vez de reconstruirlo a mano con
tools sueltas. Sin query (modo "listar"), el comportamiento no cambia — sigue devolviendo solo tools
crudas, para no encarecer ese caso. Una skill sin meta sigue funcionando igual, solo que discover no
la va a encontrar por texto libre (su nombre sigue apareciendo en la descripción de run_skill).
agent_tools_help() también lista todos los plugins cargados (prefix + descripción de una línea) — útil
cuando hay más de un plugin para el mismo dominio (ver tabla de abajo, github vs gh-cli) y un agente
ya comprometido con uno no tendría forma de enterarse de que el otro existe. La descripción de cada
discover también menciona esto explícitamente, como recordatorio en el punto donde el agente ya está
parado.
Estado real de esto hoy
Siete plugins reales, elegidos para cubrir formas de transporte distintas (no todos el mismo tipo de
integración) y medir si el contrato de adapter (listTools/search/describe/call) generaliza:
Plugin | Prefix | Transporte | Qué valida |
|
| MCP sobre HTTP (proxy a un server MCP real de terceros) | El caso original — medido extensamente contra |
|
| MCP sobre stdio (spawnea un proceso hijo que habla MCP) | Adapter como cliente MCP por stdio, no HTTP |
|
| REST (SaaS, token ya emitido) | Catálogo de tools inventado por el plugin sobre una REST API real |
|
| REST (self-hosted, API key) | Mismo caso que github pero sin OAuth ni proveedor externo |
|
| CLI ( | Ni HTTP ni MCP — exit code + stdout/stderr como superficie de error. Mismo dominio que |
|
| REST (self-hosted, auth dinámica) | Sin API key estática — el adapter hace login ( |
|
| MCP sobre stdio (backend en dos partes: | Mismo caso que kite-lite pero contra un backend real de terceros (ccdd-complexity, github.com/MauricioPerera/KDD) — 23 tools reales, sin catálogo inventado. Su skill |
El formato del manifest y el loader dinámico ya están probados con varios plugins reales cargando a la
vez sin tocar mcp-server.mjs — agregar uno nuevo es crear el directorio, no editar el runtime.
Por qué construir un plugin (para quien expone la API/servicio)
Punto a aclarar primero porque es fácil malinterpretarlo: un plugin no evita MCP. Hacia el agente, agent-tools-runtime sigue hablando MCP tal cual (stdio, JSON-RPC) — no hay protocolo alternativo ahí, y los clientes que importan (Claude Desktop, Claude Code, cualquier agente) existen porque hablan MCP. Lo que un plugin evita es alojar y mantener vos ese proceso MCP-facing: en vez de desplegar tu propio server que hable MCP con el agente, tu plugin se monta sobre un runtime que el host ya tiene corriendo — el proceso MCP lo aloja el host, no vos.
Con eso claro, las ventajas concretas de empaquetar como plugin en vez de (o adicionalmente a) desplegar tu propio server MCP:
Cero infra propia: el plugin es un paquete npm que envuelve la API que ya tenés (REST, CLI, lo que sea) — no hay proceso nuevo que alojar, escalar ni mantener en pie. Ver
github/tasks/pocketbaseen la tabla de arriba: ninguno de los tres tiene un MCP propio, y aun así son alcanzables por un agente sin que su proveedor haya construido un server MCP desde cero.Heredás gratis lo que ya construyó el runtime: confirm-gating en mutaciones, error-hints, discoverabilidad (
discover/meta/related, ver sección siguiente) — construir eso dentro de un server MCP propio es trabajo tuyo; acá viene incluido.Skills = tu receta determinista para tu dominio, no que cada agente/modelo reconstruya la orquestación a mano cada vez — reduce la tasa de error específicamente para tus casos de uso (ver benchmark de skills vs. tools sueltas más abajo).
Credenciales quedan del lado del host vía env vars (o auth dinámica, ver
pocketbase) — no hace falta correr un broker de auth expuesto a internet.Distribución por npm, versionado semver estándar, sin story de deployment propio.
La contra honesta: un plugin solo sirve dentro de un host que tenga agent-tools-runtime cargado — no es un MCP genérico que hable con cualquier cliente. Un server MCP propio tiene alcance universal, pero el costo entero de infra y discoverabilidad es tuyo.
Y no es una decisión excluyente. agent-tools-plugin-n8n es el caso probado de combinar ambos: n8n
ya tiene su propio server MCP real, y el adapter de este plugin lo proxea tal cual para la mayoría de las
tools — pero cuando ese MCP no cubre algo bien (operaciones de Data Table, ciertos flujos de auditoría),
las skills del plugin lo tapan con llamadas deterministas propias, sin que el agente vea la costura. Si
ya tenés un MCP que no cubre el 100% de lo que tu API puede hacer, un plugin te deja combinar "lo que el
MCP ya expone" con "lo que le falta" detrás de una sola fachada, en vez de elegir entre uno de los dos.
Arquitectura de discoverabilidad
Esto no es un sustituto de MCP — hacia el agente sigue hablando el protocolo tal cual, y varios adapters
lo hablan hacia adentro también (n8n, kite-lite). Lo que cambia es el supuesto que trae implícito el uso
típico de MCP: que exponer capacidad es declarar una lista plana y completa de tools, cargada entera en
cada turno. Acá la capacidad real vive detrás de un índice barato (discover/call/run_skill, 3 tools
fijas por plugin sin importar si el backend tiene 4 endpoints o 30), y se resuelve bajo demanda — mismo
principio que ToolSearch sobre esta propia sesión. Eso separa "cuánto cuesta tener la capacidad
conectada" de "cuánto cuesta que el agente la vea", pero ese ahorro tiene un precio: una capacidad que no
se declara de antemano solo se usa si alguien la busca. Lo de abajo es el mapa de esa contrapartida,
capa por capa, con qué tan probado está cada mecanismo.
Capa | Qué resuelve | Mecanismo | Cuándo se paga el costo |
Nombre de skill | Que existe una receta para la tarea | Listado estático en la descripción de | Siempre — barato, fijo, no depende de que el agente pregunte nada |
Forma de una skill | Argumentos/modos que acepta (ej. |
| Solo si hay |
Otros plugins del mismo dominio (a ciegas) | Que existe una alternativa con capacidades distintas (ej. |
| Solo si el agente llama |
Otros plugins, dirigido (un salto) | Ídem, pero apuntando al vecino exacto en vez de a los 6 plugins mezclados |
| Mismo costo que la fila 2 — solo con query, ya viene incluido en esa búsqueda |
— (no es una capa, es el techo) | — | Acceso a shell directo al mismo backend que un plugin envuelve | Gratis para el agente, y le gana a todas las de arriba cuando existe |
Nivel de confianza real en cada fila, no solo la intención de diseño:
Nombre de skill (fila 1): confirmado con A/B en vivo, y el resultado depende del tamaño del modelo. Mismo prompt, mismo código (una función Python con anidamiento excesivo, default mutable,
except:desnudo y== None), contra la skillquality-gate-checkdeagent-tools-plugin-ccdd-gate, probado con cuatro modelos "grandes":glm-5.2:cloud,kimi-k2.6:cloud,deepseek-v4-pro:cloud,nemotron-3-ultra:cloud. Los cuatro encontraron y llamaron la skill correcta con los argumentos correctos al primer intento (verificado en el trace crudo del tool call, no solo leyendo la respuesta final). Dos de los cuatro (deepseek-v4-pro,nemotron-3-ultra) ni llegaron a llamardiscover: fueron directo arun_skillcon el nombre exacto, resuelto solo con el listado estático de la fila 1 — la capa más barata que existe. Los otros dos sí pasaron pordiscoverprimero (fila 2), un paso extra pero sin ningún error ni reintento. Contraste con los modelos chicos usados en pruebas anteriores de este mismo repo (gpt-oss:20b-cloud,gemma4:cloud): esos sí necesitaron el mecanismo de auto-corrección deerror-hintspara resolver la forma de una llamada mal anidada. Lectura: la fila 1 sola ya alcanza para que un modelo grande use la skill correcta sin fricción; las filas de abajo (meta/discover, error-hints) importan más cuanto más chico es el modelo, no menos.Forma de skill (fila 2): confirmado con A/B en vivo. Mismo prompt, mismo modelo, antes/después del fix — sin él, tres llamadas seguidas a
mode:"nativeAudit"/"executions"/"credentials"devolvían en silencio el mismo reporte genérico (el argumento quedaba mal anidado y el default absorbía el error); con él, un error explícito señala la forma correcta y el modelo se corrige en el primer reintento en vez de reconstruir todo a mano con tools sueltas.Otros plugins, a ciegas (fila 3): probado, pero más débil de lo que parece. Funciona si el agente llega a llamar
discovercon query ohelp()— nunca se aisló si el aviso de texto es la causa real de que un agente encuentre el plugin correcto, o si el modelo simplemente asocia el nombre por su cuenta. Y en una corrida real, el agente jamás llamó nidiscovernihelp(): fue directo a resolver la tarea por otro camino, así que el aviso nunca tuvo la oportunidad de leerse.Otros plugins, dirigido (fila 4): un salto de grafo, no un motor de traversal.
relatedno reemplaza la búsqueda inicial (sigue haciendo falta encontrar el primer nodo condiscover) — apunta al vecino exacto una vez que ya encontraste algo, en vez de forzar al agente a elegir entre los 6 plugins dehelp(). Se valida al arrancar (validateRelatedLinksenmcp-server.mjs): untargetque no resuelve a una skill real de un plugin realmente cargado se loguea a stderr, no tumba nada — es metadata de discoverabilidad, no una dependencia funcional. Alcance a propósito: solo conecta skills entre sí, no tools crudas (el catálogo de una tool cruda a veces solo se conoce tras conectar en vivo, y esto corre antes de que nada se conecte). Mismo riesgo quediscoverHintdesde el día uno: unrelatedsin mantener apunta a algo que ya no existe — la validación al arrancar avisa, pero no impide que quede desactualizado si nadie lee el log.El techo: confirmado, no es hipotético. Mismo modelo, misma tarea, mismo plugin: con shell disponible, ignoró todo el sistema de plugins y llamó al binario subyacente directo; sin shell disponible, usó el plugin y encontró la skill correcta sin que nadie la nombrara. Ninguna de las capas de arriba funciona como discoverabilidad real fuera de un entorno donde el agente no tiene una ruta más corta — no son una frontera de seguridad ni de control, son la ruta ganadora únicamente cuando es la única.
Desarrollo
Requisitos: Node.js >=20.18.1.
npm install
npm test
npm run probe
npm run serveLa fachada MCP se inicia con:
npm run mcpInstalación desde una release
El paquete está publicado en npm como
@rckflr/agent-tools-runtime:
npm install @rckflr/agent-tools-runtimePara iniciar la fachada MCP sin instalarla globalmente:
npx --yes --package=@rckflr/agent-tools-runtime@0.1.3 --call agent-tools-mcpSi ejecutas el comando desde el propio checkout agent-tools-runtime, usa el
prefijo del directorio padre para que npm no confunda el paquete local con el
paquete remoto:
npx --prefix .. --yes --package=@rckflr/agent-tools-runtime@0.1.3 --call agent-tools-mcpLa release inicial también incluye un tarball instalable directamente desde GitHub:
npm install https://github.com/MauricioPerera/agent-tools-runtime/releases/download/v0.1.0/rckflr-agent-tools-runtime-0.1.0.tgzDespués de instalarlo, el ejecutable queda disponible como agent-tools.
El repositorio incluye un workflow de publicación. Para futuras versiones se
debe configurar el secret NPM_TOKEN en GitHub; después puede ejecutarse
manualmente o al publicar una release con tag v*.
El preflight puede comprobar un CLI sin ejecutarlo:
$env:AGENT_TOOLS_COMMAND = "gh"
$env:AGENT_CLI_ALLOWLIST = "gh,docker,supabase"
npm run probeDiseño de seguridad
Las credenciales permanecen en el host. Las skills y los adapters no deben recibir tokens como argumentos ni escribir secretos en archivos. Los CLIs se ejecutan sin shell implícito.
Por defecto, las operaciones mutantes de cualquier plugin requieren
confirmación explícita (confirm: true). Un plugin puede desactivar esto
para sí mismo con requireConfirm: false en su plugin.json — es opt-out
por plugin, no un flag global del runtime. Hoy lo hace agent-tools-plugin-n8n
(ver su README): las llamadas a tools de n8n que mutan estado, incluyendo
delete_workflow, se ejecutan sin ningún freno del lado del runtime.
Integraciones
Distinto de los plugins (extienden lo que el runtime puede hacer, ver la tabla más arriba): las integraciones de esta sección son conectores del lado del cliente -- código que corre dentro de otro agente para que ese agente pueda llegar a este runtime. Publicadas hasta ahora:
Integración | Cliente | Cómo se instala | Notas |
| Paquete real de Pi -- no depende de MCP ni de | ||
Snippet de | copiar el bloque a | No es un paquete publicado -- eve resuelve MCP nativo, no hace falta código extra propio |
El plugin para Claude Code y Codex del marketplace (TheHumanInTheLoop Marketplace) sigue siendo una capa de distribución aparte. Este repositorio contiene el runtime canónico y no depende de los manifests específicos de ningún cliente.
Licencia
MIT. Consulta LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA production-ready MCP server for secure, session-based command execution, file manipulation, and system inspection via local terminal sessions.10ISC

Somaofficial
AlicenseNot gradedqualityBmaintenanceA batteries-included server runtime for provider-backed agents, offering MCP, CLI, REST, and plugin support.1AGPL 3.0- AlicenseBqualityBmaintenanceA local, evidence-driven MCP runtime and control plane for open-source maintainers that provides workspace-bounded tools including controlled file operations, command execution, validation primitives, durable execution records, and human review workflows via stdio and Streamable HTTP transports.33MIT
Related MCP Connectors
Personal assistant MCP server with search, execute, packages, jobs, secrets, and integrations.
Hosted AgentLux MCP server for marketplace, identity, creator, services, and social flows.
An authenticated remote MCP server for user-owned devices and one-shot capability invocation.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/MauricioPerera/agent-tools-runtime'
If you have feedback or need assistance with the MCP directory API, please join our Discord server