Skip to main content
Glama

mcp-simple-job

Pasa una subtarea a un modelo local en otra máquina y verifica el resultado antes de devolverlo.

Construido para una forma concreta de problema: el asistente capaz y medido se ejecuta en la máquina ante la que estás sentado, mientras una caja con GPU perfectamente buena permanece inactiva en un rincón. Leer cuarenta archivos, resumir una página larga, descargar un conjunto de datos — nada de eso necesita el modelo caro, y hacerlo en el contexto del asistente gasta el único recurso que es genuinamente escaso.

Medido en la configuración del autor, frente a hacer los mismos cuatro trabajos en contexto: ~2,7 veces más rápido, y 6,1 veces menos contexto gastado, porque las páginas en bruto nunca entran en él.

La única regla

Un trabajo debe venir con una verificación. Una tarea que no pueda decir cómo se sabría que ha funcionado es rechazada — no advertida, rechazada.

Esto no es desconfianza del modelo local. Es que el que llama no puede saberlo. Cuando el asistente delega un resumen y recibe cuatrocientas palabras confiadas, no tiene ninguna forma independiente de saber si esas palabras describen el documento o algo más. Delegar sin una verificación añade un segundo punto donde una luz verde no significa nada.

Las verificaciones son deliberadamente aburridas: nonempty, contains, regex, json_keys, line_count, shell (tu argv, salida 0 pasa, salida canalizada a stdin) y summary_of (más corto que la fuente y no una copia de ella).

Related MCP server: any-model-plugin

Las cuatro subtareas

tool

qué hace

dónde ocurre el trabajo

simple_job

una solicitud sencilla con tu propia verificación

el host del modelo

summarize

texto, o URL(s) obtenidas primero

máquina trabajadora (obtención + modelo)

search_and_summarize

buscar, leer las páginas principales, resumir

la búsqueda alterna hosts, el resto en la máquina trabajadora

download

obtener un archivo, informar bytes + sha256

máquina trabajadora por defecto, cualquier host configurado

simple_job_stats informa de cómo han ido realmente todos ellos, a partir de una fila del registro escrita para cada trabajo.


Poniéndolo en marcha

Requisitos

  • Node 18+ en la máquina que ejecuta el servidor MCP.

  • Un endpoint de chat compatible con OpenAI. llama-server de llama.cpp, Ollama, vLLM, LM Studio o una API alojada: cualquier cosa que responda a POST /v1/chat/completions.

  • Python 3.9+ en cualquier máquina que haga la obtención. Solo biblioteca estándar; sin pip install, sin node, sin beautifulsoup.

  • Autenticación por clave SSH a la segunda máquina, si usas una. Es opcional (ver más abajo).

Cómo está conectado en las máquinas del autor

Dos ordenadores:

  • Un Mac mini — la estación de trabajo diaria. 16 GB, normalmente unos pocos gigabytes en swap. Ejecuta Claude Desktop y, por tanto, este servidor MCP.

  • Una máquina Pop!_OS con una RTX 5070 Ti — el laboratorio. Ejecuta un modelo de 35B llamado ornith bajo llama-server en el puerto 8080, y está inactiva la mayor parte del día.

Un túnel SSH hace que el modelo remoto parezca local para el Mac:

ssh -N -o ServerAliveInterval=30 -L 127.0.0.1:8081:127.0.0.1:8080 pop-os

pop-os es un alias de ~/.ssh/config con una clave y IdentitiesOnly yes, de modo que el servidor puede alcanzarlo de forma no interactiva con BatchMode=yes.

La entrada del cliente MCP es simplemente:

{ "mcpServers": { "simple-job": { "command": "node", "args": ["/path/to/mcp-simple-job/index.js"] } } }

Los valores por defecto hacen el resto: el modelo en 127.0.0.1:8081, la máquina trabajadora en pop-os y el registro en un ~/Code/harness/ existente, si lo hay.

Nada se llama nunca a mano. El asistente elige la herramienta — que es un problema más difícil de lo que parece, y se cubre más abajo.

Ejecutándolo en las tuyas

Dos máquinas, una que ejecuta el cliente y otra que ejecuta el modelo:

{
  "mcpServers": {
    "simple-job": {
      "command": "node",
      "args": ["/path/to/mcp-simple-job/index.js"],
      "env": {
        "ORNITH_URL": "http://127.0.0.1:8081/v1/chat/completions",
        "ORNITH_MODEL": "your-model-name",
        "POP_HOST": "your-ssh-alias"
      }
    }
  }
}

Una sola máquina — todo local, sin SSH en ningún sitio:

{
  "env": {
    "ORNITH_URL": "http://127.0.0.1:11434/v1/chat/completions",
    "ORNITH_MODEL": "qwen3:8b",
    "SEARCH_HOSTS": "mac"
  }
}

SEARCH_HOSTS=mac es el interruptor que dice «no hay segunda máquina». La obtención, la descarga y la búsqueda suceden todas localmente, y la búsqueda simplemente tiene un presupuesto de frecuencia en lugar de dos. Todo lo demás se comporta igual.

Variables de entorno

variable

valor por defecto

qué hace

ORNITH_URL

http://127.0.0.1:8081/v1/chat/completions

el endpoint de chat

ORNITH_MODEL

ornith:35b

nombre del modelo enviado en la solicitud

POP_HOST

pop-os

alias SSH de la máquina trabajadora

SEARCH_HOSTS

mac,pop

máquinas entre las que alternar las búsquedas; establece mac para una instalación de una sola máquina

SEARCH_GAP_MS

30000

intervalo mínimo entre búsquedas desde una misma máquina

SEARCH_BLOCK_MS

300000

cuánto tiempo permanece fuera una máquina tras ser limitada

SIMPLE_JOB_STATE

junto a index.js

dónde se guarda el estado de temporización de la búsqueda

HARNESS_LEDGER

~/Code/harness/ledger.db si ese directorio existe; si no, ~/.mcp-simple-job/ledger.db

el registro SQLite

HARNESS_TRACE

~/Code/harness/current_trace.txt

identificador de traza opcional para estampar en las filas

El registro es opcional. Se crea en el primer uso, y si no se puede escribir los trabajos siguen ejecutándose: el registro se hace con el mejor esfuerzo y nunca bloquea el trabajo. HARNESS_TRACE es un gancho para la configuración de trazado del propio autor; ignóralo y las filas simplemente tendrán un identificador de traza nulo.

Conseguir que se use de verdad

Esta es la parte que la mayoría de la gente se salta, y es la parte que decide si todo lo anterior importa.

Una herramienta a la que nada enruta es invisible, por muy bien que funcione. El autor tiene un servidor MCP aparte que funciona perfectamente y tuvo cero llamadas en meses, puramente porque nada le dijo nunca al asistente que lo utilizara. Construir una capacidad y enrutar hacia ella son dos trabajos distintos, y terminar el primero se siente como terminar.

Hay tres maneras de cerrar esa brecha, la más barata primero. La mayoría de la gente quiere la segunda.

1. No hagas nada, y observa. Algunos clientes leen las descripciones de herramientas lo bastante bien como para que una solicitud suficientemente obvia — «resume estas cuarenta páginas» — encuentre la herramienta por sí sola. Vale la pena probarlo durante un día antes de añadir mecanismos. Observa si realmente se llama.

2. Pon una regla donde tu cliente guarde las instrucciones permanentes. Instrucciones de proyecto de Claude Desktop, un CLAUDE.md para Claude Code, .cursorrules, las instrucciones de un GPT personalizado — lo que sea que tu cliente lea en cada turno. Algo así:

Hay un modelo local disponible a través de simple-job. Úsalo cuando el material no esté ya en contexto y el trabajo sea mecánico: resumir páginas o archivos, búsqueda web más lectura, descargar archivos, extracción y reformateo. Es gratis y no gasta contexto en la fuente.

Hazlo tú mismo cuando el texto ya esté en contexto, cuando el trabajo necesite interpretación en lugar de transcripción, o cuando acertar importe más que poder verificarlo. Nunca delegues decisiones de criterio, código que deba ser correcto o edición de archivos.

Cada trabajo debe llevar una verificación: el servidor rechaza el trabajo que no puede verificar.

Dedica tantas palabras a cuándo no delegar como a cuándo hacerlo. El modo de fallo de enrutar hacia una herramienta de delegación es la sobredelegación, y un asistente que lo envía todo cuesta abajo te entregará una transcripción fiel donde querías criterio.

3. Conéctalo a un enrutador, si tienes uno. Si tu configuración ya hace corresponder situaciones con herramientas, añade una entrada para «lectura masiva u obtención de material aún no en contexto». La ventaja sobre una instrucción permanente es que es medible: puedes contar si se activó cuando debería. Una instrucción permanente o funciona o no, y nada registra cuál de las dos.

Qué no enviarle

Cualquier cosa donde «tiene buena pinta» sea la única prueba. Decisiones de criterio. Código que deba ser correcto. Editar archivos.

Y un límite encontrado por medición más que por gusto: un modelo local pequeño transcribe fielmente pero no interpreta. En las pruebas reprodujo literalmente el fraseo ambiguo de una fuente en lugar de resolver lo que significaba, y resumió el número de estrellas de un repositorio como si formara parte de un informe de error. Envíale transcripción. Conserva la interpretación.


Notas al construirlo

Todo lo que sigue es una medición, no una opinión. Los números están también en los comentarios del código.

El razonamiento está desactivado por defecto

Los modelos de razonamiento emiten su deliberación y su respuesta desde el mismo presupuesto de tokens. En un trabajo de resumen ejecutado tres veces de forma idéntica, dos de las tres veces gastaron 5.500–6.000 caracteres pensando, alcanzaron el límite y devolvieron una respuesta vacía con HTTP 200.

Subir max_tokens no lo arregló. reasoning_effort: "low" no lo arregló. Una etiqueta de sistema /no_think no lo arregló. Solo chat_template_kwargs: {enable_thinking: false} lo hizo, y el mismo trabajo respondió entonces en 258 tokens. Pasa think: true para un trabajo que realmente necesite deliberación, y sube max_tokens con él.

La búsqueda está racionada, y DuckDuckGo miente sobre el motivo

DuckDuckGo no limita la tasa de forma educada:

  • una consulta atendida es HTTP 200, ~28 KB, diez enlaces de resultados

  • una rechazada es HTTP 202, ~14,2 KB, y su texto dice "Please complete the following challenge... Select all squares containing a duck"

Es una marca de captcha en la IP, no un límite temporal, y espaciar no la elimina. Después de cuatro minutos de silencio, seis consultas con 30 s de separación desde una máquina y seis con 15 s desde la otra fueron 0 de 12. El sondeo cada 5 s durante un bloque no se recuperó en 162 s — reintentar la alimenta. La marca se disipó por sí sola en unos veinte minutos.

Por eso las búsquedas se espacian, se alternan entre máquinas (dos IP son dos presupuestos), y una limitación se informa como throttled, nunca como «sin resultados». Esas dos cosas significan lo contrario.

La lectura sigue a la pregunta

Una página larga se recorta para que quepa en la ventana del modelo, y recortar desde arriba responde a la pregunta equivocada en silencio. Al preguntar por "sparse gating and load balancing" en una página de 40.063 caracteres con una ventana de 8.000 caracteres, la primera versión devolvió un resumen fluido del inicio del artículo — en el que "load balancing" nunca aparece (empieza en el carácter 16.181) y "sparse" nunca aparece (14.671).

Así que focus dirige la ventana: una cabecera para el contexto, luego los pasajes alrededor de cada término nombrado, una ventana garantizada por término antes de que ningún término tenga una segunda. Dos versiones anteriores no fueron suficientes: la coincidencia de subcadenas encontró "load" dentro de "download" e informó de 42 coincidencias de ruido, y tomar los pasajes en orden del documento gastó el presupuesto antes de llegar al carácter 16.181.

Si la página nunca usa esas palabras, la llamada devuelve ok:false con focus_not_found. Cero coincidencias es una respuesta mejor que un resumen plausible de otro material.

Una página que es mayormente script es rechazada

Un sitio sirvió 68.896 bytes que contenían 40 caracteres de texto ("Loading..."), que el guard if not text original dejó pasar, así que el armazón entró en un resumen como material fuente y el modelo escribió con total confianza una cifra de referencia citándolo.

Ahora hay dos pruebas, porque cada una por separado se deja engañar: un mínimo absoluto y una proporción de texto a bytes que solo condena a una página que también es corta. Un issue de GitHub son 290.000 bytes de marcado alrededor de 3.896 caracteres de discusión real, y la proporción por sí sola lo descartó.

Las citas están numeradas para poder comprobarse

search_and_summarize numera las páginas y pide [1], [2] en lugar de urls. Al pedirle urls, el modelo atribuyó una cifra que había leído en un blog a una página de documentación: el dato era real y estaba en el material, la atribución no lo era, y summary_of no puede verlo porque una viñeta mal etiquetada tiene la longitud correcta y no es una copia.

Una url es una cadena larga y opaca de copiar correctamente. Un entero no lo es, y se puede comprobar su rango contra las páginas realmente leídas, lo que el código hace, fallando la llamada si el número está fuera de rango y contando las viñetas sin ninguna fuente.

¿Qué máquina es más rápida?

Verificado con sha256 idéntico en ambos lados:

máquina cliente

máquina trabajadora

ida y vuelta de ssh

~185 ms por llamada

obtener 4 páginas

~1,7 s

~2,0 s

descargar 20 MB

17,5 MB/s

12,6 MB/s

La máquina cliente fue más rápida en ambas. La velocidad no es la razón para enviar trabajo a la trabajadora. Las razones son que contiene el modelo, que está inactiva mientras la otra máquina está en uso, y que una segunda máquina es un segundo presupuesto de búsqueda. Elige el host de una descarga según dónde se necesite el archivo, no según el rendimiento.

Pruebas

node test_e2e.mjs                        # 24 assertions, spawns the real server over JSON-RPC
node --test test/simple-job.test.mjs     # 19 unit assertions on the checks

La suite de extremo a extremo lanza el servidor real de la misma manera que lo haría un cliente, contra un libro de contabilidad desechable. Una prueba en proceso no detectaría un error de PATH o de entorno, y esos son exactamente los que solo aparecen después de un reinicio.

Los cambios en index.js surten efecto la próxima vez que el cliente inicie el servidor. pop_agent.py se vuelve a leer en cada llamada, por lo que los cambios en la obtención, la búsqueda y la descarga se aplican de inmediato.

Install Server
F
license - not found
A
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

View all related MCP servers

Related MCP Connectors

  • Verifies AI agent work end to end: real artifacts and outcomes checked, not self-reported success.

  • LLM chat, text summarization and AI image generation

  • Free OpenAI-compatible inference with signed provenance receipts and 3 focused MCP tools.

View all MCP Connectors

Latest Blog Posts

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/MikeyBeez/mcp-simple-job'

If you have feedback or need assistance with the MCP directory API, please join our Discord server