@socialneuron/mcp-server 1.8.0 → 1.8.1
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/CHANGELOG.md +9 -0
- package/dist/http.js +87 -33
- package/dist/index.js +87 -33
- package/dist/sn.js +87 -33
- package/package.json +2 -2
- package/tools.lock.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@socialneuron/mcp-server` will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.8.1] - 2026-07-14
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **`create_storyboard` empty responses.** The tool now accepts the AI service's canonical `text` response field as well as the legacy `content` field, and reports a real tool error when neither contains a storyboard.
|
|
10
|
+
- **Project isolation across the creative workflow.** `generate_image`, `create_storyboard`, `generate_voiceover`, and `render_hyperframes` now accept `project_id`. Analytics refresh, analytics reads, performance insights, and best-time recommendations are also project-scoped and default to the active project context.
|
|
11
|
+
- **Autopilot run visibility.** `get_autopilot_status` now returns recent runs supplied by the backend instead of replacing them with an empty array.
|
|
12
|
+
- **HyperFrames capability copy.** Removed the obsolete Phase-2/runtime-not-installed warning now that live HyperFrames renders complete successfully, and corrected image/storyboard/voiceover credit descriptions to match the client budget checks.
|
|
13
|
+
|
|
5
14
|
## [1.8.0] - 2026-07-14
|
|
6
15
|
|
|
7
16
|
### Changed
|
package/dist/http.js
CHANGED
|
@@ -1997,7 +1997,7 @@ function checkRateLimit(category, key) {
|
|
|
1997
1997
|
init_supabase();
|
|
1998
1998
|
|
|
1999
1999
|
// src/lib/version.ts
|
|
2000
|
-
var MCP_VERSION = "1.8.
|
|
2000
|
+
var MCP_VERSION = "1.8.1";
|
|
2001
2001
|
|
|
2002
2002
|
// src/tools/ideation.ts
|
|
2003
2003
|
function asEnvelope(data) {
|
|
@@ -2717,7 +2717,7 @@ function registerContentTools(server) {
|
|
|
2717
2717
|
);
|
|
2718
2718
|
server.tool(
|
|
2719
2719
|
"generate_image",
|
|
2720
|
-
"Start an async AI image generation job \u2014 returns a job_id immediately. Poll with check_status every 5-15s until complete. Costs
|
|
2720
|
+
"Start an async AI image generation job \u2014 returns a job_id immediately. Poll with check_status every 5-15s until complete. Costs 15-50 credits depending on model. Use for social media posts, carousel slides, or as input to generate_video (image-to-video). Pass project_id so the asset is stored with the correct brand/project.",
|
|
2721
2721
|
{
|
|
2722
2722
|
prompt: z2.string().max(2e3).describe(
|
|
2723
2723
|
"Text prompt describing the image to generate. Be specific about style, composition, colors, lighting, and subject matter."
|
|
@@ -2739,9 +2739,10 @@ function registerContentTools(server) {
|
|
|
2739
2739
|
image_url: z2.string().optional().describe(
|
|
2740
2740
|
"Reference image URL for image-to-image generation. Required for ideogram model. Optional for others."
|
|
2741
2741
|
),
|
|
2742
|
+
project_id: z2.string().optional().describe("Project ID to associate the generated image with."),
|
|
2742
2743
|
response_format: z2.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
2743
2744
|
},
|
|
2744
|
-
async ({ prompt, model, aspect_ratio, image_url, response_format }) => {
|
|
2745
|
+
async ({ prompt, model, aspect_ratio, image_url, project_id, response_format }) => {
|
|
2745
2746
|
const format = response_format ?? "text";
|
|
2746
2747
|
const userId = await getDefaultUserId();
|
|
2747
2748
|
const assetBudget = checkAssetBudget();
|
|
@@ -2777,7 +2778,8 @@ function registerContentTools(server) {
|
|
|
2777
2778
|
prompt,
|
|
2778
2779
|
model,
|
|
2779
2780
|
aspectRatio: aspect_ratio ?? "1:1",
|
|
2780
|
-
imageUrl: image_url
|
|
2781
|
+
imageUrl: image_url,
|
|
2782
|
+
...project_id && { projectId: project_id }
|
|
2781
2783
|
},
|
|
2782
2784
|
{ timeoutMs: 3e4 }
|
|
2783
2785
|
);
|
|
@@ -2816,7 +2818,8 @@ function registerContentTools(server) {
|
|
|
2816
2818
|
jobId,
|
|
2817
2819
|
taskId: data.taskId,
|
|
2818
2820
|
asyncJobId: data.asyncJobId,
|
|
2819
|
-
model: data.model
|
|
2821
|
+
model: data.model,
|
|
2822
|
+
projectId: project_id ?? null
|
|
2820
2823
|
}),
|
|
2821
2824
|
null,
|
|
2822
2825
|
2
|
|
@@ -2997,7 +3000,7 @@ function registerContentTools(server) {
|
|
|
2997
3000
|
);
|
|
2998
3001
|
server.tool(
|
|
2999
3002
|
"create_storyboard",
|
|
3000
|
-
"Plan a multi-scene video storyboard with AI-generated prompts, durations, captions, and voiceover text per frame. Use before generate_video or generate_image to create cohesive multi-shot content. Include brand_context from get_brand_profile for consistent
|
|
3003
|
+
"Plan a multi-scene video storyboard with AI-generated prompts, durations, captions, and voiceover text per frame. Use before generate_video or generate_image to create cohesive multi-shot content. Include brand_context from get_brand_profile and project_id for consistent, project-scoped production. Costs 10 credits.",
|
|
3001
3004
|
{
|
|
3002
3005
|
concept: z2.string().max(2e3).describe(
|
|
3003
3006
|
'The video concept/idea. Include: hook, key messages, target audience, and desired outcome (e.g., "TikTok ad for VPN app targeting privacy-conscious millennials, hook with shocking stat about data leaks").'
|
|
@@ -3021,6 +3024,7 @@ function registerContentTools(server) {
|
|
|
3021
3024
|
style: z2.string().optional().describe(
|
|
3022
3025
|
'Visual style direction (e.g., "cinematic", "anime", "documentary", "motion graphics").'
|
|
3023
3026
|
),
|
|
3027
|
+
project_id: z2.string().optional().describe("Project ID for brand-scoped generation and attribution."),
|
|
3024
3028
|
response_format: z2.enum(["text", "json"]).optional().describe(
|
|
3025
3029
|
"Response format. Defaults to json for structured storyboard data."
|
|
3026
3030
|
)
|
|
@@ -3032,6 +3036,7 @@ function registerContentTools(server) {
|
|
|
3032
3036
|
target_duration,
|
|
3033
3037
|
num_scenes,
|
|
3034
3038
|
style,
|
|
3039
|
+
project_id,
|
|
3035
3040
|
response_format
|
|
3036
3041
|
}) => {
|
|
3037
3042
|
const format = response_format ?? "json";
|
|
@@ -3114,7 +3119,8 @@ Return ONLY valid JSON in this exact format:
|
|
|
3114
3119
|
prompt: storyboardPrompt,
|
|
3115
3120
|
type: "storyboard",
|
|
3116
3121
|
model: "gemini-2.5-flash",
|
|
3117
|
-
responseFormat: "json"
|
|
3122
|
+
responseFormat: "json",
|
|
3123
|
+
...project_id && { projectId: project_id }
|
|
3118
3124
|
},
|
|
3119
3125
|
{ timeoutMs: 6e4 }
|
|
3120
3126
|
);
|
|
@@ -3129,7 +3135,18 @@ Return ONLY valid JSON in this exact format:
|
|
|
3129
3135
|
isError: true
|
|
3130
3136
|
};
|
|
3131
3137
|
}
|
|
3132
|
-
const rawContent = data?.content
|
|
3138
|
+
const rawContent = data?.content?.trim() || data?.text?.trim() || "";
|
|
3139
|
+
if (!rawContent) {
|
|
3140
|
+
return {
|
|
3141
|
+
content: [
|
|
3142
|
+
{
|
|
3143
|
+
type: "text",
|
|
3144
|
+
text: "Storyboard generation failed: the AI service returned an empty response."
|
|
3145
|
+
}
|
|
3146
|
+
],
|
|
3147
|
+
isError: true
|
|
3148
|
+
};
|
|
3149
|
+
}
|
|
3133
3150
|
addCreditsUsed(estimatedCost);
|
|
3134
3151
|
if (format === "json") {
|
|
3135
3152
|
try {
|
|
@@ -3165,16 +3182,17 @@ Return ONLY valid JSON in this exact format:
|
|
|
3165
3182
|
);
|
|
3166
3183
|
server.tool(
|
|
3167
3184
|
"generate_voiceover",
|
|
3168
|
-
"Generate a voiceover audio file for video narration. Returns an R2-hosted audio URL. Use after create_storyboard to add narration to each scene, or standalone for podcast intros and ad reads. Costs
|
|
3185
|
+
"Generate a voiceover audio file for video narration. Returns an R2-hosted audio URL. Use after create_storyboard to add narration to each scene, or standalone for podcast intros and ad reads. Pass project_id to keep the asset with the correct brand/project. Costs 15 credits per generation.",
|
|
3169
3186
|
{
|
|
3170
3187
|
text: z2.string().max(5e3).describe("The script/text to convert to speech."),
|
|
3171
3188
|
voice: z2.enum(["rachel", "domi"]).optional().describe(
|
|
3172
3189
|
"Voice selection. rachel=warm female, domi=confident female. Defaults to rachel."
|
|
3173
3190
|
),
|
|
3174
3191
|
speed: z2.number().min(0.5).max(2).optional().describe("Speech speed multiplier. 1.0 is normal. Defaults to 1.0."),
|
|
3192
|
+
project_id: z2.string().optional().describe("Project ID to associate the generated voiceover with."),
|
|
3175
3193
|
response_format: z2.enum(["text", "json"]).optional().describe("Response format. Defaults to text.")
|
|
3176
3194
|
},
|
|
3177
|
-
async ({ text, voice, speed, response_format }) => {
|
|
3195
|
+
async ({ text, voice, speed, project_id, response_format }) => {
|
|
3178
3196
|
const format = response_format ?? "text";
|
|
3179
3197
|
const userId = await getDefaultUserId();
|
|
3180
3198
|
const estimatedCost = 15;
|
|
@@ -3209,7 +3227,8 @@ Return ONLY valid JSON in this exact format:
|
|
|
3209
3227
|
{
|
|
3210
3228
|
text,
|
|
3211
3229
|
voiceId: ELEVENLABS_VOICE_IDS[voice ?? "rachel"],
|
|
3212
|
-
speed: speed ?? 1
|
|
3230
|
+
speed: speed ?? 1,
|
|
3231
|
+
...project_id && { projectId: project_id }
|
|
3213
3232
|
},
|
|
3214
3233
|
{ timeoutMs: 6e4 }
|
|
3215
3234
|
);
|
|
@@ -3245,7 +3264,8 @@ Return ONLY valid JSON in this exact format:
|
|
|
3245
3264
|
asEnvelope2({
|
|
3246
3265
|
audioUrl: data.audioUrl,
|
|
3247
3266
|
durationSeconds: data.durationSeconds,
|
|
3248
|
-
voice: voice ?? "rachel"
|
|
3267
|
+
voice: voice ?? "rachel",
|
|
3268
|
+
projectId: project_id ?? null
|
|
3249
3269
|
}),
|
|
3250
3270
|
null,
|
|
3251
3271
|
2
|
|
@@ -5713,7 +5733,7 @@ function asEnvelope4(data) {
|
|
|
5713
5733
|
function registerAnalyticsTools(server) {
|
|
5714
5734
|
server.tool(
|
|
5715
5735
|
"fetch_analytics",
|
|
5716
|
-
"Get post performance metrics \u2014 views, likes, comments, shares, and engagement rate. Filter by platform, time range (default 30 days), or specific content_id. Call refresh_platform_analytics first if data seems stale. Results sorted by most recent capture.",
|
|
5736
|
+
"Get project-scoped post performance metrics \u2014 views, likes, comments, shares, and engagement rate. Filter by platform, time range (default 30 days), or specific content_id. Call refresh_platform_analytics first if data seems stale. Results sorted by most recent capture.",
|
|
5717
5737
|
{
|
|
5718
5738
|
platform: z5.enum([
|
|
5719
5739
|
"youtube",
|
|
@@ -5731,19 +5751,22 @@ function registerAnalyticsTools(server) {
|
|
|
5731
5751
|
content_id: z5.string().uuid().optional().describe(
|
|
5732
5752
|
"Filter to a specific content_history ID to see performance of one piece of content."
|
|
5733
5753
|
),
|
|
5754
|
+
project_id: z5.string().optional().describe("Project ID. Defaults to the active project context."),
|
|
5734
5755
|
limit: z5.number().min(1).max(100).optional().describe("Maximum number of posts to return. Defaults to 20."),
|
|
5735
5756
|
response_format: z5.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
5736
5757
|
},
|
|
5737
|
-
async ({ platform: platform2, days, content_id, limit, response_format }) => {
|
|
5758
|
+
async ({ platform: platform2, days, content_id, project_id, limit, response_format }) => {
|
|
5738
5759
|
const format = response_format ?? "text";
|
|
5739
5760
|
const lookbackDays = days ?? 30;
|
|
5740
5761
|
const maxPosts = limit ?? 20;
|
|
5762
|
+
const resolvedProjectId = project_id ?? await getDefaultProjectId() ?? void 0;
|
|
5741
5763
|
const { data: result, error: efError } = await callEdgeFunction("mcp-data", {
|
|
5742
5764
|
action: "analytics",
|
|
5743
5765
|
platform: platform2,
|
|
5744
5766
|
days: lookbackDays,
|
|
5745
5767
|
limit: maxPosts,
|
|
5746
|
-
contentId: content_id
|
|
5768
|
+
contentId: content_id,
|
|
5769
|
+
...resolvedProjectId ? { projectId: resolvedProjectId, project_id: resolvedProjectId } : {}
|
|
5747
5770
|
});
|
|
5748
5771
|
if (efError) {
|
|
5749
5772
|
return {
|
|
@@ -5813,14 +5836,19 @@ function registerAnalyticsTools(server) {
|
|
|
5813
5836
|
);
|
|
5814
5837
|
server.tool(
|
|
5815
5838
|
"refresh_platform_analytics",
|
|
5816
|
-
"Queue analytics refresh jobs for
|
|
5839
|
+
"Queue analytics refresh jobs for posts from the last 7 days in one project across its connected platforms. Call this before fetch_analytics if you need fresh data. Returns immediately \u2014 data updates asynchronously over the next 1-5 minutes.",
|
|
5817
5840
|
{
|
|
5841
|
+
project_id: z5.string().optional().describe("Project ID. Defaults to the active project context."),
|
|
5818
5842
|
response_format: z5.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
5819
5843
|
},
|
|
5820
|
-
async ({ response_format }) => {
|
|
5844
|
+
async ({ project_id, response_format }) => {
|
|
5821
5845
|
const format = response_format ?? "text";
|
|
5822
5846
|
const userId = await getDefaultUserId();
|
|
5823
|
-
const
|
|
5847
|
+
const resolvedProjectId = project_id ?? await getDefaultProjectId() ?? void 0;
|
|
5848
|
+
const rateLimit = checkRateLimit(
|
|
5849
|
+
"posting",
|
|
5850
|
+
`refresh_platform_analytics:${userId}:${resolvedProjectId ?? "default"}`
|
|
5851
|
+
);
|
|
5824
5852
|
if (!rateLimit.allowed) {
|
|
5825
5853
|
return {
|
|
5826
5854
|
content: [
|
|
@@ -5832,7 +5860,10 @@ function registerAnalyticsTools(server) {
|
|
|
5832
5860
|
isError: true
|
|
5833
5861
|
};
|
|
5834
5862
|
}
|
|
5835
|
-
const { data, error } = await callEdgeFunction("fetch-analytics", {
|
|
5863
|
+
const { data, error } = await callEdgeFunction("fetch-analytics", {
|
|
5864
|
+
userId,
|
|
5865
|
+
...resolvedProjectId ? { projectId: resolvedProjectId, project_id: resolvedProjectId } : {}
|
|
5866
|
+
});
|
|
5836
5867
|
if (error) {
|
|
5837
5868
|
return {
|
|
5838
5869
|
content: [{ type: "text", text: `Error refreshing analytics: ${error}` }],
|
|
@@ -5861,7 +5892,8 @@ function registerAnalyticsTools(server) {
|
|
|
5861
5892
|
success: true,
|
|
5862
5893
|
postsProcessed: result.postsProcessed,
|
|
5863
5894
|
queued,
|
|
5864
|
-
errored
|
|
5895
|
+
errored,
|
|
5896
|
+
projectId: resolvedProjectId ?? null
|
|
5865
5897
|
});
|
|
5866
5898
|
return {
|
|
5867
5899
|
structuredContent,
|
|
@@ -6913,6 +6945,7 @@ function registerRemotionTools(server) {
|
|
|
6913
6945
|
|
|
6914
6946
|
// src/tools/insights.ts
|
|
6915
6947
|
import { z as z9 } from "zod";
|
|
6948
|
+
init_supabase();
|
|
6916
6949
|
var MAX_INSIGHT_AGE_DAYS = 30;
|
|
6917
6950
|
var PLATFORM_ENUM = [
|
|
6918
6951
|
"youtube",
|
|
@@ -6937,22 +6970,25 @@ function asEnvelope6(data) {
|
|
|
6937
6970
|
function registerInsightsTools(server) {
|
|
6938
6971
|
server.tool(
|
|
6939
6972
|
"get_performance_insights",
|
|
6940
|
-
"Query performance insights derived from post analytics. Returns metrics like engagement rate, view velocity, and click rate aggregated over time. Use this to understand what content is performing well.",
|
|
6973
|
+
"Query project-scoped performance insights derived from post analytics. Returns metrics like engagement rate, view velocity, and click rate aggregated over time. Use this to understand what content is performing well.",
|
|
6941
6974
|
{
|
|
6942
6975
|
insight_type: z9.enum(["top_hooks", "optimal_timing", "best_models", "competitor_patterns"]).optional().describe("Filter to a specific insight type."),
|
|
6943
6976
|
days: z9.number().min(1).max(90).optional().describe("Number of days to look back. Defaults to 30. Max 90."),
|
|
6944
6977
|
limit: z9.number().min(1).max(50).optional().describe("Maximum number of insights to return. Defaults to 10."),
|
|
6978
|
+
project_id: z9.string().optional().describe("Project ID. Defaults to the active project context."),
|
|
6945
6979
|
response_format: z9.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
6946
6980
|
},
|
|
6947
|
-
async ({ insight_type, days, limit, response_format }) => {
|
|
6981
|
+
async ({ insight_type, days, limit, project_id, response_format }) => {
|
|
6948
6982
|
const format = response_format ?? "text";
|
|
6949
6983
|
const lookbackDays = days ?? 30;
|
|
6950
6984
|
const maxRows = limit ?? 10;
|
|
6951
6985
|
const effectiveDays = Math.min(lookbackDays, MAX_INSIGHT_AGE_DAYS);
|
|
6986
|
+
const resolvedProjectId = project_id ?? await getDefaultProjectId() ?? void 0;
|
|
6952
6987
|
const { data: result, error: efError } = await callEdgeFunction("mcp-data", {
|
|
6953
6988
|
action: "performance-insights",
|
|
6954
6989
|
days: effectiveDays,
|
|
6955
|
-
limit: maxRows
|
|
6990
|
+
limit: maxRows,
|
|
6991
|
+
...resolvedProjectId ? { projectId: resolvedProjectId, project_id: resolvedProjectId } : {}
|
|
6956
6992
|
});
|
|
6957
6993
|
if (efError || !result?.success) {
|
|
6958
6994
|
return {
|
|
@@ -7039,19 +7075,22 @@ function registerInsightsTools(server) {
|
|
|
7039
7075
|
);
|
|
7040
7076
|
server.tool(
|
|
7041
7077
|
"get_best_posting_times",
|
|
7042
|
-
"Analyze post analytics data to find the best times to post for maximum engagement. Returns the top 5 time slots (day of week + hour) ranked by average engagement.",
|
|
7078
|
+
"Analyze project-scoped post analytics data to find the best times to post for maximum engagement. Returns the top 5 time slots (day of week + hour) ranked by average engagement.",
|
|
7043
7079
|
{
|
|
7044
7080
|
platform: z9.enum(PLATFORM_ENUM).optional().describe("Filter to a specific platform."),
|
|
7045
7081
|
days: z9.number().min(1).max(90).optional().describe("Number of days to analyze. Defaults to 30. Max 90."),
|
|
7082
|
+
project_id: z9.string().optional().describe("Project ID. Defaults to the active project context."),
|
|
7046
7083
|
response_format: z9.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
7047
7084
|
},
|
|
7048
|
-
async ({ platform: platform2, days, response_format }) => {
|
|
7085
|
+
async ({ platform: platform2, days, project_id, response_format }) => {
|
|
7049
7086
|
const format = response_format ?? "text";
|
|
7050
7087
|
const lookbackDays = days ?? 30;
|
|
7088
|
+
const resolvedProjectId = project_id ?? await getDefaultProjectId() ?? void 0;
|
|
7051
7089
|
const { data: result, error: efError } = await callEdgeFunction("mcp-data", {
|
|
7052
7090
|
action: "best-posting-times",
|
|
7053
7091
|
days: lookbackDays,
|
|
7054
|
-
platform: platform2 ?? void 0
|
|
7092
|
+
platform: platform2 ?? void 0,
|
|
7093
|
+
...resolvedProjectId ? { projectId: resolvedProjectId, project_id: resolvedProjectId } : {}
|
|
7055
7094
|
});
|
|
7056
7095
|
if (efError || !result?.success) {
|
|
7057
7096
|
return {
|
|
@@ -8144,7 +8183,7 @@ Schedule: ${JSON.stringify(updated.schedule_config)}`
|
|
|
8144
8183
|
}
|
|
8145
8184
|
const statusData = {
|
|
8146
8185
|
activeConfigs: result?.activeConfigs ?? 0,
|
|
8147
|
-
recentRuns: [],
|
|
8186
|
+
recentRuns: result?.recentRuns ?? result?.recent_runs ?? [],
|
|
8148
8187
|
pendingApprovals: result?.pendingApprovals ?? 0
|
|
8149
8188
|
};
|
|
8150
8189
|
if (format === "json") {
|
|
@@ -8166,8 +8205,20 @@ ${"=".repeat(40)}
|
|
|
8166
8205
|
text += `Pending Approvals: ${statusData.pendingApprovals}
|
|
8167
8206
|
|
|
8168
8207
|
`;
|
|
8169
|
-
|
|
8208
|
+
if (statusData.recentRuns.length === 0) {
|
|
8209
|
+
text += `No recent runs.
|
|
8170
8210
|
`;
|
|
8211
|
+
} else {
|
|
8212
|
+
text += `Recent Runs (${statusData.recentRuns.length}):
|
|
8213
|
+
`;
|
|
8214
|
+
for (const run of statusData.recentRuns.slice(0, 10)) {
|
|
8215
|
+
const id = String(run.id ?? run.run_id ?? "unknown");
|
|
8216
|
+
const status = String(run.status ?? "unknown");
|
|
8217
|
+
const credits = run.credits_used ?? run.creditsUsed;
|
|
8218
|
+
text += ` ${id}: ${status}${credits == null ? "" : ` (${String(credits)} credits)`}
|
|
8219
|
+
`;
|
|
8220
|
+
}
|
|
8221
|
+
}
|
|
8171
8222
|
return {
|
|
8172
8223
|
content: [{ type: "text", text }]
|
|
8173
8224
|
};
|
|
@@ -13297,7 +13348,7 @@ function registerHyperframesTools(server) {
|
|
|
13297
13348
|
);
|
|
13298
13349
|
server.tool(
|
|
13299
13350
|
"render_hyperframes",
|
|
13300
|
-
"Render an HTML video composition (Hyperframes) to MP4. Author the composition as HTML with data-* timing attributes and GSAP timelines \u2014 frame-accurate, no React build step. Use list_hyperframes_blocks to see the pre-built block catalog.
|
|
13351
|
+
"Render an HTML video composition (Hyperframes) to MP4. Author the composition as HTML with data-* timing attributes and GSAP timelines \u2014 frame-accurate, no React build step. Use list_hyperframes_blocks to see the pre-built block catalog. Pass project_id to keep the render with the correct brand/project. Returns a job ID \u2014 poll with check_status.",
|
|
13301
13352
|
{
|
|
13302
13353
|
composition_html: z30.string().max(5e5).optional().describe(
|
|
13303
13354
|
"Inline HTML composition (full <html>...</html>). Max 500KB. Use composition_url for larger."
|
|
@@ -13309,7 +13360,8 @@ function registerHyperframesTools(server) {
|
|
|
13309
13360
|
aspect_ratio: z30.enum(["9:16", "16:9", "1:1"]).optional().describe('Output aspect ratio. Default "9:16".'),
|
|
13310
13361
|
duration_sec: z30.number().min(0.1).max(600).describe("Output duration in seconds. Required. Max 600 (10 min)."),
|
|
13311
13362
|
fps: z30.union([z30.literal(24), z30.literal(30), z30.literal(60)]).optional().describe("Frames per second. Default 30."),
|
|
13312
|
-
quality: z30.enum(["draft", "standard", "high"]).optional().describe("draft: fast iteration; standard: production; high: final master.")
|
|
13363
|
+
quality: z30.enum(["draft", "standard", "high"]).optional().describe("draft: fast iteration; standard: production; high: final master."),
|
|
13364
|
+
project_id: z30.string().optional().describe("Project ID to associate the Hyperframes render with.")
|
|
13313
13365
|
},
|
|
13314
13366
|
async ({
|
|
13315
13367
|
composition_html,
|
|
@@ -13318,7 +13370,8 @@ function registerHyperframesTools(server) {
|
|
|
13318
13370
|
aspect_ratio,
|
|
13319
13371
|
duration_sec,
|
|
13320
13372
|
fps,
|
|
13321
|
-
quality
|
|
13373
|
+
quality,
|
|
13374
|
+
project_id
|
|
13322
13375
|
}) => {
|
|
13323
13376
|
const userId = await getDefaultUserId();
|
|
13324
13377
|
const rateLimit = checkRateLimit("generation", `render_hyperframes:${userId}`);
|
|
@@ -13363,7 +13416,8 @@ function registerHyperframesTools(server) {
|
|
|
13363
13416
|
aspectRatio: aspect_ratio || "9:16",
|
|
13364
13417
|
durationSec: duration_sec,
|
|
13365
13418
|
fps: fps || 30,
|
|
13366
|
-
quality: quality || "standard"
|
|
13419
|
+
quality: quality || "standard",
|
|
13420
|
+
...project_id && { projectId: project_id }
|
|
13367
13421
|
});
|
|
13368
13422
|
if (error || !data?.jobId) {
|
|
13369
13423
|
throw new Error(error || data?.error || "Failed to create Hyperframes render job");
|
|
@@ -13379,7 +13433,7 @@ function registerHyperframesTools(server) {
|
|
|
13379
13433
|
` Duration: ${duration_sec}s @ ${fps || 30}fps (${aspect_ratio || "9:16"})`,
|
|
13380
13434
|
` Quality: ${quality || "standard"}`,
|
|
13381
13435
|
``,
|
|
13382
|
-
`Poll with check_status
|
|
13436
|
+
`Poll with check_status.`
|
|
13383
13437
|
].join("\n")
|
|
13384
13438
|
}
|
|
13385
13439
|
]
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ var MCP_VERSION;
|
|
|
19
19
|
var init_version = __esm({
|
|
20
20
|
"src/lib/version.ts"() {
|
|
21
21
|
"use strict";
|
|
22
|
-
MCP_VERSION = "1.8.
|
|
22
|
+
MCP_VERSION = "1.8.1";
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
|
|
@@ -2775,7 +2775,7 @@ function registerContentTools(server2) {
|
|
|
2775
2775
|
);
|
|
2776
2776
|
server2.tool(
|
|
2777
2777
|
"generate_image",
|
|
2778
|
-
"Start an async AI image generation job \u2014 returns a job_id immediately. Poll with check_status every 5-15s until complete. Costs
|
|
2778
|
+
"Start an async AI image generation job \u2014 returns a job_id immediately. Poll with check_status every 5-15s until complete. Costs 15-50 credits depending on model. Use for social media posts, carousel slides, or as input to generate_video (image-to-video). Pass project_id so the asset is stored with the correct brand/project.",
|
|
2779
2779
|
{
|
|
2780
2780
|
prompt: z2.string().max(2e3).describe(
|
|
2781
2781
|
"Text prompt describing the image to generate. Be specific about style, composition, colors, lighting, and subject matter."
|
|
@@ -2797,9 +2797,10 @@ function registerContentTools(server2) {
|
|
|
2797
2797
|
image_url: z2.string().optional().describe(
|
|
2798
2798
|
"Reference image URL for image-to-image generation. Required for ideogram model. Optional for others."
|
|
2799
2799
|
),
|
|
2800
|
+
project_id: z2.string().optional().describe("Project ID to associate the generated image with."),
|
|
2800
2801
|
response_format: z2.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
2801
2802
|
},
|
|
2802
|
-
async ({ prompt: prompt2, model, aspect_ratio, image_url, response_format }) => {
|
|
2803
|
+
async ({ prompt: prompt2, model, aspect_ratio, image_url, project_id, response_format }) => {
|
|
2803
2804
|
const format = response_format ?? "text";
|
|
2804
2805
|
const userId = await getDefaultUserId();
|
|
2805
2806
|
const assetBudget = checkAssetBudget();
|
|
@@ -2835,7 +2836,8 @@ function registerContentTools(server2) {
|
|
|
2835
2836
|
prompt: prompt2,
|
|
2836
2837
|
model,
|
|
2837
2838
|
aspectRatio: aspect_ratio ?? "1:1",
|
|
2838
|
-
imageUrl: image_url
|
|
2839
|
+
imageUrl: image_url,
|
|
2840
|
+
...project_id && { projectId: project_id }
|
|
2839
2841
|
},
|
|
2840
2842
|
{ timeoutMs: 3e4 }
|
|
2841
2843
|
);
|
|
@@ -2874,7 +2876,8 @@ function registerContentTools(server2) {
|
|
|
2874
2876
|
jobId,
|
|
2875
2877
|
taskId: data.taskId,
|
|
2876
2878
|
asyncJobId: data.asyncJobId,
|
|
2877
|
-
model: data.model
|
|
2879
|
+
model: data.model,
|
|
2880
|
+
projectId: project_id ?? null
|
|
2878
2881
|
}),
|
|
2879
2882
|
null,
|
|
2880
2883
|
2
|
|
@@ -3055,7 +3058,7 @@ function registerContentTools(server2) {
|
|
|
3055
3058
|
);
|
|
3056
3059
|
server2.tool(
|
|
3057
3060
|
"create_storyboard",
|
|
3058
|
-
"Plan a multi-scene video storyboard with AI-generated prompts, durations, captions, and voiceover text per frame. Use before generate_video or generate_image to create cohesive multi-shot content. Include brand_context from get_brand_profile for consistent
|
|
3061
|
+
"Plan a multi-scene video storyboard with AI-generated prompts, durations, captions, and voiceover text per frame. Use before generate_video or generate_image to create cohesive multi-shot content. Include brand_context from get_brand_profile and project_id for consistent, project-scoped production. Costs 10 credits.",
|
|
3059
3062
|
{
|
|
3060
3063
|
concept: z2.string().max(2e3).describe(
|
|
3061
3064
|
'The video concept/idea. Include: hook, key messages, target audience, and desired outcome (e.g., "TikTok ad for VPN app targeting privacy-conscious millennials, hook with shocking stat about data leaks").'
|
|
@@ -3079,6 +3082,7 @@ function registerContentTools(server2) {
|
|
|
3079
3082
|
style: z2.string().optional().describe(
|
|
3080
3083
|
'Visual style direction (e.g., "cinematic", "anime", "documentary", "motion graphics").'
|
|
3081
3084
|
),
|
|
3085
|
+
project_id: z2.string().optional().describe("Project ID for brand-scoped generation and attribution."),
|
|
3082
3086
|
response_format: z2.enum(["text", "json"]).optional().describe(
|
|
3083
3087
|
"Response format. Defaults to json for structured storyboard data."
|
|
3084
3088
|
)
|
|
@@ -3090,6 +3094,7 @@ function registerContentTools(server2) {
|
|
|
3090
3094
|
target_duration,
|
|
3091
3095
|
num_scenes,
|
|
3092
3096
|
style,
|
|
3097
|
+
project_id,
|
|
3093
3098
|
response_format
|
|
3094
3099
|
}) => {
|
|
3095
3100
|
const format = response_format ?? "json";
|
|
@@ -3172,7 +3177,8 @@ Return ONLY valid JSON in this exact format:
|
|
|
3172
3177
|
prompt: storyboardPrompt,
|
|
3173
3178
|
type: "storyboard",
|
|
3174
3179
|
model: "gemini-2.5-flash",
|
|
3175
|
-
responseFormat: "json"
|
|
3180
|
+
responseFormat: "json",
|
|
3181
|
+
...project_id && { projectId: project_id }
|
|
3176
3182
|
},
|
|
3177
3183
|
{ timeoutMs: 6e4 }
|
|
3178
3184
|
);
|
|
@@ -3187,7 +3193,18 @@ Return ONLY valid JSON in this exact format:
|
|
|
3187
3193
|
isError: true
|
|
3188
3194
|
};
|
|
3189
3195
|
}
|
|
3190
|
-
const rawContent = data?.content
|
|
3196
|
+
const rawContent = data?.content?.trim() || data?.text?.trim() || "";
|
|
3197
|
+
if (!rawContent) {
|
|
3198
|
+
return {
|
|
3199
|
+
content: [
|
|
3200
|
+
{
|
|
3201
|
+
type: "text",
|
|
3202
|
+
text: "Storyboard generation failed: the AI service returned an empty response."
|
|
3203
|
+
}
|
|
3204
|
+
],
|
|
3205
|
+
isError: true
|
|
3206
|
+
};
|
|
3207
|
+
}
|
|
3191
3208
|
addCreditsUsed(estimatedCost);
|
|
3192
3209
|
if (format === "json") {
|
|
3193
3210
|
try {
|
|
@@ -3223,16 +3240,17 @@ Return ONLY valid JSON in this exact format:
|
|
|
3223
3240
|
);
|
|
3224
3241
|
server2.tool(
|
|
3225
3242
|
"generate_voiceover",
|
|
3226
|
-
"Generate a voiceover audio file for video narration. Returns an R2-hosted audio URL. Use after create_storyboard to add narration to each scene, or standalone for podcast intros and ad reads. Costs
|
|
3243
|
+
"Generate a voiceover audio file for video narration. Returns an R2-hosted audio URL. Use after create_storyboard to add narration to each scene, or standalone for podcast intros and ad reads. Pass project_id to keep the asset with the correct brand/project. Costs 15 credits per generation.",
|
|
3227
3244
|
{
|
|
3228
3245
|
text: z2.string().max(5e3).describe("The script/text to convert to speech."),
|
|
3229
3246
|
voice: z2.enum(["rachel", "domi"]).optional().describe(
|
|
3230
3247
|
"Voice selection. rachel=warm female, domi=confident female. Defaults to rachel."
|
|
3231
3248
|
),
|
|
3232
3249
|
speed: z2.number().min(0.5).max(2).optional().describe("Speech speed multiplier. 1.0 is normal. Defaults to 1.0."),
|
|
3250
|
+
project_id: z2.string().optional().describe("Project ID to associate the generated voiceover with."),
|
|
3233
3251
|
response_format: z2.enum(["text", "json"]).optional().describe("Response format. Defaults to text.")
|
|
3234
3252
|
},
|
|
3235
|
-
async ({ text, voice, speed, response_format }) => {
|
|
3253
|
+
async ({ text, voice, speed, project_id, response_format }) => {
|
|
3236
3254
|
const format = response_format ?? "text";
|
|
3237
3255
|
const userId = await getDefaultUserId();
|
|
3238
3256
|
const estimatedCost = 15;
|
|
@@ -3267,7 +3285,8 @@ Return ONLY valid JSON in this exact format:
|
|
|
3267
3285
|
{
|
|
3268
3286
|
text,
|
|
3269
3287
|
voiceId: ELEVENLABS_VOICE_IDS[voice ?? "rachel"],
|
|
3270
|
-
speed: speed ?? 1
|
|
3288
|
+
speed: speed ?? 1,
|
|
3289
|
+
...project_id && { projectId: project_id }
|
|
3271
3290
|
},
|
|
3272
3291
|
{ timeoutMs: 6e4 }
|
|
3273
3292
|
);
|
|
@@ -3303,7 +3322,8 @@ Return ONLY valid JSON in this exact format:
|
|
|
3303
3322
|
asEnvelope2({
|
|
3304
3323
|
audioUrl: data.audioUrl,
|
|
3305
3324
|
durationSeconds: data.durationSeconds,
|
|
3306
|
-
voice: voice ?? "rachel"
|
|
3325
|
+
voice: voice ?? "rachel",
|
|
3326
|
+
projectId: project_id ?? null
|
|
3307
3327
|
}),
|
|
3308
3328
|
null,
|
|
3309
3329
|
2
|
|
@@ -5856,7 +5876,7 @@ function asEnvelope4(data) {
|
|
|
5856
5876
|
function registerAnalyticsTools(server2) {
|
|
5857
5877
|
server2.tool(
|
|
5858
5878
|
"fetch_analytics",
|
|
5859
|
-
"Get post performance metrics \u2014 views, likes, comments, shares, and engagement rate. Filter by platform, time range (default 30 days), or specific content_id. Call refresh_platform_analytics first if data seems stale. Results sorted by most recent capture.",
|
|
5879
|
+
"Get project-scoped post performance metrics \u2014 views, likes, comments, shares, and engagement rate. Filter by platform, time range (default 30 days), or specific content_id. Call refresh_platform_analytics first if data seems stale. Results sorted by most recent capture.",
|
|
5860
5880
|
{
|
|
5861
5881
|
platform: z5.enum([
|
|
5862
5882
|
"youtube",
|
|
@@ -5874,19 +5894,22 @@ function registerAnalyticsTools(server2) {
|
|
|
5874
5894
|
content_id: z5.string().uuid().optional().describe(
|
|
5875
5895
|
"Filter to a specific content_history ID to see performance of one piece of content."
|
|
5876
5896
|
),
|
|
5897
|
+
project_id: z5.string().optional().describe("Project ID. Defaults to the active project context."),
|
|
5877
5898
|
limit: z5.number().min(1).max(100).optional().describe("Maximum number of posts to return. Defaults to 20."),
|
|
5878
5899
|
response_format: z5.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
5879
5900
|
},
|
|
5880
|
-
async ({ platform: platform3, days, content_id, limit, response_format }) => {
|
|
5901
|
+
async ({ platform: platform3, days, content_id, project_id, limit, response_format }) => {
|
|
5881
5902
|
const format = response_format ?? "text";
|
|
5882
5903
|
const lookbackDays = days ?? 30;
|
|
5883
5904
|
const maxPosts = limit ?? 20;
|
|
5905
|
+
const resolvedProjectId = project_id ?? await getDefaultProjectId() ?? void 0;
|
|
5884
5906
|
const { data: result, error: efError } = await callEdgeFunction("mcp-data", {
|
|
5885
5907
|
action: "analytics",
|
|
5886
5908
|
platform: platform3,
|
|
5887
5909
|
days: lookbackDays,
|
|
5888
5910
|
limit: maxPosts,
|
|
5889
|
-
contentId: content_id
|
|
5911
|
+
contentId: content_id,
|
|
5912
|
+
...resolvedProjectId ? { projectId: resolvedProjectId, project_id: resolvedProjectId } : {}
|
|
5890
5913
|
});
|
|
5891
5914
|
if (efError) {
|
|
5892
5915
|
return {
|
|
@@ -5956,14 +5979,19 @@ function registerAnalyticsTools(server2) {
|
|
|
5956
5979
|
);
|
|
5957
5980
|
server2.tool(
|
|
5958
5981
|
"refresh_platform_analytics",
|
|
5959
|
-
"Queue analytics refresh jobs for
|
|
5982
|
+
"Queue analytics refresh jobs for posts from the last 7 days in one project across its connected platforms. Call this before fetch_analytics if you need fresh data. Returns immediately \u2014 data updates asynchronously over the next 1-5 minutes.",
|
|
5960
5983
|
{
|
|
5984
|
+
project_id: z5.string().optional().describe("Project ID. Defaults to the active project context."),
|
|
5961
5985
|
response_format: z5.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
5962
5986
|
},
|
|
5963
|
-
async ({ response_format }) => {
|
|
5987
|
+
async ({ project_id, response_format }) => {
|
|
5964
5988
|
const format = response_format ?? "text";
|
|
5965
5989
|
const userId = await getDefaultUserId();
|
|
5966
|
-
const
|
|
5990
|
+
const resolvedProjectId = project_id ?? await getDefaultProjectId() ?? void 0;
|
|
5991
|
+
const rateLimit = checkRateLimit(
|
|
5992
|
+
"posting",
|
|
5993
|
+
`refresh_platform_analytics:${userId}:${resolvedProjectId ?? "default"}`
|
|
5994
|
+
);
|
|
5967
5995
|
if (!rateLimit.allowed) {
|
|
5968
5996
|
return {
|
|
5969
5997
|
content: [
|
|
@@ -5975,7 +6003,10 @@ function registerAnalyticsTools(server2) {
|
|
|
5975
6003
|
isError: true
|
|
5976
6004
|
};
|
|
5977
6005
|
}
|
|
5978
|
-
const { data, error } = await callEdgeFunction("fetch-analytics", {
|
|
6006
|
+
const { data, error } = await callEdgeFunction("fetch-analytics", {
|
|
6007
|
+
userId,
|
|
6008
|
+
...resolvedProjectId ? { projectId: resolvedProjectId, project_id: resolvedProjectId } : {}
|
|
6009
|
+
});
|
|
5979
6010
|
if (error) {
|
|
5980
6011
|
return {
|
|
5981
6012
|
content: [{ type: "text", text: `Error refreshing analytics: ${error}` }],
|
|
@@ -6004,7 +6035,8 @@ function registerAnalyticsTools(server2) {
|
|
|
6004
6035
|
success: true,
|
|
6005
6036
|
postsProcessed: result.postsProcessed,
|
|
6006
6037
|
queued,
|
|
6007
|
-
errored
|
|
6038
|
+
errored,
|
|
6039
|
+
projectId: resolvedProjectId ?? null
|
|
6008
6040
|
});
|
|
6009
6041
|
return {
|
|
6010
6042
|
structuredContent,
|
|
@@ -7107,22 +7139,25 @@ function asEnvelope6(data) {
|
|
|
7107
7139
|
function registerInsightsTools(server2) {
|
|
7108
7140
|
server2.tool(
|
|
7109
7141
|
"get_performance_insights",
|
|
7110
|
-
"Query performance insights derived from post analytics. Returns metrics like engagement rate, view velocity, and click rate aggregated over time. Use this to understand what content is performing well.",
|
|
7142
|
+
"Query project-scoped performance insights derived from post analytics. Returns metrics like engagement rate, view velocity, and click rate aggregated over time. Use this to understand what content is performing well.",
|
|
7111
7143
|
{
|
|
7112
7144
|
insight_type: z9.enum(["top_hooks", "optimal_timing", "best_models", "competitor_patterns"]).optional().describe("Filter to a specific insight type."),
|
|
7113
7145
|
days: z9.number().min(1).max(90).optional().describe("Number of days to look back. Defaults to 30. Max 90."),
|
|
7114
7146
|
limit: z9.number().min(1).max(50).optional().describe("Maximum number of insights to return. Defaults to 10."),
|
|
7147
|
+
project_id: z9.string().optional().describe("Project ID. Defaults to the active project context."),
|
|
7115
7148
|
response_format: z9.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
7116
7149
|
},
|
|
7117
|
-
async ({ insight_type, days, limit, response_format }) => {
|
|
7150
|
+
async ({ insight_type, days, limit, project_id, response_format }) => {
|
|
7118
7151
|
const format = response_format ?? "text";
|
|
7119
7152
|
const lookbackDays = days ?? 30;
|
|
7120
7153
|
const maxRows = limit ?? 10;
|
|
7121
7154
|
const effectiveDays = Math.min(lookbackDays, MAX_INSIGHT_AGE_DAYS);
|
|
7155
|
+
const resolvedProjectId = project_id ?? await getDefaultProjectId() ?? void 0;
|
|
7122
7156
|
const { data: result, error: efError } = await callEdgeFunction("mcp-data", {
|
|
7123
7157
|
action: "performance-insights",
|
|
7124
7158
|
days: effectiveDays,
|
|
7125
|
-
limit: maxRows
|
|
7159
|
+
limit: maxRows,
|
|
7160
|
+
...resolvedProjectId ? { projectId: resolvedProjectId, project_id: resolvedProjectId } : {}
|
|
7126
7161
|
});
|
|
7127
7162
|
if (efError || !result?.success) {
|
|
7128
7163
|
return {
|
|
@@ -7209,19 +7244,22 @@ function registerInsightsTools(server2) {
|
|
|
7209
7244
|
);
|
|
7210
7245
|
server2.tool(
|
|
7211
7246
|
"get_best_posting_times",
|
|
7212
|
-
"Analyze post analytics data to find the best times to post for maximum engagement. Returns the top 5 time slots (day of week + hour) ranked by average engagement.",
|
|
7247
|
+
"Analyze project-scoped post analytics data to find the best times to post for maximum engagement. Returns the top 5 time slots (day of week + hour) ranked by average engagement.",
|
|
7213
7248
|
{
|
|
7214
7249
|
platform: z9.enum(PLATFORM_ENUM).optional().describe("Filter to a specific platform."),
|
|
7215
7250
|
days: z9.number().min(1).max(90).optional().describe("Number of days to analyze. Defaults to 30. Max 90."),
|
|
7251
|
+
project_id: z9.string().optional().describe("Project ID. Defaults to the active project context."),
|
|
7216
7252
|
response_format: z9.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
7217
7253
|
},
|
|
7218
|
-
async ({ platform: platform3, days, response_format }) => {
|
|
7254
|
+
async ({ platform: platform3, days, project_id, response_format }) => {
|
|
7219
7255
|
const format = response_format ?? "text";
|
|
7220
7256
|
const lookbackDays = days ?? 30;
|
|
7257
|
+
const resolvedProjectId = project_id ?? await getDefaultProjectId() ?? void 0;
|
|
7221
7258
|
const { data: result, error: efError } = await callEdgeFunction("mcp-data", {
|
|
7222
7259
|
action: "best-posting-times",
|
|
7223
7260
|
days: lookbackDays,
|
|
7224
|
-
platform: platform3 ?? void 0
|
|
7261
|
+
platform: platform3 ?? void 0,
|
|
7262
|
+
...resolvedProjectId ? { projectId: resolvedProjectId, project_id: resolvedProjectId } : {}
|
|
7225
7263
|
});
|
|
7226
7264
|
if (efError || !result?.success) {
|
|
7227
7265
|
return {
|
|
@@ -7335,6 +7373,7 @@ var init_insights = __esm({
|
|
|
7335
7373
|
"src/tools/insights.ts"() {
|
|
7336
7374
|
"use strict";
|
|
7337
7375
|
init_edge_function();
|
|
7376
|
+
init_supabase();
|
|
7338
7377
|
init_version();
|
|
7339
7378
|
MAX_INSIGHT_AGE_DAYS = 30;
|
|
7340
7379
|
PLATFORM_ENUM = [
|
|
@@ -8378,7 +8417,7 @@ Schedule: ${JSON.stringify(updated.schedule_config)}`
|
|
|
8378
8417
|
}
|
|
8379
8418
|
const statusData = {
|
|
8380
8419
|
activeConfigs: result?.activeConfigs ?? 0,
|
|
8381
|
-
recentRuns: [],
|
|
8420
|
+
recentRuns: result?.recentRuns ?? result?.recent_runs ?? [],
|
|
8382
8421
|
pendingApprovals: result?.pendingApprovals ?? 0
|
|
8383
8422
|
};
|
|
8384
8423
|
if (format === "json") {
|
|
@@ -8400,8 +8439,20 @@ ${"=".repeat(40)}
|
|
|
8400
8439
|
text += `Pending Approvals: ${statusData.pendingApprovals}
|
|
8401
8440
|
|
|
8402
8441
|
`;
|
|
8403
|
-
|
|
8442
|
+
if (statusData.recentRuns.length === 0) {
|
|
8443
|
+
text += `No recent runs.
|
|
8444
|
+
`;
|
|
8445
|
+
} else {
|
|
8446
|
+
text += `Recent Runs (${statusData.recentRuns.length}):
|
|
8447
|
+
`;
|
|
8448
|
+
for (const run of statusData.recentRuns.slice(0, 10)) {
|
|
8449
|
+
const id = String(run.id ?? run.run_id ?? "unknown");
|
|
8450
|
+
const status = String(run.status ?? "unknown");
|
|
8451
|
+
const credits = run.credits_used ?? run.creditsUsed;
|
|
8452
|
+
text += ` ${id}: ${status}${credits == null ? "" : ` (${String(credits)} credits)`}
|
|
8404
8453
|
`;
|
|
8454
|
+
}
|
|
8455
|
+
}
|
|
8405
8456
|
return {
|
|
8406
8457
|
content: [{ type: "text", text }]
|
|
8407
8458
|
};
|
|
@@ -13571,7 +13622,7 @@ function registerHyperframesTools(server2) {
|
|
|
13571
13622
|
);
|
|
13572
13623
|
server2.tool(
|
|
13573
13624
|
"render_hyperframes",
|
|
13574
|
-
"Render an HTML video composition (Hyperframes) to MP4. Author the composition as HTML with data-* timing attributes and GSAP timelines \u2014 frame-accurate, no React build step. Use list_hyperframes_blocks to see the pre-built block catalog.
|
|
13625
|
+
"Render an HTML video composition (Hyperframes) to MP4. Author the composition as HTML with data-* timing attributes and GSAP timelines \u2014 frame-accurate, no React build step. Use list_hyperframes_blocks to see the pre-built block catalog. Pass project_id to keep the render with the correct brand/project. Returns a job ID \u2014 poll with check_status.",
|
|
13575
13626
|
{
|
|
13576
13627
|
composition_html: z30.string().max(5e5).optional().describe(
|
|
13577
13628
|
"Inline HTML composition (full <html>...</html>). Max 500KB. Use composition_url for larger."
|
|
@@ -13583,7 +13634,8 @@ function registerHyperframesTools(server2) {
|
|
|
13583
13634
|
aspect_ratio: z30.enum(["9:16", "16:9", "1:1"]).optional().describe('Output aspect ratio. Default "9:16".'),
|
|
13584
13635
|
duration_sec: z30.number().min(0.1).max(600).describe("Output duration in seconds. Required. Max 600 (10 min)."),
|
|
13585
13636
|
fps: z30.union([z30.literal(24), z30.literal(30), z30.literal(60)]).optional().describe("Frames per second. Default 30."),
|
|
13586
|
-
quality: z30.enum(["draft", "standard", "high"]).optional().describe("draft: fast iteration; standard: production; high: final master.")
|
|
13637
|
+
quality: z30.enum(["draft", "standard", "high"]).optional().describe("draft: fast iteration; standard: production; high: final master."),
|
|
13638
|
+
project_id: z30.string().optional().describe("Project ID to associate the Hyperframes render with.")
|
|
13587
13639
|
},
|
|
13588
13640
|
async ({
|
|
13589
13641
|
composition_html,
|
|
@@ -13592,7 +13644,8 @@ function registerHyperframesTools(server2) {
|
|
|
13592
13644
|
aspect_ratio,
|
|
13593
13645
|
duration_sec,
|
|
13594
13646
|
fps,
|
|
13595
|
-
quality
|
|
13647
|
+
quality,
|
|
13648
|
+
project_id
|
|
13596
13649
|
}) => {
|
|
13597
13650
|
const userId = await getDefaultUserId();
|
|
13598
13651
|
const rateLimit = checkRateLimit("generation", `render_hyperframes:${userId}`);
|
|
@@ -13637,7 +13690,8 @@ function registerHyperframesTools(server2) {
|
|
|
13637
13690
|
aspectRatio: aspect_ratio || "9:16",
|
|
13638
13691
|
durationSec: duration_sec,
|
|
13639
13692
|
fps: fps || 30,
|
|
13640
|
-
quality: quality || "standard"
|
|
13693
|
+
quality: quality || "standard",
|
|
13694
|
+
...project_id && { projectId: project_id }
|
|
13641
13695
|
});
|
|
13642
13696
|
if (error || !data?.jobId) {
|
|
13643
13697
|
throw new Error(error || data?.error || "Failed to create Hyperframes render job");
|
|
@@ -13653,7 +13707,7 @@ function registerHyperframesTools(server2) {
|
|
|
13653
13707
|
` Duration: ${duration_sec}s @ ${fps || 30}fps (${aspect_ratio || "9:16"})`,
|
|
13654
13708
|
` Quality: ${quality || "standard"}`,
|
|
13655
13709
|
``,
|
|
13656
|
-
`Poll with check_status
|
|
13710
|
+
`Poll with check_status.`
|
|
13657
13711
|
].join("\n")
|
|
13658
13712
|
}
|
|
13659
13713
|
]
|
package/dist/sn.js
CHANGED
|
@@ -19,7 +19,7 @@ var MCP_VERSION;
|
|
|
19
19
|
var init_version = __esm({
|
|
20
20
|
"src/lib/version.ts"() {
|
|
21
21
|
"use strict";
|
|
22
|
-
MCP_VERSION = "1.8.
|
|
22
|
+
MCP_VERSION = "1.8.1";
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
|
|
@@ -4164,7 +4164,7 @@ function registerContentTools(server) {
|
|
|
4164
4164
|
);
|
|
4165
4165
|
server.tool(
|
|
4166
4166
|
"generate_image",
|
|
4167
|
-
"Start an async AI image generation job \u2014 returns a job_id immediately. Poll with check_status every 5-15s until complete. Costs
|
|
4167
|
+
"Start an async AI image generation job \u2014 returns a job_id immediately. Poll with check_status every 5-15s until complete. Costs 15-50 credits depending on model. Use for social media posts, carousel slides, or as input to generate_video (image-to-video). Pass project_id so the asset is stored with the correct brand/project.",
|
|
4168
4168
|
{
|
|
4169
4169
|
prompt: z2.string().max(2e3).describe(
|
|
4170
4170
|
"Text prompt describing the image to generate. Be specific about style, composition, colors, lighting, and subject matter."
|
|
@@ -4186,9 +4186,10 @@ function registerContentTools(server) {
|
|
|
4186
4186
|
image_url: z2.string().optional().describe(
|
|
4187
4187
|
"Reference image URL for image-to-image generation. Required for ideogram model. Optional for others."
|
|
4188
4188
|
),
|
|
4189
|
+
project_id: z2.string().optional().describe("Project ID to associate the generated image with."),
|
|
4189
4190
|
response_format: z2.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
4190
4191
|
},
|
|
4191
|
-
async ({ prompt: prompt2, model, aspect_ratio, image_url, response_format }) => {
|
|
4192
|
+
async ({ prompt: prompt2, model, aspect_ratio, image_url, project_id, response_format }) => {
|
|
4192
4193
|
const format = response_format ?? "text";
|
|
4193
4194
|
const userId = await getDefaultUserId();
|
|
4194
4195
|
const assetBudget = checkAssetBudget();
|
|
@@ -4224,7 +4225,8 @@ function registerContentTools(server) {
|
|
|
4224
4225
|
prompt: prompt2,
|
|
4225
4226
|
model,
|
|
4226
4227
|
aspectRatio: aspect_ratio ?? "1:1",
|
|
4227
|
-
imageUrl: image_url
|
|
4228
|
+
imageUrl: image_url,
|
|
4229
|
+
...project_id && { projectId: project_id }
|
|
4228
4230
|
},
|
|
4229
4231
|
{ timeoutMs: 3e4 }
|
|
4230
4232
|
);
|
|
@@ -4263,7 +4265,8 @@ function registerContentTools(server) {
|
|
|
4263
4265
|
jobId,
|
|
4264
4266
|
taskId: data.taskId,
|
|
4265
4267
|
asyncJobId: data.asyncJobId,
|
|
4266
|
-
model: data.model
|
|
4268
|
+
model: data.model,
|
|
4269
|
+
projectId: project_id ?? null
|
|
4267
4270
|
}),
|
|
4268
4271
|
null,
|
|
4269
4272
|
2
|
|
@@ -4444,7 +4447,7 @@ function registerContentTools(server) {
|
|
|
4444
4447
|
);
|
|
4445
4448
|
server.tool(
|
|
4446
4449
|
"create_storyboard",
|
|
4447
|
-
"Plan a multi-scene video storyboard with AI-generated prompts, durations, captions, and voiceover text per frame. Use before generate_video or generate_image to create cohesive multi-shot content. Include brand_context from get_brand_profile for consistent
|
|
4450
|
+
"Plan a multi-scene video storyboard with AI-generated prompts, durations, captions, and voiceover text per frame. Use before generate_video or generate_image to create cohesive multi-shot content. Include brand_context from get_brand_profile and project_id for consistent, project-scoped production. Costs 10 credits.",
|
|
4448
4451
|
{
|
|
4449
4452
|
concept: z2.string().max(2e3).describe(
|
|
4450
4453
|
'The video concept/idea. Include: hook, key messages, target audience, and desired outcome (e.g., "TikTok ad for VPN app targeting privacy-conscious millennials, hook with shocking stat about data leaks").'
|
|
@@ -4468,6 +4471,7 @@ function registerContentTools(server) {
|
|
|
4468
4471
|
style: z2.string().optional().describe(
|
|
4469
4472
|
'Visual style direction (e.g., "cinematic", "anime", "documentary", "motion graphics").'
|
|
4470
4473
|
),
|
|
4474
|
+
project_id: z2.string().optional().describe("Project ID for brand-scoped generation and attribution."),
|
|
4471
4475
|
response_format: z2.enum(["text", "json"]).optional().describe(
|
|
4472
4476
|
"Response format. Defaults to json for structured storyboard data."
|
|
4473
4477
|
)
|
|
@@ -4479,6 +4483,7 @@ function registerContentTools(server) {
|
|
|
4479
4483
|
target_duration,
|
|
4480
4484
|
num_scenes,
|
|
4481
4485
|
style,
|
|
4486
|
+
project_id,
|
|
4482
4487
|
response_format
|
|
4483
4488
|
}) => {
|
|
4484
4489
|
const format = response_format ?? "json";
|
|
@@ -4561,7 +4566,8 @@ Return ONLY valid JSON in this exact format:
|
|
|
4561
4566
|
prompt: storyboardPrompt,
|
|
4562
4567
|
type: "storyboard",
|
|
4563
4568
|
model: "gemini-2.5-flash",
|
|
4564
|
-
responseFormat: "json"
|
|
4569
|
+
responseFormat: "json",
|
|
4570
|
+
...project_id && { projectId: project_id }
|
|
4565
4571
|
},
|
|
4566
4572
|
{ timeoutMs: 6e4 }
|
|
4567
4573
|
);
|
|
@@ -4576,7 +4582,18 @@ Return ONLY valid JSON in this exact format:
|
|
|
4576
4582
|
isError: true
|
|
4577
4583
|
};
|
|
4578
4584
|
}
|
|
4579
|
-
const rawContent = data?.content
|
|
4585
|
+
const rawContent = data?.content?.trim() || data?.text?.trim() || "";
|
|
4586
|
+
if (!rawContent) {
|
|
4587
|
+
return {
|
|
4588
|
+
content: [
|
|
4589
|
+
{
|
|
4590
|
+
type: "text",
|
|
4591
|
+
text: "Storyboard generation failed: the AI service returned an empty response."
|
|
4592
|
+
}
|
|
4593
|
+
],
|
|
4594
|
+
isError: true
|
|
4595
|
+
};
|
|
4596
|
+
}
|
|
4580
4597
|
addCreditsUsed(estimatedCost);
|
|
4581
4598
|
if (format === "json") {
|
|
4582
4599
|
try {
|
|
@@ -4612,16 +4629,17 @@ Return ONLY valid JSON in this exact format:
|
|
|
4612
4629
|
);
|
|
4613
4630
|
server.tool(
|
|
4614
4631
|
"generate_voiceover",
|
|
4615
|
-
"Generate a voiceover audio file for video narration. Returns an R2-hosted audio URL. Use after create_storyboard to add narration to each scene, or standalone for podcast intros and ad reads. Costs
|
|
4632
|
+
"Generate a voiceover audio file for video narration. Returns an R2-hosted audio URL. Use after create_storyboard to add narration to each scene, or standalone for podcast intros and ad reads. Pass project_id to keep the asset with the correct brand/project. Costs 15 credits per generation.",
|
|
4616
4633
|
{
|
|
4617
4634
|
text: z2.string().max(5e3).describe("The script/text to convert to speech."),
|
|
4618
4635
|
voice: z2.enum(["rachel", "domi"]).optional().describe(
|
|
4619
4636
|
"Voice selection. rachel=warm female, domi=confident female. Defaults to rachel."
|
|
4620
4637
|
),
|
|
4621
4638
|
speed: z2.number().min(0.5).max(2).optional().describe("Speech speed multiplier. 1.0 is normal. Defaults to 1.0."),
|
|
4639
|
+
project_id: z2.string().optional().describe("Project ID to associate the generated voiceover with."),
|
|
4622
4640
|
response_format: z2.enum(["text", "json"]).optional().describe("Response format. Defaults to text.")
|
|
4623
4641
|
},
|
|
4624
|
-
async ({ text, voice, speed, response_format }) => {
|
|
4642
|
+
async ({ text, voice, speed, project_id, response_format }) => {
|
|
4625
4643
|
const format = response_format ?? "text";
|
|
4626
4644
|
const userId = await getDefaultUserId();
|
|
4627
4645
|
const estimatedCost = 15;
|
|
@@ -4656,7 +4674,8 @@ Return ONLY valid JSON in this exact format:
|
|
|
4656
4674
|
{
|
|
4657
4675
|
text,
|
|
4658
4676
|
voiceId: ELEVENLABS_VOICE_IDS[voice ?? "rachel"],
|
|
4659
|
-
speed: speed ?? 1
|
|
4677
|
+
speed: speed ?? 1,
|
|
4678
|
+
...project_id && { projectId: project_id }
|
|
4660
4679
|
},
|
|
4661
4680
|
{ timeoutMs: 6e4 }
|
|
4662
4681
|
);
|
|
@@ -4692,7 +4711,8 @@ Return ONLY valid JSON in this exact format:
|
|
|
4692
4711
|
asEnvelope2({
|
|
4693
4712
|
audioUrl: data.audioUrl,
|
|
4694
4713
|
durationSeconds: data.durationSeconds,
|
|
4695
|
-
voice: voice ?? "rachel"
|
|
4714
|
+
voice: voice ?? "rachel",
|
|
4715
|
+
projectId: project_id ?? null
|
|
4696
4716
|
}),
|
|
4697
4717
|
null,
|
|
4698
4718
|
2
|
|
@@ -7118,7 +7138,7 @@ function asEnvelope4(data) {
|
|
|
7118
7138
|
function registerAnalyticsTools(server) {
|
|
7119
7139
|
server.tool(
|
|
7120
7140
|
"fetch_analytics",
|
|
7121
|
-
"Get post performance metrics \u2014 views, likes, comments, shares, and engagement rate. Filter by platform, time range (default 30 days), or specific content_id. Call refresh_platform_analytics first if data seems stale. Results sorted by most recent capture.",
|
|
7141
|
+
"Get project-scoped post performance metrics \u2014 views, likes, comments, shares, and engagement rate. Filter by platform, time range (default 30 days), or specific content_id. Call refresh_platform_analytics first if data seems stale. Results sorted by most recent capture.",
|
|
7122
7142
|
{
|
|
7123
7143
|
platform: z5.enum([
|
|
7124
7144
|
"youtube",
|
|
@@ -7136,19 +7156,22 @@ function registerAnalyticsTools(server) {
|
|
|
7136
7156
|
content_id: z5.string().uuid().optional().describe(
|
|
7137
7157
|
"Filter to a specific content_history ID to see performance of one piece of content."
|
|
7138
7158
|
),
|
|
7159
|
+
project_id: z5.string().optional().describe("Project ID. Defaults to the active project context."),
|
|
7139
7160
|
limit: z5.number().min(1).max(100).optional().describe("Maximum number of posts to return. Defaults to 20."),
|
|
7140
7161
|
response_format: z5.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
7141
7162
|
},
|
|
7142
|
-
async ({ platform: platform3, days, content_id, limit, response_format }) => {
|
|
7163
|
+
async ({ platform: platform3, days, content_id, project_id, limit, response_format }) => {
|
|
7143
7164
|
const format = response_format ?? "text";
|
|
7144
7165
|
const lookbackDays = days ?? 30;
|
|
7145
7166
|
const maxPosts = limit ?? 20;
|
|
7167
|
+
const resolvedProjectId = project_id ?? await getDefaultProjectId() ?? void 0;
|
|
7146
7168
|
const { data: result, error: efError } = await callEdgeFunction("mcp-data", {
|
|
7147
7169
|
action: "analytics",
|
|
7148
7170
|
platform: platform3,
|
|
7149
7171
|
days: lookbackDays,
|
|
7150
7172
|
limit: maxPosts,
|
|
7151
|
-
contentId: content_id
|
|
7173
|
+
contentId: content_id,
|
|
7174
|
+
...resolvedProjectId ? { projectId: resolvedProjectId, project_id: resolvedProjectId } : {}
|
|
7152
7175
|
});
|
|
7153
7176
|
if (efError) {
|
|
7154
7177
|
return {
|
|
@@ -7218,14 +7241,19 @@ function registerAnalyticsTools(server) {
|
|
|
7218
7241
|
);
|
|
7219
7242
|
server.tool(
|
|
7220
7243
|
"refresh_platform_analytics",
|
|
7221
|
-
"Queue analytics refresh jobs for
|
|
7244
|
+
"Queue analytics refresh jobs for posts from the last 7 days in one project across its connected platforms. Call this before fetch_analytics if you need fresh data. Returns immediately \u2014 data updates asynchronously over the next 1-5 minutes.",
|
|
7222
7245
|
{
|
|
7246
|
+
project_id: z5.string().optional().describe("Project ID. Defaults to the active project context."),
|
|
7223
7247
|
response_format: z5.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
7224
7248
|
},
|
|
7225
|
-
async ({ response_format }) => {
|
|
7249
|
+
async ({ project_id, response_format }) => {
|
|
7226
7250
|
const format = response_format ?? "text";
|
|
7227
7251
|
const userId = await getDefaultUserId();
|
|
7228
|
-
const
|
|
7252
|
+
const resolvedProjectId = project_id ?? await getDefaultProjectId() ?? void 0;
|
|
7253
|
+
const rateLimit = checkRateLimit(
|
|
7254
|
+
"posting",
|
|
7255
|
+
`refresh_platform_analytics:${userId}:${resolvedProjectId ?? "default"}`
|
|
7256
|
+
);
|
|
7229
7257
|
if (!rateLimit.allowed) {
|
|
7230
7258
|
return {
|
|
7231
7259
|
content: [
|
|
@@ -7237,7 +7265,10 @@ function registerAnalyticsTools(server) {
|
|
|
7237
7265
|
isError: true
|
|
7238
7266
|
};
|
|
7239
7267
|
}
|
|
7240
|
-
const { data, error } = await callEdgeFunction("fetch-analytics", {
|
|
7268
|
+
const { data, error } = await callEdgeFunction("fetch-analytics", {
|
|
7269
|
+
userId,
|
|
7270
|
+
...resolvedProjectId ? { projectId: resolvedProjectId, project_id: resolvedProjectId } : {}
|
|
7271
|
+
});
|
|
7241
7272
|
if (error) {
|
|
7242
7273
|
return {
|
|
7243
7274
|
content: [{ type: "text", text: `Error refreshing analytics: ${error}` }],
|
|
@@ -7266,7 +7297,8 @@ function registerAnalyticsTools(server) {
|
|
|
7266
7297
|
success: true,
|
|
7267
7298
|
postsProcessed: result.postsProcessed,
|
|
7268
7299
|
queued,
|
|
7269
|
-
errored
|
|
7300
|
+
errored,
|
|
7301
|
+
projectId: resolvedProjectId ?? null
|
|
7270
7302
|
});
|
|
7271
7303
|
return {
|
|
7272
7304
|
structuredContent,
|
|
@@ -8369,22 +8401,25 @@ function asEnvelope6(data) {
|
|
|
8369
8401
|
function registerInsightsTools(server) {
|
|
8370
8402
|
server.tool(
|
|
8371
8403
|
"get_performance_insights",
|
|
8372
|
-
"Query performance insights derived from post analytics. Returns metrics like engagement rate, view velocity, and click rate aggregated over time. Use this to understand what content is performing well.",
|
|
8404
|
+
"Query project-scoped performance insights derived from post analytics. Returns metrics like engagement rate, view velocity, and click rate aggregated over time. Use this to understand what content is performing well.",
|
|
8373
8405
|
{
|
|
8374
8406
|
insight_type: z9.enum(["top_hooks", "optimal_timing", "best_models", "competitor_patterns"]).optional().describe("Filter to a specific insight type."),
|
|
8375
8407
|
days: z9.number().min(1).max(90).optional().describe("Number of days to look back. Defaults to 30. Max 90."),
|
|
8376
8408
|
limit: z9.number().min(1).max(50).optional().describe("Maximum number of insights to return. Defaults to 10."),
|
|
8409
|
+
project_id: z9.string().optional().describe("Project ID. Defaults to the active project context."),
|
|
8377
8410
|
response_format: z9.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
8378
8411
|
},
|
|
8379
|
-
async ({ insight_type, days, limit, response_format }) => {
|
|
8412
|
+
async ({ insight_type, days, limit, project_id, response_format }) => {
|
|
8380
8413
|
const format = response_format ?? "text";
|
|
8381
8414
|
const lookbackDays = days ?? 30;
|
|
8382
8415
|
const maxRows = limit ?? 10;
|
|
8383
8416
|
const effectiveDays = Math.min(lookbackDays, MAX_INSIGHT_AGE_DAYS);
|
|
8417
|
+
const resolvedProjectId = project_id ?? await getDefaultProjectId() ?? void 0;
|
|
8384
8418
|
const { data: result, error: efError } = await callEdgeFunction("mcp-data", {
|
|
8385
8419
|
action: "performance-insights",
|
|
8386
8420
|
days: effectiveDays,
|
|
8387
|
-
limit: maxRows
|
|
8421
|
+
limit: maxRows,
|
|
8422
|
+
...resolvedProjectId ? { projectId: resolvedProjectId, project_id: resolvedProjectId } : {}
|
|
8388
8423
|
});
|
|
8389
8424
|
if (efError || !result?.success) {
|
|
8390
8425
|
return {
|
|
@@ -8471,19 +8506,22 @@ function registerInsightsTools(server) {
|
|
|
8471
8506
|
);
|
|
8472
8507
|
server.tool(
|
|
8473
8508
|
"get_best_posting_times",
|
|
8474
|
-
"Analyze post analytics data to find the best times to post for maximum engagement. Returns the top 5 time slots (day of week + hour) ranked by average engagement.",
|
|
8509
|
+
"Analyze project-scoped post analytics data to find the best times to post for maximum engagement. Returns the top 5 time slots (day of week + hour) ranked by average engagement.",
|
|
8475
8510
|
{
|
|
8476
8511
|
platform: z9.enum(PLATFORM_ENUM).optional().describe("Filter to a specific platform."),
|
|
8477
8512
|
days: z9.number().min(1).max(90).optional().describe("Number of days to analyze. Defaults to 30. Max 90."),
|
|
8513
|
+
project_id: z9.string().optional().describe("Project ID. Defaults to the active project context."),
|
|
8478
8514
|
response_format: z9.enum(["text", "json"]).optional().describe("Optional response format. Defaults to text.")
|
|
8479
8515
|
},
|
|
8480
|
-
async ({ platform: platform3, days, response_format }) => {
|
|
8516
|
+
async ({ platform: platform3, days, project_id, response_format }) => {
|
|
8481
8517
|
const format = response_format ?? "text";
|
|
8482
8518
|
const lookbackDays = days ?? 30;
|
|
8519
|
+
const resolvedProjectId = project_id ?? await getDefaultProjectId() ?? void 0;
|
|
8483
8520
|
const { data: result, error: efError } = await callEdgeFunction("mcp-data", {
|
|
8484
8521
|
action: "best-posting-times",
|
|
8485
8522
|
days: lookbackDays,
|
|
8486
|
-
platform: platform3 ?? void 0
|
|
8523
|
+
platform: platform3 ?? void 0,
|
|
8524
|
+
...resolvedProjectId ? { projectId: resolvedProjectId, project_id: resolvedProjectId } : {}
|
|
8487
8525
|
});
|
|
8488
8526
|
if (efError || !result?.success) {
|
|
8489
8527
|
return {
|
|
@@ -8597,6 +8635,7 @@ var init_insights = __esm({
|
|
|
8597
8635
|
"src/tools/insights.ts"() {
|
|
8598
8636
|
"use strict";
|
|
8599
8637
|
init_edge_function();
|
|
8638
|
+
init_supabase();
|
|
8600
8639
|
init_version();
|
|
8601
8640
|
MAX_INSIGHT_AGE_DAYS = 30;
|
|
8602
8641
|
PLATFORM_ENUM = [
|
|
@@ -9640,7 +9679,7 @@ Schedule: ${JSON.stringify(updated.schedule_config)}`
|
|
|
9640
9679
|
}
|
|
9641
9680
|
const statusData = {
|
|
9642
9681
|
activeConfigs: result?.activeConfigs ?? 0,
|
|
9643
|
-
recentRuns: [],
|
|
9682
|
+
recentRuns: result?.recentRuns ?? result?.recent_runs ?? [],
|
|
9644
9683
|
pendingApprovals: result?.pendingApprovals ?? 0
|
|
9645
9684
|
};
|
|
9646
9685
|
if (format === "json") {
|
|
@@ -9662,8 +9701,20 @@ ${"=".repeat(40)}
|
|
|
9662
9701
|
text += `Pending Approvals: ${statusData.pendingApprovals}
|
|
9663
9702
|
|
|
9664
9703
|
`;
|
|
9665
|
-
|
|
9704
|
+
if (statusData.recentRuns.length === 0) {
|
|
9705
|
+
text += `No recent runs.
|
|
9706
|
+
`;
|
|
9707
|
+
} else {
|
|
9708
|
+
text += `Recent Runs (${statusData.recentRuns.length}):
|
|
9709
|
+
`;
|
|
9710
|
+
for (const run of statusData.recentRuns.slice(0, 10)) {
|
|
9711
|
+
const id = String(run.id ?? run.run_id ?? "unknown");
|
|
9712
|
+
const status = String(run.status ?? "unknown");
|
|
9713
|
+
const credits = run.credits_used ?? run.creditsUsed;
|
|
9714
|
+
text += ` ${id}: ${status}${credits == null ? "" : ` (${String(credits)} credits)`}
|
|
9666
9715
|
`;
|
|
9716
|
+
}
|
|
9717
|
+
}
|
|
9667
9718
|
return {
|
|
9668
9719
|
content: [{ type: "text", text }]
|
|
9669
9720
|
};
|
|
@@ -14833,7 +14884,7 @@ function registerHyperframesTools(server) {
|
|
|
14833
14884
|
);
|
|
14834
14885
|
server.tool(
|
|
14835
14886
|
"render_hyperframes",
|
|
14836
|
-
"Render an HTML video composition (Hyperframes) to MP4. Author the composition as HTML with data-* timing attributes and GSAP timelines \u2014 frame-accurate, no React build step. Use list_hyperframes_blocks to see the pre-built block catalog.
|
|
14887
|
+
"Render an HTML video composition (Hyperframes) to MP4. Author the composition as HTML with data-* timing attributes and GSAP timelines \u2014 frame-accurate, no React build step. Use list_hyperframes_blocks to see the pre-built block catalog. Pass project_id to keep the render with the correct brand/project. Returns a job ID \u2014 poll with check_status.",
|
|
14837
14888
|
{
|
|
14838
14889
|
composition_html: z30.string().max(5e5).optional().describe(
|
|
14839
14890
|
"Inline HTML composition (full <html>...</html>). Max 500KB. Use composition_url for larger."
|
|
@@ -14845,7 +14896,8 @@ function registerHyperframesTools(server) {
|
|
|
14845
14896
|
aspect_ratio: z30.enum(["9:16", "16:9", "1:1"]).optional().describe('Output aspect ratio. Default "9:16".'),
|
|
14846
14897
|
duration_sec: z30.number().min(0.1).max(600).describe("Output duration in seconds. Required. Max 600 (10 min)."),
|
|
14847
14898
|
fps: z30.union([z30.literal(24), z30.literal(30), z30.literal(60)]).optional().describe("Frames per second. Default 30."),
|
|
14848
|
-
quality: z30.enum(["draft", "standard", "high"]).optional().describe("draft: fast iteration; standard: production; high: final master.")
|
|
14899
|
+
quality: z30.enum(["draft", "standard", "high"]).optional().describe("draft: fast iteration; standard: production; high: final master."),
|
|
14900
|
+
project_id: z30.string().optional().describe("Project ID to associate the Hyperframes render with.")
|
|
14849
14901
|
},
|
|
14850
14902
|
async ({
|
|
14851
14903
|
composition_html,
|
|
@@ -14854,7 +14906,8 @@ function registerHyperframesTools(server) {
|
|
|
14854
14906
|
aspect_ratio,
|
|
14855
14907
|
duration_sec,
|
|
14856
14908
|
fps,
|
|
14857
|
-
quality
|
|
14909
|
+
quality,
|
|
14910
|
+
project_id
|
|
14858
14911
|
}) => {
|
|
14859
14912
|
const userId = await getDefaultUserId();
|
|
14860
14913
|
const rateLimit = checkRateLimit("generation", `render_hyperframes:${userId}`);
|
|
@@ -14899,7 +14952,8 @@ function registerHyperframesTools(server) {
|
|
|
14899
14952
|
aspectRatio: aspect_ratio || "9:16",
|
|
14900
14953
|
durationSec: duration_sec,
|
|
14901
14954
|
fps: fps || 30,
|
|
14902
|
-
quality: quality || "standard"
|
|
14955
|
+
quality: quality || "standard",
|
|
14956
|
+
...project_id && { projectId: project_id }
|
|
14903
14957
|
});
|
|
14904
14958
|
if (error || !data?.jobId) {
|
|
14905
14959
|
throw new Error(error || data?.error || "Failed to create Hyperframes render job");
|
|
@@ -14915,7 +14969,7 @@ function registerHyperframesTools(server) {
|
|
|
14915
14969
|
` Duration: ${duration_sec}s @ ${fps || 30}fps (${aspect_ratio || "9:16"})`,
|
|
14916
14970
|
` Quality: ${quality || "standard"}`,
|
|
14917
14971
|
``,
|
|
14918
|
-
`Poll with check_status
|
|
14972
|
+
`Poll with check_status.`
|
|
14919
14973
|
].join("\n")
|
|
14920
14974
|
}
|
|
14921
14975
|
]
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@socialneuron/mcp-server",
|
|
3
3
|
"mcpName": "com.socialneuron/mcp-server",
|
|
4
|
-
"version": "1.8.
|
|
4
|
+
"version": "1.8.1",
|
|
5
5
|
"description": "Official MCP server, REST API & CLI for Social Neuron — create, schedule, and optimize social content with 35+ AI models across YouTube, TikTok, and more.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"dependencies": {
|
|
88
88
|
"@modelcontextprotocol/ext-apps": "1.7.4",
|
|
89
89
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
90
|
-
"@supabase/supabase-js": "2.
|
|
90
|
+
"@supabase/supabase-js": "2.109.0",
|
|
91
91
|
"express": "^5.1.0",
|
|
92
92
|
"jose": "^6.2.1",
|
|
93
93
|
"open": "10.0.0",
|
package/tools.lock.json
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"create_autopilot_config": "e87d1e4a35a27ea161274731020ea250b0a737a8c2aa717d545f3d5078e4a7e3",
|
|
31
31
|
"create_carousel": "b7d3c22e960fe0c8343319d41f5c602f97990fb5796b29ee8b278161c0f08280",
|
|
32
32
|
"create_plan_approvals": "75eb7eee9d01e280c9bfe1415bc2dba8c55538abf5fdc452ac91fa84d65d3146",
|
|
33
|
-
"create_storyboard": "
|
|
33
|
+
"create_storyboard": "7660532549ddcd099b925ec0fc0152e7caf7c2975b9a1e57cf4e5ec77d069f2e",
|
|
34
34
|
"delete_comment": "4ccba2a7dc337350a9867d9c95d56bcf6c70a2631ef86d0fb11b9dec4b424045",
|
|
35
35
|
"detect_anomalies": "b7d25cccfe416b6a8d1b66dd3cbe39dc02d24ab7882ff7dca4a045fab3ce566f",
|
|
36
36
|
"execute_recipe": "a0622fedcb6bc8cb55e3219bd27dad81908190bd0294908ef2392400133c6721",
|
|
@@ -39,21 +39,21 @@
|
|
|
39
39
|
"extract_brand": "5c75c62f4c5b65ab1f23e8c7f897f23aaf54efc3df2a5487d62bd0018da28541",
|
|
40
40
|
"extract_url_content": "f9590296a6209b9c445e0487fc12c7bf9f10eeba25dbe1fb8ccd578b405a03c3",
|
|
41
41
|
"fetch": "22f077d7298f21c2eaae9e1f5c280d8f44da01a44a3bfd33f273f381132382e0",
|
|
42
|
-
"fetch_analytics": "
|
|
42
|
+
"fetch_analytics": "90980b107c4cb78611d13ab79c8851a2017fc1880212c500739b7e735e5bc1d6",
|
|
43
43
|
"fetch_trends": "9efd2a24c9f9d9a3a6fa707f2355c8ef0d12dbc25f416e9237c0b9b73d15007b",
|
|
44
44
|
"fetch_youtube_analytics": "c1d6cf67a5d0ad1696b4320ee91de7ddf58af6cf4a3be537636be746a4822af7",
|
|
45
45
|
"find_next_slots": "fccf3bc7a8ac54b7ee243c59272252dbfeb307ca8d2987b1527b3b904408c909",
|
|
46
46
|
"find_winning_content": "f124ae937ea5c3bda8ff9eae82c0ad5b6b095c9a8b1b1812931e10a5b6b35766",
|
|
47
47
|
"generate_carousel": "ef7ec2137cd8a36c4cf0435e0b96337173807f147e29b6f73b5c5f8874f36b1e",
|
|
48
48
|
"generate_content": "c741fd25d78e09fecc72fb1ee0b48371c2c2568567ad0f7697b4a95aa9f2640f",
|
|
49
|
-
"generate_image": "
|
|
49
|
+
"generate_image": "24e636035023069009c342c84648bbcc5c49814ce67b6cab245d34bc604b7832",
|
|
50
50
|
"generate_performance_digest": "8bbb53edc46698927a10372444239ba9973c5c84e2d0a8160f74192495dae74b",
|
|
51
51
|
"generate_video": "59132d0dde96d733b8b3635bbf9cf66eda7c8d8739caf6303f5d98fc36c519f8",
|
|
52
|
-
"generate_voiceover": "
|
|
52
|
+
"generate_voiceover": "989d385e10f5024906ece521a4ac9338bf478621655ae6384dbcc84efa56c27d",
|
|
53
53
|
"get_active_campaigns": "2e593f72a0a4e1a7744049556444c64eda760a40f5c08d603c32b3b34b1d760a",
|
|
54
54
|
"get_autopilot_status": "c6e689867a32a491a7afbbae46326d223bba412f0c66113ddf4be831c73fd769",
|
|
55
55
|
"get_bandit_state": "1e58ef7836e24d6c9d474bea52916c15397611200e779ded692cc34fe882352f",
|
|
56
|
-
"get_best_posting_times": "
|
|
56
|
+
"get_best_posting_times": "79c0eceb8da96265478bad0989cd543e70363f8708962a864e4a975b9a594564",
|
|
57
57
|
"get_brand_profile": "a0ebd63ebd7032de497751d2b35c0ec78c847869d64e54f7019baa5e532fed0e",
|
|
58
58
|
"get_brand_runtime": "5ac3149d38eac100aca5d769ddb0f5d900832fb29eef78c18dc97678902e3a41",
|
|
59
59
|
"get_budget_status": "ca79cc516af7ca950b0435949bd801b1e7dd4403eeb4e6152f3bde011ed4b834",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"get_loop_summary": "e37bbc18048c039876dd4f4ba640dbb5e1efaf968fb887cb5479683e2840c1cc",
|
|
65
65
|
"get_mcp_usage": "3e75743113a8e232c831f915edec38143528e92c504219ac3921d524f738df10",
|
|
66
66
|
"get_media_url": "2132e420dc3df1a3c6b8d421b3530be23a7282b8f03af53d26707b337de73370",
|
|
67
|
-
"get_performance_insights": "
|
|
67
|
+
"get_performance_insights": "d7d253877d8703da664085b1d0019824583856184152afc42211126cf3893865",
|
|
68
68
|
"get_pipeline_status": "16ece649a0516d777ea75654b336ff1cffcfec5eb903960dc8f6d3499d823a0e",
|
|
69
69
|
"get_recipe_details": "3d74e13a6b2814a414e89a4c75e6657c84aab611dce937ab117fd52e4f2b2eae",
|
|
70
70
|
"get_recipe_run_status": "70e18a604307fc01f4fc87744c9caaf3d63abcd3ed7ce04af407954ecac2cbdc",
|
|
@@ -89,9 +89,9 @@
|
|
|
89
89
|
"record_observation": "1c0e32121918b9d5891a28bde48edd364b64a73b3092a51788590f4c79ffb6c0",
|
|
90
90
|
"record_outcome": "c38a7e37f243f5d0d2c64c5401cd1108ca33f239fdb714bc685104527d3494f0",
|
|
91
91
|
"record_voice_lesson": "30590e1498a570c3ffd9a070e125b1e28e2cc5d8f98f347a76b43507e6adcc1f",
|
|
92
|
-
"refresh_platform_analytics": "
|
|
92
|
+
"refresh_platform_analytics": "495b9c132fdd094b66b00c582ec94c4ed8c24080704c13f502982761ec817d61",
|
|
93
93
|
"render_demo_video": "d11f88e7e0dd6829a00e500276c8e31501a5e44e06f53ea38b667b6db2ce64ee",
|
|
94
|
-
"render_hyperframes": "
|
|
94
|
+
"render_hyperframes": "2fbc2eaeca2da4ee7c19cf5e5b9e54f294d367cacfcbde333472d93238474a4d",
|
|
95
95
|
"render_template_video": "1a64edb7fd60370bbabfa6751402c7ae32f3174c8c66d7269737297815d8ad92",
|
|
96
96
|
"reply_to_comment": "ddf1ebefdeb25c1c1e95f7e5060224b88f8b85c4aec77cade4cfecf2c00abbf7",
|
|
97
97
|
"respond_plan_approval": "60ef3827a8e199b140a1008746947d6753e766505b2dc69291dccddb276216e2",
|