@socialneuron/mcp-server 1.9.0 → 1.9.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 +10 -0
- package/dist/http.js +220 -109
- package/dist/index.js +229 -116
- package/dist/sn.js +229 -116
- package/package.json +1 -1
- package/tools.lock.json +1 -1
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.9.
|
|
22
|
+
MCP_VERSION = "1.9.1";
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
|
|
@@ -4347,6 +4347,92 @@ var init_ideation = __esm({
|
|
|
4347
4347
|
}
|
|
4348
4348
|
});
|
|
4349
4349
|
|
|
4350
|
+
// src/lib/sanitize-error.ts
|
|
4351
|
+
function redactSensitiveIdentifiers(message) {
|
|
4352
|
+
return message.replace(EMAIL_PATTERN, "[redacted-email]").replace(UUID_PATTERN, "[redacted-id]");
|
|
4353
|
+
}
|
|
4354
|
+
function sanitizeError(error) {
|
|
4355
|
+
const msg = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
|
|
4356
|
+
for (const [pattern, userMessage] of ERROR_PATTERNS) {
|
|
4357
|
+
if (pattern.test(msg)) {
|
|
4358
|
+
return userMessage;
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
return "An unexpected error occurred. Please try again.";
|
|
4362
|
+
}
|
|
4363
|
+
var ERROR_PATTERNS, UUID_PATTERN, EMAIL_PATTERN;
|
|
4364
|
+
var init_sanitize_error = __esm({
|
|
4365
|
+
"src/lib/sanitize-error.ts"() {
|
|
4366
|
+
"use strict";
|
|
4367
|
+
ERROR_PATTERNS = [
|
|
4368
|
+
// Postgres / PostgREST
|
|
4369
|
+
[
|
|
4370
|
+
/PGRST301|permission denied/i,
|
|
4371
|
+
"Access denied. Check your account permissions."
|
|
4372
|
+
],
|
|
4373
|
+
[
|
|
4374
|
+
/42P01|does not exist/i,
|
|
4375
|
+
"Service temporarily unavailable. Please try again."
|
|
4376
|
+
],
|
|
4377
|
+
[
|
|
4378
|
+
/23505|unique.*constraint|duplicate key/i,
|
|
4379
|
+
"A duplicate record already exists."
|
|
4380
|
+
],
|
|
4381
|
+
[/23503|foreign key/i, "Referenced record not found."],
|
|
4382
|
+
// Gemini / Google AI
|
|
4383
|
+
[
|
|
4384
|
+
/google.*api.*key|googleapis\.com.*40[13]/i,
|
|
4385
|
+
"Content generation failed. Please try again."
|
|
4386
|
+
],
|
|
4387
|
+
[
|
|
4388
|
+
/RESOURCE_EXHAUSTED|quota.*exceeded|429.*google/i,
|
|
4389
|
+
"AI service rate limit reached. Please wait and retry."
|
|
4390
|
+
],
|
|
4391
|
+
[
|
|
4392
|
+
/SAFETY|prompt.*blocked|content.*filter/i,
|
|
4393
|
+
"Content was blocked by the AI safety filter. Try rephrasing."
|
|
4394
|
+
],
|
|
4395
|
+
[
|
|
4396
|
+
/gemini.*error|generativelanguage/i,
|
|
4397
|
+
"Content generation failed. Please try again."
|
|
4398
|
+
],
|
|
4399
|
+
// Kie.ai
|
|
4400
|
+
[/kie\.ai|kieai|kie_api/i, "Media generation failed. Please try again."],
|
|
4401
|
+
// Stripe
|
|
4402
|
+
[
|
|
4403
|
+
/stripe.*api|sk_live_|sk_test_/i,
|
|
4404
|
+
"Payment processing error. Please try again."
|
|
4405
|
+
],
|
|
4406
|
+
// Network / fetch
|
|
4407
|
+
[
|
|
4408
|
+
/ECONNREFUSED|ENOTFOUND|ETIMEDOUT|ECONNRESET/i,
|
|
4409
|
+
"External service unavailable. Please try again."
|
|
4410
|
+
],
|
|
4411
|
+
[
|
|
4412
|
+
/fetch failed|network error|abort.*timeout/i,
|
|
4413
|
+
"Network request failed. Please try again."
|
|
4414
|
+
],
|
|
4415
|
+
[/CERT_|certificate|SSL|TLS/i, "Secure connection failed. Please try again."],
|
|
4416
|
+
// Supabase Edge Function internals
|
|
4417
|
+
[
|
|
4418
|
+
/FunctionsHttpError|non-2xx status/i,
|
|
4419
|
+
"Backend service error. Please try again."
|
|
4420
|
+
],
|
|
4421
|
+
[
|
|
4422
|
+
/JWT|token.*expired|token.*invalid/i,
|
|
4423
|
+
"Authentication expired. Please re-authenticate."
|
|
4424
|
+
],
|
|
4425
|
+
// Generic sensitive patterns (API keys, URLs with secrets)
|
|
4426
|
+
[
|
|
4427
|
+
/[a-z0-9]{32,}.*key|Bearer [a-zA-Z0-9._-]+/i,
|
|
4428
|
+
"An internal error occurred. Please try again."
|
|
4429
|
+
]
|
|
4430
|
+
];
|
|
4431
|
+
UUID_PATTERN = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;
|
|
4432
|
+
EMAIL_PATTERN = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
|
|
4433
|
+
}
|
|
4434
|
+
});
|
|
4435
|
+
|
|
4350
4436
|
// src/lib/checkStatusShape.ts
|
|
4351
4437
|
function buildCheckStatusPayload(job, liveStatus) {
|
|
4352
4438
|
const status = liveStatus?.status ?? job.status;
|
|
@@ -4442,9 +4528,22 @@ function checkCreditBudget(estimatedCost) {
|
|
|
4442
4528
|
}
|
|
4443
4529
|
const used = getCreditsUsed();
|
|
4444
4530
|
if (used + estimatedCost > MAX_CREDITS_PER_RUN) {
|
|
4531
|
+
const message = `Per-run credit cap reached \u2014 this is a local spend guard, not a server rate limit, so retrying will not help. The SOCIALNEURON_MAX_CREDITS_PER_RUN environment variable is set to ${MAX_CREDITS_PER_RUN} credits. This run has already spent ${used} credits and this call is estimated at ~${estimatedCost} more (${used} + ${estimatedCost} = ${used + estimatedCost} > ${MAX_CREDITS_PER_RUN}). To proceed, raise or unset SOCIALNEURON_MAX_CREDITS_PER_RUN.`;
|
|
4445
4532
|
return {
|
|
4446
4533
|
ok: false,
|
|
4447
|
-
message
|
|
4534
|
+
message,
|
|
4535
|
+
error: toolError("billing_error", message, {
|
|
4536
|
+
recover_with: [
|
|
4537
|
+
"Raise SOCIALNEURON_MAX_CREDITS_PER_RUN to a higher credit ceiling.",
|
|
4538
|
+
"Unset SOCIALNEURON_MAX_CREDITS_PER_RUN to remove the per-run cap."
|
|
4539
|
+
],
|
|
4540
|
+
details: {
|
|
4541
|
+
env_var: "SOCIALNEURON_MAX_CREDITS_PER_RUN",
|
|
4542
|
+
credits_used_this_run: used,
|
|
4543
|
+
estimated_call_cost: estimatedCost,
|
|
4544
|
+
max_credits_per_run: MAX_CREDITS_PER_RUN
|
|
4545
|
+
}
|
|
4546
|
+
})
|
|
4448
4547
|
};
|
|
4449
4548
|
}
|
|
4450
4549
|
return { ok: true };
|
|
@@ -4467,8 +4566,15 @@ var init_budget = __esm({
|
|
|
4467
4566
|
"src/lib/budget.ts"() {
|
|
4468
4567
|
"use strict";
|
|
4469
4568
|
init_request_context();
|
|
4470
|
-
|
|
4471
|
-
|
|
4569
|
+
init_tool_error();
|
|
4570
|
+
MAX_CREDITS_PER_RUN = Math.max(
|
|
4571
|
+
0,
|
|
4572
|
+
Number(process.env.SOCIALNEURON_MAX_CREDITS_PER_RUN || 0)
|
|
4573
|
+
);
|
|
4574
|
+
MAX_ASSETS_PER_RUN = Math.max(
|
|
4575
|
+
0,
|
|
4576
|
+
Number(process.env.SOCIALNEURON_MAX_ASSETS_PER_RUN || 0)
|
|
4577
|
+
);
|
|
4472
4578
|
_globalCreditsUsed = 0;
|
|
4473
4579
|
_globalAssetsGenerated = 0;
|
|
4474
4580
|
}
|
|
@@ -4495,7 +4601,7 @@ function asEnvelope2(data) {
|
|
|
4495
4601
|
function registerContentTools(server) {
|
|
4496
4602
|
server.tool(
|
|
4497
4603
|
"generate_video",
|
|
4498
|
-
"Start an async AI video generation job \u2014 returns a job_id immediately. Poll with check_status every 10-30s until complete. Base credit costs (reference config; dynamic models scale with duration/audio/resolution): seedance-2-fast 264 (8s, best quality/credit) \xB7 kling-3 100 (5s no-audio) \xB7 grok-imagine 30 (6s, cheapest) \xB7 veo3-fast 65 (8s) \xB7 kling-3-pro 135 (5s no-audio) \xB7 seedance-2 328 (8s 720p) \xB7 veo3-quality 1000 (8s) \xB7 wan-2.6 105 (5s) \xB7 gemini-omni-video 126 (multi-reference composites) \xB7 hailuo-02-standard 180 / seedance-1.5-pro 150 / kling 170 (legacy/budget fallbacks). Costs are estimated pre-check and reconciled post-response from the actual charge. audio adds ~1.5x on kling-3/kling-3-pro and ~2.65x on kling
|
|
4604
|
+
"Start an async AI video generation job \u2014 returns a job_id immediately. Poll with check_status every 10-30s until complete. Base credit costs (reference config; dynamic models scale with duration/audio/resolution): seedance-2-fast 264 (8s, best quality/credit) \xB7 kling-3 100 (5s no-audio) \xB7 grok-imagine 30 (6s, cheapest) \xB7 veo3-fast 65 (8s) \xB7 kling-3-pro 135 (5s no-audio) \xB7 seedance-2 328 (8s 720p) \xB7 veo3-quality 1000 (8s) \xB7 wan-2.6 105 (5s) \xB7 gemini-omni-video 126 (multi-reference composites) \xB7 hailuo-02-standard 180 / seedance-1.5-pro 150 / kling 170 (legacy/budget fallbacks). Costs are estimated pre-check and reconciled post-response from the actual charge. audio adds ~1.5x on kling-3/kling-3-pro and ~2.65x on kling; enable_audio defaults to FALSE EXCEPT the seedance-2 family (seedance-2-fast, seedance-2) where audio is ON by default and its cost is already included. Check get_credit_balance first for expensive generations.",
|
|
4499
4605
|
{
|
|
4500
4606
|
prompt: z2.string().max(2500).describe(
|
|
4501
4607
|
'Video prompt \u2014 be specific about visual style, camera movement, lighting, and mood. Example: "Aerial drone shot of coastal cliffs at golden hour, slow dolly forward, cinematic 24fps, warm color grading." Vague prompts produce generic results.'
|
|
@@ -4510,7 +4616,7 @@ function registerContentTools(server) {
|
|
|
4510
4616
|
"Video aspect ratio. 16:9 for YouTube/landscape, 9:16 for TikTok/Reels/Shorts, 1:1 for Instagram feed/square. Defaults to 16:9."
|
|
4511
4617
|
),
|
|
4512
4618
|
enable_audio: z2.boolean().optional().describe(
|
|
4513
|
-
"Enable native audio generation. DEFAULTS TO FALSE (cost control). Cost multiplier when true: kling 2.6 ~2.65x (17->45 cr/sec), kling-3 1.5x (20->30 cr/sec), kling-3-pro ~1.5x (27->40 cr/sec).
|
|
4619
|
+
"Enable native audio generation. For most models this DEFAULTS TO FALSE (cost control). EXCEPTION: the seedance-2 family (seedance-2-fast, seedance-2) DEFAULTS TO TRUE \u2014 these generate audio natively and their credit cost already includes it, so audio is on unless you explicitly pass false. Cost multiplier when true on other models: kling 2.6 ~2.65x (17->45 cr/sec), kling-3 1.5x (20->30 cr/sec), kling-3-pro ~1.5x (27->40 cr/sec). 5+ languages."
|
|
4514
4620
|
),
|
|
4515
4621
|
image_url: z2.string().optional().describe(
|
|
4516
4622
|
"Start frame image URL for image-to-video (Kling 3.0 frame control)."
|
|
@@ -4546,10 +4652,7 @@ function registerContentTools(server) {
|
|
|
4546
4652
|
const estimatedCost = VIDEO_CREDIT_ESTIMATES[model] ?? 120;
|
|
4547
4653
|
const budgetCheck = checkCreditBudget(estimatedCost);
|
|
4548
4654
|
if (!budgetCheck.ok) {
|
|
4549
|
-
return
|
|
4550
|
-
content: [{ type: "text", text: budgetCheck.message }],
|
|
4551
|
-
isError: true
|
|
4552
|
-
};
|
|
4655
|
+
return budgetCheck.error;
|
|
4553
4656
|
}
|
|
4554
4657
|
const rateLimit = checkRateLimit(
|
|
4555
4658
|
"generation",
|
|
@@ -4573,9 +4676,12 @@ function registerContentTools(server) {
|
|
|
4573
4676
|
model,
|
|
4574
4677
|
duration: duration ?? 5,
|
|
4575
4678
|
aspectRatio: aspect_ratio ?? "16:9",
|
|
4576
|
-
// Default FALSE (2026-07-13): the old `?? true` default silently
|
|
4577
|
-
// kling-family costs (2.65x on kling 2.6) for callers that never asked
|
|
4578
|
-
|
|
4679
|
+
// Default FALSE for most models (2026-07-13): the old `?? true` default silently
|
|
4680
|
+
// multiplied kling-family costs (2.65x on kling 2.6) for callers that never asked
|
|
4681
|
+
// for audio. Exception (2026-07-17): the seedance-2 family bills audio into its base
|
|
4682
|
+
// cost and generates it natively, so a false default there shipped silent no-audio
|
|
4683
|
+
// mp4s — those models default TRUE when the caller omits enable_audio.
|
|
4684
|
+
enableAudio: enable_audio ?? AUDIO_NATIVE_DEFAULT_MODELS.has(model),
|
|
4579
4685
|
...image_url && { imageUrl: image_url },
|
|
4580
4686
|
...end_frame_url && { endFrameUrl: end_frame_url },
|
|
4581
4687
|
// The server reads projectId and enforces
|
|
@@ -4697,10 +4803,7 @@ function registerContentTools(server) {
|
|
|
4697
4803
|
const estimatedCost = IMAGE_CREDIT_ESTIMATES[model] ?? 30;
|
|
4698
4804
|
const budgetCheck = checkCreditBudget(estimatedCost);
|
|
4699
4805
|
if (!budgetCheck.ok) {
|
|
4700
|
-
return
|
|
4701
|
-
content: [{ type: "text", text: budgetCheck.message }],
|
|
4702
|
-
isError: true
|
|
4703
|
-
};
|
|
4806
|
+
return budgetCheck.error;
|
|
4704
4807
|
}
|
|
4705
4808
|
const rateLimit = checkRateLimit(
|
|
4706
4809
|
"generation",
|
|
@@ -4945,7 +5048,7 @@ function registerContentTools(server) {
|
|
|
4945
5048
|
);
|
|
4946
5049
|
}
|
|
4947
5050
|
if (job.error_message) {
|
|
4948
|
-
lines.push(`Error: ${job.error_message}`);
|
|
5051
|
+
lines.push(`Error: ${redactSensitiveIdentifiers(job.error_message)}`);
|
|
4949
5052
|
}
|
|
4950
5053
|
const fallbackDisclosure = buildFallbackDisclosureLine(
|
|
4951
5054
|
job.result_metadata
|
|
@@ -5099,10 +5202,7 @@ Return ONLY valid JSON in this exact format:
|
|
|
5099
5202
|
const estimatedCost = 10;
|
|
5100
5203
|
const budgetCheck = checkCreditBudget(estimatedCost);
|
|
5101
5204
|
if (!budgetCheck.ok) {
|
|
5102
|
-
return
|
|
5103
|
-
content: [{ type: "text", text: budgetCheck.message }],
|
|
5104
|
-
isError: true
|
|
5105
|
-
};
|
|
5205
|
+
return budgetCheck.error;
|
|
5106
5206
|
}
|
|
5107
5207
|
const { data, error } = await callEdgeFunction(
|
|
5108
5208
|
"social-neuron-ai",
|
|
@@ -5189,10 +5289,7 @@ Return ONLY valid JSON in this exact format:
|
|
|
5189
5289
|
const estimatedCost = 15;
|
|
5190
5290
|
const budgetCheck = checkCreditBudget(estimatedCost);
|
|
5191
5291
|
if (!budgetCheck.ok) {
|
|
5192
|
-
return
|
|
5193
|
-
content: [{ type: "text", text: budgetCheck.message }],
|
|
5194
|
-
isError: true
|
|
5195
|
-
};
|
|
5292
|
+
return budgetCheck.error;
|
|
5196
5293
|
}
|
|
5197
5294
|
const rateLimit = checkRateLimit(
|
|
5198
5295
|
"generation",
|
|
@@ -5355,10 +5452,7 @@ Return ONLY valid JSON in this exact format:
|
|
|
5355
5452
|
const estimatedCost = 10 + slideCount * 2;
|
|
5356
5453
|
const budgetCheck = checkCreditBudget(estimatedCost);
|
|
5357
5454
|
if (!budgetCheck.ok) {
|
|
5358
|
-
return
|
|
5359
|
-
content: [{ type: "text", text: budgetCheck.message }],
|
|
5360
|
-
isError: true
|
|
5361
|
-
};
|
|
5455
|
+
return budgetCheck.error;
|
|
5362
5456
|
}
|
|
5363
5457
|
const userId = await getDefaultUserId();
|
|
5364
5458
|
const rateLimit = checkRateLimit(
|
|
@@ -5461,13 +5555,14 @@ Return ONLY valid JSON in this exact format:
|
|
|
5461
5555
|
}
|
|
5462
5556
|
);
|
|
5463
5557
|
}
|
|
5464
|
-
var VIDEO_CREDIT_ESTIMATES, VIDEO_MODEL_ENUM, IMAGE_CREDIT_ESTIMATES;
|
|
5558
|
+
var VIDEO_CREDIT_ESTIMATES, AUDIO_NATIVE_DEFAULT_MODELS, VIDEO_MODEL_ENUM, IMAGE_CREDIT_ESTIMATES;
|
|
5465
5559
|
var init_content2 = __esm({
|
|
5466
5560
|
"src/tools/content.ts"() {
|
|
5467
5561
|
"use strict";
|
|
5468
5562
|
init_edge_function();
|
|
5469
5563
|
init_rate_limit();
|
|
5470
5564
|
init_supabase();
|
|
5565
|
+
init_sanitize_error();
|
|
5471
5566
|
init_version();
|
|
5472
5567
|
init_checkStatusShape();
|
|
5473
5568
|
init_budget();
|
|
@@ -5485,6 +5580,10 @@ var init_content2 = __esm({
|
|
|
5485
5580
|
"seedance-1.5-pro": 150,
|
|
5486
5581
|
kling: 170
|
|
5487
5582
|
};
|
|
5583
|
+
AUDIO_NATIVE_DEFAULT_MODELS = /* @__PURE__ */ new Set([
|
|
5584
|
+
"seedance-2-fast",
|
|
5585
|
+
"seedance-2"
|
|
5586
|
+
]);
|
|
5488
5587
|
VIDEO_MODEL_ENUM = [
|
|
5489
5588
|
"seedance-2-fast",
|
|
5490
5589
|
"kling-3",
|
|
@@ -5513,57 +5612,6 @@ var init_content2 = __esm({
|
|
|
5513
5612
|
}
|
|
5514
5613
|
});
|
|
5515
5614
|
|
|
5516
|
-
// src/lib/sanitize-error.ts
|
|
5517
|
-
function sanitizeError(error) {
|
|
5518
|
-
const msg = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
|
|
5519
|
-
for (const [pattern, userMessage] of ERROR_PATTERNS) {
|
|
5520
|
-
if (pattern.test(msg)) {
|
|
5521
|
-
return userMessage;
|
|
5522
|
-
}
|
|
5523
|
-
}
|
|
5524
|
-
return "An unexpected error occurred. Please try again.";
|
|
5525
|
-
}
|
|
5526
|
-
var ERROR_PATTERNS;
|
|
5527
|
-
var init_sanitize_error = __esm({
|
|
5528
|
-
"src/lib/sanitize-error.ts"() {
|
|
5529
|
-
"use strict";
|
|
5530
|
-
ERROR_PATTERNS = [
|
|
5531
|
-
// Postgres / PostgREST
|
|
5532
|
-
[/PGRST301|permission denied/i, "Access denied. Check your account permissions."],
|
|
5533
|
-
[/42P01|does not exist/i, "Service temporarily unavailable. Please try again."],
|
|
5534
|
-
[/23505|unique.*constraint|duplicate key/i, "A duplicate record already exists."],
|
|
5535
|
-
[/23503|foreign key/i, "Referenced record not found."],
|
|
5536
|
-
// Gemini / Google AI
|
|
5537
|
-
[/google.*api.*key|googleapis\.com.*40[13]/i, "Content generation failed. Please try again."],
|
|
5538
|
-
[
|
|
5539
|
-
/RESOURCE_EXHAUSTED|quota.*exceeded|429.*google/i,
|
|
5540
|
-
"AI service rate limit reached. Please wait and retry."
|
|
5541
|
-
],
|
|
5542
|
-
[
|
|
5543
|
-
/SAFETY|prompt.*blocked|content.*filter/i,
|
|
5544
|
-
"Content was blocked by the AI safety filter. Try rephrasing."
|
|
5545
|
-
],
|
|
5546
|
-
[/gemini.*error|generativelanguage/i, "Content generation failed. Please try again."],
|
|
5547
|
-
// Kie.ai
|
|
5548
|
-
[/kie\.ai|kieai|kie_api/i, "Media generation failed. Please try again."],
|
|
5549
|
-
// Stripe
|
|
5550
|
-
[/stripe.*api|sk_live_|sk_test_/i, "Payment processing error. Please try again."],
|
|
5551
|
-
// Network / fetch
|
|
5552
|
-
[
|
|
5553
|
-
/ECONNREFUSED|ENOTFOUND|ETIMEDOUT|ECONNRESET/i,
|
|
5554
|
-
"External service unavailable. Please try again."
|
|
5555
|
-
],
|
|
5556
|
-
[/fetch failed|network error|abort.*timeout/i, "Network request failed. Please try again."],
|
|
5557
|
-
[/CERT_|certificate|SSL|TLS/i, "Secure connection failed. Please try again."],
|
|
5558
|
-
// Supabase Edge Function internals
|
|
5559
|
-
[/FunctionsHttpError|non-2xx status/i, "Backend service error. Please try again."],
|
|
5560
|
-
[/JWT|token.*expired|token.*invalid/i, "Authentication expired. Please re-authenticate."],
|
|
5561
|
-
// Generic sensitive patterns (API keys, URLs with secrets)
|
|
5562
|
-
[/[a-z0-9]{32,}.*key|Bearer [a-zA-Z0-9._-]+/i, "An internal error occurred. Please try again."]
|
|
5563
|
-
];
|
|
5564
|
-
}
|
|
5565
|
-
});
|
|
5566
|
-
|
|
5567
5615
|
// src/lib/ssrf.ts
|
|
5568
5616
|
import { promises as dnsPromises } from "node:dns";
|
|
5569
5617
|
function isBlockedIP(ip) {
|
|
@@ -13397,6 +13445,14 @@ var init_plan_approvals = __esm({
|
|
|
13397
13445
|
|
|
13398
13446
|
// src/tools/discovery.ts
|
|
13399
13447
|
import { z as z23 } from "zod";
|
|
13448
|
+
function isHostedTransport() {
|
|
13449
|
+
return process.env.MCP_TRANSPORT === "http";
|
|
13450
|
+
}
|
|
13451
|
+
function isPubliclyDiscoverable(tool) {
|
|
13452
|
+
if (tool.internal || tool.hiddenFromPublicCount) return false;
|
|
13453
|
+
if (tool.localOnly && isHostedTransport()) return false;
|
|
13454
|
+
return true;
|
|
13455
|
+
}
|
|
13400
13456
|
function toolKnowledgeDocument(tool) {
|
|
13401
13457
|
const lines = [
|
|
13402
13458
|
`Tool: ${tool.name}`,
|
|
@@ -13407,7 +13463,8 @@ function toolKnowledgeDocument(tool) {
|
|
|
13407
13463
|
if (tool.task_intent) lines.push(`Task intent: ${tool.task_intent}`);
|
|
13408
13464
|
if (tool.use_when) lines.push(`Use when: ${tool.use_when}`);
|
|
13409
13465
|
if (tool.avoid_when) lines.push(`Avoid when: ${tool.avoid_when}`);
|
|
13410
|
-
if (tool.next_tools?.length)
|
|
13466
|
+
if (tool.next_tools?.length)
|
|
13467
|
+
lines.push(`Common next tools: ${tool.next_tools.join(", ")}`);
|
|
13411
13468
|
return {
|
|
13412
13469
|
id: `tool:${tool.name}`,
|
|
13413
13470
|
title: `MCP tool: ${tool.name}`,
|
|
@@ -13424,9 +13481,7 @@ function toolKnowledgeDocument(tool) {
|
|
|
13424
13481
|
function getKnowledgeDocuments() {
|
|
13425
13482
|
return [
|
|
13426
13483
|
...STATIC_KNOWLEDGE_DOCUMENTS,
|
|
13427
|
-
...TOOL_CATALOG.filter(
|
|
13428
|
-
toolKnowledgeDocument
|
|
13429
|
-
)
|
|
13484
|
+
...TOOL_CATALOG.filter(isPubliclyDiscoverable).map(toolKnowledgeDocument)
|
|
13430
13485
|
];
|
|
13431
13486
|
}
|
|
13432
13487
|
function tokenize(input) {
|
|
@@ -13480,7 +13535,9 @@ function registerDiscoveryTools(server) {
|
|
|
13480
13535
|
};
|
|
13481
13536
|
return {
|
|
13482
13537
|
structuredContent,
|
|
13483
|
-
content: [
|
|
13538
|
+
content: [
|
|
13539
|
+
{ type: "text", text: JSON.stringify(structuredContent) }
|
|
13540
|
+
]
|
|
13484
13541
|
};
|
|
13485
13542
|
}
|
|
13486
13543
|
);
|
|
@@ -13501,10 +13558,14 @@ function registerDiscoveryTools(server) {
|
|
|
13501
13558
|
}
|
|
13502
13559
|
},
|
|
13503
13560
|
async ({ id }) => {
|
|
13504
|
-
const doc = getKnowledgeDocuments().find(
|
|
13561
|
+
const doc = getKnowledgeDocuments().find(
|
|
13562
|
+
(candidate) => candidate.id === id
|
|
13563
|
+
);
|
|
13505
13564
|
if (!doc) {
|
|
13506
13565
|
return {
|
|
13507
|
-
content: [
|
|
13566
|
+
content: [
|
|
13567
|
+
{ type: "text", text: `Document not found: ${id}` }
|
|
13568
|
+
],
|
|
13508
13569
|
isError: true
|
|
13509
13570
|
};
|
|
13510
13571
|
}
|
|
@@ -13517,7 +13578,9 @@ function registerDiscoveryTools(server) {
|
|
|
13517
13578
|
};
|
|
13518
13579
|
return {
|
|
13519
13580
|
structuredContent,
|
|
13520
|
-
content: [
|
|
13581
|
+
content: [
|
|
13582
|
+
{ type: "text", text: JSON.stringify(structuredContent) }
|
|
13583
|
+
]
|
|
13521
13584
|
};
|
|
13522
13585
|
}
|
|
13523
13586
|
);
|
|
@@ -13526,7 +13589,9 @@ function registerDiscoveryTools(server) {
|
|
|
13526
13589
|
'Search available tools by name, description, module, or scope. Use "name" detail (~50 tokens) for quick lookup, "summary" (~500 tokens) for descriptions, "full" for complete input schemas. Start here if unsure which tool to call \u2014 filter by module (e.g. "planning", "content", "analytics") to narrow results.',
|
|
13527
13590
|
{
|
|
13528
13591
|
query: z23.string().optional().describe("Search query to filter tools by name or description"),
|
|
13529
|
-
module: z23.string().optional().describe(
|
|
13592
|
+
module: z23.string().optional().describe(
|
|
13593
|
+
'Filter by module name (e.g. "planning", "content", "analytics")'
|
|
13594
|
+
),
|
|
13530
13595
|
scope: z23.string().optional().describe('Filter by required scope (e.g. "mcp:read", "mcp:write")'),
|
|
13531
13596
|
detail: z23.enum(["name", "summary", "full"]).default("summary").describe(
|
|
13532
13597
|
'Detail level: "name" for just tool names, "summary" for names + descriptions, "full" for complete info including scope and module'
|
|
@@ -13543,14 +13608,18 @@ function registerDiscoveryTools(server) {
|
|
|
13543
13608
|
if (query) {
|
|
13544
13609
|
results = searchTools(query);
|
|
13545
13610
|
}
|
|
13546
|
-
results = results.filter(
|
|
13611
|
+
results = results.filter(isPubliclyDiscoverable);
|
|
13547
13612
|
if (module) {
|
|
13548
13613
|
const moduleTools = getToolsByModule(module);
|
|
13549
|
-
results = results.filter(
|
|
13614
|
+
results = results.filter(
|
|
13615
|
+
(t) => moduleTools.some((mt) => mt.name === t.name)
|
|
13616
|
+
);
|
|
13550
13617
|
}
|
|
13551
13618
|
if (scope) {
|
|
13552
13619
|
const scopeTools = getToolsByScope(scope);
|
|
13553
|
-
results = results.filter(
|
|
13620
|
+
results = results.filter(
|
|
13621
|
+
(t) => scopeTools.some((st) => st.name === t.name)
|
|
13622
|
+
);
|
|
13554
13623
|
}
|
|
13555
13624
|
if (available_only) {
|
|
13556
13625
|
results = results.filter(isAvailable);
|
|
@@ -13588,7 +13657,12 @@ function registerDiscoveryTools(server) {
|
|
|
13588
13657
|
text: JSON.stringify(
|
|
13589
13658
|
{
|
|
13590
13659
|
toolCount: results.length,
|
|
13591
|
-
...hasKnownScopes ? {
|
|
13660
|
+
...hasKnownScopes ? {
|
|
13661
|
+
scopes: {
|
|
13662
|
+
available: currentScopes,
|
|
13663
|
+
unavailable_matches: unavailableCount
|
|
13664
|
+
}
|
|
13665
|
+
} : {},
|
|
13592
13666
|
tools: output
|
|
13593
13667
|
},
|
|
13594
13668
|
null,
|
|
@@ -16179,25 +16253,39 @@ function registerCarouselTools(server) {
|
|
|
16179
16253
|
]).optional().describe("Carousel template. Default: hormozi-authority."),
|
|
16180
16254
|
slide_count: z28.number().min(3).max(10).optional().describe("Number of slides (3-10). Default: 7."),
|
|
16181
16255
|
aspect_ratio: z28.enum(["1:1", "4:5", "9:16"]).optional().describe("Aspect ratio for both carousel and images. Default: 1:1."),
|
|
16182
|
-
style: z28.enum(["minimal", "bold", "professional", "playful", "hormozi"]).optional().describe(
|
|
16256
|
+
style: z28.enum(["minimal", "bold", "professional", "playful", "hormozi"]).optional().describe(
|
|
16257
|
+
"Visual style. Default: hormozi for hormozi-authority template."
|
|
16258
|
+
),
|
|
16183
16259
|
image_style_suffix: z28.string().max(500).optional().describe(
|
|
16184
16260
|
'Style suffix appended to every image prompt for visual consistency across slides. Example: "dark moody lighting, cinematic, 35mm film grain".'
|
|
16185
16261
|
),
|
|
16186
16262
|
hook: z28.string().max(300).optional().describe(
|
|
16187
16263
|
"Explicit hook/opener for slide 1. Overrides any hook derived from topic. Keep under 15 words for strongest scroll-stop."
|
|
16188
16264
|
),
|
|
16189
|
-
hook_family: z28.enum([
|
|
16265
|
+
hook_family: z28.enum([
|
|
16266
|
+
"curiosity",
|
|
16267
|
+
"authority",
|
|
16268
|
+
"pain_point",
|
|
16269
|
+
"contrarian",
|
|
16270
|
+
"data_driven"
|
|
16271
|
+
]).optional().describe(
|
|
16190
16272
|
"Hook family tag. Recorded on the carousel so downstream analytics can attribute engagement to hook pattern."
|
|
16191
16273
|
),
|
|
16192
|
-
cta_text: z28.string().max(200).optional().describe(
|
|
16193
|
-
|
|
16274
|
+
cta_text: z28.string().max(200).optional().describe(
|
|
16275
|
+
"Explicit CTA copy for the final slide. If omitted, derived from topic."
|
|
16276
|
+
),
|
|
16277
|
+
cta_url: z28.string().url().optional().describe(
|
|
16278
|
+
"URL promoted on the CTA slide. Defaults to the project landing page."
|
|
16279
|
+
),
|
|
16194
16280
|
tone: z28.string().max(200).optional().describe(
|
|
16195
16281
|
'Voice/tone override. Composes with the brand profile voice when present. Example: "educational, confident, not arrogant".'
|
|
16196
16282
|
),
|
|
16197
16283
|
constraints: z28.string().max(500).optional().describe(
|
|
16198
16284
|
'Content constraints applied at generation time. Example: "No fabricated statistics. Sentence case only. No ALL CAPS."'
|
|
16199
16285
|
),
|
|
16200
|
-
platform: z28.enum(["linkedin", "instagram", "tiktok", "x"]).optional().describe(
|
|
16286
|
+
platform: z28.enum(["linkedin", "instagram", "tiktok", "x"]).optional().describe(
|
|
16287
|
+
"Target platform. Affects tone conventions and slide-count guardrails."
|
|
16288
|
+
),
|
|
16201
16289
|
brand_id: z28.string().optional().describe(
|
|
16202
16290
|
"Brand/project ID to pull visual context from (colors, logo, mood). Falls back to project_id, then default project."
|
|
16203
16291
|
),
|
|
@@ -16229,7 +16317,9 @@ function registerCarouselTools(server) {
|
|
|
16229
16317
|
const slideCount = slide_count ?? 7;
|
|
16230
16318
|
const ratio = aspect_ratio ?? "1:1";
|
|
16231
16319
|
let brandContext = null;
|
|
16232
|
-
const
|
|
16320
|
+
const defaultProjectId = await getDefaultProjectId();
|
|
16321
|
+
const brandProjectId = brand_id || project_id || defaultProjectId;
|
|
16322
|
+
const resolvedProjectId = project_id || defaultProjectId;
|
|
16233
16323
|
if (brandProjectId) {
|
|
16234
16324
|
brandContext = await fetchBrandVisualContext(brandProjectId);
|
|
16235
16325
|
}
|
|
@@ -16238,10 +16328,7 @@ function registerCarouselTools(server) {
|
|
|
16238
16328
|
const totalEstimatedCost = carouselTextCost + slideCount * perImageCost;
|
|
16239
16329
|
const budgetCheck = checkCreditBudget(totalEstimatedCost);
|
|
16240
16330
|
if (!budgetCheck.ok) {
|
|
16241
|
-
return
|
|
16242
|
-
content: [{ type: "text", text: budgetCheck.message }],
|
|
16243
|
-
isError: true
|
|
16244
|
-
};
|
|
16331
|
+
return budgetCheck.error;
|
|
16245
16332
|
}
|
|
16246
16333
|
const assetBudget = checkAssetBudget(slideCount);
|
|
16247
16334
|
if (!assetBudget.ok) {
|
|
@@ -16251,7 +16338,10 @@ function registerCarouselTools(server) {
|
|
|
16251
16338
|
};
|
|
16252
16339
|
}
|
|
16253
16340
|
const userId = await getDefaultUserId();
|
|
16254
|
-
const rateLimit = checkRateLimit(
|
|
16341
|
+
const rateLimit = checkRateLimit(
|
|
16342
|
+
"generation",
|
|
16343
|
+
`create_carousel:${userId}`
|
|
16344
|
+
);
|
|
16255
16345
|
if (!rateLimit.allowed) {
|
|
16256
16346
|
return {
|
|
16257
16347
|
content: [
|
|
@@ -16271,7 +16361,7 @@ function registerCarouselTools(server) {
|
|
|
16271
16361
|
slideCount,
|
|
16272
16362
|
aspectRatio: ratio,
|
|
16273
16363
|
style: resolvedStyle,
|
|
16274
|
-
projectId:
|
|
16364
|
+
projectId: resolvedProjectId,
|
|
16275
16365
|
hook,
|
|
16276
16366
|
hookFamily: hook_family,
|
|
16277
16367
|
ctaText: cta_text,
|
|
@@ -16285,7 +16375,12 @@ function registerCarouselTools(server) {
|
|
|
16285
16375
|
if (carouselError || !carouselData?.carousel) {
|
|
16286
16376
|
const errMsg = carouselError ?? "No carousel data returned";
|
|
16287
16377
|
return {
|
|
16288
|
-
content: [
|
|
16378
|
+
content: [
|
|
16379
|
+
{
|
|
16380
|
+
type: "text",
|
|
16381
|
+
text: `Carousel text generation failed: ${errMsg}`
|
|
16382
|
+
}
|
|
16383
|
+
],
|
|
16289
16384
|
isError: true
|
|
16290
16385
|
};
|
|
16291
16386
|
}
|
|
@@ -16314,7 +16409,8 @@ function registerCarouselTools(server) {
|
|
|
16314
16409
|
{
|
|
16315
16410
|
prompt: imagePrompt,
|
|
16316
16411
|
model: image_model,
|
|
16317
|
-
aspectRatio: ratio
|
|
16412
|
+
aspectRatio: ratio,
|
|
16413
|
+
...resolvedProjectId && { projectId: resolvedProjectId }
|
|
16318
16414
|
},
|
|
16319
16415
|
{ timeoutMs: 3e4 }
|
|
16320
16416
|
);
|
|
@@ -16371,14 +16467,19 @@ function registerCarouselTools(server) {
|
|
|
16371
16467
|
type: "text",
|
|
16372
16468
|
text: JSON.stringify(
|
|
16373
16469
|
{
|
|
16374
|
-
_meta: {
|
|
16470
|
+
_meta: {
|
|
16471
|
+
version: MCP_VERSION,
|
|
16472
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
16473
|
+
},
|
|
16375
16474
|
data: {
|
|
16376
16475
|
carouselId: carousel.id,
|
|
16377
16476
|
templateId,
|
|
16378
16477
|
style: resolvedStyle,
|
|
16379
16478
|
slideCount: carousel.slides.length,
|
|
16380
16479
|
slides: carousel.slides.map((s) => {
|
|
16381
|
-
const job = imageJobs.find(
|
|
16480
|
+
const job = imageJobs.find(
|
|
16481
|
+
(j) => j.slideNumber === s.slideNumber
|
|
16482
|
+
);
|
|
16382
16483
|
return {
|
|
16383
16484
|
...s,
|
|
16384
16485
|
imageJobId: job?.jobId ?? null,
|
|
@@ -16405,9 +16506,17 @@ function registerCarouselTools(server) {
|
|
|
16405
16506
|
credits: {
|
|
16406
16507
|
textGeneration: textCredits,
|
|
16407
16508
|
imagesEstimated: successfulJobs.length * perImageCost,
|
|
16408
|
-
imagesCharged: imageJobs.reduce(
|
|
16409
|
-
|
|
16410
|
-
|
|
16509
|
+
imagesCharged: imageJobs.reduce(
|
|
16510
|
+
(sum, job) => sum + (job.creditsCharged ?? 0),
|
|
16511
|
+
0
|
|
16512
|
+
),
|
|
16513
|
+
imagesRefunded: imageJobs.reduce(
|
|
16514
|
+
(sum, job) => sum + (job.creditsRefunded ?? 0),
|
|
16515
|
+
0
|
|
16516
|
+
),
|
|
16517
|
+
billingUnknownSlides: imageJobs.filter(
|
|
16518
|
+
(job) => job.billingStatus === "unknown"
|
|
16519
|
+
).length,
|
|
16411
16520
|
totalEstimated: textCredits + successfulJobs.length * perImageCost
|
|
16412
16521
|
}
|
|
16413
16522
|
}
|
|
@@ -16435,7 +16544,9 @@ function registerCarouselTools(server) {
|
|
|
16435
16544
|
for (const slide of carousel.slides) {
|
|
16436
16545
|
const job = imageJobs.find((j) => j.slideNumber === slide.slideNumber);
|
|
16437
16546
|
const status = job?.jobId ? `image: ${job.jobId}` : `image FAILED: ${job?.error}`;
|
|
16438
|
-
lines.push(
|
|
16547
|
+
lines.push(
|
|
16548
|
+
` ${slide.slideNumber}. ${slide.headline || "(no headline)"} [${status}]`
|
|
16549
|
+
);
|
|
16439
16550
|
}
|
|
16440
16551
|
if (failedJobs.length > 0) {
|
|
16441
16552
|
lines.push("");
|
|
@@ -16446,7 +16557,9 @@ function registerCarouselTools(server) {
|
|
|
16446
16557
|
const jobIdList = successfulJobs.map((j) => j.jobId).join(", ");
|
|
16447
16558
|
lines.push("");
|
|
16448
16559
|
lines.push("Next steps:");
|
|
16449
|
-
lines.push(
|
|
16560
|
+
lines.push(
|
|
16561
|
+
` 1. Poll each job: check_status with job_id for each of: ${jobIdList}`
|
|
16562
|
+
);
|
|
16450
16563
|
lines.push(
|
|
16451
16564
|
" 2. When all complete: schedule_post with job_ids=[...] and media_type=CAROUSEL_ALBUM"
|
|
16452
16565
|
);
|
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.9.
|
|
4
|
+
"version": "1.9.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",
|
package/tools.lock.json
CHANGED
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"generate_content": "20f9bdf78f1b5ab13ab698b59e6eb73246b79d0f5ffd88cd1ce4461ce3a052ed",
|
|
61
61
|
"generate_image": "415eb2a66595d4312f1082c037061cb684085ba2dc40512dd36ef430bdb0de30",
|
|
62
62
|
"generate_performance_digest": "6601d42259a5449069f7a8c802c9fbae841961a99d0735f2e80ac739f32360f5",
|
|
63
|
-
"generate_video": "
|
|
63
|
+
"generate_video": "d6488e325dee2e24365b6a050517deadfde0358968362769363f9a70b0b729f0",
|
|
64
64
|
"generate_voiceover": "6e4e864bdcb0e47e66ccef1e3fc9aaea4ebf49ab17043bdee1708c81ae1f80aa",
|
|
65
65
|
"get_active_campaigns": "62c89b5e574d3fbef9fc71c7807874e7d2c96c2608b3e4e1d27e429833dc1ef1",
|
|
66
66
|
"get_autopilot_status": "ca00b246fcd55755a88b1ae655a6f715066060e1ebbb0b40fd6fc9f160fdecc5",
|