@contenthero/mcp 0.3.8 → 0.4.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/dist/format.d.ts +20 -9
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +39 -5
- package/dist/format.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +207 -52
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -37,7 +37,7 @@ import { z } from 'zod';
|
|
|
37
37
|
import { GenerationTimeoutError, pendingOutputId, } from '@contenthero/sdk';
|
|
38
38
|
import { getClient as defaultGetClient } from './client.js';
|
|
39
39
|
import { resolveModelEnums, BOARD_TYPES, BOARD_TYPE_GUIDANCE, IMAGE_MODEL_GUIDANCE, VIDEO_MODEL_GUIDANCE, AUDIO_MODEL_GUIDANCE, EDIT_AUDIO_MODEL_GUIDANCE, UPSCALE_MODEL_GUIDANCE, LIP_SYNC_MODEL_GUIDANCE, } from './models.js';
|
|
40
|
-
import { audioResult, avatarListResult, avatarResult, balanceResult, brandKitListResult, brandKitResult, brandKnowledgeListResult, brandKnowledgeDetailResult, brandKnowledgeSearchResult, brandKnowledgeItemResult, completedResult, connectedAccountListResult, connectedAccountResult, costResult, accountDetailResult, inspirationContentResult, mediaListResult, mediaSearchResult, folderListResult, folderContentsResult, mediaBatchResult, mediaUploadResult, uploadedMediaResult, tagListResult, tagResult, tagDeletedResult, modelListResult, modelResult, platformListResult, platformResult, elementListResult, elementResult, elementDeletedResult, errorResult, generationBatchResult, outlierListResult, enhanceClipsResult, pendingResult,
|
|
40
|
+
import { audioResult, avatarListResult, avatarResult, balanceResult, brandKitListResult, brandKitResult, brandKnowledgeListResult, brandKnowledgeDetailResult, brandKnowledgeSearchResult, brandKnowledgeItemResult, completedResult, connectedAccountListResult, connectedAccountResult, costResult, accountDetailResult, inspirationContentResult, mediaListResult, mediaSearchResult, folderListResult, folderContentsResult, mediaBatchResult, mediaUploadResult, uploadedMediaResult, tagListResult, tagResult, tagDeletedResult, modelListResult, modelResult, platformListResult, platformResult, elementListResult, elementResult, elementDeletedResult, errorResult, generationBatchResult, outlierListResult, enhanceClipsResult, pendingResult, stageListResult, spaceDeletedResult, spaceListResult, spaceResult, cardListResult, cardResult, postSummaryResult, publishResult, statusActionResult, editorOpsResult, text, projectDetailResult, liveContextResult, projectListResult, projectCreatedResult, projectDeletedResult, layerTypesResult, timelineTypesResult, editorTranscriptResult, exportJobResult, exportFormatsResult, trackedAccountListResult, transcriptResult, voiceListResult, voiceResult, } from './format.js';
|
|
41
41
|
/** Platforms a post or destination may target. */
|
|
42
42
|
const POST_PLATFORMS = [
|
|
43
43
|
'youtube',
|
|
@@ -207,6 +207,62 @@ function buildReferences(parts) {
|
|
|
207
207
|
*/
|
|
208
208
|
export function registerTools(server, opts) {
|
|
209
209
|
const { getClient, models } = opts;
|
|
210
|
+
/**
|
|
211
|
+
* ⚠️ THESE SHAPES ARE DECLARED, NOT LEFT AS `z.unknown()`. An array of unknown serialises to
|
|
212
|
+
* `{"type":"array","items":{}}`, which tells a client NOTHING about what may go inside it. The server
|
|
213
|
+
* accepted every shape when called directly, and Claude Desktop rejected all of them before they left,
|
|
214
|
+
* because a validator cannot check a value against an empty schema and a model cannot pattern an argument
|
|
215
|
+
* on one either. Measured 2026-08-23: 12 fields across 4 tools were advertised that way.
|
|
216
|
+
*
|
|
217
|
+
* Every entry below stays permissive at the EDGES (optional fields, free-form payload objects) because the
|
|
218
|
+
* server does the real validation. The point is to describe the shape, not to duplicate the rules.
|
|
219
|
+
*/
|
|
220
|
+
const logoEntrySchema = z.object({
|
|
221
|
+
url: z.string().optional().describe('A url the kit already has.'),
|
|
222
|
+
outputId: z.string().optional().describe('A generation to copy in: "<id>", or "<id>-2" for variation 2.'),
|
|
223
|
+
name: z.string().optional(),
|
|
224
|
+
is_primary: z.boolean().optional().describe("Make this the kit's cover. Exactly one logo ends up primary."),
|
|
225
|
+
layout: z.enum(['horizontal', 'stacked', 'icon', 'wordmark']).optional(),
|
|
226
|
+
colorMode: z.enum(['full_color', 'light', 'dark', 'grayscale']).optional(),
|
|
227
|
+
});
|
|
228
|
+
const assetEntrySchema = z.object({
|
|
229
|
+
url: z.string().optional().describe('A url the kit already has.'),
|
|
230
|
+
outputId: z.string().optional().describe('A generation to copy in.'),
|
|
231
|
+
name: z.string().optional(),
|
|
232
|
+
});
|
|
233
|
+
const sectionEntrySchema = z.object({
|
|
234
|
+
tab: z.string().describe('The tab this section belongs to, e.g. "voice", "overview". Part of the key.'),
|
|
235
|
+
sectionName: z.string().describe('The section title. Part of the key.'),
|
|
236
|
+
sortOrder: z.number().int().optional(),
|
|
237
|
+
fields: z.array(z.record(z.string(), z.unknown())).optional().describe('Field objects: { key, label, type, value }.'),
|
|
238
|
+
});
|
|
239
|
+
/** An existing tracked-account id, OR a profile to add by handle/url. */
|
|
240
|
+
const accountEntrySchema = z.union([
|
|
241
|
+
z.string().describe('A tracked-account id, or a full profile url.'),
|
|
242
|
+
z.object({
|
|
243
|
+
platform: z
|
|
244
|
+
.enum(['youtube', 'instagram', 'facebook', 'tiktok', 'x', 'threads', 'linkedin'])
|
|
245
|
+
.optional()
|
|
246
|
+
.describe('Only needed for a bare handle; a full profile url carries its own platform.'),
|
|
247
|
+
handleOrUrl: z.string().describe('A profile url, or a handle when platform is given.'),
|
|
248
|
+
}),
|
|
249
|
+
]);
|
|
250
|
+
const postDestinationSchema = z.object({
|
|
251
|
+
platform: z.enum(POST_PLATFORMS).describe('The platform. This is the KEY: one destination per platform.'),
|
|
252
|
+
format: z.string().optional(),
|
|
253
|
+
connectedAccountId: z.string().nullable().optional(),
|
|
254
|
+
scheduledAt: z.string().nullable().optional().describe("Per-destination override of the post's schedule."),
|
|
255
|
+
platformSpecificData: z.record(z.string(), z.unknown()).optional().describe('The publish payload for this platform.'),
|
|
256
|
+
status: z.string().optional(),
|
|
257
|
+
});
|
|
258
|
+
const postAssetSchema = z.object({
|
|
259
|
+
id: z.string().optional().describe('Keep an existing asset, at this position in the order.'),
|
|
260
|
+
assetUrl: z.string().optional().describe('A public url for a NEW asset.'),
|
|
261
|
+
outputId: z.string().optional().describe('A generation for a NEW asset.'),
|
|
262
|
+
assetType: z.string().optional().describe('Required with assetUrl; inferred from outputId.'),
|
|
263
|
+
displayName: z.string().optional(),
|
|
264
|
+
metadata: z.record(z.string(), z.unknown()).nullable().optional(),
|
|
265
|
+
});
|
|
210
266
|
// -- generate_image -------------------------------------------------------
|
|
211
267
|
server.registerTool('generate_image', {
|
|
212
268
|
title: 'Generate Image',
|
|
@@ -770,18 +826,18 @@ export function registerTools(server, opts) {
|
|
|
770
826
|
visualStyle: z.string().optional(),
|
|
771
827
|
designPrinciples: z.array(z.string()).optional(),
|
|
772
828
|
contentStrategy: z.record(z.string(), z.unknown()).optional().describe('Content strategy object (free-form).'),
|
|
773
|
-
logos: z.array(
|
|
774
|
-
assets: z.array(
|
|
829
|
+
logos: z.array(logoEntrySchema).optional().describe("The kit's logos, each { url | outputId, name?, is_primary?, layout?, colorMode? }. Use outputId to bring in a generation."),
|
|
830
|
+
assets: z.array(assetEntrySchema).optional().describe("The kit's brand assets, each { url | outputId, name? }."),
|
|
775
831
|
sections: z
|
|
776
|
-
.array(
|
|
832
|
+
.array(sectionEntrySchema)
|
|
777
833
|
.optional()
|
|
778
834
|
.describe("The kit's curated sections, each { tab, sectionName, sortOrder?, fields? }. Array position is the default order."),
|
|
779
835
|
brandAccounts: z
|
|
780
|
-
.array(
|
|
836
|
+
.array(accountEntrySchema)
|
|
781
837
|
.optional()
|
|
782
838
|
.describe("The account owner's OWN profiles. A tracked-account id, or { platform?, handleOrUrl } to ADD one and start ingesting it."),
|
|
783
839
|
inspirationAccounts: z
|
|
784
|
-
.array(
|
|
840
|
+
.array(accountEntrySchema)
|
|
785
841
|
.optional()
|
|
786
842
|
.describe('Competitor/creator profiles they watch. Same entry shape as brandAccounts.'),
|
|
787
843
|
},
|
|
@@ -825,19 +881,19 @@ export function registerTools(server, opts) {
|
|
|
825
881
|
.boolean()
|
|
826
882
|
.optional()
|
|
827
883
|
.describe('Re-run website extraction for this kit. Returns immediately; poll extractionStatus.'),
|
|
828
|
-
logos: z.array(
|
|
829
|
-
assets: z.array(
|
|
884
|
+
logos: z.array(logoEntrySchema).optional().describe('The kit\'s logos, each { url | outputId, name?, is_primary?, layout?: horizontal|stacked|icon|wordmark, colorMode?: full_color|light|dark|grayscale }. REPLACES the list; [] clears it. Exactly one ends up primary (the kit\'s cover); name none and the first wins.'),
|
|
885
|
+
assets: z.array(assetEntrySchema).optional().describe('The kit\'s brand assets, each { url | outputId, name? }. REPLACES the list; [] clears it.'),
|
|
830
886
|
sections: z
|
|
831
|
-
.array(
|
|
887
|
+
.array(sectionEntrySchema)
|
|
832
888
|
.optional()
|
|
833
889
|
.describe("The kit's curated sections, each { tab, sectionName, sortOrder?, fields? }. REPLACES the set, keyed by (tab, sectionName); a section left out is ARCHIVED, never deleted. Array position is the default order."),
|
|
834
890
|
isDefault: z.literal(true).optional().describe('Make this the default kit, un-defaulting every other.'),
|
|
835
891
|
brandAccounts: z
|
|
836
|
-
.array(
|
|
892
|
+
.array(accountEntrySchema)
|
|
837
893
|
.optional()
|
|
838
894
|
.describe("The account owner's OWN profiles. Each entry is a tracked-account id, or { platform?, handleOrUrl } to ADD one and start ingesting it. REPLACES the list; [] clears it."),
|
|
839
895
|
inspirationAccounts: z
|
|
840
|
-
.array(
|
|
896
|
+
.array(accountEntrySchema)
|
|
841
897
|
.optional()
|
|
842
898
|
.describe('Competitor/creator profiles they watch. Same entry shape as brandAccounts. REPLACES the list; [] clears it.'),
|
|
843
899
|
name: z.string().optional(),
|
|
@@ -1086,7 +1142,7 @@ export function registerTools(server, opts) {
|
|
|
1086
1142
|
server.registerTool('list_folders', {
|
|
1087
1143
|
title: 'List Folders',
|
|
1088
1144
|
annotations: READ,
|
|
1089
|
-
description: "List the account's library folders (their own manual and smart folders, as a flat list with parent links for nesting) together with the built-in derived folders (recents, favorites, edits, canvas,
|
|
1145
|
+
description: "List the account's library folders (their own manual and smart folders, as a flat list with parent links for nesting) together with the built-in derived folders (recents, favorites, edits, canvas, cards). Use this to see how the library is organized before browsing or filing items.",
|
|
1090
1146
|
inputSchema: {},
|
|
1091
1147
|
}, async (_args, extra) => {
|
|
1092
1148
|
try {
|
|
@@ -1101,7 +1157,7 @@ export function registerTools(server, opts) {
|
|
|
1101
1157
|
annotations: READ,
|
|
1102
1158
|
description: "Return the contents of one folder. The folder id is either one of the account's own folder ids or a built-in derived-folder key. A manual folder returns exactly the items filed in it; a smart folder computes its members live from its saved query; a derived folder returns its built-in set. Items are media (with kind and a description) and, in manual folders, entities such as projects or posts.",
|
|
1103
1159
|
inputSchema: {
|
|
1104
|
-
folder_id: z.string().describe('A folder id, or a derived-folder key (recents, favorites, edits, canvas,
|
|
1160
|
+
folder_id: z.string().describe('A folder id, or a derived-folder key (recents, favorites, edits, canvas, cards).'),
|
|
1105
1161
|
},
|
|
1106
1162
|
}, async (args, extra) => {
|
|
1107
1163
|
try {
|
|
@@ -1207,12 +1263,12 @@ export function registerTools(server, opts) {
|
|
|
1207
1263
|
server.registerTool('get_media', {
|
|
1208
1264
|
title: 'Get Media',
|
|
1209
1265
|
annotations: READ,
|
|
1210
|
-
description: 'SEE specific media. Pass a batch of items (up to 10) to view them at once: each item is either a { url } (e.g. a URL threaded from get_context, a layer/asset URL from get_project /
|
|
1266
|
+
description: 'SEE specific media. Pass a batch of items (up to 10) to view them at once: each item is either a { url } (e.g. a URL threaded from get_context, a layer/asset URL from get_project / get_card, or an upload URL from list_media source=uploads) or an { mediaId, variation? } (a studio output id, full or first-8; omit variation to get the primary one). Returns light metadata per item plus an IMAGE block for each image so you can actually see it. For a VIDEO, set frames (and optionally fromSec/toSec) on the item to get low-res KEYFRAMES across that source-time window, so you can watch the raw footage (judge B-roll relevance, take quality) without editing it; audio still returns metadata + the url. An mediaId without a variation returns ONLY the primary variation and lists the others; request a specific variation to see it. Use this to inspect the actual pixels, not just URLs.',
|
|
1211
1267
|
inputSchema: {
|
|
1212
1268
|
items: z
|
|
1213
1269
|
.array(z.union([
|
|
1214
1270
|
z.object({
|
|
1215
|
-
url: z.string().describe('A media URL on our storage (from get_context / get_project /
|
|
1271
|
+
url: z.string().describe('A media URL on our storage (from get_context / get_project / get_card).'),
|
|
1216
1272
|
fromSec: z.number().min(0).optional().describe('Video keyframes: start of the source-time window (seconds). Omit for the whole clip.'),
|
|
1217
1273
|
toSec: z.number().min(0).optional().describe('Video keyframes: end of the source-time window (seconds).'),
|
|
1218
1274
|
frames: z.number().int().min(1).optional().describe('Video keyframes: how many to return across the window. Set this (or fromSec/toSec) to watch the raw footage.'),
|
|
@@ -1525,11 +1581,11 @@ export function registerTools(server, opts) {
|
|
|
1525
1581
|
return errorResult(err);
|
|
1526
1582
|
}
|
|
1527
1583
|
});
|
|
1528
|
-
// --
|
|
1529
|
-
server.registerTool('
|
|
1584
|
+
// -- list_cards -----------------------------------------------------------
|
|
1585
|
+
server.registerTool('list_cards', {
|
|
1530
1586
|
title: 'List Posts',
|
|
1531
1587
|
annotations: READ,
|
|
1532
|
-
description: "List the account's content-pipeline posts (newest-updated first). Filter by status, platform, pipeline_stage (id/slug/name), folder, favorite, or a title search. Call
|
|
1588
|
+
description: "List the account's content-pipeline posts (newest-updated first). Filter by status, platform, pipeline_stage (id/slug/name), folder, favorite, or a title search. Call get_card for one post's full detail (destinations + assets).",
|
|
1533
1589
|
inputSchema: {
|
|
1534
1590
|
status: z.enum(['draft', 'active', 'completed', 'archived']).optional().describe('Filter by lifecycle status.'),
|
|
1535
1591
|
platform: z.enum(POST_PLATFORMS).optional().describe('Filter by the post platform.'),
|
|
@@ -1541,7 +1597,7 @@ export function registerTools(server, opts) {
|
|
|
1541
1597
|
}, async (args, extra) => {
|
|
1542
1598
|
try {
|
|
1543
1599
|
const client = await getClient(extra);
|
|
1544
|
-
return
|
|
1600
|
+
return cardListResult(await client.listCards({
|
|
1545
1601
|
status: args.status,
|
|
1546
1602
|
platform: args.platform,
|
|
1547
1603
|
pipelineStage: args.pipelineStage,
|
|
@@ -1554,46 +1610,147 @@ export function registerTools(server, opts) {
|
|
|
1554
1610
|
return errorResult(err);
|
|
1555
1611
|
}
|
|
1556
1612
|
});
|
|
1557
|
-
// --
|
|
1558
|
-
server.registerTool('
|
|
1613
|
+
// -- get_card -------------------------------------------------------------
|
|
1614
|
+
server.registerTool('get_card', {
|
|
1559
1615
|
title: 'Get Post',
|
|
1560
1616
|
annotations: READ,
|
|
1561
1617
|
description: "Get one post in full: its fields (title, description, script, notes, status, stage, schedule), plus its publish destinations and attached assets.",
|
|
1562
1618
|
inputSchema: {
|
|
1563
|
-
postId: z.string().describe('The post id from
|
|
1619
|
+
postId: z.string().describe('The post id from list_cards.'),
|
|
1620
|
+
},
|
|
1621
|
+
}, async (args, extra) => {
|
|
1622
|
+
try {
|
|
1623
|
+
const client = await getClient(extra);
|
|
1624
|
+
return cardResult(await client.getCard(args.postId));
|
|
1625
|
+
}
|
|
1626
|
+
catch (err) {
|
|
1627
|
+
return errorResult(err);
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
// -- list_spaces ----------------------------------------------------------
|
|
1631
|
+
server.registerTool('list_spaces', {
|
|
1632
|
+
title: 'List Spaces',
|
|
1633
|
+
annotations: READ,
|
|
1634
|
+
description: "List the account's SPACES. A space is the planner's top-level container: Space > Stage > Card > Post. Each space has its own stages, so two spaces can both hold a stage called 'Published'. Call this FIRST to discover which board to work in, then pass a space id to list_stages, list_cards or create_card. Archived spaces are excluded unless includeArchived is set.",
|
|
1635
|
+
inputSchema: {
|
|
1636
|
+
includeArchived: z
|
|
1637
|
+
.boolean()
|
|
1638
|
+
.optional()
|
|
1639
|
+
.describe('Include archived spaces. Default false, matching the grid in the app.'),
|
|
1640
|
+
},
|
|
1641
|
+
}, async (args, extra) => {
|
|
1642
|
+
try {
|
|
1643
|
+
const client = await getClient(extra);
|
|
1644
|
+
return spaceListResult(await client.listSpaces({ includeArchived: args.includeArchived }));
|
|
1645
|
+
}
|
|
1646
|
+
catch (err) {
|
|
1647
|
+
return errorResult(err);
|
|
1648
|
+
}
|
|
1649
|
+
});
|
|
1650
|
+
// -- get_space ------------------------------------------------------------
|
|
1651
|
+
server.registerTool('get_space', {
|
|
1652
|
+
title: 'Get Space',
|
|
1653
|
+
annotations: READ,
|
|
1654
|
+
description: 'Return one space with its live card count. Use list_spaces to discover ids. The count excludes archived cards, and is the same number list_spaces reports for that space.',
|
|
1655
|
+
inputSchema: { spaceId: z.string().describe('The space id.') },
|
|
1656
|
+
}, async (args, extra) => {
|
|
1657
|
+
try {
|
|
1658
|
+
const client = await getClient(extra);
|
|
1659
|
+
return spaceResult(await client.getSpace(args.spaceId));
|
|
1660
|
+
}
|
|
1661
|
+
catch (err) {
|
|
1662
|
+
return errorResult(err);
|
|
1663
|
+
}
|
|
1664
|
+
});
|
|
1665
|
+
// -- create_space ---------------------------------------------------------
|
|
1666
|
+
server.registerTool('create_space', {
|
|
1667
|
+
title: 'Create Space',
|
|
1668
|
+
annotations: WRITE,
|
|
1669
|
+
description: "Create a space: a new planner board with its own stages. duplicateFrom copies another space's STAGES, never its cards, so the new board arrives with the columns and none of the work. Requires the pipeline:write scope.",
|
|
1670
|
+
inputSchema: {
|
|
1671
|
+
name: z.string().describe('The space name.'),
|
|
1672
|
+
coverUrl: z.string().optional().describe('A cover image URL for the space tile.'),
|
|
1673
|
+
duplicateFrom: z
|
|
1674
|
+
.string()
|
|
1675
|
+
.optional()
|
|
1676
|
+
.describe("Copy this space's stages into the new one. Cards are never copied."),
|
|
1677
|
+
},
|
|
1678
|
+
}, async (args, extra) => {
|
|
1679
|
+
try {
|
|
1680
|
+
const client = await getClient(extra);
|
|
1681
|
+
return spaceResult(await client.createSpace({
|
|
1682
|
+
name: args.name,
|
|
1683
|
+
coverUrl: args.coverUrl,
|
|
1684
|
+
duplicateFrom: args.duplicateFrom,
|
|
1685
|
+
}));
|
|
1686
|
+
}
|
|
1687
|
+
catch (err) {
|
|
1688
|
+
return errorResult(err);
|
|
1689
|
+
}
|
|
1690
|
+
});
|
|
1691
|
+
// -- update_space ---------------------------------------------------------
|
|
1692
|
+
server.registerTool('update_space', {
|
|
1693
|
+
title: 'Update Space',
|
|
1694
|
+
annotations: WRITE,
|
|
1695
|
+
description: "Rename a space or change its cover. This is a PATCH: a field you omit is left alone, so renaming does not disturb the cover. Pass coverUrl as an empty string to REMOVE the cover. To favorite or archive a space, use the `favorite` and `archive` tools with assetType 'space' instead. Requires the pipeline:write scope.",
|
|
1696
|
+
inputSchema: {
|
|
1697
|
+
spaceId: z.string().describe('The space id to update.'),
|
|
1698
|
+
name: z.string().optional().describe('A new name.'),
|
|
1699
|
+
coverUrl: z
|
|
1700
|
+
.string()
|
|
1701
|
+
.optional()
|
|
1702
|
+
.describe('A new cover image URL. Pass an empty string to remove the cover entirely.'),
|
|
1564
1703
|
},
|
|
1565
1704
|
}, async (args, extra) => {
|
|
1566
1705
|
try {
|
|
1567
1706
|
const client = await getClient(extra);
|
|
1568
|
-
|
|
1707
|
+
// An empty string is how a tool caller says "remove it": JSON Schema has no way to
|
|
1708
|
+
// distinguish an omitted string from an explicit null in a plain string field.
|
|
1709
|
+
const coverUrl = args.coverUrl === undefined ? undefined : args.coverUrl === '' ? null : args.coverUrl;
|
|
1710
|
+
return spaceResult(await client.updateSpace(args.spaceId, { name: args.name, coverUrl }));
|
|
1569
1711
|
}
|
|
1570
1712
|
catch (err) {
|
|
1571
1713
|
return errorResult(err);
|
|
1572
1714
|
}
|
|
1573
1715
|
});
|
|
1574
|
-
// --
|
|
1575
|
-
server.registerTool('
|
|
1716
|
+
// -- delete_space ---------------------------------------------------------
|
|
1717
|
+
server.registerTool('delete_space', {
|
|
1718
|
+
title: 'Delete Space',
|
|
1719
|
+
annotations: WRITE,
|
|
1720
|
+
description: 'Delete a space. The server REFUSES a space that still holds cards and names the count, because the delete cascades to every card in it along with their covers, captions, destinations and schedules. Archive the space instead if you want it out of the way. Requires the pipeline:write scope.',
|
|
1721
|
+
inputSchema: { spaceId: z.string().describe('The space id to delete.') },
|
|
1722
|
+
}, async (args, extra) => {
|
|
1723
|
+
try {
|
|
1724
|
+
const client = await getClient(extra);
|
|
1725
|
+
await client.deleteSpace(args.spaceId);
|
|
1726
|
+
return spaceDeletedResult(args.spaceId);
|
|
1727
|
+
}
|
|
1728
|
+
catch (err) {
|
|
1729
|
+
return errorResult(err);
|
|
1730
|
+
}
|
|
1731
|
+
});
|
|
1732
|
+
// -- list_stages -------------------------------------------------
|
|
1733
|
+
server.registerTool('list_stages', {
|
|
1576
1734
|
title: 'List Pipeline Stages',
|
|
1577
1735
|
annotations: READ,
|
|
1578
|
-
description: "List the account's pipeline stages, in order. Stages are user-customizable (renamed, reordered, added, removed), so call this to discover the real stages before placing a post; pass a stage's id (most stable), slug, or name to
|
|
1736
|
+
description: "List the account's pipeline stages, in order. Stages are user-customizable (renamed, reordered, added, removed), so call this to discover the real stages before placing a post; pass a stage's id (most stable), slug, or name to create_card / update_card.",
|
|
1579
1737
|
}, async (extra) => {
|
|
1580
1738
|
try {
|
|
1581
1739
|
const client = await getClient(extra);
|
|
1582
|
-
return
|
|
1740
|
+
return stageListResult(await client.listStages());
|
|
1583
1741
|
}
|
|
1584
1742
|
catch (err) {
|
|
1585
1743
|
return errorResult(err);
|
|
1586
1744
|
}
|
|
1587
1745
|
});
|
|
1588
|
-
// --
|
|
1589
|
-
server.registerTool('
|
|
1746
|
+
// -- create_card ----------------------------------------------------------
|
|
1747
|
+
server.registerTool('create_card', {
|
|
1590
1748
|
title: 'Create Post',
|
|
1591
1749
|
annotations: WRITE,
|
|
1592
|
-
description: "Create a content-pipeline post. The post is the container; attach platforms with add_post_destination and media with add_post_asset, then schedule_post or
|
|
1750
|
+
description: "Create a content-pipeline post. The post is the container; attach platforms with add_post_destination and media with add_post_asset, then schedule_post or publish_card. `stage` accepts a stage id/slug/name (defaults to the first stage). Requires a key with the pipeline:write scope.",
|
|
1593
1751
|
inputSchema: {
|
|
1594
1752
|
title: z.string().describe('Post title (required).'),
|
|
1595
1753
|
platform: z.enum(POST_PLATFORMS).describe('Primary platform for the post.'),
|
|
1596
|
-
description: z.string().optional().describe('Optional description / caption draft.'),
|
|
1597
1754
|
stage: z.string().optional().describe('Pipeline stage id, slug, or name. Defaults to the first stage.'),
|
|
1598
1755
|
coverUrl: z.string().optional().describe('Public URL for the post cover (the card thumbnail).'),
|
|
1599
1756
|
coverOutputId: z
|
|
@@ -1608,10 +1765,9 @@ export function registerTools(server, opts) {
|
|
|
1608
1765
|
}, async (args, extra) => {
|
|
1609
1766
|
try {
|
|
1610
1767
|
const client = await getClient(extra);
|
|
1611
|
-
return postSummaryResult(await client.
|
|
1768
|
+
return postSummaryResult(await client.createCard({
|
|
1612
1769
|
title: args.title,
|
|
1613
1770
|
platform: args.platform,
|
|
1614
|
-
description: args.description,
|
|
1615
1771
|
stage: args.stage,
|
|
1616
1772
|
coverUrl: args.coverUrl,
|
|
1617
1773
|
coverOutputId: args.coverOutputId,
|
|
@@ -1622,15 +1778,14 @@ export function registerTools(server, opts) {
|
|
|
1622
1778
|
return errorResult(err);
|
|
1623
1779
|
}
|
|
1624
1780
|
});
|
|
1625
|
-
// --
|
|
1626
|
-
server.registerTool('
|
|
1781
|
+
// -- update_card ----------------------------------------------------------
|
|
1782
|
+
server.registerTool('update_card', {
|
|
1627
1783
|
title: 'Update Post',
|
|
1628
1784
|
annotations: WRITE,
|
|
1629
|
-
description: "Update a post: its fields (title, description, script, notes, status, platform, cover, pipeline stage), its DESTINATIONS (which platforms it publishes to), its ASSETS (the media on it, in order), and its SCHEDULE. destinations and assets are DECLARATIVE: pass the WHOLE set, because anything you leave out is removed. Destinations key on platform. Assets key on id, and THE ARRAY ORDER IS THE carousel ORDER, so reordering is just sending the same ids in a different order; keep an existing asset by id, add a new one by assetUrl or outputId. scheduledAt sets the time on the post AND every destination (pass null to clear); give a destination its own scheduledAt to override it for that platform. To publish NOW, use
|
|
1785
|
+
description: "Update a post: its fields (title, description, script, notes, status, platform, cover, pipeline stage), its DESTINATIONS (which platforms it publishes to), its ASSETS (the media on it, in order), and its SCHEDULE. destinations and assets are DECLARATIVE: pass the WHOLE set, because anything you leave out is removed. Destinations key on platform. Assets key on id, and THE ARRAY ORDER IS THE carousel ORDER, so reordering is just sending the same ids in a different order; keep an existing asset by id, add a new one by assetUrl or outputId. scheduledAt sets the time on the post AND every destination (pass null to clear); give a destination its own scheduledAt to override it for that platform. To publish NOW, use publish_card. Requires the pipeline:write scope.",
|
|
1630
1786
|
inputSchema: {
|
|
1631
1787
|
postId: z.string().describe('The post id.'),
|
|
1632
1788
|
title: z.string().optional(),
|
|
1633
|
-
description: z.string().optional(),
|
|
1634
1789
|
platform: z.enum(POST_PLATFORMS).optional(),
|
|
1635
1790
|
status: z.enum(['draft', 'active', 'completed', 'archived']).optional(),
|
|
1636
1791
|
stage: z.string().optional().describe('Move the post to this stage (id, slug, or name).'),
|
|
@@ -1651,11 +1806,11 @@ export function registerTools(server, opts) {
|
|
|
1651
1806
|
.optional()
|
|
1652
1807
|
.describe('ISO time to publish. Sets the post AND every destination. null clears the schedule.'),
|
|
1653
1808
|
destinations: z
|
|
1654
|
-
.array(
|
|
1809
|
+
.array(postDestinationSchema)
|
|
1655
1810
|
.optional()
|
|
1656
1811
|
.describe("The post's destinations, each { platform, format?, connectedAccountId?, platformSpecificData?, scheduledAt?, status? }. REPLACES the set, keyed by platform; [] detaches all."),
|
|
1657
1812
|
assets: z
|
|
1658
|
-
.array(
|
|
1813
|
+
.array(postAssetSchema)
|
|
1659
1814
|
.optional()
|
|
1660
1815
|
.describe("The post's assets IN ORDER, each { id } to keep an existing one or { assetUrl | outputId, assetType?, displayName? } to add. REPLACES the list; [] clears it."),
|
|
1661
1816
|
},
|
|
@@ -1665,7 +1820,7 @@ export function registerTools(server, opts) {
|
|
|
1665
1820
|
const { postId, destinations, assets, ...input } = args;
|
|
1666
1821
|
// The two declarative arrays are `unknown[]` in the schema (their entries are free-form objects the
|
|
1667
1822
|
// server validates), so they are cast at this one boundary rather than duplicating the shape in zod.
|
|
1668
|
-
return postSummaryResult(await client.
|
|
1823
|
+
return postSummaryResult(await client.updateCard(postId, {
|
|
1669
1824
|
...input,
|
|
1670
1825
|
...(destinations !== undefined ? { destinations: destinations } : {}),
|
|
1671
1826
|
...(assets !== undefined ? { assets: assets } : {}),
|
|
@@ -1679,7 +1834,7 @@ export function registerTools(server, opts) {
|
|
|
1679
1834
|
server.registerTool('list_tags', {
|
|
1680
1835
|
title: 'List Tags',
|
|
1681
1836
|
annotations: READ,
|
|
1682
|
-
description: "List the account's tags (the organizational tag library). Set a post's tags with the `tags` field on
|
|
1837
|
+
description: "List the account's tags (the organizational tag library). Set a post's tags with the `tags` field on create_card / update_card. A tag is just a lowercase name.",
|
|
1683
1838
|
inputSchema: {},
|
|
1684
1839
|
}, async (extra) => {
|
|
1685
1840
|
try {
|
|
@@ -1694,7 +1849,7 @@ export function registerTools(server, opts) {
|
|
|
1694
1849
|
server.registerTool('create_tag', {
|
|
1695
1850
|
title: 'Create Tag',
|
|
1696
1851
|
annotations: WRITE,
|
|
1697
|
-
description: "Create a tag in the account's tag library (the name is lowercased). Tags organize posts; apply them with the `tags` field on
|
|
1852
|
+
description: "Create a tag in the account's tag library (the name is lowercased). Tags organize posts; apply them with the `tags` field on create_card / update_card. Requires the pipeline:write scope.",
|
|
1698
1853
|
inputSchema: {
|
|
1699
1854
|
name: z.string().describe('The tag name (lowercased on save).'),
|
|
1700
1855
|
},
|
|
@@ -1711,7 +1866,7 @@ export function registerTools(server, opts) {
|
|
|
1711
1866
|
server.registerTool('update_tag', {
|
|
1712
1867
|
title: 'Update Tag',
|
|
1713
1868
|
annotations: WRITE,
|
|
1714
|
-
description: 'Rename a tag (preserves its assignments on all posts). To detach a tag from one post, set that post\'s `tags` without it via
|
|
1869
|
+
description: 'Rename a tag (preserves its assignments on all posts). To detach a tag from one post, set that post\'s `tags` without it via update_card. Requires the pipeline:write scope.',
|
|
1715
1870
|
inputSchema: {
|
|
1716
1871
|
tagId: z.string().describe('The tag id (from list_tags).'),
|
|
1717
1872
|
name: z.string().describe('The new tag name (lowercased on save).'),
|
|
@@ -1729,7 +1884,7 @@ export function registerTools(server, opts) {
|
|
|
1729
1884
|
server.registerTool('delete_tag', {
|
|
1730
1885
|
title: 'Delete Tag',
|
|
1731
1886
|
annotations: WRITE,
|
|
1732
|
-
description: "Delete a tag from the account's library. This DESTROYS the tag and removes it from every post it was on. To just detach a tag from one post, set that post's `tags` without it via
|
|
1887
|
+
description: "Delete a tag from the account's library. This DESTROYS the tag and removes it from every post it was on. To just detach a tag from one post, set that post's `tags` without it via update_card instead. Requires the pipeline:write scope.",
|
|
1733
1888
|
inputSchema: {
|
|
1734
1889
|
tagId: z.string().describe('The tag id (from list_tags).'),
|
|
1735
1890
|
},
|
|
@@ -1742,8 +1897,8 @@ export function registerTools(server, opts) {
|
|
|
1742
1897
|
return errorResult(err);
|
|
1743
1898
|
}
|
|
1744
1899
|
});
|
|
1745
|
-
// --
|
|
1746
|
-
server.registerTool('
|
|
1900
|
+
// -- publish_card ---------------------------------------------------------
|
|
1901
|
+
server.registerTool('publish_card', {
|
|
1747
1902
|
title: 'Publish Post',
|
|
1748
1903
|
annotations: PUBLISH,
|
|
1749
1904
|
description: "Publish a post NOW to its destinations (a single platform when `platform` is given, otherwise all). Each destination must have a connected account. Requires a key with the publish:write scope; holding that scope is the account owner's consent to autonomous publishing. Returns per-destination results.",
|
|
@@ -1754,7 +1909,7 @@ export function registerTools(server, opts) {
|
|
|
1754
1909
|
}, async (args, extra) => {
|
|
1755
1910
|
try {
|
|
1756
1911
|
const client = await getClient(extra);
|
|
1757
|
-
return publishResult(await client.
|
|
1912
|
+
return publishResult(await client.publishCard(args.postId, { platform: args.platform }));
|
|
1758
1913
|
}
|
|
1759
1914
|
catch (err) {
|
|
1760
1915
|
return errorResult(err);
|
|
@@ -1876,7 +2031,7 @@ export function registerTools(server, opts) {
|
|
|
1876
2031
|
server.registerTool('list_connected_accounts', {
|
|
1877
2032
|
title: 'List Connected Accounts',
|
|
1878
2033
|
annotations: READ,
|
|
1879
|
-
description: "List the social accounts the owner has connected (the publish targets), default first. Use an account's id as connectedAccountId on add_post_destination, then
|
|
2034
|
+
description: "List the social accounts the owner has connected (the publish targets), default first. Use an account's id as connectedAccountId on add_post_destination, then publish_card. Read-only: connecting an account is done in the ContentHero app.",
|
|
1880
2035
|
}, async (extra) => {
|
|
1881
2036
|
try {
|
|
1882
2037
|
const client = await getClient(extra);
|
|
@@ -1924,10 +2079,10 @@ export function registerTools(server, opts) {
|
|
|
1924
2079
|
server.registerTool('favorite', {
|
|
1925
2080
|
title: 'Favorite',
|
|
1926
2081
|
annotations: WRITE,
|
|
1927
|
-
description: "Favorite or UNfavorite an asset: pass favorited:false to clear it (default true). For a top-level asset, pass assetType + id (
|
|
2082
|
+
description: "Favorite or UNfavorite an asset: pass favorited:false to clear it (default true). For a top-level asset, pass assetType + id (card, voice, brand_kit, project, inspiration_content, gallery, transition, space). To favorite a single studio media variation (one image/video/audio slot from list_media / get_media), pass the output id + variationIndex (1-based) and omit assetType. Requires the favorites:write scope. Idempotent in both directions.",
|
|
1928
2083
|
inputSchema: {
|
|
1929
2084
|
assetType: z
|
|
1930
|
-
.enum(['
|
|
2085
|
+
.enum(['card', 'voice', 'brand_kit', 'project', 'inspiration_content', 'gallery', 'transition', 'space'])
|
|
1931
2086
|
.optional()
|
|
1932
2087
|
.describe('The kind of asset. Required unless targeting a media variation via variationIndex.'),
|
|
1933
2088
|
id: z.string().describe('The asset id (or studio output id when using variationIndex).'),
|
|
@@ -1955,10 +2110,10 @@ export function registerTools(server, opts) {
|
|
|
1955
2110
|
server.registerTool('archive', {
|
|
1956
2111
|
title: 'Archive',
|
|
1957
2112
|
annotations: WRITE,
|
|
1958
|
-
description: "Archive or UNarchive an asset: pass archived:false to restore it (default true). ContentHero never hard-deletes, so this is always reversible. For a top-level asset, pass assetType + id (
|
|
2113
|
+
description: "Archive or UNarchive an asset: pass archived:false to restore it (default true). ContentHero never hard-deletes, so this is always reversible. For a top-level asset, pass assetType + id (card, brand_kit, brand_kit_section, project, space). To archive a single studio media variation, pass the output id + variationIndex (1-based) and omit assetType. Archiving a card sets its status to 'archived'; restoring returns it to 'draft'. Requires the favorites:write scope. Idempotent in both directions.",
|
|
1959
2114
|
inputSchema: {
|
|
1960
2115
|
assetType: z
|
|
1961
|
-
.enum(['
|
|
2116
|
+
.enum(['card', 'brand_kit', 'brand_kit_section', 'project', 'space'])
|
|
1962
2117
|
.optional()
|
|
1963
2118
|
.describe('The kind of asset. Required unless targeting a media variation via variationIndex.'),
|
|
1964
2119
|
id: z.string().describe('The asset id (or studio output id when using variationIndex).'),
|