Skip to main content
Glama

Kyno

Un plano de control de coherencia para sistemas multiagente: una única fuente de verdad versionada para la misión y los principios de tu sistema (su constitución), servida a través de MCP para que cada agente pueda actuar según la dirección vigente ahora mismo — incluso cuando esa dirección cambia en pleno vuelo.

Por qué

Cuando los objetivos de un sistema multiagente cambian, los agentes que tienen una copia obsoleta de la dirección anterior siguen produciendo trabajo para ella — y peor aún, las comprobaciones de calidad contra la copia obsoleta empujan activamente el trabajo hacia atrás, hacia el objetivo obsoleto. Kyno elimina la copia obsoleta: la dirección reside en un almacén versionado, los agentes obtienen la versión actual en cada límite de paso, y los suscriptores son notificados en el momento en que cambia.

Inicio rápido

pip install .             # from a clone; CLI: kyno
kyno init-db
kyno set --mission "Ship a lending product people trust" \
         --note "initial constitution"
kyno current
kyno serve --transport stdio    # or --transport http

Una constitución es una misión (el propósito general — el desempate cuando los principios entran en conflicto) más principios ordenados. Cada cambio añade una nueva versión inmutable con una nota de cambio en lenguaje sencillo; nada se edita en su lugar, por lo que siempre se puede responder a "¿cuál era la dirección cuando actuó el agente X?".

Redactar una

Un principio de una línea es un identificador, no una regla. Dale a una constitución todo lo que necesite y nada más — cada uno de estos es opcional:

  • una declaración, el documento extenso del cual la misión es el titular;

  • una descripción bajo cualquier principio, el párrafo que resuelve una discusión sobre lo que significa el identificador.

Ambos son prosa, y la prosa a través de banderas de línea de comandos es un suplicio, por lo que una constitución se escribe en un archivo:

# constitution.yaml
mission: Ship a lending product people trust with their worst month
declaration: |
  ## What we are for

  Lending is a promise about somebody's worst month. We would rather lose
  the deal than make a promise we cannot keep.

  ## What that costs us

  - We say no early, in plain words, rather than late in a maze.
  - We publish the number before the story that softens it.
principles:
  - Say the hard number first
  - title: Refuse quietly
    description: |
      A refusal is a sentence, not a maze. If we cannot lend, say so on the
      first screen and say why.
note: the constitution as written
by: camilo
kyno set --file constitution.yaml
kyno set --file constitution.yaml --constitution eu --note "the EU edit"

La declaración es markdown, y la página publicada la renderiza: encabezados, listas, énfasis, citas, enlaces. El HTML sin procesar dentro de ella se escapa en lugar de pasarse, y los enlaces javascript: son rechazados — la página se sirve a visitantes anónimos, por lo que tu propio texto nunca puede llegar a ellos como marcado que se ejecuta. Las imágenes tampoco se renderizan, lo que mantiene la página como una única respuesta autocontenida.

En todos los demás lugares, la declaración permanece exactamente como el markdown que escribiste: el endpoint JSON, las herramientas MCP y kyno export sirven la fuente, no el documento renderizado.

--note, --by y --constitution pueden anular el archivo, porque se refieren a esta edición y no a la constitución; las banderas de campo (--mission, --declaration, --principle) no se pueden combinar con --file, porque dos fuentes para un mismo campo es una pregunta que nadie debería tener que responder. Los campos que el archivo omite se trasladan de la versión anterior — borrar uno se escribe declaration: "".

Las banderas siguen ahí para una edición rápida:

kyno set --mission "Ship a lending product people trust" --note "sharpen the mission"

El contrato

A través de MCP o Python:

  • get_constitution — la dirección vigente ahora (misión, principios, versión).

  • get_changes_since(known_version) — la extracción que hace un agente antes de un paso: la dirección actual más las notas de cambio desde la versión que vio por última vez. Una notificación perdida es inofensiva — la siguiente extracción se describe a sí misma.

  • get_mission, get_declaration, get_principles, get_principle(title) — una pieza del documento cada una, para cuando una lectura compacta la omitió.

  • set_direction(mission?, declaration?, principles?, change_note) — añade la siguiente versión. Los campos omitidos se trasladan; "" borra uno. En HTTP esto requiere el token de portador.

Cada lectura es tan pequeña como puede ser por defecto — identificadores, no el texto largo — porque un agente extrae antes de cada paso y de otro modo compraría el documento completo cada vez. Pide más cuando algo realmente lo necesite: detail="full" en las dos extracciones, detail="full" en get_principles, o una de las lecturas específicas. Cada respuesta lleva la versión de la que proviene, por lo que un cliente que las mezcla puede saber cuándo se han desviado.

Los clientes también pueden suscribirse al recurso kyno://constitution/current y recibir una notificación estándar de MCP resources/updated en cada incremento de versión. Sirve la forma compacta: un recurso no toma parámetros, y el documento completo está a una llamada de herramienta de distancia.

Múltiples constituciones

Un solo Kyno puede contener varias constituciones simultáneamente — por ejemplo, una por línea de producto o por jurisdicción. Cada operación toma un nombre de constitution opcional, tanto en MCP como en la CLI (--constitution eu), y por defecto es "default", por lo que una configuración de una sola constitución nunca tiene que mencionarlo. Cada nombre tiene su propia secuencia de versiones: incrementar eu a v2 deja default en la versión que tuviera. Un nombre al que nunca has escrito se lee como el mismo estado vacío de versión 0 que un almacén no tocado. El recurso suscribible es la constitución predeterminada; los agentes en otra la obtienen por nombre con get_changes_since.

Adaptadores (CrewAI, LangGraph)

pip install "kyno[crewai]"      # or: pip install "kyno[langgraph]"

Un adaptador vincula un crew o un grafo a una constitución nombrada y lo re-vincula en cada siguiente paso a la versión vigente en ese momento:

from kyno.adapters.core import (
    DirectionBinder,
    KynoBinding,
    McpDirectionSource,
    SessionRunner,
    http_session,
)
from kyno.adapters.crewai import CrewAiKyno

binding = KynoBinding.from_env(constitution="eu")  # KYNO_URL, KYNO_TOKEN
runner = SessionRunner(http_session(binding))
runner.start()

binder = DirectionBinder(McpDirectionSource(runner))
adapter = CrewAiKyno(binder, constitution=binding.constitution)
adapter.register()  # injects the current direction before each model call
crew = Crew(..., task_callback=adapter.task_callback)  # gates each finished task

¿Integrar Kyno en el mismo proceso en su lugar? Cambia la fuente: DirectionBinder(LocalDirectionSource(control_plane)).

  • Extraer antes de cada paso — la misión actual y los títulos de los principios se inyectan en la siguiente llamada al modelo, etiquetados con la constitución y la versión de la que provienen. Ese bloque viaja en cada llamada al modelo, por lo que se mantiene pequeño por defecto. Vincula con DirectionBinder(source, context="full") cuando prefieras gastar los tokens: la declaración y las descripciones de los principios también se inyectan, y la extracción las obtiene en lugar de solo los identificadores. Cuando Kyno no está disponible — o responde con algo ilegible — la extracción se degrada: el paso se ejecuta con la última dirección que tiene el binder, y la obsolescencia se emite como telemetría. Vincula con DirectionBinder(source, policy=PullPolicy(fail_closed=True)) cuando tu postura sea "sin dirección, sin trabajo": el paso lanza una excepción en lugar de continuar.

  • Consumo pushBackgroundSubscriber convierte una notificación de MCP resources/updated en una re-extracción por nombre. Un paso que ya se está ejecutando nunca se interrumpe; el siguiente vincula la nueva dirección.

  • Puerta de realineación — sin modelo, y revisada por tarea finalizada (callback de finalización de tarea de CrewAI), no después de cada llamada LLM — más barato y menos ruidoso una vez que se adjunta un juez real, y la tarea finalizada ya es la unidad revisable. Llama a un VerdictSource que tú proporcionas y lanza una excepción (CrewAI, desde task_callback) o interrupt() para una decisión (LangGraph) en DRIFTED. Con ningún juez disponible, el trabajo continúa, marcado como unchecked, y el evento se emite como telemetría: el valor predeterminado cambia una verificación omitida por una ejecución ininterrumpida. Establece GatePolicy(fail_closed=True) en una puerta que debería detenerse en su lugar.

  • Los adaptadores son de solo lectura — extraen y se suscriben; set_direction sigue siendo una acción de operador/CLI contra Kyno, nunca algo que un adaptador llame en nombre de un crew o un grafo.

En LangGraph, hereda KynoState en el esquema de estado de tu grafo. LangGraph solo lleva las claves que declara un esquema, por lo que sin él, la dirección que extrae un nodo nunca llega al nodo de puerta que juzga contra ella:

from kyno.adapters.langgraph import KynoState, direction_node, gate_node


class State(KynoState, total=False):
    output: str

Almacenamiento

SQLite de serie; PostgreSQL para producción a través de KYNO_DATABASE_URL. El almacenamiento es conectable: entrega SqlConstitutionStore tu propio Engine de SQLAlchemy para vivir dentro de una base de datos existente, o implementa el pequeño protocolo de almacenamiento para traer tu propia persistencia por completo. Los escritores concurrentes son seguros: las versiones se serializan mediante un índice único y un reintento, nunca se pierden ni duplican.

Las lecturas nunca fallan en un almacén vacío: antes de establecer cualquier dirección, los consumidores obtienen un estado vacío de versión 0, por lo que integrar Kyno antes de adoptarlo no cuesta nada.

Publicar tu constitución

Si quieres mostrar a la gente los principios por los que dices operar, Kyno puede servir esa página por sí mismo — así la página publicada y la que obedecen tus agentes son el mismo registro, no dos copias que se desvían.

kyno publish                                  # the default constitution
kyno publish --constitution eu --with-history
kyno unpublish --constitution eu

Mientras kyno serve --transport http se está ejecutando, una constitución publicada es legible por cualquiera en:

  • GET /constitutions/{name} — una página HTML autocontenida (sin scripts, sin activos externos, claro y oscuro). La declaración es el cuerpo de la misma, renderizada a partir de markdown, y un principio descrito lleva su párrafo.

  • GET /constitutions/{name}.json — el mismo contenido, legible por máquina.

  • GET /constitutions/ y GET /constitutions.json — un índice de lo que has publicado.

Dos cosas que vale la pena saber:

  • Un nombre publicado debe ser un slug — letras minúsculas, dígitos y guiones simples (acme, acme-eu). Es tanto la URL como el nombre que usan tus agentes, por lo que Kyno rechaza cualquier otra cosa en lugar de reescribirlo silenciosamente. Los nombres que nunca publicas no tienen restricciones.

  • Nada es público hasta que lo publicas, y la publicación es por nombre. Un solo Kyno puede contener tu constitución interna y tu constitución pública una al lado de la otra; publicar la segunda no afecta a la primera.

  • Publicar muestra solo la dirección actual — misión, declaración, principios, versión, fecha del último cambio. El historial de versiones permanece privado a menos que añadas --with-history, porque las notas de cambio están escritas para tus operadores y explican rutinariamente por qué cambiaste de rumbo. Un historial publicado muestra las 100 versiones más recientes — ese es el contrato de la página; el historial completo permanece disponible para llamantes autenticados a través de MCP y kyno export.

Cualquier cosa que no hayas publicado responde 404, exactamente como lo haría un nombre que no existe. Nada en el lado público revela cuál de los dos era.

Personalizarlo

Para un cambio de color, seis variables de entorno. Establece las que te importen y deja el resto:

Variable

Valor por defecto

Qué colorea

KYNO_PAGE_ACCENT

#6d6d66

subrayados de enlaces, números de principios

KYNO_PAGE_BACKGROUND

#fbfbf9

la página

KYNO_PAGE_TEXT

#1b1b19

texto del cuerpo

KYNO_PAGE_MUTED

#6d6d66

etiquetas, fechas, el sello de versión

KYNO_PAGE_RULE

#e4e3de

las líneas finas entre elementos

KYNO_PAGE_FONT

sans-serif del sistema

font-family para la página

Sin establecer, obtienes el aspecto incorporado, con su modo oscuro automático. Establece cualquier color y Kyno deja de intercambiar la paleta para el modo oscuro — invertir colores que elegiste te daría una página que nunca aprobaste, por lo que a partir de ese punto la paleta es tuya. Establecer solo la fuente mantiene el intercambio oscuro.

Personalizarlo correctamente

Las páginas que Kyno sirve son archivos de plantilla, y te entregará las reales:

kyno page export ./pages          # constitution.html, index.html, page.css

Edítalos, luego apunta Kyno a tus copias — imprime estas dos líneas por ti:

export KYNO_CONSTITUTION_TEMPLATE=/srv/pages/constitution.html
export KYNO_INDEX_TEMPLATE=/srv/pages/index.html      # optional

Ese es todo el flujo de trabajo. Lo que exportaste es lo que Kyno ya estaba renderizando — los mismos archivos, rellenados de la misma manera — por lo que estás editando una página funcional en lugar de reconstruir una, y cualquier cosa que dejes intacta sigue funcionando.

kyno page export se niega a sobrescribir archivos que ya están ahí, y no escribe nada en absoluto cuando tendría que hacerlo.

El page.css exportado es un punto de partida para tus propios estilos: enlácelo, incrústalo o tíralo. El marcador de posición $stylesheet a continuación siempre sirve los estilos integrados en Kyno, no tu copia de ellos — por lo que una plantilla que conserva $stylesheet se mantiene en el aspecto de la casa (y sigue las variables de color anteriores), y una que lo elimina es completamente tuya.

Marcadores de posición

constitution.html

Marcador de posición

Qué es

$stylesheet

todo el bloque <style>: variables de color + estilos de página de Kyno

$name

el nombre de la constitución

$mission

la misión, o el nombre cuando no hay misión

$declaration

la declaración renderizada desde markdown, envuelta en su <div> — vacía cuando no hay

$principles

la sección de principios, encabezado y lista — vacía cuando no hay

$version

el número de versión, ej. 3

$updated

la fecha de última modificación, ej. 2026-08-13

$history

el bloque de historial de versiones — vacío a menos que hayas publicado el historial

index.html

Marcador de posición

Qué es

$stylesheet

como arriba

$items

la lista de constituciones publicadas, o la línea "nada publicado aún"

$count

cuántas están publicadas

Cada marcador de posición de bloque trae su propio envoltorio y desaparece por completo cuando no tiene nada que decir, por lo que una plantilla nunca tiene que preguntar "¿y si no hay declaración?". Esto es deliberado: son marcadores de posición, no un lenguaje de plantillas — sin bucles, sin condiciones, sin expresiones — y los valores predeterminados se mantienen bajo el mismo límite, por lo que son los mismos archivos que acabas de exportar.

La propiedad de seguridad que esto aporta: Kyno escapa tu misión, principios y notas de cambio antes de que lleguen a tu archivo, y renderiza el markdown de tu declaración con HTML deshabilitado, por lo que ninguna plantilla puede convertir texto escrito por alguien en una constitución en marcado que se ejecute. Un marcador de posición mal escrito se deja intacto en lugar de romper la página, y si tu archivo falta o es ilegible cuando llega una solicitud, Kyno sirve su propia página y registra una advertencia — una plantilla defectuosa nunca derriba tu página pública.

Autenticación

  • stdio: abierto. Un proceso que puede iniciar el servidor ya posee el archivo de base de datos bajo él; un token allí sería ceremonia, no un límite.

  • HTTP: un token portador compartido (KYNO_TOKEN) protege cada solicitud al endpoint MCP (/mcp). El servidor se niega a iniciarse sin token sobre HTTP a menos que optes explícitamente (KYNO_ALLOW_INSECURE_HTTP, solo para experimentación local — advierte), y un KYNO_TOKEN que está configurado pero en blanco es un error de configuración en lugar de una ausencia silenciosa de autenticación. Los integradores que construyen la aplicación en código optan de la misma manera: build_http_app(..., allow_insecure=True). Las páginas de constitución publicadas arriba están fuera de esa puerta a propósito — son la superficie que elegiste abrir.

El token de escritura es control de dirección: quien lo posee dirige las instrucciones de cada agente vinculado a este Kyno. Trátalo como una credencial de aviso del sistema — sirve /mcp sobre TLS y mantén el token fuera de registros y puntos de control (las representaciones de Kyno nunca lo imprimen). Relacionadamente, el encabezado [kyno:direction …] en el bloque inyectado es contabilidad de transcripción, no un límite de autenticidad: el texto que llega de herramientas o usuarios puede imitarlo, por lo que nada debería confiar en un bloque por parecerse a uno. Kyno rechaza el texto de la constitución que contenga el marcador, y los adaptadores solo reemplazan el bloque que ellos mismos inyectaron.

Despliegue

  • Usa una KYNO_DATABASE_URL absoluta en producción. El valor predeterminado (sqlite:///kyno.sqlite3) es una conveniencia de desarrollo que se resuelve contra el directorio de trabajo en el que se inicia el proceso.

  • Ejecuta un Kyno alojado detrás de un proxy inverso que aplique límites de tasa; las páginas públicas responden a tráfico anónimo, y la limitación de tasa es trabajo del proxy, no de Kyno.

  • Los tamaños de campo son parte del contrato de la API: misión ≤ 4,000 caracteres, declaración ≤ 200,000, nota de cambio ≤ 2,000, hasta 100 principios con títulos ≤ 300 y descripciones ≤ 4,000, nombres de constitución ≤ 200. set_direction rechaza cualquier cosa más grande, y los cuerpos de solicitud /mcp están limitados a 5 MB.

  • Un Kyno instalado con pip lleva sus propios scripts de migración: kyno init-db crea un esquema nuevo sellado en la cabecera actual, y kyno upgrade-db actualiza una base de datos existente después de una actualización.

Pruebas

python -m pytest -q                      # SQLite, no network
KYNO_TEST_POSTGRES_URL=postgresql+psycopg://… python -m pytest -q   # + Postgres

Proyecto hermano: Canon prueba si las salidas de tu sistema realmente cohesionan con la constitución que Kyno sirve.

Consulta CONTRIBUTING.md para expectativas de estilo y pruebas.

-
license - not tested
-
quality - not tested
C
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 Connectors

  • Control plane for autonomous software labor. Agents claim objectives over MCP with audit trail.

  • MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.

  • Agent-native collaboration network: orchestrate a team of long-running agents from any MCP client.

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/cizambra/kyno'

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