@socialneuron/mcp-server 1.7.1 → 1.7.2

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.
Files changed (3) hide show
  1. package/dist/http.js +195 -11
  2. package/dist/index.js +195 -11
  3. package/package.json +1 -1
package/dist/http.js CHANGED
@@ -1985,7 +1985,24 @@ function registerContentTools(server) {
1985
1985
  `Status: ${job.status}`
1986
1986
  ];
1987
1987
  if (job.result_url) {
1988
- lines.push(`Result URL: ${job.result_url}`);
1988
+ const isR2Key = !job.result_url.startsWith("http");
1989
+ if (isR2Key) {
1990
+ const segments = job.result_url.split("/");
1991
+ const filename = segments[segments.length - 1] || "media";
1992
+ lines.push(`Media ready: ${filename}`);
1993
+ lines.push(
1994
+ "(Pass job_id directly to schedule_post, or use get_media_url with job_id for a download link)"
1995
+ );
1996
+ } else {
1997
+ lines.push(`Result URL: ${job.result_url}`);
1998
+ }
1999
+ }
2000
+ const allUrls = job.result_metadata?.all_urls;
2001
+ if (allUrls && allUrls.length > 1) {
2002
+ lines.push(`Media files: ${allUrls.length} outputs available`);
2003
+ lines.push(
2004
+ "(Use job_id with schedule_post for carousel, or response_format=json for programmatic access)"
2005
+ );
1989
2006
  }
1990
2007
  if (job.error_message) {
1991
2008
  lines.push(`Error: ${job.error_message}`);
@@ -2002,8 +2019,13 @@ function registerContentTools(server) {
2002
2019
  details: { status: job.status, jobId: job.id }
2003
2020
  });
2004
2021
  if (format === "json") {
2022
+ const enriched = {
2023
+ ...job,
2024
+ r2_key: job.result_url && !job.result_url.startsWith("http") ? job.result_url : null,
2025
+ all_urls: allUrls ?? null
2026
+ };
2005
2027
  return {
2006
- content: [{ type: "text", text: JSON.stringify(asEnvelope(job), null, 2) }]
2028
+ content: [{ type: "text", text: JSON.stringify(asEnvelope(enriched), null, 2) }]
2007
2029
  };
2008
2030
  }
2009
2031
  return {
@@ -2610,6 +2632,22 @@ function evaluateQuality(input) {
2610
2632
  }
2611
2633
 
2612
2634
  // src/tools/distribution.ts
2635
+ function snakeToCamel(obj) {
2636
+ const result = {};
2637
+ for (const [key, value] of Object.entries(obj)) {
2638
+ const camelKey = key.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
2639
+ result[camelKey] = value;
2640
+ }
2641
+ return result;
2642
+ }
2643
+ function convertPlatformMetadata(meta) {
2644
+ if (!meta) return void 0;
2645
+ const converted = {};
2646
+ for (const [platform2, fields] of Object.entries(meta)) {
2647
+ converted[platform2] = snakeToCamel(fields);
2648
+ }
2649
+ return converted;
2650
+ }
2613
2651
  var PLATFORM_CASE_MAP = {
2614
2652
  youtube: "YouTube",
2615
2653
  tiktok: "TikTok",
@@ -2652,8 +2690,53 @@ function registerDistributionTools(server) {
2652
2690
  job_ids: z3.array(z3.string()).optional().describe(
2653
2691
  "Array of async job IDs for carousel posts. Each resolved to its R2 key. Alternative to media_urls/r2_keys."
2654
2692
  ),
2655
- platform_metadata: z3.record(z3.string(), z3.record(z3.string(), z3.unknown())).optional().describe(
2656
- 'Platform-specific metadata. Keys: tiktok (privacy_status), youtube (title, description, privacy), facebook (page_id), threads, bluesky. Example: {"tiktok":{"privacy_status":"PUBLIC_TO_ALL"}}'
2693
+ platform_metadata: z3.object({
2694
+ tiktok: z3.object({
2695
+ privacy_status: z3.enum([
2696
+ "PUBLIC_TO_EVERYONE",
2697
+ "MUTUAL_FOLLOW_FRIENDS",
2698
+ "FOLLOWER_OF_CREATOR",
2699
+ "SELF_ONLY"
2700
+ ]).optional().describe("Required unless useInbox=true. Who can view the video."),
2701
+ enable_duet: z3.boolean().optional(),
2702
+ enable_comment: z3.boolean().optional(),
2703
+ enable_stitch: z3.boolean().optional(),
2704
+ is_ai_generated: z3.boolean().optional(),
2705
+ brand_content: z3.boolean().optional(),
2706
+ brand_organic: z3.boolean().optional(),
2707
+ use_inbox: z3.boolean().optional().describe("Post to TikTok inbox/draft instead of direct publish.")
2708
+ }).optional(),
2709
+ youtube: z3.object({
2710
+ title: z3.string().optional().describe("Video title (required for YouTube)."),
2711
+ description: z3.string().optional(),
2712
+ privacy_status: z3.enum(["public", "unlisted", "private"]).optional(),
2713
+ category_id: z3.string().optional(),
2714
+ tags: z3.array(z3.string()).optional(),
2715
+ made_for_kids: z3.boolean().optional(),
2716
+ notify_subscribers: z3.boolean().optional()
2717
+ }).optional(),
2718
+ facebook: z3.object({
2719
+ page_id: z3.string().optional().describe("Facebook Page ID to post to."),
2720
+ audience: z3.string().optional()
2721
+ }).optional(),
2722
+ instagram: z3.object({
2723
+ location: z3.string().optional(),
2724
+ collaborators: z3.array(z3.string()).optional(),
2725
+ cover_timestamp: z3.number().optional(),
2726
+ share_to_feed: z3.boolean().optional(),
2727
+ first_comment: z3.string().optional(),
2728
+ is_ai_generated: z3.boolean().optional()
2729
+ }).optional(),
2730
+ threads: z3.object({}).passthrough().optional(),
2731
+ bluesky: z3.object({
2732
+ content_labels: z3.array(z3.string()).optional()
2733
+ }).optional(),
2734
+ linkedin: z3.object({
2735
+ article_url: z3.string().optional()
2736
+ }).optional(),
2737
+ twitter: z3.object({}).passthrough().optional()
2738
+ }).optional().describe(
2739
+ 'Platform-specific metadata. Example: {"tiktok":{"privacy_status":"PUBLIC_TO_EVERYONE"}, "youtube":{"title":"My Video"}}'
2657
2740
  ),
2658
2741
  media_type: z3.enum(["IMAGE", "VIDEO", "CAROUSEL_ALBUM"]).optional().describe(
2659
2742
  "Media type. Set to CAROUSEL_ALBUM with media_urls for Instagram carousels. Default: auto-detected from media_url."
@@ -2832,7 +2915,11 @@ Created with Social Neuron`;
2832
2915
  hashtags,
2833
2916
  scheduledAt: schedule_at,
2834
2917
  projectId: project_id,
2835
- ...platform_metadata ? { platformMetadata: platform_metadata } : {}
2918
+ ...platform_metadata ? {
2919
+ platformMetadata: convertPlatformMetadata(
2920
+ platform_metadata
2921
+ )
2922
+ } : {}
2836
2923
  },
2837
2924
  { timeoutMs: 3e4 }
2838
2925
  );
@@ -3710,6 +3797,10 @@ import { readFile } from "node:fs/promises";
3710
3797
  import { basename, extname } from "node:path";
3711
3798
  init_supabase();
3712
3799
  var MAX_BASE64_SIZE = 10 * 1024 * 1024;
3800
+ function maskR2Key(key) {
3801
+ const segments = key.split("/");
3802
+ return segments.length >= 3 ? `\u2026/${segments.slice(-2).join("/")}` : key;
3803
+ }
3713
3804
  function inferContentType(filePath) {
3714
3805
  const ext = extname(filePath).toLowerCase();
3715
3806
  const map = {
@@ -3794,18 +3885,111 @@ function registerMediaTools(server) {
3794
3885
  isError: true
3795
3886
  };
3796
3887
  }
3888
+ const ct = content_type || inferContentType(source);
3797
3889
  if (fileBuffer.length > MAX_BASE64_SIZE) {
3890
+ const { data: putData, error: putError } = await callEdgeFunction(
3891
+ "get-signed-url",
3892
+ {
3893
+ operation: "put",
3894
+ contentType: ct,
3895
+ filename: basename(source),
3896
+ projectId: project_id
3897
+ },
3898
+ { timeoutMs: 1e4 }
3899
+ );
3900
+ if (putError || !putData?.signedUrl) {
3901
+ return {
3902
+ content: [
3903
+ {
3904
+ type: "text",
3905
+ text: `Failed to get presigned upload URL: ${putError || "No URL returned"}`
3906
+ }
3907
+ ],
3908
+ isError: true
3909
+ };
3910
+ }
3911
+ try {
3912
+ const putResp = await fetch(putData.signedUrl, {
3913
+ method: "PUT",
3914
+ headers: { "Content-Type": ct },
3915
+ body: fileBuffer
3916
+ });
3917
+ if (!putResp.ok) {
3918
+ return {
3919
+ content: [
3920
+ {
3921
+ type: "text",
3922
+ text: `R2 upload failed (HTTP ${putResp.status}): ${await putResp.text().catch(() => "Unknown error")}`
3923
+ }
3924
+ ],
3925
+ isError: true
3926
+ };
3927
+ }
3928
+ } catch (uploadErr) {
3929
+ return {
3930
+ content: [
3931
+ {
3932
+ type: "text",
3933
+ text: `R2 upload failed: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`
3934
+ }
3935
+ ],
3936
+ isError: true
3937
+ };
3938
+ }
3939
+ const { data: signData } = await callEdgeFunction(
3940
+ "get-signed-url",
3941
+ { key: putData.key, operation: "get" },
3942
+ { timeoutMs: 1e4 }
3943
+ );
3944
+ await logMcpToolInvocation({
3945
+ toolName: "upload_media",
3946
+ status: "success",
3947
+ durationMs: Date.now() - startedAt,
3948
+ details: {
3949
+ source: "local-presigned-put",
3950
+ r2Key: putData.key,
3951
+ size: fileBuffer.length,
3952
+ contentType: ct
3953
+ }
3954
+ });
3955
+ if (format === "json") {
3956
+ return {
3957
+ content: [
3958
+ {
3959
+ type: "text",
3960
+ text: JSON.stringify(
3961
+ {
3962
+ r2_key: putData.key,
3963
+ signed_url: signData?.signedUrl ?? null,
3964
+ size: fileBuffer.length,
3965
+ content_type: ct
3966
+ },
3967
+ null,
3968
+ 2
3969
+ )
3970
+ }
3971
+ ],
3972
+ isError: false
3973
+ };
3974
+ }
3798
3975
  return {
3799
3976
  content: [
3800
3977
  {
3801
3978
  type: "text",
3802
- text: `File too large for base64 upload (${(fileBuffer.length / 1024 / 1024).toFixed(1)}MB, max ${MAX_BASE64_SIZE / 1024 / 1024}MB). For large videos, host the file at a public URL first and pass the URL as source.`
3979
+ text: [
3980
+ "Media uploaded successfully (presigned PUT).",
3981
+ `Media key: ${maskR2Key(putData.key)}`,
3982
+ signData?.signedUrl ? `Signed URL: ${signData.signedUrl}` : "",
3983
+ `Size: ${(fileBuffer.length / 1024 / 1024).toFixed(1)}MB`,
3984
+ `Type: ${ct}`,
3985
+ "",
3986
+ "Use job_id or response_format=json with schedule_post to post to any platform."
3987
+ ].filter(Boolean).join("\n")
3803
3988
  }
3804
3989
  ],
3805
- isError: true
3990
+ isError: false
3806
3991
  };
3807
3992
  }
3808
- const ct = content_type || inferContentType(source);
3809
3993
  const base64 = `data:${ct};base64,${fileBuffer.toString("base64")}`;
3810
3994
  uploadBody = {
3811
3995
  fileData: base64,
@@ -3870,12 +4054,12 @@ function registerMediaTools(server) {
3870
4054
  type: "text",
3871
4055
  text: [
3872
4056
  "Media uploaded successfully.",
3873
- `R2 Key: ${data.key}`,
4057
+ `Media key: ${maskR2Key(data.key)}`,
3874
4058
  `Signed URL: ${data.url}`,
3875
4059
  `Size: ${(data.size / 1024).toFixed(0)}KB`,
3876
4060
  `Type: ${data.contentType}`,
3877
4061
  "",
3878
- "Use this r2_key with schedule_post to post to any platform."
4062
+ "Use job_id or response_format=json with schedule_post to post to any platform."
3879
4063
  ].join("\n")
3880
4064
  }
3881
4065
  ],
@@ -3939,7 +4123,7 @@ function registerMediaTools(server) {
3939
4123
  type: "text",
3940
4124
  text: [
3941
4125
  `Signed URL: ${data.signedUrl}`,
3942
- `R2 Key: ${r2_key}`,
4126
+ `Media key: ${maskR2Key(r2_key)}`,
3943
4127
  `Expires in: ${data.expiresIn ?? 3600}s`
3944
4128
  ].join("\n")
3945
4129
  }
package/dist/index.js CHANGED
@@ -5135,7 +5135,24 @@ function registerContentTools(server2) {
5135
5135
  `Status: ${job.status}`
5136
5136
  ];
5137
5137
  if (job.result_url) {
5138
- lines.push(`Result URL: ${job.result_url}`);
5138
+ const isR2Key = !job.result_url.startsWith("http");
5139
+ if (isR2Key) {
5140
+ const segments = job.result_url.split("/");
5141
+ const filename = segments[segments.length - 1] || "media";
5142
+ lines.push(`Media ready: ${filename}`);
5143
+ lines.push(
5144
+ "(Pass job_id directly to schedule_post, or use get_media_url with job_id for a download link)"
5145
+ );
5146
+ } else {
5147
+ lines.push(`Result URL: ${job.result_url}`);
5148
+ }
5149
+ }
5150
+ const allUrls = job.result_metadata?.all_urls;
5151
+ if (allUrls && allUrls.length > 1) {
5152
+ lines.push(`Media files: ${allUrls.length} outputs available`);
5153
+ lines.push(
5154
+ "(Use job_id with schedule_post for carousel, or response_format=json for programmatic access)"
5155
+ );
5139
5156
  }
5140
5157
  if (job.error_message) {
5141
5158
  lines.push(`Error: ${job.error_message}`);
@@ -5152,8 +5169,13 @@ function registerContentTools(server2) {
5152
5169
  details: { status: job.status, jobId: job.id }
5153
5170
  });
5154
5171
  if (format === "json") {
5172
+ const enriched = {
5173
+ ...job,
5174
+ r2_key: job.result_url && !job.result_url.startsWith("http") ? job.result_url : null,
5175
+ all_urls: allUrls ?? null
5176
+ };
5155
5177
  return {
5156
- content: [{ type: "text", text: JSON.stringify(asEnvelope(job), null, 2) }]
5178
+ content: [{ type: "text", text: JSON.stringify(asEnvelope(enriched), null, 2) }]
5157
5179
  };
5158
5180
  }
5159
5181
  return {
@@ -5639,6 +5661,22 @@ import { createHash as createHash2 } from "node:crypto";
5639
5661
  init_supabase();
5640
5662
  init_quality();
5641
5663
  init_version();
5664
+ function snakeToCamel(obj) {
5665
+ const result = {};
5666
+ for (const [key, value] of Object.entries(obj)) {
5667
+ const camelKey = key.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
5668
+ result[camelKey] = value;
5669
+ }
5670
+ return result;
5671
+ }
5672
+ function convertPlatformMetadata(meta) {
5673
+ if (!meta) return void 0;
5674
+ const converted = {};
5675
+ for (const [platform3, fields] of Object.entries(meta)) {
5676
+ converted[platform3] = snakeToCamel(fields);
5677
+ }
5678
+ return converted;
5679
+ }
5642
5680
  var PLATFORM_CASE_MAP = {
5643
5681
  youtube: "YouTube",
5644
5682
  tiktok: "TikTok",
@@ -5681,8 +5719,53 @@ function registerDistributionTools(server2) {
5681
5719
  job_ids: z3.array(z3.string()).optional().describe(
5682
5720
  "Array of async job IDs for carousel posts. Each resolved to its R2 key. Alternative to media_urls/r2_keys."
5683
5721
  ),
5684
- platform_metadata: z3.record(z3.string(), z3.record(z3.string(), z3.unknown())).optional().describe(
5685
- 'Platform-specific metadata. Keys: tiktok (privacy_status), youtube (title, description, privacy), facebook (page_id), threads, bluesky. Example: {"tiktok":{"privacy_status":"PUBLIC_TO_ALL"}}'
5722
+ platform_metadata: z3.object({
5723
+ tiktok: z3.object({
5724
+ privacy_status: z3.enum([
5725
+ "PUBLIC_TO_EVERYONE",
5726
+ "MUTUAL_FOLLOW_FRIENDS",
5727
+ "FOLLOWER_OF_CREATOR",
5728
+ "SELF_ONLY"
5729
+ ]).optional().describe("Required unless useInbox=true. Who can view the video."),
5730
+ enable_duet: z3.boolean().optional(),
5731
+ enable_comment: z3.boolean().optional(),
5732
+ enable_stitch: z3.boolean().optional(),
5733
+ is_ai_generated: z3.boolean().optional(),
5734
+ brand_content: z3.boolean().optional(),
5735
+ brand_organic: z3.boolean().optional(),
5736
+ use_inbox: z3.boolean().optional().describe("Post to TikTok inbox/draft instead of direct publish.")
5737
+ }).optional(),
5738
+ youtube: z3.object({
5739
+ title: z3.string().optional().describe("Video title (required for YouTube)."),
5740
+ description: z3.string().optional(),
5741
+ privacy_status: z3.enum(["public", "unlisted", "private"]).optional(),
5742
+ category_id: z3.string().optional(),
5743
+ tags: z3.array(z3.string()).optional(),
5744
+ made_for_kids: z3.boolean().optional(),
5745
+ notify_subscribers: z3.boolean().optional()
5746
+ }).optional(),
5747
+ facebook: z3.object({
5748
+ page_id: z3.string().optional().describe("Facebook Page ID to post to."),
5749
+ audience: z3.string().optional()
5750
+ }).optional(),
5751
+ instagram: z3.object({
5752
+ location: z3.string().optional(),
5753
+ collaborators: z3.array(z3.string()).optional(),
5754
+ cover_timestamp: z3.number().optional(),
5755
+ share_to_feed: z3.boolean().optional(),
5756
+ first_comment: z3.string().optional(),
5757
+ is_ai_generated: z3.boolean().optional()
5758
+ }).optional(),
5759
+ threads: z3.object({}).passthrough().optional(),
5760
+ bluesky: z3.object({
5761
+ content_labels: z3.array(z3.string()).optional()
5762
+ }).optional(),
5763
+ linkedin: z3.object({
5764
+ article_url: z3.string().optional()
5765
+ }).optional(),
5766
+ twitter: z3.object({}).passthrough().optional()
5767
+ }).optional().describe(
5768
+ 'Platform-specific metadata. Example: {"tiktok":{"privacy_status":"PUBLIC_TO_EVERYONE"}, "youtube":{"title":"My Video"}}'
5686
5769
  ),
5687
5770
  media_type: z3.enum(["IMAGE", "VIDEO", "CAROUSEL_ALBUM"]).optional().describe(
5688
5771
  "Media type. Set to CAROUSEL_ALBUM with media_urls for Instagram carousels. Default: auto-detected from media_url."
@@ -5861,7 +5944,11 @@ Created with Social Neuron`;
5861
5944
  hashtags,
5862
5945
  scheduledAt: schedule_at,
5863
5946
  projectId: project_id,
5864
- ...platform_metadata ? { platformMetadata: platform_metadata } : {}
5947
+ ...platform_metadata ? {
5948
+ platformMetadata: convertPlatformMetadata(
5949
+ platform_metadata
5950
+ )
5951
+ } : {}
5865
5952
  },
5866
5953
  { timeoutMs: 3e4 }
5867
5954
  );
@@ -6740,6 +6827,10 @@ import { readFile } from "node:fs/promises";
6740
6827
  import { basename, extname } from "node:path";
6741
6828
  init_supabase();
6742
6829
  var MAX_BASE64_SIZE = 10 * 1024 * 1024;
6830
+ function maskR2Key(key) {
6831
+ const segments = key.split("/");
6832
+ return segments.length >= 3 ? `\u2026/${segments.slice(-2).join("/")}` : key;
6833
+ }
6743
6834
  function inferContentType(filePath) {
6744
6835
  const ext = extname(filePath).toLowerCase();
6745
6836
  const map = {
@@ -6824,18 +6915,111 @@ function registerMediaTools(server2) {
6824
6915
  isError: true
6825
6916
  };
6826
6917
  }
6918
+ const ct = content_type || inferContentType(source);
6827
6919
  if (fileBuffer.length > MAX_BASE64_SIZE) {
6920
+ const { data: putData, error: putError } = await callEdgeFunction(
6921
+ "get-signed-url",
6922
+ {
6923
+ operation: "put",
6924
+ contentType: ct,
6925
+ filename: basename(source),
6926
+ projectId: project_id
6927
+ },
6928
+ { timeoutMs: 1e4 }
6929
+ );
6930
+ if (putError || !putData?.signedUrl) {
6931
+ return {
6932
+ content: [
6933
+ {
6934
+ type: "text",
6935
+ text: `Failed to get presigned upload URL: ${putError || "No URL returned"}`
6936
+ }
6937
+ ],
6938
+ isError: true
6939
+ };
6940
+ }
6941
+ try {
6942
+ const putResp = await fetch(putData.signedUrl, {
6943
+ method: "PUT",
6944
+ headers: { "Content-Type": ct },
6945
+ body: fileBuffer
6946
+ });
6947
+ if (!putResp.ok) {
6948
+ return {
6949
+ content: [
6950
+ {
6951
+ type: "text",
6952
+ text: `R2 upload failed (HTTP ${putResp.status}): ${await putResp.text().catch(() => "Unknown error")}`
6953
+ }
6954
+ ],
6955
+ isError: true
6956
+ };
6957
+ }
6958
+ } catch (uploadErr) {
6959
+ return {
6960
+ content: [
6961
+ {
6962
+ type: "text",
6963
+ text: `R2 upload failed: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`
6964
+ }
6965
+ ],
6966
+ isError: true
6967
+ };
6968
+ }
6969
+ const { data: signData } = await callEdgeFunction(
6970
+ "get-signed-url",
6971
+ { key: putData.key, operation: "get" },
6972
+ { timeoutMs: 1e4 }
6973
+ );
6974
+ await logMcpToolInvocation({
6975
+ toolName: "upload_media",
6976
+ status: "success",
6977
+ durationMs: Date.now() - startedAt,
6978
+ details: {
6979
+ source: "local-presigned-put",
6980
+ r2Key: putData.key,
6981
+ size: fileBuffer.length,
6982
+ contentType: ct
6983
+ }
6984
+ });
6985
+ if (format === "json") {
6986
+ return {
6987
+ content: [
6988
+ {
6989
+ type: "text",
6990
+ text: JSON.stringify(
6991
+ {
6992
+ r2_key: putData.key,
6993
+ signed_url: signData?.signedUrl ?? null,
6994
+ size: fileBuffer.length,
6995
+ content_type: ct
6996
+ },
6997
+ null,
6998
+ 2
6999
+ )
7000
+ }
7001
+ ],
7002
+ isError: false
7003
+ };
7004
+ }
6828
7005
  return {
6829
7006
  content: [
6830
7007
  {
6831
7008
  type: "text",
6832
- text: `File too large for base64 upload (${(fileBuffer.length / 1024 / 1024).toFixed(1)}MB, max ${MAX_BASE64_SIZE / 1024 / 1024}MB). For large videos, host the file at a public URL first and pass the URL as source.`
7009
+ text: [
7010
+ "Media uploaded successfully (presigned PUT).",
7011
+ `Media key: ${maskR2Key(putData.key)}`,
7012
+ signData?.signedUrl ? `Signed URL: ${signData.signedUrl}` : "",
7013
+ `Size: ${(fileBuffer.length / 1024 / 1024).toFixed(1)}MB`,
7014
+ `Type: ${ct}`,
7015
+ "",
7016
+ "Use job_id or response_format=json with schedule_post to post to any platform."
7017
+ ].filter(Boolean).join("\n")
6833
7018
  }
6834
7019
  ],
6835
- isError: true
7020
+ isError: false
6836
7021
  };
6837
7022
  }
6838
- const ct = content_type || inferContentType(source);
6839
7023
  const base64 = `data:${ct};base64,${fileBuffer.toString("base64")}`;
6840
7024
  uploadBody = {
6841
7025
  fileData: base64,
@@ -6900,12 +7084,12 @@ function registerMediaTools(server2) {
6900
7084
  type: "text",
6901
7085
  text: [
6902
7086
  "Media uploaded successfully.",
6903
- `R2 Key: ${data.key}`,
7087
+ `Media key: ${maskR2Key(data.key)}`,
6904
7088
  `Signed URL: ${data.url}`,
6905
7089
  `Size: ${(data.size / 1024).toFixed(0)}KB`,
6906
7090
  `Type: ${data.contentType}`,
6907
7091
  "",
6908
- "Use this r2_key with schedule_post to post to any platform."
7092
+ "Use job_id or response_format=json with schedule_post to post to any platform."
6909
7093
  ].join("\n")
6910
7094
  }
6911
7095
  ],
@@ -6969,7 +7153,7 @@ function registerMediaTools(server2) {
6969
7153
  type: "text",
6970
7154
  text: [
6971
7155
  `Signed URL: ${data.signedUrl}`,
6972
- `R2 Key: ${r2_key}`,
7156
+ `Media key: ${maskR2Key(r2_key)}`,
6973
7157
  `Expires in: ${data.expiresIn ?? 3600}s`
6974
7158
  ].join("\n")
6975
7159
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@socialneuron/mcp-server",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
4
4
  "description": "MCP server for Social Neuron - AI content creation platform",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",