@contenthero/mcp 0.4.5 → 0.4.7
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.map +1 -1
- package/dist/format.js +9 -1
- package/dist/format.js.map +1 -1
- package/dist/groups.d.ts +42 -0
- package/dist/groups.d.ts.map +1 -0
- package/dist/groups.js +142 -0
- package/dist/groups.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +63 -40
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -297,7 +297,7 @@ export function registerTools(server, opts) {
|
|
|
297
297
|
server.registerTool('generate_image', {
|
|
298
298
|
title: 'Generate Image',
|
|
299
299
|
annotations: WRITE,
|
|
300
|
-
description: 'Generate one or more images from a text prompt (optionally image-to-image with reference images). Waits for the result and returns the image URLs. Optionally pass projectId to place the generated image onto that project in the same call, controlled by an optional placement: a VIDEO timeline places a clip on a track, a CANVAS design places a layer on a slide (defaulting to the slide the user is focused on). Omit projectId to save a standalone library output.',
|
|
300
|
+
description: 'Generate one or more images from a text prompt (optionally image-to-image with reference images). Waits for the result and returns the image URLs. Optionally pass projectId to place the generated image onto that project in the same call, controlled by an optional placement: a VIDEO timeline places a clip on a track, a CANVAS design places a layer on a slide (defaulting to the slide the user is focused on). Omit projectId to save a standalone library output. SPENDS CREDITS: pass getCost to preview the price first, which runs nothing and charges nothing.',
|
|
301
301
|
inputSchema: {
|
|
302
302
|
modelId: z.enum(models.image).describe(IMAGE_MODEL_GUIDANCE),
|
|
303
303
|
prompt: z
|
|
@@ -360,7 +360,7 @@ export function registerTools(server, opts) {
|
|
|
360
360
|
server.registerTool('generate_board', {
|
|
361
361
|
title: 'Generate Reference Board',
|
|
362
362
|
annotations: WRITE,
|
|
363
|
-
description: 'Generate a Reference Board: a dense multi-panel reference sheet (3:4, 4K) built from a source image and/or a written description, used to keep a subject on-model across later generations (feed the board back in as a referenceImage). Provide referenceImages and/or a prompt (at least one is required). Waits up to ~50s; boards render slowly (minutes), so it usually returns an outputId to poll with get_generation_status.',
|
|
363
|
+
description: 'Generate a Reference Board: a dense multi-panel reference sheet (3:4, 4K) built from a source image and/or a written description, used to keep a subject on-model across later generations (feed the board back in as a referenceImage). Provide referenceImages and/or a prompt (at least one is required). Waits up to ~50s; boards render slowly (minutes), so it usually returns an outputId to poll with get_generation_status. SPENDS CREDITS: pass getCost to preview the price first, which runs nothing and charges nothing.',
|
|
364
364
|
inputSchema: {
|
|
365
365
|
boardType: z.enum(BOARD_TYPES).describe(BOARD_TYPE_GUIDANCE),
|
|
366
366
|
prompt: z
|
|
@@ -415,7 +415,7 @@ export function registerTools(server, opts) {
|
|
|
415
415
|
server.registerTool('generate_video', {
|
|
416
416
|
title: 'Generate Video',
|
|
417
417
|
annotations: WRITE,
|
|
418
|
-
description: 'Generate a video from a text prompt (optionally from a start/end frame or reference images/videos/audio). Waits up to ~50s; if the render is still running it returns an outputId to poll with get_generation_status. Seedance 2.0 has two input modes selected by which references you pass: a startFrame (and optional endFrame) runs start/end-frame mode; referenceImages / referenceVideos / referenceAudio (without a startFrame) run references mode. Optionally pass projectId to place the generated video onto that project in the same call, controlled by an optional placement: a VIDEO timeline places a clip on a track, a CANVAS design places a layer on a slide (defaulting to the slide the user is focused on). Omit projectId to save a standalone library output.',
|
|
418
|
+
description: 'Generate a video from a text prompt (optionally from a start/end frame or reference images/videos/audio). Waits up to ~50s; if the render is still running it returns an outputId to poll with get_generation_status. Seedance 2.0 has two input modes selected by which references you pass: a startFrame (and optional endFrame) runs start/end-frame mode; referenceImages / referenceVideos / referenceAudio (without a startFrame) run references mode. Optionally pass projectId to place the generated video onto that project in the same call, controlled by an optional placement: a VIDEO timeline places a clip on a track, a CANVAS design places a layer on a slide (defaulting to the slide the user is focused on). Omit projectId to save a standalone library output. SPENDS CREDITS: pass getCost to preview the price first, which runs nothing and charges nothing.',
|
|
419
419
|
inputSchema: {
|
|
420
420
|
modelId: z.enum(models.video).describe(VIDEO_MODEL_GUIDANCE),
|
|
421
421
|
prompt: z
|
|
@@ -516,7 +516,7 @@ export function registerTools(server, opts) {
|
|
|
516
516
|
server.registerTool('generate_audio', {
|
|
517
517
|
title: 'Generate Audio',
|
|
518
518
|
annotations: WRITE,
|
|
519
|
-
description: 'Generate audio with ElevenLabs: speech (TTS), music, or a sound effect. Returns the audio URL directly (synchronous, no polling). Optionally pass projectId to place the generated audio onto that editor project\'s timeline in the same call, controlled by an optional placement; omit projectId to save a standalone library output.',
|
|
519
|
+
description: 'Generate audio with ElevenLabs: speech (TTS), music, or a sound effect. Returns the audio URL directly (synchronous, no polling). Optionally pass projectId to place the generated audio onto that editor project\'s timeline in the same call, controlled by an optional placement; omit projectId to save a standalone library output. SPENDS CREDITS: pass getCost to preview the price first, which runs nothing and charges nothing.',
|
|
520
520
|
inputSchema: {
|
|
521
521
|
modelId: z.enum(models.audio).describe(AUDIO_MODEL_GUIDANCE),
|
|
522
522
|
prompt: z.string().optional().describe('For music / sfx: what to generate.'),
|
|
@@ -562,7 +562,7 @@ export function registerTools(server, opts) {
|
|
|
562
562
|
server.registerTool('edit_audio', {
|
|
563
563
|
title: 'Edit Audio',
|
|
564
564
|
annotations: WRITE,
|
|
565
|
-
description: 'Transform existing audio with an audio-processing model, in one of TWO shapes. FILE mode: pass sourceUrl to process a standalone file into a new library asset. Voice isolation removes background noise and music and returns the processed URL directly; audio enhancement levels loudness and cleans up background noise, is asynchronous, and returns an outputId to poll with get_generation_status. Optionally pass projectId to place the result onto that editor project\'s timeline in the same call, controlled by an optional placement. IN-PLACE mode: pass projectId with clipIds (or enhanceClips for the whole timeline) to enhance the audio OF EXISTING CLIPS instead of producing a new asset, which is how you clean up a recording already on a timeline. In-place returns a LIST on outputs, one job per SOURCE, because the vendor estimates a noise profile per production: one recording\'s clips are concatenated and enhanced together so the level and noise floor stay consistent across cuts, while separate recordings stay separate jobs. Poll every outputId. The enhanced audio is applied to the clips automatically when each job lands: an audio clip has its source swapped, and a video clip is muted with the enhanced audio placed on its own clip. Silenced clips are skipped. In-place mode is enhancement only and needs no sourceUrl.',
|
|
565
|
+
description: 'Transform existing audio with an audio-processing model, in one of TWO shapes. FILE mode: pass sourceUrl to process a standalone file into a new library asset. Voice isolation removes background noise and music and returns the processed URL directly; audio enhancement levels loudness and cleans up background noise, is asynchronous, and returns an outputId to poll with get_generation_status. Optionally pass projectId to place the result onto that editor project\'s timeline in the same call, controlled by an optional placement. IN-PLACE mode: pass projectId with clipIds (or enhanceClips for the whole timeline) to enhance the audio OF EXISTING CLIPS instead of producing a new asset, which is how you clean up a recording already on a timeline. In-place returns a LIST on outputs, one job per SOURCE, because the vendor estimates a noise profile per production: one recording\'s clips are concatenated and enhanced together so the level and noise floor stay consistent across cuts, while separate recordings stay separate jobs. Poll every outputId. The enhanced audio is applied to the clips automatically when each job lands: an audio clip has its source swapped, and a video clip is muted with the enhanced audio placed on its own clip. Silenced clips are skipped. In-place mode is enhancement only and needs no sourceUrl. SPENDS CREDITS: pass getCost to preview the price first, which runs nothing and charges nothing.',
|
|
566
566
|
inputSchema: {
|
|
567
567
|
modelId: z.enum(models.editAudio).describe(EDIT_AUDIO_MODEL_GUIDANCE),
|
|
568
568
|
sourceUrl: z
|
|
@@ -619,7 +619,7 @@ export function registerTools(server, opts) {
|
|
|
619
619
|
server.registerTool('upscale', {
|
|
620
620
|
title: 'Upscale',
|
|
621
621
|
annotations: WRITE,
|
|
622
|
-
description: 'Upscale an existing image or video to a higher resolution. Provide the source media URL and a model-supported factor. Waits for the result; if the job is still running it returns an outputId to poll with get_generation_status.',
|
|
622
|
+
description: 'Upscale an existing image or video to a higher resolution. Provide the source media URL and a model-supported factor. Waits for the result; if the job is still running it returns an outputId to poll with get_generation_status. SPENDS CREDITS: pass getCost to preview the price first, which runs nothing and charges nothing.',
|
|
623
623
|
inputSchema: {
|
|
624
624
|
modelId: z.enum(models.upscale).describe(UPSCALE_MODEL_GUIDANCE),
|
|
625
625
|
sourceUrl: z.string().describe('The source image (image upscalers) or video (video upscalers): a URL or a previous output id (e.g. "<id>-1") to upscale an earlier generation.'),
|
|
@@ -660,7 +660,7 @@ export function registerTools(server, opts) {
|
|
|
660
660
|
server.registerTool('generate_lip_sync', {
|
|
661
661
|
title: 'Generate Lip Sync',
|
|
662
662
|
annotations: WRITE,
|
|
663
|
-
description: 'Animate a portrait image so the subject speaks. Provide imageUrl (the face) plus a voice source: either audioUrl (an existing speech clip) or script + voiceId (we synthesize the speech). Optional motionPrompt nudges expression/motion. Waits up to ~50s; if still rendering it returns an outputId to poll with get_generation_status.',
|
|
663
|
+
description: 'Animate a portrait image so the subject speaks. Provide imageUrl (the face) plus a voice source: either audioUrl (an existing speech clip) or script + voiceId (we synthesize the speech). Optional motionPrompt nudges expression/motion. Waits up to ~50s; if still rendering it returns an outputId to poll with get_generation_status. SPENDS CREDITS: pass getCost to preview the price first, which runs nothing and charges nothing.',
|
|
664
664
|
inputSchema: {
|
|
665
665
|
modelId: z.enum(models.lipSync).describe(LIP_SYNC_MODEL_GUIDANCE),
|
|
666
666
|
imageUrl: z.string().describe('The portrait to animate (the speaking subject): an image URL or a previous output id (e.g. "<id>-1") to chain.'),
|
|
@@ -720,8 +720,18 @@ export function registerTools(server, opts) {
|
|
|
720
720
|
// -- transcribe -----------------------------------------------------------
|
|
721
721
|
server.registerTool('transcribe', {
|
|
722
722
|
title: 'Transcribe Audio',
|
|
723
|
-
|
|
724
|
-
|
|
723
|
+
// NOT read-only, despite only returning text. readOnlyHint is a host's signal that a
|
|
724
|
+
// tool is safe to call without asking the user, and this one is metered per minute of
|
|
725
|
+
// audio: annotated READ, an agent could transcribe a two-hour file repeatedly,
|
|
726
|
+
// unattended, spending real credits. Every other metered tool here is a write with a
|
|
727
|
+
// getCost preflight, and a test now holds that line.
|
|
728
|
+
//
|
|
729
|
+
// ⚠️ This tool has NO getCost, and that is a server limitation, not an oversight:
|
|
730
|
+
// POST /api/v1/studio/transcribe does not accept the flag, and pricing the call
|
|
731
|
+
// means knowing the audio's duration before transcribing it. Until the route can
|
|
732
|
+
// price it, the cost is only knowable after the fact, from creditsUsed on the
|
|
733
|
+
// result. It is the single documented entry in METERED_WITHOUT_PREFLIGHT.
|
|
734
|
+
description: 'Transcribe an audio URL to text (speech-to-text). Returns the transcript directly (synchronous, no polling). SPENDS CREDITS, metered per minute of audio, and the cost cannot be previewed: the result reports the credits it cost after the fact. Zero only when the account runs on its own ElevenLabs key.',
|
|
725
735
|
inputSchema: {
|
|
726
736
|
audioUrl: z.string().describe('Public URL of the audio file to transcribe.'),
|
|
727
737
|
languageCode: z
|
|
@@ -791,7 +801,7 @@ export function registerTools(server, opts) {
|
|
|
791
801
|
.string()
|
|
792
802
|
.optional()
|
|
793
803
|
.describe('Free-text description of the character. The strongest single input when no reference photos are given.'),
|
|
794
|
-
defaultVoiceId: z.string().optional().describe(
|
|
804
|
+
defaultVoiceId: z.string().optional().describe("A voiceId from list_voices, used as this avatar's default voice."),
|
|
795
805
|
referenceImageUrls: z
|
|
796
806
|
.array(z.string())
|
|
797
807
|
.optional()
|
|
@@ -961,7 +971,7 @@ export function registerTools(server, opts) {
|
|
|
961
971
|
server.registerTool('create_brand_kit', {
|
|
962
972
|
title: 'Create Brand Kit',
|
|
963
973
|
annotations: WRITE,
|
|
964
|
-
description: "Create a brand kit. THREE SOURCES, chosen by what you pass: (1) EMPTY, just a name, then fill it in with update_brand_kit; or FROM A SOCIAL PROFILE, pass its url in brandAccounts (your own) or inspirationAccounts (a creator you watch) with no name at all, and the kit is named after the handle and starts ingesting that account's posts if it is YouTube or Instagram; (2) FROM A WEBSITE, pass websiteUrl + extract:true and ContentHero scrapes that site and fills in business name, positioning, voice,
|
|
974
|
+
description: "Create a brand kit. THREE SOURCES, chosen by what you pass: (1) EMPTY, just a name, then fill it in with update_brand_kit; or FROM A SOCIAL PROFILE, pass its url in brandAccounts (your own) or inspirationAccounts (a creator you watch) with no name at all, and the kit is named after the handle and starts ingesting that account's posts if it is YouTube or Instagram; (2) FROM A WEBSITE, pass websiteUrl + extract:true and ContentHero scrapes that site and fills in business name, positioning, voice, colors, typography, logos and assets by itself, which is by far the fastest way to get a real kit; (3) A COPY, pass duplicateFrom with an existing kit id, which copies its sections and brand media (assets re-link rather than duplicate, so a copy costs no storage). A brand with NO WEBSITE (so nothing to extract) is built by passing its fields directly, including logos, whose entries may name outputId to bring in a generation you just made rather than a url. With extract it RETURNS IMMEDIATELY, before the kit has any content: that empty kit is the handle, and the fields fill in over the next minute or two, so poll extractionStatus with get_brand_kit rather than assuming it failed. name is OPTIONAL when websiteUrl or a social profile url is given: it defaults to the site's hostname or the @handle, a placeholder extraction or you overwrite later. Brand kits are capped by plan, so this fails with a limit error near the cap, and a duplicate counts against it like any other kit. Requires the brandkit:write scope.",
|
|
965
975
|
inputSchema: {
|
|
966
976
|
name: z.string().optional().describe("The kit's name. Optional when websiteUrl is given."),
|
|
967
977
|
websiteUrl: z.string().optional().describe('The business website. Required to use extract.'),
|
|
@@ -970,14 +980,14 @@ export function registerTools(server, opts) {
|
|
|
970
980
|
.optional()
|
|
971
981
|
.describe('Scrape websiteUrl and fill the kit in automatically. Returns at once; poll extractionStatus.'),
|
|
972
982
|
duplicateFrom: z.string().optional().describe('Copy an existing brand kit id instead of starting empty.'),
|
|
973
|
-
businessName: z.string().optional(),
|
|
974
|
-
primaryOffer: z.string().optional(),
|
|
975
|
-
nicheDefinition: z.string().optional(),
|
|
983
|
+
businessName: z.string().optional().describe('The business or creator name this kit represents.'),
|
|
984
|
+
primaryOffer: z.string().optional().describe('What this business sells, in one line. Grounds copy in what is actually being promoted.'),
|
|
985
|
+
nicheDefinition: z.string().optional().describe('The niche this brand operates in. Keeps generated angles on-topic.'),
|
|
976
986
|
positioning: z.record(z.string(), z.unknown()).optional().describe('Positioning object (free-form).'),
|
|
977
987
|
audience: z.record(z.string(), z.unknown()).optional().describe('Audience object (free-form).'),
|
|
978
988
|
voiceProfile: z.record(z.string(), z.unknown()).optional().describe('Voice profile object (tone, style, ...).'),
|
|
979
|
-
visualStyle: z.string().optional(),
|
|
980
|
-
designPrinciples: z.array(z.string()).optional(),
|
|
989
|
+
visualStyle: z.string().optional().describe("The look in words, e.g. 'warm film grain, muted earth tones'. Grounds image and video prompts."),
|
|
990
|
+
designPrinciples: z.array(z.string()).optional().describe('Design rules to hold to, one per entry. REPLACES the list; [] clears it.'),
|
|
981
991
|
contentStrategy: z.record(z.string(), z.unknown()).optional().describe('Content strategy object (free-form).'),
|
|
982
992
|
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."),
|
|
983
993
|
assets: z.array(assetEntrySchema).optional().describe("The kit's brand assets, each { url | outputId, name? }."),
|
|
@@ -1049,16 +1059,16 @@ export function registerTools(server, opts) {
|
|
|
1049
1059
|
.array(accountEntrySchema)
|
|
1050
1060
|
.optional()
|
|
1051
1061
|
.describe('Competitor/creator profiles they watch. Same entry shape as brandAccounts. REPLACES the list; [] clears it.'),
|
|
1052
|
-
name: z.string().optional(),
|
|
1053
|
-
businessName: z.string().optional(),
|
|
1054
|
-
websiteUrl: z.string().optional(),
|
|
1055
|
-
primaryOffer: z.string().optional(),
|
|
1056
|
-
nicheDefinition: z.string().optional(),
|
|
1062
|
+
name: z.string().optional().describe('Rename the kit. This is the label in the UI, not the business name.'),
|
|
1063
|
+
businessName: z.string().optional().describe('The business or creator name this kit represents.'),
|
|
1064
|
+
websiteUrl: z.string().optional().describe('The business website. Stored as a reference; it does not re-extract on its own.'),
|
|
1065
|
+
primaryOffer: z.string().optional().describe('What this business sells, in one line. Grounds copy in what is actually being promoted.'),
|
|
1066
|
+
nicheDefinition: z.string().optional().describe('The niche this brand operates in. Keeps generated angles on-topic.'),
|
|
1057
1067
|
positioning: z.record(z.string(), z.unknown()).optional().describe('Positioning object (free-form).'),
|
|
1058
1068
|
audience: z.record(z.string(), z.unknown()).optional().describe('Audience object (free-form).'),
|
|
1059
1069
|
voiceProfile: z.record(z.string(), z.unknown()).optional().describe('Voice profile object (tone, style, ...).'),
|
|
1060
|
-
visualStyle: z.string().optional(),
|
|
1061
|
-
designPrinciples: z.array(z.string()).optional(),
|
|
1070
|
+
visualStyle: z.string().optional().describe("The look in words, e.g. 'warm film grain, muted earth tones'. Grounds image and video prompts."),
|
|
1071
|
+
designPrinciples: z.array(z.string()).optional().describe('Design rules to hold to, one per entry. REPLACES the list; [] clears it.'),
|
|
1062
1072
|
contentStrategy: z.record(z.string(), z.unknown()).optional().describe('Content strategy object (free-form).'),
|
|
1063
1073
|
},
|
|
1064
1074
|
}, async (args, extra) => {
|
|
@@ -1461,7 +1471,7 @@ export function registerTools(server, opts) {
|
|
|
1461
1471
|
server.registerTool('create_media_upload', {
|
|
1462
1472
|
title: 'Create Media Upload',
|
|
1463
1473
|
annotations: WRITE,
|
|
1464
|
-
description: 'Upload a local file as first-class media (phase 1 of 2). Returns a signed uploadUrl and the exact headers to send; PUT the file bytes to that URL with those headers unchanged, then call complete_media_upload with the returned outputId. The finished media is referenceable by outputId in generate_* and
|
|
1474
|
+
description: 'Upload a local file as first-class media (phase 1 of 2). Returns a signed uploadUrl and the exact headers to send; PUT the file bytes to that URL with those headers unchanged, then call complete_media_upload with the returned outputId. The finished media is referenceable by outputId in generate_* and as an asset on a card via update_card. For a file already on a public URL, use import_media instead. Requires the assets:write scope.',
|
|
1465
1475
|
inputSchema: {
|
|
1466
1476
|
fileName: z.string().describe('The file name (used for its extension), e.g. "cover.png".'),
|
|
1467
1477
|
contentType: z.string().describe('The file MIME type, e.g. "image/png" or "video/mp4".'),
|
|
@@ -1501,7 +1511,7 @@ export function registerTools(server, opts) {
|
|
|
1501
1511
|
server.registerTool('import_media', {
|
|
1502
1512
|
title: 'Import Media',
|
|
1503
1513
|
annotations: WRITE,
|
|
1504
|
-
description: 'Import a remote URL as first-class media: the server fetches and re-hosts it, returning its outputId + public URL (referenceable by outputId in generate_* and
|
|
1514
|
+
description: 'Import a remote URL as first-class media: the server fetches and re-hosts it, returning its outputId + public URL (referenceable by outputId in generate_* and as an asset on a card via update_card). Use this for a file already on a public URL, or from a hosted client that cannot read local files. Requires the assets:write scope.',
|
|
1505
1515
|
inputSchema: {
|
|
1506
1516
|
url: z.string().describe('A public http(s) URL to fetch and re-host.'),
|
|
1507
1517
|
contentType: z.string().optional().describe('Optional MIME override (else taken from the response).'),
|
|
@@ -1659,9 +1669,9 @@ export function registerTools(server, opts) {
|
|
|
1659
1669
|
description: "Update a saved element's name, description, or category.",
|
|
1660
1670
|
inputSchema: {
|
|
1661
1671
|
elementId: z.string().describe('The element id.'),
|
|
1662
|
-
name: z.string().optional(),
|
|
1663
|
-
description: z.string().optional(),
|
|
1664
|
-
category: z.enum(['auto', 'character', 'location', 'prop']).optional(),
|
|
1672
|
+
name: z.string().optional().describe('Rename the element.'),
|
|
1673
|
+
description: z.string().optional().describe('What this element is, in words. This is what makes it findable later.'),
|
|
1674
|
+
category: z.enum(['auto', 'character', 'location', 'prop']).optional().describe("What kind of element this is. 'auto' lets the server classify it from the image."),
|
|
1665
1675
|
},
|
|
1666
1676
|
}, async (args, extra) => {
|
|
1667
1677
|
try {
|
|
@@ -1775,7 +1785,7 @@ export function registerTools(server, opts) {
|
|
|
1775
1785
|
server.registerTool('get_card', {
|
|
1776
1786
|
title: 'Get Card',
|
|
1777
1787
|
annotations: READ,
|
|
1778
|
-
description:
|
|
1788
|
+
description: 'Get one CARD in full: its fields (title, description, script, notes, status, stage, schedule), plus its posts (one per platform, which is how it publishes) and its attached assets in carousel order.',
|
|
1779
1789
|
inputSchema: {
|
|
1780
1790
|
cardId: z.string().describe('The card id from list_cards.'),
|
|
1781
1791
|
},
|
|
@@ -2012,7 +2022,7 @@ export function registerTools(server, opts) {
|
|
|
2012
2022
|
server.registerTool('create_card', {
|
|
2013
2023
|
title: 'Create Card',
|
|
2014
2024
|
annotations: WRITE,
|
|
2015
|
-
description: "Create a card, the container in the content pipeline. A card holds the work (title,
|
|
2025
|
+
description: "Create a card, the container in the content pipeline. A card holds the work (title, notes, cover) and the posts that publish it. Attach posts and media by passing `posts` and `assets` to update_card, then publish with publish_post. ⚠️ WITHOUT spaceId THIS LANDS ON THE ACCOUNT'S DEFAULT BOARD, which is rarely what you want once more than one space exists, so call list_spaces first. `stage` accepts a stage id/slug/name and DECIDES the space when it is an id; a spaceId that disagrees with it is rejected rather than guessed. Requires a key with the planner:write scope.",
|
|
2016
2026
|
inputSchema: {
|
|
2017
2027
|
title: z.string().describe('Post title (required).'),
|
|
2018
2028
|
platform: z.enum(POST_PLATFORMS).describe('Primary platform for the post.'),
|
|
@@ -2052,12 +2062,12 @@ export function registerTools(server, opts) {
|
|
|
2052
2062
|
server.registerTool('update_card', {
|
|
2053
2063
|
title: 'Update Card',
|
|
2054
2064
|
annotations: WRITE,
|
|
2055
|
-
description: "Update a card: its fields (title,
|
|
2065
|
+
description: "Update a card: its fields (title, notes, platform, cover, stage), its POSTS (one per platform, which is how it publishes), its ASSETS (the media on it, in order), and its SCHEDULE. posts and assets are DECLARATIVE: pass the WHOLE set, because anything you leave out is removed. Posts 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 card AND every post (pass null to clear); give a post its own scheduledAt to override it for that platform. To publish NOW, use publish_post. Pass spaceId to MOVE the card to another space; without a stage it lands in the target space's stage whose slug matches its current one, or that space's first stage. Pass cardIds to update several cards at once, which crossed with spaceId is how a selection moves in one call; fields that describe ONE card (title, notes, cover) still need exactly one. WRITING notes REQUIRES expectedRevision: read the card first and pass the revision it reported, or the call is refused. Requires the planner:write scope.",
|
|
2056
2066
|
inputSchema: {
|
|
2057
2067
|
cardId: z.string().describe('The card id.'),
|
|
2058
|
-
title: z.string().optional(),
|
|
2059
|
-
platform: z.enum(POST_PLATFORMS).optional(),
|
|
2060
|
-
stage: z.string().optional().describe('Move the
|
|
2068
|
+
title: z.string().optional().describe('Rename the card. Keep it short: a long title wraps and makes the column unreadable.'),
|
|
2069
|
+
platform: z.enum(POST_PLATFORMS).optional().describe("The card's primary platform. This is a label on the card; what actually publishes is its posts."),
|
|
2070
|
+
stage: z.string().optional().describe('Move the card to this stage (id, slug, or name).'),
|
|
2061
2071
|
spaceId: z
|
|
2062
2072
|
.string()
|
|
2063
2073
|
.optional()
|
|
@@ -2065,9 +2075,18 @@ export function registerTools(server, opts) {
|
|
|
2065
2075
|
cardIds: z
|
|
2066
2076
|
.array(z.string())
|
|
2067
2077
|
.optional()
|
|
2068
|
-
.describe('Update several cards at once. Fields that describe ONE card (title, notes,
|
|
2069
|
-
|
|
2070
|
-
|
|
2078
|
+
.describe('Update several cards at once. Fields that describe ONE card (title, notes, cover) still require exactly one.'),
|
|
2079
|
+
expectedRevision: z
|
|
2080
|
+
.number()
|
|
2081
|
+
.int()
|
|
2082
|
+
.optional()
|
|
2083
|
+
.describe('REQUIRED when writing notes: the revision get_card reported for this card. A card\'s notes have four '
|
|
2084
|
+
+ 'independent writers, each reading the whole document and writing it back, so without this the last '
|
|
2085
|
+
+ 'writer silently erases the others and still gets a success. If the card changed since you read it, '
|
|
2086
|
+
+ 'the call fails with a conflict carrying the current notes and revision: merge onto those and retry '
|
|
2087
|
+
+ 'with the revision they came with. Never guess it or add one to it, because it only advances when '
|
|
2088
|
+
+ 'notes actually change.'),
|
|
2089
|
+
notes: z.string().optional().describe('Working notes on the card. Plain text or markdown; tables render here. No emojis.'),
|
|
2071
2090
|
coverUrl: z.string().optional().describe('Public URL for the post cover.'),
|
|
2072
2091
|
coverOutputId: z
|
|
2073
2092
|
.string()
|
|
@@ -2253,8 +2272,8 @@ export function registerTools(server, opts) {
|
|
|
2253
2272
|
contentType: z.string().optional().describe("Filter by content type, e.g. 'video', 'short', 'reel'."),
|
|
2254
2273
|
outlierScoreMin: z.number().optional().describe('Only content at or above this outlier score.'),
|
|
2255
2274
|
outlierScoreMax: z.number().optional().describe('Only content at or below this outlier score.'),
|
|
2256
|
-
viewsMin: z.number().optional(),
|
|
2257
|
-
viewsMax: z.number().optional(),
|
|
2275
|
+
viewsMin: z.number().optional().describe('Only content at or above this view count.'),
|
|
2276
|
+
viewsMax: z.number().optional().describe('Only content at or below this view count.'),
|
|
2258
2277
|
durationMin: z.number().optional().describe('Minimum duration in seconds.'),
|
|
2259
2278
|
durationMax: z.number().optional().describe('Maximum duration in seconds.'),
|
|
2260
2279
|
subscribersMin: z.number().optional().describe("Minimum follower count of the post's account."),
|
|
@@ -2513,7 +2532,11 @@ export function registerTools(server, opts) {
|
|
|
2513
2532
|
});
|
|
2514
2533
|
server.registerTool('create_preview', {
|
|
2515
2534
|
title: 'Create Preview',
|
|
2516
|
-
|
|
2535
|
+
// NOT read-only. It does not charge the caller's credits (unlike transcribe), but it
|
|
2536
|
+
// STARTS A RENDER JOB: it returns a renderId you then poll, which is state that did
|
|
2537
|
+
// not exist before the call. readOnlyHint says a tool does not modify its
|
|
2538
|
+
// environment, and dispatching a Lambda render does. get_preview, which only reads
|
|
2539
|
+
// that job, stays READ.
|
|
2517
2540
|
description: "Create an async PREVIEW of your work (ephemeral, never stored, not a deliverable). Currently a short low-res COMPOSED VIDEO of an editor range, so you can assess motion, cuts, transitions, and pacing that a still cannot show. This is a JOB: it returns a renderId + bucketName; poll get_preview with those until it is done, then fetch the returned url. To see a single frame or a few frames instead (cheaper, instant), use get_context render. Requires the context:read scope.",
|
|
2518
2541
|
inputSchema: {
|
|
2519
2542
|
projectId: z.string().describe('The editor project to preview.'),
|