Sync Licensing MCP Server
Sync Licensing MCP Server
Un servidor local de Model Context Protocol que expone el catálogo y la lógica de negocio de una plataforma de licencias de sincronización musical: búsqueda de pistas por brief creativo, comprobación de su autorización de derechos, cotización de una licencia bajo reglas de precios condicionales, emisión del contrato y registro del uso.
Desarrollado para CC3067 Redes (Universidad del Valle de Guatemala), Proyecto 1. El flujo de mensajes MCP está implementado directamente sobre JSON-RPC 2.0 — sin MCP SDK, sin FastMCP, sin framework. El paquete del servidor depende únicamente de la biblioteca estándar de Python.
Tabla de contenidos
Related MCP server: MusicBrainz MCP Server
1. El caso de negocio
La licencia de sincronización es el modelo de negocio de plataformas como Epidemic Sound, Artlist y Musicbed: un creador o una agencia de publicidad debe comprar una licencia antes de usar una pista en contenido audiovisual. El proceso tiene tres fricciones:
Encontrar una pista que encaje con el brief creativo y con el presupuesto es lento.
El estado legal de una pista no es evidente: puede contener samples que nunca se autorizaron, o estar bloqueada por una disputa de autoría.
El precio no es fijo. La misma pista cuesta una cosa para un post de Instagram y algo completamente distinto para una campaña nacional de televisión.
Este servidor convierte ese flujo de trabajo en cinco herramientas que un asistente puede encadenar. No es un buscador con una lista de precios adjunta: la tarifa se calcula con reglas condicionales, y las herramientas rechazan operaciones que pondrían al cliente en riesgo legal.
2. Arquitectura
┌────────────────────────┐
│ Host (chatbot / CLI) │
└───────────┬────────────┘
│ spawns as a subprocess
┌───────────▼────────────┐
│ MCP client │ client/mcp_cli.py
└───────────┬────────────┘
│ JSON-RPC 2.0 over stdio
│ (one JSON object per line)
┌───────────▼────────────┐
│ MCP server │ synclicense_mcp/
│ │
│ jsonrpc.py framing │
│ server.py dispatch │
│ tools.py 5 tools │
│ pricing.py rate card│
│ contracts.py contracts
│ catalog.py catalog │
└───────────┬────────────┘
│
┌───────────▼────────────┐
│ data/catalog.json │ built by scripts/seed_catalog.py
│ data/usage_log.jsonl │ append-only audit log
└────────────────────────┘stdout transporta únicamente tráfico de protocolo; cada diagnóstico que el servidor imprime va a stderr, por lo que canalizar la salida del servidor nunca corrompe el flujo.
3. Requisitos
Python 3.10 o superior (desarrollado en 3.11).
Ninguna otra dependencia para ejecutar el servidor.
requestssolo se necesita para obtener metadatos reales de Jamendo, ypytestsolo para ejecutar la suite de pruebas. Ambos están enrequirements.txt.
4. Instalación
git clone https://github.com/ecarcamo/MCP-Local-Redes.git
cd MCP-Local-Redes
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txtEl paquete no se instala: se importa desde la raíz del repositorio, así que todos los comandos siguientes se ejecutan desde el directorio del proyecto.
5. Construcción del catálogo
El repositorio ya incluye un catálogo en data/catalog.json con 800 pistas reales obtenidas de la API de Jamendo, por lo que puedes omitir esta sección e ir directamente a Uso. Reconstruyelo solo si quieres un tamaño diferente, una semilla diferente o un catálogo que no requiera credenciales.
Modo sin conexión (predeterminado, sin credenciales, sin red)
python scripts/seed_catalog.py --offline --count 800Determinista: el mismo --seed siempre produce el mismo catálogo. También fija tres pistas conocidas al principio (TRK-00001 autorizada, TRK-00002 con samples pendientes, TRK-00003 bloqueada), lo que hace que los escenarios de error sean fáciles de demostrar.
Modo Jamendo (metadatos reales de Creative Commons)
Regístrate en https://devportal.jamendo.com para obtener un client_id; luego:
cp .env.example .env
# edit .env and set JAMENDO_CLIENT_ID=your_client_id
python scripts/seed_catalog.py --jamendo --count 800Los metadatos de las pistas provienen de la API; la tarifa base y el estado de derechos se siguen generando localmente (ver sección 10). La popularidad se toma del orden popularity_total de la propia API. El plan gratuito de Jamendo limita las ráfagas de solicitudes y responde a una página limitada con una lista de resultados vacía en lugar de un error, por lo que el script hace una pausa entre páginas y reintenta una página vacía antes de concluir que el catálogo está agotado.
Opción | Predeterminado | Descripción |
|
| Fuente de los metadatos de las pistas |
|
| Cuántas pistas escribir |
|
| Semilla para la capa de negocio simulada |
|
| Dónde escribir el catálogo |
6. Uso
6.1 Ejecutar la demo guiada
Una ejecución completa de extremo a extremo por script, útil como prueba de humo. Lanza el servidor, reproduce la conversación de licenciamiento completa e imprime cada mensaje JSON-RPC que cruza el cable (--> enviado, <-- recibido):
python client/mcp_cli.py --demoLa demo recorre: handshake → tools/list → buscar una pista → comprobar su autorización → cotizarla → emitir el contrato → registrar el uso → y tres casos de error (una pista bloqueada, una cotización que pertenece a otra pista y un argumento inválido).
Añade --quiet para ocultar el rastro del protocolo sin procesar y ver solo las respuestas:
python client/mcp_cli.py --demo --quiet6.2 Sesión interactiva (la forma principal de usarlo)
Un REPL para manejar el servidor manualmente, una herramienta a la vez:
python client/mcp_cli.py --interactiveComando | Descripción |
| Herramientas publicadas por el servidor |
| JSON Schema de una herramienta |
| Llamar a una herramienta con argumentos JSON |
| IDs recordados de respuestas anteriores |
| Enviar un ping JSON-RPC |
| Enviar cualquier método JSON-RPC manualmente |
| Cerrar la sesión |
Los IDs se recuerdan. Cada *_id que devuelve una herramienta se almacena y se puede reutilizar como $name en la siguiente llamada, de modo que una negociación de licencia completa se puede escribir sin copiar ni un solo id a mano:
mcp> call buscar_pista {"mood": "epico", "instrumental": true, "presupuesto_max": 100, "limite": 3}
...
remembered: $pista_id=TRK-00312
mcp> call verificar_clearance {"pista_id": "$pista_id"}
mcp> call calcular_costo_licencia {"pista_id": "$pista_id", "tipo_uso": "publicidad_online", "territorio": "latam", "exclusividad": "sectorial", "duracion_meses": 12}
...
remembered: $cotizacion_id=COT-719E615733
mcp> call generar_contrato {"pista_id": "$pista_id", "cliente": "Agencia Lumen S.A.", "cotizacion_id": "$cotizacion_id"}
...
remembered: $contrato_id=CTR-F9D1D72B0D
mcp> call registrar_uso {"contrato_id": "$contrato_id", "plataforma": "YouTube", "url_proyecto": "https://youtube.com/watch?v=demo"}
mcp> vars
mcp> quit$pista_id toma por defecto el mejor candidato de la última búsqueda. Usa vars en cualquier momento para ver lo que se recuerda actualmente.
6.3 Ejecutar el servidor por sí solo
python -m synclicense_mcpEntonces espera mensajes JSON-RPC en stdin. Usa --catalog PATH para apuntarlo a un archivo de catálogo diferente.
6.4 Hablar con él sin ningún cliente
Como el transporte es simplemente JSON delimitado por líneas, puedes manejar el servidor directamente desde el shell:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"shell","version":"0"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
'{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"verificar_clearance","arguments":{"pista_id":"TRK-00001"}}}' \
| python -m synclicense_mcp7. Referencia de herramientas
Herramienta | Argumentos requeridos | Devuelve |
| (ninguno — todos los filtros son opcionales) | Pistas candidatas con id, título, artista, duración y tarifa base |
|
| Estado legal: autorizada, samples pendientes o bloqueada |
|
| Desglose completo de la tarifa, total en USD y un |
|
| Contrato con alcance, plazo, monto, restricciones y un |
|
| Registro de uso archivado para regalías y auditoría |
7.1 buscar_pista
Filtros opcionales: mood, genero, instrumental, duracion_seg_min, duracion_seg_max, presupuesto_max, limite (1–20, predeterminado 5).
mood:alegre,epico,melancolico,relajado,tenso,energetico,inspirador,oscurogenero:pop,rock,electronica,hip_hop,jazz,clasica,folk,ambient,cinematica,latina
Las pistas bloqueadas por una disputa de autoría se excluyen: no se pueden licenciar, así que ofrecerlas sería un falso positivo.
7.2 verificar_clearance
Estado | Licenciable | Efecto |
| sí | Sin cargas |
| sí | Recargo de depósito en garantía del +15 % y cláusula de retención |
| no | Disputa de autoría; se rechazan cotización y contratación |
7.3 calcular_costo_licencia
Argumento | Valores permitidos |
|
|
|
|
|
|
|
|
Ejemplo de solicitud y respuesta:
--> {"jsonrpc":"2.0","id":5,"method":"tools/call","params":{
"name":"calcular_costo_licencia",
"arguments":{"pista_id":"TRK-00312","tipo_uso":"redes_sociales",
"territorio":"local","exclusividad":"no","duracion_meses":6}}}
<-- {"jsonrpc":"2.0","id":5,"result":{
"content":[{"type":"text","text":"Quote for TRK-00312 \"Stop!\" ... TOTAL USD 94.50"}],
"structuredContent":{
"ok":true,
"cotizacion_id":"COT-3D18B1547D",
"pista_id":"TRK-00312",
"alcance":{"tipo_uso":"redes_sociales","territorio":"local",
"exclusividad":"no","duracion_meses":6},
"desglose":{"tarifa_base_usd":94.5,
"multiplicadores":{"tipo_uso":1.0,"territorio":1.0,
"exclusividad":1.0,"vigencia":1.0},
"subtotal_usd":94.5,"recargo_escrow_usd":0.0,
"total_usd":94.5,"moneda":"USD"},
"valida_hasta":"2026-09-19T18:15:54+00:00"},
"isError":false}}7.4 Encadenamiento de herramientas
Las herramientas mantienen estado dentro de una sesión, que es el objetivo del caso de uso:
buscar_pista ──► pista_id
├──► verificar_clearance (can stop the whole flow)
└──► calcular_costo_licencia ──► cotizacion_id
└──► generar_contrato ──► contrato_id
└──► registrar_usogenerar_contrato rechaza una cotización que no existe, ha caducado (30 días) o fue emitida para una pista diferente. registrar_uso rechaza un contrato desconocido o inactivo. Las cotizaciones y los contratos pertenecen a una conexión y no se comparten entre sesiones.
8. Reglas de precios
subtotal = tarifa_base × mult_use × mult_territory × mult_exclusivity × mult_term
total = subtotal + escrow surcharge (15% when the track has pending samples)Tipo de uso | × | Territorio | × | Exclusividad | × | Plazo | × |
| 1.0 |
| 1.0 |
| 1.0 | ≤ 3 meses | 0.8 |
| 1.1 |
| 1.8 |
| 2.0 | ≤ 6 meses | 1.0 |
| 1.3 |
| 2.2 |
| 4.5 | ≤ 12 meses | 1.5 |
| 1.6 |
| 2.4 | ≤ 24 meses | 2.2 | ||
| 2.5 |
| 3.2 | ≤ 36 meses | 2.8 | ||
| 4.0 | > 36 meses | 3.2 | ||||
| 6.0 | perpetuo | 3.5 | ||||
| 8.0 |
Seis meses es el plazo de referencia, por eso se sitúa en 1.0. Una cotización mantiene su precio durante 30 días.
9. Detalles del protocolo
Transporte. stdio, un mensaje JSON-RPC 2.0 por línea, UTF-8, sin nuevas líneas incrustadas. El servidor termina limpiamente al recibir EOF.
Versiones del protocolo. 2025-11-25 (preferida) y 2025-06-18. Si el cliente solicita cualquier otra cosa, el servidor responde con su versión preferida en lugar de fallar el handshake.
Métodos.
Método | Resultado |
| Versión negociada, capacidades, información del servidor, instrucciones |
| (notificación — sin respuesta) |
|
|
| Los cinco descriptores de herramientas con sus JSON Schemas |
|
|
Códigos de error.
Código | Significado |
| Error de análisis: la línea no es JSON válido |
| Solicitud no válida: sobre mal formado |
| Método no encontrado |
| Parámetros no válidos: argumento faltante, mal tipado o fuera de enumeración, o herramienta desconocida |
| Error interno |
| Servidor no inicializado: una solicitud llegó antes del handshake |
Errores de protocolo vs. errores de negocio. Una llamada mal formada devuelve un
error JSON-RPC. Una llamada bien formada que las reglas de licencia rechazan — una pista
bloqueada, una licencia vencida, un contrato desconocido — devuelve una
respuesta exitosa que lleva isError: true y una explicación legible, para que un modelo pueda
leer el motivo y corregir el rumbo en lugar de ver una falla de transporte.
La especificación completa está en docs/SERVER_SPEC.md.
10. De dónde vienen los datos
Los metadatos de las pistas (título, artista, duración, género, estado de ánimo, licencia, ranking de popularidad) provienen de la API pública de Jamendo, que expone un catálogo Creative Commons. El catálogo incluido en este repositorio se generó de esa manera. El generador offline produce la misma estructura localmente, así que el proyecto funciona sin credenciales y sin acceso a la red.
La capa de negocio es simulada, a propósito. Ninguna plataforma publica su
tarifa ni el estado legal interno de cada pista, así que tarifa_base_usd y
estado_derechos se generan a partir de una semilla fija con una distribución realista
(82% liberadas, 13% con muestras pendientes, 5% bloqueadas). Los multiplicadores de tarifa se
diseñaron a partir de las tarifas públicas libres de regalías de plataformas como
Jamendo Licensing.
Este alcance fue revisado y aprobado por el instructor del curso.
11. Pruebas
python -m pytest tests/ -vLa suite cubre las reglas de tarifa, el enmarcado JSON-RPC, el handshake, los códigos de error, la cadena de herramientas y sus rechazos, el generador de semillas y una prueba de extremo a extremo que lanza el proceso real del servidor y habla con el transporte stdio. Las pruebas buscan pistas por estado de derechos en lugar de por un id fijo, así que pasan contra cualquier catálogo: offline, Jamendo o regenerado con una semilla distinta.
12. Estructura del proyecto
MCP-Local-Redes/
├── synclicense_mcp/ MCP server package (standard library only)
│ ├── __main__.py entry point: python -m synclicense_mcp
│ ├── jsonrpc.py JSON-RPC 2.0 framing over stdio
│ ├── server.py MCP method dispatch
│ ├── tools.py the five tools: schemas, validation, handlers
│ ├── pricing.py conditional rate card
│ ├── contracts.py contracts and usage registration
│ ├── catalog.py catalog loading and search
│ └── errors.py business-rule failures
├── client/mcp_cli.py manual JSON-RPC client (demo + REPL)
├── scripts/seed_catalog.py catalog builder (offline / Jamendo)
├── data/catalog.json generated catalog
├── tests/ pytest suite
└── docs/ proposal, assignment brief, server specification13. Estado del proyecto
Entregado en esta etapa:
Servidor MCP local sobre stdio con las cinco herramientas del caso de uso aprobado.
JSON-RPC 2.0 y el handshake de MCP implementados a mano.
Cliente de línea de comandos con una demo guionizada y un REPL interactivo.
Generación de catálogo, tanto en modo offline como en modo Jamendo.
Suite de pruebas.
Planificado para el resto del proyecto:
Host de chatbot en la API de Anthropic, con contexto de sesión y un registro visible de cada interacción MCP.
Integración con los servidores MCP oficiales de Filesystem y Git.
El mismo servidor desplegado de forma remota sobre HTTP.
Captura con Wireshark y análisis capa por capa del tráfico remoto.
Autor: Esteban Cárcamo (23016) — CC3067 Redes, Sección 20
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceAn MCP server for Spotify control and synchronized lyrics retrieval that enables playback management, queue navigation, and music search capabilities. It also features perception tools for real-time track analysis, including BPM, key detection, and timestamped lyrics.1293Apache 2.0
- AlicenseNot gradedqualityCmaintenanceA comprehensive MCP server for querying the MusicBrainz database, providing tools to search for artists, releases, recordings, and browse music metadata.4MIT
- FlicenseNot gradedqualityCmaintenanceA remote MCP server for the Arxpot processing core, enabling music search, metadata retrieval, and download management with remote storage delivery.
- AlicenseAqualityAmaintenanceAn MCP server that enables searching tracks and fetching lyrics, including time-synced LRC lyrics, from LRCLIB without requiring an API key.2398MIT
Related MCP Connectors
Personal MCP server for humans who create. Proof of authorship, license control.
A paid remote MCP for CLI tool MCP, built to return verdicts, receipts, usage logs, and audit-ready
A paid remote MCP for hosted MCP server, built to return verdicts, receipts, usage logs, and audit-r
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/ecarcamo/MCP-Local-Redes'
If you have feedback or need assistance with the MCP directory API, please join our Discord server