Cómo Construir tu Primer Servidor MCP con Novita AI

Cómo Construir tu Primer Servidor MCP con Novita AI

El Protocolo de Contexto del Modelo (MCP) se está convirtiendo rápidamente en uno de los conceptos más comentados en el mundo de la IA. A pesar de ser relativamente nuevo, ya ha captado la atención de desarrolladores y grandes empresas tecnológicas por igual.

Creado por Anthropic, MCP es un protocolo estándar abierto, similar a otros protocolos como HTTP. Sin embargo, mientras HTTP está diseñado para conectar usuarios a recursos a través de un servidor y un navegador web (o cliente HTTP), MCP está diseñado para conectar Modelos de Lenguaje Grandes (LLMs) a herramientas y datos externos.

Supongamos que tienes un chatbot y quieres que pueda acceder a GitHub. Quieres que tus usuarios usen el chatbot para crear issues, cerrarlos, agregar comentarios o ver pull requests existentes. Para que tu chatbot pueda realizar todas estas acciones, necesitará acceso a herramientas.

Aquí es donde entra un servidor MCP.

El servidor MCP aloja varias herramientas que el chatbot puede llamar usando su LLM interno para obtener acceso a estas capacidades. En el caso de GitHub, podemos usar el servidor MCP oficial de GitHub para realizar todas las tareas mencionadas anteriormente.

En este artículo, construiremos un servidor MCP usando la API de Novita. Con este servidor MCP, cualquier aplicación que soporte el protocolo MCP podrá acceder a todos los LLMs disponibles en Novita, generar imágenes, crear videos y realizar síntesis de voz.

Comprendiendo la Arquitectura MCP

La Arquitectura MCP

La Arquitectura MCP [fuente]

La arquitectura MCP es similar a la configuración típica de cliente-servidor. Tiene cuatro componentes importantes:

  • Cliente MCP
  • Servidor MCP
  • Transportes MCP
  • Host MCP

Cliente MCP

El cliente MCP actúa como la puerta de enlace de comunicación entre la aplicación de IA y un servidor MCP. Una vez establecida esta comunicación, la aplicación puede acceder a todas las herramientas y recursos que posee el servidor.

Servidor MCP

El servidor MCP aloja todas las herramientas y datos que el cliente puede proporcionar a la aplicación de IA. Alberga lo siguiente:

  • Herramientas
  • Recursos
  • Prompts

Herramientas

Las herramientas proporcionan capacidades externas a los LLMs que normalmente no tendrían, como la capacidad de decir la hora, actualizar y leer de una base de datos, obtener el clima y más. Estas herramientas son esencialmente funciones definidas por el programador que se pueden llamar.

Un servidor MCP puede alojar varias herramientas. Estas herramientas permiten que el LLM funcione como un agente, debido a las acciones que puede realizar.

Las herramientas son gestionadas por el agente y pueden implicar supervisión humana opcional durante su uso. Son similares a las solicitudes POST en el protocolo HTTP porque realizan efectos secundarios.

Recursos

Los recursos son piezas de información de solo lectura que proporcionan datos a la aplicación de IA. Son análogos a las solicitudes GET de HTTP, ya que no están destinados a causar efectos secundarios. Los recursos están destinados a ser gestionados por la aplicación, que decide cómo los usuarios o el agente interactuarán con ellos. Ejemplos de recursos incluyen contenidos de archivos, respuestas de API y registros de bases de datos.

Prompts

Los servidores MCP pueden alojar plantillas de prompts. Estos prompts permiten al usuario obtener prompts del servidor MCP, que luego se pueden usar en la aplicación de IA. Los prompts son gestionados por el usuario, quien decide qué prompts proporcionar al agente.

Todos estos conforman el servidor MCP. En este artículo, solo implementaremos herramientas, ya que son la parte más utilizada del servidor MCP.

Transporte MCP

El transporte MCP se refiere al método mediante el cual el cliente MCP se comunica con el servidor MCP. Esta comunicación puede ocurrir localmente, cuando tanto el cliente como el servidor se ejecutan en la misma máquina, o de forma remota, cuando están en dispositivos separados. MCP actualmente soporta dos mecanismos de transporte principales:

  • STDIO: En este modo, el servidor y el cliente MCP se ejecutan en la misma máquina y se comunican a través de la entrada y salida estándar.
  • SSE: En este modo, el servidor MCP se ejecuta sobre HTTP. Se utiliza HTTP POST para enviar mensajes al servidor, mientras que los Eventos Enviados por el Servidor (SSE) se utilizan para enviar mensajes desde el servidor al cliente.
  • HTTP Transmisible: Este modo también usa HTTP pero se basa en solicitudes HTTP GET y POST. Recurre a SSE solo cuando necesita transmitir múltiples mensajes desde el servidor al cliente.

Entrada/Salida Estándar

Transporte Usando STDIO

Transporte MCP Usando STDIO [fuente]

Pie: Transporte MCP Usando STDIO [fuente]

El cliente y servidor MCP pueden comunicarse a través de la Entrada y Salida Estándar (STDIO). Al usar STDIO, tanto el cliente como el servidor se ejecutan en la misma máquina. El cliente escribe todas las solicitudes en stdin, mientras que el servidor escribe la respuesta en stdout.

HTTP Transmisible

Transporte MCP Usando HTTP Transmisible

Transporte MCP Usando HTTP Transmisible [fuente]

HTTP Transmisible permite que el cliente y servidor MCP se comuniquen a través de HTTP. Utiliza el método HTTP POST para enviar solicitudes desde el cliente y recibir respuestas del servidor. Cuando es necesario, puede cambiar opcionalmente a Eventos Enviados por el Servidor (SSE) para transmitir mensajes desde el servidor al cliente.

HTTP Transmisible es adecuado para la comunicación remota entre un cliente y un servidor, y sirve como reemplazo del transporte MCP SSE obsoleto.

HOST MCP

Donde MCP difiere de la arquitectura cliente-servidor típica es en el rol del host. La especificación MCP lo define como una arquitectura cliente-host-servidor. Esto se debe a que el cliente y el host conforman la interfaz de la arquitectura. El host MCP consta de dos componentes importantes:

  • El(los) cliente(s) MCP
  • Un modelo de lenguaje grande

El trabajo del cliente MCP es obtener herramientas, recursos y prompts que el LLM requiere del servidor. Una vez que se han recopilado los recursos, se colocan dentro del contexto del modelo. Un solo host MCP puede tener múltiples clientes, cada uno conectado a su propio servidor MCP individual.

El host MCP también puede considerarse la aplicación en la que el usuario está trabajando, que puede realizar otras funciones. Por ejemplo, Claude Desktop es un host MCP que funciona como chatbot, Cursor es un host MCP que también sirve como IDE, y Claude Code es un host MCP diseñado como un agente de codificación de IA.

Trabajando con un Servidor MCP

Antes de comenzar a construir nuestro servidor MCP, veamos cómo podemos trabajar con un servidor MCP existente. Usaremos el SDK de MCP para Python para interactuar con el novita-mcp-server.

Al momento de escribir este artículo, el novita-mcp-server proporciona las siguientes herramientas:

  • Una herramienta para listar todos los clusters de Novita
  • Una herramienta para listar los productos de instancias GPU de Novita
  • Una herramienta para listar todas las instancias GPU en ejecución
  • Una herramienta para crear nuevas instancias GPU

Confirmemos esto escribiendo un script simple que se conecte al servidor MCP a través de STDIO y liste todas las herramientas disponibles en el servidor.

Para comenzar, instalemos el SDK de MCP: pip install "mcp[cli]"

Después de instalar el SDK, podemos crear un archivo llamado client.py. Luego, haremos todas las importaciones necesarias:

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
import os

Luego usaremos StdioServerParameters para configurar nuestros parámetros para lanzar nuestro servidor MCP a través de stdio. El novita-mcp-server está implementado en Node.js, por lo que para usarlo, necesitamos ejecutarlo con el comando npx. También necesitamos almacenar nuestra clave API de Novita en la variable de entorno NOVITA_API_KEY.

# Create server parameters for stdio connection
server_params = StdioServerParameters(
   command="npx",
   args=["-y", "@novitalabs/novita-mcp-server"],
   env={"NOVITA_API_KEY":  os.environ["NOVITA_API_KEY"]},
)

A continuación, creemos una función asíncrona. Dentro de la función, pasaremos los parámetros del servidor a la función stdio_client, que creará un contexto que devuelve flujos de lectura y escritura. Estos flujos nos permiten leer y escribir en stdio, respectivamente.

async def run():
   async with stdio_client(server_params) as (read, write):
       async with ClientSession(read, write) as session:
           # Initialize the connection
           await session.initialize()

           # List available tools
           tools = await session.list_tools()
           print("Available tools:", tools)

Estos flujos luego se utilizan para crear una sesión con la clase ClientSession. Una vez que hemos creado nuestra sesión, podemos inicializarla y luego listar todas las herramientas en el servidor.

Para ejecutar este programa, solo necesitamos usar la biblioteca asyncio para llamar a la función run. Aquí está el código completo:

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
import os

# Create server parameters for stdio connection
server_params = StdioServerParameters(
   command="npx",
   args=["-y", "@novitalabs/novita-mcp-server"],
   env={"NOVITA_API_KEY":  os.environ["NOVITA_API_KEY"]},
)

async def run():
   async with stdio_client(server_params) as (read, write):
       async with ClientSession(read, write) as session:
           # Initialize the connection
           await session.initialize()

           # List available tools
           tools = await session.list_tools()
           print("Available tools:", tools)

if __name__ == "__main__":
   import asyncio

   asyncio.run(run())

Al ejecutar el script, podemos ver todas las herramientas en el servidor MCP de Novita.

Este ejemplo demuestra cómo puedes usar el cliente MCP. Podríamos agregar un LLM a este ejemplo y transformarlo efectivamente en un host, pero ese no es el enfoque de este tutorial. El enfoque es construir un servidor MCP.

Construyendo un Servidor MCP con FastMCP

En el SDK de MCP para Python, hay dos formas de construir servidores MCP. Un método es usando el servidor de bajo nivel, y el otro es usando FastMCP. FastMCP es una clase dentro del SDK de MCP para Python que se inspira en FastAPI para facilitar la creación de servidores MCP.

Usemos FastMCP para crear nuestro servidor MCP. Antes de comenzar, consideremos las funcionalidades de nuestro servidor MCP. El servidor MCP actual de Novita solo implementa herramientas que manejan la gestión de GPU. Intentemos construir un servidor en torno a la API de Novita que vaya más allá de la gestión de GPU.

En cambio, construyamos un servidor MCP que proporcione acceso a los modelos en la plataforma Novita. Nuestro servidor MCP solo tendrá herramientas, sin recursos ni prompts. Aquí hay una lista de herramientas que tendrá el servidor:

  • list_models: Esta herramienta listará todos los modelos de lenguaje grande en la plataforma Novita.
  • get_model: Esta herramienta recuperará y usará un modelo de lenguaje grande específico.
  • text2image: Esta herramienta generará imágenes a partir de un prompt dado.
  • task_result: Esta herramienta se usará para obtener el estado de una tarea en ejecución a través de su ID.
  • text_to_speech: Esta herramienta convertirá el texto proporcionado en voz.
  • generate_video: Esta herramienta generará un video a partir de un prompt dado.

Ahora que conocemos las herramientas que construiremos, comencemos a crear nuestro servidor. Primero, crea un archivo llamado server.py y agrega el siguiente código:

import os
import sys
from mcp.server.fastmcp import FastMCP
import requests
import uvicorn
from starlette.applications import Starlette
from starlette.routing import Mount

base_url = "https://api.novita.ai/v3"

headers = {
   "Content-Type": "application/json",
   "Authorization": f"Bearer {os.environ['NOVITA_API_KEY']}"
}

mcp = FastMCP("Novita_API")

Con esto, hemos importado los módulos necesarios, definido nuestra URL base y encabezados para la API, y creado una instancia de la clase FastMCP. Pasemos a crear nuestras herramientas.

Listar modelos

Para crear una herramienta en FastMCP, necesitamos decorar una función con el método tool de la instancia de FastMCP. La función list_models hace una llamada al endpoint de listar modelos para obtener la lista de modelos. La respuesta se formatea adecuadamente para que pueda pasarse al LLM que la llamó.

@mcp.tool()
def list_models() -> str:
   """
   Lista todos los modelos disponibles de la API de Novita.
   """

   url = base_url + "/openai/models"

   response = requests.request("GET", url, headers=headers)
   data = response.json()["data"]
  
   text = ""
   for i, model in enumerate(data, start=1):
       text += f"Model id: {model['id']}\
"
       text += f"Model description: {model['description']}\
"
       text += f"Model type: {model['model_type']}\
\
"
  
   return text

El docstring en la herramienta sirve como una forma de describir la funcionalidad de la herramienta para que el LLM pueda entender para qué se usa.

Obtener modelo

Esta herramienta se usa para acceder a los LLMs desplegados en Novita. Toma el ID del modelo y el prompt para el modelo. Utiliza el endpoint de completaciones de chat de Novita.

@mcp.tool()
def get_model(model_id: str, message):
   """
   Proporciona un ID de modelo y un mensaje para obtener una respuesta de la API de Novita.
   """

   url = base_url + "/openai/chat/completions"

   payload = {
       "model": model_id,
       "messages": [
           {
               "content": message,
               "role": "user",
           }
       ],
       "max_tokens": 200,
       "response_format": {
           "type": "text",
       },
   }

   response = requests.request("POST", url, json=payload, headers=headers)
  
   content = response.json()["choices"][0]["message"]["content"]

   return content

Texto a Imagen

Esta herramienta genera imágenes a partir de un prompt. Utiliza el endpoint de texto a imagen de Novita internamente. Este endpoint es asíncrono, por lo que no devuelve la imagen inmediatamente; en su lugar, devuelve un ID de tarea.

@mcp.tool()
def text2Image(prompt):
   """
   Genera una imagen a partir de un prompt de texto usando la API de Novita.
   """

   url = base_url + "/async/txt2img"

   payload = {
       "request": {
           "model_name": "sd_xl_base_1.0.safetensors",
           "prompt": prompt,
           "width": 1024,
           "height": 1024,
           "image_num": 1,
           "steps": 20,
           "clip_skip": 1,
           "sampler_name": "Euler a",
           "guidance_scale": 7.5,
       },
       "extra": {
           "response_image_type": "jpeg"
       }
   }

   response = requests.request("POST", url, json=payload, headers=headers)

   return response.json()["task_id"]

Resultado de tarea

Esta herramienta se usa para obtener el estado de una tarea en curso en la API de Novita. Solo toma el ID de la tarea. Por ejemplo, la herramienta de Imagen genera una imagen de forma asíncrona y devuelve un ID de tarea al host. El usuario puede pedirle al host que recupere el resultado de esa tarea usando la herramienta Resultado de tarea.

@mcp.tool()
def task_result(task_id: str):
   """
   Obtiene el estado actual de una tarea en ejecución usando su ID de tarea.
   """

   url = base_url + f'/async/task-result?task_id={task_id}'

   response = requests.request("GET", url, headers=headers)
   return response.json()

Generar Video

Esta herramienta genera videos usando el endpoint de texto a video Kling AI V1.6 de la API de Novita. Al igual que la herramienta de Imagen, genera videos de forma asíncrona y devuelve un ID de tarea.

@mcp.tool()
def generateVideo(prompt: str):
   """
   Genera una imagen usando un prompt.
   """

   url = base_url + "/async/kling-v1.6-t2v"

   payload = {
       "mode": "Standard",
       "prompt": prompt,
       "negative_prompt": "low quality",
       "guidance_scale": 0.6
   }

   response = requests.post(url, json=payload, headers=headers)
  
   return response.json()

Texto a Voz

Esta herramienta genera voz usando el endpoint de texto a voz de la API de Novita. También es un endpoint asíncrono, por lo que devuelve un ID de tarea.

@mcp.tool()
def textToSpeech(text, voice_id) -> str:
   """
   Genera voz usando texto y un ID de voz.
   Devuelve el ID de tarea de la voz generada.
  
   Los IDs de voz disponibles son:
   - Emily
   - James
   - Olivia
   - Michael
   - Sarah
   - John
   """

   url = base_url + "/async/txt2speech"

   payload = {
       "request": {
           "voice_id": voice_id,
           "language": "en-US",
           "texts": [text]
       }
   }

   response = requests.post(url, json=payload, headers=headers)
  
   return response.json()["task_id"]

Con todas nuestras herramientas definidas, podemos configurar nuestro mecanismo de transporte. Vamos a usar stdio.

if __name__ == "__main__":
   # Run using stdio transport
   mcp.run(transport="stdio")

Ahora podemos juntar todo el código:

import os
import sys

from mcp.server.fastmcp import FastMCP

import requests

import uvicorn

from starlette.applications import Starlette
from starlette.routing import Mount

base_url = "https://api.novita.ai/v3"

headers = {
   "Content-Type": "application/json",
   "Authorization": f"Bearer {os.environ['NOVITA_API_KEY']}"
}

mcp = FastMCP("Novita_API")

@mcp.tool()
def list_models() -> str:
   """
   Lista todos los modelos disponibles de la API de Novita.
   """

   url = base_url + "/openai/models"

   response = requests.request("GET", url, headers=headers)
   data = response.json()["data"]
  
   text = ""
   for i, model in enumerate(data, start=1):
       text += f"Model id: {model['id']}\n"
       text += f"Model description: {model['description']}\n"
       text += f"Model type: {model['model_type']}\n\n"
  
   return text

@mcp.tool()
def get_model(model_id: str, message) -> str:
   """
   Proporciona un ID de modelo y un mensaje para obtener una respuesta de la API de Novita.
   """

   url = base_url + "/openai/chat/completions"

   payload = {
       "model": model_id,
       "messages": [
           {
               "content": message,
               "role": "user",
           }
       ],
       "max_tokens": 200,
       "response_format": {
           "type": "text",
       },
   }

   response = requests.request("POST", url, json=payload, headers=headers)
  
   content = response.json()["choices"][0]["message"]["content"]

   return content

@mcp.tool()
def text2Image(prompt: str) -> str:
   """
   Genera una imagen a partir de un prompt de texto usando la API de Novita.
   """

   url = base_url + "/async/txt2img"

   payload = {
       "request": {
           "model_name": "sd_xl_base_1.0.safetensors",
           "prompt": prompt,
           "width": 1024,
           "height": 1024,
           "image_num": 1,
           "steps": 20,
           "clip_skip": 1,
           "sampler_name": "Euler a",
           "guidance_scale": 7.5,
       },
       "extra": {
           "response_image_type": "jpeg"
       }
   }

   response = requests.request("POST", url, json=payload, headers=headers)

   return response.json()["task_id"]

@mcp.tool()
def task_result(task_id: str) -> str:
   """
   Obtiene el estado actual de una tarea en ejecución usando su ID de tarea.
   """

   url = base_url + f'/async/task-result?task_id={task_id}'

   response = requests.request("GET", url, headers=headers)
   return response.json()

@mcp.tool()
def generateVideo(prompt: str) -> str:
   """
   Genera una imagen usando un prompt.
   """

   url = base_url + "/async/kling-v1.6-t2v"

   payload = {
       "mode": "Standard",
       "prompt": prompt,
       "negative_prompt": "low quality",
       "guidance_scale": 0.6
   }

   response = requests.post(url, json=payload, headers=headers)
  
   return response.json()["task_id"]

@mcp.tool()
def textToSpeech(text, voice_id) -> str:
   """
   Genera voz usando texto y un ID de voz.
   Devuelve el ID de tarea de la voz generada.
  
   Los IDs de voz disponibles son:
   - Emily
   - James
   - Olivia
   - Michael
   - Sarah
   - John
   """

   url = base_url + "/async/txt2speech"

   payload = {
       "request": {
           "voice_id": voice_id,
           "language": "en-US",
           "texts": [text]
       }
   }

   response = requests.post(url, json=payload, headers=headers)
  
   return response.json()["task_id"]

if __name__ == "__main__":
   # Run using stdio transport
   mcp.run(transport="stdio")

Podemos probar nuestro servidor MCP en cualquier host MCP, como Claude Desktop, Cursor o VS Code, usando la siguiente configuración:

{
     "command": "python",
     "args": [
       "path/to/server.py" 
     ],
     "env": {
       "NOVITA_API_KEY": "sk_...."
     }
   }

También podemos usar el script de cliente que desarrollamos anteriormente para probar nuestro servidor.

Usando el Servidor de Bajo Nivel de MCP

El servidor que construimos en la sección anterior utiliza la clase FastMCP, que proporciona una interfaz de alto nivel para construir servidores MCP. También puedes construir servidores MCP usando el servidor de bajo nivel, que te da control granular sobre el protocolo MCP. Veamos cómo podemos modificar el servidor de la sección anterior para usar el servidor de bajo nivel.

Gestión de Herramientas

Una de las cosas que FastMCP hace es gestionar las herramientas que defines. Cuando decoras una función con el decorador tool de la clase FastMCP, FastMCP agrega esa herramienta a una lista. Cuando el agente solicita esa herramienta, FastMCP la obtiene, la llama y luego envía el resultado de vuelta al agente.

Con el servidor de bajo nivel, podemos gestionar este proceso nosotros mismos usando el decorador call_tool.

@app.call_tool()
async def manage_tool(name: str, arguments: dict ) -> list[types.TextContent]:
   if name == "list_models":
       return await list_models_tool()
  
   if name == "get_model":
       return await get_model_tool(arguments)
  
   else:
       raise ValueError(f"Unknown tool: {name}")

El código anterior muestra una función que ha sido decorada con el método call_tool. Cuando el agente llama a una herramienta, pasa el nombre de la herramienta y cualquier argumento que la herramienta espere. Usando el nombre de la función, podemos determinar qué herramienta desea llamar el agente y ejecutarla.

También podemos gestionar el listado de las herramientas usando el servidor de bajo nivel, a diferencia de FastMCP, que lo hace automáticamente cuando una función ha sido decorada con el método tool.

@app.list_tools()
async def list_tools() -> list[types.Tool]:
   return [
       types.Tool(
           name="list_models",
           description="Lista todos los modelos disponibles de la API de Novita.",
           inputSchema={"type": "object", "properties": {}},
       ),
       types.Tool(
           name="get_model",
           description="Proporciona un ID de modelo y un mensaje para obtener una respuesta de la API de Novita.",
           inputSchema={
               "type": "object",
               "required": ["model_id", "message"],
               "properties": {
                   "model_id": {
                       "type": "string",
                       "description": "El ID del modelo a usar.",
                   },
                   "message": {
                       "type": "string",
                       "description": "El mensaje de entrada a enviar.",
                   },
               },
           },
       ),
   ]

Aquí está el código completo del servidor de bajo nivel. Contiene dos herramientas y puede comunicarse con el cliente a través de la entrada y salida estándar.

import os
import asyncio
import requests
from mcp.server.lowlevel import Server
from mcp.server.stdio import stdio_server
import mcp.types as types

base_url = "https://api.novita.ai/v3"

headers = {
   "Content-Type": "application/json",
   "Authorization": f"Bearer {os.environ['NOVITA_API_KEY']}"
}

app = Server("Novita_API")

async def list_models_tool():
   """
   Lista todos los modelos disponibles de la API de Novita.
   """
   url = base_url + "/openai/models"
   response = requests.get(url, headers=headers)
   data = response.json()["data"]

   text = ""
   for i, model in enumerate(data, start=1):
       text += f"Model id: {model['id']}\n"
       text += f"Model description: {model['description']}\n"
       text += f"Model type: {model['model_type']}\n\n"

   return [types.TextContent(type="text", text=text)]

async def get_model_tool(arguments: dict):
   """
   Dado un ID de modelo y un mensaje de usuario, obtiene una respuesta de la API de Novita.
   """
   model_id = arguments.get("model_id")
   message = arguments.get("message")

   if not model_id or not message:
       raise ValueError("Se requieren tanto 'model_id' como 'message'.")

   url = base_url + "/openai/chat/completions"

   payload = {
       "model": model_id,
       "messages": [
           {
               "content": message,
               "role": "user",
           }
       ],
       "max_tokens": 200,
       "response_format": {
           "type": "text",
       },
   }

   response = requests.post(url, json=payload, headers=headers)
   content = response.json()["choices"][0]["message"]["content"]
   return [types.TextContent(type="text", text=content)]

@app.call_tool()
async def manage_tool(name: str, arguments: dict ) -> list[types.TextContent]:
   if name == "list_models":
       return await list_models_tool()
  
   if name == "get_model":
       return await get_model_tool(arguments)
  
   else:
       raise ValueError(f"Unknown tool: {name}")

@app.list_tools()
async def list_tools() -> list[types.Tool]:
   return [
       types.Tool(
           name="list_models",
           description="Lista todos los modelos disponibles de la API de Novita.",
           inputSchema={"type": "object", "properties": {}},
       ),
       types.Tool(
           name="get_model",
           description="Proporciona un ID de modelo y un mensaje para obtener una respuesta de la API de Novita.",
           inputSchema={
               "type": "object",
               "required": ["model_id", "message"],
               "properties": {
                   "model_id": {
                       "type": "string",
                       "description": "El ID del modelo a usar.",
                   },
                   "message": {
                       "type": "string",
                       "description": "El mensaje de entrada a enviar.",
                   },
               },
           },
       ),
   ]

async def main():
   async with stdio_server() as streams:
       await app.run(streams[0], streams[1], app.create_initialization_options())

if __name__ == "__main__":
   asyncio.run(main())

El servidor de bajo nivel no solo te da control detallado sobre la gestión de herramientas; también proporciona control de bajo nivel sobre recursos, prompts y otras partes del protocolo MCP, como la gestión del ciclo de vida.

Conclusión

En este tutorial, construimos un servidor MCP que utiliza la API de Novita para mejorar las capacidades de cualquier host MCP. Has aprendido los conceptos básicos de lo que se necesita para crear un servidor MCP, incluyendo cómo definir y exponer herramientas.

Con esta base, puedes comenzar a explorar temas más avanzados como autenticación, despliegue de servidores MCP remotos y trabajo directo con la implementación de bajo nivel de Python.

También exploramos la API de Novita en sí misma y vimos sus capacidades, desde un conjunto diverso de modelos de lenguaje hasta herramientas de generación de video, audio e imágenes. Visita el Novita LLM Playground para probar otras API que no cubrimos, como los endpoints de edición de imágenes y rostros.

Novita AI es una plataforma de nube de IA que ofrece a los desarrolladores una manera fácil de desplegar modelos de IA usando nuestra API simple, al mismo tiempo que proporciona la nube de GPU confiable y asequible para construir y escalar.