Skip to main content
Glama

Myrcael

by noname9091

Просто МКП

Фреймворк TypeScript для создания MCP -серверов, способных обрабатывать клиентские сеансы.

[!ПРИМЕЧАНИЕ]

Реализацию на Python см. в sova .

Функции

Установка

npm install sova

Быстрый старт

[!ПРИМЕЧАНИЕ]

Существует множество реальных примеров использования sova в дикой природе. Смотрите примеры в Showcase .

import { sova } from "simply"; import { z } from "zod"; // Or any validation library that supports Standard Schema const server = new simply({ 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", });

Вот и все! У вас есть работающий MCP-сервер.

Вы можете протестировать сервер в терминале с помощью:

git clone https://github.com/nonameguy9091/simply.git cd simply 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

СШЭ

Server-Sent Events (SSE) предоставляют механизм для серверов, чтобы отправлять обновления в реальном времени клиентам через соединение HTTPS. В контексте MCP SSE в основном используется для включения удаленной связи MCP, позволяя получать доступ к MCP, размещенному на удаленной машине, и ретранслировать обновления по сети.

Вы также можете запустить сервер с поддержкой SSE:

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

Это запустит сервер и будет прослушивать соединения SSE по http://localhost:8080/sse .

Затем вы можете использовать SSEClientTransport для подключения к серверу:

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);

Основные концепции

Инструменты

Инструменты в MCP позволяют серверам предоставлять исполняемые функции, которые могут вызываться клиентами и использоваться LLM для выполнения действий.

sova использует спецификацию Standard Schema для определения параметров инструмента. Это позволяет вам использовать предпочтительную библиотеку проверки схемы (например, Zod, ArkType или Valibot), пока она реализует спецификацию.

Пример Зода:

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); }, });

Пример 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); }, });

Пример Валибота:

Valibot требует одноранговой зависимости @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); }, });
Возврат строки

execute может вернуть строку:

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

Последнее эквивалентно:

server.addTool({ name: "download", description: "Download a file", parameters: z.object({ url: z.string(), }), execute: async (args) => { return { content: [ { type: "text", text: "Hello, world!", }, ], }; }, });
Возврат списка

Если вы хотите вернуть список сообщений, вы можете вернуть объект со свойством 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" }, ], }; }, });
Возврат изображения

Используйте imageContent для создания объекта контента для изображения:

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(...) // ], // }; }, });

Функция imageContent принимает следующие параметры:

  • url : URL-адрес изображения.
  • path : путь к файлу изображения.
  • buffer : данные изображения в качестве буфера.

Необходимо указать только один из следующих параметров: url , path или buffer .

Приведенный выше пример эквивалентен следующему:

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", }, ], }; }, });
Возврат аудио

Используйте audioContent для создания объекта контента для аудио:

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(...) // ], // }; }, });

Функция audioContent принимает следующие параметры:

  • url : URL-адрес аудио.
  • path : Путь к аудиофайлу.
  • buffer : Аудиоданные как буфер.

Необходимо указать только один из следующих параметров: url , path или buffer .

Приведенный выше пример эквивалентен следующему:

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", }, ], }; }, });
Возврат комбинированного типа

Таким образом можно комбинировать различные типы и отправлять их обратно в ИИ.

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, // ], // }; // }, });
Ведение журнала

Инструменты могут регистрировать сообщения для клиента, используя объект log в объекте контекста:

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"; }, });

Объект log имеет следующие методы:

  • debug(message: string, data?: SerializableValue)
  • error(message: string, data?: SerializableValue)
  • info(message: string, data?: SerializableValue)
  • warn(message: string, data?: SerializableValue)
Ошибки

Ошибки, которые должны отображаться пользователю, следует выдавать как экземпляры 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"; }, });
Прогресс

Инструменты могут сообщать о ходе выполнения, вызывая reportProgress в объекте контекста:

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"; }, });
Аннотации инструментов

Согласно спецификации MCP (2025-03-26), инструменты могут включать аннотации, которые обеспечивают более расширенный контекст и контроль за счет добавления метаданных о поведении инструмента:

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); }, });

Доступные аннотации:

АннотацияТипПо умолчаниюОписание
titleнить-Понятное для человека название инструмента, полезное для отображения в пользовательском интерфейсе.
readOnlyHintбулевfalseЕсли true, это означает, что инструмент не изменяет свою среду.
destructiveHintбулевtrueЕсли true, инструмент может выполнять деструктивные обновления (имеет смысл только в том случае, если readOnlyHint имеет значение false)
idempotentHintбулевfalseЕсли true, повторный вызов инструмента с теми же аргументами не имеет дополнительного эффекта (имеет смысл только в том случае, если readOnlyHint имеет значение false)
openWorldHintбулевtrueЕсли это правда, инструмент может взаимодействовать с «открытым миром» внешних сущностей.

Эти аннотации помогают клиентам и магистрам права лучше понять, как использовать инструменты и чего ожидать при их использовании.

Ресурсы

Ресурсы представляют собой любые данные, которые сервер MCP хочет сделать доступными для клиентов. Это может включать:

  • Содержимое файла
  • Скриншоты и изображения
  • И многое другое

Каждый ресурс идентифицируется уникальным URI и может содержать текстовые или двоичные данные.

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

[!ПРИМЕЧАНИЕ]

load может возвращать несколько ресурсов. Это может быть использовано, например, для возврата списка файлов внутри каталога при чтении каталога.

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

Вы также можете вернуть двоичное содержимое в load :

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

Шаблоны ресурсов

Вы также можете определить шаблоны ресурсов:

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}`, }; }, });
Автодополнение аргумента шаблона ресурса

Предоставьте complete функции для аргументов шаблона ресурса, чтобы включить автоматическое завершение:

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}`, }; }, });

Подсказки

Подсказки позволяют серверам определять повторно используемые шаблоны подсказок и рабочие процессы, которые клиенты могут легко отображать для пользователей и LLM. Они предоставляют мощный способ стандартизации и совместного использования общих взаимодействий 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}`; }, });
Автоматическое завершение аргумента Prompt

Подсказки могут обеспечивать автоматическое завершение аргументов:

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: [], }; }, }, ], });
Автоматическое завершение аргумента с помощью enum

Если вы предоставите массив enum для аргумента, сервер автоматически предоставит дополнения для аргумента.

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"], }, ], });

Аутентификация

sova позволяет authenticate клиентов с помощью пользовательской функции:

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, }; }, });

Теперь вы можете получить доступ к аутентифицированным данным сеанса в своих инструментах:

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

Предоставление инструкций

Вы можете предоставить инструкции серверу, используя опцию 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.', });

Сессии

Объект session является экземпляром sovaSession и описывает активные клиентские сеансы.

server.sessions;

Мы выделяем новый экземпляр сервера для каждого клиентского подключения, чтобы обеспечить связь 1:1 между клиентом и сервером.

Типизированные серверные события

Вы можете прослушивать события, отправляемые сервером, используя метод on :

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

sovaSession

sovaSession представляет собой клиентский сеанс и предоставляет методы для взаимодействия с клиентом.

Примеры получения экземпляра sovaSession см. в разделе Сеансы .

requestSampling

requestSampling создает запрос выборки и возвращает ответ.

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

Свойство clientCapabilities содержит возможности клиента.

session.clientCapabilities;

loggingLevel

Свойство loggingLevel описывает уровень ведения журнала, установленный клиентом.

session.loggingLevel;

roots

Свойство roots содержит корни, установленные клиентом.

session.roots;

server

Свойство server содержит экземпляр сервера MCP, связанный с сеансом.

session.server;

Типизированные события сеанса

Вы можете прослушивать события, генерируемые сеансом, используя метод on :

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

Запуск вашего сервера

Тест с mcp-cli

Самый быстрый способ протестировать и отладить ваш сервер — использовать sova dev :

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

Это запустит ваш сервер с mcp-cli для тестирования и отладки вашего сервера MCP в терминале.

Осмотр с помощью MCP Inspector

Другой способ — использовать официальный MCP Inspector для проверки вашего сервера с помощью веб-интерфейса:

npx sova inspect server.ts

Часто задаваемые вопросы

Как использовать с Claude Desktop?

Следуйте руководству https://modelcontextprotocol.io/quickstart/user и добавьте следующую конфигурацию:

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

Благодарности

-
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.

Фреймворк TypeScript для создания серверов MCP с функциями клиентских сеансов, аутентификации, изображений/аудиоконтента и типизированных событий сервера.

  1. Функции
    1. Установка
      1. Быстрый старт
        1. СШЭ
      2. Основные концепции
        1. Инструменты
        2. Ресурсы
        3. Шаблоны ресурсов
        4. Подсказки
        5. Аутентификация
        6. Предоставление инструкций
        7. Сессии
        8. Типизированные серверные события
      3. sovaSession
        1. requestSampling
        2. clientCapabilities
        3. loggingLevel
        4. roots
        5. server
        6. Типизированные события сеанса
      4. Запуск вашего сервера
        1. Тест с mcp-cli
        2. Осмотр с помощью MCP Inspector
      5. Часто задаваемые вопросы
        1. Как использовать с Claude Desktop?
      6. Благодарности

        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