@xiawan-play/steam-tools-mcp 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -9882,11 +9882,11 @@ function parseAppIdCollection(value) {
9882
9882
  function getNestedValue(value, ...keys) {
9883
9883
  let current = value;
9884
9884
  for (const key of keys) {
9885
- const record3 = asRecord(current);
9886
- if (!record3) {
9885
+ const record2 = asRecord(current);
9886
+ if (!record2) {
9887
9887
  return void 0;
9888
9888
  }
9889
- current = record3[key];
9889
+ current = record2[key];
9890
9890
  }
9891
9891
  return current;
9892
9892
  }
@@ -9948,119 +9948,16 @@ function chunkStrings(values, chunkSize) {
9948
9948
  }
9949
9949
  return chunks;
9950
9950
  }
9951
- function normalizeAppCatalogType(record3) {
9952
- if (!record3) {
9951
+ function normalizeAppCatalogType(record2) {
9952
+ if (!record2) {
9953
9953
  return void 0;
9954
9954
  }
9955
- return getStringValue(record3.type) ?? getStringValue(record3.app_type) ?? getStringValue(record3.app_type_string);
9955
+ return getStringValue(record2.type) ?? getStringValue(record2.app_type) ?? getStringValue(record2.app_type_string);
9956
9956
  }
9957
9957
 
9958
9958
  // src/lib/steam-client.ts
9959
9959
  import * as z from "zod/v4";
9960
9960
  var DEFAULT_TIMEOUT_MS = 3e4;
9961
- var NUMERIC_PARAM_TYPES = [
9962
- "int32",
9963
- "int64",
9964
- "uint8",
9965
- "uint16",
9966
- "uint32",
9967
- "uint64",
9968
- "float",
9969
- "double",
9970
- "steamid",
9971
- "steamid64",
9972
- "gameid",
9973
- "appid",
9974
- "publishedfileid",
9975
- "eresult"
9976
- ];
9977
- function createToolDescription(method) {
9978
- const auth = method.requiresPublisherKey ? "\u9700\u8981 Steam publisher key\uFF08\u53D1\u884C\u5546\u5BC6\u94A5\uFF09\u3002" : method.requiresUserKey ? "\u9700\u8981 Steam user Web API key\uFF08\u7528\u6237\u5BC6\u94A5\uFF09\u3002" : "\u6309\u5F53\u524D\u6587\u6863\u53EF\u533F\u540D\u8C03\u7528\uFF0C\u4E0D\u9700\u8981 key\u3002";
9979
- const service = method.serviceInterface ? "\u8FD9\u662F Steam Service Interface\uFF0C\u8BF7\u6C42\u4F1A\u4EE5 input_json \u65B9\u5F0F\u53D1\u9001\u3002" : "\u8FD9\u662F\u6807\u51C6 Steam Web API \u63A5\u53E3\uFF0C\u4F7F\u7528\u666E\u901A\u53C2\u6570\u98CE\u683C\u8C03\u7528\u3002";
9980
- const params = method.parameters.length ? `\u53C2\u6570\u6982\u89C8\uFF1A${method.parameters.map(
9981
- (param) => `${param.inputName}${param.required && param.originalName !== "key" ? "*" : ""}`
9982
- ).join(", ")}\u3002` : "\u53C2\u6570\u6982\u89C8\uFF1A\u65E0\u53C2\u6570\u3002";
9983
- const officialDescription = method.description?.trim() ? `\u5B98\u65B9\u82F1\u6587\u8BF4\u660E\uFF1A${method.description}` : "\u5B98\u65B9\u82F1\u6587\u8BF4\u660E\uFF1A\u65E0\u3002";
9984
- return `${officialDescription} \u9274\u6743\u8981\u6C42\uFF1A${auth} \u8C03\u7528\u65B9\u5F0F\uFF1A${service} ${params}`;
9985
- }
9986
- function createParamSchema(param, options) {
9987
- let schema;
9988
- const normalizedType = param.type.toLowerCase();
9989
- if (normalizedType === "bool") {
9990
- schema = z.union([
9991
- z.boolean(),
9992
- z.literal("true"),
9993
- z.literal("false"),
9994
- z.literal("1"),
9995
- z.literal("0"),
9996
- z.literal(1),
9997
- z.literal(0)
9998
- ]);
9999
- } else if (normalizedType === "string" || normalizedType === "rawbinary") {
10000
- schema = z.string();
10001
- } else if (normalizedType === "{message}" || normalizedType.includes("message")) {
10002
- schema = z.union([z.record(z.string(), z.any()), z.array(z.any())]);
10003
- } else if (normalizedType.endsWith("[]")) {
10004
- const itemType = normalizedType.slice(0, -2);
10005
- schema = z.array(createScalarSchema(itemType));
10006
- } else if (NUMERIC_PARAM_TYPES.some((type) => normalizedType.includes(type))) {
10007
- schema = createScalarSchema(normalizedType);
10008
- } else {
10009
- schema = z.union([
10010
- z.string(),
10011
- z.number(),
10012
- z.boolean(),
10013
- z.record(z.string(), z.any()),
10014
- z.array(z.any())
10015
- ]);
10016
- }
10017
- if (param.isArray && !normalizedType.endsWith("[]")) {
10018
- schema = z.union([schema, z.array(schema)]);
10019
- }
10020
- if (options?.allowDefaultSteamId) {
10021
- schema = schema.optional();
10022
- } else if (param.originalName === "key") {
10023
- schema = schema.optional();
10024
- } else if (!param.required) {
10025
- schema = schema.optional();
10026
- }
10027
- const details = [
10028
- `Steam \u53C2\u6570\u540D\uFF1A${param.originalName}\u3002`,
10029
- `\u7C7B\u578B\uFF1A${param.type}\u3002`,
10030
- param.required || options?.allowDefaultSteamId ? options?.allowDefaultSteamId ? "\u9ED8\u8BA4\u60C5\u51B5\u4E0B\u5FC5\u586B\uFF1B\u5982\u679C\u5DF2\u7ECF\u914D\u7F6E STEAM_DEFAULT_STEAMID\uFF0C\u5219\u53EF\u4EE5\u7701\u7565\u3002" : "\u662F\u5426\u5FC5\u586B\uFF1A\u662F\u3002" : "\u662F\u5426\u5FC5\u586B\uFF1A\u5426\u3002",
10031
- param.description ? `\u5B98\u65B9\u82F1\u6587\u8BF4\u660E\uFF1A${param.description}` : "\u5B98\u65B9\u82F1\u6587\u8BF4\u660E\uFF1A\u65E0\u3002"
10032
- ].filter(Boolean).join(" ");
10033
- return schema.describe(details);
10034
- }
10035
- function createScalarSchema(type) {
10036
- if (type === "bool") {
10037
- return z.union([
10038
- z.boolean(),
10039
- z.literal("true"),
10040
- z.literal("false"),
10041
- z.literal("1"),
10042
- z.literal("0"),
10043
- z.literal(1),
10044
- z.literal(0)
10045
- ]);
10046
- }
10047
- if (type === "string" || type === "rawbinary") {
10048
- return z.string();
10049
- }
10050
- return z.union([z.string(), z.number()]);
10051
- }
10052
- function buildInputSchema(method, options) {
10053
- if (!method.parameters.length) {
10054
- return void 0;
10055
- }
10056
- const schemaEntries = method.parameters.map((param) => [
10057
- param.inputName,
10058
- createParamSchema(param, {
10059
- allowDefaultSteamId: Boolean(options?.defaultSteamId) && (param.originalName === "steamid" || param.originalName === "steamids")
10060
- })
10061
- ]);
10062
- return Object.fromEntries(schemaEntries);
10063
- }
10064
9961
  async function callSteamApi(method, args, options) {
10065
9962
  const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
10066
9963
  const endpoint = resolveEndpointUrl(method.url, options?.baseUrl);
@@ -10203,6 +10100,9 @@ function redactUrl(url) {
10203
10100
  return `${url.origin}${url.pathname}`;
10204
10101
  }
10205
10102
 
10103
+ // src/services/base.ts
10104
+ import { load } from "cheerio";
10105
+
10206
10106
  // src/normalizers/games.ts
10207
10107
  function normalizeRecentlyPlayedGames(data) {
10208
10108
  const response = asRecord(getNestedValue(data, "response"));
@@ -10303,18 +10203,18 @@ function parseAppCatalogPage(data) {
10303
10203
  ];
10304
10204
  const entries = [];
10305
10205
  for (const entry of rawEntries) {
10306
- const record3 = asRecord(entry);
10307
- const appid = normalizeScalarInput(record3?.appid);
10308
- const name = getStringValue(record3?.name);
10206
+ const record2 = asRecord(entry);
10207
+ const appid = normalizeScalarInput(record2?.appid);
10208
+ const name = getStringValue(record2?.name);
10309
10209
  if (!appid || !name) {
10310
10210
  continue;
10311
10211
  }
10312
10212
  entries.push({
10313
10213
  appid,
10314
10214
  name,
10315
- type: normalizeAppCatalogType(record3),
10316
- lastModified: toNumber(record3?.last_modified),
10317
- priceChangeNumber: toNumber(record3?.price_change_number)
10215
+ type: normalizeAppCatalogType(record2),
10216
+ lastModified: toNumber(record2?.last_modified),
10217
+ priceChangeNumber: toNumber(record2?.price_change_number)
10318
10218
  });
10319
10219
  }
10320
10220
  const lastAppId = normalizeScalarInput(getNestedValue(data, "response", "last_appid")) ?? entries.at(-1)?.appid;
@@ -10686,6 +10586,8 @@ function createSteamBaseRuntime(spec2, config) {
10686
10586
  spec2.methods.map((method) => [method.toolName, method])
10687
10587
  );
10688
10588
  let appCatalogCache = null;
10589
+ const localizedAppNameCache = /* @__PURE__ */ new Map();
10590
+ const storeSearchCache = /* @__PURE__ */ new Map();
10689
10591
  async function invokeSteamMethod(method, args, apiKeyOverride) {
10690
10592
  if (method.interfaceName === "ISteamApps" && method.methodName === "GetAppList") {
10691
10593
  throw new Error(
@@ -10830,10 +10732,10 @@ function createSteamBaseRuntime(spec2, config) {
10830
10732
  apiKey
10831
10733
  );
10832
10734
  for (const player of getNestedArray(result.data, "response", "players")) {
10833
- const record3 = normalizePlayerSummary({ response: { players: [player] } });
10735
+ const record2 = normalizePlayerSummary({ response: { players: [player] } });
10834
10736
  const steamid = getStringValue(asRecord(player)?.steamid);
10835
- if (steamid && record3) {
10836
- summaryMap.set(steamid, record3);
10737
+ if (steamid && record2) {
10738
+ summaryMap.set(steamid, record2);
10837
10739
  }
10838
10740
  }
10839
10741
  }
@@ -10849,10 +10751,10 @@ function createSteamBaseRuntime(spec2, config) {
10849
10751
  apiKey
10850
10752
  );
10851
10753
  for (const player of getNestedArray(result.data, "players")) {
10852
- const record3 = normalizePlayerBan({ players: [player] });
10754
+ const record2 = normalizePlayerBan({ players: [player] });
10853
10755
  const steamid = getStringValue(asRecord(player)?.SteamId);
10854
- if (steamid && record3) {
10855
- banMap.set(steamid, record3);
10756
+ if (steamid && record2) {
10757
+ banMap.set(steamid, record2);
10856
10758
  }
10857
10759
  }
10858
10760
  }
@@ -10957,11 +10859,186 @@ function createSteamBaseRuntime(spec2, config) {
10957
10859
  };
10958
10860
  return appCatalogCache;
10959
10861
  }
10862
+ async function fetchStoreSearchCandidates(query, locale, limit) {
10863
+ const normalizedQuery = query.trim();
10864
+ if (!normalizedQuery) {
10865
+ return [];
10866
+ }
10867
+ const cacheKey = `${locale}:${normalizedQuery.toLowerCase()}`;
10868
+ const cached = storeSearchCache.get(cacheKey);
10869
+ const now = Date.now();
10870
+ if (cached && cached.expiresAt > now) {
10871
+ return cached.results.slice(0, limit);
10872
+ }
10873
+ const results = [];
10874
+ const seenAppIds = /* @__PURE__ */ new Set();
10875
+ const suggestUrl = new URL("https://store.steampowered.com/search/suggest");
10876
+ suggestUrl.searchParams.set("term", normalizedQuery);
10877
+ suggestUrl.searchParams.set("f", "games");
10878
+ suggestUrl.searchParams.set("cc", "CN");
10879
+ suggestUrl.searchParams.set("l", locale);
10880
+ suggestUrl.searchParams.set("realm", "1");
10881
+ const suggestText = await fetchTextResponse(suggestUrl, 3500);
10882
+ const suggest$ = load(`<div>${suggestText}</div>`);
10883
+ suggest$("a").each((index, element) => {
10884
+ const anchor = suggest$(element);
10885
+ const href = anchor.attr("href") ?? "";
10886
+ const hrefMatch = href.match(/\/app\/(\d+)/);
10887
+ const appid = normalizeScalarInput(anchor.attr("data-ds-appid")) ?? normalizeScalarInput(anchor.attr("data-appid")) ?? normalizeScalarInput(hrefMatch?.[1]);
10888
+ const localizedName = anchor.find(".match_name").first().text().trim() || anchor.find(".match_title").first().text().trim() || anchor.text().replace(/\s+/g, " ").trim();
10889
+ if (!appid || !localizedName || seenAppIds.has(appid)) {
10890
+ return;
10891
+ }
10892
+ seenAppIds.add(appid);
10893
+ results.push({
10894
+ appid,
10895
+ localizedName,
10896
+ displayName: localizedName,
10897
+ score: 2e3 - index,
10898
+ matchType: "localizedQuery",
10899
+ source: "storeSearch"
10900
+ });
10901
+ });
10902
+ if (results.length === 0) {
10903
+ const searchUrl = new URL("https://store.steampowered.com/search/");
10904
+ searchUrl.searchParams.set("term", normalizedQuery);
10905
+ searchUrl.searchParams.set("ignore_preferences", "1");
10906
+ searchUrl.searchParams.set("ndl", "1");
10907
+ searchUrl.searchParams.set("cc", "CN");
10908
+ searchUrl.searchParams.set("l", locale);
10909
+ const searchText = await fetchTextResponse(searchUrl, 3500);
10910
+ const search$ = load(searchText);
10911
+ search$("a.search_result_row").each((index, element) => {
10912
+ const anchor = search$(element);
10913
+ const href = anchor.attr("href") ?? "";
10914
+ const hrefMatch = href.match(/\/app\/(\d+)/);
10915
+ const appid = normalizeScalarInput(anchor.attr("data-ds-appid")) ?? normalizeScalarInput(anchor.attr("data-appid")) ?? normalizeScalarInput(hrefMatch?.[1]);
10916
+ const title = anchor.find(".title").first().text().trim();
10917
+ if (!appid || !title || seenAppIds.has(appid)) {
10918
+ return;
10919
+ }
10920
+ seenAppIds.add(appid);
10921
+ results.push({
10922
+ appid,
10923
+ name: title,
10924
+ displayName: title,
10925
+ score: 1800 - index,
10926
+ matchType: "storeSearchPage",
10927
+ source: "storeSearch"
10928
+ });
10929
+ });
10930
+ }
10931
+ storeSearchCache.set(cacheKey, {
10932
+ expiresAt: now + 60 * 60 * 1e3,
10933
+ results
10934
+ });
10935
+ return results.slice(0, limit);
10936
+ }
10937
+ async function fetchLocalizedStoreAppName(appid, locale) {
10938
+ const cacheKey = `${appid}:${locale}`;
10939
+ const cached = localizedAppNameCache.get(cacheKey);
10940
+ const now = Date.now();
10941
+ if (cached && cached.expiresAt > now) {
10942
+ return cached.value;
10943
+ }
10944
+ const url = new URL("https://store.steampowered.com/api/appdetails");
10945
+ url.searchParams.set("appids", appid);
10946
+ url.searchParams.set("l", locale);
10947
+ url.searchParams.set("cc", "cn");
10948
+ const payload = await fetchJsonResponse(url, 3500);
10949
+ const entry = asRecord(getNestedValue(payload, appid));
10950
+ const data = asRecord(entry?.data);
10951
+ const localizedName = getStringValue(data?.name);
10952
+ const value = {
10953
+ localizedName,
10954
+ source: localizedName ? "storeAppDetails" : "fallback",
10955
+ locale
10956
+ };
10957
+ localizedAppNameCache.set(cacheKey, {
10958
+ expiresAt: now + 24 * 60 * 60 * 1e3,
10959
+ value
10960
+ });
10961
+ return value;
10962
+ }
10963
+ function mergeResolutionCandidates(catalogCandidates, localizedCandidates, catalogByAppId) {
10964
+ const merged = /* @__PURE__ */ new Map();
10965
+ for (const candidate of localizedCandidates) {
10966
+ merged.set(candidate.appid, candidate);
10967
+ }
10968
+ for (const candidate of catalogCandidates) {
10969
+ const appid = normalizeScalarInput(candidate.appid);
10970
+ if (!appid) {
10971
+ continue;
10972
+ }
10973
+ const existing = merged.get(appid);
10974
+ const catalogEntry = catalogByAppId.get(appid);
10975
+ const name = getStringValue(candidate.name) ?? catalogEntry?.name;
10976
+ const type = getStringValue(candidate.type) ?? catalogEntry?.type;
10977
+ const score = Number(candidate.score) || 0;
10978
+ const matchType = getStringValue(candidate.matchType) ?? "catalog";
10979
+ if (existing) {
10980
+ merged.set(appid, {
10981
+ ...existing,
10982
+ name: existing.name ?? name,
10983
+ type: existing.type ?? type,
10984
+ displayName: existing.displayName || existing.localizedName || name || appid,
10985
+ score: Math.max(existing.score, score)
10986
+ });
10987
+ continue;
10988
+ }
10989
+ merged.set(appid, {
10990
+ appid,
10991
+ name,
10992
+ type,
10993
+ displayName: name ?? appid,
10994
+ score,
10995
+ matchType,
10996
+ source: "catalog"
10997
+ });
10998
+ }
10999
+ return [...merged.values()].sort((left, right) => {
11000
+ if (right.score !== left.score) {
11001
+ return right.score - left.score;
11002
+ }
11003
+ return left.displayName.localeCompare(right.displayName);
11004
+ });
11005
+ }
11006
+ async function buildAppTarget(input) {
11007
+ const locale = normalizeLocale(input.locale);
11008
+ const name = input.app?.name ?? input.candidate?.name;
11009
+ let localizedName = input.candidate?.localizedName;
11010
+ let localizationSource = localizedName ? "storeSearch" : name ? "catalog" : "fallback";
11011
+ if (!localizedName && input.preferLocalizedName && locale) {
11012
+ try {
11013
+ const localized = await fetchLocalizedStoreAppName(input.appid, locale);
11014
+ if (localized.localizedName) {
11015
+ localizedName = localized.localizedName;
11016
+ localizationSource = localized.source;
11017
+ }
11018
+ } catch (error) {
11019
+ input.warnings.push(
11020
+ `Localized app name lookup failed for appid ${input.appid}: ${getErrorMessage(error)}`
11021
+ );
11022
+ }
11023
+ }
11024
+ return {
11025
+ appid: input.appid,
11026
+ query: input.query,
11027
+ name,
11028
+ localizedName,
11029
+ displayName: localizedName ?? name ?? input.appid,
11030
+ type: input.app?.type ?? input.candidate?.type,
11031
+ resolvedFrom: input.resolvedFrom,
11032
+ localizationSource,
11033
+ locale
11034
+ };
11035
+ }
10960
11036
  async function resolveAppSelection(input) {
10961
11037
  const explicitAppId = normalizeScalarInput(input.appid);
10962
11038
  const queryValue = normalizeScalarInput(input.query);
10963
11039
  const warnings = [];
10964
11040
  const catalogApiKey = input.key ?? config.apiKey;
11041
+ const locale = normalizeLocale(input.locale) ?? (queryValue && containsNonAscii(queryValue) ? "schinese" : void 0);
10965
11042
  const directAppId = explicitAppId ?? (queryValue && /^\d+$/.test(queryValue) ? queryValue : void 0);
10966
11043
  if (directAppId) {
10967
11044
  let app = null;
@@ -10973,21 +11050,35 @@ function createSteamBaseRuntime(spec2, config) {
10973
11050
  warnings.push(`App catalog lookup failed: ${getErrorMessage(error)}`);
10974
11051
  }
10975
11052
  }
11053
+ const candidates2 = app ? [
11054
+ {
11055
+ appid: app.appid,
11056
+ name: app.name,
11057
+ displayName: app.name,
11058
+ type: app.type,
11059
+ score: 1e3,
11060
+ matchType: "appid",
11061
+ source: "catalog"
11062
+ }
11063
+ ] : [];
11064
+ const target2 = await buildAppTarget({
11065
+ appid: directAppId,
11066
+ app,
11067
+ query: queryValue,
11068
+ resolvedFrom: "appid",
11069
+ locale,
11070
+ preferLocalizedName: input.preferLocalizedName ?? false,
11071
+ warnings,
11072
+ candidate: candidates2[0] ?? null
11073
+ });
10976
11074
  return {
10977
11075
  appid: directAppId,
10978
11076
  app,
10979
11077
  query: queryValue,
10980
11078
  resolvedFrom: "appid",
10981
- candidates: app ? [
10982
- {
10983
- appid: app.appid,
10984
- name: app.name,
10985
- type: app.type,
10986
- score: 1e3,
10987
- matchType: "appid"
10988
- }
10989
- ] : [],
10990
- warnings
11079
+ candidates: candidates2,
11080
+ warnings,
11081
+ target: target2
10991
11082
  };
10992
11083
  }
10993
11084
  if (!queryValue) {
@@ -11001,11 +11092,30 @@ function createSteamBaseRuntime(spec2, config) {
11001
11092
  includeVideos: input.includeVideos ?? false,
11002
11093
  includeHardware: input.includeHardware ?? false
11003
11094
  });
11004
- const candidates = searchAppCatalog(filteredEntries, queryValue, {
11095
+ const catalogCandidates = searchAppCatalog(filteredEntries, queryValue, {
11005
11096
  limit: input.limit,
11006
11097
  exactOnly: false
11007
11098
  });
11008
- const bestCandidate = asRecord(candidates[0]);
11099
+ let localizedCandidates = [];
11100
+ if (containsNonAscii(queryValue) || catalogCandidates.length === 0) {
11101
+ try {
11102
+ localizedCandidates = await fetchStoreSearchCandidates(
11103
+ queryValue,
11104
+ locale ?? "schinese",
11105
+ input.limit
11106
+ );
11107
+ } catch (error) {
11108
+ warnings.push(
11109
+ `Localized app search failed for '${queryValue}': ${getErrorMessage(error)}`
11110
+ );
11111
+ }
11112
+ }
11113
+ const candidates = mergeResolutionCandidates(
11114
+ catalogCandidates,
11115
+ localizedCandidates,
11116
+ catalog.byAppId
11117
+ ).slice(0, input.limit);
11118
+ const bestCandidate = candidates[0];
11009
11119
  if (!bestCandidate) {
11010
11120
  throw new Error(`No Steam app matched query '${queryValue}'.`);
11011
11121
  }
@@ -11013,13 +11123,24 @@ function createSteamBaseRuntime(spec2, config) {
11013
11123
  if (!bestAppId) {
11014
11124
  throw new Error(`No Steam app matched query '${queryValue}'.`);
11015
11125
  }
11126
+ const target = await buildAppTarget({
11127
+ appid: bestAppId,
11128
+ app: catalog.byAppId.get(bestAppId) ?? null,
11129
+ query: queryValue,
11130
+ resolvedFrom: "query",
11131
+ locale,
11132
+ preferLocalizedName: input.preferLocalizedName ?? false,
11133
+ warnings,
11134
+ candidate: bestCandidate
11135
+ });
11016
11136
  return {
11017
11137
  appid: bestAppId,
11018
11138
  app: catalog.byAppId.get(bestAppId) ?? null,
11019
11139
  query: queryValue,
11020
11140
  resolvedFrom: "query",
11021
11141
  candidates,
11022
- warnings
11142
+ warnings,
11143
+ target
11023
11144
  };
11024
11145
  }
11025
11146
  function applyDefaultSteamIdentityArgs(method, args) {
@@ -11094,6 +11215,8 @@ function createSteamBaseRuntime(spec2, config) {
11094
11215
  fetchTopOwnedAppIds,
11095
11216
  fetchOwnedGamesContext,
11096
11217
  fetchAppCatalog,
11218
+ fetchLocalizedStoreAppName,
11219
+ fetchStoreSearchCandidates,
11097
11220
  resolveAppSelection,
11098
11221
  resolveMethod,
11099
11222
  mapRawParams,
@@ -11107,6 +11230,36 @@ function formatSteamCallError(response) {
11107
11230
  function getErrorMessage(error) {
11108
11231
  return error instanceof Error ? error.message : String(error);
11109
11232
  }
11233
+ function containsNonAscii(value) {
11234
+ return /[^\u0000-\u007f]/.test(value);
11235
+ }
11236
+ function normalizeLocale(value) {
11237
+ const trimmed = value?.trim();
11238
+ return trimmed ? trimmed.toLowerCase() : void 0;
11239
+ }
11240
+ async function fetchTextResponse(url, timeoutMs) {
11241
+ const controller = new AbortController();
11242
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
11243
+ try {
11244
+ const response = await fetch(url, {
11245
+ signal: controller.signal,
11246
+ headers: {
11247
+ "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
11248
+ "user-agent": "steam-tools-mcp/0.2.0"
11249
+ }
11250
+ });
11251
+ if (!response.ok) {
11252
+ throw new Error(`HTTP ${response.status} ${response.statusText}`);
11253
+ }
11254
+ return await response.text();
11255
+ } finally {
11256
+ clearTimeout(timeout);
11257
+ }
11258
+ }
11259
+ async function fetchJsonResponse(url, timeoutMs) {
11260
+ const text = await fetchTextResponse(url, timeoutMs);
11261
+ return JSON.parse(text);
11262
+ }
11110
11263
 
11111
11264
  // src/tools/apps.ts
11112
11265
  import * as z2 from "zod/v4";
@@ -11359,18 +11512,32 @@ async function buildPlayerProfileOverviewData(runtime2, input) {
11359
11512
  const badges = normalizeBadgeSummary(badgesCall.data);
11360
11513
  const recentGames = normalizeRecentlyPlayedGames(recentGamesCall?.data);
11361
11514
  const level = toNumber(getNestedValue(steamLevelCall.data, "response", "player_level")) ?? toNumber(getNestedValue(steamLevelCall.data, "player_level"));
11515
+ const recentItems = input.includeRecentlyPlayed && Array.isArray(recentGames?.games) ? recentGames?.games : [];
11362
11516
  return {
11363
11517
  ok: true,
11364
11518
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
11365
- player: {
11366
- steamid: identity.steamid,
11367
- resolvedFrom: identity.resolvedFrom,
11368
- vanityUrl: identity.vanityUrl,
11369
- summary: playerSummary,
11519
+ target: {
11520
+ player: {
11521
+ steamid: identity.steamid,
11522
+ resolvedFrom: identity.resolvedFrom,
11523
+ vanityUrl: identity.vanityUrl
11524
+ }
11525
+ },
11526
+ summary: {
11370
11527
  level,
11528
+ badgeCount: toNumber(badges?.badgeCount) ?? 0,
11529
+ recentlyPlayedCount: recentItems.length,
11530
+ hasVacBan: toBoolean(banSummary?.vacBanned) ?? false,
11531
+ hasGameBan: (toNumber(banSummary?.numberOfGameBans) ?? 0) > 0
11532
+ },
11533
+ sections: {
11534
+ profile: playerSummary,
11371
11535
  bans: banSummary,
11372
11536
  badges,
11373
- recentlyPlayed: input.includeRecentlyPlayed ? recentGames : void 0
11537
+ recentlyPlayed: input.includeRecentlyPlayed ? {
11538
+ totalCount: toNumber(recentGames?.totalCount) ?? recentItems.length,
11539
+ items: recentItems
11540
+ } : void 0
11374
11541
  },
11375
11542
  warnings: runtime2.collectWarnings([
11376
11543
  playerSummaryCall,
@@ -11422,23 +11589,32 @@ async function buildLibraryOverviewData(runtime2, input) {
11422
11589
  (sum, game) => sum + (toNumber(game.playtimeForeverMinutes) ?? 0),
11423
11590
  0
11424
11591
  );
11592
+ const recentItems = input.includeRecentGames && Array.isArray(recentGames?.games) ? recentGames.games : [];
11425
11593
  return {
11426
11594
  ok: true,
11427
11595
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
11428
- player: {
11429
- steamid: identity.steamid,
11430
- resolvedFrom: identity.resolvedFrom,
11431
- vanityUrl: identity.vanityUrl,
11432
- summary: playerSummary
11596
+ target: {
11597
+ player: {
11598
+ steamid: identity.steamid,
11599
+ resolvedFrom: identity.resolvedFrom,
11600
+ vanityUrl: identity.vanityUrl
11601
+ }
11433
11602
  },
11434
- library: {
11603
+ summary: {
11435
11604
  gameCount: ownedGames.gameCount,
11436
11605
  totalPlaytimeMinutes,
11437
11606
  totalPlaytimeHours: roundTo(totalPlaytimeMinutes / 60, 2),
11607
+ recentGameCount: recentItems.length,
11438
11608
  includeAppInfo: input.includeAppInfo,
11439
- includePlayedFreeGames: input.includePlayedFreeGames,
11440
- topGamesByPlaytime,
11441
- recentGames: input.includeRecentGames ? recentGames : void 0
11609
+ includePlayedFreeGames: input.includePlayedFreeGames
11610
+ },
11611
+ items: topGamesByPlaytime,
11612
+ sections: {
11613
+ profile: playerSummary,
11614
+ recentlyPlayed: input.includeRecentGames ? {
11615
+ totalCount: toNumber(recentGames?.totalCount) ?? recentItems.length,
11616
+ items: recentItems
11617
+ } : void 0
11442
11618
  },
11443
11619
  warnings: runtime2.collectWarnings([
11444
11620
  ownedGamesCall,
@@ -11447,20 +11623,29 @@ async function buildLibraryOverviewData(runtime2, input) {
11447
11623
  ])
11448
11624
  };
11449
11625
  }
11450
- async function buildAchievementOverviewData(runtime2, input) {
11626
+ async function buildPlayerGameAchievementsData(runtime2, input) {
11451
11627
  const apiKey = runtime2.requireSteamApiKey(input.toolName, input.key);
11452
11628
  const identity = await runtime2.resolvePlayerIdentity(
11453
11629
  { steamid: input.steamid, vanityUrl: input.vanityUrl },
11454
11630
  apiKey
11455
11631
  );
11456
- const appIdValue = normalizeScalarInput(input.appid);
11632
+ const resolved = await runtime2.resolveAppSelection({
11633
+ toolName: input.toolName,
11634
+ appid: input.appid,
11635
+ query: input.query,
11636
+ key: apiKey,
11637
+ limit: 5,
11638
+ forceRefresh: false,
11639
+ locale: input.language,
11640
+ preferLocalizedName: Boolean(input.query) || Boolean(input.language)
11641
+ });
11457
11642
  const requests = [
11458
11643
  runtime2.safeInvokeKnownMethod(
11459
11644
  "ISteamUserStats",
11460
11645
  "GetPlayerAchievements",
11461
11646
  {
11462
11647
  steamid: identity.steamid,
11463
- appid: appIdValue,
11648
+ appid: resolved.appid,
11464
11649
  l: input.language
11465
11650
  },
11466
11651
  apiKey
@@ -11469,7 +11654,7 @@ async function buildAchievementOverviewData(runtime2, input) {
11469
11654
  "ISteamUserStats",
11470
11655
  "GetSchemaForGame",
11471
11656
  {
11472
- appid: appIdValue,
11657
+ appid: resolved.appid,
11473
11658
  l: input.language
11474
11659
  },
11475
11660
  apiKey
@@ -11479,7 +11664,7 @@ async function buildAchievementOverviewData(runtime2, input) {
11479
11664
  "GetUserStatsForGame",
11480
11665
  {
11481
11666
  steamid: identity.steamid,
11482
- appid: appIdValue
11667
+ appid: resolved.appid
11483
11668
  },
11484
11669
  apiKey
11485
11670
  ) : Promise.resolve(null),
@@ -11487,7 +11672,7 @@ async function buildAchievementOverviewData(runtime2, input) {
11487
11672
  "ISteamUserStats",
11488
11673
  "GetGlobalAchievementPercentagesForApp",
11489
11674
  {
11490
- gameid: appIdValue
11675
+ gameid: resolved.appid
11491
11676
  },
11492
11677
  void 0
11493
11678
  )
@@ -11512,47 +11697,70 @@ async function buildAchievementOverviewData(runtime2, input) {
11512
11697
  ).slice(0, input.recentUnlockedCount);
11513
11698
  const totalAchievements = mergedAchievements.length || normalizeAchievementSchema(schemaCall.data).length;
11514
11699
  const unlockedCount = unlockedAchievements.length;
11700
+ const schemaGameName = getStringValue(getNestedValue(schemaCall.data, "game", "gameName"));
11701
+ const gameName = getStringValue(
11702
+ getNestedValue(playerAchievementsCall.data, "playerstats", "gameName")
11703
+ ) ?? schemaGameName ?? getStringValue(getNestedValue(playerStatsCall?.data, "playerstats", "gameName"));
11704
+ const appTarget = schemaGameName && schemaGameName !== resolved.target.displayName ? {
11705
+ ...resolved.target,
11706
+ localizedName: schemaGameName,
11707
+ displayName: schemaGameName,
11708
+ localizationSource: "schema",
11709
+ locale: input.language?.trim().toLowerCase() ?? resolved.target.locale
11710
+ } : {
11711
+ ...resolved.target,
11712
+ name: resolved.target.name ?? gameName,
11713
+ displayName: resolved.target.displayName ?? gameName ?? resolved.target.appid
11714
+ };
11515
11715
  return {
11516
11716
  ok: true,
11517
11717
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
11518
- player: {
11519
- steamid: identity.steamid,
11520
- resolvedFrom: identity.resolvedFrom,
11521
- vanityUrl: identity.vanityUrl
11522
- },
11523
- game: {
11524
- appid: appIdValue,
11525
- gameName: getStringValue(
11526
- getNestedValue(playerAchievementsCall.data, "playerstats", "gameName")
11527
- ) ?? getStringValue(getNestedValue(schemaCall.data, "game", "gameName")) ?? getStringValue(
11528
- getNestedValue(playerStatsCall?.data, "playerstats", "gameName")
11529
- )
11718
+ target: {
11719
+ player: {
11720
+ steamid: identity.steamid,
11721
+ resolvedFrom: identity.resolvedFrom,
11722
+ vanityUrl: identity.vanityUrl
11723
+ },
11724
+ app: appTarget
11530
11725
  },
11531
- achievementSummary: {
11726
+ summary: {
11532
11727
  totalAchievements,
11533
11728
  unlockedCount,
11534
11729
  lockedCount: Math.max(totalAchievements - unlockedCount, 0),
11535
11730
  completionRate: totalAchievements > 0 ? roundTo(unlockedCount / totalAchievements * 100, 2) : 0,
11536
- recentlyUnlocked
11731
+ recentlyUnlockedCount: recentlyUnlocked.length
11537
11732
  },
11538
- achievements: mergedAchievements,
11539
- stats: input.includeStats ? playerStats : void 0,
11540
- warnings: runtime2.collectWarnings([
11541
- playerAchievementsCall,
11542
- schemaCall,
11543
- playerStatsCall,
11544
- globalPercentagesCall
11545
- ])
11733
+ items: mergedAchievements,
11734
+ sections: {
11735
+ recentlyUnlocked,
11736
+ stats: input.includeStats ? playerStats : void 0
11737
+ },
11738
+ warnings: [
11739
+ ...resolved.warnings,
11740
+ ...runtime2.collectWarnings([
11741
+ playerAchievementsCall,
11742
+ schemaCall,
11743
+ playerStatsCall,
11744
+ globalPercentagesCall
11745
+ ])
11746
+ ]
11546
11747
  };
11547
11748
  }
11548
- async function buildNewsOverviewData(runtime2, input) {
11549
- const appIdValue = normalizeScalarInput(input.appid);
11749
+ async function buildAppNewsData(runtime2, input) {
11750
+ const resolved = await runtime2.resolveAppSelection({
11751
+ toolName: input.toolName,
11752
+ appid: input.appid,
11753
+ query: input.query,
11754
+ limit: 5,
11755
+ forceRefresh: false,
11756
+ preferLocalizedName: Boolean(input.query)
11757
+ });
11550
11758
  const feedValue = Array.isArray(input.feeds) ? input.feeds.join(",") : input.feeds;
11551
11759
  const newsCall = await runtime2.safeInvokeKnownMethod(
11552
11760
  "ISteamNews",
11553
11761
  "GetNewsForApp",
11554
11762
  {
11555
- appid: appIdValue,
11763
+ appid: resolved.appid,
11556
11764
  count: input.count,
11557
11765
  maxlength: input.maxlength,
11558
11766
  enddate: input.enddate,
@@ -11563,7 +11771,7 @@ async function buildNewsOverviewData(runtime2, input) {
11563
11771
  const playerCountCall = input.includePlayerCount ? await runtime2.safeInvokeKnownMethod(
11564
11772
  "ISteamUserStats",
11565
11773
  "GetNumberOfCurrentPlayers",
11566
- { appid: appIdValue },
11774
+ { appid: resolved.appid },
11567
11775
  void 0
11568
11776
  ) : null;
11569
11777
  const newsItems = normalizeNewsItems(newsCall.data);
@@ -11571,16 +11779,19 @@ async function buildNewsOverviewData(runtime2, input) {
11571
11779
  return {
11572
11780
  ok: true,
11573
11781
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
11574
- game: {
11575
- appid: appIdValue,
11576
- currentPlayers
11782
+ target: {
11783
+ app: resolved.target
11577
11784
  },
11578
- news: {
11785
+ summary: {
11579
11786
  requestedCount: input.count,
11580
11787
  returnedCount: newsItems.length,
11581
- items: newsItems
11788
+ currentPlayers
11582
11789
  },
11583
- warnings: runtime2.collectWarnings([newsCall, playerCountCall])
11790
+ items: newsItems,
11791
+ warnings: [
11792
+ ...resolved.warnings,
11793
+ ...runtime2.collectWarnings([newsCall, playerCountCall])
11794
+ ]
11584
11795
  };
11585
11796
  }
11586
11797
 
@@ -11594,27 +11805,44 @@ async function buildAppSearchData(runtime2, input) {
11594
11805
  includeVideos: input.includeVideos,
11595
11806
  includeHardware: input.includeHardware
11596
11807
  });
11597
- const results = searchAppCatalog(filteredEntries, input.query, {
11808
+ const catalogResults = searchAppCatalog(filteredEntries, input.query, {
11598
11809
  limit: input.limit,
11599
11810
  exactOnly: input.exactOnly
11600
11811
  });
11812
+ let localizedResults = [];
11813
+ if (/[^\u0000-\u007f]/.test(input.query) || catalogResults.length === 0) {
11814
+ try {
11815
+ localizedResults = await runtime2.fetchStoreSearchCandidates(
11816
+ input.query,
11817
+ "schinese",
11818
+ input.limit
11819
+ );
11820
+ } catch {
11821
+ localizedResults = [];
11822
+ }
11823
+ }
11824
+ const items = [...localizedResults, ...catalogResults].slice(0, input.limit);
11601
11825
  return {
11602
11826
  ok: true,
11603
11827
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
11604
- query: input.query,
11605
- resultCount: results.length,
11606
- catalog: {
11607
- totalEntries: catalog.entries.length,
11828
+ target: {
11829
+ query: input.query
11830
+ },
11831
+ summary: {
11832
+ resultCount: items.length,
11833
+ totalCatalogEntries: catalog.entries.length,
11608
11834
  searchableEntries: filteredEntries.length,
11609
11835
  cacheExpiresAt: new Date(catalog.expiresAt).toISOString(),
11610
11836
  filters: {
11611
11837
  includeDlc: input.includeDlc,
11612
11838
  includeSoftware: input.includeSoftware,
11613
11839
  includeVideos: input.includeVideos,
11614
- includeHardware: input.includeHardware
11840
+ includeHardware: input.includeHardware,
11841
+ exactOnly: input.exactOnly
11615
11842
  }
11616
11843
  },
11617
- results
11844
+ items,
11845
+ warnings: []
11618
11846
  };
11619
11847
  }
11620
11848
  async function buildAppResolveData(runtime2, input) {
@@ -11628,19 +11856,20 @@ async function buildAppResolveData(runtime2, input) {
11628
11856
  includeDlc: input.includeDlc,
11629
11857
  includeSoftware: input.includeSoftware,
11630
11858
  includeVideos: input.includeVideos,
11631
- includeHardware: input.includeHardware
11859
+ includeHardware: input.includeHardware,
11860
+ preferLocalizedName: true
11632
11861
  });
11633
11862
  return {
11634
11863
  ok: true,
11635
11864
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
11636
- query: normalizeScalarInput(input.query),
11637
- resolved: {
11638
- appid: resolved.appid,
11639
- name: resolved.app?.name,
11640
- type: resolved.app?.type,
11641
- resolvedFrom: resolved.resolvedFrom
11865
+ target: {
11866
+ query: normalizeScalarInput(input.query),
11867
+ app: resolved.target
11642
11868
  },
11643
- candidates: resolved.candidates,
11869
+ summary: {
11870
+ candidateCount: resolved.candidates.length
11871
+ },
11872
+ items: resolved.candidates,
11644
11873
  warnings: resolved.warnings
11645
11874
  };
11646
11875
  } catch (error) {
@@ -11651,9 +11880,14 @@ async function buildAppResolveData(runtime2, input) {
11651
11880
  return {
11652
11881
  ok: false,
11653
11882
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
11654
- query: normalizeScalarInput(input.query),
11883
+ target: {
11884
+ query: normalizeScalarInput(input.query)
11885
+ },
11886
+ summary: {
11887
+ candidateCount: 0
11888
+ },
11889
+ items: [],
11655
11890
  note: message,
11656
- candidates: [],
11657
11891
  warnings: []
11658
11892
  };
11659
11893
  }
@@ -11665,7 +11899,9 @@ async function buildGlobalAchievementOverviewData(runtime2, input) {
11665
11899
  query: input.query,
11666
11900
  key: input.key,
11667
11901
  limit: 5,
11668
- forceRefresh: false
11902
+ forceRefresh: false,
11903
+ locale: input.language,
11904
+ preferLocalizedName: Boolean(input.query) || Boolean(input.language)
11669
11905
  });
11670
11906
  const apiKey = input.key ?? runtime2.config.apiKey;
11671
11907
  const warnings = [...resolved.warnings];
@@ -11707,25 +11943,31 @@ async function buildGlobalAchievementOverviewData(runtime2, input) {
11707
11943
  ) / achievementsWithPercent.length,
11708
11944
  2
11709
11945
  ) : null;
11946
+ const schemaGameName = asRecord(getNestedValue(schemaCall?.data, "game"));
11947
+ const appTarget = schemaGameName && normalizeScalarInput(schemaGameName.gameName) ? {
11948
+ ...resolved.target,
11949
+ localizedName: normalizeScalarInput(schemaGameName.gameName) ?? resolved.target.localizedName,
11950
+ displayName: normalizeScalarInput(schemaGameName.gameName) ?? resolved.target.displayName,
11951
+ localizationSource: "schema",
11952
+ locale: input.language?.trim().toLowerCase() ?? resolved.target.locale
11953
+ } : resolved.target;
11710
11954
  return {
11711
11955
  ok: globalCall.ok,
11712
11956
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
11713
- app: {
11714
- appid: resolved.appid,
11715
- name: resolved.app?.name,
11716
- type: resolved.app?.type,
11717
- resolvedFrom: resolved.resolvedFrom,
11718
- query: resolved.query
11957
+ target: {
11958
+ app: appTarget
11719
11959
  },
11720
- achievementSummary: {
11960
+ summary: {
11721
11961
  totalAchievements: achievements.length,
11722
11962
  achievementsWithGlobalPercent: achievementsWithPercent.length,
11723
11963
  schemaEnriched: Boolean(schemaCall?.ok),
11724
11964
  averageUnlockRate
11725
11965
  },
11726
- rarestAchievements: achievementsWithPercent.slice(0, input.topCount),
11727
- mostCommonAchievements: [...achievementsWithPercent].reverse().slice(0, input.topCount),
11728
- achievements,
11966
+ items: achievements,
11967
+ sections: {
11968
+ rarest: achievementsWithPercent.slice(0, input.topCount),
11969
+ mostCommon: [...achievementsWithPercent].reverse().slice(0, input.topCount)
11970
+ },
11729
11971
  warnings: [...warnings, ...runtime2.collectWarnings([globalCall, schemaCall])]
11730
11972
  };
11731
11973
  }
@@ -11736,10 +11978,12 @@ async function buildAppSnapshotData(runtime2, input) {
11736
11978
  query: input.query,
11737
11979
  key: input.key,
11738
11980
  limit: 5,
11739
- forceRefresh: false
11981
+ forceRefresh: false,
11982
+ locale: input.language,
11983
+ preferLocalizedName: Boolean(input.query) || Boolean(input.language)
11740
11984
  });
11741
11985
  const warnings = [...resolved.warnings];
11742
- const newsOverview = input.includeNews || input.includePlayerCount ? await buildNewsOverviewData(runtime2, {
11986
+ const newsOverview = input.includeNews || input.includePlayerCount ? await buildAppNewsData(runtime2, {
11743
11987
  toolName: input.toolName,
11744
11988
  appid: resolved.appid,
11745
11989
  count: input.newsCount,
@@ -11754,31 +11998,37 @@ async function buildAppSnapshotData(runtime2, input) {
11754
11998
  includeSchemaDetails: input.includeSchemaDetails,
11755
11999
  topCount: input.topGlobalAchievementsCount
11756
12000
  }) : null;
12001
+ const globalTarget = asRecord(getNestedValue(globalAchievementOverview, "target", "app"));
11757
12002
  return {
11758
12003
  ok: true,
11759
12004
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
11760
- app: {
11761
- appid: resolved.appid,
11762
- name: resolved.app?.name,
11763
- type: resolved.app?.type,
11764
- resolvedFrom: resolved.resolvedFrom,
11765
- query: resolved.query
11766
- },
11767
- live: input.includePlayerCount ? {
11768
- currentPlayers: getNestedValue(newsOverview, "game", "currentPlayers")
11769
- } : void 0,
11770
- news: input.includeNews ? getNestedValue(newsOverview, "news") : void 0,
11771
- globalAchievements: input.includeGlobalAchievements ? {
11772
- summary: getNestedValue(globalAchievementOverview, "achievementSummary"),
11773
- rarestAchievements: getNestedValue(
11774
- globalAchievementOverview,
11775
- "rarestAchievements"
11776
- ),
11777
- mostCommonAchievements: getNestedValue(
11778
- globalAchievementOverview,
11779
- "mostCommonAchievements"
11780
- )
11781
- } : void 0,
12005
+ target: {
12006
+ app: globalTarget ?? resolved.target
12007
+ },
12008
+ summary: {
12009
+ hasNews: input.includeNews,
12010
+ hasPlayerCount: input.includePlayerCount,
12011
+ hasGlobalAchievements: input.includeGlobalAchievements
12012
+ },
12013
+ sections: {
12014
+ live: input.includePlayerCount ? {
12015
+ currentPlayers: getNestedValue(newsOverview, "summary", "currentPlayers")
12016
+ } : void 0,
12017
+ news: input.includeNews ? {
12018
+ summary: getNestedValue(newsOverview, "summary"),
12019
+ items: getNestedValue(newsOverview, "items")
12020
+ } : void 0,
12021
+ globalAchievements: input.includeGlobalAchievements ? {
12022
+ summary: getNestedValue(globalAchievementOverview, "summary"),
12023
+ items: getNestedValue(globalAchievementOverview, "items"),
12024
+ rarest: getNestedValue(globalAchievementOverview, "sections", "rarest"),
12025
+ mostCommon: getNestedValue(
12026
+ globalAchievementOverview,
12027
+ "sections",
12028
+ "mostCommon"
12029
+ )
12030
+ } : void 0
12031
+ },
11782
12032
  warnings: [
11783
12033
  ...warnings,
11784
12034
  ...Array.isArray(getNestedValue(newsOverview, "warnings")) ? getNestedValue(newsOverview, "warnings") : [],
@@ -11787,23 +12037,48 @@ async function buildAppSnapshotData(runtime2, input) {
11787
12037
  };
11788
12038
  }
11789
12039
 
12040
+ // src/tools/register-steam-tool.ts
12041
+ function isErrorPayload(payload) {
12042
+ return payload.ok === false;
12043
+ }
12044
+ function withSteamToolAspect(toolName, runtime2, handler) {
12045
+ return async (input) => {
12046
+ try {
12047
+ const payload = await handler(input);
12048
+ return runtime2.createJsonToolResult(payload, isErrorPayload(payload));
12049
+ } catch (error) {
12050
+ return runtime2.createErrorToolResult(toolName, error);
12051
+ }
12052
+ };
12053
+ }
12054
+ function registerSteamTool(server, runtime2, toolName, definition, handler) {
12055
+ server.registerTool(
12056
+ toolName,
12057
+ definition,
12058
+ // 工具模块只通过这一个入口注册,后续新增工具会默认继承同样的包装行为。
12059
+ async (input) => withSteamToolAspect(toolName, runtime2, handler)(input)
12060
+ );
12061
+ }
12062
+
11790
12063
  // src/tools/apps.ts
11791
12064
  function registerAppTools(server, runtime2) {
11792
- server.registerTool(
12065
+ registerSteamTool(
12066
+ server,
12067
+ runtime2,
11793
12068
  "steam_search_apps",
11794
12069
  {
11795
- title: "Steam \u5E94\u7528\u641C\u7D22",
11796
- description: "\u6309\u540D\u79F0\u641C\u7D22 Steam \u5E94\u7528\u76EE\u5F55\u5E76\u8FD4\u56DE\u5019\u9009 AppID\u3002",
12070
+ title: "\u641C\u7D22\u6E38\u620F",
12071
+ description: "\u6309\u540D\u79F0\u641C\u7D22 Steam \u5E94\u7528\u76EE\u5F55\uFF0C\u9002\u5408\u62FF\u5019\u9009\u5217\u8868\u3002",
11797
12072
  inputSchema: {
11798
- query: z2.string().describe("\u8981\u641C\u7D22\u7684\u5E94\u7528\u540D\u79F0\u6216\u540D\u79F0\u7247\u6BB5\u3002"),
11799
- key: z2.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\uFF1B\u82E5\u672A\u914D\u7F6E STEAM_WEB_API_KEY\uFF0C\u5219\u6B64\u9879\u5FC5\u586B\u3002"),
12073
+ query: z2.string().describe("\u8981\u641C\u7D22\u7684\u6E38\u620F\u540D\u6216\u5173\u952E\u8BCD\uFF0C\u652F\u6301\u4E2D\u6587\u3002"),
12074
+ key: z2.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
11800
12075
  limit: z2.number().int().min(1).max(50).optional().describe("\u6700\u591A\u8FD4\u56DE\u591A\u5C11\u4E2A\u7ED3\u679C\uFF0C\u9ED8\u8BA4 10\u3002"),
11801
- exactOnly: z2.boolean().optional().describe("\u662F\u5426\u53EA\u8FD4\u56DE\u540D\u79F0\u5B8C\u5168\u5339\u914D\u7684\u7ED3\u679C\uFF0C\u9ED8\u8BA4 false\u3002"),
11802
- includeDlc: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B DLC \u6761\u76EE\uFF0C\u9ED8\u8BA4 false\u3002"),
12076
+ exactOnly: z2.boolean().optional().describe("\u662F\u5426\u53EA\u8FD4\u56DE\u5B8C\u5168\u5339\u914D\u7684\u7ED3\u679C\uFF0C\u9ED8\u8BA4 false\u3002"),
12077
+ includeDlc: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B DLC\uFF0C\u9ED8\u8BA4 false\u3002"),
11803
12078
  includeSoftware: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B\u8F6F\u4EF6\u6761\u76EE\uFF0C\u9ED8\u8BA4 false\u3002"),
11804
- includeVideos: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B\u89C6\u9891\u548C\u5267\u96C6\u6761\u76EE\uFF0C\u9ED8\u8BA4 false\u3002"),
12079
+ includeVideos: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B\u89C6\u9891\u548C\u5267\u96C6\uFF0C\u9ED8\u8BA4 false\u3002"),
11805
12080
  includeHardware: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B\u786C\u4EF6\u6761\u76EE\uFF0C\u9ED8\u8BA4 false\u3002"),
11806
- forceRefresh: z2.boolean().optional().describe("\u662F\u5426\u8DF3\u8FC7\u5185\u5B58\u4E2D\u7684\u5E94\u7528\u76EE\u5F55\u7F13\u5B58\uFF0C\u9ED8\u8BA4 false\u3002")
12081
+ forceRefresh: z2.boolean().optional().describe("\u662F\u5426\u8DF3\u8FC7\u5E94\u7528\u76EE\u5F55\u7F13\u5B58\uFF0C\u9ED8\u8BA4 false\u3002")
11807
12082
  },
11808
12083
  annotations: {
11809
12084
  readOnlyHint: true,
@@ -11820,40 +12095,35 @@ function registerAppTools(server, runtime2) {
11820
12095
  includeVideos = false,
11821
12096
  includeHardware = false,
11822
12097
  forceRefresh = false
11823
- }) => {
11824
- try {
11825
- const payload = await buildAppSearchData(runtime2, {
11826
- toolName: "steam_search_apps",
11827
- query,
11828
- key,
11829
- limit,
11830
- exactOnly,
11831
- includeDlc,
11832
- includeSoftware,
11833
- includeVideos,
11834
- includeHardware,
11835
- forceRefresh
11836
- });
11837
- return runtime2.createJsonToolResult(payload);
11838
- } catch (error) {
11839
- return runtime2.createErrorToolResult("steam_search_apps", error);
11840
- }
11841
- }
12098
+ }) => buildAppSearchData(runtime2, {
12099
+ toolName: "steam_search_apps",
12100
+ query,
12101
+ key,
12102
+ limit,
12103
+ exactOnly,
12104
+ includeDlc,
12105
+ includeSoftware,
12106
+ includeVideos,
12107
+ includeHardware,
12108
+ forceRefresh
12109
+ })
11842
12110
  );
11843
- server.registerTool(
12111
+ registerSteamTool(
12112
+ server,
12113
+ runtime2,
11844
12114
  "steam_resolve_app",
11845
12115
  {
11846
- title: "Steam \u5E94\u7528\u89E3\u6790",
11847
- description: "\u628A\u5E94\u7528\u540D\u79F0\u6216 AppID \u89E3\u6790\u6210\u6700\u53EF\u80FD\u7684\u76EE\u6807 AppID\uFF0C\u5E76\u8FD4\u56DE\u5019\u9009\u7ED3\u679C\u3002",
12116
+ title: "\u89E3\u6790\u6E38\u620F\u76EE\u6807",
12117
+ description: "\u628A\u6E38\u620F\u540D\u6216 AppID \u89E3\u6790\u6210\u6700\u53EF\u80FD\u7684\u76EE\u6807\u6E38\u620F\uFF0C\u5E76\u8FD4\u56DE\u5019\u9009\u5217\u8868\u3002",
11848
12118
  inputSchema: {
11849
- query: z2.union([z2.string(), z2.number()]).describe("\u8981\u89E3\u6790\u7684 AppID \u6216\u5E94\u7528\u540D\u79F0\u3002"),
11850
- key: z2.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\uFF1B\u5982\u679C\u6309\u540D\u79F0\u89E3\u6790\u4E14\u672A\u914D\u7F6E STEAM_WEB_API_KEY\uFF0C\u5219\u6B64\u9879\u5FC5\u586B\u3002"),
12119
+ query: z2.union([z2.string(), z2.number()]).describe("\u6E38\u620F\u540D\u6216 AppID\uFF0C\u652F\u6301\u76F4\u63A5\u8F93\u5165\u4E2D\u6587\u540D\u3002"),
12120
+ key: z2.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
11851
12121
  limit: z2.number().int().min(1).max(20).optional().describe("\u6700\u591A\u8FD4\u56DE\u591A\u5C11\u4E2A\u5019\u9009\u7ED3\u679C\uFF0C\u9ED8\u8BA4 5\u3002"),
11852
- includeDlc: z2.boolean().optional().describe("\u6309\u540D\u79F0\u89E3\u6790\u65F6\u662F\u5426\u5305\u542B DLC \u6761\u76EE\uFF0C\u9ED8\u8BA4 false\u3002"),
11853
- includeSoftware: z2.boolean().optional().describe("\u6309\u540D\u79F0\u89E3\u6790\u65F6\u662F\u5426\u5305\u542B\u8F6F\u4EF6\u6761\u76EE\uFF0C\u9ED8\u8BA4 false\u3002"),
11854
- includeVideos: z2.boolean().optional().describe("\u6309\u540D\u79F0\u89E3\u6790\u65F6\u662F\u5426\u5305\u542B\u89C6\u9891\u548C\u5267\u96C6\u6761\u76EE\uFF0C\u9ED8\u8BA4 false\u3002"),
11855
- includeHardware: z2.boolean().optional().describe("\u6309\u540D\u79F0\u89E3\u6790\u65F6\u662F\u5426\u5305\u542B\u786C\u4EF6\u6761\u76EE\uFF0C\u9ED8\u8BA4 false\u3002"),
11856
- forceRefresh: z2.boolean().optional().describe("\u662F\u5426\u8DF3\u8FC7\u5185\u5B58\u4E2D\u7684\u5E94\u7528\u76EE\u5F55\u7F13\u5B58\uFF0C\u9ED8\u8BA4 false\u3002")
12122
+ includeDlc: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B DLC\uFF0C\u9ED8\u8BA4 false\u3002"),
12123
+ includeSoftware: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B\u8F6F\u4EF6\u6761\u76EE\uFF0C\u9ED8\u8BA4 false\u3002"),
12124
+ includeVideos: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B\u89C6\u9891\u548C\u5267\u96C6\uFF0C\u9ED8\u8BA4 false\u3002"),
12125
+ includeHardware: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B\u786C\u4EF6\u6761\u76EE\uFF0C\u9ED8\u8BA4 false\u3002"),
12126
+ forceRefresh: z2.boolean().optional().describe("\u662F\u5426\u8DF3\u8FC7\u5E94\u7528\u76EE\u5F55\u7F13\u5B58\uFF0C\u9ED8\u8BA4 false\u3002")
11857
12127
  },
11858
12128
  annotations: {
11859
12129
  readOnlyHint: true,
@@ -11869,36 +12139,31 @@ function registerAppTools(server, runtime2) {
11869
12139
  includeVideos = false,
11870
12140
  includeHardware = false,
11871
12141
  forceRefresh = false
11872
- }) => {
11873
- try {
11874
- const payload = await buildAppResolveData(runtime2, {
11875
- toolName: "steam_resolve_app",
11876
- query,
11877
- key,
11878
- limit,
11879
- includeDlc,
11880
- includeSoftware,
11881
- includeVideos,
11882
- includeHardware,
11883
- forceRefresh
11884
- });
11885
- return runtime2.createJsonToolResult(payload, payload.ok === false);
11886
- } catch (error) {
11887
- return runtime2.createErrorToolResult("steam_resolve_app", error);
11888
- }
11889
- }
12142
+ }) => buildAppResolveData(runtime2, {
12143
+ toolName: "steam_resolve_app",
12144
+ query,
12145
+ key,
12146
+ limit,
12147
+ includeDlc,
12148
+ includeSoftware,
12149
+ includeVideos,
12150
+ includeHardware,
12151
+ forceRefresh
12152
+ })
11890
12153
  );
11891
- server.registerTool(
12154
+ registerSteamTool(
12155
+ server,
12156
+ runtime2,
11892
12157
  "steam_get_global_achievement_overview",
11893
12158
  {
11894
- title: "Steam \u5168\u5C40\u6210\u5C31\u6982\u89C8",
11895
- description: "\u6C47\u603B\u67D0\u4E2A\u5E94\u7528\u7684\u5168\u5C40\u6210\u5C31\u5B8C\u6210\u7387\uFF0C\u5E76\u53EF\u9009\u9644\u5E26 schema \u5143\u6570\u636E\u3002",
12159
+ title: "\u5168\u5C40\u6210\u5C31\u603B\u89C8",
12160
+ description: "\u67E5\u770B\u67D0\u4E2A\u6E38\u620F\u7684\u5168\u5C40\u6210\u5C31\u5B8C\u6210\u7387\uFF0C\u5E76\u53EF\u9009\u8865\u5145 schema \u7EC6\u8282\u3002",
11896
12161
  inputSchema: {
11897
- appid: z2.union([z2.string(), z2.number()]).optional().describe("\u8981\u67E5\u770B\u7684 Steam AppID\u3002\u4E0E query \u4E8C\u9009\u4E00\u3002"),
11898
- query: z2.union([z2.string(), z2.number()]).optional().describe("\u5148\u89E3\u6790\u518D\u67E5\u8BE2\u7684\u5E94\u7528\u540D\u79F0\u6216 AppID\u3002\u4E0E appid \u4E8C\u9009\u4E00\u3002"),
11899
- key: z2.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\uFF1B\u6309\u540D\u79F0\u89E3\u6790\u548C\u83B7\u53D6 schema \u5143\u6570\u636E\u65F6\u9700\u8981\u3002"),
11900
- language: z2.string().optional().describe("\u53EF\u9009\u7684 schema \u8BED\u8A00\u4EE3\u7801\uFF0C\u4F8B\u5982 'schinese' \u6216 'english'\u3002"),
11901
- includeSchemaDetails: z2.boolean().optional().describe("\u662F\u5426\u7528 schema \u7684\u663E\u793A\u540D\u548C\u63CF\u8FF0\u8865\u5145\u6210\u5C31\u4FE1\u606F\uFF0C\u9ED8\u8BA4 true\u3002"),
12162
+ appid: z2.union([z2.string(), z2.number()]).optional().describe("Steam AppID\u3002\u4E0E query \u4E8C\u9009\u4E00\u3002"),
12163
+ query: z2.union([z2.string(), z2.number()]).optional().describe("\u6E38\u620F\u540D\u6216 AppID\uFF0C\u652F\u6301\u76F4\u63A5\u8F93\u5165\u4E2D\u6587\u540D\u3002\u4E0E appid \u4E8C\u9009\u4E00\u3002"),
12164
+ key: z2.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
12165
+ language: z2.string().optional().describe("\u8BED\u8A00\u4EE3\u7801\uFF0C\u4F8B\u5982 schinese \u6216 english\u3002"),
12166
+ includeSchemaDetails: z2.boolean().optional().describe("\u662F\u5426\u4F7F\u7528 schema \u8865\u5145\u663E\u793A\u540D\u548C\u63CF\u8FF0\uFF0C\u9ED8\u8BA4 true\u3002"),
11902
12167
  topCount: z2.number().int().min(1).max(50).optional().describe("\u6700\u7A00\u6709\u548C\u6700\u5E38\u89C1\u6210\u5C31\u5404\u8FD4\u56DE\u591A\u5C11\u6761\uFF0C\u9ED8\u8BA4 10\u3002")
11903
12168
  },
11904
12169
  annotations: {
@@ -11913,39 +12178,34 @@ function registerAppTools(server, runtime2) {
11913
12178
  language,
11914
12179
  includeSchemaDetails = true,
11915
12180
  topCount = 10
11916
- }) => {
11917
- try {
11918
- const payload = await buildGlobalAchievementOverviewData(runtime2, {
11919
- toolName: "steam_get_global_achievement_overview",
11920
- appid,
11921
- query,
11922
- key,
11923
- language,
11924
- includeSchemaDetails,
11925
- topCount
11926
- });
11927
- return runtime2.createJsonToolResult(payload, payload.ok === false);
11928
- } catch (error) {
11929
- return runtime2.createErrorToolResult("steam_get_global_achievement_overview", error);
11930
- }
11931
- }
12181
+ }) => buildGlobalAchievementOverviewData(runtime2, {
12182
+ toolName: "steam_get_global_achievement_overview",
12183
+ appid,
12184
+ query,
12185
+ key,
12186
+ language,
12187
+ includeSchemaDetails,
12188
+ topCount
12189
+ })
11932
12190
  );
11933
- server.registerTool(
12191
+ registerSteamTool(
12192
+ server,
12193
+ runtime2,
11934
12194
  "steam_get_app_snapshot",
11935
12195
  {
11936
- title: "Steam \u5E94\u7528\u5FEB\u7167",
11937
- description: "\u805A\u5408\u5E94\u7528\u57FA\u672C\u4FE1\u606F\u3001\u5F53\u524D\u5728\u7EBF\u4EBA\u6570\u3001\u6700\u8FD1\u65B0\u95FB\u548C\u5168\u5C40\u6210\u5C31\u8D8B\u52BF\u3002",
12196
+ title: "\u6E38\u620F\u5FEB\u7167",
12197
+ description: "\u516C\u5171\u5355\u6E38\u620F\u4E3B\u5165\u53E3\uFF0C\u805A\u5408\u65B0\u95FB\u3001\u5728\u7EBF\u4EBA\u6570\u548C\u5168\u5C40\u6210\u5C31\u4FE1\u606F\u3002",
11938
12198
  inputSchema: {
11939
- appid: z2.union([z2.string(), z2.number()]).optional().describe("\u8981\u67E5\u770B\u7684 Steam AppID\u3002\u4E0E query \u4E8C\u9009\u4E00\u3002"),
11940
- query: z2.union([z2.string(), z2.number()]).optional().describe("\u5148\u89E3\u6790\u518D\u67E5\u8BE2\u7684\u5E94\u7528\u540D\u79F0\u6216 AppID\u3002\u4E0E appid \u4E8C\u9009\u4E00\u3002"),
11941
- key: z2.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\uFF1B\u6309\u540D\u79F0\u89E3\u6790\u548C\u83B7\u53D6 schema \u5143\u6570\u636E\u65F6\u9700\u8981\u3002"),
11942
- language: z2.string().optional().describe("\u53EF\u9009\u7684 schema \u8BED\u8A00\u4EE3\u7801\u3002"),
11943
- includeNews: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6700\u8FD1\u7684 Steam \u65B0\u95FB\uFF0C\u9ED8\u8BA4 true\u3002"),
11944
- includePlayerCount: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B\u5F53\u524D\u5168\u7403\u5728\u7EBF\u4EBA\u6570\uFF0C\u9ED8\u8BA4 true\u3002"),
11945
- includeGlobalAchievements: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B\u5168\u5C40\u6210\u5C31\u7EDF\u8BA1\uFF0C\u9ED8\u8BA4 true\u3002"),
11946
- includeSchemaDetails: z2.boolean().optional().describe("\u662F\u5426\u7528 schema \u5143\u6570\u636E\u8865\u5145\u5168\u5C40\u6210\u5C31\u4FE1\u606F\uFF0C\u9ED8\u8BA4 true\u3002"),
11947
- newsCount: z2.number().int().min(1).max(10).optional().describe("\u8FD4\u56DE\u591A\u5C11\u6761\u65B0\u95FB\uFF0C\u9ED8\u8BA4 3\u3002"),
11948
- newsMaxLength: z2.number().int().min(0).max(1e3).optional().describe("\u6BCF\u6761\u65B0\u95FB\u5185\u5BB9\u7684\u6700\u5927\u957F\u5EA6\uFF0C\u9ED8\u8BA4 180\u3002"),
12199
+ appid: z2.union([z2.string(), z2.number()]).optional().describe("Steam AppID\u3002\u4E0E query \u4E8C\u9009\u4E00\u3002"),
12200
+ query: z2.union([z2.string(), z2.number()]).optional().describe("\u6E38\u620F\u540D\u6216 AppID\uFF0C\u652F\u6301\u76F4\u63A5\u8F93\u5165\u4E2D\u6587\u540D\u3002\u4E0E appid \u4E8C\u9009\u4E00\u3002"),
12201
+ key: z2.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
12202
+ language: z2.string().optional().describe("\u8BED\u8A00\u4EE3\u7801\uFF0C\u4F8B\u5982 schinese \u6216 english\u3002"),
12203
+ includeNews: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6700\u8FD1\u65B0\u95FB\uFF0C\u9ED8\u8BA4 true\u3002"),
12204
+ includePlayerCount: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B\u5F53\u524D\u5728\u7EBF\u4EBA\u6570\uFF0C\u9ED8\u8BA4 true\u3002"),
12205
+ includeGlobalAchievements: z2.boolean().optional().describe("\u662F\u5426\u5305\u542B\u5168\u5C40\u6210\u5C31\uFF0C\u9ED8\u8BA4 true\u3002"),
12206
+ includeSchemaDetails: z2.boolean().optional().describe("\u662F\u5426\u5728\u5168\u5C40\u6210\u5C31\u91CC\u8865\u5145 schema \u7EC6\u8282\uFF0C\u9ED8\u8BA4 true\u3002"),
12207
+ newsCount: z2.number().int().min(1).max(10).optional().describe("\u65B0\u95FB\u8FD4\u56DE\u6761\u6570\uFF0C\u9ED8\u8BA4 3\u3002"),
12208
+ newsMaxLength: z2.number().int().min(0).max(5e3).optional().describe("\u6BCF\u6761\u65B0\u95FB\u6700\u5927\u957F\u5EA6\uFF0C\u9ED8\u8BA4 180\u3002"),
11949
12209
  topGlobalAchievementsCount: z2.number().int().min(1).max(50).optional().describe("\u6700\u7A00\u6709\u548C\u6700\u5E38\u89C1\u6210\u5C31\u5404\u8FD4\u56DE\u591A\u5C11\u6761\uFF0C\u9ED8\u8BA4 10\u3002")
11950
12210
  },
11951
12211
  annotations: {
@@ -11953,6 +12213,7 @@ function registerAppTools(server, runtime2) {
11953
12213
  openWorldHint: true
11954
12214
  }
11955
12215
  },
12216
+ // 这里刻意把 snapshot 保留成公共单游戏主入口;更窄的专项工具再单独暴露。
11956
12217
  async ({
11957
12218
  appid,
11958
12219
  query,
@@ -11965,198 +12226,25 @@ function registerAppTools(server, runtime2) {
11965
12226
  newsCount = 3,
11966
12227
  newsMaxLength = 180,
11967
12228
  topGlobalAchievementsCount = 10
11968
- }) => {
11969
- try {
11970
- const payload = await buildAppSnapshotData(runtime2, {
11971
- toolName: "steam_get_app_snapshot",
11972
- appid,
11973
- query,
11974
- key,
11975
- language,
11976
- includeNews,
11977
- includePlayerCount,
11978
- includeGlobalAchievements,
11979
- includeSchemaDetails,
11980
- newsCount,
11981
- newsMaxLength,
11982
- topGlobalAchievementsCount
11983
- });
11984
- return runtime2.createJsonToolResult(payload);
11985
- } catch (error) {
11986
- return runtime2.createErrorToolResult("steam_get_app_snapshot", error);
11987
- }
11988
- }
12229
+ }) => buildAppSnapshotData(runtime2, {
12230
+ toolName: "steam_get_app_snapshot",
12231
+ appid,
12232
+ query,
12233
+ key,
12234
+ language,
12235
+ includeNews,
12236
+ includePlayerCount,
12237
+ includeGlobalAchievements,
12238
+ includeSchemaDetails,
12239
+ newsCount,
12240
+ newsMaxLength,
12241
+ topGlobalAchievementsCount
12242
+ })
11989
12243
  );
11990
12244
  }
11991
12245
 
11992
- // src/tools/generated.ts
11993
- function registerGeneratedTools(server, runtime2) {
11994
- for (const method of runtime2.spec.methods) {
11995
- const inputSchema = buildInputSchema(method, {
11996
- defaultSteamId: runtime2.config.defaultSteamId
11997
- });
11998
- const annotations = {
11999
- readOnlyHint: method.httpMethod === "GET",
12000
- openWorldHint: true
12001
- };
12002
- server.registerTool(
12003
- method.toolName,
12004
- {
12005
- title: createGeneratedToolTitle(method),
12006
- description: createToolDescription(method),
12007
- ...inputSchema ? { inputSchema } : {},
12008
- annotations
12009
- },
12010
- async (args) => runtime2.executeSteamMethod(method, args ?? {})
12011
- );
12012
- }
12013
- }
12014
-
12015
- // src/tools/helpers.ts
12246
+ // src/tools/me.ts
12016
12247
  import * as z3 from "zod/v4";
12017
- function registerHelperTools(server, runtime2) {
12018
- server.registerTool(
12019
- "steam_list_interfaces",
12020
- {
12021
- title: "Steam \u63A5\u53E3\u5217\u8868",
12022
- description: "\u5217\u51FA\u5F53\u524D\u5DF2\u540C\u6B65\u7684 Steam Web API \u63A5\u53E3\u3002",
12023
- inputSchema: {
12024
- search: z3.string().optional().describe("\u53EF\u9009\u5173\u952E\u5B57\uFF0C\u5927\u5C0F\u5199\u4E0D\u654F\u611F\uFF0C\u5339\u914D\u63A5\u53E3\u540D\u6216\u63A5\u53E3\u63CF\u8FF0\u3002"),
12025
- serviceOnly: z3.boolean().optional().describe("\u662F\u5426\u53EA\u8FD4\u56DE Steam Service Interface\u3002")
12026
- },
12027
- annotations: {
12028
- readOnlyHint: true,
12029
- openWorldHint: false
12030
- }
12031
- },
12032
- async ({ search, serviceOnly }) => {
12033
- const query = search?.toLowerCase();
12034
- const interfaces = runtime2.spec.interfaces.filter((entry) => {
12035
- if (serviceOnly && !entry.serviceInterface) {
12036
- return false;
12037
- }
12038
- if (!query) {
12039
- return true;
12040
- }
12041
- return entry.name.toLowerCase().includes(query) || entry.description.toLowerCase().includes(query);
12042
- });
12043
- const content = {
12044
- generatedAt: runtime2.spec.generatedAt,
12045
- interfaceCount: interfaces.length,
12046
- interfaces: interfaces.map((entry) => ({
12047
- name: entry.name,
12048
- description: entry.description,
12049
- serviceInterface: entry.serviceInterface,
12050
- sourceUrl: entry.sourceUrl,
12051
- methodCount: runtime2.spec.methods.filter(
12052
- (method) => method.interfaceName === entry.name
12053
- ).length
12054
- }))
12055
- };
12056
- return runtime2.createJsonToolResult(content);
12057
- }
12058
- );
12059
- server.registerTool(
12060
- "steam_list_methods",
12061
- {
12062
- title: "Steam \u65B9\u6CD5\u641C\u7D22",
12063
- description: "\u641C\u7D22\u5F53\u524D\u5DF2\u540C\u6B65\u7684 Steam Web API \u65B9\u6CD5\u76EE\u5F55\u3002",
12064
- inputSchema: {
12065
- interfaceName: z3.string().optional().describe("\u53EF\u9009\u63A5\u53E3\u540D\u8FC7\u6EE4\uFF0C\u4F8B\u5982 ISteamUserStats\u3002"),
12066
- search: z3.string().optional().describe("\u53EF\u9009\u5173\u952E\u5B57\uFF0C\u5927\u5C0F\u5199\u4E0D\u654F\u611F\uFF0C\u5339\u914D\u65B9\u6CD5\u540D\u6216\u65B9\u6CD5\u63CF\u8FF0\u3002"),
12067
- auth: z3.enum(["any", "anonymous", "userKey", "publisherKey"]).optional().describe("\u6309\u9274\u6743\u8981\u6C42\u8FC7\u6EE4\u3002"),
12068
- limit: z3.number().int().min(1).max(500).optional().describe("\u6700\u591A\u8FD4\u56DE\u591A\u5C11\u4E2A\u65B9\u6CD5\uFF0C\u9ED8\u8BA4 50\u3002")
12069
- },
12070
- annotations: {
12071
- readOnlyHint: true,
12072
- openWorldHint: false
12073
- }
12074
- },
12075
- async ({ interfaceName, search, auth = "any", limit = 50 }) => {
12076
- const query = search?.toLowerCase();
12077
- const methods = runtime2.spec.methods.filter((method) => {
12078
- if (interfaceName && method.interfaceName !== interfaceName) {
12079
- return false;
12080
- }
12081
- if (auth === "anonymous" && !method.supportsAnonymous) {
12082
- return false;
12083
- }
12084
- if (auth === "userKey" && !method.requiresUserKey) {
12085
- return false;
12086
- }
12087
- if (auth === "publisherKey" && !method.requiresPublisherKey) {
12088
- return false;
12089
- }
12090
- if (!query) {
12091
- return true;
12092
- }
12093
- return method.methodName.toLowerCase().includes(query) || method.interfaceName.toLowerCase().includes(query) || method.description.toLowerCase().includes(query);
12094
- });
12095
- const content = {
12096
- generatedAt: runtime2.spec.generatedAt,
12097
- totalMatches: methods.length,
12098
- returned: Math.min(methods.length, limit),
12099
- methods: methods.slice(0, limit).map((method) => ({
12100
- interfaceName: method.interfaceName,
12101
- methodName: method.methodName,
12102
- toolName: method.toolName,
12103
- httpMethod: method.httpMethod,
12104
- serviceInterface: method.serviceInterface,
12105
- requiresUserKey: method.requiresUserKey,
12106
- requiresPublisherKey: method.requiresPublisherKey,
12107
- supportsAnonymous: method.supportsAnonymous,
12108
- sourceUrl: method.sourceUrl
12109
- }))
12110
- };
12111
- return runtime2.createJsonToolResult(content);
12112
- }
12113
- );
12114
- server.registerTool(
12115
- "steam_get_method_details",
12116
- {
12117
- title: "Steam \u65B9\u6CD5\u8BE6\u60C5",
12118
- description: "\u67E5\u770B\u67D0\u4E2A\u5DF2\u540C\u6B65 Steam Web API \u65B9\u6CD5\u7684\u5B8C\u6574\u5143\u4FE1\u606F\u3002",
12119
- inputSchema: {
12120
- toolName: z3.string().optional().describe("\u751F\u6210\u540E\u7684 MCP tool \u540D\uFF0C\u4F8B\u5982 steam_i_player_service_get_owned_games\u3002"),
12121
- interfaceName: z3.string().optional().describe("\u4E0D\u4F7F\u7528 toolName \u65F6\u53EF\u4F20\u63A5\u53E3\u540D\u3002"),
12122
- methodName: z3.string().optional().describe("\u4E0D\u4F7F\u7528 toolName \u65F6\u53EF\u4F20\u65B9\u6CD5\u540D\u3002")
12123
- },
12124
- annotations: {
12125
- readOnlyHint: true,
12126
- openWorldHint: false
12127
- }
12128
- },
12129
- async ({ toolName, interfaceName, methodName }) => {
12130
- const method = runtime2.resolveMethod({ toolName, interfaceName, methodName });
12131
- return runtime2.createJsonToolResult(method);
12132
- }
12133
- );
12134
- server.registerTool(
12135
- "steam_call_raw",
12136
- {
12137
- title: "Steam \u539F\u59CB\u8C03\u7528",
12138
- description: "\u6309\u63A5\u53E3\u540D\u548C\u65B9\u6CD5\u540D\u76F4\u63A5\u8C03\u7528\u4EFB\u610F\u5DF2\u540C\u6B65\u7684 Steam Web API \u65B9\u6CD5\u3002\u9002\u5408\u5728\u751F\u6210\u5DE5\u5177\u7EA6\u675F\u8FC7\u4E25\u65F6\u515C\u5E95\u4F7F\u7528\u3002",
12139
- inputSchema: {
12140
- interfaceName: z3.string().describe("Steam Web API \u63A5\u53E3\u540D\uFF0C\u4F8B\u5982 ISteamUserStats\u3002"),
12141
- methodName: z3.string().describe("Steam Web API \u65B9\u6CD5\u540D\uFF0C\u4F8B\u5982 GetNumberOfCurrentPlayers\u3002"),
12142
- params: z3.record(z3.string(), z3.any()).optional().describe("\u539F\u59CB\u53C2\u6570\u5BF9\u8C61\uFF0C\u952E\u540D\u53EF\u4EE5\u4F7F\u7528 Steam \u539F\u59CB\u53C2\u6570\u540D\u6216\u751F\u6210\u540E\u7684\u8F93\u5165\u53C2\u6570\u540D\u3002"),
12143
- key: z3.string().optional().describe("\u53EF\u9009 API key \u8986\u76D6\u503C\uFF1B\u4E0D\u4F20\u65F6\u56DE\u9000\u5230 STEAM_WEB_API_KEY\u3002")
12144
- },
12145
- annotations: {
12146
- readOnlyHint: false,
12147
- openWorldHint: true
12148
- }
12149
- },
12150
- async ({ interfaceName, methodName, params = {}, key }) => {
12151
- const method = runtime2.resolveMethod({ interfaceName, methodName });
12152
- const mappedArgs = runtime2.mapRawParams(method, params);
12153
- return runtime2.executeSteamMethod(method, { ...mappedArgs, key });
12154
- }
12155
- );
12156
- }
12157
-
12158
- // src/tools/me.ts
12159
- import * as z4 from "zod/v4";
12160
12248
 
12161
12249
  // src/services/account.ts
12162
12250
  async function buildRecentlyPlayedData(runtime2, input) {
@@ -12176,16 +12264,21 @@ async function buildRecentlyPlayedData(runtime2, input) {
12176
12264
  apiKey
12177
12265
  )
12178
12266
  ]);
12267
+ const recentGames = normalizeRecentlyPlayedGames(recentGamesCall.data);
12268
+ const items = getRecordItems(recentGames?.games);
12179
12269
  return {
12180
12270
  ok: true,
12181
12271
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12182
- player: {
12183
- steamid: identity.steamid,
12184
- resolvedFrom: identity.resolvedFrom,
12185
- vanityUrl: identity.vanityUrl,
12186
- summary: normalizePlayerSummary(playerSummaryCall.data)
12272
+ target: buildDefaultPlayerTarget(identity),
12273
+ summary: {
12274
+ requestedCount: input.count,
12275
+ totalCount: toNumber(recentGames?.totalCount) ?? items.length,
12276
+ returnedCount: items.length
12277
+ },
12278
+ items,
12279
+ sections: {
12280
+ profile: normalizePlayerSummary(playerSummaryCall.data)
12187
12281
  },
12188
- recentlyPlayed: normalizeRecentlyPlayedGames(recentGamesCall.data),
12189
12282
  warnings: runtime2.collectWarnings([playerSummaryCall, recentGamesCall])
12190
12283
  };
12191
12284
  }
@@ -12212,17 +12305,20 @@ async function buildBadgesOverviewData(runtime2, input) {
12212
12305
  apiKey
12213
12306
  )
12214
12307
  ]);
12308
+ const badges = normalizeBadgeSummary(badgesCall.data);
12215
12309
  const level = toNumber(getNestedValue(steamLevelCall.data, "response", "player_level")) ?? toNumber(getNestedValue(steamLevelCall.data, "player_level"));
12216
12310
  return {
12217
12311
  ok: true,
12218
12312
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12219
- player: {
12220
- steamid: identity.steamid,
12221
- resolvedFrom: identity.resolvedFrom,
12222
- vanityUrl: identity.vanityUrl,
12223
- summary: normalizePlayerSummary(playerSummaryCall.data),
12313
+ target: buildDefaultPlayerTarget(identity),
12314
+ summary: {
12224
12315
  level,
12225
- badges: normalizeBadgeSummary(badgesCall.data)
12316
+ badgeCount: toNumber(badges?.badgeCount) ?? 0,
12317
+ playerXp: toNumber(badges?.playerXp) ?? null
12318
+ },
12319
+ sections: {
12320
+ profile: normalizePlayerSummary(playerSummaryCall.data),
12321
+ badges
12226
12322
  },
12227
12323
  warnings: runtime2.collectWarnings([playerSummaryCall, steamLevelCall, badgesCall])
12228
12324
  };
@@ -12244,15 +12340,21 @@ async function buildBansData(runtime2, input) {
12244
12340
  apiKey
12245
12341
  )
12246
12342
  ]);
12343
+ const bans = normalizePlayerBan(bansCall.data);
12247
12344
  return {
12248
12345
  ok: true,
12249
12346
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12250
- player: {
12251
- steamid: identity.steamid,
12252
- resolvedFrom: identity.resolvedFrom,
12253
- vanityUrl: identity.vanityUrl,
12254
- summary: normalizePlayerSummary(playerSummaryCall.data),
12255
- bans: normalizePlayerBan(bansCall.data)
12347
+ target: buildDefaultPlayerTarget(identity),
12348
+ summary: {
12349
+ communityBanned: toBoolean(bans?.communityBanned) ?? false,
12350
+ vacBanned: toBoolean(bans?.vacBanned) ?? false,
12351
+ numberOfVacBans: toNumber(bans?.numberOfVacBans) ?? 0,
12352
+ numberOfGameBans: toNumber(bans?.numberOfGameBans) ?? 0,
12353
+ daysSinceLastBan: toNumber(bans?.daysSinceLastBan) ?? null
12354
+ },
12355
+ sections: {
12356
+ profile: normalizePlayerSummary(playerSummaryCall.data),
12357
+ bans
12256
12358
  },
12257
12359
  warnings: runtime2.collectWarnings([playerSummaryCall, bansCall])
12258
12360
  };
@@ -12290,24 +12392,25 @@ async function buildFriendsData(runtime2, input) {
12290
12392
  const friendIds = limitedFriends.map((friend) => friend.steamid).filter((steamid) => Boolean(steamid));
12291
12393
  const summaryMap = input.includeSummaries && friendIds.length > 0 ? await runtime2.fetchPlayerSummaryMap(friendIds, apiKey) : /* @__PURE__ */ new Map();
12292
12394
  const banMap = input.includeBans && friendIds.length > 0 ? await runtime2.fetchPlayerBanMap(friendIds, apiKey) : /* @__PURE__ */ new Map();
12395
+ const items = limitedFriends.map((friend) => ({
12396
+ ...friend,
12397
+ summary: friend.steamid ? summaryMap.get(friend.steamid) ?? null : null,
12398
+ bans: friend.steamid ? banMap.get(friend.steamid) ?? null : null
12399
+ }));
12293
12400
  return {
12294
12401
  ok: true,
12295
12402
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12296
- player: {
12297
- steamid: identity.steamid,
12298
- resolvedFrom: identity.resolvedFrom,
12299
- vanityUrl: identity.vanityUrl,
12300
- summary: normalizePlayerSummary(playerSummaryCall.data)
12301
- },
12302
- friends: {
12403
+ target: buildDefaultPlayerTarget(identity),
12404
+ summary: {
12303
12405
  relationship: input.relationship,
12304
12406
  totalFriends: allFriends.length,
12305
- returnedFriends: limitedFriends.length,
12306
- items: limitedFriends.map((friend) => ({
12307
- ...friend,
12308
- summary: friend.steamid ? summaryMap.get(friend.steamid) ?? null : null,
12309
- bans: friend.steamid ? banMap.get(friend.steamid) ?? null : null
12310
- }))
12407
+ returnedFriends: items.length,
12408
+ includeSummaries: input.includeSummaries,
12409
+ includeBans: input.includeBans
12410
+ },
12411
+ items,
12412
+ sections: {
12413
+ profile: normalizePlayerSummary(playerSummaryCall.data)
12311
12414
  },
12312
12415
  warnings: runtime2.collectWarnings([playerSummaryCall, friendListCall])
12313
12416
  };
@@ -12315,7 +12418,16 @@ async function buildFriendsData(runtime2, input) {
12315
12418
  async function buildGameSnapshotData(runtime2, input) {
12316
12419
  const apiKey = runtime2.requireSteamApiKey(input.toolName, input.key);
12317
12420
  const identity = await runtime2.resolvePlayerIdentity({}, apiKey);
12318
- const appIdValue = normalizeScalarInput(input.appid);
12421
+ const resolved = await runtime2.resolveAppSelection({
12422
+ toolName: input.toolName,
12423
+ appid: input.appid,
12424
+ query: input.query,
12425
+ key: apiKey,
12426
+ limit: 5,
12427
+ forceRefresh: false,
12428
+ locale: input.language,
12429
+ preferLocalizedName: Boolean(input.query) || Boolean(input.language)
12430
+ });
12319
12431
  const [playerSummaryCall, ownedGamesCall] = await Promise.all([
12320
12432
  runtime2.safeInvokeKnownMethod(
12321
12433
  "ISteamUser",
@@ -12330,128 +12442,78 @@ async function buildGameSnapshotData(runtime2, input) {
12330
12442
  steamid: identity.steamid,
12331
12443
  include_appinfo: true,
12332
12444
  include_played_free_games: true,
12333
- appids_filter: [appIdValue]
12445
+ appids_filter: [resolved.appid]
12334
12446
  },
12335
12447
  apiKey
12336
12448
  )
12337
12449
  ]);
12338
12450
  const ownedGames = normalizeOwnedGames(ownedGamesCall.data);
12339
12451
  const ownedGame = ownedGames.games[0] ?? null;
12340
- const achievementOverview = input.includeAchievements || input.includeStats ? await buildAchievementOverviewData(runtime2, {
12452
+ const achievementOverview = input.includeAchievements || input.includeStats ? await buildPlayerGameAchievementsData(runtime2, {
12341
12453
  toolName: input.toolName,
12342
12454
  steamid: identity.steamid,
12343
- appid: appIdValue ?? "",
12455
+ appid: resolved.appid,
12344
12456
  key: apiKey,
12345
12457
  language: input.language,
12346
12458
  includeStats: input.includeStats,
12347
12459
  recentUnlockedCount: 10
12348
12460
  }) : null;
12349
- const newsOverview = input.includeNews ? await buildNewsOverviewData(runtime2, {
12461
+ const newsOverview = input.includeNews || input.includePlayerCount ? await buildAppNewsData(runtime2, {
12350
12462
  toolName: input.toolName,
12351
- appid: appIdValue ?? "",
12352
- count: input.newsCount,
12353
- maxlength: input.newsMaxLength,
12463
+ appid: resolved.appid,
12464
+ count: input.includeNews ? input.newsCount : 1,
12465
+ maxlength: input.includeNews ? input.newsMaxLength : 0,
12354
12466
  includePlayerCount: input.includePlayerCount
12355
- }) : input.includePlayerCount ? await buildNewsOverviewData(runtime2, {
12356
- toolName: input.toolName,
12357
- appid: appIdValue ?? "",
12358
- count: 1,
12359
- maxlength: 0,
12360
- includePlayerCount: true
12361
12467
  }) : null;
12468
+ const appTarget = getRecord(getNestedValue(achievementOverview, "target", "app")) ?? getRecord(getNestedValue(newsOverview, "target", "app")) ?? resolved.target;
12469
+ const warnings = [
12470
+ ...resolved.warnings,
12471
+ ...runtime2.collectWarnings([playerSummaryCall, ownedGamesCall]),
12472
+ ...getWarnings(achievementOverview),
12473
+ ...getWarnings(newsOverview)
12474
+ ];
12362
12475
  return {
12363
12476
  ok: true,
12364
12477
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12365
- player: {
12366
- steamid: identity.steamid,
12367
- resolvedFrom: identity.resolvedFrom,
12368
- vanityUrl: identity.vanityUrl,
12369
- summary: normalizePlayerSummary(playerSummaryCall.data)
12478
+ target: {
12479
+ player: buildDefaultPlayerTarget(identity).player,
12480
+ app: appTarget
12370
12481
  },
12371
- game: {
12372
- appid: appIdValue,
12482
+ summary: {
12373
12483
  owned: Boolean(ownedGame),
12484
+ playtimeForeverMinutes: toNumber(ownedGame?.playtimeForeverMinutes) ?? 0,
12485
+ playtimeForeverHours: toNumber(ownedGame?.playtimeForeverHours) ?? 0,
12486
+ hasAchievements: input.includeAchievements,
12487
+ hasStats: input.includeStats,
12488
+ hasNews: input.includeNews,
12489
+ hasPlayerCount: input.includePlayerCount
12490
+ },
12491
+ sections: {
12492
+ profile: normalizePlayerSummary(playerSummaryCall.data),
12374
12493
  ownership: ownedGame,
12375
- currentPlayers: newsOverview && input.includePlayerCount ? getNestedValue(newsOverview, "game", "currentPlayers") : void 0,
12376
- news: newsOverview && input.includeNews ? getNestedValue(newsOverview, "news") : void 0,
12377
- achievementOverview: achievementOverview && input.includeAchievements ? {
12378
- achievementSummary: getNestedValue(
12494
+ live: input.includePlayerCount ? {
12495
+ currentPlayers: getNestedValue(newsOverview, "summary", "currentPlayers")
12496
+ } : void 0,
12497
+ news: input.includeNews ? {
12498
+ summary: getNestedValue(newsOverview, "summary"),
12499
+ items: getNestedValue(newsOverview, "items")
12500
+ } : void 0,
12501
+ achievements: input.includeAchievements ? {
12502
+ summary: getNestedValue(achievementOverview, "summary"),
12503
+ items: getNestedValue(achievementOverview, "items"),
12504
+ recentlyUnlocked: getNestedValue(
12379
12505
  achievementOverview,
12380
- "achievementSummary"
12381
- ),
12382
- achievements: getNestedValue(achievementOverview, "achievements")
12506
+ "sections",
12507
+ "recentlyUnlocked"
12508
+ )
12383
12509
  } : void 0,
12384
- stats: achievementOverview && input.includeStats ? getNestedValue(achievementOverview, "stats") : void 0
12510
+ stats: input.includeStats ? getNestedValue(achievementOverview, "sections", "stats") : void 0
12385
12511
  },
12386
- warnings: [
12387
- ...runtime2.collectWarnings([playerSummaryCall, ownedGamesCall]),
12388
- ...Array.isArray(getNestedValue(achievementOverview, "warnings")) ? getNestedValue(achievementOverview, "warnings") : [],
12389
- ...Array.isArray(getNestedValue(newsOverview, "warnings")) ? getNestedValue(newsOverview, "warnings") : []
12390
- ]
12512
+ warnings
12391
12513
  };
12392
12514
  }
12393
- async function buildGroupsData(runtime2, input) {
12394
- const apiKey = runtime2.requireSteamApiKey(input.toolName, input.key);
12395
- const identity = await runtime2.resolvePlayerIdentity({}, apiKey);
12396
- const [playerSummaryCall, groupListCall] = await Promise.all([
12397
- runtime2.safeInvokeKnownMethod(
12398
- "ISteamUser",
12399
- "GetPlayerSummaries",
12400
- { steamids: identity.steamid },
12401
- apiKey
12402
- ),
12403
- runtime2.safeInvokeKnownMethod(
12404
- "ISteamUser",
12405
- "GetUserGroupList",
12406
- { steamid: identity.steamid },
12407
- apiKey
12408
- )
12409
- ]);
12410
- if (!groupListCall.ok) {
12411
- return {
12412
- ok: false,
12413
- requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12414
- player: {
12415
- steamid: identity.steamid,
12416
- resolvedFrom: identity.resolvedFrom,
12417
- vanityUrl: identity.vanityUrl,
12418
- summary: normalizePlayerSummary(playerSummaryCall.data)
12419
- },
12420
- note: "Steam docs currently mark ISteamUser.GetUserGroupList as requiring a publisher key, so normal user keys often fail here.",
12421
- warnings: runtime2.collectWarnings([playerSummaryCall, groupListCall])
12422
- };
12423
- }
12424
- const groups = [];
12425
- for (const entry of getNestedArray(groupListCall.data, "response", "groups")) {
12426
- const group = asRecord(entry);
12427
- if (!group) {
12428
- continue;
12429
- }
12430
- groups.push({
12431
- gid: getStringValue(group.gid)
12432
- });
12433
- }
12434
- return {
12435
- ok: true,
12436
- requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12437
- player: {
12438
- steamid: identity.steamid,
12439
- resolvedFrom: identity.resolvedFrom,
12440
- vanityUrl: identity.vanityUrl,
12441
- summary: normalizePlayerSummary(playerSummaryCall.data)
12442
- },
12443
- groups: {
12444
- totalGroups: groups.length,
12445
- items: groups
12446
- },
12447
- warnings: runtime2.collectWarnings([playerSummaryCall, groupListCall])
12448
- };
12449
- }
12450
- async function buildWishlistLikeFeedData(runtime2, input) {
12515
+ async function buildGameFeedData(runtime2, input) {
12451
12516
  const explicitAppIds = parseAppIdCollection(input.appids);
12452
- const sourceMeta = {
12453
- source: input.source
12454
- };
12455
12517
  const needsUserContext = input.source === "recent" || input.source === "topOwned" || input.source === "mixed" || input.includeOwnedContext;
12456
12518
  const apiKey = needsUserContext ? runtime2.requireSteamApiKey(input.toolName, input.key) : input.key ?? runtime2.config.apiKey;
12457
12519
  const identity = await runtime2.resolvePlayerIdentity({}, apiKey);
@@ -12490,23 +12552,20 @@ async function buildWishlistLikeFeedData(runtime2, input) {
12490
12552
  apiKey,
12491
12553
  input.limitApps
12492
12554
  );
12493
- selectedAppIds = [.../* @__PURE__ */ new Set([
12494
- ...recentAppIds,
12495
- ...topOwnedAppIds,
12496
- ...explicitAppIds
12497
- ])];
12555
+ selectedAppIds = [.../* @__PURE__ */ new Set([...recentAppIds, ...topOwnedAppIds, ...explicitAppIds])];
12498
12556
  }
12499
12557
  if (selectedAppIds.length === 0) {
12500
12558
  return {
12501
12559
  ok: false,
12502
12560
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12503
- player: {
12504
- steamid: identity.steamid,
12505
- resolvedFrom: identity.resolvedFrom,
12506
- vanityUrl: identity.vanityUrl
12561
+ target: buildDefaultPlayerTarget(identity),
12562
+ summary: {
12563
+ source: input.source,
12564
+ appCount: 0,
12565
+ feedItemCount: 0
12507
12566
  },
12508
- note: "No apps were selected for the feed. Provide appids or switch to a source that can derive apps from your account.",
12509
- source: sourceMeta,
12567
+ items: [],
12568
+ note: input.source === "explicit" ? "No AppIDs were provided for source='explicit'." : "No apps were selected for the feed.",
12510
12569
  warnings: []
12511
12570
  };
12512
12571
  }
@@ -12516,48 +12575,52 @@ async function buildWishlistLikeFeedData(runtime2, input) {
12516
12575
  identity.steamid,
12517
12576
  runtime2.requireSteamApiKey(input.toolName, input.key)
12518
12577
  ) : /* @__PURE__ */ new Map();
12519
- const appResults = await Promise.all(
12578
+ const appSections = await Promise.all(
12520
12579
  selectedAppIds.map(async (appid) => {
12521
- const newsOverview = await buildNewsOverviewData(runtime2, {
12580
+ const newsOverview = await buildAppNewsData(runtime2, {
12522
12581
  toolName: input.toolName,
12523
12582
  appid,
12524
12583
  count: input.newsPerApp,
12525
12584
  maxlength: input.newsMaxLength,
12526
12585
  includePlayerCount: input.includePlayerCount
12527
12586
  });
12587
+ const appTarget = getRecord(getNestedValue(newsOverview, "target", "app"));
12588
+ const newsItems = getRecordItems(getNestedValue(newsOverview, "items"));
12528
12589
  return {
12529
- appid,
12530
- currentPlayers: getNestedValue(newsOverview, "game", "currentPlayers"),
12590
+ app: appTarget,
12591
+ currentPlayers: getNestedValue(newsOverview, "summary", "currentPlayers"),
12531
12592
  ownedContext: ownedContextMap.get(appid) ?? null,
12532
- news: getNestedValue(newsOverview, "news", "items"),
12533
- warnings: getNestedValue(newsOverview, "warnings")
12593
+ items: newsItems,
12594
+ warnings: getWarnings(newsOverview)
12534
12595
  };
12535
12596
  })
12536
12597
  );
12537
- const flatFeed = appResults.flatMap(
12538
- (app) => Array.isArray(app.news) ? app.news.map((item) => ({
12539
- appid: app.appid,
12540
- currentPlayers: app.currentPlayers,
12541
- ownedContext: app.ownedContext,
12598
+ const items = appSections.flatMap(
12599
+ (section) => section.items.map((item) => ({
12600
+ app: section.app,
12601
+ currentPlayers: section.currentPlayers,
12602
+ ownedContext: section.ownedContext,
12542
12603
  item
12543
- })) : []
12604
+ }))
12544
12605
  ).sort(
12545
12606
  (left, right) => (toNumber(asRecord(right.item)?.date) ?? 0) - (toNumber(asRecord(left.item)?.date) ?? 0)
12546
12607
  );
12547
12608
  return {
12548
12609
  ok: true,
12549
12610
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12550
- player: {
12551
- steamid: identity.steamid,
12552
- resolvedFrom: identity.resolvedFrom,
12553
- vanityUrl: identity.vanityUrl
12611
+ target: buildDefaultPlayerTarget(identity),
12612
+ summary: {
12613
+ source: input.source,
12614
+ appCount: appSections.length,
12615
+ feedItemCount: items.length,
12616
+ includeOwnedContext: input.includeOwnedContext,
12617
+ includePlayerCount: input.includePlayerCount
12554
12618
  },
12555
- source: sourceMeta,
12556
- apps: appResults,
12557
- feed: flatFeed,
12558
- warnings: appResults.flatMap(
12559
- (app) => Array.isArray(app.warnings) ? app.warnings : []
12560
- )
12619
+ items,
12620
+ sections: {
12621
+ apps: appSections
12622
+ },
12623
+ warnings: appSections.flatMap((section) => section.warnings)
12561
12624
  };
12562
12625
  }
12563
12626
  async function buildFriendActivityData(runtime2, input) {
@@ -12569,19 +12632,17 @@ async function buildFriendActivityData(runtime2, input) {
12569
12632
  includeBans: input.includeBans,
12570
12633
  limit: input.limit
12571
12634
  });
12572
- const friendItems = Array.isArray(getNestedValue(friendsData, "friends", "items")) ? getNestedValue(friendsData, "friends", "items") ?? [] : [];
12635
+ const friendItems = getRecordItems(getNestedValue(friendsData, "items"));
12573
12636
  const normalizedActivity = friendItems.map((entry) => normalizeFriendActivityItem(entry)).filter(
12574
12637
  (entry) => entry !== null && (!input.onlyInGame || toBoolean(entry.inGame) === true)
12575
12638
  ).sort(compareFriendActivityItems);
12576
12639
  return {
12577
12640
  ok: true,
12578
12641
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12579
- player: getNestedValue(friendsData, "player"),
12580
- activity: {
12581
- inspectedFriends: toNumber(
12582
- getNestedValue(friendsData, "friends", "returnedFriends")
12583
- ),
12584
- totalFriends: toNumber(getNestedValue(friendsData, "friends", "totalFriends")),
12642
+ target: getNestedValue(friendsData, "target"),
12643
+ summary: {
12644
+ inspectedFriends: toNumber(getNestedValue(friendsData, "summary", "returnedFriends")),
12645
+ totalFriends: toNumber(getNestedValue(friendsData, "summary", "totalFriends")),
12585
12646
  returnedFriends: normalizedActivity.length,
12586
12647
  currentlyInGameCount: normalizedActivity.filter(
12587
12648
  (entry) => toBoolean(entry.inGame) === true
@@ -12589,9 +12650,13 @@ async function buildFriendActivityData(runtime2, input) {
12589
12650
  onlineCount: normalizedActivity.filter(
12590
12651
  (entry) => toBoolean(entry.online) === true
12591
12652
  ).length,
12592
- items: normalizedActivity
12653
+ onlyInGame: input.onlyInGame
12593
12654
  },
12594
- warnings: Array.isArray(getNestedValue(friendsData, "warnings")) ? getNestedValue(friendsData, "warnings") : []
12655
+ items: normalizedActivity,
12656
+ sections: {
12657
+ profile: getNestedValue(friendsData, "sections", "profile")
12658
+ },
12659
+ warnings: getWarnings(friendsData)
12595
12660
  };
12596
12661
  }
12597
12662
  async function buildFriendNetworkData(runtime2, input) {
@@ -12603,10 +12668,10 @@ async function buildFriendNetworkData(runtime2, input) {
12603
12668
  includeBans: input.includeBans,
12604
12669
  limit: input.limit
12605
12670
  });
12606
- const player = asRecord(getNestedValue(friendsData, "player"));
12607
- const playerSummary = asRecord(player?.summary);
12608
- const playerSteamId = getStringValue(player?.steamid) ?? runtime2.config.defaultSteamId ?? "";
12609
- const friendItems = Array.isArray(getNestedValue(friendsData, "friends", "items")) ? getNestedValue(friendsData, "friends", "items") ?? [] : [];
12671
+ const playerTarget = getRecord(getNestedValue(friendsData, "target", "player"));
12672
+ const playerSummary = getRecord(getNestedValue(friendsData, "sections", "profile"));
12673
+ const playerSteamId = getStringValue(playerTarget?.steamid) ?? runtime2.config.defaultSteamId ?? "";
12674
+ const friendItems = getRecordItems(getNestedValue(friendsData, "items"));
12610
12675
  const normalizedFriends = friendItems.map((entry) => normalizeFriendActivityItem(entry)).filter((entry) => entry !== null).sort(compareFriendActivityItems);
12611
12676
  const recentThreshold = Math.floor(Date.now() / 1e3) - input.activityWindowHours * 60 * 60;
12612
12677
  const inGameFriends = normalizedFriends.filter(
@@ -12635,12 +12700,10 @@ async function buildFriendNetworkData(runtime2, input) {
12635
12700
  return {
12636
12701
  ok: true,
12637
12702
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12638
- player: getNestedValue(friendsData, "player"),
12639
- networkSummary: {
12640
- totalFriends: toNumber(getNestedValue(friendsData, "friends", "totalFriends")),
12641
- inspectedFriends: toNumber(
12642
- getNestedValue(friendsData, "friends", "returnedFriends")
12643
- ),
12703
+ target: getNestedValue(friendsData, "target"),
12704
+ summary: {
12705
+ totalFriends: toNumber(getNestedValue(friendsData, "summary", "totalFriends")),
12706
+ inspectedFriends: toNumber(getNestedValue(friendsData, "summary", "returnedFriends")),
12644
12707
  onlineCount: onlineFriends.length,
12645
12708
  inGameCount: inGameFriends.length,
12646
12709
  recentlyActiveCount: recentlyActiveFriends.length,
@@ -12648,18 +12711,21 @@ async function buildFriendNetworkData(runtime2, input) {
12648
12711
  activeGameCount: gameGroups.length,
12649
12712
  activityWindowHours: input.activityWindowHours
12650
12713
  },
12651
- groups: {
12652
- inGameByTitle: gameGroups,
12653
- online: onlineFriends,
12654
- recentlyActive: recentlyActiveFriends,
12655
- offline: normalizedFriends.filter(
12656
- (entry) => toBoolean(entry.online) !== true && !recentlyActiveFriends.some(
12657
- (candidate) => getStringValue(candidate.steamid) === getStringValue(entry.steamid)
12714
+ sections: {
12715
+ profile: playerSummary,
12716
+ groups: {
12717
+ inGameByTitle: gameGroups,
12718
+ online: onlineFriends,
12719
+ recentlyActive: recentlyActiveFriends,
12720
+ offline: normalizedFriends.filter(
12721
+ (entry) => toBoolean(entry.online) !== true && !recentlyActiveFriends.some(
12722
+ (candidate) => getStringValue(candidate.steamid) === getStringValue(entry.steamid)
12723
+ )
12658
12724
  )
12659
- )
12725
+ },
12726
+ graph
12660
12727
  },
12661
- graph,
12662
- warnings: Array.isArray(getNestedValue(friendsData, "warnings")) ? getNestedValue(friendsData, "warnings") : []
12728
+ warnings: getWarnings(friendsData)
12663
12729
  };
12664
12730
  }
12665
12731
  async function buildBacklogCandidatesData(runtime2, input) {
@@ -12745,35 +12811,31 @@ async function buildBacklogCandidatesData(runtime2, input) {
12745
12811
  };
12746
12812
  })
12747
12813
  );
12748
- const candidates = shortlist.sort(compareBacklogCandidates).slice(0, input.limit);
12814
+ const items = shortlist.sort(compareBacklogCandidates).slice(0, input.limit);
12749
12815
  return {
12750
12816
  ok: true,
12751
12817
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12752
- player: {
12753
- steamid: identity.steamid,
12754
- resolvedFrom: identity.resolvedFrom,
12755
- vanityUrl: identity.vanityUrl,
12756
- summary: normalizePlayerSummary(playerSummaryCall.data)
12757
- },
12758
- backlogSummary: {
12818
+ target: buildDefaultPlayerTarget(identity),
12819
+ summary: {
12759
12820
  ownedGameCount: ownedGames.length,
12760
12821
  backlogPoolCount: backlogPool.length,
12761
- returnedCandidates: candidates.length,
12762
- neverPlayedCount: candidates.filter(
12822
+ returnedCount: items.length,
12823
+ neverPlayedCount: items.filter(
12763
12824
  (candidate) => toBoolean(candidate.neverPlayed) === true
12764
12825
  ).length,
12765
- barelyPlayedCount: candidates.filter(
12826
+ barelyPlayedCount: items.filter(
12766
12827
  (candidate) => toBoolean(candidate.neverPlayed) !== true && (toNumber(candidate.playtimeForeverHours) ?? 0) <= input.maxPlaytimeHours
12767
12828
  ).length,
12768
12829
  maxPlaytimeHours: input.maxPlaytimeHours,
12769
12830
  minDaysSinceLastPlayed: input.minDaysSinceLastPlayed
12770
12831
  },
12771
- candidates,
12832
+ items,
12833
+ sections: {
12834
+ profile: normalizePlayerSummary(playerSummaryCall.data)
12835
+ },
12772
12836
  warnings: [
12773
12837
  ...runtime2.collectWarnings([playerSummaryCall, ownedGamesCall]),
12774
- ...shortlist.flatMap(
12775
- (candidate) => Array.isArray(candidate.warnings) ? candidate.warnings : []
12776
- )
12838
+ ...shortlist.flatMap((candidate) => getWarnings(candidate))
12777
12839
  ]
12778
12840
  };
12779
12841
  }
@@ -12819,21 +12881,21 @@ async function buildAchievementHuntData(runtime2, input) {
12819
12881
  return {
12820
12882
  ok: false,
12821
12883
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12822
- player: {
12823
- steamid: identity.steamid,
12824
- resolvedFrom: identity.resolvedFrom,
12825
- vanityUrl: identity.vanityUrl,
12826
- summary: normalizePlayerSummary(playerSummaryCall.data)
12884
+ target: buildDefaultPlayerTarget(identity),
12885
+ summary: {
12886
+ source: input.source,
12887
+ inspectedGameCount: 0,
12888
+ candidateCount: 0
12827
12889
  },
12890
+ items: [],
12828
12891
  note: input.source === "explicit" ? "No AppIDs were provided for source='explicit'." : "No suitable games were selected to inspect for achievements.",
12829
- source: input.source,
12830
12892
  warnings: runtime2.collectWarnings([playerSummaryCall, ownedGamesCall])
12831
12893
  };
12832
12894
  }
12833
12895
  const inspectedGames = await Promise.all(
12834
12896
  appIdsToInspect.map(async (appid) => {
12835
12897
  try {
12836
- const overview = await buildAchievementOverviewData(runtime2, {
12898
+ const overview = await buildPlayerGameAchievementsData(runtime2, {
12837
12899
  toolName: input.toolName,
12838
12900
  steamid: identity.steamid,
12839
12901
  appid,
@@ -12842,8 +12904,9 @@ async function buildAchievementHuntData(runtime2, input) {
12842
12904
  includeStats: false,
12843
12905
  recentUnlockedCount: input.recentUnlockedCount
12844
12906
  });
12845
- const summary = asRecord(getNestedValue(overview, "achievementSummary"));
12846
- const achievements = Array.isArray(overview.achievements) ? overview.achievements : Array.isArray(getNestedValue(overview, "achievements")) ? getNestedValue(overview, "achievements") : [];
12907
+ const summary = asRecord(getNestedValue(overview, "summary"));
12908
+ const appTarget = asRecord(getNestedValue(overview, "target", "app"));
12909
+ const achievements = getRecordItems(getNestedValue(overview, "items"));
12847
12910
  const lockedAchievements = achievements.filter(
12848
12911
  (achievement) => toBoolean(achievement.achieved) !== true
12849
12912
  );
@@ -12854,7 +12917,8 @@ async function buildAchievementHuntData(runtime2, input) {
12854
12917
  return {
12855
12918
  ok: true,
12856
12919
  appid,
12857
- gameName: getStringValue(getNestedValue(overview, "game", "gameName")) ?? getStringValue(ownedGame?.name),
12920
+ app: appTarget,
12921
+ gameName: getStringValue(appTarget?.displayName) ?? getStringValue(appTarget?.name) ?? getStringValue(ownedGame?.name),
12858
12922
  playtimeForeverHours: toNumber(ownedGame?.playtimeForeverHours) ?? 0,
12859
12923
  playtimeForeverMinutes: toNumber(ownedGame?.playtimeForeverMinutes) ?? 0,
12860
12924
  hasCommunityVisibleStats: toBoolean(ownedGame?.hasCommunityVisibleStats),
@@ -12862,9 +12926,11 @@ async function buildAchievementHuntData(runtime2, input) {
12862
12926
  unlockedCount: toNumber(summary?.unlockedCount) ?? 0,
12863
12927
  lockedCount: toNumber(summary?.lockedCount) ?? 0,
12864
12928
  completionRate: toNumber(summary?.completionRate) ?? 0,
12865
- recentlyUnlocked: Array.isArray(summary?.recentlyUnlocked) ? summary?.recentlyUnlocked : [],
12929
+ recentlyUnlocked: getRecordItems(
12930
+ getNestedValue(overview, "sections", "recentlyUnlocked")
12931
+ ),
12866
12932
  easiestRemaining,
12867
- warnings: Array.isArray(overview.warnings) ? overview.warnings : []
12933
+ warnings: getWarnings(overview)
12868
12934
  };
12869
12935
  } catch (error) {
12870
12936
  return {
@@ -12875,39 +12941,37 @@ async function buildAchievementHuntData(runtime2, input) {
12875
12941
  }
12876
12942
  })
12877
12943
  );
12878
- const candidates = inspectedGames.filter((entry) => toBoolean(entry.ok) === true).filter(
12944
+ const items = inspectedGames.filter((entry) => toBoolean(entry.ok) === true).filter(
12879
12945
  (entry) => (toNumber(entry.totalAchievements) ?? 0) > 0 && (toNumber(entry.completionRate) ?? 0) >= input.minCompletionRate && (toNumber(entry.lockedCount) ?? Number.MAX_SAFE_INTEGER) <= input.maxRemainingAchievements
12880
12946
  ).sort(compareAchievementHuntCandidates).slice(0, input.limit);
12881
12947
  return {
12882
12948
  ok: true,
12883
12949
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
12884
- player: {
12885
- steamid: identity.steamid,
12886
- resolvedFrom: identity.resolvedFrom,
12887
- vanityUrl: identity.vanityUrl,
12888
- summary: normalizePlayerSummary(playerSummaryCall.data)
12889
- },
12890
- huntSummary: {
12950
+ target: buildDefaultPlayerTarget(identity),
12951
+ summary: {
12891
12952
  source: input.source,
12892
12953
  inspectedGameCount: inspectedGames.length,
12893
- candidateCount: candidates.length,
12954
+ candidateCount: items.length,
12894
12955
  minCompletionRate: input.minCompletionRate,
12895
12956
  maxRemainingAchievements: input.maxRemainingAchievements
12896
12957
  },
12897
- candidates,
12898
- inspectedGames,
12958
+ items,
12959
+ sections: {
12960
+ profile: normalizePlayerSummary(playerSummaryCall.data),
12961
+ inspected: inspectedGames
12962
+ },
12899
12963
  warnings: [
12900
12964
  ...runtime2.collectWarnings([playerSummaryCall, ownedGamesCall]),
12901
12965
  ...inspectedGames.flatMap((entry) => {
12902
- const record3 = asRecord(entry);
12903
- if (!record3) {
12966
+ const record2 = asRecord(entry);
12967
+ if (!record2) {
12904
12968
  return [];
12905
12969
  }
12906
- if (Array.isArray(record3.warnings)) {
12907
- return record3.warnings;
12970
+ if (Array.isArray(record2.warnings)) {
12971
+ return record2.warnings;
12908
12972
  }
12909
- if (typeof record3.error === "string") {
12910
- return [`appid ${record3.appid ?? "unknown"}: ${record3.error}`];
12973
+ if (typeof record2.error === "string") {
12974
+ return [`appid ${record2.appid ?? "unknown"}: ${record2.error}`];
12911
12975
  }
12912
12976
  return [];
12913
12977
  })
@@ -12952,9 +13016,9 @@ async function buildLibraryCompareData(runtime2, input) {
12952
13016
  const summaryMap = /* @__PURE__ */ new Map();
12953
13017
  for (const player of getNestedArray(summaryCall.data, "response", "players")) {
12954
13018
  const steamid = getStringValue(asRecord(player)?.steamid);
12955
- const record3 = normalizePlayerSummary({ response: { players: [player] } });
12956
- if (steamid && record3) {
12957
- summaryMap.set(steamid, record3);
13019
+ const record2 = normalizePlayerSummary({ response: { players: [player] } });
13020
+ if (steamid && record2) {
13021
+ summaryMap.set(steamid, record2);
12958
13022
  }
12959
13023
  }
12960
13024
  const myOwnedGames = normalizeOwnedGames(myOwnedGamesCall.data).games;
@@ -13001,27 +13065,35 @@ async function buildLibraryCompareData(runtime2, input) {
13001
13065
  return {
13002
13066
  ok: true,
13003
13067
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
13004
- me: {
13005
- steamid: myIdentity.steamid,
13006
- resolvedFrom: myIdentity.resolvedFrom,
13007
- vanityUrl: myIdentity.vanityUrl,
13008
- summary: summaryMap.get(myIdentity.steamid) ?? null,
13009
- gameCount: myOwnedGames.length
13010
- },
13011
- other: {
13012
- steamid: otherIdentity.steamid,
13013
- resolvedFrom: otherIdentity.resolvedFrom,
13014
- vanityUrl: otherIdentity.vanityUrl,
13015
- summary: summaryMap.get(otherIdentity.steamid) ?? null,
13016
- gameCount: otherOwnedGames.length
13017
- },
13018
- comparison: {
13068
+ target: {
13069
+ me: {
13070
+ steamid: myIdentity.steamid,
13071
+ resolvedFrom: myIdentity.resolvedFrom,
13072
+ vanityUrl: myIdentity.vanityUrl
13073
+ },
13074
+ other: {
13075
+ steamid: otherIdentity.steamid,
13076
+ resolvedFrom: otherIdentity.resolvedFrom,
13077
+ vanityUrl: otherIdentity.vanityUrl
13078
+ }
13079
+ },
13080
+ summary: {
13081
+ myGameCount: myOwnedGames.length,
13082
+ otherGameCount: otherOwnedGames.length,
13019
13083
  sharedGameCount: sharedGames.length,
13020
13084
  overlapPercentOfMine: myOwnedGames.length > 0 ? roundTo(sharedGames.length / myOwnedGames.length * 100, 2) : 0,
13021
- overlapPercentOfOther: otherOwnedGames.length > 0 ? roundTo(sharedGames.length / otherOwnedGames.length * 100, 2) : 0,
13022
- topSharedGames: sharedGames.slice(0, input.includeSharedTop),
13023
- onlyMineSample: onlyMine.slice(0, input.includeOnlyMineSample),
13024
- onlyOtherSample: onlyOther.slice(0, input.includeOnlyOtherSample)
13085
+ overlapPercentOfOther: otherOwnedGames.length > 0 ? roundTo(sharedGames.length / otherOwnedGames.length * 100, 2) : 0
13086
+ },
13087
+ sections: {
13088
+ me: {
13089
+ profile: summaryMap.get(myIdentity.steamid) ?? null
13090
+ },
13091
+ other: {
13092
+ profile: summaryMap.get(otherIdentity.steamid) ?? null
13093
+ },
13094
+ sharedTop: sharedGames.slice(0, input.includeSharedTop),
13095
+ onlyMine: onlyMine.slice(0, input.includeOnlyMineSample),
13096
+ onlyOther: onlyOther.slice(0, input.includeOnlyOtherSample)
13025
13097
  },
13026
13098
  warnings: runtime2.collectWarnings([summaryCall, myOwnedGamesCall, otherOwnedGamesCall])
13027
13099
  };
@@ -13071,18 +13143,39 @@ async function selectAchievementHuntAppIds(runtime2, input) {
13071
13143
  ...input.explicitAppIds
13072
13144
  ])].slice(0, input.inspectCount);
13073
13145
  }
13146
+ function buildDefaultPlayerTarget(identity) {
13147
+ return {
13148
+ player: {
13149
+ steamid: identity.steamid,
13150
+ resolvedFrom: identity.resolvedFrom,
13151
+ vanityUrl: identity.vanityUrl
13152
+ }
13153
+ };
13154
+ }
13155
+ function getWarnings(payload) {
13156
+ const warnings = getNestedValue(payload, "warnings");
13157
+ return Array.isArray(warnings) ? warnings : [];
13158
+ }
13159
+ function getRecord(value) {
13160
+ return asRecord(value);
13161
+ }
13162
+ function getRecordItems(value) {
13163
+ return Array.isArray(value) ? value.map((entry) => asRecord(entry)).filter((entry) => Boolean(entry)) : [];
13164
+ }
13074
13165
 
13075
13166
  // src/tools/me.ts
13076
13167
  function registerMyAccountTools(server, runtime2) {
13077
- server.registerTool(
13168
+ registerSteamTool(
13169
+ server,
13170
+ runtime2,
13078
13171
  "steam_me_profile",
13079
13172
  {
13080
- title: "\u6211\u7684 Steam \u8D44\u6599",
13081
- description: "\u5FEB\u6377\u67E5\u770B\u5DF2\u914D\u7F6E\u9ED8\u8BA4\u8D26\u53F7\u7684 Steam \u8D44\u6599\u603B\u89C8\u3002",
13173
+ title: "\u6211\u7684\u8D44\u6599",
13174
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u6211\u7684 Steam \u8D44\u6599\u603B\u89C8\u3002",
13082
13175
  inputSchema: {
13083
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13084
- includeRecentlyPlayed: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6700\u8FD1\u6E38\u73A9\u7684\u6E38\u620F\uFF0C\u9ED8\u8BA4 true\u3002"),
13085
- recentGamesCount: z4.number().int().min(1).max(20).optional().describe("\u6700\u8FD1\u6E38\u73A9\u6E38\u620F\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 3\u3002")
13176
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13177
+ includeRecentlyPlayed: z3.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6700\u8FD1\u6E38\u73A9\uFF0C\u9ED8\u8BA4 true\u3002"),
13178
+ recentGamesCount: z3.number().int().min(1).max(20).optional().describe("\u6700\u8FD1\u6E38\u73A9\u6E38\u620F\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 3\u3002")
13086
13179
  },
13087
13180
  annotations: {
13088
13181
  readOnlyHint: true,
@@ -13090,32 +13183,29 @@ function registerMyAccountTools(server, runtime2) {
13090
13183
  }
13091
13184
  },
13092
13185
  async ({ key, includeRecentlyPlayed = true, recentGamesCount = 3 }) => {
13093
- try {
13094
- runtime2.requireConfiguredDefaultIdentity("steam_me_profile");
13095
- const payload = await buildPlayerProfileOverviewData(runtime2, {
13096
- toolName: "steam_me_profile",
13097
- key,
13098
- includeRecentlyPlayed,
13099
- recentGamesCount
13100
- });
13101
- return runtime2.createJsonToolResult(payload);
13102
- } catch (error) {
13103
- return runtime2.createErrorToolResult("steam_me_profile", error);
13104
- }
13186
+ runtime2.requireConfiguredDefaultIdentity("steam_me_profile");
13187
+ return buildPlayerProfileOverviewData(runtime2, {
13188
+ toolName: "steam_me_profile",
13189
+ key,
13190
+ includeRecentlyPlayed,
13191
+ recentGamesCount
13192
+ });
13105
13193
  }
13106
13194
  );
13107
- server.registerTool(
13195
+ registerSteamTool(
13196
+ server,
13197
+ runtime2,
13108
13198
  "steam_me_library",
13109
13199
  {
13110
- title: "\u6211\u7684 Steam \u6E38\u620F\u5E93",
13111
- description: "\u5FEB\u6377\u67E5\u770B\u5DF2\u914D\u7F6E\u9ED8\u8BA4\u8D26\u53F7\u7684\u6E38\u620F\u5E93\u603B\u89C8\u3002",
13200
+ title: "\u6211\u7684\u6E38\u620F\u5E93",
13201
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u6211\u7684\u6E38\u620F\u5E93\u603B\u89C8\u3002",
13112
13202
  inputSchema: {
13113
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13114
- includeAppInfo: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6E38\u620F\u540D\u79F0\u548C\u56FE\u6807\uFF0C\u9ED8\u8BA4 true\u3002"),
13115
- includePlayedFreeGames: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u73A9\u8FC7\u7684\u514D\u8D39\u6E38\u620F\uFF0C\u9ED8\u8BA4 true\u3002"),
13116
- includeRecentGames: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6700\u8FD1\u6E38\u73A9\u7684\u6E38\u620F\uFF0C\u9ED8\u8BA4 true\u3002"),
13117
- recentGamesCount: z4.number().int().min(1).max(20).optional().describe("\u6700\u8FD1\u6E38\u73A9\u6E38\u620F\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 5\u3002"),
13118
- topGamesCount: z4.number().int().min(1).max(50).optional().describe("\u6309\u6E38\u73A9\u65F6\u957F\u6392\u5E8F\u8FD4\u56DE\u524D\u591A\u5C11\u4E2A\u6E38\u620F\uFF0C\u9ED8\u8BA4 10\u3002")
13203
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13204
+ includeAppInfo: z3.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6E38\u620F\u540D\u79F0\u548C\u56FE\u6807\uFF0C\u9ED8\u8BA4 true\u3002"),
13205
+ includePlayedFreeGames: z3.boolean().optional().describe("\u662F\u5426\u5305\u542B\u73A9\u8FC7\u7684\u514D\u8D39\u6E38\u620F\uFF0C\u9ED8\u8BA4 true\u3002"),
13206
+ includeRecentGames: z3.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6700\u8FD1\u6E38\u73A9\uFF0C\u9ED8\u8BA4 true\u3002"),
13207
+ recentGamesCount: z3.number().int().min(1).max(20).optional().describe("\u6700\u8FD1\u6E38\u73A9\u6E38\u620F\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 5\u3002"),
13208
+ topGamesCount: z3.number().int().min(1).max(50).optional().describe("\u6309\u65F6\u957F\u6392\u5E8F\u8FD4\u56DE\u524D\u591A\u5C11\u4E2A\u6E38\u620F\uFF0C\u9ED8\u8BA4 10\u3002")
13119
13209
  },
13120
13210
  annotations: {
13121
13211
  readOnlyHint: true,
@@ -13130,31 +13220,28 @@ function registerMyAccountTools(server, runtime2) {
13130
13220
  recentGamesCount = 5,
13131
13221
  topGamesCount = 10
13132
13222
  }) => {
13133
- try {
13134
- runtime2.requireConfiguredDefaultIdentity("steam_me_library");
13135
- const payload = await buildLibraryOverviewData(runtime2, {
13136
- toolName: "steam_me_library",
13137
- key,
13138
- includeAppInfo,
13139
- includePlayedFreeGames,
13140
- includeRecentGames,
13141
- recentGamesCount,
13142
- topGamesCount
13143
- });
13144
- return runtime2.createJsonToolResult(payload);
13145
- } catch (error) {
13146
- return runtime2.createErrorToolResult("steam_me_library", error);
13147
- }
13223
+ runtime2.requireConfiguredDefaultIdentity("steam_me_library");
13224
+ return buildLibraryOverviewData(runtime2, {
13225
+ toolName: "steam_me_library",
13226
+ key,
13227
+ includeAppInfo,
13228
+ includePlayedFreeGames,
13229
+ includeRecentGames,
13230
+ recentGamesCount,
13231
+ topGamesCount
13232
+ });
13148
13233
  }
13149
13234
  );
13150
- server.registerTool(
13235
+ registerSteamTool(
13236
+ server,
13237
+ runtime2,
13151
13238
  "steam_me_recently_played",
13152
13239
  {
13153
13240
  title: "\u6211\u7684\u6700\u8FD1\u6E38\u73A9",
13154
- description: "\u5FEB\u6377\u67E5\u770B\u5DF2\u914D\u7F6E\u9ED8\u8BA4\u8D26\u53F7\u7684\u6700\u8FD1\u6E38\u73A9\u6E38\u620F\u3002",
13241
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u6700\u8FD1\u6E38\u73A9\u7684\u6E38\u620F\u3002",
13155
13242
  inputSchema: {
13156
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13157
- count: z4.number().int().min(1).max(20).optional().describe("\u6700\u8FD1\u6E38\u73A9\u6E38\u620F\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 10\u3002")
13243
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13244
+ count: z3.number().int().min(1).max(20).optional().describe("\u6700\u8FD1\u6E38\u73A9\u6E38\u620F\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 10\u3002")
13158
13245
  },
13159
13246
  annotations: {
13160
13247
  readOnlyHint: true,
@@ -13162,26 +13249,23 @@ function registerMyAccountTools(server, runtime2) {
13162
13249
  }
13163
13250
  },
13164
13251
  async ({ key, count = 10 }) => {
13165
- try {
13166
- runtime2.requireConfiguredDefaultIdentity("steam_me_recently_played");
13167
- const payload = await buildRecentlyPlayedData(runtime2, {
13168
- toolName: "steam_me_recently_played",
13169
- key,
13170
- count
13171
- });
13172
- return runtime2.createJsonToolResult(payload);
13173
- } catch (error) {
13174
- return runtime2.createErrorToolResult("steam_me_recently_played", error);
13175
- }
13252
+ runtime2.requireConfiguredDefaultIdentity("steam_me_recently_played");
13253
+ return buildRecentlyPlayedData(runtime2, {
13254
+ toolName: "steam_me_recently_played",
13255
+ key,
13256
+ count
13257
+ });
13176
13258
  }
13177
13259
  );
13178
- server.registerTool(
13260
+ registerSteamTool(
13261
+ server,
13262
+ runtime2,
13179
13263
  "steam_me_badges",
13180
13264
  {
13181
- title: "\u6211\u7684 Steam \u5FBD\u7AE0",
13182
- description: "\u5FEB\u6377\u67E5\u770B\u5DF2\u914D\u7F6E\u9ED8\u8BA4\u8D26\u53F7\u7684\u7B49\u7EA7\u548C\u5FBD\u7AE0\u4FE1\u606F\u3002",
13265
+ title: "\u6211\u7684\u5FBD\u7AE0",
13266
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u6211\u7684\u7B49\u7EA7\u548C\u5FBD\u7AE0\u4FE1\u606F\u3002",
13183
13267
  inputSchema: {
13184
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002")
13268
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002")
13185
13269
  },
13186
13270
  annotations: {
13187
13271
  readOnlyHint: true,
@@ -13189,29 +13273,27 @@ function registerMyAccountTools(server, runtime2) {
13189
13273
  }
13190
13274
  },
13191
13275
  async ({ key }) => {
13192
- try {
13193
- runtime2.requireConfiguredDefaultIdentity("steam_me_badges");
13194
- const payload = await buildBadgesOverviewData(runtime2, {
13195
- toolName: "steam_me_badges",
13196
- key
13197
- });
13198
- return runtime2.createJsonToolResult(payload);
13199
- } catch (error) {
13200
- return runtime2.createErrorToolResult("steam_me_badges", error);
13201
- }
13276
+ runtime2.requireConfiguredDefaultIdentity("steam_me_badges");
13277
+ return buildBadgesOverviewData(runtime2, {
13278
+ toolName: "steam_me_badges",
13279
+ key
13280
+ });
13202
13281
  }
13203
13282
  );
13204
- server.registerTool(
13205
- "steam_me_achievement_overview",
13283
+ registerSteamTool(
13284
+ server,
13285
+ runtime2,
13286
+ "steam_me_game_achievements",
13206
13287
  {
13207
- title: "\u6211\u7684 Steam \u6210\u5C31\u603B\u89C8",
13208
- description: "\u5FEB\u6377\u67E5\u770B\u5DF2\u914D\u7F6E\u9ED8\u8BA4\u8D26\u53F7\u5728\u67D0\u4E2A\u6E38\u620F\u4E2D\u7684\u6210\u5C31\u603B\u89C8\u3002",
13288
+ title: "\u6211\u7684\u6E38\u620F\u6210\u5C31",
13289
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u6211\u5728\u67D0\u4E2A\u6E38\u620F\u91CC\u7684\u6210\u5C31\u548C\u5B8C\u6210\u7387\u3002",
13209
13290
  inputSchema: {
13210
- appid: z4.union([z4.string(), z4.number()]).describe("\u8981\u67E5\u770B\u7684 Steam AppID\u3002"),
13211
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13212
- language: z4.string().optional().describe("\u53EF\u9009\u8BED\u8A00\u4EE3\u7801\uFF0C\u4F8B\u5982 'schinese' \u6216 'english'\u3002"),
13213
- includeStats: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u8BE5\u6E38\u620F\u7684\u73A9\u5BB6\u7EDF\u8BA1\uFF0C\u9ED8\u8BA4 true\u3002"),
13214
- recentUnlockedCount: z4.number().int().min(1).max(50).optional().describe("\u9AD8\u4EAE\u5C55\u793A\u6700\u8FD1\u89E3\u9501\u6210\u5C31\u7684\u6570\u91CF\uFF0C\u9ED8\u8BA4 10\u3002")
13291
+ appid: z3.union([z3.string(), z3.number()]).optional().describe("Steam AppID\u3002\u4E0E query \u4E8C\u9009\u4E00\u3002"),
13292
+ query: z3.union([z3.string(), z3.number()]).optional().describe("\u6E38\u620F\u540D\u6216 AppID\uFF0C\u652F\u6301\u76F4\u63A5\u8F93\u5165\u4E2D\u6587\u540D\u3002\u4E0E appid \u4E8C\u9009\u4E00\u3002"),
13293
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13294
+ language: z3.string().optional().describe("\u8BED\u8A00\u4EE3\u7801\uFF0C\u4F8B\u5982 schinese \u6216 english\u3002"),
13295
+ includeStats: z3.boolean().optional().describe("\u662F\u5426\u5305\u542B\u73A9\u5BB6\u7EDF\u8BA1\uFF0C\u9ED8\u8BA4 true\u3002"),
13296
+ recentUnlockedCount: z3.number().int().min(1).max(50).optional().describe("\u6700\u8FD1\u89E3\u9501\u6210\u5C31\u7684\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 10\u3002")
13215
13297
  },
13216
13298
  annotations: {
13217
13299
  readOnlyHint: true,
@@ -13220,38 +13302,37 @@ function registerMyAccountTools(server, runtime2) {
13220
13302
  },
13221
13303
  async ({
13222
13304
  appid,
13305
+ query,
13223
13306
  key,
13224
13307
  language,
13225
13308
  includeStats = true,
13226
13309
  recentUnlockedCount = 10
13227
13310
  }) => {
13228
- try {
13229
- runtime2.requireConfiguredDefaultIdentity("steam_me_achievement_overview");
13230
- const payload = await buildAchievementOverviewData(runtime2, {
13231
- toolName: "steam_me_achievement_overview",
13232
- appid,
13233
- key,
13234
- language,
13235
- includeStats,
13236
- recentUnlockedCount
13237
- });
13238
- return runtime2.createJsonToolResult(payload);
13239
- } catch (error) {
13240
- return runtime2.createErrorToolResult("steam_me_achievement_overview", error);
13241
- }
13311
+ runtime2.requireConfiguredDefaultIdentity("steam_me_game_achievements");
13312
+ return buildPlayerGameAchievementsData(runtime2, {
13313
+ toolName: "steam_me_game_achievements",
13314
+ appid,
13315
+ query,
13316
+ key,
13317
+ language,
13318
+ includeStats,
13319
+ recentUnlockedCount
13320
+ });
13242
13321
  }
13243
13322
  );
13244
- server.registerTool(
13323
+ registerSteamTool(
13324
+ server,
13325
+ runtime2,
13245
13326
  "steam_me_friends",
13246
13327
  {
13247
- title: "\u6211\u7684 Steam \u597D\u53CB",
13248
- description: "\u5FEB\u6377\u67E5\u770B\u5DF2\u914D\u7F6E\u9ED8\u8BA4\u8D26\u53F7\u7684\u597D\u53CB\u5217\u8868\uFF0C\u5E76\u53EF\u9009\u9644\u5E26\u8D44\u6599\u8865\u5145\u4FE1\u606F\u3002",
13328
+ title: "\u597D\u53CB\u5217\u8868",
13329
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u597D\u53CB\u5217\u8868\uFF0C\u53EF\u9009\u8865\u5145\u8D44\u6599\u548C\u5C01\u7981\u4FE1\u606F\u3002",
13249
13330
  inputSchema: {
13250
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13251
- relationship: z4.string().optional().describe("\u597D\u53CB\u5173\u7CFB\u8FC7\u6EE4\u6761\u4EF6\uFF0C\u9ED8\u8BA4 'friend'\u3002"),
13252
- includeSummaries: z4.boolean().optional().describe("\u662F\u5426\u4E3A\u597D\u53CB\u8865\u5145\u8D44\u6599\u6458\u8981\uFF0C\u9ED8\u8BA4 true\u3002"),
13253
- includeBans: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u597D\u53CB\u5C01\u7981\u6458\u8981\uFF0C\u9ED8\u8BA4 false\u3002"),
13254
- limit: z4.number().int().min(1).max(500).optional().describe("\u6700\u591A\u8FD4\u56DE\u591A\u5C11\u4F4D\u597D\u53CB\uFF0C\u9ED8\u8BA4 100\u3002")
13331
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13332
+ relationship: z3.string().optional().describe("\u597D\u53CB\u5173\u7CFB\u8FC7\u6EE4\u6761\u4EF6\uFF0C\u9ED8\u8BA4 friend\u3002"),
13333
+ includeSummaries: z3.boolean().optional().describe("\u662F\u5426\u8865\u5145\u597D\u53CB\u8D44\u6599\u6458\u8981\uFF0C\u9ED8\u8BA4 true\u3002"),
13334
+ includeBans: z3.boolean().optional().describe("\u662F\u5426\u8865\u5145\u597D\u53CB\u5C01\u7981\u6458\u8981\uFF0C\u9ED8\u8BA4 false\u3002"),
13335
+ limit: z3.number().int().min(1).max(500).optional().describe("\u6700\u591A\u8FD4\u56DE\u591A\u5C11\u4F4D\u597D\u53CB\uFF0C\u9ED8\u8BA4 100\u3002")
13255
13336
  },
13256
13337
  annotations: {
13257
13338
  readOnlyHint: true,
@@ -13265,32 +13346,29 @@ function registerMyAccountTools(server, runtime2) {
13265
13346
  includeBans = false,
13266
13347
  limit = 100
13267
13348
  }) => {
13268
- try {
13269
- runtime2.requireConfiguredDefaultIdentity("steam_me_friends");
13270
- const payload = await buildFriendsData(runtime2, {
13271
- toolName: "steam_me_friends",
13272
- key,
13273
- relationship,
13274
- includeSummaries,
13275
- includeBans,
13276
- limit
13277
- });
13278
- return runtime2.createJsonToolResult(payload);
13279
- } catch (error) {
13280
- return runtime2.createErrorToolResult("steam_me_friends", error);
13281
- }
13349
+ runtime2.requireConfiguredDefaultIdentity("steam_me_friends");
13350
+ return buildFriendsData(runtime2, {
13351
+ toolName: "steam_me_friends",
13352
+ key,
13353
+ relationship,
13354
+ includeSummaries,
13355
+ includeBans,
13356
+ limit
13357
+ });
13282
13358
  }
13283
13359
  );
13284
- server.registerTool(
13360
+ registerSteamTool(
13361
+ server,
13362
+ runtime2,
13285
13363
  "steam_me_friend_activity",
13286
13364
  {
13287
- title: "\u6211\u7684\u597D\u53CB\u52A8\u6001",
13288
- description: "\u7A81\u51FA\u663E\u793A\u54EA\u4E9B\u597D\u53CB\u6B63\u5728\u6E38\u620F\u3001\u5728\u7EBF\uFF0C\u6216\u6700\u8FD1\u6D3B\u8DC3\u8FC7\u3002",
13365
+ title: "\u597D\u53CB\u5728\u7EBF\u52A8\u6001",
13366
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u54EA\u4E9B\u597D\u53CB\u5728\u7EBF\u3001\u5728\u73A9\u6E38\u620F\u6216\u6700\u8FD1\u6D3B\u8DC3\u3002",
13289
13367
  inputSchema: {
13290
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13291
- limit: z4.number().int().min(1).max(500).optional().describe("\u6700\u591A\u68C0\u67E5\u591A\u5C11\u4F4D\u597D\u53CB\uFF0C\u9ED8\u8BA4 100\u3002"),
13292
- onlyInGame: z4.boolean().optional().describe("\u662F\u5426\u53EA\u8FD4\u56DE\u5F53\u524D\u6B63\u5728\u6E38\u620F\u7684\u597D\u53CB\uFF0C\u9ED8\u8BA4 false\u3002"),
13293
- includeBans: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u8FD4\u56DE\u597D\u53CB\u7684\u5C01\u7981\u6458\u8981\uFF0C\u9ED8\u8BA4 false\u3002")
13368
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13369
+ limit: z3.number().int().min(1).max(500).optional().describe("\u6700\u591A\u68C0\u67E5\u591A\u5C11\u4F4D\u597D\u53CB\uFF0C\u9ED8\u8BA4 100\u3002"),
13370
+ onlyInGame: z3.boolean().optional().describe("\u662F\u5426\u53EA\u8FD4\u56DE\u6B63\u5728\u6E38\u620F\u4E2D\u7684\u597D\u53CB\uFF0C\u9ED8\u8BA4 false\u3002"),
13371
+ includeBans: z3.boolean().optional().describe("\u662F\u5426\u8865\u5145\u597D\u53CB\u5C01\u7981\u6458\u8981\uFF0C\u9ED8\u8BA4 false\u3002")
13294
13372
  },
13295
13373
  annotations: {
13296
13374
  readOnlyHint: true,
@@ -13298,39 +13376,37 @@ function registerMyAccountTools(server, runtime2) {
13298
13376
  }
13299
13377
  },
13300
13378
  async ({ key, limit = 100, onlyInGame = false, includeBans = false }) => {
13301
- try {
13302
- runtime2.requireConfiguredDefaultIdentity("steam_me_friend_activity");
13303
- const payload = await buildFriendActivityData(runtime2, {
13304
- toolName: "steam_me_friend_activity",
13305
- key,
13306
- limit,
13307
- onlyInGame,
13308
- includeBans
13309
- });
13310
- return runtime2.createJsonToolResult(payload);
13311
- } catch (error) {
13312
- return runtime2.createErrorToolResult("steam_me_friend_activity", error);
13313
- }
13379
+ runtime2.requireConfiguredDefaultIdentity("steam_me_friend_activity");
13380
+ return buildFriendActivityData(runtime2, {
13381
+ toolName: "steam_me_friend_activity",
13382
+ key,
13383
+ limit,
13384
+ onlyInGame,
13385
+ includeBans
13386
+ });
13314
13387
  }
13315
13388
  );
13316
- server.registerTool(
13389
+ registerSteamTool(
13390
+ server,
13391
+ runtime2,
13317
13392
  "steam_me_friend_network",
13318
13393
  {
13319
- title: "\u6211\u7684\u597D\u53CB\u7F51\u7EDC",
13320
- description: "\u4E3A\u5DF2\u914D\u7F6E\u9ED8\u8BA4\u8D26\u53F7\u751F\u6210\u597D\u53CB\u7F51\u7EDC\u89C6\u56FE\uFF0C\u5305\u542B\u6D3B\u52A8\u5206\u7EC4\u548C\u9002\u5408\u56FE\u8C31\u5C55\u793A\u7684 nodes/edges\u3002",
13394
+ title: "\u597D\u53CB\u5173\u7CFB\u56FE\u8C31",
13395
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u751F\u6210\u597D\u53CB\u5173\u7CFB\u548C\u6D3B\u52A8\u5206\u7EC4\u89C6\u56FE\u3002",
13321
13396
  inputSchema: {
13322
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13323
- limit: z4.number().int().min(1).max(500).optional().describe("\u6700\u591A\u68C0\u67E5\u591A\u5C11\u4F4D\u597D\u53CB\uFF0C\u9ED8\u8BA4 150\u3002"),
13324
- activityWindowHours: z4.number().int().min(1).max(720).optional().describe("\u591A\u5C11\u5C0F\u65F6\u5185\u7B97\u201C\u6700\u8FD1\u6D3B\u8DC3\u201D\uFF0C\u9ED8\u8BA4 72\u3002"),
13325
- includeBans: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u8FD4\u56DE\u597D\u53CB\u7684\u5C01\u7981\u6458\u8981\uFF0C\u9ED8\u8BA4 false\u3002"),
13326
- includeGraph: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u9002\u5408\u56FE\u8C31\u6E32\u67D3\u7684 nodes \u548C edges\uFF0C\u9ED8\u8BA4 true\u3002"),
13327
- includeGameNodes: z4.boolean().optional().describe("\u56FE\u8C31\u8F93\u51FA\u4E2D\u662F\u5426\u5305\u542B\u5F53\u524D\u6B63\u5728\u73A9\u7684\u6E38\u620F\u8282\u70B9\uFF0C\u9ED8\u8BA4 true\u3002")
13397
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13398
+ limit: z3.number().int().min(1).max(500).optional().describe("\u6700\u591A\u68C0\u67E5\u591A\u5C11\u4F4D\u597D\u53CB\uFF0C\u9ED8\u8BA4 150\u3002"),
13399
+ activityWindowHours: z3.number().int().min(1).max(720).optional().describe("\u591A\u5C11\u5C0F\u65F6\u5185\u7B97\u6700\u8FD1\u6D3B\u8DC3\uFF0C\u9ED8\u8BA4 72\u3002"),
13400
+ includeBans: z3.boolean().optional().describe("\u662F\u5426\u8865\u5145\u597D\u53CB\u5C01\u7981\u6458\u8981\uFF0C\u9ED8\u8BA4 false\u3002"),
13401
+ includeGraph: z3.boolean().optional().describe("\u662F\u5426\u8FD4\u56DE\u53EF\u89C6\u5316 graph\uFF0C\u9ED8\u8BA4 true\u3002"),
13402
+ includeGameNodes: z3.boolean().optional().describe("graph \u4E2D\u662F\u5426\u5305\u542B\u6E38\u620F\u8282\u70B9\uFF0C\u9ED8\u8BA4 true\u3002")
13328
13403
  },
13329
13404
  annotations: {
13330
13405
  readOnlyHint: true,
13331
13406
  openWorldHint: false
13332
13407
  }
13333
13408
  },
13409
+ // 好友网络不是另一个数据源,而是对好友列表做更高层的关系视图整理。
13334
13410
  async ({
13335
13411
  key,
13336
13412
  limit = 150,
@@ -13339,30 +13415,27 @@ function registerMyAccountTools(server, runtime2) {
13339
13415
  includeGraph = true,
13340
13416
  includeGameNodes = true
13341
13417
  }) => {
13342
- try {
13343
- runtime2.requireConfiguredDefaultIdentity("steam_me_friend_network");
13344
- const payload = await buildFriendNetworkData(runtime2, {
13345
- toolName: "steam_me_friend_network",
13346
- key,
13347
- limit,
13348
- activityWindowHours,
13349
- includeBans,
13350
- includeGraph,
13351
- includeGameNodes
13352
- });
13353
- return runtime2.createJsonToolResult(payload);
13354
- } catch (error) {
13355
- return runtime2.createErrorToolResult("steam_me_friend_network", error);
13356
- }
13418
+ runtime2.requireConfiguredDefaultIdentity("steam_me_friend_network");
13419
+ return buildFriendNetworkData(runtime2, {
13420
+ toolName: "steam_me_friend_network",
13421
+ key,
13422
+ limit,
13423
+ activityWindowHours,
13424
+ includeBans,
13425
+ includeGraph,
13426
+ includeGameNodes
13427
+ });
13357
13428
  }
13358
13429
  );
13359
- server.registerTool(
13430
+ registerSteamTool(
13431
+ server,
13432
+ runtime2,
13360
13433
  "steam_me_bans",
13361
13434
  {
13362
- title: "\u6211\u7684\u5C01\u7981\u72B6\u6001",
13363
- description: "\u5FEB\u6377\u67E5\u770B\u5DF2\u914D\u7F6E\u9ED8\u8BA4\u8D26\u53F7\u7684\u5C01\u7981\u6458\u8981\u3002",
13435
+ title: "\u6211\u7684\u5C01\u7981\u6458\u8981",
13436
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u6211\u7684\u5C01\u7981\u72B6\u6001\u3002",
13364
13437
  inputSchema: {
13365
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002")
13438
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002")
13366
13439
  },
13367
13440
  annotations: {
13368
13441
  readOnlyHint: true,
@@ -13370,34 +13443,31 @@ function registerMyAccountTools(server, runtime2) {
13370
13443
  }
13371
13444
  },
13372
13445
  async ({ key }) => {
13373
- try {
13374
- runtime2.requireConfiguredDefaultIdentity("steam_me_bans");
13375
- const payload = await buildBansData(runtime2, {
13376
- toolName: "steam_me_bans",
13377
- key
13378
- });
13379
- return runtime2.createJsonToolResult(payload);
13380
- } catch (error) {
13381
- return runtime2.createErrorToolResult("steam_me_bans", error);
13382
- }
13446
+ runtime2.requireConfiguredDefaultIdentity("steam_me_bans");
13447
+ return buildBansData(runtime2, {
13448
+ toolName: "steam_me_bans",
13449
+ key
13450
+ });
13383
13451
  }
13384
13452
  );
13385
- server.registerTool(
13453
+ registerSteamTool(
13454
+ server,
13455
+ runtime2,
13386
13456
  "steam_me_library_compare",
13387
13457
  {
13388
- title: "\u6211\u7684\u6E38\u620F\u5E93\u5BF9\u6BD4",
13389
- description: "\u628A\u5DF2\u914D\u7F6E\u9ED8\u8BA4\u8D26\u53F7\u7684\u6E38\u620F\u5E93\u4E0E\u53E6\u4E00\u4F4D\u73A9\u5BB6\u8FDB\u884C\u5BF9\u6BD4\u3002",
13458
+ title: "\u6211\u7684\u5E93\u5BF9\u6BD4",
13459
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u628A\u6211\u7684\u5E93\u548C\u53E6\u4E00\u4F4D\u73A9\u5BB6\u505A\u5BF9\u6BD4\u3002",
13390
13460
  inputSchema: {
13391
- steamid: z4.union([z4.string(), z4.number()]).optional().describe("\u5BF9\u65B9\u73A9\u5BB6\u7684 SteamID64\u3002\u4E0E vanityUrl \u4E8C\u9009\u4E00\u3002"),
13392
- vanityUrl: z4.string().optional().describe("\u5BF9\u65B9\u73A9\u5BB6\u7684 Steam \u81EA\u5B9A\u4E49\u4E3B\u9875\u6807\u8BC6\u3002\u4E0E steamid \u4E8C\u9009\u4E00\u3002"),
13393
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13394
- includeOnlyMineSample: z4.number().int().min(1).max(50).optional().describe("\u4F60\u7684\u5E93\u4E2D\u72EC\u6709\u6E38\u620F\u91C7\u6837\u6570\u91CF\uFF0C\u9ED8\u8BA4 10\u3002"),
13395
- includeOnlyOtherSample: z4.number().int().min(1).max(50).optional().describe("\u5BF9\u65B9\u5E93\u4E2D\u72EC\u6709\u6E38\u620F\u91C7\u6837\u6570\u91CF\uFF0C\u9ED8\u8BA4 10\u3002"),
13396
- includeSharedTop: z4.number().int().min(1).max(50).optional().describe("\u5171\u540C\u62E5\u6709\u6E38\u620F\u91CC\u6309\u603B\u6E38\u73A9\u65F6\u957F\u9AD8\u4EAE\u591A\u5C11\u4E2A\uFF0C\u9ED8\u8BA4 20\u3002")
13461
+ steamid: z3.union([z3.string(), z3.number()]).optional().describe("\u5BF9\u65B9\u73A9\u5BB6\u7684 SteamID64\u3002\u4E0E vanityUrl \u4E8C\u9009\u4E00\u3002"),
13462
+ vanityUrl: z3.string().optional().describe("\u5BF9\u65B9\u73A9\u5BB6\u7684\u81EA\u5B9A\u4E49\u4E3B\u9875\u6807\u8BC6\u3002\u4E0E steamid \u4E8C\u9009\u4E00\u3002"),
13463
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13464
+ includeOnlyMineSample: z3.number().int().min(0).max(50).optional().describe("\u6211\u7684\u72EC\u6709\u6E38\u620F\u91C7\u6837\u6570\u91CF\uFF0C\u9ED8\u8BA4 10\u3002"),
13465
+ includeOnlyOtherSample: z3.number().int().min(0).max(50).optional().describe("\u5BF9\u65B9\u72EC\u6709\u6E38\u620F\u91C7\u6837\u6570\u91CF\uFF0C\u9ED8\u8BA4 10\u3002"),
13466
+ includeSharedTop: z3.number().int().min(1).max(50).optional().describe("\u5171\u540C\u62E5\u6709\u6E38\u620F\u4E2D\u6309\u603B\u65F6\u957F\u9AD8\u4EAE\u591A\u5C11\u4E2A\uFF0C\u9ED8\u8BA4 20\u3002")
13397
13467
  },
13398
13468
  annotations: {
13399
13469
  readOnlyHint: true,
13400
- openWorldHint: true
13470
+ openWorldHint: false
13401
13471
  }
13402
13472
  },
13403
13473
  async ({
@@ -13408,36 +13478,33 @@ function registerMyAccountTools(server, runtime2) {
13408
13478
  includeOnlyOtherSample = 10,
13409
13479
  includeSharedTop = 20
13410
13480
  }) => {
13411
- try {
13412
- runtime2.requireConfiguredDefaultIdentity("steam_me_library_compare");
13413
- const payload = await buildLibraryCompareData(runtime2, {
13414
- toolName: "steam_me_library_compare",
13415
- steamid,
13416
- vanityUrl,
13417
- key,
13418
- includeOnlyMineSample,
13419
- includeOnlyOtherSample,
13420
- includeSharedTop
13421
- });
13422
- return runtime2.createJsonToolResult(payload);
13423
- } catch (error) {
13424
- return runtime2.createErrorToolResult("steam_me_library_compare", error);
13425
- }
13481
+ runtime2.requireConfiguredDefaultIdentity("steam_me_library_compare");
13482
+ return buildLibraryCompareData(runtime2, {
13483
+ toolName: "steam_me_library_compare",
13484
+ steamid,
13485
+ vanityUrl,
13486
+ key,
13487
+ includeOnlyMineSample,
13488
+ includeOnlyOtherSample,
13489
+ includeSharedTop
13490
+ });
13426
13491
  }
13427
13492
  );
13428
- server.registerTool(
13493
+ registerSteamTool(
13494
+ server,
13495
+ runtime2,
13429
13496
  "steam_me_backlog_candidates",
13430
13497
  {
13431
- title: "\u6211\u7684\u79EF\u538B\u6E38\u620F\u5019\u9009",
13432
- description: "\u4ECE\u5DF2\u62E5\u6709\u6E38\u620F\u91CC\u627E\u51FA\u51E0\u4E4E\u6CA1\u73A9\u6216\u8FD8\u6CA1\u5F00\u59CB\u7684\u5019\u9009\uFF0C\u5E76\u53EF\u9009\u9644\u5E26\u70ED\u5EA6\u548C\u65B0\u95FB\u4FE1\u53F7\u3002",
13498
+ title: "\u6211\u7684\u79EF\u538B\u5019\u9009",
13499
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u4ECE\u6211\u7684\u5E93\u91CC\u6311\u51FA\u503C\u5F97\u5F00\u5751\u7684\u79EF\u538B\u6E38\u620F\u3002",
13433
13500
  inputSchema: {
13434
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13435
- limit: z4.number().int().min(1).max(30).optional().describe("\u6700\u591A\u8FD4\u56DE\u591A\u5C11\u4E2A\u79EF\u538B\u5019\u9009\uFF0C\u9ED8\u8BA4 12\u3002"),
13436
- maxPlaytimeHours: z4.number().min(0).max(20).optional().describe("\u53EA\u5305\u542B\u6E38\u73A9\u65F6\u957F\u4E0D\u8D85\u8FC7\u8BE5\u5C0F\u65F6\u6570\u7684\u6E38\u620F\uFF0C\u9ED8\u8BA4 2\u3002"),
13437
- minDaysSinceLastPlayed: z4.number().int().min(0).max(3650).optional().describe("\u53EA\u5305\u542B\u81F3\u5C11\u8FD9\u4E48\u591A\u5929\u6CA1\u6709\u6E38\u73A9\u7684\u6E38\u620F\uFF0C\u9ED8\u8BA4 30\u3002"),
13438
- includeCurrentPlayers: z4.boolean().optional().describe("\u662F\u5426\u4E3A\u5019\u9009\u6E38\u620F\u9644\u5E26\u5F53\u524D\u5168\u7403\u5728\u7EBF\u4EBA\u6570\uFF0C\u9ED8\u8BA4 true\u3002"),
13439
- includeNews: z4.boolean().optional().describe("\u662F\u5426\u4E3A\u5019\u9009\u6E38\u620F\u9644\u5E26\u4E00\u6761\u6700\u8FD1\u65B0\u95FB\uFF0C\u9ED8\u8BA4 false\u3002"),
13440
- preferCommunityVisibleStats: z4.boolean().optional().describe("\u662F\u5426\u8BA9\u5E26\u793E\u533A\u53EF\u89C1\u7EDF\u8BA1\u7684\u6E38\u620F\u83B7\u5F97\u66F4\u9AD8\u6392\u5E8F\u6743\u91CD\uFF0C\u9ED8\u8BA4 true\u3002")
13501
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13502
+ limit: z3.number().int().min(1).max(30).optional().describe("\u6700\u591A\u8FD4\u56DE\u591A\u5C11\u4E2A\u5019\u9009\uFF0C\u9ED8\u8BA4 12\u3002"),
13503
+ maxPlaytimeHours: z3.number().min(0).max(200).optional().describe("\u53EA\u5305\u542B\u6E38\u73A9\u65F6\u957F\u4E0D\u8D85\u8FC7\u8BE5\u5C0F\u65F6\u6570\u7684\u6E38\u620F\uFF0C\u9ED8\u8BA4 2\u3002"),
13504
+ minDaysSinceLastPlayed: z3.number().int().min(0).max(3650).optional().describe("\u53EA\u5305\u542B\u81F3\u5C11\u8FD9\u4E48\u591A\u5929\u6CA1\u73A9\u7684\u6E38\u620F\uFF0C\u9ED8\u8BA4 30\u3002"),
13505
+ includeCurrentPlayers: z3.boolean().optional().describe("\u662F\u5426\u8865\u5145\u5F53\u524D\u5728\u7EBF\u4EBA\u6570\uFF0C\u9ED8\u8BA4 true\u3002"),
13506
+ includeNews: z3.boolean().optional().describe("\u662F\u5426\u8865\u5145\u4E00\u6761\u6700\u8FD1\u65B0\u95FB\uFF0C\u9ED8\u8BA4 false\u3002"),
13507
+ preferCommunityVisibleStats: z3.boolean().optional().describe("\u662F\u5426\u4F18\u5148\u6709\u793E\u533A\u7EDF\u8BA1\u7684\u6E38\u620F\uFF0C\u9ED8\u8BA4 true\u3002")
13441
13508
  },
13442
13509
  annotations: {
13443
13510
  readOnlyHint: true,
@@ -13453,43 +13520,36 @@ function registerMyAccountTools(server, runtime2) {
13453
13520
  includeNews = false,
13454
13521
  preferCommunityVisibleStats = true
13455
13522
  }) => {
13456
- try {
13457
- runtime2.requireConfiguredDefaultIdentity("steam_me_backlog_candidates");
13458
- const payload = await buildBacklogCandidatesData(runtime2, {
13459
- toolName: "steam_me_backlog_candidates",
13460
- key,
13461
- limit,
13462
- maxPlaytimeHours,
13463
- minDaysSinceLastPlayed,
13464
- includeCurrentPlayers,
13465
- includeNews,
13466
- preferCommunityVisibleStats
13467
- });
13468
- return runtime2.createJsonToolResult(payload);
13469
- } catch (error) {
13470
- return runtime2.createErrorToolResult("steam_me_backlog_candidates", error);
13471
- }
13523
+ runtime2.requireConfiguredDefaultIdentity("steam_me_backlog_candidates");
13524
+ return buildBacklogCandidatesData(runtime2, {
13525
+ toolName: "steam_me_backlog_candidates",
13526
+ key,
13527
+ limit,
13528
+ maxPlaytimeHours,
13529
+ minDaysSinceLastPlayed,
13530
+ includeCurrentPlayers,
13531
+ includeNews,
13532
+ preferCommunityVisibleStats
13533
+ });
13472
13534
  }
13473
13535
  );
13474
- server.registerTool(
13536
+ registerSteamTool(
13537
+ server,
13538
+ runtime2,
13475
13539
  "steam_me_achievement_hunt",
13476
13540
  {
13477
- title: "\u6211\u7684\u6210\u5C31\u72E9\u730E",
13478
- description: "\u4ECE\u4F60\u7684\u6E38\u620F\u5E93\u91CC\u6311\u51FA\u6700\u63A5\u8FD1\u5168\u6210\u5C31\u5B8C\u6210\u7684\u5019\u9009\u6E38\u620F\u3002",
13541
+ title: "\u6211\u7684\u6210\u5C31\u8865\u5B8C\u5019\u9009",
13542
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u627E\u51FA\u79BB\u5168\u6210\u5C31\u4E0D\u8FDC\u7684\u6E38\u620F\u3002",
13479
13543
  inputSchema: {
13480
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13481
- language: z4.string().optional().describe("\u53EF\u9009\u7684\u6210\u5C31 schema \u8BED\u8A00\u4EE3\u7801\u3002"),
13482
- source: z4.enum(["recent", "topOwned", "mixed", "explicit"]).optional().describe("\u5982\u4F55\u9009\u62E9\u8981\u68C0\u67E5\u7684\u6E38\u620F\u6765\u6E90\uFF0C\u9ED8\u8BA4 'mixed'\u3002"),
13483
- appids: z4.union([
13484
- z4.string(),
13485
- z4.number(),
13486
- z4.array(z4.union([z4.string(), z4.number()]))
13487
- ]).optional().describe("\u663E\u5F0F\u6307\u5B9A\u8981\u68C0\u67E5\u7684 AppID\uFF1B\u5F53 source \u4E3A 'explicit' \u65F6\u5FC5\u586B\u3002"),
13488
- inspectCount: z4.number().int().min(1).max(30).optional().describe("\u5728\u7B5B\u9009\u524D\u6700\u591A\u68C0\u67E5\u591A\u5C11\u4E2A\u6E38\u620F\uFF0C\u9ED8\u8BA4 12\u3002"),
13489
- limit: z4.number().int().min(1).max(20).optional().describe("\u6700\u591A\u8FD4\u56DE\u591A\u5C11\u4E2A\u6210\u5C31\u72E9\u730E\u5019\u9009\uFF0C\u9ED8\u8BA4 8\u3002"),
13490
- minCompletionRate: z4.number().min(0).max(100).optional().describe("\u53EA\u5305\u542B\u5B8C\u6210\u7387\u4E0D\u4F4E\u4E8E\u8BE5\u503C\u7684\u6E38\u620F\uFF0C\u9ED8\u8BA4 50\u3002"),
13491
- maxRemainingAchievements: z4.number().int().min(0).max(200).optional().describe("\u53EA\u5305\u542B\u5269\u4F59\u672A\u89E3\u9501\u6210\u5C31\u4E0D\u8D85\u8FC7\u8BE5\u6570\u91CF\u7684\u6E38\u620F\uFF0C\u9ED8\u8BA4 20\u3002"),
13492
- recentUnlockedCount: z4.number().int().min(1).max(20).optional().describe("\u6BCF\u4E2A\u6E38\u620F\u4FDD\u7559\u591A\u5C11\u6761\u6700\u8FD1\u89E3\u9501\u6210\u5C31\uFF0C\u9ED8\u8BA4 5\u3002")
13544
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13545
+ language: z3.string().optional().describe("\u8BED\u8A00\u4EE3\u7801\uFF0C\u4F8B\u5982 schinese \u6216 english\u3002"),
13546
+ source: z3.enum(["recent", "topOwned", "mixed", "explicit"]).optional().describe("\u9009\u62E9\u8981\u68C0\u67E5\u7684\u6E38\u620F\u6765\u6E90\uFF0C\u9ED8\u8BA4 mixed\u3002"),
13547
+ appids: z3.union([z3.string(), z3.number(), z3.array(z3.union([z3.string(), z3.number()]))]).optional().describe("\u663E\u5F0F\u6307\u5B9A\u8981\u68C0\u67E5\u7684 AppID\uFF1B\u5F53 source=explicit \u65F6\u4F7F\u7528\u3002"),
13548
+ inspectCount: z3.number().int().min(1).max(50).optional().describe("\u7B5B\u9009\u524D\u6700\u591A\u68C0\u67E5\u591A\u5C11\u4E2A\u6E38\u620F\uFF0C\u9ED8\u8BA4 12\u3002"),
13549
+ limit: z3.number().int().min(1).max(20).optional().describe("\u6700\u591A\u8FD4\u56DE\u591A\u5C11\u4E2A\u5019\u9009\uFF0C\u9ED8\u8BA4 8\u3002"),
13550
+ minCompletionRate: z3.number().min(0).max(100).optional().describe("\u53EA\u5305\u542B\u5B8C\u6210\u7387\u4E0D\u4F4E\u4E8E\u8BE5\u503C\u7684\u6E38\u620F\uFF0C\u9ED8\u8BA4 50\u3002"),
13551
+ maxRemainingAchievements: z3.number().int().min(0).max(200).optional().describe("\u53EA\u5305\u542B\u5269\u4F59\u672A\u89E3\u9501\u6210\u5C31\u4E0D\u8D85\u8FC7\u8BE5\u6570\u91CF\u7684\u6E38\u620F\uFF0C\u9ED8\u8BA4 20\u3002"),
13552
+ recentUnlockedCount: z3.number().int().min(1).max(50).optional().describe("\u6700\u8FD1\u89E3\u9501\u6210\u5C31\u7684\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 5\u3002")
13493
13553
  },
13494
13554
  annotations: {
13495
13555
  readOnlyHint: true,
@@ -13507,49 +13567,49 @@ function registerMyAccountTools(server, runtime2) {
13507
13567
  maxRemainingAchievements = 20,
13508
13568
  recentUnlockedCount = 5
13509
13569
  }) => {
13510
- try {
13511
- runtime2.requireConfiguredDefaultIdentity("steam_me_achievement_hunt");
13512
- const payload = await buildAchievementHuntData(runtime2, {
13513
- toolName: "steam_me_achievement_hunt",
13514
- key,
13515
- language,
13516
- source,
13517
- appids,
13518
- inspectCount,
13519
- limit,
13520
- minCompletionRate,
13521
- maxRemainingAchievements,
13522
- recentUnlockedCount
13523
- });
13524
- return runtime2.createJsonToolResult(payload, payload.ok === false);
13525
- } catch (error) {
13526
- return runtime2.createErrorToolResult("steam_me_achievement_hunt", error);
13527
- }
13570
+ runtime2.requireConfiguredDefaultIdentity("steam_me_achievement_hunt");
13571
+ return buildAchievementHuntData(runtime2, {
13572
+ toolName: "steam_me_achievement_hunt",
13573
+ key,
13574
+ language,
13575
+ source,
13576
+ appids,
13577
+ inspectCount,
13578
+ limit,
13579
+ minCompletionRate,
13580
+ maxRemainingAchievements,
13581
+ recentUnlockedCount
13582
+ });
13528
13583
  }
13529
13584
  );
13530
- server.registerTool(
13585
+ registerSteamTool(
13586
+ server,
13587
+ runtime2,
13531
13588
  "steam_me_game_snapshot",
13532
13589
  {
13533
13590
  title: "\u6211\u7684\u6E38\u620F\u5FEB\u7167",
13534
- description: "\u9488\u5BF9\u5355\u4E2A AppID \u805A\u5408\u6211\u662F\u5426\u62E5\u6709\u3001\u6E38\u73A9\u65F6\u957F\u3001\u6210\u5C31\u3001\u7EDF\u8BA1\u3001\u65B0\u95FB\u548C\u5F53\u524D\u5728\u7EBF\u4EBA\u6570\u3002",
13591
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u6211\u4E0E\u67D0\u4E2A\u6E38\u620F\u7684\u5173\u7CFB\u3001\u65B0\u95FB\u548C\u6210\u5C31\u3002",
13535
13592
  inputSchema: {
13536
- appid: z4.union([z4.string(), z4.number()]).describe("\u8981\u67E5\u770B\u7684 Steam AppID\u3002"),
13537
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13538
- language: z4.string().optional().describe("\u53EF\u9009\u8BED\u8A00\u4EE3\u7801\uFF0C\u4F8B\u5982 'schinese' \u6216 'english'\u3002"),
13539
- includeAchievements: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u4E2A\u4EBA\u6210\u5C31\u660E\u7EC6\uFF0C\u9ED8\u8BA4 true\u3002"),
13540
- includeStats: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u4E2A\u4EBA\u6E38\u620F\u7EDF\u8BA1\uFF0C\u9ED8\u8BA4 true\u3002"),
13541
- includeNews: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6700\u8FD1 Steam \u65B0\u95FB\uFF0C\u9ED8\u8BA4 true\u3002"),
13542
- includePlayerCount: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u5F53\u524D\u5168\u7403\u5728\u7EBF\u4EBA\u6570\uFF0C\u9ED8\u8BA4 true\u3002"),
13543
- newsCount: z4.number().int().min(1).max(10).optional().describe("\u8FD4\u56DE\u591A\u5C11\u6761\u65B0\u95FB\uFF0C\u9ED8\u8BA4 3\u3002"),
13544
- newsMaxLength: z4.number().int().min(0).max(1e3).optional().describe("\u6BCF\u6761\u65B0\u95FB\u5185\u5BB9\u7684\u6700\u5927\u957F\u5EA6\uFF0C\u9ED8\u8BA4 180\u3002")
13593
+ appid: z3.union([z3.string(), z3.number()]).optional().describe("Steam AppID\u3002\u4E0E query \u4E8C\u9009\u4E00\u3002"),
13594
+ query: z3.union([z3.string(), z3.number()]).optional().describe("\u6E38\u620F\u540D\u6216 AppID\uFF0C\u652F\u6301\u76F4\u63A5\u8F93\u5165\u4E2D\u6587\u540D\u3002\u4E0E appid \u4E8C\u9009\u4E00\u3002"),
13595
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13596
+ language: z3.string().optional().describe("\u8BED\u8A00\u4EE3\u7801\uFF0C\u4F8B\u5982 schinese \u6216 english\u3002"),
13597
+ includeAchievements: z3.boolean().optional().describe("\u662F\u5426\u5305\u542B\u4E2A\u4EBA\u6210\u5C31\uFF0C\u9ED8\u8BA4 true\u3002"),
13598
+ includeStats: z3.boolean().optional().describe("\u662F\u5426\u5305\u542B\u4E2A\u4EBA\u7EDF\u8BA1\uFF0C\u9ED8\u8BA4 true\u3002"),
13599
+ includeNews: z3.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6700\u8FD1\u65B0\u95FB\uFF0C\u9ED8\u8BA4 true\u3002"),
13600
+ includePlayerCount: z3.boolean().optional().describe("\u662F\u5426\u5305\u542B\u5F53\u524D\u5728\u7EBF\u4EBA\u6570\uFF0C\u9ED8\u8BA4 true\u3002"),
13601
+ newsCount: z3.number().int().min(1).max(10).optional().describe("\u65B0\u95FB\u8FD4\u56DE\u6761\u6570\uFF0C\u9ED8\u8BA4 3\u3002"),
13602
+ newsMaxLength: z3.number().int().min(0).max(5e3).optional().describe("\u6BCF\u6761\u65B0\u95FB\u6700\u5927\u957F\u5EA6\uFF0C\u9ED8\u8BA4 180\u3002")
13545
13603
  },
13546
13604
  annotations: {
13547
13605
  readOnlyHint: true,
13548
13606
  openWorldHint: false
13549
13607
  }
13550
13608
  },
13609
+ // 个人单游戏主入口;需要更窄的数据时再走成就或动态等专项工具。
13551
13610
  async ({
13552
13611
  appid,
13612
+ query,
13553
13613
  key,
13554
13614
  language,
13555
13615
  includeAchievements = true,
@@ -13559,76 +13619,45 @@ function registerMyAccountTools(server, runtime2) {
13559
13619
  newsCount = 3,
13560
13620
  newsMaxLength = 180
13561
13621
  }) => {
13562
- try {
13563
- runtime2.requireConfiguredDefaultIdentity("steam_me_game_snapshot");
13564
- const payload = await buildGameSnapshotData(runtime2, {
13565
- toolName: "steam_me_game_snapshot",
13566
- appid,
13567
- key,
13568
- language,
13569
- includeAchievements,
13570
- includeStats,
13571
- includeNews,
13572
- includePlayerCount,
13573
- newsCount,
13574
- newsMaxLength
13575
- });
13576
- return runtime2.createJsonToolResult(payload);
13577
- } catch (error) {
13578
- return runtime2.createErrorToolResult("steam_me_game_snapshot", error);
13579
- }
13580
- }
13581
- );
13582
- server.registerTool(
13583
- "steam_me_groups",
13584
- {
13585
- title: "\u6211\u7684 Steam \u7FA4\u7EC4",
13586
- description: "\u5FEB\u6377\u67E5\u770B\u5DF2\u914D\u7F6E\u9ED8\u8BA4\u8D26\u53F7\u7684\u7FA4\u7EC4\u5217\u8868\u3002\u6CE8\u610F\uFF1ASteam \u6587\u6863\u628A\u5E95\u5C42\u63A5\u53E3\u6807\u8BB0\u4E3A publisher-key-only\u3002",
13587
- inputSchema: {
13588
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002")
13589
- },
13590
- annotations: {
13591
- readOnlyHint: true,
13592
- openWorldHint: false
13593
- }
13594
- },
13595
- async ({ key }) => {
13596
- try {
13597
- runtime2.requireConfiguredDefaultIdentity("steam_me_groups");
13598
- const payload = await buildGroupsData(runtime2, {
13599
- toolName: "steam_me_groups",
13600
- key
13601
- });
13602
- return runtime2.createJsonToolResult(payload, payload.ok === false);
13603
- } catch (error) {
13604
- return runtime2.createErrorToolResult("steam_me_groups", error);
13605
- }
13622
+ runtime2.requireConfiguredDefaultIdentity("steam_me_game_snapshot");
13623
+ return buildGameSnapshotData(runtime2, {
13624
+ toolName: "steam_me_game_snapshot",
13625
+ appid,
13626
+ query,
13627
+ key,
13628
+ language,
13629
+ includeAchievements,
13630
+ includeStats,
13631
+ includeNews,
13632
+ includePlayerCount,
13633
+ newsCount,
13634
+ newsMaxLength
13635
+ });
13606
13636
  }
13607
13637
  );
13608
- server.registerTool(
13609
- "steam_me_wishlist_like_feed",
13638
+ registerSteamTool(
13639
+ server,
13640
+ runtime2,
13641
+ "steam_me_game_feed",
13610
13642
  {
13611
- title: "\u6211\u7684 Steam \u5173\u6CE8\u6D41",
13612
- description: "\u4EE5\u5173\u6CE8\u6D41\u7684\u5F62\u5F0F\u67E5\u770B\u4F60\u6700\u8FD1\u73A9\u8FC7\u6216\u5173\u5FC3\u7684\u5E94\u7528\uFF0C\u9ED8\u8BA4\u6309\u6700\u8FD1\u6E38\u73A9\u7684 AppID \u805A\u5408\u65B0\u95FB\u548C\u70ED\u5EA6\u3002",
13643
+ title: "\u6211\u7684\u6E38\u620F\u52A8\u6001\u6D41",
13644
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u6309\u6700\u8FD1\u6E38\u73A9\u3001\u5E38\u73A9\u6216\u663E\u5F0F App \u751F\u6210\u4E2A\u4EBA\u6E38\u620F\u52A8\u6001\u6D41\u3002",
13613
13645
  inputSchema: {
13614
- key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13615
- source: z4.enum(["recent", "topOwned", "mixed", "explicit"]).optional().describe("\u5982\u4F55\u4E3A\u5173\u6CE8\u6D41\u9009\u62E9\u5E94\u7528\u6765\u6E90\uFF0C\u9ED8\u8BA4 'recent'\u3002"),
13616
- appids: z4.union([
13617
- z4.string(),
13618
- z4.number(),
13619
- z4.array(z4.union([z4.string(), z4.number()]))
13620
- ]).optional().describe("\u663E\u5F0F\u6307\u5B9A\u8981\u5173\u6CE8\u7684 appid\uFF1B\u5F53 source \u4E3A 'explicit' \u65F6\u5FC5\u586B\u3002"),
13621
- limitApps: z4.number().int().min(1).max(20).optional().describe("\u6700\u591A\u5305\u542B\u591A\u5C11\u4E2A\u5E94\u7528\uFF0C\u9ED8\u8BA4 5\u3002"),
13622
- newsPerApp: z4.number().int().min(1).max(10).optional().describe("\u6BCF\u4E2A\u5E94\u7528\u6293\u53D6\u591A\u5C11\u6761\u65B0\u95FB\uFF0C\u9ED8\u8BA4 2\u3002"),
13623
- newsMaxLength: z4.number().int().min(0).max(500).optional().describe("\u6BCF\u6761\u65B0\u95FB\u5185\u5BB9\u7684\u6700\u5927\u957F\u5EA6\uFF0C\u9ED8\u8BA4 160\u3002"),
13624
- includePlayerCount: z4.boolean().optional().describe("\u662F\u5426\u4E3A\u6BCF\u4E2A\u5E94\u7528\u9644\u5E26\u5F53\u524D\u5728\u7EBF\u4EBA\u6570\uFF0C\u9ED8\u8BA4 true\u3002"),
13625
- includeOwnedContext: z4.boolean().optional().describe("\u662F\u5426\u9644\u5E26\u4F60\u662F\u5426\u62E5\u6709\u8BE5\u5E94\u7528\u4EE5\u53CA\u4F60\u7684\u6E38\u73A9\u65F6\u957F\uFF0C\u9ED8\u8BA4 true\u3002")
13646
+ key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13647
+ source: z3.enum(["recent", "topOwned", "mixed", "explicit"]).optional().describe("\u52A8\u6001\u6D41\u7684\u6E38\u620F\u6765\u6E90\uFF0C\u9ED8\u8BA4 recent\u3002"),
13648
+ appids: z3.union([z3.string(), z3.number(), z3.array(z3.union([z3.string(), z3.number()]))]).optional().describe("\u663E\u5F0F\u6307\u5B9A AppID\uFF1B\u5F53 source=explicit \u65F6\u4F7F\u7528\u3002"),
13649
+ limitApps: z3.number().int().min(1).max(20).optional().describe("\u6700\u591A\u5305\u542B\u591A\u5C11\u4E2A\u6E38\u620F\uFF0C\u9ED8\u8BA4 5\u3002"),
13650
+ newsPerApp: z3.number().int().min(1).max(10).optional().describe("\u6BCF\u4E2A\u6E38\u620F\u6293\u53D6\u591A\u5C11\u6761\u65B0\u95FB\uFF0C\u9ED8\u8BA4 2\u3002"),
13651
+ newsMaxLength: z3.number().int().min(0).max(5e3).optional().describe("\u6BCF\u6761\u65B0\u95FB\u6700\u5927\u957F\u5EA6\uFF0C\u9ED8\u8BA4 160\u3002"),
13652
+ includePlayerCount: z3.boolean().optional().describe("\u662F\u5426\u9644\u5E26\u5F53\u524D\u5728\u7EBF\u4EBA\u6570\uFF0C\u9ED8\u8BA4 true\u3002"),
13653
+ includeOwnedContext: z3.boolean().optional().describe("\u662F\u5426\u9644\u5E26\u6211\u662F\u5426\u62E5\u6709\u548C\u6E38\u73A9\u65F6\u957F\uFF0C\u9ED8\u8BA4 true\u3002")
13626
13654
  },
13627
13655
  annotations: {
13628
13656
  readOnlyHint: true,
13629
13657
  openWorldHint: false
13630
13658
  }
13631
13659
  },
13660
+ // feed 只描述“我的游戏动态流”,不再暗示真实 wishlist 数据源。
13632
13661
  async ({
13633
13662
  key,
13634
13663
  source = "recent",
@@ -13639,41 +13668,38 @@ function registerMyAccountTools(server, runtime2) {
13639
13668
  includePlayerCount = true,
13640
13669
  includeOwnedContext = true
13641
13670
  }) => {
13642
- try {
13643
- runtime2.requireConfiguredDefaultIdentity("steam_me_wishlist_like_feed");
13644
- const payload = await buildWishlistLikeFeedData(runtime2, {
13645
- toolName: "steam_me_wishlist_like_feed",
13646
- key,
13647
- source,
13648
- appids,
13649
- limitApps,
13650
- newsPerApp,
13651
- newsMaxLength,
13652
- includePlayerCount,
13653
- includeOwnedContext
13654
- });
13655
- return runtime2.createJsonToolResult(payload, payload.ok === false);
13656
- } catch (error) {
13657
- return runtime2.createErrorToolResult("steam_me_wishlist_like_feed", error);
13658
- }
13671
+ runtime2.requireConfiguredDefaultIdentity("steam_me_game_feed");
13672
+ return buildGameFeedData(runtime2, {
13673
+ toolName: "steam_me_game_feed",
13674
+ key,
13675
+ source,
13676
+ appids,
13677
+ limitApps,
13678
+ newsPerApp,
13679
+ newsMaxLength,
13680
+ includePlayerCount,
13681
+ includeOwnedContext
13682
+ });
13659
13683
  }
13660
13684
  );
13661
13685
  }
13662
13686
 
13663
13687
  // src/tools/overview.ts
13664
- import * as z5 from "zod/v4";
13688
+ import * as z4 from "zod/v4";
13665
13689
  function registerOverviewTools(server, runtime2) {
13666
- server.registerTool(
13690
+ registerSteamTool(
13691
+ server,
13692
+ runtime2,
13667
13693
  "steam_get_player_profile_overview",
13668
13694
  {
13669
- title: "Steam \u73A9\u5BB6\u8D44\u6599\u603B\u89C8",
13670
- description: "\u805A\u5408\u73A9\u5BB6\u7684 Steam \u8D44\u6599\u3001\u5C01\u7981\u72B6\u6001\u3001\u7B49\u7EA7\u3001\u5FBD\u7AE0\u548C\u6700\u8FD1\u6E38\u73A9\u4FE1\u606F\u3002",
13695
+ title: "\u73A9\u5BB6\u8D44\u6599\u603B\u89C8",
13696
+ description: "\u805A\u5408\u73A9\u5BB6\u8D44\u6599\u3001\u5C01\u7981\u3001\u7B49\u7EA7\u3001\u5FBD\u7AE0\u548C\u6700\u8FD1\u6E38\u73A9\u4FE1\u606F\u3002",
13671
13697
  inputSchema: {
13672
- steamid: z5.union([z5.string(), z5.number()]).optional().describe("\u73A9\u5BB6\u7684 SteamID64\u3002\u4E0E vanityUrl \u4E8C\u9009\u4E00\uFF1B\u82E5\u90FD\u4E0D\u4F20\u4E14\u5DF2\u914D\u7F6E STEAM_DEFAULT_STEAMID\uFF0C\u5219\u9ED8\u8BA4\u4F7F\u7528\u8BE5\u503C\u3002"),
13673
- vanityUrl: z5.string().optional().describe("\u73A9\u5BB6\u7684 Steam \u81EA\u5B9A\u4E49\u4E3B\u9875\u6807\u8BC6\uFF0C\u4F8B\u5982 'gaben'\u3002\u4E0E steamid \u4E8C\u9009\u4E00\uFF1B\u82E5\u90FD\u4E0D\u4F20\u4E14\u5DF2\u914D\u7F6E STEAM_DEFAULT_STEAMID\uFF0C\u5219\u9ED8\u8BA4\u4F7F\u7528\u8BE5\u503C\u3002"),
13674
- key: z5.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13675
- includeRecentlyPlayed: z5.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6700\u8FD1\u6E38\u73A9\u7684\u6E38\u620F\uFF0C\u9ED8\u8BA4 true\u3002"),
13676
- recentGamesCount: z5.number().int().min(1).max(20).optional().describe("\u6700\u8FD1\u6E38\u73A9\u6E38\u620F\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 3\u3002")
13698
+ steamid: z4.union([z4.string(), z4.number()]).optional().describe("\u73A9\u5BB6\u7684 SteamID64\u3002\u4E0E vanityUrl \u4E8C\u9009\u4E00\u3002"),
13699
+ vanityUrl: z4.string().optional().describe("\u73A9\u5BB6\u7684\u81EA\u5B9A\u4E49\u4E3B\u9875\u6807\u8BC6\uFF0C\u4F8B\u5982 gaben\u3002\u4E0E steamid \u4E8C\u9009\u4E00\u3002"),
13700
+ key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13701
+ includeRecentlyPlayed: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6700\u8FD1\u6E38\u73A9\u6570\u636E\uFF0C\u9ED8\u8BA4 true\u3002"),
13702
+ recentGamesCount: z4.number().int().min(1).max(20).optional().describe("\u6700\u8FD1\u6E38\u73A9\u6E38\u620F\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 3\u3002")
13677
13703
  },
13678
13704
  annotations: {
13679
13705
  readOnlyHint: true,
@@ -13686,39 +13712,31 @@ function registerOverviewTools(server, runtime2) {
13686
13712
  key,
13687
13713
  includeRecentlyPlayed = true,
13688
13714
  recentGamesCount = 3
13689
- }) => {
13690
- try {
13691
- const payload = await buildPlayerProfileOverviewData(runtime2, {
13692
- toolName: "steam_get_player_profile_overview",
13693
- steamid,
13694
- vanityUrl,
13695
- key,
13696
- includeRecentlyPlayed,
13697
- recentGamesCount
13698
- });
13699
- return runtime2.createJsonToolResult(payload);
13700
- } catch (error) {
13701
- return runtime2.createErrorToolResult(
13702
- "steam_get_player_profile_overview",
13703
- error
13704
- );
13705
- }
13706
- }
13715
+ }) => buildPlayerProfileOverviewData(runtime2, {
13716
+ toolName: "steam_get_player_profile_overview",
13717
+ steamid,
13718
+ vanityUrl,
13719
+ key,
13720
+ includeRecentlyPlayed,
13721
+ recentGamesCount
13722
+ })
13707
13723
  );
13708
- server.registerTool(
13724
+ registerSteamTool(
13725
+ server,
13726
+ runtime2,
13709
13727
  "steam_get_library_overview",
13710
13728
  {
13711
- title: "Steam \u6E38\u620F\u5E93\u603B\u89C8",
13712
- description: "\u805A\u5408\u73A9\u5BB6\u62E5\u6709\u7684\u6E38\u620F\u3001\u6700\u8FD1\u6D3B\u52A8\u548C\u6309\u65F6\u957F\u6392\u5E8F\u7684\u5E38\u73A9\u6E38\u620F\u3002",
13729
+ title: "\u6E38\u620F\u5E93\u603B\u89C8",
13730
+ description: "\u805A\u5408\u73A9\u5BB6\u62E5\u6709\u7684\u6E38\u620F\u3001\u6700\u8FD1\u6E38\u73A9\u548C\u5E38\u73A9\u6E38\u620F\u3002",
13713
13731
  inputSchema: {
13714
- steamid: z5.union([z5.string(), z5.number()]).optional().describe("\u73A9\u5BB6\u7684 SteamID64\u3002\u4E0E vanityUrl \u4E8C\u9009\u4E00\uFF1B\u82E5\u90FD\u4E0D\u4F20\u4E14\u5DF2\u914D\u7F6E STEAM_DEFAULT_STEAMID\uFF0C\u5219\u9ED8\u8BA4\u4F7F\u7528\u8BE5\u503C\u3002"),
13715
- vanityUrl: z5.string().optional().describe("\u73A9\u5BB6\u7684 Steam \u81EA\u5B9A\u4E49\u4E3B\u9875\u6807\u8BC6\uFF0C\u4F8B\u5982 'gaben'\u3002\u4E0E steamid \u4E8C\u9009\u4E00\uFF1B\u82E5\u90FD\u4E0D\u4F20\u4E14\u5DF2\u914D\u7F6E STEAM_DEFAULT_STEAMID\uFF0C\u5219\u9ED8\u8BA4\u4F7F\u7528\u8BE5\u503C\u3002"),
13716
- key: z5.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13717
- includeAppInfo: z5.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6E38\u620F\u540D\u79F0\u548C\u56FE\u6807\uFF0C\u9ED8\u8BA4 true\u3002"),
13718
- includePlayedFreeGames: z5.boolean().optional().describe("\u662F\u5426\u5305\u542B\u73A9\u8FC7\u7684\u514D\u8D39\u6E38\u620F\uFF0C\u9ED8\u8BA4 true\u3002"),
13719
- includeRecentGames: z5.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6700\u8FD1\u6E38\u73A9\u7684\u6E38\u620F\uFF0C\u9ED8\u8BA4 true\u3002"),
13720
- recentGamesCount: z5.number().int().min(1).max(20).optional().describe("\u6700\u8FD1\u6E38\u73A9\u6E38\u620F\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 5\u3002"),
13721
- topGamesCount: z5.number().int().min(1).max(50).optional().describe("\u6309\u6E38\u73A9\u65F6\u957F\u6392\u5E8F\u8FD4\u56DE\u524D\u591A\u5C11\u4E2A\u6E38\u620F\uFF0C\u9ED8\u8BA4 10\u3002")
13732
+ steamid: z4.union([z4.string(), z4.number()]).optional().describe("\u73A9\u5BB6\u7684 SteamID64\u3002\u4E0E vanityUrl \u4E8C\u9009\u4E00\u3002"),
13733
+ vanityUrl: z4.string().optional().describe("\u73A9\u5BB6\u7684\u81EA\u5B9A\u4E49\u4E3B\u9875\u6807\u8BC6\uFF0C\u4F8B\u5982 gaben\u3002\u4E0E steamid \u4E8C\u9009\u4E00\u3002"),
13734
+ key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13735
+ includeAppInfo: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6E38\u620F\u540D\u79F0\u548C\u56FE\u6807\uFF0C\u9ED8\u8BA4 true\u3002"),
13736
+ includePlayedFreeGames: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u73A9\u8FC7\u7684\u514D\u8D39\u6E38\u620F\uFF0C\u9ED8\u8BA4 true\u3002"),
13737
+ includeRecentGames: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6700\u8FD1\u6E38\u73A9\uFF0C\u9ED8\u8BA4 true\u3002"),
13738
+ recentGamesCount: z4.number().int().min(1).max(20).optional().describe("\u6700\u8FD1\u6E38\u73A9\u6E38\u620F\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 5\u3002"),
13739
+ topGamesCount: z4.number().int().min(1).max(50).optional().describe("\u6309\u65F6\u957F\u6392\u5E8F\u8FD4\u56DE\u524D\u591A\u5C11\u4E2A\u6E38\u620F\uFF0C\u9ED8\u8BA4 10\u3002")
13722
13740
  },
13723
13741
  annotations: {
13724
13742
  readOnlyHint: true,
@@ -13734,38 +13752,34 @@ function registerOverviewTools(server, runtime2) {
13734
13752
  includeRecentGames = true,
13735
13753
  recentGamesCount = 5,
13736
13754
  topGamesCount = 10
13737
- }) => {
13738
- try {
13739
- const payload = await buildLibraryOverviewData(runtime2, {
13740
- toolName: "steam_get_library_overview",
13741
- steamid,
13742
- vanityUrl,
13743
- key,
13744
- includeAppInfo,
13745
- includePlayedFreeGames,
13746
- includeRecentGames,
13747
- recentGamesCount,
13748
- topGamesCount
13749
- });
13750
- return runtime2.createJsonToolResult(payload);
13751
- } catch (error) {
13752
- return runtime2.createErrorToolResult("steam_get_library_overview", error);
13753
- }
13754
- }
13755
+ }) => buildLibraryOverviewData(runtime2, {
13756
+ toolName: "steam_get_library_overview",
13757
+ steamid,
13758
+ vanityUrl,
13759
+ key,
13760
+ includeAppInfo,
13761
+ includePlayedFreeGames,
13762
+ includeRecentGames,
13763
+ recentGamesCount,
13764
+ topGamesCount
13765
+ })
13755
13766
  );
13756
- server.registerTool(
13757
- "steam_get_achievement_overview",
13767
+ registerSteamTool(
13768
+ server,
13769
+ runtime2,
13770
+ "steam_get_player_game_achievements",
13758
13771
  {
13759
- title: "Steam \u6210\u5C31\u603B\u89C8",
13760
- description: "\u805A\u5408\u67D0\u4E2A\u6E38\u620F\u7684\u73A9\u5BB6\u6210\u5C31\u3001\u6E38\u620F schema\u3001\u5168\u5C40\u5B8C\u6210\u7387\u548C\u53EF\u9009\u7684\u4E2A\u4EBA\u7EDF\u8BA1\u3002",
13772
+ title: "\u73A9\u5BB6\u6E38\u620F\u6210\u5C31",
13773
+ description: "\u67E5\u770B\u67D0\u4E2A\u73A9\u5BB6\u5728\u67D0\u4E2A\u6E38\u620F\u91CC\u7684\u6210\u5C31\u3001\u5B8C\u6210\u7387\u548C\u53EF\u9009\u7EDF\u8BA1\u3002",
13761
13774
  inputSchema: {
13762
- steamid: z5.union([z5.string(), z5.number()]).optional().describe("\u73A9\u5BB6\u7684 SteamID64\u3002\u4E0E vanityUrl \u4E8C\u9009\u4E00\uFF1B\u82E5\u90FD\u4E0D\u4F20\u4E14\u5DF2\u914D\u7F6E STEAM_DEFAULT_STEAMID\uFF0C\u5219\u9ED8\u8BA4\u4F7F\u7528\u8BE5\u503C\u3002"),
13763
- vanityUrl: z5.string().optional().describe("\u73A9\u5BB6\u7684 Steam \u81EA\u5B9A\u4E49\u4E3B\u9875\u6807\u8BC6\uFF0C\u4F8B\u5982 'gaben'\u3002\u4E0E steamid \u4E8C\u9009\u4E00\uFF1B\u82E5\u90FD\u4E0D\u4F20\u4E14\u5DF2\u914D\u7F6E STEAM_DEFAULT_STEAMID\uFF0C\u5219\u9ED8\u8BA4\u4F7F\u7528\u8BE5\u503C\u3002"),
13764
- appid: z5.union([z5.string(), z5.number()]).describe("\u8981\u67E5\u770B\u7684 Steam AppID\u3002"),
13765
- key: z5.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13766
- language: z5.string().optional().describe("\u53EF\u9009\u8BED\u8A00\u4EE3\u7801\uFF0C\u4F8B\u5982 'schinese' \u6216 'english'\u3002"),
13767
- includeStats: z5.boolean().optional().describe("\u662F\u5426\u5305\u542B\u8BE5\u6E38\u620F\u7684\u73A9\u5BB6\u7EDF\u8BA1\uFF0C\u9ED8\u8BA4 true\u3002"),
13768
- recentUnlockedCount: z5.number().int().min(1).max(50).optional().describe("\u9AD8\u4EAE\u5C55\u793A\u6700\u8FD1\u89E3\u9501\u6210\u5C31\u7684\u6570\u91CF\uFF0C\u9ED8\u8BA4 10\u3002")
13775
+ steamid: z4.union([z4.string(), z4.number()]).optional().describe("\u73A9\u5BB6\u7684 SteamID64\u3002\u4E0E vanityUrl \u4E8C\u9009\u4E00\u3002"),
13776
+ vanityUrl: z4.string().optional().describe("\u73A9\u5BB6\u7684\u81EA\u5B9A\u4E49\u4E3B\u9875\u6807\u8BC6\uFF0C\u4F8B\u5982 gaben\u3002\u4E0E steamid \u4E8C\u9009\u4E00\u3002"),
13777
+ appid: z4.union([z4.string(), z4.number()]).optional().describe("Steam AppID\u3002\u4E0E query \u4E8C\u9009\u4E00\u3002"),
13778
+ query: z4.union([z4.string(), z4.number()]).optional().describe("\u6E38\u620F\u540D\u6216 AppID\uFF0C\u652F\u6301\u76F4\u63A5\u8F93\u5165\u4E2D\u6587\u540D\u3002\u4E0E appid \u4E8C\u9009\u4E00\u3002"),
13779
+ key: z4.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
13780
+ language: z4.string().optional().describe("\u8BED\u8A00\u4EE3\u7801\uFF0C\u4F8B\u5982 schinese \u6216 english\u3002"),
13781
+ includeStats: z4.boolean().optional().describe("\u662F\u5426\u5305\u542B\u73A9\u5BB6\u7EDF\u8BA1\uFF0C\u9ED8\u8BA4 true\u3002"),
13782
+ recentUnlockedCount: z4.number().int().min(1).max(50).optional().describe("\u6700\u8FD1\u89E3\u9501\u6210\u5C31\u7684\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 10\u3002")
13769
13783
  },
13770
13784
  annotations: {
13771
13785
  readOnlyHint: true,
@@ -13776,40 +13790,38 @@ function registerOverviewTools(server, runtime2) {
13776
13790
  steamid,
13777
13791
  vanityUrl,
13778
13792
  appid,
13793
+ query,
13779
13794
  key,
13780
13795
  language,
13781
13796
  includeStats = true,
13782
13797
  recentUnlockedCount = 10
13783
- }) => {
13784
- try {
13785
- const payload = await buildAchievementOverviewData(runtime2, {
13786
- toolName: "steam_get_achievement_overview",
13787
- steamid,
13788
- vanityUrl,
13789
- appid,
13790
- key,
13791
- language,
13792
- includeStats,
13793
- recentUnlockedCount
13794
- });
13795
- return runtime2.createJsonToolResult(payload);
13796
- } catch (error) {
13797
- return runtime2.createErrorToolResult("steam_get_achievement_overview", error);
13798
- }
13799
- }
13798
+ }) => buildPlayerGameAchievementsData(runtime2, {
13799
+ toolName: "steam_get_player_game_achievements",
13800
+ steamid,
13801
+ vanityUrl,
13802
+ appid,
13803
+ query,
13804
+ key,
13805
+ language,
13806
+ includeStats,
13807
+ recentUnlockedCount
13808
+ })
13800
13809
  );
13801
- server.registerTool(
13802
- "steam_get_news_overview",
13810
+ registerSteamTool(
13811
+ server,
13812
+ runtime2,
13813
+ "steam_get_app_news",
13803
13814
  {
13804
- title: "Steam \u65B0\u95FB\u603B\u89C8",
13805
- description: "\u805A\u5408\u67D0\u4E2A\u5E94\u7528\u7684 Steam \u65B0\u95FB\uFF0C\u5E76\u53EF\u9009\u9644\u5E26\u5F53\u524D\u5728\u7EBF\u4EBA\u6570\u3002",
13815
+ title: "\u6E38\u620F\u65B0\u95FB",
13816
+ description: "\u67E5\u770B\u67D0\u4E2A\u6E38\u620F\u7684 Steam \u65B0\u95FB\uFF0C\u5E76\u53EF\u9009\u9644\u5E26\u5F53\u524D\u5728\u7EBF\u4EBA\u6570\u3002",
13806
13817
  inputSchema: {
13807
- appid: z5.union([z5.string(), z5.number()]).describe("\u8981\u67E5\u770B\u7684 Steam AppID\u3002"),
13808
- count: z5.number().int().min(1).max(20).optional().describe("\u8FD4\u56DE\u591A\u5C11\u6761\u65B0\u95FB\uFF0C\u9ED8\u8BA4 5\u3002"),
13809
- maxlength: z5.number().int().min(0).max(5e3).optional().describe("\u6BCF\u6761\u65B0\u95FB\u5185\u5BB9\u7684\u6700\u5927\u957F\u5EA6\uFF0C\u9ED8\u8BA4 300\u3002"),
13810
- enddate: z5.number().int().optional().describe("\u53EF\u9009\u7684 Unix \u65F6\u95F4\u6233\u4E0A\u9650\uFF0C\u53EA\u8FD4\u56DE\u66F4\u65E9\u7684\u65B0\u95FB\u3002"),
13811
- feeds: z5.union([z5.string(), z5.array(z5.string())]).optional().describe("\u53EF\u9009\u7684 feed \u540D\u79F0\u6216 feed \u5217\u8868\u3002"),
13812
- includePlayerCount: z5.boolean().optional().describe("\u662F\u5426\u5305\u542B\u5F53\u524D\u5728\u7EBF\u4EBA\u6570\uFF0C\u9ED8\u8BA4 true\u3002")
13818
+ appid: z4.union([z4.string(), z4.number()]).optional().describe("Steam AppID\u3002\u4E0E query \u4E8C\u9009\u4E00\u3002"),
13819
+ query: z4.union([z4.string(), z4.number()]).optional().describe("\u6E38\u620F\u540D\u6216 AppID\uFF0C\u652F\u6301\u76F4\u63A5\u8F93\u5165\u4E2D\u6587\u540D\u3002\u4E0E appid \u4E8C\u9009\u4E00\u3002"),
13820
+ count: z4.number().int().min(1).max(20).optional().describe("\u8FD4\u56DE\u65B0\u95FB\u6761\u6570\uFF0C\u9ED8\u8BA4 5\u3002"),
13821
+ maxlength: z4.number().int().min(0).max(5e3).optional().describe("\u6BCF\u6761\u65B0\u95FB\u5185\u5BB9\u6700\u5927\u957F\u5EA6\uFF0C\u9ED8\u8BA4 300\u3002"),
13822
+ enddate: z4.number().int().optional().describe("\u53EF\u9009\u7684 Unix \u65F6\u95F4\u6233\u4E0A\u9650\u3002"),
13823
+ feeds: z4.union([z4.string(), z4.array(z4.string())]).optional().describe("\u53EF\u9009\u7684 feed \u540D\u79F0\u6216\u5217\u8868\u3002"),
13824
+ includePlayerCount: z4.boolean().optional().describe("\u662F\u5426\u9644\u5E26\u5F53\u524D\u5728\u7EBF\u4EBA\u6570\uFF0C\u9ED8\u8BA4 true\u3002")
13813
13825
  },
13814
13826
  annotations: {
13815
13827
  readOnlyHint: true,
@@ -13818,34 +13830,27 @@ function registerOverviewTools(server, runtime2) {
13818
13830
  },
13819
13831
  async ({
13820
13832
  appid,
13833
+ query,
13821
13834
  count = 5,
13822
13835
  maxlength = 300,
13823
13836
  enddate,
13824
13837
  feeds,
13825
13838
  includePlayerCount = true
13826
- }) => {
13827
- try {
13828
- const payload = await buildNewsOverviewData(runtime2, {
13829
- toolName: "steam_get_news_overview",
13830
- appid,
13831
- count,
13832
- maxlength,
13833
- enddate,
13834
- feeds,
13835
- includePlayerCount
13836
- });
13837
- return runtime2.createJsonToolResult(payload);
13838
- } catch (error) {
13839
- return runtime2.createErrorToolResult("steam_get_news_overview", error);
13840
- }
13841
- }
13839
+ }) => buildAppNewsData(runtime2, {
13840
+ toolName: "steam_get_app_news",
13841
+ appid,
13842
+ query,
13843
+ count,
13844
+ maxlength,
13845
+ enddate,
13846
+ feeds,
13847
+ includePlayerCount
13848
+ })
13842
13849
  );
13843
13850
  }
13844
13851
 
13845
13852
  // src/server.ts
13846
13853
  var spec = applyGeneratedToolTitles(steam_web_api_spec_default);
13847
- var generatedToolsEnabled = toBoolean(process.env.STEAM_ENABLE_GENERATED_TOOLS) ?? false;
13848
- var helperToolCount = 4;
13849
13854
  var runtime = createSteamBaseRuntime(spec, {
13850
13855
  apiKey: process.env.STEAM_WEB_API_KEY,
13851
13856
  defaultSteamId: normalizeScalarInput(process.env.STEAM_DEFAULT_STEAMID),
@@ -13855,34 +13860,22 @@ var runtime = createSteamBaseRuntime(spec, {
13855
13860
  ),
13856
13861
  timeoutMs: Number.parseInt(process.env.STEAM_REQUEST_TIMEOUT_MS ?? "30000", 10)
13857
13862
  });
13858
- function createAdvancedToolsStatus() {
13859
- if (!generatedToolsEnabled) {
13860
- return "advanced tools: disabled";
13861
- }
13862
- return `advanced tools: enabled (${helperToolCount} helper + ${runtime.spec.methodCount} generated)`;
13863
- }
13864
13863
  function createServer() {
13865
13864
  const server = new McpServer({
13866
13865
  name: "steam-tools-mcp",
13867
- version: "0.1.0",
13866
+ version: "0.2.0",
13868
13867
  websiteUrl: "https://partner.steamgames.com/doc/webapi"
13869
13868
  });
13870
13869
  registerOverviewTools(server, runtime);
13871
13870
  registerAppTools(server, runtime);
13872
13871
  registerMyAccountTools(server, runtime);
13873
- if (generatedToolsEnabled) {
13874
- registerHelperTools(server, runtime);
13875
- registerGeneratedTools(server, runtime);
13876
- }
13877
13872
  return server;
13878
13873
  }
13879
13874
  async function startStdioServer() {
13880
13875
  const server = createServer();
13881
13876
  const transport = new StdioServerTransport();
13882
13877
  await server.connect(transport);
13883
- console.error(
13884
- `steam-tools-mcp running over stdio. ${createAdvancedToolsStatus()}.`
13885
- );
13878
+ console.error("steam-tools-mcp running over stdio.");
13886
13879
  }
13887
13880
  async function handleHttpRequest(req, res) {
13888
13881
  const server = createServer();
@@ -13920,9 +13913,7 @@ async function startHttpServer() {
13920
13913
  ok: true,
13921
13914
  generatedAt: runtime.spec.generatedAt,
13922
13915
  methodCount: runtime.spec.methodCount,
13923
- interfaceCount: runtime.spec.interfaceCount,
13924
- generatedToolsEnabled,
13925
- enabledGeneratedToolCount: generatedToolsEnabled ? runtime.spec.methodCount : 0
13916
+ interfaceCount: runtime.spec.interfaceCount
13926
13917
  });
13927
13918
  });
13928
13919
  app.post("/mcp", async (req, res) => {
@@ -13954,9 +13945,7 @@ async function startHttpServer() {
13954
13945
  reject(error);
13955
13946
  return;
13956
13947
  }
13957
- console.log(
13958
- `steam-tools-mcp listening on http://${host}:${port}/mcp. ${createAdvancedToolsStatus()}.`
13959
- );
13948
+ console.log(`steam-tools-mcp listening on http://${host}:${port}/mcp.`);
13960
13949
  resolve();
13961
13950
  });
13962
13951
  server.on("error", reject);