Skip to main content
Glama
ecarcamo

Sync Licensing MCP Server

by ecarcamo

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

  1. El caso de negocio

  2. Arquitectura

  3. Requisitos

  4. Instalación

  5. Construcción del catálogo

  6. Uso

  7. Referencia de herramientas

  8. Reglas de precios

  9. Detalles del protocolo

  10. De dónde provienen los datos

  11. Pruebas

  12. Estructura del proyecto

  13. Estado del proyecto


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.

  • requests solo se necesita para obtener metadatos reales de Jamendo, y pytest solo para ejecutar la suite de pruebas. Ambos están en requirements.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.txt

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

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 800

Determinista: 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 800

Los 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

--offline / --jamendo

--offline

Fuente de los metadatos de las pistas

--count N

800

Cuántas pistas escribir

--seed N

23016

Semilla para la capa de negocio simulada

--output PATH

data/catalog.json

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

La 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 --quiet

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

Comando

Descripción

list

Herramientas publicadas por el servidor

schema <tool>

JSON Schema de una herramienta

call <tool> <json>

Llamar a una herramienta con argumentos JSON

vars

IDs recordados de respuestas anteriores

ping

Enviar un ping JSON-RPC

raw <method> [json]

Enviar cualquier método JSON-RPC manualmente

quit

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_mcp

Entonces 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_mcp

7. Referencia de herramientas

Herramienta

Argumentos requeridos

Devuelve

buscar_pista

(ninguno — todos los filtros son opcionales)

Pistas candidatas con id, título, artista, duración y tarifa base

verificar_clearance

pista_id

Estado legal: autorizada, samples pendientes o bloqueada

calcular_costo_licencia

pista_id, tipo_uso, territorio, exclusividad, duracion_meses

Desglose completo de la tarifa, total en USD y un cotizacion_id

generar_contrato

pista_id, cliente, cotizacion_id

Contrato con alcance, plazo, monto, restricciones y un contrato_id

registrar_uso

contrato_id, plataforma, url_proyecto

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, oscuro

  • genero: 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

libre

Sin cargas

samples_pendientes

Recargo de depósito en garantía del +15 % y cláusula de retención

bloqueada

no

Disputa de autoría; se rechazan cotización y contratación

7.3 calcular_costo_licencia

Argumento

Valores permitidos

tipo_uso

redes_sociales, evento_interno, podcast, web_corporativo, publicidad_online, videojuego, tv_nacional, cine

territorio

local, latam, europa, norteamerica, mundial

exclusividad

no, sectorial, total

duracion_meses

0 (perpetua) o 1–120

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_uso

generar_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

×

redes_sociales

1.0

local

1.0

no

1.0

≤ 3 meses

0.8

evento_interno

1.1

latam

1.8

sectorial

2.0

≤ 6 meses

1.0

podcast

1.3

europa

2.2

total

4.5

≤ 12 meses

1.5

web_corporativo

1.6

norteamerica

2.4

≤ 24 meses

2.2

publicidad_online

2.5

mundial

3.2

≤ 36 meses

2.8

videojuego

4.0

> 36 meses

3.2

tv_nacional

6.0

perpetuo

3.5

cine

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

initialize

Versión negociada, capacidades, información del servidor, instrucciones

notifications/initialized

(notificación — sin respuesta)

ping

{}

tools/list

Los cinco descriptores de herramientas con sus JSON Schemas

tools/call

content, structuredContent, isError

Códigos de error.

Código

Significado

-32700

Error de análisis: la línea no es JSON válido

-32600

Solicitud no válida: sobre mal formado

-32601

Método no encontrado

-32602

Parámetros no válidos: argumento faltante, mal tipado o fuera de enumeración, o herramienta desconocida

-32603

Error interno

-32002

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/ -v

La 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 specification

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

F
license - not found
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

  • A
    license
    Not graded
    quality
    C
    maintenance
    An 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.
    129
    3
    Apache 2.0
  • F
    license
    Not graded
    quality
    C
    maintenance
    A remote MCP server for the Arxpot processing core, enabling music search, metadata retrieval, and download management with remote storage delivery.

View all related MCP servers

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

View all MCP Connectors

Latest Blog Posts

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