orquestra-mcp 1.26.0 → 1.27.0
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.
- package/package.json +2 -2
- package/src/server.mjs +74 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orquestra-mcp",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Servidor MCP de Orquestra --
|
|
3
|
+
"version": "1.27.0",
|
|
4
|
+
"description": "Servidor MCP de Orquestra -- 112 tools para leer y escribir tareas, ideas, changelog, infraestructura, contenido, QA y más desde un agente de IA",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"orquestra-mcp": "bin/orquestra-mcp.mjs"
|
package/src/server.mjs
CHANGED
|
@@ -924,7 +924,7 @@ export function createServer() {
|
|
|
924
924
|
server.registerTool(
|
|
925
925
|
'publish_content_postproxy',
|
|
926
926
|
{
|
|
927
|
-
description: 'Publica o programa una pieza de Contenido en redes sociales vía Postproxy -- solo funciona si la pieza tiene al menos una variante en estatus "approved" y su canal ya está conectado (ver connect_postproxy_channel/list_channels). Respeta approvalRequired -- no la llames para saltarte una aprobación pendiente. Cada variante aprobada va en SU PROPIO post de Postproxy (caption/media/fecha propios); antes de cada uno el server verifica el perfil EN VIVO y rechaza con un 400 claro si la cuenta está vencida, ya no existe, o (Instagram/TikTok) vive en el group de Postproxy de otro proyecto -- es decir, el canal apunta a la cuenta de otro proyecto y hay que reconectarlo. Devuelve {posts:[{channelId, postproxyPostId, status}], failed:[{channelId, channelName, error}]}: con éxito parcial responde 200 igual, así que revisa `failed` y cuéntaselo al usuario -- cada variante queda con su externalGuid o su publishError en la pieza.',
|
|
927
|
+
description: 'Publica o programa una pieza de Contenido en redes sociales vía Postproxy -- solo funciona si la pieza tiene al menos una variante en estatus "approved" y su canal ya está conectado (ver connect_postproxy_channel/list_channels). Si el workspace todavía no tiene Postproxy habilitado (BAJO SOLICITUD, ver connect_postproxy_channel), responde 403 `postproxy_not_enabled` -- no reintentes, dile al usuario que un admin lo solicite desde Contenido → Cuentas conectadas → "Solicitar conexión". Respeta approvalRequired -- no la llames para saltarte una aprobación pendiente. Cada variante aprobada va en SU PROPIO post de Postproxy (caption/media/fecha propios); antes de cada uno el server verifica el perfil EN VIVO y rechaza con un 400 claro si la cuenta está vencida, ya no existe, o (Instagram/TikTok) vive en el group de Postproxy de otro proyecto -- es decir, el canal apunta a la cuenta de otro proyecto y hay que reconectarlo. Devuelve {posts:[{channelId, postproxyPostId, status}], failed:[{channelId, channelName, error}]}: con éxito parcial responde 200 igual, así que revisa `failed` y cuéntaselo al usuario -- cada variante queda con su externalGuid o su publishError en la pieza.',
|
|
928
928
|
inputSchema: {
|
|
929
929
|
projectId: z.string().optional().describe(PROJECT_ID_DESC),
|
|
930
930
|
contentId: z.string().describe('Pieza de Contenido a publicar (ver list_content)'),
|
|
@@ -941,6 +941,79 @@ export function createServer() {
|
|
|
941
941
|
}
|
|
942
942
|
)
|
|
943
943
|
|
|
944
|
+
// sync_content_postproxy / reschedule_content_postproxy (10 oct 2026, a
|
|
945
|
+
// pedido de José: "todo esto que estamos haciendo en web, también se
|
|
946
|
+
// puede hacer por MCP?") -- paridad con los botones "Consultar ahora" y
|
|
947
|
+
// "Reagendar en Postproxy" de ContentDetail.jsx, que hasta ahora solo
|
|
948
|
+
// existían en la web. Mismo criterio que publish_content_postproxy
|
|
949
|
+
// (server.registerTool directo, no CRUD estándar).
|
|
950
|
+
server.registerTool(
|
|
951
|
+
'sync_content_postproxy',
|
|
952
|
+
{
|
|
953
|
+
description: 'Pide el estado REAL de Postproxy para las variantes ya enviadas de una pieza (con externalGuid), en vez de esperar pasivamente el webhook -- usala si una pieza quedó "esperando confirmación" más tiempo del esperado (el webhook de Postproxy se perdió o tardó) o si el usuario pregunta "¿ya se publicó?" y querés confirmarlo ahora en vez de asumir. Actualiza status/publishedAt/postproxyPermalink/publishError en Firestore si Postproxy tiene novedades, y siempre estampa lastPostproxyCheckAt aunque no haya cambios -- también si la pieza ya está agendada a futuro (scheduledAt), en cuyo caso Postproxy SIEMPRE va a responder "todavía no", no es un error ni señal de que algo falló. Devuelve {checked}: cuántas variantes con externalGuid se consultaron (0 si la pieza nunca se envió a Postproxy). Después de llamarla, volvé a leer la pieza con list_content para ver si cambió -- la respuesta de esta tool no trae el contenido actualizado.',
|
|
954
|
+
inputSchema: {
|
|
955
|
+
projectId: z.string().optional().describe(PROJECT_ID_DESC),
|
|
956
|
+
contentId: z.string().describe('Pieza de Contenido a consultar (ver list_content)'),
|
|
957
|
+
},
|
|
958
|
+
},
|
|
959
|
+
async ({ projectId, contentId }) => {
|
|
960
|
+
try {
|
|
961
|
+
const data = await apiPost(config, `/v1/content/${contentId}/sync-postproxy`, { projectId: resolveProjectId(projectId) })
|
|
962
|
+
return textResult(data)
|
|
963
|
+
} catch (err) {
|
|
964
|
+
return errorResult(err)
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
)
|
|
968
|
+
|
|
969
|
+
server.registerTool(
|
|
970
|
+
'reschedule_content_postproxy',
|
|
971
|
+
{
|
|
972
|
+
description: 'Reagenda en Postproxy un canal de una pieza YA enviado (con externalGuid). Cambiar scheduledAt con update_content SOLO escribe Firestore -- el post real en Postproxy sigue en la hora VIEJA hasta llamar esta tool, así que después de mover la fecha con update_content llamá siempre a esta para que el cambio aplique de verdad. Postproxy solo deja editar drafts o posts programados a MÁS de 5 minutos de su hora real de publicación -- devuelve 422 "Post cannot be edited..." si ya se publicó o está a menos de 5 minutos, ese mensaje sube tal cual, no hay forma de evitarlo. Si sale bien, también actualiza scheduledAt en Firestore para esa variante (no hace falta un update_content aparte). channelId tiene que ser un canal que la pieza YA tenga y que ya esté enviado a Postproxy (ver list_content) -- si no tiene externalGuid todavía, falla con 400 explicando que primero hay que publish_content_postproxy.',
|
|
973
|
+
inputSchema: {
|
|
974
|
+
projectId: z.string().optional().describe(PROJECT_ID_DESC),
|
|
975
|
+
contentId: z.string().describe('Pieza de Contenido a reagendar (ver list_content)'),
|
|
976
|
+
channelId: z.string().describe('Canal ya enviado a Postproxy (con externalGuid) dentro de esa pieza'),
|
|
977
|
+
scheduledAt: z.number().describe('Nuevo timestamp en ms de cuándo debe publicarse'),
|
|
978
|
+
},
|
|
979
|
+
},
|
|
980
|
+
async ({ projectId, contentId, channelId, scheduledAt }) => {
|
|
981
|
+
try {
|
|
982
|
+
const data = await apiPost(config, `/v1/content/${contentId}/reschedule-postproxy`, { projectId: resolveProjectId(projectId), channelId, scheduledAt })
|
|
983
|
+
return textResult(data)
|
|
984
|
+
} catch (err) {
|
|
985
|
+
return errorResult(err)
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
)
|
|
989
|
+
|
|
990
|
+
// backfill_channel_content (10 oct 2026, a pedido de José: "agregar canal
|
|
991
|
+
// y agregar los contenidos que aún no se publican... para que todos
|
|
992
|
+
// estén parejos") -- recorre TODO el proyecto en un solo llamado, no una
|
|
993
|
+
// pieza a la vez (equivalente en bulk al botón "Copiar contenido acá" de
|
|
994
|
+
// ContentDetail.jsx en la web, que solo opera sobre una pieza). No es CRUD
|
|
995
|
+
// estándar (no crea ni edita UNA entidad con un id propio), así que va
|
|
996
|
+
// con server.registerTool directo, mismo criterio que publish_content_postproxy.
|
|
997
|
+
server.registerTool(
|
|
998
|
+
'backfill_channel_content',
|
|
999
|
+
{
|
|
1000
|
+
description: 'Agrega un canal a TODAS las piezas de Contenido del proyecto que todavía no lo tienen, copiando caption/media/prompts/formato de otro canal que sí los tenga -- para que un canal recién conectado (ej. LinkedIn) no arranque en blanco pieza por pieza. Solo toca piezas pendientes: se SALTEA automáticamente una pieza si el canal fuente ya está en estatus "published" ahí, si el canal fuente no tiene contenido real que copiar, o si la pieza ya tiene el canal destino. NUNCA pisa fecha (scheduledAt)/aprobación/estatus/desempeño -- el canal nuevo arranca siempre en estatus "idea", sin fecha, cada canal programa/aprueba por separado. Devuelve {updated, updatedIds, skipped, skippedDetail:[{id,title,reason}], failed}: revisa `failed` (reintentos que fallaron la escritura) y contale al usuario cuántas piezas se saltearon y por qué, no asumas que "updated" es el total de piezas del proyecto.',
|
|
1001
|
+
inputSchema: {
|
|
1002
|
+
projectId: z.string().optional().describe(PROJECT_ID_DESC),
|
|
1003
|
+
channelId: z.string().describe('Canal a agregar donde falte (ver list_channels) -- recién conectado o con pocas piezas todavía'),
|
|
1004
|
+
sourceChannelId: z.string().describe('Canal del que copiar el contenido en cada pieza (ver list_channels) -- tiene que ser distinto de channelId'),
|
|
1005
|
+
},
|
|
1006
|
+
},
|
|
1007
|
+
async ({ projectId, channelId, sourceChannelId }) => {
|
|
1008
|
+
try {
|
|
1009
|
+
const data = await apiPost(config, '/v1/content/backfill-channel', { projectId: resolveProjectId(projectId), channelId, sourceChannelId })
|
|
1010
|
+
return textResult(data)
|
|
1011
|
+
} catch (err) {
|
|
1012
|
+
return errorResult(err)
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
)
|
|
1016
|
+
|
|
944
1017
|
// Checklist de una tarea -- registradas directo con server.registerTool
|
|
945
1018
|
// (no con registerCreate/registerUpdate) porque su ruta necesita DOS ids
|
|
946
1019
|
// dinámicos (taskId + itemId del checklist), y esos helpers solo arman
|