Mi IA no pudo ver mis archivos: construí un servidor MCP sin dependencia

. Las funciones se habían alargado demasiado y los nombres de las variables ya no tenían sentido. Cada vez que quería comentarios sobre un archivo, me detenía, abría el chat, copiaba todo y esperaba. Luego volví al editor, apliqué el cambio, abrí el siguiente archivo y lo hice de nuevo.

En algún momento lo conté. Seis archivos. Once pastas. Veinte minutos de cambio antes de escribir una sola línea nueva.

La solución obvia fue darle a la herramienta de inteligencia artificial acceso directo a la carpeta de mi proyecto. Fue entonces cuando me encontré con MCP (el protocolo de contexto modelo), que está diseñado exactamente para esto. Un servidor se ejecuta localmente, expone herramientas y el cliente de IA llama a esas herramientas directamente en lugar de esperar a que pegue las cosas.

Entonces miré las implementaciones existentes. La mayoría requiere FastAPI, uvicorn, LangChain o el SDK oficial de MCP. Antes de escribir una sola línea de lógica de negocios, tenía cinco paquetes en mi archivo de requisitos y un servidor que no estaba seguro de que se ejecutaría en Windows sin luchar.

Di un paso atrás y leí la especificación MCP real.[1]. El protocolo es JSON-RPC 2.0[2]sobre una capa de transporte. Un objeto JSON por línea. El cliente envía, el servidor responde. La especificación define exactamente dos transportes: stdio para conexiones locales de un solo cliente y HTTP con eventos enviados por el servidor para clientes concurrentes.

Ese es todo el protocolo.

Hice una pregunta diferente: ¿qué es lo que realmente necesita que la biblioteca estándar de Python no proporcione? sys.stdin, sys.stdout, http.server, subprocesos, cola, pathlib, json. Eso es todo. Ni una sola instalación de pip.

Este artículo es esa implementación: ambos transportes, un modelo de seguridad de producción, 50 pruebas y los números de su ejecución.

TL;DR

La mayoría de las implementaciones de MCP parecen más pesadas de lo que deberían. La especificación solo define dos transportes, stdio y HTTP/SSE, pero en la práctica generalmente están envueltos en marcos y dependencias adicionales.

Construí ambos transportes desde cero usando solo la biblioteca estándar de Python.

Se ejecuta como un único archivo con un indicador de tiempo de ejecución. Sin instalaciones, sin configuración.

Para trabajo local utiliza stdio con un único cliente. Cuando necesita simultaneidad, cambia a HTTP/SSE y maneja múltiples clientes sin cambiar nada más.

Debajo del capó, todo se mantiene consistente. Mismo despachador, mismas herramientas, mismo modelo de seguridad.

Debido a que afecta al sistema de archivos, agregué verificaciones estrictas de rutas desde el principio. Los patrones de escape comunes como ../../, trucos de enlaces simbólicos y rutas UNC de Windows están bloqueados.

5 clientes simultáneos. Menos de 50 ms de tiempo total de pared. Verificado en Windows 11, Python 3.12.6, solo CPU.

Código completo: https://github.com/Emmimal/local-mcp-server/

El error que dio forma a todo el diseño

Antes de la arquitectura, quiero contarles algo que casi me hizo renunciar a todo.

Al principio del desarrollo estaba probando la herramienta de búsqueda. Señalé el servidor a C:UsersAdmin y lo ejecuté buscando archivos Python. El servidor se inició. La demostración comenzó a ejecutarse. Luego siguió funcionando.

Treinta segundos. Un minuto. Cinco minutos. Pensé que había un bucle infinito en alguna parte. Revisé el código tres veces. Todo parecía correcto. Maté el proceso y reinicié. Mismo resultado.

Diez minutos después finalmente entendí lo que estaba pasando. La herramienta de búsqueda usaba rglob() de forma predeterminada. Lo apunté a todo mi directorio de usuarios y estaba escaneando todo: entornos virtuales, AppData, cada archivo almacenado en caché en la máquina. Decenas de miles de archivos, uno a la vez.

Maté el proceso y cambié una línea:

# Antes: recursivo de forma predeterminada, analiza todo en busca de coincidencias en target.rglob(patrón): # Después: superficial de manera predeterminada, opta por la recursividad para coincidencias en target.glob(patrón):

Y convertimos recursive=False en el parámetro predeterminado. El cliente tiene que pasar recursive=True explícitamente. El servidor nunca escaneará de forma recursiva por sí solo.

Ese único cambio es la razón por la que la búsqueda se completa hoy en menos de 30 ms en una carpeta de proyecto real en lugar de ejecutarse para siempre. Y se convirtió en la regla que apliqué en todas partes: ningún comportamiento que destruya el rendimiento debería ser el predeterminado.

¿Qué es realmente MCP?

El protocolo de contexto modelo[1]es una forma estandarizada para que los clientes de IA llamen a herramientas en servidores externos. Utiliza JSON-RPC 2.0[2]como su formato de mensaje.

En la práctica, esto significa que los clientes de IA como Claude o ChatGPT pueden acceder directamente y razonar sobre archivos locales en lugar de depender de copiar y pegar.

El apretón de manos tiene tres fases. Primero el cliente se inicializa, luego pregunta qué herramientas están disponibles y luego comienza a llamarlas:

El ciclo de vida del mensaje del Protocolo de contexto modelo (MCP). Una descripción general de la arquitectura limpia que muestra el intercambio JSON-RPC bidireccional y secuencial entre el Cliente y el Servidor durante las etapas de Inicialización, Descubrimiento y Ejecución. Imagen por autor

Todo lo que sigue es el transporte que lleva mensajes de un lado a otro.

La especificación define dos transportes. stdio ejecuta la entrada y salida estándar: un objeto JSON por línea, que se descarga inmediatamente. HTTP/SSE ejecuta solicitudes a través de HTTP POST, con respuestas transmitidas a través de una conexión persistente de eventos enviados por el servidor[3].

La mayoría de las implementaciones eligen uno. Éste implementa ambos, con el mismo despachador y las mismas cuatro herramientas detrás de cada uno.

Esto es lo que muestra la demostración al inicio: ambos transportes registran las mismas herramientas:

[2]Herramientas disponibles [list_directory] Listar archivos y directorios. Devuelve nombre, tipo, tamaño… [read_file] Lee el contenido de un archivo. Máximo 1 MB. Archivos binarios devueltos… [search_files] Buscar archivos por patrón global. Utilice recursive=true para… [get_file_info] Obtenga metadatos para un archivo o directorio: tamaño, tipo, extensión…

Arquitectura: cuatro capas

El sistema tiene cuatro capas.

Un diagrama de pila arquitectónico que ilustra las capas desacopladas de la implementación del Protocolo de contexto modelo (MCP). El diseño asigna un flujo descendente operativo desde un cliente AI que pasa solicitudes JSON-RPC 2.0 a través de una capa de transporte que admite stdio y HTTP/SSE. La solicitud llega a un enrutador Dispatcher sin estado, analiza los nombres de las herramientas y los argumentos en una capa de herramientas, se somete a una validación en una capa de seguridad que presenta una resolución de ruta segura dentro de un entorno limitado MCP_ROOT y, finalmente, se ejecuta de forma segura dentro del sistema de archivos local subyacente.
La pila arquitectónica desacoplada del Model Context Protocol (MCP). Un desglose estructural que destaca cómo los mensajes sin procesar de los clientes se transportan, enrutan, validan y ejecutan de forma segura dentro de un entorno de sistema de archivos local estrictamente aislado. Imagen por autor

Capa de seguridad: valida cada ruta antes de cualquier operación del sistema de archivos. Se ejecuta antes que nada, en cada llamada.

Capa de herramientas: cuatro herramientas para el trabajo real del sistema de archivos: list_directory, read_file, search_files, get_file_info.

Dispatcher: un enrutador JSON-RPC sin estado. Analiza el método, llama al controlador correcto y devuelve la respuesta. No tiene idea de qué transporte está en marcha y no es necesario.

Capa de transporte: dos implementaciones. StdioTransport para clientes locales de IA. HTTPSSETransport para conexiones simultáneas. El despachador no tiene idea de cuál se está ejecutando.

El punto de entrada selecciona el transporte al inicio:

despachador = MCPDispatcher(raíz) si args.http: HTTPSSETransport(dispatcher, host=args.host, puerto=args.port).run() más: StdioTransport(dispatcher).run()

Una bandera. Eso es todo.

El modelo de seguridad

Lo primero en lo que tuve que pensar al construir un servidor que lee archivos locales fue en qué impide que un cliente lea archivos que no debería. El ataque obvio es el recorrido de ruta: en lugar de enviar README.md, un cliente envía ../../etc/passwd y un servidor que no verifica lo sigue directamente fuera del entorno de pruebas.

La solución fue resolver ambos caminos por completo antes de compararlos. La línea clave:

target.resolve().relative_to(base.resolve())

Path.resolve() expande todos los enlaces simbólicos y colapsa todos… los segmentos. relativo_to() genera ValueError si el resultado cae fuera de la base.[6]Sin analizar cadenas, sin contar… manualmente. El sistema operativo resuelve la ruta; Python comprueba el resultado.

MCP_ROOT establece la raíz de la zona de pruebas mediante una variable de entorno. Lo configuré específicamente en la carpeta de mi proyecto, no en mi directorio de inicio. Cada herramienta ejecuta esta verificación antes de tocar el sistema de archivos. Si falla, el error vuelve al cliente inmediatamente.

Las pruebas de seguridad verifican esto en cada compilación:

AttackResult../../etc/passwdAcceso denegadoEnlace simbólico apuntando fuera de rootAcceso denegadoRuta UNC de Windows \servershareAccess denegadosrc/main.py dentro de rootAllowed

Las cuatro herramientas

directorio_lista

Enumera todo lo que hay en un directorio: nombre, tipo, tamaño, marca de tiempo modificada, ruta relativa. Directorios antes de los archivos, entradas ocultas excluidas de forma predeterminada.

Apuntándolo a la carpeta del proyecto:

[3]list_directory 8 entradas: [F] concurrent_demo.py 4,711B [F] demo.py 10,451B [F] http_client.py 5,140B [F] local_desktop_config.json 228B [F] README.md 7,542B [F] server.py 29,222B [F] test_server.py 17.500 mil millones

Ocho entradas, tamaños, todo dentro del sandbox. El orden de clasificación coloca los directorios primero porque la clave de clasificación usa p.is_file() – False < True en Python, por lo que los directorios flotan naturalmente.

Una cosa que me molestó en Windows: un archivo puede aparecer en una lista de directorio mientras otro proceso lo bloquea. item.stat() genera PermissionError en esa entrada. La herramienta envuelve cada llamada de estadística en su propio try/except y omite las entradas bloqueadas silenciosamente en lugar de bloquear toda la lista.

leer_archivo

Lee el contenido de archivos con un límite máximo de 1 MB. Los archivos de texto se devuelven como UTF-8 simple. Los archivos binarios se devuelven como base64.

read_file concurrent_demo.py: #!/usr/bin/env python3 """ concurrent_demo.py ================================================================================================================================================================================================= Prueba que el transporte HTTP/SSE maneja múltiples clientes simultáneos. Activa 5 clientes simultáneamente, cada uno ejecutando… (4509 caracteres más)

Agregué el respaldo binario después de apuntar el servidor a una carpeta de proyecto real por primera vez. Las carpetas de proyectos de Python contienen archivos .pyc, extensiones compiladas y bases de datos SQLite. La primera versión los rechazó todos con UnicodeDecodeError. La solución: si read_text() falla en la decodificación, recurra a read_bytes() y devuelva base64. El cliente obtiene una respuesta estructurada con un indicador binario: verdadero en lugar de un bloqueo.

El límite de 1 MB existe porque una de las primeras pruebas leyó accidentalmente una base de datos SQLite de 200 MB y congeló el proceso durante treinta segundos. MAX_FILE_BYTES es una constante en la parte superior de server.py; cámbiela si su flujo de trabajo necesita archivos más grandes.

archivos_de_búsqueda

Después del incidente rglob(), esta herramienta funciona así:

[6]search_files – *.py (superficial) Se encontraron 5 archivos: -> concurrent_demo.py 4,711B -> demo.py 10,451B -> http_client.py 5,140B -> server.py 29,222B -> test_server.py 17,500B

Cinco archivos, menos de 30 ms. La misma llamada a C:UsersAdmin con recursive=True aún escanearía todo, pero ahora esa es una elección deliberada que el cliente debe hacer, no algo que el servidor haga automáticamente.

El indicador truncado le indica al cliente cuándo se cortaron los resultados en max_results. La primera versión arrojaba resultados silenciosamente sin señal; agregué truncados después de darme cuenta de que el cliente no tenía forma de saber que no estaba obteniendo todo.

obtener_info_archivo

Devuelve metadatos sin leer el contenido del archivo, lo que resulta útil cuando el cliente necesita comprobar los permisos antes de decidir si desea leerlos.

[4]get_file_info nombre ruta del servidor-mcp-local. escriba tamaño del directorio 4096 modificado 1780246573 creado 1780227648 extensión Ninguno legible Verdadero escribible Verdadero

os.access() verifica los permisos reales, no solo la existencia. En Windows, un archivo puede ser visible en una lista mientras está bloqueado. Saber que es ilegible antes de intentar leerlo ahorra un viaje de ida y vuelta.

El despachador

No quería reinventar la rueda o reescribir mi lógica central solo para manejar diferentes configuraciones de red, así que construí un despachador central para manejar todo. Funciona como un motor básico y sin estado. Llega una cadena JSON sin formato, el despachador la analiza para ver exactamente qué necesita el cliente y luego devuelve una respuesta.

Mantuve explícitamente todas las E/S de red y archivos fuera de este componente. No sabe nada sobre stdin, stdout o HTTP. Toda esa comunicación desordenada se deja enteramente en manos de las capas de transporte. Los transportes hacen el trabajo pesado con los sockets o flujos reales y simplemente pasan los datos limpios a la función de envío().

Para mantener el sistema eficiente, el motor solo escucha cuatro métodos de especificaciones: inicializar, herramientas/lista, herramientas/llamada y ping. Si algo más llega al despachador, cierra la solicitud inmediatamente con un error JSON-RPC estándar.

La única excepción es el manejo de notificaciones. Cuando llega un mensaje sin un campo de identificación, la especificación MCP dicta que no se requiere respuesta. El despachador procesa el evento internamente y simplemente devuelve Ninguno. Debido a que el motor central es completamente independiente de cómo viajan los datos, pasar de un stdio local a un servidor HTTP no requiere cambios de código interno. La capa de transporte cambia en el exterior, pero el despachador principal permanece exactamente igual.

Transporte 1: stdio

Para la configuración local, el transporte stdio es solo una línea for sin formato en el bucle self._stdin. Me salté por completo la sincronización, los subprocesos y los bucles de eventos para mantenerlo lo más simple posible.

En realidad, la corrección de Windows me llevó más tiempo que escribir el transporte en sí. De forma predeterminada, Python abre stdin y stdout en modo texto en Windows, lo que cambia automáticamente cada n a rn cada vez que escribe datos. Ese pequeño cambio corrompe por completo la secuencia JSON. En el momento en que un cliente lee }rn{, recibe un error de análisis en el siguiente mensaje, interrumpiendo toda la conexión.

si plataforma.system() == "Windows": importar msvcrt msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

Configurar O_BINARY deshabilita la traducción.[8]Sin esto, el servidor funciona en macOS y Linux y se interrumpe silenciosamente en Windows.

write_through=True en el contenedor stdout garantiza que cada escritura se vacíe inmediatamente. El cliente de IA se bloquea sincrónicamente esperando la respuesta; cualquier almacenamiento en búfer detiene la interacción.

Aquí está la salida de demostración estándar completa de mi máquina:

==================================================================== demostración del servidor local-mcp [transporte stdio] Raíz: C:UsersAdminPycharmProjectspythonProjectlocal-mcp-server ===============================================================[1]Inicializar servidor: local-mcp-server v1.0.0 Protocolo: 2024-11-05[2]Herramientas disponibles [list_directory] Listar archivos y directorios… [read_file] Leer el contenido de un archivo. Máximo 1 MB… [search_files] Buscar archivos por patrón global… [get_file_info] Obtener metadatos para un archivo o directorio…[3]list_directory 8 entradas[4]get_file_info legible: Verdadero escribible: Verdadero[5]read_file primer archivo pequeño leído correctamente[6]search_files Se encontraron 5 archivos .py ============================================================== Todas las comprobaciones pasaron. Listo para conectar el escritorio local. ===============================================================

Transporte 2: HTTP/SSE

Cada cliente abre una conexión GET /sse (construida en el servidor http.de Python[4]) que permanece abierto durante toda la sesión, lo que permite que el servidor envíe respuestas a ese canal como eventos enviados por el servidor. Cada conexión recibe un client_id único[9]al conectar. Cuando un cliente necesita responder o enviar una solicitud, envía un mensaje POST por separado.

El flujo por cliente se ve así:

Diagrama de secuencia que muestra el ciclo de vida del transporte de eventos enviados por el servidor (SSE) del Protocolo de contexto modelo (MCP). Representa a un cliente que establece una conexión persistente a un servidor a través de una solicitud inicial
La arquitectura de transporte de eventos enviados por el servidor (SSE) del protocolo de contexto modelo (MCP). Este diagrama detalla el establecimiento de un flujo de eventos descendente persistente emparejado con operaciones HTTP POST independientes para el enrutamiento de mensajes del cliente ascendente. Imagen por autor

Para manejar la concurrencia de manera limpia, cada cliente obtiene su propia cola de mensajes independiente.[7]El controlador POST envía la llamada, coloca el resultado directamente en la cola de ese cliente e inmediatamente devuelve un estado 202. No espera a que finalice la entrega SSE. El cliente simplemente recoge la respuesta de su propio flujo abierto. Eso es lo que hace que la concurrencia funcione.

Configuré 16 subprocesos de trabajo de demonio para gestionar las solicitudes entrantes. Dado que cada conexión SSE activa retiene un subproceso, tener 5 clientes SSE activos deja 11 subprocesos completamente libres para manejar solicitudes POST entrantes en cualquier momento. No hay sintaxis async/await ni bucle de eventos, solo subprocesos de biblioteca estándar.[5]

La demostración concurrente

Este es el resultado que responde si el transporte HTTP/SSE realmente funciona:

============================================================= Demostración de cliente simultáneo: 5 clientes, 5 llamadas simultáneas ==================================================================== Lanzando 5 clientes simultáneos… Tiempo de resultado de la herramienta cliente ———- ——————– ———- ——– 1 list_directory OK ~0.034s 2 get_file_info OK ~0.021s 3 list_directory OK ~0.038s 4 search_files OK ~0.023s 5 search_files OK ~0.021s Tiempo total de pared: ~0.04s para 5 clientes simultáneos Resultado: TODO PASADO ===============================================================

Cinco clientes. Cinco llamadas de herramientas diferentes. Menos de 50 ms de tiempo total de pared en todas las ejecuciones. Ninguno se bloqueó entre sí. Medido en Windows 11, Python 3.12.6, solo CPU.

Lo que se rompió durante el desarrollo

Los diez minutos que ya describí. Otras tres cosas se rompieron antes de que el servidor se estabilizara.

El problema de Windows rn. La primera vez que conecté un cliente de IA real, apareció un error de análisis en el segundo mensaje. Todo parecía estar bien en las pruebas. El problema era la traducción estándar: n se convierte en rn en Windows. Pasé una hora mirando el despachador antes de encontrarlo. Dos líneas lo arreglaron.

El fallo del archivo binario. Primera versión de read_file llamada read_text() en todo. La primera carpeta del proyecto real encontró un archivo .pyc y generó UnicodeDecodeError. Después de eso, se agregó el respaldo base64.

La base de datos de 200 MB se congela. Antes del límite de 1 MB, una prueba leyó accidentalmente una base de datos SQLite. El proceso se congeló durante treinta segundos. La gorra entró inmediatamente después.

Cada uno de estos sólo apareció cuando el servidor se ejecutó en una máquina real, no en un directorio de prueba.

El conjunto de pruebas

50 pruebas en siete clases. La seguridad es lo primero.

ClaseQué cubreTestSecurityAtaques transversales, escapes de enlaces simbólicos, rutas vacíasTestListDirectoryArchivos ocultos, orden de clasificación, entradas bloqueadas, erroresTestReadFileText, binario/base64, límite de 1 MB, errores de permisosTestSearchFilesShallow vs recursivo, max_results, indicador de truncamientoTestGetFileInfoFile vs directorio, permisos, marcas de tiempoTestDispatcherTodos los métodos, notificaciones, análisis errores, métodos desconocidosTestHTTPTransportHealth endpoint, conexión SSE, códigos de error 400/404

Ejecute el conjunto de pruebas con pytest en modo detallado. Para omitir las pruebas de integración, pase el indicador de marcador de no integración.

Conexión a un cliente de IA local

macOS: ~/Biblioteca/Soporte de aplicaciones/Claude/local_desktop_config.json

Windows: %APPDATA%Claudelocal_desktop_config.json

{ "mcpServers": { "local-desktop": { "command": "python", "args": ["C:/absolute/path/to/local-mcp-server/server.py"], "env": { "MCP_ROOT": "C:/absolute/path/to/your/workspace" } } } }

Para HTTP/SSE:

# Terminal 1: inicia el servidor python server.py –http –port 8765 # Terminal 2: ejecuta el cliente de ejemplo python ejemplos/http_client.py

Decisiones de diseño honestas

Un grupo de 16 subprocesos de trabajo es suficiente para el desarrollo local, pero no lo diseñé para escalarlo a un servidor compartido que maneje cientos de conexiones simultáneas. Si necesita ese tipo de escala, probablemente debería cambiarla por asyncio y un marco asíncrono dedicado. Para herramientas de IA locales que ejecutan un puñado de clientes en su propia máquina, 16 subprocesos son más que suficientes.

El modelo de seguridad confía en el propio límite de la zona de pruebas, ignorando por completo los tipos de archivos. No escribí una lista permitida de extensiones seguras ni una lista bloqueada de extensiones peligrosas. Si una ruta se resuelve dentro de MCP_ROOT, es legible. Es más difícil eludir una regla que diez.

También omití intencionalmente el recuento de fichas. Este servidor simplemente devuelve el contenido del archivo sin formato. La gestión de su presupuesto de tokens pertenece a la capa de ejecución entre el servidor y el modelo. Agregar un contador aquí forzaría una dependencia del tokenizador (rompiendo el objetivo de dependencia cero) o forzaría una aproximación con sus propios casos extremos desordenados.

Por último, la búsqueda es superficial de forma predeterminada. Una suspensión de diez minutos durante las pruebas tomó esta decisión por mí. Cualquier comportamiento que destruya silenciosamente el rendimiento como ese nunca debería ser la opción predeterminada.

Lo que esto realmente enseña

Esperaba que construir un servidor MCP fuera complicado. Los tutoriales hicieron que pareciera complicado. Cada implementación que encontré tenía FastAPI, uvicorn y otros tres paquetes antes de que se registrara una sola herramienta. Entonces supuse que la complejidad era necesaria.

No lo fue. Cuando finalmente leí las especificaciones reales, el protocolo era un bucle. Leer una línea. Analizar JSON. Llame a una función. Escribe una línea. Eso es todo. Los marcos no resolvían problemas de MCP, sino problemas de HTTP que MCP sobre stdio no tiene.

La biblioteca estándar fue suficiente porque el problema era pequeño. No necesitaba un marco. Necesitaba http.server para conexiones TCP, subprocesos para solicitudes paralelas, cola para desacoplar SSE del manejo POST y pathlib para resolución de rutas. Un módulo por problema. No sobra nada.

Lo que más me sorprendió fue lo mucho que importaban los valores predeterminados. Cada falla real en este código base (el bloqueo de diez minutos, la congelación de 200 MB, la corrupción de JSON de Windows) provino de un valor predeterminado que funcionó bien en las pruebas y se rompió en una máquina real. rglob() estaba bien en una pequeña carpeta de prueba. La salida estándar del modo texto estaba bien en Linux. El valor predeterminado que parece conveniente en el desarrollo suele ser el que destruye silenciosamente las cosas en producción.

Código completo: https://github.com/Emmimal/local-mcp-server/

Referencias

[1]Protocolo de contexto modelo. (Dakota del Norte). Especificación del protocolo de contexto del modelo. https://modelcontextprotocol.io

[2]Grupo de trabajo JSON-RPC. (2010). Especificación JSON-RPC 2.0. https://www.jsonrpc.org/specification

[3]QUÉ. (Dakota del Norte). Eventos enviados por el servidor. Estándar de vida HTML. https://html.spec.whatwg.org/multipage/server-sent-events.html

[4]Fundación de software Python. http.server: servidores HTTP. Documentación de Python 3. https://docs.python.org/3/library/http.server.html

[5]Fundación de software Python. threading: paralelismo basado en subprocesos. Documentación de Python 3. https://docs.python.org/3/library/threading.html

[6]Fundación de software Python. pathlib: rutas del sistema de archivos orientado a objetos. Documentación de Python 3. https://docs.python.org/3/library/pathlib.html

[7]Fundación de software Python. cola: una clase de cola sincronizada. Documentación de Python 3. https://docs.python.org/3/library/queue.html

[8]Fundación de software Python. msvcrt: rutinas útiles del tiempo de ejecución de MS VC++. Documentación de Python 3. https://docs.python.org/3/library/msvcrt.html

[9]Fundación de software Python. (Dakota del Norte). uuid: objetos UUID según RFC 4122. Documentación de Python 3. https://docs.python.org/3/library/uuid.html

[10]Fundación de software Python. subproceso: gestión de subprocesos. Documentación de Python 3. https://docs.python.org/3/library/subprocess.html

Divulgación

Todo el código de este artículo fue escrito por mí y es un trabajo original, desarrollado y probado en Python 3.12.6, Windows 11, solo CPU. No se utilizó GPU en ningún momento. Todos los números de referencia (tiempos de respuesta, resultados de clientes simultáneos, recuentos de pruebas) provienen de ejecuciones reales en mi máquina local y son completamente reproducibles clonando el repositorio y ejecutando demo.py y concurrent_demo.py como se describe anteriormente. Toda la implementación utiliza únicamente la biblioteca estándar de Python. No se requieren ni utilizan paquetes de terceros en ningún momento. Todas las decisiones de arquitectura, opciones de implementación, compensaciones de diseño, experiencias de depuración y las fallas descritas en "Lo que se rompió durante el desarrollo" son mías. No tengo ninguna relación financiera con ninguna herramienta, biblioteca, marco o empresa mencionada en este artículo. El protocolo MCP es una especificación abierta publicada por Anthropic[1]; Esta implementación es independiente y no está afiliada ni respaldada por Anthropic.

Si construye sistemas de IA de producción y desea profundizar: tutoriales, pistas de aprendizaje y proyectos prácticos en EmiTechLogic, mi plataforma de aprendizaje de IA y Python.