socialdatax-skills 0.2.16 → 0.2.17

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/README.md CHANGED
@@ -68,7 +68,7 @@ Common search phrases for this skill package:
68
68
  - Sensitive Check MCP server name: `sensitive-check`
69
69
  - Sensitive Check future MCP registry name: `com.socialdatax/sensitive-check`
70
70
  - Unified MCP registry name: none; this package installs skills and calls explicit platform services.
71
- - Current public capability version: `0.2.16`
71
+ - Current public capability version: `0.2.17`
72
72
 
73
73
  ## Direct CLI
74
74
 
@@ -120,9 +120,7 @@ npx -y socialdatax-skills@latest weibo comments --post-id "<post_id>" --all --in
120
120
  npx -y socialdatax-skills@latest weibo comments --post-url "<weibo_post_url_or_share_text>" --pretty
121
121
  npx -y socialdatax-skills@latest weibo replies --post-id "<post_id>" --comment-id "<comment_id>" --pretty
122
122
  npx -y socialdatax-skills@latest weibo likers --post-id "<post_id>" --pretty
123
- npx -y socialdatax-skills@latest weibo likers --post-url "<weibo_post_url_or_share_text>" --pretty
124
123
  npx -y socialdatax-skills@latest weibo reposts --post-id "<post_id>" --pretty
125
- npx -y socialdatax-skills@latest weibo reposts --post-url "<weibo_post_url_or_share_text>" --pretty
126
124
  npx -y socialdatax-skills@latest weibo user-info --user-id "<user_id>" --pretty
127
125
  npx -y socialdatax-skills@latest weibo user-info --profile-url "<profile_url_or_share_text>" --pretty
128
126
  npx -y socialdatax-skills@latest weibo user-posts --user-id "<user_id>" --pretty
@@ -139,10 +137,9 @@ npx -y socialdatax-skills@latest wechat user-info --user-id "<finder_user_id>" -
139
137
  npx -y socialdatax-skills@latest wechat user-posts --user-id "<finder_user_id>" --pretty
140
138
  npx -y socialdatax-skills@latest wechat user-posts --url "<wechat_video_url_or_share_text>" --pretty
141
139
  npx -y socialdatax-skills@latest sensitive-check text --text "<content>" --platform xhs --pretty
142
- cat draft.txt | npx -y socialdatax-skills@latest sensitive-check text --text-stdin --platform xhs --pretty
143
140
  ```
144
141
 
145
- Most direct CLI commands print a JSON envelope with `platform`, `tool`, `arguments`, and `data`. `sensitive-check` prints `platform`, `tool`, and `data` only; it does not echo the original text in CLI output. SocialDataX service records the submitted text and structured detection result for history, billing, and troubleshooting; the local CLI does not write API keys to local files or write local text copies.
142
+ Most direct CLI commands print a JSON envelope with `platform`, `tool`, `arguments`, and `data`. `sensitive-check` prints `platform`, `tool`, and `data` only; it does not echo the original text in CLI output.
146
143
 
147
144
  For XHS outputs with a returned `note_url` field, when `note_url` is non-null, preserve it exactly as the full URL, including `xsec_token` query parameters, such as in final answers or display. Do not modify, truncate, redact, normalize, rebuild, or replace it with a link assembled from `note_id`. If `note_url` is null, do not synthesize a public link from `note_id`.
148
145
 
@@ -158,6 +155,8 @@ For commands that accept `--page-token`, continue only with the complete returne
158
155
 
159
156
  Search pagination uses `--page-token` when continuing with a returned `next_page_token`. Omit `--page-token` on the first search request and pass the complete returned `next_page_token` only when continuing the same search chain.
160
157
 
158
+ Generated Agent Skill examples include `--source-client socialdatax-skills --source-platform <marketplace> --source-skill <skill-slug>` so SocialDataX can attribute direct CLI usage to the current marketplace Skill. Keep those values unchanged when running commands from a Skill; omit them for ad hoc CLI use. The direct CLI also accepts `SOCIALDATAX_SOURCE_CLIENT`, `SOCIALDATAX_SOURCE_PLATFORM`, and `SOCIALDATAX_SOURCE_SKILL` as environment-variable fallbacks.
159
+
161
160
  XHS, Douyin, Kuaishou, Weibo, and WeChat Channels search use `--keyword` and optional `--page-token`.
162
161
  Kuaishou work search uses `kuaishou search --keyword` and optional `--page-token`; Kuaishou creator search uses `kuaishou user-search --keyword` and optional `--page-token`. Kuaishou search does not accept Douyin semantic filters, and `kuaishou user-search` does not support `--since-days`.
163
162
  Weibo and WeChat Channels search use `--keyword` and optional `--page-token`.
@@ -194,7 +193,7 @@ npx -y socialdatax-skills@latest doctor
194
193
  npx -y socialdatax-skills@latest doctor --json
195
194
  ```
196
195
 
197
- The public package declares no npm lifecycle scripts such as `preinstall`, `install`, or `postinstall`. The installer copies AgentSkills files only, does not write API keys to local files, and does not change MCP server configuration. Authenticated data calls require `SOCIALDATAX_API_KEY` at runtime, do not read local browser data, and do not perform account actions.
196
+ The public package declares no npm lifecycle scripts such as `preinstall`, `install`, or `postinstall`. The installer copies AgentSkills files only, does not save API keys, and does not change MCP server configuration. Authenticated data calls require `SOCIALDATAX_API_KEY` at runtime, do not read local browser data, and do not perform account actions.
198
197
 
199
198
  ## Platform Names
200
199
 
@@ -264,7 +263,6 @@ npx -y socialdatax-skills@latest weibo search --keyword "露营" --pretty
264
263
  npx -y socialdatax-skills@latest wechat hot-search --pretty
265
264
  npx -y socialdatax-skills@latest wechat search --keyword "露营" --pretty
266
265
  npx -y socialdatax-skills@latest sensitive-check text --text "<content>" --platform xhs --pretty
267
- cat draft.txt | npx -y socialdatax-skills@latest sensitive-check text --text-stdin --platform xhs --pretty
268
266
  ```
269
267
 
270
268
  Available skills:
@@ -316,8 +314,8 @@ Current Kuaishou workflows include:
316
314
  - Fetch paginated first-level comments for comment analysis.
317
315
  - Fetch paginated replies under a first-level comment.
318
316
  - Continue Kuaishou list pagination only when `next_page_token` is non-empty; an empty string means there are no more results to request.
319
- - Read creator profile data from a profile link, short link, share text, or user_id.
320
- - Fetch paginated creator work lists from a user_id, profile link, short link, or share text.
317
+ - Read creator profile data from a non-empty user_id, profile link, short link, or share text. Live/fw-user profile shares are supported for profile data; successful results return a reusable non-empty user_id.
318
+ - Fetch paginated creator work lists from a non-empty user_id, or from a profile link, short link, or share text that resolves directly to a non-empty user_id. For live/fw-user profile shares, call creator profile first and use the returned non-empty user_id.
321
319
  - Submit and check video work 口播转文字 / speech-to-text transcript jobs; submit tools 提交完成后最多短等 15 秒.
322
320
 
323
321
  Current Douyin workflows include:
@@ -340,10 +338,9 @@ Current Weibo workflows include:
340
338
  - Resolve a Weibo post URL, short link, share text, or post_id into structured post details.
341
339
  - Fetch paginated first-level comments for comment analysis.
342
340
  - Fetch paginated replies under a first-level comment.
343
- - Fetch paginated liker and repost lists from a post_id, post URL, short link, or share text.
344
341
  - Continue Weibo list pagination only when `next_page_token` is non-empty; an empty string means there are no more results to request.
345
- - Read user profile data from a profile link, short link, share text, or user_id.
346
- - Fetch paginated user post lists from a user_id, profile link, short link, or share text.
342
+ - Read creator profile data from a profile link, short link, share text, or user_id.
343
+ - Fetch paginated creator post lists from a user_id, profile link, short link, or share text.
347
344
  - Submit and check Weibo video 口播转文字 / speech-to-text transcript jobs; submit tools 提交完成后最多短等 15 秒.
348
345
 
349
346
  Current WeChat Channels / 视频号 workflows include:
@@ -361,10 +358,8 @@ Current WeChat Channels / 视频号 workflows include:
361
358
  Current Sensitive Check workflows include:
362
359
 
363
360
  - Check draft text for 敏感检测 / sensitive-content risks before publishing.
364
- - Pass short drafts with `--text`, or pipe longer/sensitive drafts through `--text-stdin` so the text is not placed directly in the shell command.
365
361
  - Choose a platform context with `--platform generic`, `--platform xhs`, `--platform douyin`, or `--platform kuaishou`.
366
362
  - Return structured fields such as `violation`, `risk_level`, `types`, `highlights`, `summary`, `platform`, and `suggestions` when available.
367
- - SocialDataX service records submitted text and structured detection results for history, billing, and troubleshooting; the local CLI does not write API keys to local files or write local text copies.
368
363
  - Text checks are read-only. Image sensitive detection is reserved for a future separate tool.
369
364
 
370
365
  ## XHS Tools
@@ -418,10 +413,10 @@ Current Sensitive Check workflows include:
418
413
  | `kuaishou_get_video_comments_by_photo_id` | Fetch paginated first-level comments when the caller already has a photo_id. |
419
414
  | `kuaishou_get_video_comments_by_url` | Fetch paginated first-level comments directly from a Kuaishou work page link, short link, or share text. |
420
415
  | `kuaishou_get_video_comment_replies_by_comment_id` | Fetch paginated replies under a first-level comment by photo_id and comment_id. |
421
- | `kuaishou_get_user_info_by_user_id` | Fetch creator profile data when the caller already has a user_id. |
422
- | `kuaishou_get_user_info_by_profile_url` | Resolve a Kuaishou profile link, short link, or share text into creator profile data. |
423
- | `kuaishou_get_user_posted_videos_by_user_id` | Fetch a paginated list of works published by a creator when the caller already has a user_id. |
424
- | `kuaishou_get_user_posted_videos_by_profile_url` | Fetch a paginated list of works published by a creator from a profile link, short link, or share text. |
416
+ | `kuaishou_get_user_info_by_user_id` | Fetch creator profile data when the caller already has a non-empty user_id. |
417
+ | `kuaishou_get_user_info_by_profile_url` | Resolve a Kuaishou profile link, including live/fw-user profile shares, short link, or share text into creator profile data; successful results return a reusable non-empty user_id. |
418
+ | `kuaishou_get_user_posted_videos_by_user_id` | Fetch a paginated list of works published by a creator when the caller already has a non-empty user_id. |
419
+ | `kuaishou_get_user_posted_videos_by_profile_url` | Fetch a paginated list of works published by a creator from a profile link, short link, or share text that resolves directly to a non-empty user_id; for live/fw-user profile shares, call creator profile first and use the returned non-empty user_id. |
425
420
  | `kuaishou_submit_video_speech_text_by_video_url` | Submit a Kuaishou video speech-to-text transcript job from a work page link, short link, or share text; submit waits up to 15 seconds. |
426
421
  | `kuaishou_submit_video_speech_text_by_photo_id` | Submit a Kuaishou video speech-to-text transcript job when the caller already has a photo_id; submit waits up to 15 seconds. |
427
422
  | `kuaishou_get_video_speech_text_job` | Check a Kuaishou speech-to-text transcript job by job_id without creating a new task. |
@@ -437,14 +432,10 @@ Current Sensitive Check workflows include:
437
432
  | `weibo_get_post_comments_by_post_id` | Fetch paginated first-level comments when the caller already has a post_id. |
438
433
  | `weibo_get_post_comments_by_post_url` | Fetch paginated first-level comments directly from a Weibo post URL, short link, or share text. |
439
434
  | `weibo_get_post_comment_replies_by_comment_id` | Fetch paginated replies under a first-level comment by post_id and comment_id. |
440
- | `weibo_get_post_liker_list_by_post_id` | Fetch paginated users who liked a Weibo post by post_id. |
441
- | `weibo_get_post_liker_list_by_post_url` | Fetch paginated users who liked a Weibo post from a post URL, short link, or share text. |
442
- | `weibo_get_post_repost_list_by_post_id` | Fetch paginated reposts for a Weibo post by post_id. |
443
- | `weibo_get_post_repost_list_by_post_url` | Fetch paginated reposts for a Weibo post from a post URL, short link, or share text. |
444
- | `weibo_get_user_info_by_user_id` | Fetch user profile data when the caller already has a user_id. |
445
- | `weibo_get_user_info_by_profile_url` | Resolve a Weibo profile link, short link, or share text into user profile data. |
446
- | `weibo_get_user_posts_by_user_id` | Fetch a paginated list of posts published by a user when the caller already has a user_id. |
447
- | `weibo_get_user_posts_by_profile_url` | Fetch a paginated list of posts published by a user from a profile link, short link, or share text. |
435
+ | `weibo_get_user_info_by_user_id` | Fetch creator profile data when the caller already has a user_id. |
436
+ | `weibo_get_user_info_by_profile_url` | Resolve a Weibo profile link, short link, or share text into creator profile data. |
437
+ | `weibo_get_user_posts_by_user_id` | Fetch a paginated list of posts published by a creator when the caller already has a user_id. |
438
+ | `weibo_get_user_posts_by_profile_url` | Fetch a paginated list of posts published by a creator from a profile link, short link, or share text. |
448
439
  | `weibo_submit_video_speech_text_by_post_url` | Submit a Weibo video speech-to-text transcript job from a post URL, short link, or share text; submit waits up to 15 seconds. |
449
440
  | `weibo_submit_video_speech_text_by_post_id` | Submit a Weibo video speech-to-text transcript job when the caller already has a post_id; submit waits up to 15 seconds. |
450
441
  | `weibo_get_video_speech_text_job` | Check a Weibo speech-to-text transcript job by job_id without creating a new task. |
@@ -485,7 +476,6 @@ npx -y socialdatax-skills@latest kuaishou search --keyword "露营" --pretty
485
476
  npx -y socialdatax-skills@latest weibo search --keyword "露营" --pretty
486
477
  npx -y socialdatax-skills@latest wechat search --keyword "露营" --pretty
487
478
  npx -y socialdatax-skills@latest sensitive-check text --text "<content>" --platform xhs --pretty
488
- cat draft.txt | npx -y socialdatax-skills@latest sensitive-check text --text-stdin --platform xhs --pretty
489
479
  ```
490
480
 
491
481
  MCP client configuration belongs to platform or capability MCP listings. For XHS, use the `xhs-insights` docs; for Douyin, use the `douyin-insights` listing; for Kuaishou, use the `kuaishou-insights` listing; for Weibo, use the `weibo-insights` listing; for WeChat Channels / 视频号, use the `wechat-channels-insights` listing; for 敏感检测, use the `sensitive-check` server.
package/cli.mjs CHANGED
@@ -7,7 +7,7 @@ import { dirname, join, resolve } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
 
9
9
  const PACKAGE_NAME = "socialdatax-skills";
10
- const PACKAGE_VERSION = "0.2.16";
10
+ const PACKAGE_VERSION = "0.2.17";
11
11
  const PACKAGE_SPEC = `${PACKAGE_NAME}@latest`;
12
12
  const LOG_PREFIX = `[${PACKAGE_NAME}]`;
13
13
  const MIN_NODE_VERSION = "20.18.1";
@@ -15,6 +15,16 @@ const HOMEPAGE_URL = "https://socialdatax.com";
15
15
  const PRIMARY_API_KEY_ENV = "SOCIALDATAX_API_KEY";
16
16
  const LEGACY_API_KEY_ENV = "SOCIAL_MEDIA_MCP_API_KEY";
17
17
  const API_KEY_ENV_NAMES = [PRIMARY_API_KEY_ENV, LEGACY_API_KEY_ENV];
18
+ const SOURCE_CLIENT_ENV = "SOCIALDATAX_SOURCE_CLIENT";
19
+ const SOURCE_PLATFORM_ENV = "SOCIALDATAX_SOURCE_PLATFORM";
20
+ const SOURCE_SKILL_ENV = "SOCIALDATAX_SOURCE_SKILL";
21
+ const SOURCE_CLIENT_ENV_NAMES = [SOURCE_CLIENT_ENV];
22
+ const SOURCE_PLATFORM_ENV_NAMES = [SOURCE_PLATFORM_ENV];
23
+ const SOURCE_SKILL_ENV_NAMES = [SOURCE_SKILL_ENV];
24
+ const SOURCE_CLIENT_HEADER = "X-SocialDataX-Client";
25
+ const SOURCE_PLATFORM_HEADER = "X-SocialDataX-Source-Platform";
26
+ const SOURCE_SKILL_HEADER = "X-SocialDataX-Source-Skill";
27
+ const SOURCE_ATTRIBUTION_PATTERN = /^[a-z0-9][a-z0-9-]{0,99}$/;
18
28
  const AVAILABLE_SKILLS = [
19
29
  {
20
30
  name: "socialdatax-content-research-assistant",
@@ -73,14 +83,9 @@ const BOOLEAN_OPTIONS = new Set([
73
83
  "includeReplies",
74
84
  "json",
75
85
  "pretty",
76
- "textStdin",
77
- ]);
78
- const DIRECT_BOOLEAN_OPTIONS = new Set([
79
- "all",
80
- "includeReplies",
81
- "pretty",
82
- "textStdin",
83
86
  ]);
87
+ const DIRECT_BOOLEAN_OPTIONS = new Set(["all", "includeReplies", "pretty"]);
88
+ const DIRECT_META_OPTIONS = ["sourceClient", "sourcePlatform", "sourceSkill"];
84
89
  const INSTALL_TARGETS = ["openclaw", "hermes", "agents", "codex", "claude-code", "claude"];
85
90
  const VALID_SCOPES = ["user", "workspace", "shared"];
86
91
  const XHS_DIRECT_ACTION_OPTIONS = {
@@ -312,8 +317,8 @@ const WEIBO_DIRECT_ACTION_OPTIONS = {
312
317
  "includeReplies",
313
318
  "pretty",
314
319
  ],
315
- likers: ["postId", "postUrl", "pageToken", "pages", "all", "maxItems", "pretty"],
316
- reposts: ["postId", "postUrl", "pageToken", "pages", "all", "maxItems", "pretty"],
320
+ likers: ["postId", "pageToken", "pages", "all", "maxItems", "pretty"],
321
+ reposts: ["postId", "pageToken", "pages", "all", "maxItems", "pretty"],
317
322
  "user-info": ["userId", "profileUrl", "pretty"],
318
323
  "user-posts": [
319
324
  "userId",
@@ -419,7 +424,7 @@ const WECHAT_OPTION_DISPLAY_NAMES = {
419
424
  userId: "--user-id",
420
425
  };
421
426
  const SENSITIVE_CHECK_DIRECT_ACTION_OPTIONS = {
422
- text: ["text", "textStdin", "platform", "pretty"],
427
+ text: ["text", "platform", "pretty"],
423
428
  };
424
429
  const SENSITIVE_CHECK_DIRECT_ACTION_NAMES = Object.keys(
425
430
  SENSITIVE_CHECK_DIRECT_ACTION_OPTIONS
@@ -427,7 +432,6 @@ const SENSITIVE_CHECK_DIRECT_ACTION_NAMES = Object.keys(
427
432
  const SENSITIVE_CHECK_PLATFORMS = ["generic", "xhs", "douyin", "kuaishou"];
428
433
  const SENSITIVE_CHECK_OPTION_DISPLAY_NAMES = {
429
434
  text: "--text",
430
- textStdin: "--text-stdin",
431
435
  platform: "--platform",
432
436
  };
433
437
  const PLATFORMS = {
@@ -643,19 +647,22 @@ const PLATFORMS = {
643
647
  },
644
648
  {
645
649
  name: "kuaishou_get_user_info_by_user_id",
646
- description: "Fetch creator profile data when the caller already has a user_id.",
650
+ description: "Fetch creator profile data when the caller already has a non-empty user_id.",
647
651
  },
648
652
  {
649
653
  name: "kuaishou_get_user_info_by_profile_url",
650
- description: "Resolve a Kuaishou profile link, short link, or share text into creator profile data.",
654
+ description:
655
+ "Resolve a Kuaishou profile link, including live/fw-user profile shares, short link, or share text into creator profile data; successful results return a reusable non-empty user_id.",
651
656
  },
652
657
  {
653
658
  name: "kuaishou_get_user_posted_videos_by_user_id",
654
- description: "Fetch a paginated list of works published by a creator when the caller already has a user_id.",
659
+ description:
660
+ "Fetch a paginated list of works published by a creator when the caller already has a non-empty user_id.",
655
661
  },
656
662
  {
657
663
  name: "kuaishou_get_user_posted_videos_by_profile_url",
658
- description: "Fetch a paginated list of works published by a creator from a profile link, short link, or share text.",
664
+ description:
665
+ "Fetch a paginated list of works published by a creator from a profile link, short link, or share text that resolves directly to a non-empty user_id; for live/fw-user profile shares, call creator profile first and use the returned non-empty user_id.",
659
666
  },
660
667
  {
661
668
  name: "kuaishou_submit_video_speech_text_by_video_url",
@@ -721,20 +728,10 @@ const PLATFORMS = {
721
728
  name: "weibo_get_post_liker_list_by_post_id",
722
729
  description: "Fetch a paginated list of users who liked a Weibo post by post_id.",
723
730
  },
724
- {
725
- name: "weibo_get_post_liker_list_by_post_url",
726
- description:
727
- "Fetch a paginated list of users who liked a Weibo post from a post URL, short link, or share text.",
728
- },
729
731
  {
730
732
  name: "weibo_get_post_repost_list_by_post_id",
731
733
  description: "Fetch a paginated repost list for a Weibo post by post_id.",
732
734
  },
733
- {
734
- name: "weibo_get_post_repost_list_by_post_url",
735
- description:
736
- "Fetch a paginated repost list for a Weibo post from a post URL, short link, or share text.",
737
- },
738
735
  {
739
736
  name: "weibo_get_user_info_by_user_id",
740
737
  description: "Fetch creator profile data when the caller already has a user_id.",
@@ -975,6 +972,10 @@ function validateKnownOptions(options, allowedOptions) {
975
972
  }
976
973
  }
977
974
 
975
+ function allowedDirectOptions(allowedOptions) {
976
+ return [...allowedOptions, ...DIRECT_META_OPTIONS];
977
+ }
978
+
978
979
  function requireOptionValue(options, key, displayName) {
979
980
  if (options[key] === true || options[key] === "") {
980
981
  throw new Error(`Missing value for ${displayName}.`);
@@ -993,7 +994,8 @@ function validateXhsDirectActionOptions(action, options) {
993
994
  return;
994
995
  }
995
996
 
996
- validateKnownOptions(options, allowedOptions);
997
+ validateKnownOptions(options, allowedDirectOptions(allowedOptions));
998
+ validateDirectMetaOptions(options);
997
999
  validateDirectPaginationOptions("xhs", action, options);
998
1000
  for (const key of allowedOptions) {
999
1001
  if (!DIRECT_BOOLEAN_OPTIONS.has(key)) {
@@ -1008,7 +1010,8 @@ function validateDouyinDirectActionOptions(action, options) {
1008
1010
  return;
1009
1011
  }
1010
1012
 
1011
- validateKnownOptions(options, allowedOptions);
1013
+ validateKnownOptions(options, allowedDirectOptions(allowedOptions));
1014
+ validateDirectMetaOptions(options);
1012
1015
  validateDirectPaginationOptions("douyin", action, options);
1013
1016
  for (const key of allowedOptions) {
1014
1017
  if (!DIRECT_BOOLEAN_OPTIONS.has(key)) {
@@ -1023,7 +1026,8 @@ function validateKuaishouDirectActionOptions(action, options) {
1023
1026
  return;
1024
1027
  }
1025
1028
 
1026
- validateKnownOptions(options, allowedOptions);
1029
+ validateKnownOptions(options, allowedDirectOptions(allowedOptions));
1030
+ validateDirectMetaOptions(options);
1027
1031
  validateDirectPaginationOptions("kuaishou", action, options);
1028
1032
  for (const key of allowedOptions) {
1029
1033
  if (!DIRECT_BOOLEAN_OPTIONS.has(key)) {
@@ -1038,7 +1042,8 @@ function validateWeiboDirectActionOptions(action, options) {
1038
1042
  return;
1039
1043
  }
1040
1044
 
1041
- validateKnownOptions(options, allowedOptions);
1045
+ validateKnownOptions(options, allowedDirectOptions(allowedOptions));
1046
+ validateDirectMetaOptions(options);
1042
1047
  validateDirectPaginationOptions("weibo", action, options);
1043
1048
  for (const key of allowedOptions) {
1044
1049
  if (!DIRECT_BOOLEAN_OPTIONS.has(key)) {
@@ -1053,7 +1058,8 @@ function validateWechatDirectActionOptions(action, options) {
1053
1058
  return;
1054
1059
  }
1055
1060
 
1056
- validateKnownOptions(options, allowedOptions);
1061
+ validateKnownOptions(options, allowedDirectOptions(allowedOptions));
1062
+ validateDirectMetaOptions(options);
1057
1063
  validateDirectPaginationOptions("wechat", action, options);
1058
1064
  for (const key of allowedOptions) {
1059
1065
  if (!DIRECT_BOOLEAN_OPTIONS.has(key)) {
@@ -1068,9 +1074,9 @@ function validateSensitiveCheckDirectActionOptions(action, options) {
1068
1074
  return;
1069
1075
  }
1070
1076
 
1071
- validateKnownOptions(options, allowedOptions);
1077
+ validateKnownOptions(options, allowedDirectOptions(allowedOptions));
1078
+ validateDirectMetaOptions(options);
1072
1079
  validateFlagOption(options, "pretty", "--pretty");
1073
- validateFlagOption(options, "textStdin", "--text-stdin");
1074
1080
  for (const key of allowedOptions) {
1075
1081
  if (!DIRECT_BOOLEAN_OPTIONS.has(key)) {
1076
1082
  requireOptionValue(options, key, SENSITIVE_CHECK_OPTION_DISPLAY_NAMES[key]);
@@ -1078,6 +1084,21 @@ function validateSensitiveCheckDirectActionOptions(action, options) {
1078
1084
  }
1079
1085
  }
1080
1086
 
1087
+ function validateDirectMetaOptions(options) {
1088
+ if (options.sourceClient !== undefined) {
1089
+ requireOptionValue(options, "sourceClient", "--source-client");
1090
+ normalizeSourceAttribution(options.sourceClient, "--source-client");
1091
+ }
1092
+ if (options.sourcePlatform !== undefined) {
1093
+ requireOptionValue(options, "sourcePlatform", "--source-platform");
1094
+ normalizeSourceAttribution(options.sourcePlatform, "--source-platform");
1095
+ }
1096
+ if (options.sourceSkill !== undefined) {
1097
+ requireOptionValue(options, "sourceSkill", "--source-skill");
1098
+ normalizeSourceAttribution(options.sourceSkill, "--source-skill");
1099
+ }
1100
+ }
1101
+
1081
1102
  function validateDirectPaginationOptions(platformId, action, options) {
1082
1103
  validateFlagOption(options, "pretty", "--pretty");
1083
1104
  validateFlagOption(options, "all", "--all");
@@ -1227,6 +1248,47 @@ function readFirstEnv(names) {
1227
1248
  return undefined;
1228
1249
  }
1229
1250
 
1251
+ function normalizeSourceAttribution(value, displayName) {
1252
+ const text = String(value || "").trim().toLowerCase();
1253
+ if (!text) {
1254
+ return "";
1255
+ }
1256
+ if (!SOURCE_ATTRIBUTION_PATTERN.test(text)) {
1257
+ throw new Error(
1258
+ `${displayName} must be a lowercase skill slug using letters, numbers, and hyphens.`
1259
+ );
1260
+ }
1261
+ return text;
1262
+ }
1263
+
1264
+ function resolveSourceAttribution(options = {}) {
1265
+ return {
1266
+ sourceClient:
1267
+ options.sourceClient !== undefined
1268
+ ? normalizeSourceAttribution(options.sourceClient, "--source-client")
1269
+ : normalizeSourceAttribution(
1270
+ readFirstEnv(SOURCE_CLIENT_ENV_NAMES) || PACKAGE_NAME,
1271
+ SOURCE_CLIENT_ENV
1272
+ ),
1273
+ sourcePlatform:
1274
+ options.sourcePlatform !== undefined
1275
+ ? normalizeSourceAttribution(options.sourcePlatform, "--source-platform")
1276
+ : normalizeSourceAttribution(
1277
+ readFirstEnv(SOURCE_PLATFORM_ENV_NAMES),
1278
+ SOURCE_PLATFORM_ENV
1279
+ ),
1280
+ sourceSkill:
1281
+ options.sourceSkill !== undefined
1282
+ ? normalizeSourceAttribution(options.sourceSkill, "--source-skill")
1283
+ : normalizeSourceAttribution(readFirstEnv(SOURCE_SKILL_ENV_NAMES), SOURCE_SKILL_ENV),
1284
+ };
1285
+ }
1286
+
1287
+ function attachDirectMetadata(operation, options) {
1288
+ operation.sourceAttribution = resolveSourceAttribution(options);
1289
+ return operation;
1290
+ }
1291
+
1230
1292
  function resolveUpstreamUrl(platform) {
1231
1293
  return readFirstEnv(platform.upstreamEnv) || platform.endpoint;
1232
1294
  }
@@ -1388,10 +1450,15 @@ async function installSkills(args) {
1388
1450
  console.log("Data calls do not perform login, posting, editing, liking, commenting, or account actions.");
1389
1451
  console.log("Configure your API Key before making authenticated calls:");
1390
1452
  console.log(` export ${PRIMARY_API_KEY_ENV}="<${PRIMARY_API_KEY_ENV}>"`);
1453
+ printInstalledSkillAttributionNote();
1454
+ }
1455
+
1456
+ function printInstalledSkillAttributionNote() {
1391
1457
  console.log("");
1392
- console.log("Direct CLI examples:");
1393
- console.log(` npx -y ${PACKAGE_SPEC} xhs search --keyword "露营" --pretty`);
1394
- console.log(` npx -y ${PACKAGE_SPEC} xhs detail --note-id "<note_id>" --pretty`);
1458
+ console.log(
1459
+ "Direct CLI examples in installed SKILL.md files already include source attribution for agents."
1460
+ );
1461
+ console.log("No extra source attribution setup is required.");
1395
1462
  }
1396
1463
 
1397
1464
  function resolveInstallDestination({
@@ -1577,7 +1644,7 @@ function printDoctor(args) {
1577
1644
  console.log("Install safety:");
1578
1645
  console.log("- npm lifecycle scripts: none declared by this package.");
1579
1646
  console.log("- install writes AgentSkills files only.");
1580
- console.log("- install does not write API keys to local files.");
1647
+ console.log("- install does not store API keys.");
1581
1648
  console.log("- install does not change MCP server configuration.");
1582
1649
  console.log("- install --dry-run previews destinations without writing files.");
1583
1650
  console.log("");
@@ -1713,7 +1780,9 @@ function printHelp() {
1713
1780
  console.log(" Call the Kuaishou creator works tool directly and print JSON.");
1714
1781
  console.log("");
1715
1782
  console.log(` npx -y ${PACKAGE_SPEC} kuaishou user-posts --profile-url "<profile_url_or_share_text>" --pretty`);
1716
- console.log(" Call the Kuaishou creator works tool from a profile link or share text.");
1783
+ console.log(
1784
+ " Call the Kuaishou creator works tool from a profile link or share text that resolves directly to a non-empty user_id."
1785
+ );
1717
1786
  console.log("");
1718
1787
  console.log(` npx -y ${PACKAGE_SPEC} weibo hot-search --pretty`);
1719
1788
  console.log(" Call the Weibo hot-search list tool directly and print JSON.");
@@ -1739,15 +1808,9 @@ function printHelp() {
1739
1808
  console.log(` npx -y ${PACKAGE_SPEC} weibo likers --post-id "<post_id>" --pretty`);
1740
1809
  console.log(" Call the Weibo post liker list tool directly and print JSON.");
1741
1810
  console.log("");
1742
- console.log(` npx -y ${PACKAGE_SPEC} weibo likers --post-url "<weibo_post_url_or_share_text>" --pretty`);
1743
- console.log(" Call the Weibo post liker list tool from a post link or share text.");
1744
- console.log("");
1745
1811
  console.log(` npx -y ${PACKAGE_SPEC} weibo reposts --post-id "<post_id>" --pretty`);
1746
1812
  console.log(" Call the Weibo post repost list tool directly and print JSON.");
1747
1813
  console.log("");
1748
- console.log(` npx -y ${PACKAGE_SPEC} weibo reposts --post-url "<weibo_post_url_or_share_text>" --pretty`);
1749
- console.log(" Call the Weibo post repost list tool from a post link or share text.");
1750
- console.log("");
1751
1814
  console.log(` npx -y ${PACKAGE_SPEC} weibo user-info --user-id "<user_id>" --pretty`);
1752
1815
  console.log(" Call the Weibo creator profile tool directly and print JSON.");
1753
1816
  console.log("");
@@ -1876,8 +1939,6 @@ function printHelp() {
1876
1939
  console.log(" --sec-user-id <sec_user_id>");
1877
1940
  console.log(" --text <content>");
1878
1941
  console.log(" Text content for sensitive-check text.");
1879
- console.log(" --text-stdin");
1880
- console.log(" Read sensitive-check text from stdin instead of a shell argument.");
1881
1942
  console.log(" --platform <generic|xhs|douyin|kuaishou>");
1882
1943
  console.log(" Target platform for sensitive-check text; default is generic.");
1883
1944
  console.log(" --pages <number>");
@@ -1905,6 +1966,12 @@ function printHelp() {
1905
1966
  console.log(" Douyin content type filter; omit for all content types.");
1906
1967
  console.log(" --page-token <token>");
1907
1968
  console.log(" Continue token-paginated commands with the complete returned next_page_token. For search, omit it on the first request.");
1969
+ console.log(" --source-client <slug>");
1970
+ console.log(" Attribution client slug for authenticated direct CLI calls; defaults to socialdatax-skills.");
1971
+ console.log(" --source-platform <slug>");
1972
+ console.log(" Attribution marketplace/platform slug for the current Agent Skill, such as modelscope or skillhub.");
1973
+ console.log(" --source-skill <slug>");
1974
+ console.log(" Attribute authenticated direct CLI calls to the current Agent Skill usage slug.");
1908
1975
  console.log(" --sort-type <all|time_descending|collect_count_descending>");
1909
1976
  console.log(" WeChat Channels / 视频号 search sort; collect_count_descending means hottest first / most collected first; omit for default sort.");
1910
1977
  console.log(" --duration-range <all|under_5_min|between_5_and_20_min|over_20_min>");
@@ -1964,7 +2031,7 @@ async function runXhsDirectCommand(args) {
1964
2031
  }
1965
2032
  validateXhsDirectActionOptions(action, options);
1966
2033
 
1967
- const operation = buildXhsOperation(action, options);
2034
+ const operation = attachDirectMetadata(buildXhsOperation(action, options), options);
1968
2035
  const data = shouldUsePaginatedDirectOutput(options)
1969
2036
  ? await callPaginatedDirectOperation(operation, options)
1970
2037
  : await callDirectOperation(operation);
@@ -1991,7 +2058,7 @@ async function runDouyinDirectCommand(args) {
1991
2058
  }
1992
2059
  validateDouyinDirectActionOptions(action, options);
1993
2060
 
1994
- const operation = buildDouyinOperation(action, options);
2061
+ const operation = attachDirectMetadata(buildDouyinOperation(action, options), options);
1995
2062
  const data = shouldUsePaginatedDirectOutput(options)
1996
2063
  ? await callPaginatedDirectOperation(operation, options)
1997
2064
  : await callDirectOperation(operation);
@@ -2018,7 +2085,7 @@ async function runKuaishouDirectCommand(args) {
2018
2085
  }
2019
2086
  validateKuaishouDirectActionOptions(action, options);
2020
2087
 
2021
- const operation = buildKuaishouOperation(action, options);
2088
+ const operation = attachDirectMetadata(buildKuaishouOperation(action, options), options);
2022
2089
  const data = shouldUsePaginatedDirectOutput(options)
2023
2090
  ? await callPaginatedDirectOperation(operation, options)
2024
2091
  : await callDirectOperation(operation);
@@ -2045,7 +2112,7 @@ async function runWeiboDirectCommand(args) {
2045
2112
  }
2046
2113
  validateWeiboDirectActionOptions(action, options);
2047
2114
 
2048
- const operation = buildWeiboOperation(action, options);
2115
+ const operation = attachDirectMetadata(buildWeiboOperation(action, options), options);
2049
2116
  const data = shouldUsePaginatedDirectOutput(options)
2050
2117
  ? await callPaginatedDirectOperation(operation, options)
2051
2118
  : await callDirectOperation(operation);
@@ -2072,7 +2139,7 @@ async function runWechatDirectCommand(args) {
2072
2139
  }
2073
2140
  validateWechatDirectActionOptions(action, options);
2074
2141
 
2075
- const operation = buildWechatOperation(action, options);
2142
+ const operation = attachDirectMetadata(buildWechatOperation(action, options), options);
2076
2143
  const data = shouldUsePaginatedDirectOutput(options)
2077
2144
  ? await callPaginatedDirectOperation(operation, options)
2078
2145
  : await callDirectOperation(operation);
@@ -2099,8 +2166,10 @@ async function runSensitiveCheckDirectCommand(args) {
2099
2166
  }
2100
2167
  validateSensitiveCheckDirectActionOptions(action, options);
2101
2168
 
2102
- const resolvedOptions = await resolveSensitiveCheckDirectOptions(action, options);
2103
- const operation = buildSensitiveCheckOperation(action, resolvedOptions);
2169
+ const operation = attachDirectMetadata(
2170
+ buildSensitiveCheckOperation(action, options),
2171
+ options
2172
+ );
2104
2173
  const data = await callDirectOperation(operation);
2105
2174
  const envelope = {
2106
2175
  platform: operation.platform.id,
@@ -2111,38 +2180,6 @@ async function runSensitiveCheckDirectCommand(args) {
2111
2180
  process.stdout.write("\n");
2112
2181
  }
2113
2182
 
2114
- async function resolveSensitiveCheckDirectOptions(action, options) {
2115
- if (action !== "text") {
2116
- return options;
2117
- }
2118
- if (options.text !== undefined && options.textStdin !== undefined) {
2119
- throw new Error("Use only one of --text or --text-stdin for sensitive-check text.");
2120
- }
2121
- if (options.textStdin === undefined) {
2122
- return options;
2123
- }
2124
- const text = await readStdinText();
2125
- if (!text.trim()) {
2126
- throw new Error("Missing stdin text for --text-stdin.");
2127
- }
2128
- return {
2129
- ...options,
2130
- text,
2131
- };
2132
- }
2133
-
2134
- async function readStdinText() {
2135
- if (process.stdin.isTTY) {
2136
- return "";
2137
- }
2138
- process.stdin.setEncoding("utf8");
2139
- let text = "";
2140
- for await (const chunk of process.stdin) {
2141
- text += chunk;
2142
- }
2143
- return text;
2144
- }
2145
-
2146
2183
  function buildXhsOperation(action, options) {
2147
2184
  switch (action) {
2148
2185
  case "hot-search":
@@ -2476,33 +2513,13 @@ function buildWeiboOperation(action, options) {
2476
2513
  case "likers":
2477
2514
  return buildDirectOperation(
2478
2515
  "likers",
2479
- buildOneOfCall(options, {
2480
- idOption: "postId",
2481
- urlOption: "postUrl",
2482
- idTool: "weibo_get_post_liker_list_by_post_id",
2483
- urlTool: "weibo_get_post_liker_list_by_post_url",
2484
- idArgument: "post_id",
2485
- urlArgument: "post_url",
2486
- idDisplay: "--post-id",
2487
- urlDisplay: "--post-url",
2488
- pageToken: options.pageToken,
2489
- }),
2516
+ buildWeiboPostListByPostIdCall(options, "likers", "weibo_get_post_liker_list_by_post_id"),
2490
2517
  PLATFORMS.weibo
2491
2518
  );
2492
2519
  case "reposts":
2493
2520
  return buildDirectOperation(
2494
2521
  "reposts",
2495
- buildOneOfCall(options, {
2496
- idOption: "postId",
2497
- urlOption: "postUrl",
2498
- idTool: "weibo_get_post_repost_list_by_post_id",
2499
- urlTool: "weibo_get_post_repost_list_by_post_url",
2500
- idArgument: "post_id",
2501
- urlArgument: "post_url",
2502
- idDisplay: "--post-id",
2503
- urlDisplay: "--post-url",
2504
- pageToken: options.pageToken,
2505
- }),
2522
+ buildWeiboPostListByPostIdCall(options, "reposts", "weibo_get_post_repost_list_by_post_id"),
2506
2523
  PLATFORMS.weibo
2507
2524
  );
2508
2525
  case "user-info":
@@ -2900,7 +2917,7 @@ function buildWechatSearchCall(options) {
2900
2917
  }
2901
2918
 
2902
2919
  function buildSensitiveCheckTextCall(options) {
2903
- if (typeof options.text !== "string" || !options.text.trim()) {
2920
+ if (!options.text) {
2904
2921
  throw new Error("Missing --text for sensitive-check text.");
2905
2922
  }
2906
2923
  const platform = parseAllowedStringOption(
@@ -2978,6 +2995,22 @@ function buildWeiboRepliesCall(options) {
2978
2995
  };
2979
2996
  }
2980
2997
 
2998
+ function buildWeiboPostListByPostIdCall(options, action, tool) {
2999
+ if (!options.postId) {
3000
+ throw new Error(`Missing --post-id for weibo ${action}.`);
3001
+ }
3002
+ const toolArguments = {
3003
+ post_id: options.postId,
3004
+ };
3005
+ if (options.pageToken) {
3006
+ toolArguments.page_token = options.pageToken;
3007
+ }
3008
+ return {
3009
+ tool,
3010
+ toolArguments,
3011
+ };
3012
+ }
3013
+
2981
3014
  function buildWechatCommentsCall(options) {
2982
3015
  const hasObjectInput = Boolean(options.objectId || options.objectNonceId);
2983
3016
  const hasUrlInput = Boolean(options.url);
@@ -3499,6 +3532,9 @@ async function attachRepliesToCommentItems(operation, items, pageData) {
3499
3532
  operation.arguments,
3500
3533
  pageData
3501
3534
  );
3535
+ repliesOperation.sourceAttribution = operation.sourceAttribution
3536
+ ? { ...operation.sourceAttribution }
3537
+ : undefined;
3502
3538
  const repliesData = await collectPaginatedDirectData(
3503
3539
  repliesOperation,
3504
3540
  { all: true, pages: undefined, maxItems: undefined },
@@ -3661,11 +3697,22 @@ async function callMcpBackend(operation) {
3661
3697
  { name: PACKAGE_NAME, version: PACKAGE_VERSION },
3662
3698
  { capabilities: {} }
3663
3699
  );
3700
+ const headers = {
3701
+ Authorization: `Bearer ${apiKey}`,
3702
+ };
3703
+ const sourceAttribution = operation.sourceAttribution || {};
3704
+ if (sourceAttribution.sourceClient) {
3705
+ headers[SOURCE_CLIENT_HEADER] = sourceAttribution.sourceClient;
3706
+ }
3707
+ if (sourceAttribution.sourcePlatform) {
3708
+ headers[SOURCE_PLATFORM_HEADER] = sourceAttribution.sourcePlatform;
3709
+ }
3710
+ if (sourceAttribution.sourceSkill) {
3711
+ headers[SOURCE_SKILL_HEADER] = sourceAttribution.sourceSkill;
3712
+ }
3664
3713
  const transport = new StreamableHTTPClientTransport(new URL(upstreamUrl), {
3665
3714
  requestInit: {
3666
- headers: {
3667
- Authorization: `Bearer ${apiKey}`,
3668
- },
3715
+ headers,
3669
3716
  },
3670
3717
  });
3671
3718