JSON Canvas MCP Server
Un servidor para crear, modificar, validar y exportar archivos JSON Canvas, un formato para datos de lienzo infinito.
Documentación
Servidor MCP de JSON Canvas
Un servidor de Protocolo de Contexto de Modelo (MCP) para trabajar con
archivos de JSON Canvas — el formato de lienzo infinito abierto utilizado
por Obsidian. Permite que un cliente MCP cree, valide,
lea y liste archivos .canvas.
Construido sobre el SDK oficial de Python mcp (>=1.27), que negocia la revisión del protocolo MCP 2025-11-25.
Se ejecuta sobre stdio de forma predeterminada y opcionalmente sobre el transporte HTTP Streamable.
Los hosts que admiten la extensión de interfaz de usuario MCP Apps renderizan un visor de lienzo interactivo en línea al leer o crear un lienzo — una vista de paneo/zoom, al estilo de Obsidian, de los nodos y bordes. Los clientes de solo texto no se ven afectados y continúan recibiendo el lienzo como salida de texto/estructurada.
Componentes
Herramientas
- create_canvas — Crea un lienzo a partir de
nodes(yedgesopcional) y lo escribe en un archivo.canvascon prefijo de fecha bajoOUTPUT_PATH.- Entrada:
nodes(matriz de objetos de nodo JSON Canvas),filename(cadena, sin extensión),edges(matriz opcional de objetos de borde). - Devuelve (estructurado):
{ path, node_count, edge_count }.
- Entrada:
- validate_canvas — Valida los datos del lienzo contra la especificación JSON Canvas 1.0.
- Entrada:
canvas(objeto connodesyedgesopcionales). - Devuelve (estructurado):
{ valid, error }.
- Entrada:
- read_canvas — Lee un archivo
.canvasalmacenado y devuelve sus nodos y bordes.- Entrada:
filename(cadena, con o sin la extensión.canvas). - Devuelve (estructurado):
{ nodes, edges }(también renderizado por el visor de lienzo; el texto de respaldo es el JSON del lienzo).
- Entrada:
- list_canvases — Lista los archivos
.canvasdisponibles enOUTPUT_PATH.- Devuelve: matriz de nombres de archivo.
- edit_canvas — Agrega, actualiza y/o elimina nodos y bordes en un lienzo almacenado en una sola
escritura atómica (una operación fallida deja el archivo sin cambios).
- Entrada:
filename, másadd_nodesopcional,update_nodes(parcial, debe incluirid),remove_node_ids(cascada de bordes conectados),add_edges,update_edges,remove_edge_ids. - Devuelve (estructurado):
{ path, node_count, edge_count, canvas }— el lienzo actualizado, de modo que los hosts con capacidad de interfaz de usuario lo vuelvan a renderizar en línea.
- Entrada:
- export_canvas — Exporta un lienzo almacenado a otro formato.
- Entrada:
filename,format(markdown|svg). - Devuelve (estructurado):
{ format, mime_type, content }. Markdown es un esquema ordenado por bordes; SVG es una imagen vectorial independiente (solo líneas de título de nodo — el SVG simple no puede renderizar Markdown).
- Entrada:
- search_canvases — Búsqueda de subcadenas sin distinción de mayúsculas y minúsculas en los lienzos almacenados.
- Entrada:
query,filenameopcional para limitar el alcance a un lienzo. - Devuelve (estructurado):
{ matches: [{ filename, kind, id, field, snippet }] }.
- Entrada:
create_canvas, read_canvas y edit_canvas están vinculados al visor de lienzo mediante
_meta.ui.resourceUri, de modo que los hosts con capacidad de interfaz de usuario renderizan el resultado en línea.
Los objetos de nodo utilizan la forma JSON Canvas: id, type (text | file | link | group),
x, y, width, height, color opcional, más campos específicos del tipo (text, file/subpath,
url, label/background/backgroundStyle). Los objetos de borde utilizan id, fromNode, toNode y
fromSide/toSide/fromEnd/toEnd/color/label opcionales.
Recursos
canvas://schema— Esquema JSON para validar archivos de lienzo.canvas://examples/basic— Un lienzo de ejemplo simple (dos nodos de texto unidos por un borde).ui://canvas/viewer.html— El visor de lienzo interactivo (interfaz de usuario de MCP Apps), servido con tipo MIMEtext/html;profile=mcp-app. Referenciado porcreate_canvasyread_canvas.
Visor de lienzo interactivo (interfaz de usuario de MCP Apps)
El visor es un paquete HTML autónomo único construido a partir del código fuente ui/ con Vite
y el cliente oficial @modelcontextprotocol/ext-apps.
Renderiza nodos (con markdown, colores y grupos) y bordes (lados, flechas, etiquetas)
en una vista de paneo/zoom, con temas mediante las variables CSS del host.
El paquete construido está confirmado en jsoncanvas/_ui/viewer.html y se incluye en el paquete, por lo que
ejecutar el servidor solo necesita Python. Reconstruyelo después de cambiar ui/:
make build-ui # cd ui && npm install && npm run build (requires Node.js)
Para previsualizar el renderizador de forma independiente (sin host MCP), ejecuta cd ui && npm run dev y abre
/preview.html.
Uso con Claude Desktop
Docker (stdio)
docker build -t mcp/jsoncanvas .
Agrega a tu claude_desktop_config.json:
{
"mcpServers": {
"jsoncanvas": {
"command": "docker",
"args": ["run", "-i", "--rm", "-v", "canvas-data:/data", "mcp/jsoncanvas"],
"env": { "OUTPUT_PATH": "/data/output" }
}
}
}
uv (stdio)
{
"mcpServers": {
"jsoncanvas": {
"command": "uv",
"args": ["--directory", "/path/to/jsoncanvas", "run", "mcp-server-jsoncanvas"],
"env": { "OUTPUT_PATH": "./output" }
}
}
}
Transporte HTTP Streamable
Para servir sobre HTTP Streamable en lugar de stdio:
mcp-server-jsoncanvas --transport streamable-http --host 127.0.0.1 --port 8000
El endpoint MCP es entonces http://127.0.0.1:8000/mcp. El transporte se vincula a localhost y, según
la especificación 2025-11-25, valida el encabezado Origin con protección contra rebinding de DNS habilitada
(solo orígenes localhost de forma predeterminada). Para aceptar conexiones desde fuera del host (por ejemplo, al
ejecutar el contenedor con HTTP), vincula --host 0.0.0.0 y configura tus orígenes permitidos
en consecuencia.
Los hosts MCP basados en navegador (el tipo que renderiza el visor de lienzo) se conectan entre orígenes y deben
leer el encabezado de respuesta mcp-session-id, por lo que el transporte HTTP Streamable sirve
encabezados CORS permisivos. Restringe los orígenes permitidos con MCP_CORS_ORIGINS (separados por comas; predeterminado
*).
Nota de seguridad. El transporte HTTP no está autenticado — cualquiera que pueda alcanzar el puerto puede leer y escribir archivos
.canvasbajoOUTPUT_PATH. El servidor está destinado para uso local; mantenlo vinculado a127.0.0.1(el predeterminado). La protección contra rebinding de DNS/Originestá fijada a orígenes y hosts de localhost al inicio, por lo que vincular--host 0.0.0.0expone el puerto en la red pero aún rechaza encabezadosHost/Originque no sean de localhost — para exponerlo de forma segura de forma remota, colócalo detrás de un proxy inverso con autenticación en lugar de publicarlo directamente, y estableceMCP_CORS_ORIGINSa los orígenes específicos que confíes (nunca*).
Configuración
Variables de entorno:
OUTPUT_PATH— Directorio donde se escriben/leen los archivos.canvas(predeterminado./output).MCP_TRANSPORT—stdio(predeterminado) ostreamable-http.MCP_HOST/MCP_PORT— Host/puerto para el transporte HTTP Streamable (predeterminado127.0.0.1:8000).MCP_CORS_ORIGINS— Orígenes CORS permitidos separados por comas para el transporte HTTP (predeterminado*).
Desarrollo
# Install uv: https://docs.astral.sh/uv/getting-started/installation/
make setup # uv venv && uv sync --extra dev
make build-ui # rebuild the canvas viewer bundle (requires Node.js)
make test # run the test suite
make lint # ruff check + format check
make audit # scan dependencies for known vulnerabilities (pip-audit)
make run # run the server over stdio
Ejecuta el ejemplo de biblioteca incluido:
make example # writes example.canvas to OUTPUT_PATH (default ./output)
Ejemplo de biblioteca
El paquete jsoncanvas también se puede usar directamente:
from jsoncanvas import Canvas, TextNode, Edge
title = TextNode(id="title", x=100, y=100, width=400, height=100,
text="# Hello Canvas", color="#4285F4")
info = TextNode(id="info", x=600, y=100, width=300, height=100,
text="More information here", color="2") # preset color
canvas = Canvas()
canvas.add_node(title)
canvas.add_node(info)
canvas.add_edge(Edge(id="edge1", from_node="title", to_node="info",
from_side="right", to_side="left", label="Connection"))
import json
print(json.dumps(canvas.to_dict(), indent=2))
Licencia
MIT. Consulta LICENCIA.