mandioca-tabs-mcp 0.2.0 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/index.mjs +61 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -3,11 +3,11 @@
3
3
  Deja que tu asistente de IA lea y escriba la obra que tienes abierta en
4
4
  [Mandioca Tabs](https://tabs.mandioca-records.com).
5
5
 
6
- La guía de instalación para cada IA está dentro de Tabs: botón **Instalar conector**, en la barra
6
+ La guía de instalación para cada IA está dentro de Tabs: botón **Conectar IA**, en la barra
7
7
  de abajo. En cualquier programa que acepte servidores MCP locales, el comando es:
8
8
 
9
9
  ```
10
10
  npx -y mandioca-tabs-mcp
11
11
  ```
12
12
 
13
- Después abre tu obra en Tabs y aprieta **Conectar con IA**. La conexión es parte del plan Cowork.
13
+ Después abre tu obra en Tabs y aprieta **Conectar IA**. La conexión es parte del plan Cowork.
package/index.mjs CHANGED
@@ -19742,7 +19742,7 @@ var init_atajos = __esm({
19742
19742
  // declara entre las superficies que esta cuenta no ve.
19743
19743
  { id: "guardar-grabacion", nombre: "Guardar la grabaci\xF3n elegida como .wav", grupo: "Audio y mezcla" },
19744
19744
  { id: "imantar", nombre: "Imantar los puntos de sync al golpe", grupo: "Audio y mezcla" },
19745
- { id: "solo-del-audio", nombre: "O\xEDr s\xF3lo la grabaci\xF3n a la vista", grupo: "Audio y mezcla" },
19745
+ { id: "solo-del-audio", nombre: "Aislar la grabaci\xF3n a la vista", grupo: "Audio y mezcla" },
19746
19746
  { id: "velocidad-mas", nombre: "Reproducir m\xE1s r\xE1pido", grupo: "Audio y mezcla" },
19747
19747
  { id: "velocidad-menos", nombre: "Reproducir m\xE1s lento", grupo: "Audio y mezcla" },
19748
19748
  // ★ Agregado el 2026-09-03. Va en «Audio y mezcla» y no en un grupo propio porque es lo que
@@ -25101,7 +25101,7 @@ var init_paneles = __esm({
25101
25101
  "audio.solo": {
25102
25102
  nombre: "Solo de la grabaci\xF3n",
25103
25103
  accion: "audio-solo-del-audio",
25104
- principiante: "Prendido, se oye s\xF3lo la grabaci\xF3n que est\xE1s viendo, sin la tablatura ni las dem\xE1s. Apagado, suena lo que diga la mezcla.",
25104
+ principiante: "Prendido, de las grabaciones se oye s\xF3lo la que est\xE1s viendo, junto con la partitura. Apagado, suena lo que diga la mezcla.",
25105
25105
  profesional: "Solo que sigue a la grabaci\xF3n a la vista."
25106
25106
  },
25107
25107
  "audio.velocidad": {
@@ -25195,6 +25195,11 @@ var init_paneles = __esm({
25195
25195
  principiante: "Guarda en tu computadora la grabaci\xF3n que se est\xE1 mirando, como archivo WAV. Sirve para sacar un stem.",
25196
25196
  profesional: "Exporta la grabaci\xF3n elegida a WAV."
25197
25197
  },
25198
+ "audio.sync-to": {
25199
+ nombre: "Sync to",
25200
+ principiante: "Las dem\xE1s grabaciones quedan sincronizadas igual que \xE9sta: cada comp\xE1s anclado cae en el mismo segundo. Reemplaza los anclajes que ten\xEDan. Se deshace con Ctrl + Z.",
25201
+ profesional: "Copia los puntos de sync de esta grabaci\xF3n a las dem\xE1s (reemplaza)."
25202
+ },
25198
25203
  "audio.quitar": {
25199
25204
  nombre: "Dejar de o\xEDrla",
25200
25205
  accion: "audio-quitar",
@@ -96396,6 +96401,15 @@ var init_sync = __esm({
96396
96401
  }
96397
96402
  });
96398
96403
 
96404
+ // apps/mcp/src/version.ts
96405
+ var VERSION_DEL_CONECTOR;
96406
+ var init_version = __esm({
96407
+ "apps/mcp/src/version.ts"() {
96408
+ "use strict";
96409
+ VERSION_DEL_CONECTOR = true ? "0.2.3" : null;
96410
+ }
96411
+ });
96412
+
96399
96413
  // apps/mcp/src/puente.ts
96400
96414
  var puente_exports = {};
96401
96415
  __export(puente_exports, {
@@ -96488,7 +96502,7 @@ function exigirPesta\u00F1a() {
96488
96502
  const p = puenteAbierto();
96489
96503
  if (p.pesta\u00F1as() === 0) {
96490
96504
  throw new ProblemaConLaObra(
96491
- `El puente est\xE1 abierto en el puerto ${p.puerto} pero no hay ninguna pesta\xF1a de Mandioca Tabs conectada. Abre el programa en el navegador y aprieta \xABConectar con IA\xBB, el bot\xF3n de la barra de estado (abajo del todo, dice \xABIA desconectada\xBB). Esto no se puede hacer sin la aplicaci\xF3n abierta: sonar, ver, seleccionar y sacar una captura s\xF3lo existen en el navegador.`
96505
+ `El puente est\xE1 abierto en el puerto ${p.puerto} pero no hay ninguna pesta\xF1a de Mandioca Tabs conectada. Abre el programa en el navegador y aprieta \xABConectar IA\xBB, el bot\xF3n de la barra de estado (abajo del todo, dice \xABConectar IA\xBB). Esto no se puede hacer sin la aplicaci\xF3n abierta: sonar, ver, seleccionar y sacar una captura s\xF3lo existen en el navegador.`
96492
96506
  );
96493
96507
  }
96494
96508
  return p;
@@ -96577,6 +96591,11 @@ function atenderElMostrador(pedido, respuesta) {
96577
96591
  respuesta.writeHead(204).end();
96578
96592
  return;
96579
96593
  }
96594
+ if (ruta === "/version") {
96595
+ respuesta.writeHead(200, { "Content-Type": "application/json" });
96596
+ respuesta.end(JSON.stringify({ version: VERSION_DEL_CONECTOR }));
96597
+ return;
96598
+ }
96580
96599
  if (ruta.startsWith("/subir/")) {
96581
96600
  recibirEnElCasillero(ruta.slice("/subir/".length), pedido, respuesta);
96582
96601
  return;
@@ -96824,7 +96843,7 @@ async function canalDePedidos(p, herramienta) {
96824
96843
  id2 = conectadas[0].id;
96825
96844
  } else {
96826
96845
  throw new ProblemaConLaObra(
96827
- `\xAB${herramienta}\xBB s\xF3lo puede hacerlo la aplicaci\xF3n, y no se sabe cu\xE1l. ` + (conectadas.length === 0 ? "No hay ninguna pesta\xF1a conectada: p\xEDdele al m\xFAsico que abra Mandioca Tabs y apriete \xABConectar con IA\xBB, el bot\xF3n de la barra de estado." : `Hay ${conectadas.length} obras conectadas: ${lista2}. Elige una con \`elegir_obra\`, por su letra, y rep\xEDtelo \u2014 adivinar entre varias har\xEDa que suene, o se cargue, la canci\xF3n equivocada.`)
96846
+ `\xAB${herramienta}\xBB s\xF3lo puede hacerlo la aplicaci\xF3n, y no se sabe cu\xE1l. ` + (conectadas.length === 0 ? "No hay ninguna pesta\xF1a conectada: p\xEDdele al m\xFAsico que abra Mandioca Tabs y apriete \xABConectar IA\xBB, el bot\xF3n de la barra de estado." : `Hay ${conectadas.length} obras conectadas: ${lista2}. Elige una con \`elegir_obra\`, por su letra, y rep\xEDtelo \u2014 adivinar entre varias har\xEDa que suene, o se cargue, la canci\xF3n equivocada.`)
96828
96847
  );
96829
96848
  }
96830
96849
  }
@@ -96917,6 +96936,7 @@ var init_puente = __esm({
96917
96936
  init_src2();
96918
96937
  init_src3();
96919
96938
  init_obra();
96939
+ init_version();
96920
96940
  envoltoriosPorDoc = /* @__PURE__ */ new WeakMap();
96921
96941
  SINCRONIZAR = 0;
96922
96942
  PRESENCIA = 1;
@@ -96966,7 +96986,7 @@ function laPesta\u00F1aSigueViva() {
96966
96986
  function obraAbierta() {
96967
96987
  if (abierta && !laPesta\u00F1aSigueViva()) {
96968
96988
  throw new ProblemaConLaObra(
96969
- "La pesta\xF1a de Mandioca Tabs se desconect\xF3. Lo \xFAltimo que se sincroniz\xF3 sigue en memoria, pero **ya no es lo que el m\xFAsico tiene en pantalla** y escribir ah\xED no lo ver\xEDa nadie. P\xEDdele que vuelva a abrir el programa y apriete \xABConectar con IA\xBB, el bot\xF3n de la barra de estado."
96989
+ "La pesta\xF1a de Mandioca Tabs se desconect\xF3. Lo \xFAltimo que se sincroniz\xF3 sigue en memoria, pero **ya no es lo que el m\xFAsico tiene en pantalla** y escribir ah\xED no lo ver\xEDa nadie. P\xEDdele que vuelva a abrir el programa y apriete \xABConectar IA\xBB, el bot\xF3n de la barra de estado."
96970
96990
  );
96971
96991
  }
96972
96992
  if (!abierta) {
@@ -97324,6 +97344,18 @@ var init_estilo = __esm({
97324
97344
  });
97325
97345
 
97326
97346
  // packages/projection/src/proyectar.ts
97347
+ function canalesDeLasPistas(tipos) {
97348
+ const libres = Array.from({ length: 16 }, (_, i2) => i2).filter((c) => c !== 9);
97349
+ const conAltura = tipos.filter((t) => t !== "percusion").length;
97350
+ const porPista = conAltura * 2 <= libres.length ? 2 : 1;
97351
+ let siguiente = 0;
97352
+ const tomar = () => libres[siguiente++ % libres.length];
97353
+ return tipos.map((tipo) => {
97354
+ if (tipo === "percusion") return [9, 9];
97355
+ const primario = tomar();
97356
+ return [primario, porPista === 2 ? tomar() : primario];
97357
+ });
97358
+ }
97327
97359
  function proyectar(documento, opciones = {}) {
97328
97360
  const ajustes = opciones.ajustes ?? new Settings();
97329
97361
  const score = new _barrel_exports$4.Score();
@@ -97414,9 +97446,14 @@ function proyectar(documento, opciones = {}) {
97414
97446
  if (lista2) lista2.push(nota);
97415
97447
  else notasPorFigura.set(nota.figuraId, [nota]);
97416
97448
  }
97417
- for (const { id: idPista, datos } of documento.pistasEnOrden()) {
97449
+ const pistasEnOrden = [...documento.pistasEnOrden()];
97450
+ const canales = canalesDeLasPistas(
97451
+ pistasEnOrden.map(({ datos }) => normalizarPista(datos).tipo)
97452
+ );
97453
+ for (const [indiceDePista, { id: idPista, datos }] of pistasEnOrden.entries()) {
97418
97454
  const pista = normalizarPista(datos);
97419
97455
  const track = new _barrel_exports$4.Track();
97456
+ [track.playbackInfo.primaryChannel, track.playbackInfo.secondaryChannel] = canales[indiceDePista];
97420
97457
  track.name = marcado(pista.nombre, pista.nombreSegundaTipografia);
97421
97458
  track.shortName = marcado(pista.nombreCorto, pista.nombreCortoSegundaTipografia);
97422
97459
  track.playbackInfo.program = pista.programaMidi;
@@ -122340,6 +122377,9 @@ function registrarHerramientasDelDocumento(servidor2) {
122340
122377
  );
122341
122378
  }
122342
122379
 
122380
+ // apps/mcp/src/servidor.ts
122381
+ init_version();
122382
+
122343
122383
  // apps/mcp/src/herramientas/puente.ts
122344
122384
  init_src2();
122345
122385
  init_src3();
@@ -122371,7 +122411,7 @@ function registrarHerramientasDelPuente(servidor2) {
122371
122411
  "conectar_con_la_app",
122372
122412
  {
122373
122413
  title: "Conectar con la aplicaci\xF3n abierta",
122374
- description: "Se engancha a la obra que el m\xFAsico tiene abierta en el navegador, en vez de a un archivo del disco. A partir de ac\xE1, lo que se escriba aparece en su pantalla y lo que \xE9l escriba se ve desde ac\xE1. Requiere que \xE9l apriete \xABConectar con IA\xBB, el bot\xF3n de la barra de estado (abajo del todo, dice \xABIA desconectada\xBB).",
122414
+ description: "Se engancha a la obra que el m\xFAsico tiene abierta en el navegador, en vez de a un archivo del disco. A partir de ac\xE1, lo que se escriba aparece en su pantalla y lo que \xE9l escriba se ve desde ac\xE1. Requiere que \xE9l apriete \xABConectar IA\xBB, el bot\xF3n de la barra de estado (abajo del todo, dice \xABConectar IA\xBB).",
122375
122415
  inputSchema: {
122376
122416
  puerto: external_exports.number().int().min(1024).max(65535).optional().describe(`El puerto del puente. Por omisi\xF3n ${PUERTO_POR_OMISION}.`)
122377
122417
  }
@@ -122390,7 +122430,7 @@ function registrarHerramientasDelPuente(servidor2) {
122390
122430
  pesta\u00F1asConectadas: p.pesta\u00F1as(),
122391
122431
  // ⚠️ Se dice lo que falta, sin adornarlo. Si no hay pestaña, el asistente tiene que
122392
122432
  // saberlo ANTES de prometerle algo al músico — no descubrirlo al primer fallo.
122393
- siguiente: conectadas.length > 1 ? `Hay ${conectadas.length} obras abiertas. Mira \`obras_conectadas\` y elige una con \`elegir_obra\`, por su letra, antes de escribir nada.` : p.pesta\u00F1as() > 0 ? "Ya hay una pesta\xF1a conectada: la obra que se lee y se escribe es la suya." : "Todav\xEDa no hay ninguna pesta\xF1a. P\xEDdele que abra Mandioca Tabs y active P\xEDdele que apriete \xABConectar con IA\xBB, el bot\xF3n de la barra de estado (abajo del todo, dice \xABIA desconectada\xBB)."
122433
+ siguiente: conectadas.length > 1 ? `Hay ${conectadas.length} obras abiertas. Mira \`obras_conectadas\` y elige una con \`elegir_obra\`, por su letra, antes de escribir nada.` : p.pesta\u00F1as() > 0 ? "Ya hay una pesta\xF1a conectada: la obra que se lee y se escribe es la suya." : "Todav\xEDa no hay ninguna pesta\xF1a. P\xEDdele que abra Mandioca Tabs y active P\xEDdele que apriete \xABConectar IA\xBB, el bot\xF3n de la barra de estado (abajo del todo, dice \xABConectar IA\xBB)."
122394
122434
  });
122395
122435
  }
122396
122436
  );
@@ -122414,7 +122454,7 @@ function registrarHerramientasDelPuente(servidor2) {
122414
122454
  ...o.id === idDeLaActiva ? { activa: true } : {}
122415
122455
  })),
122416
122456
  activa: activa ? comoSeLlama(activa) : null,
122417
- siguiente: conectadas.length === 0 ? "No hay ninguna. P\xEDdele que abra Mandioca Tabs y apriete \xABConectar con IA\xBB." : activa === null ? "Hay varias y ninguna elegida: `elegir_obra` con su letra antes de escribir." : conectadas.length > 1 ? `Se escribe en la ${activa.etiqueta} (\xAB${activa.titulo}\xBB). Para otra, \`elegir_obra\` con su letra.` : "Hay una sola y est\xE1 viva: es la activa."
122457
+ siguiente: conectadas.length === 0 ? "No hay ninguna. P\xEDdele que abra Mandioca Tabs y apriete \xABConectar IA\xBB." : activa === null ? "Hay varias y ninguna elegida: `elegir_obra` con su letra antes de escribir." : conectadas.length > 1 ? `Se escribe en la ${activa.etiqueta} (\xAB${activa.titulo}\xBB). Para otra, \`elegir_obra\` con su letra.` : "Hay una sola y est\xE1 viva: es la activa."
122418
122458
  });
122419
122459
  }
122420
122460
  );
@@ -122703,6 +122743,15 @@ function registrarHerramientasDelPuente(servidor2) {
122703
122743
  // no cree nada que la interfaz no alcance — que es la que se pagó cuatro veces hoy.
122704
122744
  async () => texto3(await pedirALaPesta\u00F1a("desanclar_todo", {}))
122705
122745
  );
122746
+ servidor2.registerTool(
122747
+ "sync_to",
122748
+ {
122749
+ title: "Sync to: igualar las dem\xE1s grabaciones a la elegida",
122750
+ description: "Es el bot\xF3n \xABSync to\xBB del panel de audio. Las DEM\xC1S grabaciones de la obra quedan con la sincronizaci\xF3n de la elegida: los mismos compases anclados en los mismos milisegundos, y el mismo ritmo antes del primer anclaje y despu\xE9s del \xFAltimo. Contesta cu\xE1ntas grabaciones igual\xF3 y cu\xE1ntos puntos les copi\xF3. \u26A0\uFE0F Reemplaza los anclajes que ten\xEDan las dem\xE1s. Sirve cuando los audios arrancan juntos (stems o tomas del mismo proyecto); no compensa un desfase entre archivos. \u26A0\uFE0F No toca la letra sincronizada. Entra en el deshacer, como cualquier edici\xF3n.",
122751
+ inputSchema: {}
122752
+ },
122753
+ async () => texto3(await pedirALaPesta\u00F1a("sync_to", {}))
122754
+ );
122706
122755
  servidor2.registerTool(
122707
122756
  "quitar_grabacion",
122708
122757
  {
@@ -122773,8 +122822,8 @@ function registrarHerramientasDelPuente(servidor2) {
122773
122822
  servidor2.registerTool(
122774
122823
  "solo_del_audio",
122775
122824
  {
122776
- title: "O\xEDr s\xF3lo la grabaci\xF3n a la vista",
122777
- description: "Con esto puesto se oye s\xF3lo la grabaci\xF3n que muestra el panel de audio, sin la tablatura ni las dem\xE1s, y sigue a la que se elija con `elegir_grabacion`. Apagado, suena la mezcla como estaba. Prenderlo abre el panel. Sin decir nada, alterna. Con `volumenDb` fija el volumen de esa grabaci\xF3n en Solo; antes del fader, s\xF3lo cuenta con Solo puesto. Contesta `topeDb`: hasta +12 dB, o menos si m\xE1s arriba picar\xEDa.",
122825
+ title: "Aislar la grabaci\xF3n a la vista",
122826
+ description: "Con esto puesto, de las grabaciones se oye s\xF3lo la que muestra el panel de audio; la tablatura sigue sonando, para comparar con ella. Las dem\xE1s grabaciones se callan, y sigue a la que se elija con `elegir_grabacion`. Apagado, suena la mezcla como estaba. Prenderlo abre el panel. Sin decir nada, alterna. Con `volumenDb` fija el volumen de esa grabaci\xF3n en Solo; antes del fader, s\xF3lo cuenta con Solo puesto. Contesta `topeDb`: hasta +12 dB, o menos si m\xE1s arriba picar\xEDa.",
122778
122827
  inputSchema: {
122779
122828
  puesto: external_exports.boolean().optional().describe("true lo enciende, false lo apaga. Sin esto, alterna."),
122780
122829
  volumenDb: external_exports.number().min(-60).max(12).optional().describe("Volumen en Solo de la grabaci\xF3n a la vista, en dB.")
@@ -125482,7 +125531,7 @@ function registrarHerramientasDeArchivo(servidor2) {
125482
125531
 
125483
125532
  // apps/mcp/src/servidor.ts
125484
125533
  var NOMBRE = "mandioca-tabs";
125485
- var VERSION = "0.0.0";
125534
+ var VERSION = VERSION_DEL_CONECTOR ?? "0.0.0";
125486
125535
  var INSTRUCCIONES = `
125487
125536
  Mandioca Tabs es un editor de partitura y tablatura. Estas herramientas leen y escriben la
125488
125537
  obra del m\xFAsico.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mandioca-tabs-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
4
4
  "description": "Conector MCP de Mandioca Tabs: tu IA lee y escribe la partitura que tienes abierta en tabs.mandioca-records.com.",
5
5
  "type": "module",
6
6
  "bin": {