Construir un servidor MCP: Python sin estado y TypeScript
Type: Build
Languages: Python, TypeScript
Prerequisites: Phase 13, Lesson 06
Time: ~85 minutes
Objetivos de aprendizaje
- Implementación obligatoria
server/discoverpara MCP2026-07-28¿ Qué ? - Valida la versión del protocolo y las capacidades del cliente en cada solicitud.
- Exponer herramientas, recursos y instrucciones con ordenamiento de lista determinista.
- Regreso .
resultType, identidad del servidor, y indicios de caché sobre los resultados correctos. - Servir el mismo contrato sin estado sobre el estudio de línea nueva y limitada en Python y TypeScript.
El problema
Un servidor que almacena las capacidades del cliente después del primer mensaje es fácil de construir y difícil de operar. El mismo proceso puede servir a clientes secuenciales. Una solicitud remota puede aterrizar en un trabajador diferente. Una declaración de capacidad obsoleta puede filtrar el comportamiento a través de los límites de autorización.
MCP 2026-07-28La aplicación puede mantener notas duraderas, trabajos o manipulaciones de estado explícito. Lo que no puede mantener es el estado de protocolo oculto que cambia la forma en que se descifre una solicitud posterior.
Esta lección construye un servidor de notas dos veces. Las versiones de Python y TypeScript usan solo sus bibliotecas estándar para el núcleo del protocolo. Ambos exponen los mismos métodos y aplican el mismo contrato de cable.
El concepto
El moderno bucle de envío
textread one JSON-RPC line
parse the envelope
if it is a notification, do not respond
validate params._meta for this request
route by method
wrap success with resultType and serverInfo
write one JSON-RPC response line
forget request-scoped metadataTres reglas del estudio siguen siendo importantes:
- Escriba sólo mensajes JSON-RPC a stdout. Envía diagnósticos a stderr.
- Delimitar los mensajes con una línea nueva y vertiente cada respuesta.
- Salir inmediatamente cuando el stdin llegue a la Oficina de Ejecuciones.
La vida útil del proceso es una vida útil del transporte.
Validación de la solicitud
Cada solicitud debe contener:
json{
"params": {
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {},
"io.modelcontextprotocol/clientInfo": {
"name": "notes-client",
"version": "1.0.0"
}
}
}
}Se requieren los dos primeros campos. clientInfoSe recomienda validar una forma de identidad actual, pero no tratarla como autenticación.
Si la versión no está soportada, devuelva el código -32022conrequestedy supported. Los metadatos de la solicitud faltantes son parámetros inválidos, código -32602Nunca llenes los campos que faltan de una llamada anterior.
Descubrimiento obligatorio
Los servidores modernos deben implementar server/discover. Un resultado completo de descubrimiento incluye versiones modernas compatibles, capacidades, instrucciones opcionales, sugerencias de caché y la identidad del servidor en el resultado _meta¿Qué es esto ?
json{
"resultType": "complete",
"supportedVersions": ["2026-07-28"],
"capabilities": {
"tools": {"listChanged": false},
"resources": {"listChanged": false, "subscribe": false},
"prompts": {"listChanged": false}
},
"ttlMs": 3600000,
"cacheScope": "public",
"_meta": {
"io.modelcontextprotocol/serverInfo": {
"name": "notes-server",
"version": "2.0.0"
}
}
}Discovery no desbloquea el servidor. Un cliente puede llamartools/listsin llamar descubrimiento porquetools/listya contiene los mismos metadatos de la solicitud.
Herramientas
tools/listEl orden estable mejora la caché de la respuesta y mantiene el contexto del modelo estable.ttlMsy cacheScope¿ Qué ?
tools/calldevuelve bloques de contenido y isError. Utilice un error JSON-RPC cuando el envase del protocolo o los parámetros del método son inválidos.isError: truecuando se ejecuta una invocación de herramienta válida pero la herramienta en sí misma falla.
Las anotaciones de las herramientas siguen siendo indicios, no ejecuciones:
readOnlyHintdestructiveHintidempotentHintopenWorldHint
El host debe usarlos para la confirmación y presentación. El servidor debe seguir aplicando la autorización real.
Recursos
resources/listdevuelve descriptores de URI estables. resources/readdevuelve el contenido escrito. ambos son caché en 2026-07-28, por lo que ambos incluyenttlMsy cacheScope¿ Qué ?
UsarcacheScope: "private"Una caché compartida no debe reutilizar una respuesta privada en contextos de autorización.
La entrega de cambios moderna no utiliza resources/subscribeUn cliente abre .subscriptions/listeny las peticiones resourceSubscriptionsLa lección 10 construye ese flujo.
Las instrucciones
prompts/listes cachéable y determinista. prompts/getEl resultado de la solicitud de solicitud de presentación está completo, pero no es uno de los resultados cachéables o de lectura que requieren sugerencias de caché.
Cada resultado exitoso es escrito
Los ejemplos utilizan un envase para cada éxito:
pythondef complete(payload):
return {
"resultType": "complete",
**payload,
"_meta": {SERVER_INFO_KEY: SERVER_INFO},
}Lista, lectura y manipulador de descubrimientos añadir ttlMsAdemáscacheScopeLa centralización de este envoltorio evita que un manipulador omita silenciosamente los campos de resultados modernos.
No se iniciaron solicitudes del servidor
Un servidor moderno puede enviar notificaciones relacionadas con una solicitud de cliente o notificaciones en una apertura de cliente subscriptions/listenNo debe enviar su propia solicitud JSON-RPC.
Cuando un manipulador necesita muestreo, elicitación o entrada de raíces, devuelve un input_requiredEl cliente cumple las solicitudes de entrada integradas y vuelve a probar el método original con un nuevo ID de solicitud.
Compatibilidad explícita con el legado
Un servidor de doble era también puede implementar la 2025-11-25El sistema de control de la carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de carga de_metalos campos están presentes y el comportamiento legado cuando recibe initialize¿ Qué ?
No ponga un 2026-07-28No se puede hacer un sello de la mano.resultTypeEl código de esta lección es deliberadamente moderno sólo para que sus invariantes permanezcan visibles.
Usalo
Ejecutar la demostración y pruebas finitas del servidor Python:
bashcd code
python3 main.py --demo
python3 -m unittest discover tests -vEjecutar el puerto TypeScript con un ejecutor TypeScript:
bashnpx tsx main.ts --demoLa demostración envía .server/discover, enumera cada primitivo, invoca herramientas y muestra un error de versión no soportado.
Envío
Esta lección nos lleva .outputs/skill-mcp-server-scaffolder.mdProduce un plan de servidor moderno con un contrato de descubrimiento, validación por solicitud, listas deterministas cachéables y un adaptador heredado aislado opcional.
Los ejercicios
- Eliminar las capacidades de una solicitud y demostrar que el servidor no reutiliza la declaración de la solicitud anterior.
- Revierten el
TOOLS¿ Qué ?PROMPTSConfirmar que todos los resultados de la lista permanecen estables. - Añadir un destructivo
notes_deleteLa aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la aplicación de la ley es.destructiveHintsólo como una pista de experiencia. - Añadir
resources/templates/listconttlMs¿ Qué ?cacheScope, y el orden determinista. - Construir un adaptador separado para
2025-11-25Añadir pruebas que demuestren que una solicitud moderna nunca entra.
Términos clave
| Term | Meaning |
|---|---|
| Stateless server | Handles each request from its own metadata without protocol-session memory |
server/discover | Mandatory modern method that advertises versions and capabilities |
| Complete result | Successful modern result with resultType: "complete" |
| Cacheable result | Discovery, list, or resource-read result with ttlMs and cacheScope |
| Deterministic list | Same logical registry produces the same item order |
| Server identity | Recommended io.modelcontextprotocol/serverInfo in result _meta |
| Tool error | Valid tool call that returns content with isError: true |
| Protocol error | Invalid JSON-RPC or MCP request returned through error |
Leer más
- MCP Specification 2026-07-28
- MCP Server Discovery
- MCP Tools
- MCP Resources
- MCP Prompts
- MCP stdio Transport
This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.
Browse the complete course catalog or open this lesson on GitHub.