Skip to main content
Glama

Myrcael

by noname9091

sova

Un marco de TypeScript para crear servidores MCP capaces de manejar sesiones de cliente.

[!NOTA]

Para una implementación de Python, consulte sova .

Características

Instalación

npm install sova

Inicio rápido

[!NOTA]

Existen numerosos ejemplos reales del uso de sova en la naturaleza. Vea ejemplos en el Showcase .

import { sova } from "sova"; import { z } from "zod"; // Or any validation library that supports Standard Schema const server = new sova({ name: "My Server", version: "1.0.0", }); server.addTool({ name: "add", description: "Add two numbers", parameters: z.object({ a: z.number(), b: z.number(), }), execute: async (args) => { return String(args.a + args.b); }, }); server.start({ transportType: "stdio", });

¡Listo! Ya tienes un servidor MCP funcionando.

Puedes probar el servidor en la terminal con:

git clone https://github.com/bounded1999/sova.git cd sova pnpm install pnpm build # Test the addition server example using CLI: npx sova dev src/examples/addition.ts # Test the addition server example using MCP Inspector: npx sova inspect src/examples/addition.ts

SSE

Los eventos enviados por el servidor (SSE) proporcionan un mecanismo para que los servidores envíen actualizaciones en tiempo real a los clientes mediante una conexión HTTPS. En el contexto de MCP, SSE se utiliza principalmente para habilitar la comunicación remota de MCP, lo que permite acceder a un MCP alojado en una máquina remota y retransmitir actualizaciones a través de la red.

También puedes ejecutar el servidor con soporte SSE:

server.start({ transportType: "sse", sse: { endpoint: "/sse", port: 8080, }, });

Esto iniciará el servidor y escuchará conexiones SSE en http://localhost:8080/sse .

Luego puede usar SSEClientTransport para conectarse al servidor:

import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; const client = new Client( { name: "example-client", version: "1.0.0", }, { capabilities: {}, }, ); const transport = new SSEClientTransport(new URL(`http://localhost:8080/sse`)); await client.connect(transport);

Conceptos básicos

Herramientas

Las herramientas en MCP permiten a los servidores exponer funciones ejecutables que pueden ser invocadas por los clientes y utilizadas por los LLM para realizar acciones.

Sova utiliza la especificación del Esquema Estándar para definir los parámetros de la herramienta. Esto le permite usar su biblioteca de validación de esquemas preferida (como Zod, ArkType o Valibot) siempre que implemente la especificación.

Ejemplo de Zod:

import { z } from "zod"; server.addTool({ name: "fetch-zod", description: "Fetch the content of a url (using Zod)", parameters: z.object({ url: z.string(), }), execute: async (args) => { return await fetchWebpageContent(args.url); }, });

Ejemplo de ArkType:

import { type } from "arktype"; server.addTool({ name: "fetch-arktype", description: "Fetch the content of a url (using ArkType)", parameters: type({ url: "string", }), execute: async (args) => { return await fetchWebpageContent(args.url); }, });

Ejemplo de Valibot:

Valibot requiere la dependencia de pares @valibot/to-json-schema.

import * as v from "valibot"; server.addTool({ name: "fetch-valibot", description: "Fetch the content of a url (using Valibot)", parameters: v.object({ url: v.string(), }), execute: async (args) => { return await fetchWebpageContent(args.url); }, });
Devolviendo una cadena

execute puede devolver una cadena:

server.addTool({ name: "download", description: "Download a file", parameters: z.object({ url: z.string(), }), execute: async (args) => { return "Hello, world!"; }, });

Esto último es equivalente a:

server.addTool({ name: "download", description: "Download a file", parameters: z.object({ url: z.string(), }), execute: async (args) => { return { content: [ { type: "text", text: "Hello, world!", }, ], }; }, });
Devolviendo una lista

Si desea devolver una lista de mensajes, puede devolver un objeto con una propiedad content :

server.addTool({ name: "download", description: "Download a file", parameters: z.object({ url: z.string(), }), execute: async (args) => { return { content: [ { type: "text", text: "First message" }, { type: "text", text: "Second message" }, ], }; }, });
Devolviendo una imagen

Utilice imageContent para crear un objeto de contenido para una imagen:

import { imageContent } from "sova"; server.addTool({ name: "download", description: "Download a file", parameters: z.object({ url: z.string(), }), execute: async (args) => { return imageContent({ url: "https://example.com/image.png", }); // or... // return imageContent({ // path: "/path/to/image.png", // }); // or... // return imageContent({ // buffer: Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=", "base64"), // }); // or... // return { // content: [ // await imageContent(...) // ], // }; }, });

La función imageContent toma las siguientes opciones:

  • url : La URL de la imagen.
  • path : la ruta al archivo de imagen.
  • buffer : Los datos de la imagen como buffer.

Solo se debe especificar uno de los siguientes: url , path o buffer .

El ejemplo anterior es equivalente a:

server.addTool({ name: "download", description: "Download a file", parameters: z.object({ url: z.string(), }), execute: async (args) => { return { content: [ { type: "image", data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=", mimeType: "image/png", }, ], }; }, });
Devolviendo un audio

Utilice audioContent para crear un objeto de contenido para un audio:

import { audioContent } from "sova"; server.addTool({ name: "download", description: "Download a file", parameters: z.object({ url: z.string(), }), execute: async (args) => { return audioContent({ url: "https://example.com/audio.mp3", }); // or... // return audioContent({ // path: "/path/to/audio.mp3", // }); // or... // return audioContent({ // buffer: Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=", "base64"), // }); // or... // return { // content: [ // await audioContent(...) // ], // }; }, });

La función audioContent toma las siguientes opciones:

  • url : La URL del audio.
  • path : La ruta al archivo de audio.
  • buffer : Los datos de audio como buffer.

Solo se debe especificar uno de los siguientes: url , path o buffer .

El ejemplo anterior es equivalente a:

server.addTool({ name: "download", description: "Download a file", parameters: z.object({ url: z.string(), }), execute: async (args) => { return { content: [ { type: "audio", data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=", mimeType: "audio/mpeg", }, ], }; }, });
Tipo de combinación de retorno

Puedes combinar varios tipos de esta manera y enviarlos de vuelta a la IA.

server.addTool({ name: "download", description: "Download a file", parameters: z.object({ url: z.string(), }), execute: async (args) => { return { content: [ { type: "text", text: "Hello, world!", }, { type: "image", data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=", mimeType: "image/png", }, { type: "audio", data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=", mimeType: "audio/mpeg", }, ], }; }, // or... // execute: async (args) => { // const imgContent = imageContent({ // url: "https://example.com/image.png", // }); // const audContent = audioContent({ // url: "https://example.com/audio.mp3", // }); // return { // content: [ // { // type: "text", // text: "Hello, world!", // }, // imgContent, // audContent, // ], // }; // }, });
Explotación florestal

Las herramientas pueden registrar mensajes en el cliente utilizando el objeto log en el objeto de contexto:

server.addTool({ name: "download", description: "Download a file", parameters: z.object({ url: z.string(), }), execute: async (args, { log }) => { log.info("Downloading file...", { url, }); // ... log.info("Downloaded file"); return "done"; }, });

El objeto log tiene los siguientes métodos:

  • debug(message: string, data?: SerializableValue)
  • error(message: string, data?: SerializableValue)
  • info(message: string, data?: SerializableValue)
  • warn(message: string, data?: SerializableValue)
Errores

Los errores que se deben mostrar al usuario deben generarse como instancias UserError :

import { UserError } from "sova"; server.addTool({ name: "download", description: "Download a file", parameters: z.object({ url: z.string(), }), execute: async (args) => { if (args.url.startsWith("https://example.com")) { throw new UserError("This URL is not allowed"); } return "done"; }, });
Progreso

Las herramientas pueden informar el progreso llamando reportProgress en el objeto de contexto:

server.addTool({ name: "download", description: "Download a file", parameters: z.object({ url: z.string(), }), execute: async (args, { reportProgress }) => { reportProgress({ progress: 0, total: 100, }); // ... reportProgress({ progress: 100, total: 100, }); return "done"; }, });
Anotaciones de herramientas

A partir de la Especificación MCP (26/03/2025), las herramientas pueden incluir anotaciones que brindan un contexto y un control más completos al agregar metadatos sobre el comportamiento de una herramienta:

server.addTool({ name: "fetch-content", description: "Fetch content from a URL", parameters: z.object({ url: z.string(), }), annotations: { title: "Web Content Fetcher", // Human-readable title for UI display readOnlyHint: true, // Tool doesn't modify its environment openWorldHint: true, // Tool interacts with external entities }, execute: async (args) => { return await fetchWebpageContent(args.url); }, });

Las anotaciones disponibles son:

AnotaciónTipoPor defectoDescripción
titlecadena-Un título legible para humanos para la herramienta, útil para la visualización de la interfaz de usuario
readOnlyHintbooleanofalseSi es verdadero, indica que la herramienta no modifica su entorno.
destructiveHintbooleanotrueSi es verdadero, la herramienta puede realizar actualizaciones destructivas (solo tiene sentido cuando readOnlyHint es falso)
idempotentHintbooleanofalseSi es verdadero, llamar a la herramienta repetidamente con los mismos argumentos no tiene ningún efecto adicional (solo es significativo cuando readOnlyHint es falso)
openWorldHintbooleanotrueSi es cierto, la herramienta puede interactuar con un "mundo abierto" de entidades externas.

Estas anotaciones ayudan a los clientes y a los LLM a comprender mejor cómo utilizar las herramientas y qué esperar al llamarlas.

Recursos

Los recursos representan cualquier tipo de datos que un servidor MCP desea poner a disposición de los clientes. Esto puede incluir:

  • Contenido del archivo
  • Capturas de pantalla e imágenes
  • Y más

Cada recurso se identifica mediante un URI único y puede contener texto o datos binarios.

server.addResource({ uri: "file:///logs/app.log", name: "Application Logs", mimeType: "text/plain", async load() { return { text: await readLogFile(), }; }, });

[!NOTA]

load puede devolver múltiples recursos. Esto podría usarse, por ejemplo, para devolver una lista de archivos dentro de un directorio al leerlo.

async load() { return [ { text: "First file content", }, { text: "Second file content", }, ]; }

También puedes devolver contenido binario en load :

async load() { return { blob: 'base64-encoded-data' }; }

Plantillas de recursos

También puedes definir plantillas de recursos:

server.addResourceTemplate({ uriTemplate: "file:///logs/{name}.log", name: "Application Logs", mimeType: "text/plain", arguments: [ { name: "name", description: "Name of the log", required: true, }, ], async load({ name }) { return { text: `Example log content for ${name}`, }; }, });
Completado automático de argumentos de plantillas de recursos

Proporcionar funciones complete para los argumentos de la plantilla de recursos para permitir la finalización automática:

server.addResourceTemplate({ uriTemplate: "file:///logs/{name}.log", name: "Application Logs", mimeType: "text/plain", arguments: [ { name: "name", description: "Name of the log", required: true, complete: async (value) => { if (value === "Example") { return { values: ["Example Log"], }; } return { values: [], }; }, }, ], async load({ name }) { return { text: `Example log content for ${name}`, }; }, });

Indicaciones

Los avisos permiten a los servidores definir plantillas de avisos y flujos de trabajo reutilizables que los clientes pueden mostrar fácilmente a los usuarios y a los LLM. Ofrecen una potente herramienta para estandarizar y compartir interacciones comunes de LLM.

server.addPrompt({ name: "git-commit", description: "Generate a Git commit message", arguments: [ { name: "changes", description: "Git diff or description of changes", required: true, }, ], load: async (args) => { return `Generate a concise but descriptive commit message for these changes:\n\n${args.changes}`; }, });
Completado automático de argumentos rápidos

Los mensajes pueden proporcionar autocompletado para sus argumentos:

server.addPrompt({ name: "countryPoem", description: "Writes a poem about a country", load: async ({ name }) => { return `Hello, ${name}!`; }, arguments: [ { name: "name", description: "Name of the country", required: true, complete: async (value) => { if (value === "Germ") { return { values: ["Germany"], }; } return { values: [], }; }, }, ], });
Completado automático de argumentos mediante enum

Si proporciona una matriz enum para un argumento, el servidor proporcionará automáticamente compleciones para el argumento.

server.addPrompt({ name: "countryPoem", description: "Writes a poem about a country", load: async ({ name }) => { return `Hello, ${name}!`; }, arguments: [ { name: "name", description: "Name of the country", required: true, enum: ["Germany", "France", "Italy"], }, ], });

Autenticación

Sova le permite authenticate clientes mediante una función personalizada:

import { AuthError } from "sova"; const server = new sova({ name: "My Server", version: "1.0.0", authenticate: ({ request }) => { const apiKey = request.headers["x-api-key"]; if (apiKey !== "123") { throw new Response(null, { status: 401, statusText: "Unauthorized", }); } // Whatever you return here will be accessible in the `context.session` object. return { id: 1, }; }, });

Ahora puedes acceder a los datos de la sesión autenticada en tus herramientas:

server.addTool({ name: "sayHello", execute: async (args, { session }) => { return `Hello, ${session.id}!`; }, });

Proporcionar instrucciones

Puede proporcionar instrucciones al servidor utilizando la opción instructions :

const server = new sova({ name: "My Server", version: "1.0.0", instructions: 'Instructions describing how to use the server and its features.\n\nThis can be used by clients to improve the LLM\'s understanding of available tools, resources, etc. It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt.', });

Sesiones

El objeto session es una instancia de sovaSession y describe sesiones de cliente activas.

server.sessions;

Asignamos una nueva instancia de servidor para cada conexión de cliente para permitir la comunicación 1:1 entre un cliente y el servidor.

Eventos de servidor tipificados

Puedes escuchar los eventos emitidos por el servidor utilizando el método on :

server.on("connect", (event) => { console.log("Client connected:", event.session); }); server.on("disconnect", (event) => { console.log("Client disconnected:", event.session); });

sovaSession

sovaSession representa una sesión de cliente y proporciona métodos para interactuar con el cliente.

Consulte Sesiones para obtener ejemplos de cómo obtener una instancia sovaSession .

requestSampling

requestSampling crea una solicitud de muestreo y devuelve la respuesta.

await session.requestSampling({ messages: [ { role: "user", content: { type: "text", text: "What files are in the current directory?", }, }, ], systemPrompt: "You are a helpful file system assistant.", includeContext: "thisServer", maxTokens: 100, });

clientCapabilities

La propiedad clientCapabilities contiene las capacidades del cliente.

session.clientCapabilities;

loggingLevel

La propiedad loggingLevel describe el nivel de registro establecido por el cliente.

session.loggingLevel;

roots

La propiedad roots contiene las raíces establecidas por el cliente.

session.roots;

server

La propiedad server contiene una instancia del servidor MCP que está asociado con la sesión.

session.server;

Eventos de sesión tipificados

Puedes escuchar los eventos emitidos por la sesión utilizando el método on :

session.on("rootsChanged", (event) => { console.log("Roots changed:", event.roots); }); session.on("error", (event) => { console.error("Error:", event.error); });

Ejecución de su servidor

Prueba con mcp-cli

La forma más rápida de probar y depurar su servidor es con sova dev :

npx sova dev server.js npx sova dev server.ts

Esto ejecutará su servidor con mcp-cli para probar y depurar su servidor MCP en la terminal.

Inspeccionar con MCP Inspector

Otra forma es utilizar el MCP Inspector para inspeccionar su servidor con una interfaz web:

npx sova inspect server.ts

Preguntas frecuentes

¿Cómo utilizar con Claude Desktop?

Siga la guía https://modelcontextprotocol.io/quickstart/user y agregue la siguiente configuración:

{ "mcpServers": { "my-mcp-server": { "command": "npx", "args": ["tsx", "/PATH/TO/YOUR_PROJECT/src/index.ts"], "env": { "YOUR_ENV_VAR": "value" } } } }

Expresiones de gratitud

-
security - not tested
A
license - permissive license
-
quality - not tested

hybrid server

The server is able to function both locally and remotely, depending on the configuration or use case.

Un marco de TypeScript para crear servidores MCP con funciones para sesiones de cliente, autenticación, contenido de imagen/audio y eventos de servidor tipificados.

  1. Características
    1. Instalación
      1. Inicio rápido
        1. SSE
      2. Conceptos básicos
        1. Herramientas
        2. Recursos
        3. Plantillas de recursos
        4. Indicaciones
        5. Autenticación
        6. Proporcionar instrucciones
        7. Sesiones
        8. Eventos de servidor tipificados
      3. sovaSession
        1. requestSampling
        2. clientCapabilities
        3. loggingLevel
        4. roots
        5. server
        6. Eventos de sesión tipificados
      4. Ejecución de su servidor
        1. Prueba con mcp-cli
        2. Inspeccionar con MCP Inspector
      5. Preguntas frecuentes
        1. ¿Cómo utilizar con Claude Desktop?
      6. Expresiones de gratitud

        Related MCP Servers

        • A
          security
          A
          license
          A
          quality
          A TypeScript-based server that allows calling other MCP clients from your own MCP client, facilitating task delegation and context window offloading for enhanced multi-agent interactions.
          Last updated -
          3
          14
          JavaScript
          MIT License
          • Apple
        • A
          security
          F
          license
          A
          quality
          A TypeScript-based MCP server designed for experimentation and integration with Calude Desktop and Cursor IDE, offering a modular playground for extending server capabilities.
          Last updated -
          2
          1,133
          4
          JavaScript
        • -
          security
          F
          license
          -
          quality
          A TypeScript framework for building Model Context Protocol (MCP) servers with automatic discovery and loading of tools, resources, and prompts.
          Last updated -
          67
          TypeScript
          • Apple
        • -
          security
          F
          license
          -
          quality
          A simple TypeScript library for creating Model Context Protocol (MCP) servers with features like type safety, parameter validation, and a minimal code API.
          Last updated -
          1
          TypeScript
          MIT License

        View all related MCP servers

        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/noname9091/myrcael'

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