@xiawan-play/steam-tools-mcp 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +70 -48
  2. package/dist/server.js +847 -751
  3. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -19504,8 +19504,8 @@ var require_subresource_integrity = __commonJS({
19504
19504
  if (parsedMetadata.length === 0) {
19505
19505
  return true;
19506
19506
  }
19507
- const metadata = getStrongestMetadata(parsedMetadata);
19508
- for (const item of metadata) {
19507
+ const metadata2 = getStrongestMetadata(parsedMetadata);
19508
+ for (const item of metadata2) {
19509
19509
  const algorithm = item.alg;
19510
19510
  const expectedValue = item.val;
19511
19511
  const actualValue = applyAlgorithmToBytes(algorithm, bytes);
@@ -19544,9 +19544,9 @@ var require_subresource_integrity = __commonJS({
19544
19544
  }
19545
19545
  return result;
19546
19546
  }
19547
- function parseMetadata(metadata) {
19547
+ function parseMetadata(metadata2) {
19548
19548
  const result = [];
19549
- for (const item of metadata.split(" ")) {
19549
+ for (const item of metadata2.split(" ")) {
19550
19550
  const expressionAndOptions = item.split("?", 1);
19551
19551
  const algorithmExpression = expressionAndOptions[0];
19552
19552
  let base64Value = "";
@@ -19558,11 +19558,11 @@ var require_subresource_integrity = __commonJS({
19558
19558
  if (algorithmAndValue[1]) {
19559
19559
  base64Value = algorithmAndValue[1];
19560
19560
  }
19561
- const metadata2 = {
19561
+ const metadata3 = {
19562
19562
  alg: algorithm,
19563
19563
  val: base64Value
19564
19564
  };
19565
- result.push(metadata2);
19565
+ result.push(metadata3);
19566
19566
  }
19567
19567
  return result;
19568
19568
  }
@@ -34612,6 +34612,80 @@ function applyGeneratedToolTitles(spec2) {
34612
34612
  return spec2;
34613
34613
  }
34614
34614
 
34615
+ // package.json
34616
+ var package_default = {
34617
+ name: "@xiawan-play/steam-tools-mcp",
34618
+ version: "0.3.2",
34619
+ description: "A Steam MCP server with Chinese-friendly high-level tools for profiles, libraries, game snapshots, achievements, and app discovery.",
34620
+ type: "module",
34621
+ license: "MIT",
34622
+ main: "./dist/server.js",
34623
+ types: "./dist/server.d.ts",
34624
+ exports: {
34625
+ ".": {
34626
+ types: "./dist/server.d.ts",
34627
+ default: "./dist/server.js"
34628
+ }
34629
+ },
34630
+ bin: {
34631
+ "steam-tools-mcp": "dist/server.js"
34632
+ },
34633
+ files: [
34634
+ "dist",
34635
+ "docs",
34636
+ "README.md",
34637
+ ".env.example"
34638
+ ],
34639
+ keywords: [
34640
+ "mcp",
34641
+ "model-context-protocol",
34642
+ "steam",
34643
+ "steam-tools",
34644
+ "steam-web-api",
34645
+ "steamworks"
34646
+ ],
34647
+ publishConfig: {
34648
+ access: "public"
34649
+ },
34650
+ repository: {
34651
+ type: "git",
34652
+ url: "git+https://github.com/XiaWan-Play/steam-tools-mcp.git"
34653
+ },
34654
+ homepage: "https://github.com/XiaWan-Play/steam-tools-mcp#readme",
34655
+ bugs: {
34656
+ url: "https://github.com/XiaWan-Play/steam-tools-mcp/issues"
34657
+ },
34658
+ engines: {
34659
+ node: ">=20.0.0"
34660
+ },
34661
+ scripts: {
34662
+ dev: "tsx src/server.ts",
34663
+ build: "tsup src/server.ts --format esm --dts --clean",
34664
+ "check:tool-registry": "tsx scripts/check-tool-registration.ts",
34665
+ start: "node dist/server.js",
34666
+ "sync:steam-spec": "tsx scripts/sync-steam-web-api-spec.ts",
34667
+ prepare: "npm run build"
34668
+ },
34669
+ dependencies: {
34670
+ "@modelcontextprotocol/sdk": "^1.27.1",
34671
+ cheerio: "^1.2.0",
34672
+ zod: "^4.3.6"
34673
+ },
34674
+ devDependencies: {
34675
+ "@types/express": "^5.0.3",
34676
+ "@types/node": "^24.6.1",
34677
+ tsup: "^8.5.1",
34678
+ tsx: "^4.20.6",
34679
+ typescript: "^5.9.3"
34680
+ }
34681
+ };
34682
+
34683
+ // src/lib/package-meta.ts
34684
+ var metadata = package_default;
34685
+ var PACKAGE_NAME = metadata.name.includes("/") ? metadata.name.split("/").pop() ?? metadata.name : metadata.name;
34686
+ var PACKAGE_VERSION = metadata.version;
34687
+ var PACKAGE_USER_AGENT = `${PACKAGE_NAME}/${PACKAGE_VERSION}`;
34688
+
34615
34689
  // src/normalizers/common.ts
34616
34690
  function normalizeScalarInput(value) {
34617
34691
  if (value === void 0 || value === null) {
@@ -35153,11 +35227,11 @@ async function fetchTextResponse(url, timeoutMs) {
35153
35227
  const controller = new AbortController();
35154
35228
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
35155
35229
  try {
35156
- const response = await fetch(url, {
35230
+ const response = await fetchWithProxy(url, {
35157
35231
  signal: controller.signal,
35158
35232
  headers: {
35159
35233
  "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
35160
- "user-agent": "steam-tools-mcp/0.2.0"
35234
+ "user-agent": PACKAGE_USER_AGENT
35161
35235
  }
35162
35236
  });
35163
35237
  if (!response.ok) {
@@ -35174,6 +35248,70 @@ async function fetchJsonResponse(url, timeoutMs) {
35174
35248
  }
35175
35249
 
35176
35250
  // src/services/base/apps.ts
35251
+ function isChineseLikeLocale(locale) {
35252
+ const normalized = normalizeLocale(locale) ?? "";
35253
+ return normalized.startsWith("zh") || normalized.includes("chinese");
35254
+ }
35255
+ function normalizeStoreTitleCandidate(value) {
35256
+ const text = value?.replace(/\s+/g, " ").trim();
35257
+ if (!text) {
35258
+ return void 0;
35259
+ }
35260
+ const patterns = [
35261
+ /^在 Steam 上购买\s+(.+?)\s+立省.*$/u,
35262
+ /^Steam 上的\s+(.+)$/u,
35263
+ /^在 Steam 上下载\s+(.+)$/u,
35264
+ /^Buy\s+(.+?)\s+on Steam$/iu,
35265
+ /^Save .* on\s+(.+?)\s+on Steam$/iu,
35266
+ /^(.+?) on Steam$/iu
35267
+ ];
35268
+ for (const pattern of patterns) {
35269
+ const match = text.match(pattern);
35270
+ const candidate = match?.[1]?.trim();
35271
+ if (candidate) {
35272
+ return candidate;
35273
+ }
35274
+ }
35275
+ return text;
35276
+ }
35277
+ function pickStorePageAppName(candidates, locale) {
35278
+ const normalizedCandidates = uniqueStrings(
35279
+ candidates.map((candidate) => normalizeStoreTitleCandidate(candidate)).filter((candidate) => Boolean(candidate))
35280
+ );
35281
+ if (normalizedCandidates.length === 0) {
35282
+ return void 0;
35283
+ }
35284
+ if (isChineseLikeLocale(locale)) {
35285
+ return normalizedCandidates.find((candidate) => containsNonAscii(candidate)) ?? normalizedCandidates[0];
35286
+ }
35287
+ return normalizedCandidates[0];
35288
+ }
35289
+ function shouldFetchStorePageName(existingName, locale) {
35290
+ if (!isChineseLikeLocale(locale)) {
35291
+ return false;
35292
+ }
35293
+ if (!existingName) {
35294
+ return true;
35295
+ }
35296
+ return !containsNonAscii(existingName);
35297
+ }
35298
+ function shouldPreferStorePageName(existingName, candidateName, locale) {
35299
+ if (!candidateName) {
35300
+ return false;
35301
+ }
35302
+ if (!existingName) {
35303
+ return true;
35304
+ }
35305
+ if (!isChineseLikeLocale(locale)) {
35306
+ return false;
35307
+ }
35308
+ const existingHasLocalizedChars = containsNonAscii(existingName);
35309
+ const candidateHasLocalizedChars = containsNonAscii(candidateName);
35310
+ if (existingHasLocalizedChars !== candidateHasLocalizedChars) {
35311
+ return candidateHasLocalizedChars;
35312
+ }
35313
+ return false;
35314
+ }
35177
35315
  function createAppRuntimeHelpers(deps) {
35178
35316
  const { config, safeInvokeKnownMethod, requireSteamApiKey } = deps;
35179
35317
  let appCatalogCache = null;
@@ -35322,6 +35460,38 @@ function createAppRuntimeHelpers(deps) {
35322
35460
  locale: normalizedLocale
35323
35461
  };
35324
35462
  }
35463
+ async function fetchStorePageLocalizedAppName(appid, locale = "schinese") {
35464
+ const normalizedLocale = normalizeLocale(locale) ?? "schinese";
35465
+ const url = new URL(`https://store.steampowered.com/app/${appid}/`);
35466
+ url.searchParams.set("l", normalizedLocale);
35467
+ url.searchParams.set("cc", "cn");
35468
+ const pageText = await fetchTextResponse(url, 3500);
35469
+ const $ = load(pageText);
35470
+ const reviewPropsRaw = $('[data-featuretarget="appreviews"]').first().attr("data-props");
35471
+ let reviewAppName;
35472
+ if (reviewPropsRaw) {
35473
+ try {
35474
+ const reviewProps = asRecord(JSON.parse(reviewPropsRaw));
35475
+ reviewAppName = getStringValue(reviewProps?.appname);
35476
+ } catch {
35477
+ reviewAppName = void 0;
35478
+ }
35479
+ }
35480
+ const localizedName = pickStorePageAppName(
35481
+ [
35482
+ reviewAppName,
35483
+ $("#appHubAppName").first().text(),
35484
+ $(".apphub_AppName").first().text(),
35485
+ $('meta[property="og:title"]').attr("content"),
35486
+ $("title").first().text()
35487
+ ],
35488
+ normalizedLocale
35489
+ );
35490
+ return {
35491
+ localizedName,
35492
+ locale: normalizedLocale
35493
+ };
35494
+ }
35325
35495
  async function fetchLocalizedStoreAppName(appid, locale) {
35326
35496
  const cacheKey = `${appid}:${locale}`;
35327
35497
  const cached = localizedAppNameCache.get(cacheKey);
@@ -35330,10 +35500,25 @@ function createAppRuntimeHelpers(deps) {
35330
35500
  return cached.value;
35331
35501
  }
35332
35502
  const details = await fetchStoreAppDetailsSummary(appid, locale);
35333
- const localizedName = details?.name;
35503
+ let localizedName = details?.name;
35504
+ let source = localizedName ? "storeAppDetails" : "fallback";
35505
+ if (shouldFetchStorePageName(localizedName, locale)) {
35506
+ try {
35507
+ const pageName = await fetchStorePageLocalizedAppName(appid, locale);
35508
+ if (shouldPreferStorePageName(
35509
+ localizedName,
35510
+ pageName.localizedName,
35511
+ pageName.locale
35512
+ )) {
35513
+ localizedName = pageName.localizedName;
35514
+ source = localizedName ? "storePage" : source;
35515
+ }
35516
+ } catch {
35517
+ }
35518
+ }
35334
35519
  const value = {
35335
35520
  localizedName,
35336
- source: localizedName ? "storeAppDetails" : "fallback",
35521
+ source,
35337
35522
  locale: details?.locale ?? normalizeLocale(locale) ?? "schinese"
35338
35523
  };
35339
35524
  localizedAppNameCache.set(cacheKey, {
@@ -37007,444 +37192,221 @@ async function buildAppSnapshotData(runtime2, input) {
37007
37192
  };
37008
37193
  }
37009
37194
 
37010
- // src/services/prices.ts
37011
- var AUGMENTED_STEAM_PRICES_ENDPOINT = "https://api.augmentedsteam.com/prices/v2";
37012
- var DEFAULT_SHOP_IDS = [61];
37013
- async function buildPriceOverviewData(runtime2, input) {
37014
- const target = await resolvePriceTarget(runtime2, input);
37015
- const country = normalizeCountryCode(input.country);
37016
- const shopIds = parseShopIds(input.shops);
37017
- const payload = await fetchAugmentedSteamPrices(runtime2, {
37018
- country,
37019
- apps: target.type === "app" ? [parseNumericId(target.id, "appid")] : [],
37020
- subs: target.type === "sub" ? [parseNumericId(target.id, "subid")] : [],
37021
- bundles: target.type === "bundle" ? [parseNumericId(target.id, "bundleid")] : [],
37022
- voucher: input.includeVoucher,
37023
- shops: shopIds
37195
+ // src/services/achievement-guide.ts
37196
+ import { load as load2 } from "cheerio";
37197
+ var STEAM_GUIDE_KEYWORDS = [
37198
+ "achievement",
37199
+ "100%",
37200
+ "all achievements",
37201
+ "guide",
37202
+ "walkthrough",
37203
+ "missable",
37204
+ "coop",
37205
+ "co-op"
37206
+ ];
37207
+ var RARITY_BANDS = [
37208
+ {
37209
+ key: "veryRare",
37210
+ label: "Very Rare",
37211
+ minPercent: 0,
37212
+ maxPercent: 5
37213
+ },
37214
+ {
37215
+ key: "rare",
37216
+ label: "Rare",
37217
+ minPercent: 5,
37218
+ maxPercent: 20
37219
+ },
37220
+ {
37221
+ key: "uncommon",
37222
+ label: "Uncommon",
37223
+ minPercent: 20,
37224
+ maxPercent: 50
37225
+ },
37226
+ {
37227
+ key: "common",
37228
+ label: "Common",
37229
+ minPercent: 50,
37230
+ maxPercent: Number.POSITIVE_INFINITY
37231
+ }
37232
+ ];
37233
+ function isPresent(value) {
37234
+ return value !== null && value !== void 0;
37235
+ }
37236
+ async function buildAchievementGuideData(runtime2, input) {
37237
+ const overview = await buildGlobalAchievementOverviewData(runtime2, {
37238
+ toolName: input.toolName,
37239
+ appid: input.appid,
37240
+ query: input.query,
37241
+ key: input.key,
37242
+ language: input.language,
37243
+ includeSchemaDetails: input.includeSchemaDetails,
37244
+ topCount: input.topCount
37024
37245
  });
37025
- const priceRecord = asRecord(getNestedValue(payload, "prices", `${target.type}/${target.id}`));
37026
- const warnings = [...target.warnings];
37027
- if (!priceRecord) {
37246
+ const target = asRecord(overview.target);
37247
+ const app = asRecord(target?.app);
37248
+ const overviewSummary = asRecord(overview.summary);
37249
+ const warnings = Array.isArray(overview.warnings) ? [...overview.warnings] : [];
37250
+ const sourceItems = getRecordItems(overview.items);
37251
+ const achievementMap = buildAchievementMap(sourceItems);
37252
+ const indexUrls = app && getStringValue(app.appid) ? buildSteamGuideIndexUrls(getStringValue(app.appid)) : [];
37253
+ const selectedGuides = await loadGuideCandidates(
37254
+ runtime2,
37255
+ indexUrls,
37256
+ input.maxGuides,
37257
+ warnings
37258
+ );
37259
+ const guideAnalyses = await Promise.all(
37260
+ selectedGuides.map(async (guide) => {
37261
+ try {
37262
+ const html = await fetchSteamCommunityHtml(guide.url, runtime2.config.timeoutMs);
37263
+ return analyzeGuidePage(html, guide, achievementMap);
37264
+ } catch (error) {
37265
+ warnings.push(
37266
+ `Steam Guide fetch failed for ${guide.url}: ${error instanceof Error ? error.message : String(error)}`
37267
+ );
37268
+ return null;
37269
+ }
37270
+ })
37271
+ );
37272
+ const validGuides = guideAnalyses.filter(
37273
+ (guide) => Boolean(guide)
37274
+ );
37275
+ const guideFacts = dedupeGuideFacts(
37276
+ validGuides.flatMap((guide) => guide.facts)
37277
+ ).slice(0, input.maxGuideFacts);
37278
+ const achievementEvidence = buildAchievementEvidence(
37279
+ validGuides,
37280
+ input.maxEvidencePerAchievement
37281
+ );
37282
+ const evidenceByAchievement = new Map(
37283
+ achievementEvidence.map((entry) => [entry.achievement, entry])
37284
+ );
37285
+ const checklist = sourceItems.map((achievement, index) => {
37286
+ const displayName = getStringValue(achievement.displayName) ?? getStringValue(achievement.apiName);
37287
+ const evidence = displayName ? evidenceByAchievement.get(displayName) : void 0;
37288
+ const globalPercent = toNumber(achievement.globalPercent);
37028
37289
  return {
37029
- ok: false,
37030
- requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
37031
- target: buildPriceToolTarget(target),
37032
- summary: {
37033
- type: target.type,
37034
- country,
37035
- source: "augmentedsteam",
37036
- requestedShopIds: shopIds
37037
- },
37038
- items: [],
37039
- note: `No price data was returned for ${target.type}/${target.id} in country '${country}'.`,
37040
- warnings
37290
+ checklistIndex: index + 1,
37291
+ apiName: getStringValue(achievement.apiName),
37292
+ displayName,
37293
+ description: getStringValue(achievement.description),
37294
+ hidden: toBoolean(achievement.hidden) ?? false,
37295
+ globalPercent,
37296
+ rarityBand: classifyRarityBand(globalPercent),
37297
+ icon: getStringValue(achievement.icon),
37298
+ iconGray: getStringValue(achievement.iconGray),
37299
+ guideEvidenceCount: evidence?.evidenceCount ?? 0,
37300
+ guideEvidence: evidence?.evidence ?? [],
37301
+ guideSources: evidence?.sources ?? []
37041
37302
  };
37042
- }
37043
- const current = normalizePriceNode(asRecord(priceRecord.current));
37044
- const lowest = normalizePriceNode(asRecord(priceRecord.lowest));
37045
- const urls = normalizePriceUrls(asRecord(priceRecord.urls));
37046
- const bundled = toNumber(priceRecord.bundled) ?? 0;
37047
- const currentAmount = toNumber(getNestedValue(current, "price", "amount"));
37048
- const lowestAmount = toNumber(getNestedValue(lowest, "price", "amount"));
37049
- const lowestTimestamp = getStringValue(lowest?.timestamp);
37050
- const historicalLowDate = lowestTimestamp ? new Date(lowestTimestamp).toISOString().slice(0, 10) : null;
37051
- const currentMatchesHistoricalLow = currentAmount !== void 0 && lowestAmount !== void 0 && currentAmount <= lowestAmount;
37052
- const item = {
37053
- type: target.type,
37054
- id: target.id,
37055
- historicalLowPrice: getNestedValue(lowest, "price"),
37056
- historicalLowDate,
37057
- currentLowestPrice: getNestedValue(current, "price"),
37058
- currentIsHistoricalLow: currentMatchesHistoricalLow,
37059
- current,
37060
- historicalLow: lowest,
37061
- bundledCount: bundled,
37062
- urls,
37063
- source: {
37064
- name: "Augmented Steam",
37065
- endpoint: AUGMENTED_STEAM_PRICES_ENDPOINT
37066
- }
37067
- };
37303
+ });
37304
+ const hidden = checklist.filter(
37305
+ (achievement) => toBoolean(achievement.hidden) === true
37306
+ );
37307
+ const withPercent = checklist.filter(
37308
+ (achievement) => toNumber(achievement.globalPercent) !== void 0
37309
+ );
37310
+ const sortedByRarity = [...withPercent].sort(
37311
+ (left, right) => (toNumber(left.globalPercent) ?? Number.POSITIVE_INFINITY) - (toNumber(right.globalPercent) ?? Number.POSITIVE_INFINITY)
37312
+ );
37313
+ const sortedByCommonness = [...sortedByRarity].reverse();
37314
+ const averageUnlockRate = withPercent.length > 0 ? roundTo(
37315
+ withPercent.reduce(
37316
+ (sum, achievement) => sum + (toNumber(achievement.globalPercent) ?? 0),
37317
+ 0
37318
+ ) / withPercent.length,
37319
+ 2
37320
+ ) : null;
37068
37321
  return {
37069
- ok: true,
37322
+ ok: overview.ok,
37070
37323
  requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
37071
- target: buildPriceToolTarget(target),
37324
+ target: {
37325
+ app
37326
+ },
37072
37327
  summary: {
37073
- type: target.type,
37074
- country,
37075
- source: "augmentedsteam",
37076
- requestedShopIds: shopIds,
37077
- hasCurrentPrice: current !== null,
37078
- hasHistoricalLow: lowest !== null,
37079
- historicalLowPrice: getNestedValue(lowest, "price"),
37080
- historicalLowDate,
37081
- currentLowestPrice: getNestedValue(current, "price"),
37082
- currentMatchesHistoricalLow,
37083
- currentIsHistoricalLow: currentMatchesHistoricalLow,
37084
- currentDiscountPercent: toNumber(current?.cut) ?? null,
37085
- historicalLowDiscountPercent: toNumber(lowest?.cut) ?? null,
37086
- bundledCount: bundled
37328
+ source: "official_achievements+steam_guides",
37329
+ totalAchievements: checklist.length,
37330
+ hiddenCount: hidden.length,
37331
+ visibleCount: checklist.length - hidden.length,
37332
+ achievementsWithGuideEvidence: achievementEvidence.length,
37333
+ guideCount: validGuides.length,
37334
+ guideFactCount: guideFacts.length,
37335
+ missableCount: guideFacts.filter((fact) => fact.category === "missable").length,
37336
+ coOpCount: guideFacts.filter((fact) => fact.category === "co_op").length,
37337
+ difficultyCount: guideFacts.filter((fact) => fact.category === "difficulty").length,
37338
+ setupCount: guideFacts.filter((fact) => fact.category === "setup").length,
37339
+ cleanupCount: guideFacts.filter((fact) => fact.category === "cleanup").length,
37340
+ averageUnlockRate,
37341
+ schemaEnriched: toBoolean(overviewSummary?.schemaEnriched) ?? false
37087
37342
  },
37088
- items: [item],
37343
+ items: checklist,
37089
37344
  sections: {
37090
- provider: {
37091
- name: "Augmented Steam",
37092
- endpoint: AUGMENTED_STEAM_PRICES_ENDPOINT,
37093
- requestedShopIds: shopIds,
37094
- steamOnly: shopIds.length === 1 && shopIds[0] === 61
37095
- },
37096
- links: urls
37345
+ rarest: sortedByRarity.slice(0, input.topCount),
37346
+ mostCommon: sortedByCommonness.slice(0, input.topCount),
37347
+ hidden,
37348
+ guideFacts,
37349
+ achievementEvidence,
37350
+ guideFactCounts: buildGuideFactCounts(guideFacts),
37351
+ rarityBands: buildRarityBandCounts(checklist),
37352
+ sources: validGuides.map((guide) => ({
37353
+ ...guide.metadata,
37354
+ mentionCount: guide.mentions.length,
37355
+ factCount: guide.facts.length,
37356
+ score: guide.score
37357
+ }))
37097
37358
  },
37098
37359
  warnings
37099
37360
  };
37100
37361
  }
37101
- async function resolvePriceTarget(runtime2, input) {
37102
- const appid = normalizeScalarInput(input.appid);
37103
- const subid = normalizeScalarInput(input.subid);
37104
- const bundleid = normalizeScalarInput(input.bundleid);
37105
- const query = normalizeScalarInput(input.query);
37106
- const explicitTargets = [appid, subid, bundleid].filter(Boolean);
37107
- if (explicitTargets.length > 1) {
37108
- throw new Error("Provide only one of appid, subid, or bundleid.");
37109
- }
37110
- if ((subid || bundleid) && query) {
37111
- throw new Error("query can only be used with app targets. Use subid or bundleid directly.");
37362
+ async function loadGuideCandidates(runtime2, indexUrls, maxGuides, warnings) {
37363
+ const indexPages = await Promise.all(
37364
+ indexUrls.map(async (url) => {
37365
+ try {
37366
+ return {
37367
+ url,
37368
+ html: await fetchSteamCommunityHtml(url, runtime2.config.timeoutMs)
37369
+ };
37370
+ } catch (error) {
37371
+ warnings.push(
37372
+ `Steam Guides index fetch failed for ${url}: ${error instanceof Error ? error.message : String(error)}`
37373
+ );
37374
+ return null;
37375
+ }
37376
+ })
37377
+ );
37378
+ const candidateMap = /* @__PURE__ */ new Map();
37379
+ for (const page of indexPages) {
37380
+ if (!page) {
37381
+ continue;
37382
+ }
37383
+ for (const candidate of parseGuideCandidates(page.html)) {
37384
+ const existing = candidateMap.get(candidate.guideId);
37385
+ if (!existing || candidate.score > existing.score) {
37386
+ candidateMap.set(candidate.guideId, candidate);
37387
+ }
37388
+ }
37112
37389
  }
37113
- if (subid) {
37114
- return {
37115
- type: "sub",
37116
- id: subid,
37117
- resolvedFrom: "subid",
37118
- warnings: []
37119
- };
37120
- }
37121
- if (bundleid) {
37122
- return {
37123
- type: "bundle",
37124
- id: bundleid,
37125
- resolvedFrom: "bundleid",
37126
- warnings: []
37127
- };
37128
- }
37129
- if (!appid && !query) {
37130
- throw new Error("steam_get_price_overview requires appid, subid, bundleid, or query.");
37131
- }
37132
- const resolved = await runtime2.resolveAppSelection({
37133
- toolName: input.toolName,
37134
- appid,
37135
- query,
37136
- key: input.key,
37137
- limit: 5,
37138
- forceRefresh: false,
37139
- locale: input.language,
37140
- preferLocalizedName: Boolean(query) || Boolean(input.language)
37141
- });
37142
- return {
37143
- type: "app",
37144
- id: resolved.appid,
37145
- resolvedFrom: query ? "query" : "appid",
37146
- query,
37147
- appTarget: resolved.target,
37148
- warnings: resolved.warnings
37149
- };
37150
- }
37151
- async function fetchAugmentedSteamPrices(runtime2, body) {
37152
- const response = await fetchWithProxy(AUGMENTED_STEAM_PRICES_ENDPOINT, {
37153
- method: "POST",
37154
- headers: {
37155
- accept: "application/json",
37156
- "content-type": "application/json",
37157
- "user-agent": "steam-tools-mcp/0.2.0"
37158
- },
37159
- body: JSON.stringify(body),
37160
- signal: AbortSignal.timeout(runtime2.config.timeoutMs)
37161
- });
37162
- const text = await response.text();
37163
- if (!response.ok) {
37164
- throw new Error(
37165
- `Augmented Steam prices request failed with ${response.status} ${response.statusText}.`
37166
- );
37167
- }
37168
- try {
37169
- return JSON.parse(text);
37170
- } catch (error) {
37171
- throw new Error(
37172
- `Augmented Steam prices response was not valid JSON: ${error instanceof Error ? error.message : String(error)}`
37173
- );
37174
- }
37175
- }
37176
- function normalizePriceNode(value) {
37177
- if (!value) {
37178
- return null;
37179
- }
37180
- return {
37181
- shop: normalizeShop(asRecord(value.shop)),
37182
- price: normalizeMoney(asRecord(value.price)),
37183
- regular: normalizeMoney(asRecord(value.regular)),
37184
- cut: toNumber(value.cut),
37185
- voucher: value.voucher ?? null,
37186
- flag: value.flag ?? null,
37187
- drm: Array.isArray(value.drm) ? value.drm : [],
37188
- platforms: Array.isArray(value.platforms) ? value.platforms.map((entry) => normalizePlatform(asRecord(entry))).filter((entry) => Boolean(entry)) : [],
37189
- timestamp: getStringValue(value.timestamp),
37190
- expiry: getStringValue(value.expiry),
37191
- url: getStringValue(value.url)
37192
- };
37193
- }
37194
- function normalizePriceUrls(value) {
37195
- if (!value) {
37196
- return null;
37197
- }
37198
- return {
37199
- info: getStringValue(value.info),
37200
- history: getStringValue(value.history),
37201
- bundles: getStringValue(value.bundles)
37202
- };
37203
- }
37204
- function normalizeShop(value) {
37205
- if (!value) {
37206
- return null;
37207
- }
37208
- return {
37209
- id: toNumber(value.id),
37210
- name: getStringValue(value.name)
37211
- };
37212
- }
37213
- function normalizePlatform(value) {
37214
- if (!value) {
37215
- return null;
37216
- }
37217
- return {
37218
- id: toNumber(value.id),
37219
- name: getStringValue(value.name)
37220
- };
37221
- }
37222
- function normalizeMoney(value) {
37223
- if (!value) {
37224
- return null;
37225
- }
37226
- return {
37227
- amount: toNumber(value.amount),
37228
- amountInt: toNumber(value.amountInt),
37229
- currency: getStringValue(value.currency)
37230
- };
37231
- }
37232
- function parseShopIds(value) {
37233
- if (value === void 0) {
37234
- return DEFAULT_SHOP_IDS;
37235
- }
37236
- const values = Array.isArray(value) ? value : [value];
37237
- const shopIds = [...new Set(
37238
- values.flatMap(
37239
- (entry) => typeof entry === "string" ? entry.split(/[,\s]+/) : [String(entry)]
37240
- ).map((entry) => normalizeScalarInput(entry)).map((entry) => entry ? Number.parseInt(entry, 10) : Number.NaN).filter((entry) => Number.isFinite(entry) && entry > 0)
37241
- )];
37242
- return shopIds.length > 0 ? shopIds : DEFAULT_SHOP_IDS;
37243
- }
37244
- function normalizeCountryCode(value) {
37245
- const normalized = value.trim().toLowerCase();
37246
- if (!/^[a-z]{2}$/.test(normalized)) {
37247
- throw new Error("country must be a two-letter ISO 3166-1 country code, such as 'cn' or 'us'.");
37248
- }
37249
- return normalized;
37250
- }
37251
- function buildPriceToolTarget(target) {
37252
- return {
37253
- subject: {
37254
- type: target.type,
37255
- id: target.id,
37256
- resolvedFrom: target.resolvedFrom,
37257
- query: target.query
37258
- },
37259
- ...target.appTarget ? { app: target.appTarget } : {}
37260
- };
37261
- }
37262
- function parseNumericId(value, fieldName) {
37263
- const parsed = Number.parseInt(value, 10);
37264
- if (!Number.isFinite(parsed) || parsed <= 0) {
37265
- throw new Error(`${fieldName} must be a positive integer.`);
37266
- }
37267
- return parsed;
37268
- }
37269
-
37270
- // src/services/roadmap.ts
37271
- import { load as load2 } from "cheerio";
37272
- var STEAM_GUIDE_KEYWORDS = [
37273
- "achievement",
37274
- "100%",
37275
- "all achievements",
37276
- "roadmap",
37277
- "walkthrough",
37278
- "missable",
37279
- "coop",
37280
- "co-op"
37281
- ];
37282
- var PHASE_ORDER = [
37283
- "preparation",
37284
- "main_story",
37285
- "co_op",
37286
- "challenge",
37287
- "cleanup"
37288
- ];
37289
- var PHASE_TITLES = {
37290
- preparation: "Before You Start",
37291
- main_story: "Main Story Progress",
37292
- co_op: "Co-op / Multiplayer",
37293
- challenge: "Harder Or Rarer Targets",
37294
- cleanup: "Cleanup"
37295
- };
37296
- function isPresent(value) {
37297
- return value !== null && value !== void 0;
37298
- }
37299
- async function buildAchievementRoadmapData(runtime2, input) {
37300
- const resolved = await runtime2.resolveAppSelection({
37301
- toolName: input.toolName,
37302
- appid: input.appid,
37303
- query: input.query,
37304
- key: input.key,
37305
- limit: 5,
37306
- forceRefresh: false,
37307
- locale: input.language,
37308
- preferLocalizedName: Boolean(input.query) || Boolean(input.language)
37309
- });
37310
- const achievementOverview = await buildGlobalAchievementOverviewData(runtime2, {
37311
- toolName: input.toolName,
37312
- appid: resolved.appid,
37313
- key: input.key,
37314
- language: input.language,
37315
- includeSchemaDetails: true,
37316
- topCount: 10
37317
- });
37318
- const achievementItems = getRecordItems(getNestedValue(achievementOverview, "items"));
37319
- const achievementMap = buildAchievementMap(achievementItems);
37320
- const warnings = [...resolved.warnings, ...getWarnings(achievementOverview)];
37321
- const indexUrls = buildSteamGuideIndexUrls(resolved.appid);
37322
- const indexPages = await Promise.all(
37323
- indexUrls.map(async (url) => {
37324
- try {
37325
- return {
37326
- url,
37327
- html: await fetchSteamCommunityHtml(url, runtime2.config.timeoutMs)
37328
- };
37329
- } catch (error) {
37330
- warnings.push(
37331
- `Steam Guides index fetch failed for ${url}: ${error instanceof Error ? error.message : String(error)}`
37332
- );
37333
- return null;
37334
- }
37335
- })
37336
- );
37337
- const candidateMap = /* @__PURE__ */ new Map();
37338
- for (const page of indexPages) {
37339
- if (!page) {
37340
- continue;
37341
- }
37342
- for (const candidate of parseGuideCandidates(page.html)) {
37343
- const existing = candidateMap.get(candidate.guideId);
37344
- if (!existing || candidate.score > existing.score) {
37345
- candidateMap.set(candidate.guideId, candidate);
37346
- }
37347
- }
37348
- }
37349
- const selectedGuides = [...candidateMap.values()].sort((left, right) => right.score - left.score).slice(0, input.maxGuides);
37350
- if (selectedGuides.length === 0) {
37351
- return {
37352
- ok: false,
37353
- requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
37354
- target: {
37355
- app: resolved.target
37356
- },
37357
- summary: {
37358
- source: "steam_guides",
37359
- guideCount: 0,
37360
- routePhaseCount: 0,
37361
- missableCount: 0
37362
- },
37363
- items: [],
37364
- sections: {
37365
- sources: [],
37366
- missables: [],
37367
- achievementNotes: []
37368
- },
37369
- note: "No Steam Guides candidates were found for this game.",
37370
- warnings
37371
- };
37372
- }
37373
- const guideAnalyses = await Promise.all(
37374
- selectedGuides.map(async (guide) => {
37375
- try {
37376
- const html = await fetchSteamCommunityHtml(guide.url, runtime2.config.timeoutMs);
37377
- return analyzeGuidePage(html, guide, achievementMap);
37378
- } catch (error) {
37379
- warnings.push(
37380
- `Steam Guide fetch failed for ${guide.url}: ${error instanceof Error ? error.message : String(error)}`
37381
- );
37382
- return null;
37383
- }
37384
- })
37385
- );
37386
- const validGuides = guideAnalyses.filter(
37387
- (guide) => Boolean(guide)
37388
- );
37389
- const routePhases = buildRoutePhases(validGuides, achievementMap);
37390
- const missables = dedupeAttentionItems(
37391
- validGuides.flatMap(
37392
- (guide) => guide.attention.filter((item) => item.category === "missable")
37393
- )
37394
- );
37395
- const achievementNotes = buildAchievementNotes(validGuides, input.maxAchievementNotes);
37396
- const guideSummary = buildGuideSummary(validGuides, routePhases, missables);
37397
- return {
37398
- ok: true,
37399
- requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
37400
- target: {
37401
- app: resolved.target
37402
- },
37403
- summary: {
37404
- source: "steam_guides",
37405
- guideCount: validGuides.length,
37406
- routePhaseCount: routePhases.length,
37407
- missableCount: missables.length,
37408
- achievementNoteCount: achievementNotes.length,
37409
- supportedByOfficialAchievements: achievementMap.length > 0
37410
- },
37411
- items: routePhases,
37412
- sections: {
37413
- guideSummary,
37414
- sources: validGuides.map((guide) => ({
37415
- ...guide.metadata,
37416
- mentionCount: guide.mentions.length,
37417
- attentionCount: guide.attention.length,
37418
- score: guide.score
37419
- })),
37420
- missables,
37421
- achievementNotes,
37422
- officialAchievements: {
37423
- totalCount: achievementMap.length,
37424
- rarest: getNestedValue(achievementOverview, "sections", "rarest")
37425
- }
37426
- },
37427
- warnings
37428
- };
37429
- }
37430
- function buildSteamGuideIndexUrls(appid) {
37431
- return [
37432
- `https://steamcommunity.com/app/${appid}/guides/?l=english`,
37433
- `https://steamcommunity.com/app/${appid}/guides/?l=english&searchText=achievement`,
37434
- `https://steamcommunity.com/app/${appid}/guides/?l=english&searchText=100%25`
37435
- ];
37436
- }
37437
- async function fetchSteamCommunityHtml(url, timeoutMs) {
37438
- const response = await fetchWithProxy(url, {
37439
- headers: {
37440
- accept: "text/html,application/xhtml+xml",
37441
- "accept-language": "en-US,en;q=0.9",
37442
- "user-agent": "steam-tools-mcp/0.2.0"
37443
- },
37444
- signal: AbortSignal.timeout(timeoutMs)
37445
- });
37446
- if (!response.ok) {
37447
- throw new Error(`HTTP ${response.status} ${response.statusText}`);
37390
+ return [...candidateMap.values()].sort((left, right) => right.score - left.score).slice(0, maxGuides);
37391
+ }
37392
+ function buildSteamGuideIndexUrls(appid) {
37393
+ return [
37394
+ `https://steamcommunity.com/app/${appid}/guides/?l=english`,
37395
+ `https://steamcommunity.com/app/${appid}/guides/?l=english&searchText=achievement`,
37396
+ `https://steamcommunity.com/app/${appid}/guides/?l=english&searchText=100%25`
37397
+ ];
37398
+ }
37399
+ async function fetchSteamCommunityHtml(url, timeoutMs) {
37400
+ const response = await fetchWithProxy(url, {
37401
+ headers: {
37402
+ accept: "text/html,application/xhtml+xml",
37403
+ "accept-language": "en-US,en;q=0.9",
37404
+ "user-agent": PACKAGE_USER_AGENT
37405
+ },
37406
+ signal: AbortSignal.timeout(timeoutMs)
37407
+ });
37408
+ if (!response.ok) {
37409
+ throw new Error(`HTTP ${response.status} ${response.statusText}`);
37448
37410
  }
37449
37411
  return response.text();
37450
37412
  }
@@ -37510,9 +37472,6 @@ function scoreGuideCandidate(title, snippet) {
37510
37472
  if (haystack.includes("definitive")) {
37511
37473
  score += 8;
37512
37474
  }
37513
- if (haystack.includes("guide")) {
37514
- score += 6;
37515
- }
37516
37475
  return score;
37517
37476
  }
37518
37477
  function analyzeGuidePage(html, candidate, achievementMap) {
@@ -37520,54 +37479,46 @@ function analyzeGuidePage(html, candidate, achievementMap) {
37520
37479
  $("script, style, noscript").remove();
37521
37480
  $("br").replaceWith("\n");
37522
37481
  const lines = $.root().text().split(/\n+/).map((line) => cleanText(line)).filter((line) => line.length > 0);
37523
- const metadata = extractGuideMetadata(lines, candidate);
37482
+ const metadata2 = extractGuideMetadata(lines, candidate);
37524
37483
  const mentions = [];
37525
- const attention = [];
37526
- let currentPhase = "preparation";
37484
+ const facts = [];
37527
37485
  for (let index = 0; index < lines.length; index += 1) {
37528
37486
  const line = lines[index];
37529
- const phaseFromLine = detectPhaseKey(line);
37530
- if (phaseFromLine) {
37531
- currentPhase = phaseFromLine;
37532
- }
37533
37487
  const matchedAchievements = findAchievementsInLine(line, achievementMap);
37534
37488
  for (const achievement of matchedAchievements) {
37535
37489
  mentions.push({
37536
37490
  achievementName: achievement.name,
37537
37491
  globalPercent: achievement.globalPercent,
37538
37492
  snippet: buildSnippet(lines, index),
37539
- phaseKey: currentPhase,
37540
37493
  source: {
37541
- guideId: metadata.guideId,
37542
- title: metadata.title,
37543
- url: metadata.url
37494
+ guideId: metadata2.guideId,
37495
+ title: metadata2.title,
37496
+ url: metadata2.url
37544
37497
  }
37545
37498
  });
37546
37499
  }
37547
- const attentionCategory = classifyAttentionCategory(line);
37548
- if (!attentionCategory) {
37500
+ const category = classifyGuideFactCategory(line);
37501
+ if (!category) {
37549
37502
  continue;
37550
37503
  }
37551
- attention.push({
37552
- category: attentionCategory,
37504
+ facts.push({
37505
+ category,
37553
37506
  text: line,
37554
- phaseKey: attentionCategory === "cleanup" ? "cleanup" : currentPhase,
37555
37507
  source: {
37556
- guideId: metadata.guideId,
37557
- title: metadata.title,
37558
- url: metadata.url
37508
+ guideId: metadata2.guideId,
37509
+ title: metadata2.title,
37510
+ url: metadata2.url
37559
37511
  },
37560
- relatedAchievements: matchedAchievements.map((achievement) => achievement.name),
37561
- confidence: matchedAchievements.length > 0 || attentionCategory === "missable" ? "high" : "medium"
37512
+ relatedAchievements: matchedAchievements.map((achievement) => achievement.name)
37562
37513
  });
37563
37514
  }
37564
37515
  const uniqueMentions = dedupeMentions(mentions);
37516
+ const uniqueFacts = dedupeGuideFacts(facts);
37565
37517
  return {
37566
- metadata,
37518
+ metadata: metadata2,
37567
37519
  mentions: uniqueMentions,
37568
- attention: dedupeAttentionItems(attention),
37569
- lines,
37570
- score: candidate.score + uniqueMentions.length * 2 + attention.length
37520
+ facts: uniqueFacts,
37521
+ score: candidate.score + uniqueMentions.length * 2 + uniqueFacts.length
37571
37522
  };
37572
37523
  }
37573
37524
  function extractGuideMetadata(lines, candidate) {
@@ -37628,289 +37579,425 @@ function findAchievementsInLine(line, achievementMap) {
37628
37579
  return tokens.length >= 2 && tokens.every((token) => normalizedLine.includes(token));
37629
37580
  });
37630
37581
  }
37631
- function buildRoutePhases(guides, achievementMap) {
37632
- const assignedAchievements = /* @__PURE__ */ new Set();
37633
- const phases = /* @__PURE__ */ new Map();
37634
- for (const phaseKey of PHASE_ORDER) {
37635
- phases.set(phaseKey, {
37636
- notes: [],
37637
- achievements: [],
37638
- sources: []
37639
- });
37582
+ function classifyGuideFactCategory(line) {
37583
+ const normalized = normalizeSearchText(line);
37584
+ if (!normalized || normalized.length < 12) {
37585
+ return null;
37586
+ }
37587
+ if (/missable|do not miss|dont miss|don't miss|before finishing|before ending|last chance|point of no return|cannot go back/.test(
37588
+ normalized
37589
+ )) {
37590
+ return "missable";
37591
+ }
37592
+ if (/co op|coop|multiplayer|friend|partner|another player|online/.test(normalized)) {
37593
+ return "co_op";
37640
37594
  }
37595
+ if (/hard|hardest|rarest|practice|difficult|challenge/.test(normalized)) {
37596
+ return "difficulty";
37597
+ }
37598
+ if (/cleanup|replay|chapter select|post game|remaining/.test(normalized)) {
37599
+ return "cleanup";
37600
+ }
37601
+ if (/setup|console|bind|command|config|save file|backup/.test(normalized)) {
37602
+ return "setup";
37603
+ }
37604
+ if (/warning|important|note/.test(normalized)) {
37605
+ return "warning";
37606
+ }
37607
+ return null;
37608
+ }
37609
+ function buildSnippet(lines, index) {
37610
+ return lines.slice(Math.max(0, index - 1), Math.min(lines.length, index + 2)).join(" ").slice(0, 320);
37611
+ }
37612
+ function buildAchievementMap(items) {
37613
+ return items.map((item) => {
37614
+ const name = getStringValue(item.displayName) ?? getStringValue(item.apiName) ?? void 0;
37615
+ if (!name) {
37616
+ return null;
37617
+ }
37618
+ return {
37619
+ apiName: getStringValue(item.apiName),
37620
+ name,
37621
+ normalizedName: normalizeSearchText(name),
37622
+ globalPercent: toNumber(item.globalPercent)
37623
+ };
37624
+ }).filter(isPresent);
37625
+ }
37626
+ function buildAchievementEvidence(guides, maxEvidencePerAchievement) {
37627
+ const evidenceByAchievement = /* @__PURE__ */ new Map();
37641
37628
  for (const guide of guides) {
37642
37629
  for (const mention of guide.mentions) {
37643
- const phase = phases.get(mention.phaseKey);
37644
- if (!phase || assignedAchievements.has(mention.achievementName)) {
37645
- continue;
37646
- }
37647
- phase.achievements.push({
37648
- achievement: mention.achievementName,
37649
- globalPercent: mention.globalPercent ?? null,
37650
- sourceGuideTitle: mention.source.title,
37651
- sourceGuideUrl: mention.source.url,
37652
- evidence: mention.snippet
37653
- });
37654
- phase.sources.push(mention.source.title);
37655
- assignedAchievements.add(mention.achievementName);
37630
+ const existing = evidenceByAchievement.get(mention.achievementName) ?? {
37631
+ globalPercent: mention.globalPercent,
37632
+ snippets: [],
37633
+ sources: []
37634
+ };
37635
+ existing.globalPercent = existing.globalPercent ?? mention.globalPercent;
37636
+ existing.snippets.push(mention.snippet);
37637
+ existing.sources.push(mention.source.title);
37638
+ evidenceByAchievement.set(mention.achievementName, existing);
37656
37639
  }
37657
- for (const item of guide.attention) {
37658
- const phase = phases.get(item.phaseKey);
37659
- if (!phase) {
37660
- continue;
37661
- }
37662
- phase.notes.push(item.text);
37663
- phase.sources.push(item.source.title);
37640
+ }
37641
+ return [...evidenceByAchievement.entries()].map(([achievement, evidence]) => ({
37642
+ achievement,
37643
+ globalPercent: evidence.globalPercent ?? null,
37644
+ evidenceCount: uniqueStrings(evidence.snippets).length,
37645
+ evidence: uniqueStrings(evidence.snippets).slice(0, maxEvidencePerAchievement),
37646
+ sources: uniqueStrings(evidence.sources)
37647
+ })).sort(
37648
+ (left, right) => (toNumber(left.globalPercent) ?? Number.POSITIVE_INFINITY) - (toNumber(right.globalPercent) ?? Number.POSITIVE_INFINITY)
37649
+ );
37650
+ }
37651
+ function buildGuideFactCounts(facts) {
37652
+ const categories = [
37653
+ "missable",
37654
+ "co_op",
37655
+ "difficulty",
37656
+ "cleanup",
37657
+ "setup",
37658
+ "warning"
37659
+ ];
37660
+ return categories.map((category) => ({
37661
+ category,
37662
+ count: facts.filter((fact) => fact.category === category).length
37663
+ }));
37664
+ }
37665
+ function dedupeMentions(mentions) {
37666
+ const seen = /* @__PURE__ */ new Set();
37667
+ return mentions.filter((mention) => {
37668
+ const key = `${mention.achievementName}:${normalizeSearchText(mention.snippet)}`;
37669
+ if (seen.has(key)) {
37670
+ return false;
37671
+ }
37672
+ seen.add(key);
37673
+ return true;
37674
+ });
37675
+ }
37676
+ function dedupeGuideFacts(facts) {
37677
+ const seen = /* @__PURE__ */ new Set();
37678
+ return facts.filter((fact) => {
37679
+ const key = `${fact.category}:${normalizeSearchText(fact.text)}`;
37680
+ if (seen.has(key)) {
37681
+ return false;
37682
+ }
37683
+ seen.add(key);
37684
+ return true;
37685
+ });
37686
+ }
37687
+ function buildRarityBandCounts(checklist) {
37688
+ return [
37689
+ ...RARITY_BANDS.map((band) => ({
37690
+ key: band.key,
37691
+ label: band.label,
37692
+ minPercent: band.minPercent,
37693
+ maxPercent: Number.isFinite(band.maxPercent) ? band.maxPercent : null,
37694
+ count: checklist.filter(
37695
+ (achievement) => achievement.rarityBand === band.key
37696
+ ).length
37697
+ })),
37698
+ {
37699
+ key: "unknown",
37700
+ label: "Unknown",
37701
+ minPercent: null,
37702
+ maxPercent: null,
37703
+ count: checklist.filter(
37704
+ (achievement) => achievement.rarityBand === "unknown"
37705
+ ).length
37706
+ }
37707
+ ];
37708
+ }
37709
+ function classifyRarityBand(globalPercent) {
37710
+ if (globalPercent === void 0) {
37711
+ return "unknown";
37712
+ }
37713
+ for (const band of RARITY_BANDS) {
37714
+ if (globalPercent >= band.minPercent && globalPercent < band.maxPercent) {
37715
+ return band.key;
37664
37716
  }
37665
37717
  }
37666
- const cleanupPhase = phases.get("cleanup");
37667
- const remainingAchievements = achievementMap.filter(
37668
- (achievement) => !assignedAchievements.has(achievement.name)
37669
- );
37670
- if (cleanupPhase) {
37671
- for (const achievement of remainingAchievements) {
37672
- cleanupPhase.achievements.push({
37673
- achievement: achievement.name,
37674
- globalPercent: achievement.globalPercent ?? null,
37675
- sourceGuideTitle: null,
37676
- sourceGuideUrl: null,
37677
- evidence: "Not explicitly placed in the selected Steam Guides, so it is left for cleanup."
37678
- });
37679
- }
37718
+ return "unknown";
37719
+ }
37720
+ function parseCount(line) {
37721
+ const match = line.match(/(\d[\d,]*)/);
37722
+ if (!match) {
37723
+ return void 0;
37724
+ }
37725
+ return Number.parseInt(match[1].replace(/,/g, ""), 10);
37726
+ }
37727
+ function extractGuideId(url) {
37728
+ try {
37729
+ const parsed = new URL(url);
37730
+ const guideId = parsed.searchParams.get("id");
37731
+ return guideId ?? void 0;
37732
+ } catch {
37733
+ return void 0;
37734
+ }
37735
+ }
37736
+ function getRecordItems(value) {
37737
+ return Array.isArray(value) ? value.map((entry) => asRecord(entry)).filter((entry) => Boolean(entry)) : [];
37738
+ }
37739
+ function cleanText(value) {
37740
+ return value.replace(/\s+/g, " ").trim();
37741
+ }
37742
+
37743
+ // src/services/prices.ts
37744
+ var AUGMENTED_STEAM_PRICES_ENDPOINT = "https://api.augmentedsteam.com/prices/v2";
37745
+ var DEFAULT_SHOP_IDS = [61];
37746
+ async function buildPriceOverviewData(runtime2, input) {
37747
+ const target = await resolvePriceTarget(runtime2, input);
37748
+ const country = normalizeCountryCode(input.country);
37749
+ const shopIds = parseShopIds(input.shops);
37750
+ const payload = await fetchAugmentedSteamPrices(runtime2, {
37751
+ country,
37752
+ apps: target.type === "app" ? [parseNumericId(target.id, "appid")] : [],
37753
+ subs: target.type === "sub" ? [parseNumericId(target.id, "subid")] : [],
37754
+ bundles: target.type === "bundle" ? [parseNumericId(target.id, "bundleid")] : [],
37755
+ voucher: input.includeVoucher,
37756
+ shops: shopIds
37757
+ });
37758
+ const priceRecord = asRecord(getNestedValue(payload, "prices", `${target.type}/${target.id}`));
37759
+ const warnings = [...target.warnings];
37760
+ if (!priceRecord) {
37761
+ return {
37762
+ ok: false,
37763
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
37764
+ target: buildPriceToolTarget(target),
37765
+ summary: {
37766
+ type: target.type,
37767
+ country,
37768
+ source: "augmentedsteam",
37769
+ requestedShopIds: shopIds
37770
+ },
37771
+ items: [],
37772
+ note: `No price data was returned for ${target.type}/${target.id} in country '${country}'.`,
37773
+ warnings
37774
+ };
37775
+ }
37776
+ const current = normalizePriceNode(asRecord(priceRecord.current));
37777
+ const lowest = normalizePriceNode(asRecord(priceRecord.lowest));
37778
+ const urls = normalizePriceUrls(asRecord(priceRecord.urls));
37779
+ const bundled = toNumber(priceRecord.bundled) ?? 0;
37780
+ const currentAmount = toNumber(getNestedValue(current, "price", "amount"));
37781
+ const lowestAmount = toNumber(getNestedValue(lowest, "price", "amount"));
37782
+ const lowestTimestamp = getStringValue(lowest?.timestamp);
37783
+ const historicalLowDate = lowestTimestamp ? new Date(lowestTimestamp).toISOString().slice(0, 10) : null;
37784
+ const currentMatchesHistoricalLow = currentAmount !== void 0 && lowestAmount !== void 0 && currentAmount <= lowestAmount;
37785
+ const item = {
37786
+ type: target.type,
37787
+ id: target.id,
37788
+ historicalLowPrice: getNestedValue(lowest, "price"),
37789
+ historicalLowDate,
37790
+ currentLowestPrice: getNestedValue(current, "price"),
37791
+ currentIsHistoricalLow: currentMatchesHistoricalLow,
37792
+ current,
37793
+ historicalLow: lowest,
37794
+ bundledCount: bundled,
37795
+ urls,
37796
+ source: {
37797
+ name: "Augmented Steam",
37798
+ endpoint: AUGMENTED_STEAM_PRICES_ENDPOINT
37799
+ }
37800
+ };
37801
+ return {
37802
+ ok: true,
37803
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
37804
+ target: buildPriceToolTarget(target),
37805
+ summary: {
37806
+ type: target.type,
37807
+ country,
37808
+ source: "augmentedsteam",
37809
+ requestedShopIds: shopIds,
37810
+ hasCurrentPrice: current !== null,
37811
+ hasHistoricalLow: lowest !== null,
37812
+ historicalLowPrice: getNestedValue(lowest, "price"),
37813
+ historicalLowDate,
37814
+ currentLowestPrice: getNestedValue(current, "price"),
37815
+ currentMatchesHistoricalLow,
37816
+ currentIsHistoricalLow: currentMatchesHistoricalLow,
37817
+ currentDiscountPercent: toNumber(current?.cut) ?? null,
37818
+ historicalLowDiscountPercent: toNumber(lowest?.cut) ?? null,
37819
+ bundledCount: bundled
37820
+ },
37821
+ items: [item],
37822
+ sections: {
37823
+ provider: {
37824
+ name: "Augmented Steam",
37825
+ endpoint: AUGMENTED_STEAM_PRICES_ENDPOINT,
37826
+ requestedShopIds: shopIds,
37827
+ steamOnly: shopIds.length === 1 && shopIds[0] === 61
37828
+ },
37829
+ links: urls
37830
+ },
37831
+ warnings
37832
+ };
37833
+ }
37834
+ async function resolvePriceTarget(runtime2, input) {
37835
+ const appid = normalizeScalarInput(input.appid);
37836
+ const subid = normalizeScalarInput(input.subid);
37837
+ const bundleid = normalizeScalarInput(input.bundleid);
37838
+ const query = normalizeScalarInput(input.query);
37839
+ const explicitTargets = [appid, subid, bundleid].filter(Boolean);
37840
+ if (explicitTargets.length > 1) {
37841
+ throw new Error("Provide only one of appid, subid, or bundleid.");
37842
+ }
37843
+ if ((subid || bundleid) && query) {
37844
+ throw new Error("query can only be used with app targets. Use subid or bundleid directly.");
37680
37845
  }
37681
- return PHASE_ORDER.map((phaseKey, index) => {
37682
- const phase = phases.get(phaseKey);
37683
- if (!phase) {
37684
- return null;
37685
- }
37686
- const notes = uniqueStrings(phase.notes).slice(0, 6);
37687
- const achievements = phase.achievements;
37688
- if (notes.length === 0 && achievements.length === 0) {
37689
- return null;
37690
- }
37846
+ if (subid) {
37691
37847
  return {
37692
- phase: index + 1,
37693
- key: phaseKey,
37694
- title: PHASE_TITLES[phaseKey],
37695
- summary: buildPhaseSummary(phaseKey, achievements.length, notes.length),
37696
- achievements,
37697
- notes,
37698
- sources: uniqueStrings(phase.sources)
37848
+ type: "sub",
37849
+ id: subid,
37850
+ resolvedFrom: "subid",
37851
+ warnings: []
37699
37852
  };
37700
- }).filter(isPresent);
37701
- }
37702
- function buildAchievementNotes(guides, maxAchievementNotes) {
37703
- const byAchievement = /* @__PURE__ */ new Map();
37704
- for (const guide of guides) {
37705
- for (const mention of guide.mentions) {
37706
- const existing = byAchievement.get(mention.achievementName) ?? {
37707
- snippets: [],
37708
- phaseKeys: [],
37709
- sources: [],
37710
- globalPercent: mention.globalPercent
37711
- };
37712
- existing.snippets.push(mention.snippet);
37713
- existing.phaseKeys.push(mention.phaseKey);
37714
- existing.sources.push(mention.source.title);
37715
- existing.globalPercent = existing.globalPercent ?? mention.globalPercent;
37716
- byAchievement.set(mention.achievementName, existing);
37717
- }
37718
- }
37719
- return [...byAchievement.entries()].map(([achievement, value]) => ({
37720
- achievement,
37721
- suggestedPhase: mostCommon(value.phaseKeys),
37722
- globalPercent: value.globalPercent ?? null,
37723
- evidence: uniqueStrings(value.snippets).slice(0, 2),
37724
- sources: uniqueStrings(value.sources)
37725
- })).sort((left, right) => {
37726
- const leftGlobal = toNumber(left.globalPercent) ?? -1;
37727
- const rightGlobal = toNumber(right.globalPercent) ?? -1;
37728
- return rightGlobal - leftGlobal;
37729
- }).slice(0, maxAchievementNotes);
37730
- }
37731
- function buildGuideSummary(guides, routePhases, missables) {
37732
- const coOpSignals = guides.flatMap(
37733
- (guide) => guide.attention.filter((item) => item.category === "co_op")
37734
- );
37735
- const difficultySignals = guides.flatMap(
37736
- (guide) => guide.attention.filter((item) => item.category === "difficulty")
37737
- );
37738
- const cleanupSignals = guides.flatMap(
37739
- (guide) => guide.attention.filter((item) => item.category === "cleanup")
37740
- );
37741
- const digest = [];
37742
- if (coOpSignals.length > 0) {
37743
- digest.push({
37744
- topic: "co_op",
37745
- summary: "Selected Steam Guides indicate that some achievements require co-op, multiplayer, or another player.",
37746
- evidenceCount: coOpSignals.length,
37747
- sources: uniqueStrings(coOpSignals.map((item) => item.source.title))
37748
- });
37749
- }
37750
- if (difficultySignals.length > 0) {
37751
- digest.push({
37752
- topic: "difficulty",
37753
- summary: "Selected Steam Guides call out harder or rarer targets that may need extra practice, planning, or setup.",
37754
- evidenceCount: difficultySignals.length,
37755
- sources: uniqueStrings(difficultySignals.map((item) => item.source.title))
37756
- });
37757
37853
  }
37758
- if (cleanupSignals.length > 0 || routePhases.some((phase) => phase.key === "cleanup")) {
37759
- digest.push({
37760
- topic: "cleanup",
37761
- summary: "The roadmap keeps a final cleanup phase for anything not clearly placed into story or co-op progression.",
37762
- evidenceCount: cleanupSignals.length,
37763
- sources: uniqueStrings(cleanupSignals.map((item) => item.source.title))
37764
- });
37854
+ if (bundleid) {
37855
+ return {
37856
+ type: "bundle",
37857
+ id: bundleid,
37858
+ resolvedFrom: "bundleid",
37859
+ warnings: []
37860
+ };
37765
37861
  }
37766
- if (missables.length > 0) {
37767
- digest.push({
37768
- topic: "missables",
37769
- summary: "The selected Steam Guides contain explicit missable or timing-sensitive warnings.",
37770
- evidenceCount: missables.length,
37771
- sources: uniqueStrings(missables.map((item) => item.source.title))
37772
- });
37862
+ if (!appid && !query) {
37863
+ throw new Error("steam_get_price_overview requires appid, subid, bundleid, or query.");
37773
37864
  }
37774
- return digest;
37775
- }
37776
- function dedupeMentions(mentions) {
37777
- const seen = /* @__PURE__ */ new Set();
37778
- return mentions.filter((mention) => {
37779
- const key = `${mention.achievementName}:${mention.phaseKey}:${normalizeSearchText(mention.snippet)}`;
37780
- if (seen.has(key)) {
37781
- return false;
37782
- }
37783
- seen.add(key);
37784
- return true;
37865
+ const resolved = await runtime2.resolveAppSelection({
37866
+ toolName: input.toolName,
37867
+ appid,
37868
+ query,
37869
+ key: input.key,
37870
+ limit: 5,
37871
+ forceRefresh: false,
37872
+ locale: input.language,
37873
+ preferLocalizedName: Boolean(query) || Boolean(input.language)
37785
37874
  });
37875
+ return {
37876
+ type: "app",
37877
+ id: resolved.appid,
37878
+ resolvedFrom: query ? "query" : "appid",
37879
+ query,
37880
+ appTarget: resolved.target,
37881
+ warnings: resolved.warnings
37882
+ };
37786
37883
  }
37787
- function dedupeAttentionItems(items) {
37788
- const seen = /* @__PURE__ */ new Set();
37789
- return items.filter((item) => {
37790
- const key = `${item.category}:${normalizeSearchText(item.text)}`;
37791
- if (seen.has(key)) {
37792
- return false;
37793
- }
37794
- seen.add(key);
37795
- return true;
37884
+ async function fetchAugmentedSteamPrices(runtime2, body) {
37885
+ const response = await fetchWithProxy(AUGMENTED_STEAM_PRICES_ENDPOINT, {
37886
+ method: "POST",
37887
+ headers: {
37888
+ accept: "application/json",
37889
+ "content-type": "application/json",
37890
+ "user-agent": PACKAGE_USER_AGENT
37891
+ },
37892
+ body: JSON.stringify(body),
37893
+ signal: AbortSignal.timeout(runtime2.config.timeoutMs)
37796
37894
  });
37797
- }
37798
- function detectPhaseKey(line) {
37799
- const normalized = normalizeSearchText(line);
37800
- if (!normalized) {
37801
- return null;
37802
- }
37803
- if (normalized.includes("before you start") || normalized === "overview" || normalized === "notes" || normalized.includes("setup")) {
37804
- return "preparation";
37805
- }
37806
- if (normalized.includes("singleplayer") || normalized.includes("campaign") || normalized.includes("main story") || normalized.includes("story")) {
37807
- return "main_story";
37808
- }
37809
- if (normalized.includes("co op") || normalized.includes("coop") || normalized.includes("multiplayer")) {
37810
- return "co_op";
37811
- }
37812
- if (normalized.includes("challenge") || normalized.includes("hard") || normalized.includes("rarest")) {
37813
- return "challenge";
37895
+ const text = await response.text();
37896
+ if (!response.ok) {
37897
+ throw new Error(
37898
+ `Augmented Steam prices request failed with ${response.status} ${response.statusText}.`
37899
+ );
37814
37900
  }
37815
- if (normalized.includes("cleanup") || normalized.includes("remaining") || normalized.includes("post game")) {
37816
- return "cleanup";
37901
+ try {
37902
+ return JSON.parse(text);
37903
+ } catch (error) {
37904
+ throw new Error(
37905
+ `Augmented Steam prices response was not valid JSON: ${error instanceof Error ? error.message : String(error)}`
37906
+ );
37817
37907
  }
37818
- return null;
37819
37908
  }
37820
- function classifyAttentionCategory(line) {
37821
- const normalized = normalizeSearchText(line);
37822
- if (!normalized || normalized.length < 12) {
37909
+ function normalizePriceNode(value) {
37910
+ if (!value) {
37823
37911
  return null;
37824
37912
  }
37825
- if (/missable|do not miss|dont miss|don't miss|before finishing|before ending|last chance|point of no return|cannot go back/.test(
37826
- normalized
37827
- )) {
37828
- return "missable";
37829
- }
37830
- if (/co op|coop|multiplayer|friend|partner|another player|online/.test(normalized)) {
37831
- return "co_op";
37832
- }
37833
- if (/hard|hardest|rarest|practice|difficult|challenge/.test(normalized)) {
37834
- return "difficulty";
37835
- }
37836
- if (/cleanup|replay|chapter select|post game|remaining/.test(normalized)) {
37837
- return "cleanup";
37838
- }
37839
- if (/setup|console|bind|command|config|save file|backup/.test(normalized)) {
37840
- return "setup";
37841
- }
37842
- if (/warning|important|note/.test(normalized)) {
37843
- return "warning";
37844
- }
37845
- return null;
37846
- }
37847
- function buildSnippet(lines, index) {
37848
- return lines.slice(Math.max(0, index - 1), Math.min(lines.length, index + 2)).join(" ").slice(0, 320);
37849
- }
37850
- function buildAchievementMap(items) {
37851
- return items.map((item) => {
37852
- const name = getStringValue(item.displayName) ?? getStringValue(item.apiName) ?? void 0;
37853
- if (!name) {
37854
- return null;
37855
- }
37856
- return {
37857
- name,
37858
- normalizedName: normalizeSearchText(name),
37859
- globalPercent: toNumber(item.globalPercent)
37860
- };
37861
- }).filter(isPresent);
37913
+ return {
37914
+ shop: normalizeShop(asRecord(value.shop)),
37915
+ price: normalizeMoney(asRecord(value.price)),
37916
+ regular: normalizeMoney(asRecord(value.regular)),
37917
+ cut: toNumber(value.cut),
37918
+ voucher: value.voucher ?? null,
37919
+ flag: value.flag ?? null,
37920
+ drm: Array.isArray(value.drm) ? value.drm : [],
37921
+ platforms: Array.isArray(value.platforms) ? value.platforms.map((entry) => normalizePlatform(asRecord(entry))).filter((entry) => Boolean(entry)) : [],
37922
+ timestamp: getStringValue(value.timestamp),
37923
+ expiry: getStringValue(value.expiry),
37924
+ url: getStringValue(value.url)
37925
+ };
37862
37926
  }
37863
- function parseCount(line) {
37864
- const match = line.match(/(\d[\d,]*)/);
37865
- if (!match) {
37866
- return void 0;
37927
+ function normalizePriceUrls(value) {
37928
+ if (!value) {
37929
+ return null;
37867
37930
  }
37868
- return Number.parseInt(match[1].replace(/,/g, ""), 10);
37931
+ return {
37932
+ info: getStringValue(value.info),
37933
+ history: getStringValue(value.history),
37934
+ bundles: getStringValue(value.bundles)
37935
+ };
37869
37936
  }
37870
- function extractGuideId(url) {
37871
- try {
37872
- const parsed = new URL(url);
37873
- const guideId = parsed.searchParams.get("id");
37874
- return guideId ?? void 0;
37875
- } catch {
37876
- return void 0;
37937
+ function normalizeShop(value) {
37938
+ if (!value) {
37939
+ return null;
37877
37940
  }
37941
+ return {
37942
+ id: toNumber(value.id),
37943
+ name: getStringValue(value.name)
37944
+ };
37878
37945
  }
37879
- function buildPhaseSummary(phaseKey, achievementCount, noteCount) {
37880
- switch (phaseKey) {
37881
- case "preparation":
37882
- return `Use this phase to capture setup notes and pre-run warnings before starting. (${achievementCount} named achievements, ${noteCount} notes)`;
37883
- case "main_story":
37884
- return `Prioritize the achievements the selected Steam Guides place alongside normal story or campaign progress. (${achievementCount} named achievements, ${noteCount} notes)`;
37885
- case "co_op":
37886
- return `Set aside the achievements the selected Steam Guides associate with co-op, multiplayer, or another player. (${achievementCount} named achievements, ${noteCount} notes)`;
37887
- case "challenge":
37888
- return `Leave the rarer or harder targets for a focused pass once the easier progression is done. (${achievementCount} named achievements, ${noteCount} notes)`;
37889
- case "cleanup":
37890
- return `Finish anything not clearly placed by the guides in a final cleanup pass. (${achievementCount} named achievements, ${noteCount} notes)`;
37891
- default:
37892
- return `${achievementCount} achievements and ${noteCount} notes`;
37946
+ function normalizePlatform(value) {
37947
+ if (!value) {
37948
+ return null;
37893
37949
  }
37950
+ return {
37951
+ id: toNumber(value.id),
37952
+ name: getStringValue(value.name)
37953
+ };
37894
37954
  }
37895
- function mostCommon(values) {
37896
- if (values.length === 0) {
37955
+ function normalizeMoney(value) {
37956
+ if (!value) {
37897
37957
  return null;
37898
37958
  }
37899
- const counts = /* @__PURE__ */ new Map();
37900
- for (const value of values) {
37901
- counts.set(value, (counts.get(value) ?? 0) + 1);
37959
+ return {
37960
+ amount: toNumber(value.amount),
37961
+ amountInt: toNumber(value.amountInt),
37962
+ currency: getStringValue(value.currency)
37963
+ };
37964
+ }
37965
+ function parseShopIds(value) {
37966
+ if (value === void 0) {
37967
+ return DEFAULT_SHOP_IDS;
37902
37968
  }
37903
- return [...counts.entries()].sort((left, right) => right[1] - left[1])[0]?.[0] ?? null;
37969
+ const values = Array.isArray(value) ? value : [value];
37970
+ const shopIds = [...new Set(
37971
+ values.flatMap(
37972
+ (entry) => typeof entry === "string" ? entry.split(/[,\s]+/) : [String(entry)]
37973
+ ).map((entry) => normalizeScalarInput(entry)).map((entry) => entry ? Number.parseInt(entry, 10) : Number.NaN).filter((entry) => Number.isFinite(entry) && entry > 0)
37974
+ )];
37975
+ return shopIds.length > 0 ? shopIds : DEFAULT_SHOP_IDS;
37904
37976
  }
37905
- function getWarnings(payload) {
37906
- const warnings = getNestedValue(payload, "warnings");
37907
- return Array.isArray(warnings) ? warnings : [];
37977
+ function normalizeCountryCode(value) {
37978
+ const normalized = value.trim().toLowerCase();
37979
+ if (!/^[a-z]{2}$/.test(normalized)) {
37980
+ throw new Error("country must be a two-letter ISO 3166-1 country code, such as 'cn' or 'us'.");
37981
+ }
37982
+ return normalized;
37908
37983
  }
37909
- function getRecordItems(value) {
37910
- return Array.isArray(value) ? value.map((entry) => asRecord(entry)).filter((entry) => Boolean(entry)) : [];
37984
+ function buildPriceToolTarget(target) {
37985
+ return {
37986
+ subject: {
37987
+ type: target.type,
37988
+ id: target.id,
37989
+ resolvedFrom: target.resolvedFrom,
37990
+ query: target.query
37991
+ },
37992
+ ...target.appTarget ? { app: target.appTarget } : {}
37993
+ };
37911
37994
  }
37912
- function cleanText(value) {
37913
- return value.replace(/\s+/g, " ").trim();
37995
+ function parseNumericId(value, fieldName) {
37996
+ const parsed = Number.parseInt(value, 10);
37997
+ if (!Number.isFinite(parsed) || parsed <= 0) {
37998
+ throw new Error(`${fieldName} must be a positive integer.`);
37999
+ }
38000
+ return parsed;
37914
38001
  }
37915
38002
 
37916
38003
  // src/tools/register-steam-tool.ts
@@ -38064,7 +38151,7 @@ function registerAppTools(server, runtime2) {
38064
38151
  "steam_get_price_overview",
38065
38152
  {
38066
38153
  title: "\u4EF7\u683C\u4E0E\u53F2\u4F4E",
38067
- description: "\u67E5\u770B app\u3001sub \u6216 bundle \u7684\u5F53\u524D\u6700\u4F4E\u4EF7\u3001\u5386\u53F2\u6700\u4F4E\u4EF7\u3001\u53F2\u4F4E\u65E5\u671F\uFF0C\u4EE5\u53CA\u5F53\u524D\u662F\u5426\u7B49\u4E8E\u53F2\u4F4E\u3002",
38154
+ description: "\u516C\u5171\u4EF7\u683C\u4E13\u9879\u89C6\u56FE\uFF0C\u67E5\u770B app\u3001sub \u6216 bundle \u7684\u5F53\u524D\u6700\u4F4E\u4EF7\u3001\u5386\u53F2\u6700\u4F4E\u4EF7\u3001\u53F2\u4F4E\u65E5\u671F\uFF0C\u4EE5\u53CA\u5F53\u524D\u662F\u5426\u7B49\u4E8E\u53F2\u4F4E\u3002",
38068
38155
  inputSchema: {
38069
38156
  appid: z2.union([z2.string(), z2.number()]).optional().describe("Steam AppID\u3002\u4E0E subid\u3001bundleid\u3001query \u56DB\u9009\u4E00\u3002"),
38070
38157
  subid: z2.union([z2.string(), z2.number()]).optional().describe("Steam Package/Sub ID\u3002\u4E0E appid\u3001bundleid\u3001query \u56DB\u9009\u4E00\u3002"),
@@ -38113,15 +38200,18 @@ function registerAppTools(server, runtime2) {
38113
38200
  runtime2,
38114
38201
  "steam_get_achievement_roadmap",
38115
38202
  {
38116
- title: "\u6210\u5C31\u8DEF\u7EBF\u56FE",
38117
- description: "\u4EC5\u57FA\u4E8E Steam \u6307\u5357\uFF0C\u4E3A\u67D0\u4E2A\u6E38\u620F\u751F\u6210\u5168\u6210\u5C31\u6CE8\u610F\u4E8B\u9879\u3001\u9636\u6BB5\u8DEF\u7EBF\u56FE\u548C\u6210\u5C31\u5907\u6CE8\u6458\u8981\u3002",
38203
+ title: "\u5168\u6210\u5C31\u653B\u7565",
38204
+ description: "\u516C\u5171\u5355\u6E38\u620F\u653B\u7565\u4E8B\u5B9E\u89C6\u56FE\uFF0C\u6574\u5408\u5B98\u65B9\u6210\u5C31\u6E05\u5355\u548C Steam \u6307\u5357\u4E2D\u7684\u53EF\u8FFD\u6EAF\u4E8B\u5B9E\uFF0C\u4F8B\u5982 missable\u3001co-op \u548C\u6761\u4EF6\u8BF4\u660E\u3002",
38118
38205
  inputSchema: {
38119
38206
  appid: z2.union([z2.string(), z2.number()]).optional().describe("Steam AppID\u3002\u4E0E query \u4E8C\u9009\u4E00\u3002"),
38120
38207
  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"),
38121
- key: z2.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\uFF1B\u6309 query \u89E3\u6790 app \u6216\u8865\u5168\u6210\u5C31\u5143\u6570\u636E\u65F6\u53EF\u80FD\u9700\u8981\u3002"),
38208
+ key: z2.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\uFF1B\u8865\u5168\u5B98\u65B9\u6210\u5C31 schema \u65F6\u53EF\u80FD\u9700\u8981\u3002"),
38122
38209
  language: z2.string().optional().describe("\u53EF\u9009\u8BED\u8A00\u4EE3\u7801\uFF0C\u4F8B\u5982 schinese \u6216 english\u3002"),
38123
- maxGuides: z2.number().int().min(1).max(8).optional().describe("\u6700\u591A\u6293\u53D6\u5E76\u6574\u5408\u591A\u5C11\u7BC7 Steam \u6307\u5357\uFF0C\u9ED8\u8BA4 3\u3002"),
38124
- maxAchievementNotes: z2.number().int().min(1).max(30).optional().describe("\u6700\u591A\u8FD4\u56DE\u591A\u5C11\u6761\u6210\u5C31\u7EA7\u522B\u5907\u6CE8\uFF0C\u9ED8\u8BA4 12\u3002")
38210
+ includeSchemaDetails: z2.boolean().optional().describe("\u662F\u5426\u8865\u5145\u5B98\u65B9\u6210\u5C31 schema \u7EC6\u8282\uFF0C\u9ED8\u8BA4 true\u3002"),
38211
+ maxGuides: z2.number().int().min(1).max(8).optional().describe("\u6700\u591A\u6293\u53D6\u591A\u5C11\u7BC7 Steam \u6307\u5357\uFF0C\u9ED8\u8BA4 3\u3002"),
38212
+ maxGuideFacts: z2.number().int().min(1).max(50).optional().describe("\u6700\u591A\u8FD4\u56DE\u591A\u5C11\u6761\u6307\u5357\u4E8B\u5B9E\uFF0C\u9ED8\u8BA4 20\u3002"),
38213
+ maxEvidencePerAchievement: z2.number().int().min(1).max(5).optional().describe("\u6BCF\u4E2A\u6210\u5C31\u6700\u591A\u4FDD\u7559\u591A\u5C11\u6761\u6307\u5357\u8BC1\u636E\uFF0C\u9ED8\u8BA4 2\u3002"),
38214
+ 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")
38125
38215
  },
38126
38216
  annotations: {
38127
38217
  readOnlyHint: true,
@@ -38133,16 +38223,22 @@ function registerAppTools(server, runtime2) {
38133
38223
  query,
38134
38224
  key,
38135
38225
  language,
38226
+ includeSchemaDetails = true,
38136
38227
  maxGuides = 3,
38137
- maxAchievementNotes = 12
38138
- }) => buildAchievementRoadmapData(runtime2, {
38228
+ maxGuideFacts = 20,
38229
+ maxEvidencePerAchievement = 2,
38230
+ topCount = 10
38231
+ }) => buildAchievementGuideData(runtime2, {
38139
38232
  toolName: "steam_get_achievement_roadmap",
38140
38233
  appid,
38141
38234
  query,
38142
38235
  key,
38143
38236
  language,
38237
+ includeSchemaDetails,
38144
38238
  maxGuides,
38145
- maxAchievementNotes
38239
+ maxGuideFacts,
38240
+ maxEvidencePerAchievement,
38241
+ topCount
38146
38242
  })
38147
38243
  );
38148
38244
  registerSteamTool(
@@ -38151,7 +38247,7 @@ function registerAppTools(server, runtime2) {
38151
38247
  "steam_get_global_achievement_overview",
38152
38248
  {
38153
38249
  title: "\u5168\u5C40\u6210\u5C31\u603B\u89C8",
38154
- 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",
38250
+ description: "\u516C\u5171\u5355\u6E38\u620F\u4E13\u9879\u89C6\u56FE\uFF0C\u67E5\u770B\u67D0\u4E2A\u6E38\u620F\u7684\u5168\u5C40\u6210\u5C31\u5B8C\u6210\u7387\uFF0C\u5E76\u53EF\u9009\u8865\u5145 schema \u7EC6\u8282\u3002",
38155
38251
  inputSchema: {
38156
38252
  appid: z2.union([z2.string(), z2.number()]).optional().describe("Steam AppID\u3002\u4E0E query \u4E8C\u9009\u4E00\u3002"),
38157
38253
  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"),
@@ -38250,7 +38346,7 @@ function buildDefaultPlayerTarget(identity) {
38250
38346
  }
38251
38347
  };
38252
38348
  }
38253
- function getWarnings2(payload) {
38349
+ function getWarnings(payload) {
38254
38350
  const warnings = getNestedValue(payload, "warnings");
38255
38351
  return Array.isArray(warnings) ? warnings : [];
38256
38352
  }
@@ -38487,7 +38583,7 @@ async function buildFriendActivityData(runtime2, input) {
38487
38583
  sections: {
38488
38584
  profile: getNestedValue(friendsData, "sections", "profile")
38489
38585
  },
38490
- warnings: getWarnings2(friendsData)
38586
+ warnings: getWarnings(friendsData)
38491
38587
  };
38492
38588
  }
38493
38589
  async function buildFriendNetworkData(runtime2, input) {
@@ -38556,7 +38652,7 @@ async function buildFriendNetworkData(runtime2, input) {
38556
38652
  },
38557
38653
  graph
38558
38654
  },
38559
- warnings: getWarnings2(friendsData)
38655
+ warnings: getWarnings(friendsData)
38560
38656
  };
38561
38657
  }
38562
38658
 
@@ -38615,8 +38711,8 @@ async function buildGameSnapshotData(runtime2, input) {
38615
38711
  const warnings = [
38616
38712
  ...resolved.warnings,
38617
38713
  ...runtime2.collectWarnings([playerSummaryCall, ownedGamesCall]),
38618
- ...getWarnings2(achievementOverview),
38619
- ...getWarnings2(newsOverview)
38714
+ ...getWarnings(achievementOverview),
38715
+ ...getWarnings(newsOverview)
38620
38716
  ];
38621
38717
  return {
38622
38718
  ok: true,
@@ -38747,7 +38843,7 @@ async function buildGameFeedData(runtime2, input) {
38747
38843
  currentPlayers: getNestedValue(newsOverview, "summary", "currentPlayers"),
38748
38844
  ownedContext: ownedContextMap.get(appid) ?? null,
38749
38845
  items: newsItems,
38750
- warnings: getWarnings2(newsOverview)
38846
+ warnings: getWarnings(newsOverview)
38751
38847
  };
38752
38848
  })
38753
38849
  );
@@ -38886,7 +38982,7 @@ async function buildBacklogCandidatesData(runtime2, input) {
38886
38982
  },
38887
38983
  warnings: [
38888
38984
  ...runtime2.collectWarnings([playerSummaryCall, ownedGamesCall]),
38889
- ...shortlist.flatMap((candidate) => getWarnings2(candidate))
38985
+ ...shortlist.flatMap((candidate) => getWarnings(candidate))
38890
38986
  ]
38891
38987
  };
38892
38988
  }
@@ -38981,7 +39077,7 @@ async function buildAchievementHuntData(runtime2, input) {
38981
39077
  getNestedValue(overview, "sections", "recentlyUnlocked")
38982
39078
  ),
38983
39079
  easiestRemaining,
38984
- warnings: getWarnings2(overview)
39080
+ warnings: getWarnings(overview)
38985
39081
  };
38986
39082
  } catch (error) {
38987
39083
  return {
@@ -39203,7 +39299,7 @@ function registerMyAccountTools(server, runtime2) {
39203
39299
  "steam_me_profile",
39204
39300
  {
39205
39301
  title: "\u6211\u7684\u8D44\u6599",
39206
- description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u6211\u7684 Steam \u8D44\u6599\u603B\u89C8\u3002",
39302
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u4E3B\u5165\u53E3\uFF0C\u67E5\u770B\u6211\u7684 Steam \u8D44\u6599\u603B\u89C8\u3002",
39207
39303
  inputSchema: {
39208
39304
  key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
39209
39305
  includeRecentlyPlayed: z3.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6700\u8FD1\u6E38\u73A9\uFF0C\u9ED8\u8BA4 true\u3002"),
@@ -39230,7 +39326,7 @@ function registerMyAccountTools(server, runtime2) {
39230
39326
  "steam_me_library",
39231
39327
  {
39232
39328
  title: "\u6211\u7684\u6E38\u620F\u5E93",
39233
- description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u6211\u7684\u6E38\u620F\u5E93\u603B\u89C8\u3002",
39329
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u4E3B\u5165\u53E3\uFF0C\u67E5\u770B\u6211\u7684\u6E38\u620F\u5E93\u603B\u89C8\u3002",
39234
39330
  inputSchema: {
39235
39331
  key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
39236
39332
  includeAppInfo: z3.boolean().optional().describe("\u662F\u5426\u5305\u542B\u6E38\u620F\u540D\u79F0\u548C\u56FE\u6807\uFF0C\u9ED8\u8BA4 true\u3002"),
@@ -39270,7 +39366,7 @@ function registerMyAccountTools(server, runtime2) {
39270
39366
  "steam_me_recently_played",
39271
39367
  {
39272
39368
  title: "\u6211\u7684\u6700\u8FD1\u6E38\u73A9",
39273
- description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u6700\u8FD1\u6E38\u73A9\u7684\u6E38\u620F\u3002",
39369
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u53EA\u770B\u6700\u8FD1\u6E38\u73A9\u7684\u6E38\u620F\u3002",
39274
39370
  inputSchema: {
39275
39371
  key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
39276
39372
  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")
@@ -39295,7 +39391,7 @@ function registerMyAccountTools(server, runtime2) {
39295
39391
  "steam_me_badges",
39296
39392
  {
39297
39393
  title: "\u6211\u7684\u5FBD\u7AE0",
39298
- description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u6211\u7684\u7B49\u7EA7\u548C\u5FBD\u7AE0\u4FE1\u606F\u3002",
39394
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u53EA\u770B\u6211\u7684\u7B49\u7EA7\u548C\u5FBD\u7AE0\u4FE1\u606F\u3002",
39299
39395
  inputSchema: {
39300
39396
  key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002")
39301
39397
  },
@@ -39318,7 +39414,7 @@ function registerMyAccountTools(server, runtime2) {
39318
39414
  "steam_me_game_achievements",
39319
39415
  {
39320
39416
  title: "\u6211\u7684\u6E38\u620F\u6210\u5C31",
39321
- 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",
39417
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5355\u6E38\u620F\u4E13\u9879\u89C6\u56FE\uFF0C\u67E5\u770B\u6211\u5728\u67D0\u4E2A\u6E38\u620F\u91CC\u7684\u6210\u5C31\u548C\u5B8C\u6210\u7387\u3002",
39322
39418
  inputSchema: {
39323
39419
  appid: z3.union([z3.string(), z3.number()]).optional().describe("Steam AppID\u3002\u4E0E query \u4E8C\u9009\u4E00\u3002"),
39324
39420
  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"),
@@ -39358,7 +39454,7 @@ function registerMyAccountTools(server, runtime2) {
39358
39454
  "steam_me_friends",
39359
39455
  {
39360
39456
  title: "\u597D\u53CB\u5217\u8868",
39361
- 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",
39457
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u4E13\u9879\u89C6\u56FE\uFF0C\u67E5\u770B\u597D\u53CB\u5217\u8868\uFF0C\u53EF\u9009\u8865\u5145\u8D44\u6599\u548C\u5C01\u7981\u4FE1\u606F\u3002",
39362
39458
  inputSchema: {
39363
39459
  key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
39364
39460
  relationship: z3.string().optional().describe("\u597D\u53CB\u5173\u7CFB\u8FC7\u6EE4\u6761\u4EF6\uFF0C\u9ED8\u8BA4 friend\u3002"),
@@ -39395,7 +39491,7 @@ function registerMyAccountTools(server, runtime2) {
39395
39491
  "steam_me_friend_activity",
39396
39492
  {
39397
39493
  title: "\u597D\u53CB\u5728\u7EBF\u52A8\u6001",
39398
- 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",
39494
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u4E13\u9879\u89C6\u56FE\uFF0C\u67E5\u770B\u54EA\u4E9B\u597D\u53CB\u5728\u7EBF\u3001\u5728\u73A9\u6E38\u620F\u6216\u6700\u8FD1\u6D3B\u8DC3\u3002",
39399
39495
  inputSchema: {
39400
39496
  key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
39401
39497
  limit: z3.number().int().min(1).max(500).optional().describe("\u6700\u591A\u68C0\u67E5\u591A\u5C11\u4F4D\u597D\u53CB\uFF0C\u9ED8\u8BA4 100\u3002"),
@@ -39424,7 +39520,7 @@ function registerMyAccountTools(server, runtime2) {
39424
39520
  "steam_me_friend_network",
39425
39521
  {
39426
39522
  title: "\u597D\u53CB\u5173\u7CFB\u56FE\u8C31",
39427
- description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u751F\u6210\u597D\u53CB\u5173\u7CFB\u548C\u6D3B\u52A8\u5206\u7EC4\u89C6\u56FE\u3002",
39523
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u4E13\u9879\u89C6\u56FE\uFF0C\u751F\u6210\u597D\u53CB\u5173\u7CFB\u548C\u6D3B\u52A8\u5206\u7EC4\u89C6\u56FE\u3002",
39428
39524
  inputSchema: {
39429
39525
  key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
39430
39526
  limit: z3.number().int().min(1).max(500).optional().describe("\u6700\u591A\u68C0\u67E5\u591A\u5C11\u4F4D\u597D\u53CB\uFF0C\u9ED8\u8BA4 150\u3002"),
@@ -39465,7 +39561,7 @@ function registerMyAccountTools(server, runtime2) {
39465
39561
  "steam_me_bans",
39466
39562
  {
39467
39563
  title: "\u6211\u7684\u5C01\u7981\u6458\u8981",
39468
- description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u67E5\u770B\u6211\u7684\u5C01\u7981\u72B6\u6001\u3002",
39564
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u53EA\u770B\u6211\u7684\u5C01\u7981\u72B6\u6001\u3002",
39469
39565
  inputSchema: {
39470
39566
  key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002")
39471
39567
  },
@@ -39488,7 +39584,7 @@ function registerMyAccountTools(server, runtime2) {
39488
39584
  "steam_me_library_compare",
39489
39585
  {
39490
39586
  title: "\u6211\u7684\u5E93\u5BF9\u6BD4",
39491
- description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u628A\u6211\u7684\u5E93\u548C\u53E6\u4E00\u4F4D\u73A9\u5BB6\u505A\u5BF9\u6BD4\u3002",
39587
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u4E13\u9879\u89C6\u56FE\uFF0C\u628A\u6211\u7684\u5E93\u548C\u53E6\u4E00\u4F4D\u73A9\u5BB6\u505A\u5BF9\u6BD4\u3002",
39492
39588
  inputSchema: {
39493
39589
  steamid: z3.union([z3.string(), z3.number()]).optional().describe("\u5BF9\u65B9\u73A9\u5BB6\u7684 SteamID64\u3002\u4E0E vanityUrl \u4E8C\u9009\u4E00\u3002"),
39494
39590
  vanityUrl: z3.string().optional().describe("\u5BF9\u65B9\u73A9\u5BB6\u7684\u81EA\u5B9A\u4E49\u4E3B\u9875\u6807\u8BC6\u3002\u4E0E steamid \u4E8C\u9009\u4E00\u3002"),
@@ -39528,7 +39624,7 @@ function registerMyAccountTools(server, runtime2) {
39528
39624
  "steam_me_backlog_candidates",
39529
39625
  {
39530
39626
  title: "\u6211\u7684\u79EF\u538B\u5019\u9009",
39531
- 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",
39627
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u4E13\u9879\u89C6\u56FE\uFF0C\u4ECE\u6211\u7684\u5E93\u91CC\u6311\u51FA\u503C\u5F97\u5F00\u5751\u7684\u79EF\u538B\u6E38\u620F\u3002",
39532
39628
  inputSchema: {
39533
39629
  key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
39534
39630
  limit: z3.number().int().min(1).max(30).optional().describe("\u6700\u591A\u8FD4\u56DE\u591A\u5C11\u4E2A\u5019\u9009\uFF0C\u9ED8\u8BA4 12\u3002"),
@@ -39571,7 +39667,7 @@ function registerMyAccountTools(server, runtime2) {
39571
39667
  "steam_me_achievement_hunt",
39572
39668
  {
39573
39669
  title: "\u6211\u7684\u6210\u5C31\u8865\u5B8C\u5019\u9009",
39574
- description: "\u9ED8\u8BA4\u8D26\u53F7\u5FEB\u6377\u5165\u53E3\uFF0C\u627E\u51FA\u79BB\u5168\u6210\u5C31\u4E0D\u8FDC\u7684\u6E38\u620F\u3002",
39670
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u4E13\u9879\u89C6\u56FE\uFF0C\u627E\u51FA\u79BB\u5168\u6210\u5C31\u4E0D\u8FDC\u7684\u6E38\u620F\u3002",
39575
39671
  inputSchema: {
39576
39672
  key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
39577
39673
  language: z3.string().optional().describe("\u8BED\u8A00\u4EE3\u7801\uFF0C\u4F8B\u5982 schinese \u6216 english\u3002"),
@@ -39620,7 +39716,7 @@ function registerMyAccountTools(server, runtime2) {
39620
39716
  "steam_me_game_snapshot",
39621
39717
  {
39622
39718
  title: "\u6211\u7684\u6E38\u620F\u5FEB\u7167",
39623
- 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",
39719
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u5355\u6E38\u620F\u4E3B\u5165\u53E3\uFF0C\u67E5\u770B\u6211\u4E0E\u67D0\u4E2A\u6E38\u620F\u7684\u5173\u7CFB\u3001\u65B0\u95FB\u548C\u6210\u5C31\u3002",
39624
39720
  inputSchema: {
39625
39721
  appid: z3.union([z3.string(), z3.number()]).optional().describe("Steam AppID\u3002\u4E0E query \u4E8C\u9009\u4E00\u3002"),
39626
39722
  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"),
@@ -39673,7 +39769,7 @@ function registerMyAccountTools(server, runtime2) {
39673
39769
  "steam_me_game_feed",
39674
39770
  {
39675
39771
  title: "\u6211\u7684\u6E38\u620F\u52A8\u6001\u6D41",
39676
- 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",
39772
+ description: "\u9ED8\u8BA4\u8D26\u53F7\u4E13\u9879\u89C6\u56FE\uFF0C\u6309\u6700\u8FD1\u6E38\u73A9\u3001\u5E38\u73A9\u6216\u663E\u5F0F App \u751F\u6210\u4E2A\u4EBA\u6E38\u620F\u52A8\u6001\u6D41\u3002",
39677
39773
  inputSchema: {
39678
39774
  key: z3.string().optional().describe("\u53EF\u9009\u7684 Steam Web API key \u8986\u76D6\u503C\u3002"),
39679
39775
  source: z3.enum(["recent", "topOwned", "mixed", "explicit"]).optional().describe("\u52A8\u6001\u6D41\u7684\u6E38\u620F\u6765\u6E90\uFF0C\u9ED8\u8BA4 recent\u3002"),
@@ -39725,7 +39821,7 @@ function registerOverviewTools(server, runtime2) {
39725
39821
  "steam_get_player_profile_overview",
39726
39822
  {
39727
39823
  title: "\u73A9\u5BB6\u8D44\u6599\u603B\u89C8",
39728
- description: "\u805A\u5408\u73A9\u5BB6\u8D44\u6599\u3001\u5C01\u7981\u3001\u7B49\u7EA7\u3001\u5FBD\u7AE0\u548C\u6700\u8FD1\u6E38\u73A9\u4FE1\u606F\u3002",
39824
+ description: "\u901A\u7528\u73A9\u5BB6\u4E3B\u5165\u53E3\uFF0C\u805A\u5408\u73A9\u5BB6\u8D44\u6599\u3001\u5C01\u7981\u3001\u7B49\u7EA7\u3001\u5FBD\u7AE0\u548C\u6700\u8FD1\u6E38\u73A9\u4FE1\u606F\u3002",
39729
39825
  inputSchema: {
39730
39826
  steamid: z4.union([z4.string(), z4.number()]).optional().describe("\u73A9\u5BB6\u7684 SteamID64\u3002\u4E0E vanityUrl \u4E8C\u9009\u4E00\u3002"),
39731
39827
  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"),
@@ -39759,7 +39855,7 @@ function registerOverviewTools(server, runtime2) {
39759
39855
  "steam_get_library_overview",
39760
39856
  {
39761
39857
  title: "\u6E38\u620F\u5E93\u603B\u89C8",
39762
- description: "\u805A\u5408\u73A9\u5BB6\u62E5\u6709\u7684\u6E38\u620F\u3001\u6700\u8FD1\u6E38\u73A9\u548C\u5E38\u73A9\u6E38\u620F\u3002",
39858
+ description: "\u901A\u7528\u73A9\u5BB6\u6E38\u620F\u5E93\u4E3B\u5165\u53E3\uFF0C\u805A\u5408\u73A9\u5BB6\u62E5\u6709\u7684\u6E38\u620F\u3001\u6700\u8FD1\u6E38\u73A9\u548C\u5E38\u73A9\u6E38\u620F\u3002",
39763
39859
  inputSchema: {
39764
39860
  steamid: z4.union([z4.string(), z4.number()]).optional().describe("\u73A9\u5BB6\u7684 SteamID64\u3002\u4E0E vanityUrl \u4E8C\u9009\u4E00\u3002"),
39765
39861
  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"),
@@ -39802,7 +39898,7 @@ function registerOverviewTools(server, runtime2) {
39802
39898
  "steam_get_player_game_achievements",
39803
39899
  {
39804
39900
  title: "\u73A9\u5BB6\u6E38\u620F\u6210\u5C31",
39805
- 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",
39901
+ description: "\u901A\u7528\u73A9\u5BB6\u5355\u6E38\u620F\u4E13\u9879\u89C6\u56FE\uFF0C\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",
39806
39902
  inputSchema: {
39807
39903
  steamid: z4.union([z4.string(), z4.number()]).optional().describe("\u73A9\u5BB6\u7684 SteamID64\u3002\u4E0E vanityUrl \u4E8C\u9009\u4E00\u3002"),
39808
39904
  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"),
@@ -39845,7 +39941,7 @@ function registerOverviewTools(server, runtime2) {
39845
39941
  "steam_get_app_news",
39846
39942
  {
39847
39943
  title: "\u6E38\u620F\u65B0\u95FB",
39848
- description: "\u67E5\u770B\u67D0\u4E2A\u6E38\u620F\u7684 Steam \u65B0\u95FB\uFF0C\u5E76\u53EF\u9009\u9644\u5E26\u5F53\u524D\u5728\u7EBF\u4EBA\u6570\u3002",
39944
+ description: "\u516C\u5171\u5355\u6E38\u620F\u4E13\u9879\u89C6\u56FE\uFF0C\u67E5\u770B\u67D0\u4E2A\u6E38\u620F\u7684 Steam \u65B0\u95FB\uFF0C\u5E76\u53EF\u9009\u9644\u5E26\u5F53\u524D\u5728\u7EBF\u4EBA\u6570\u3002",
39849
39945
  inputSchema: {
39850
39946
  appid: z4.union([z4.string(), z4.number()]).optional().describe("Steam AppID\u3002\u4E0E query \u4E8C\u9009\u4E00\u3002"),
39851
39947
  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"),
@@ -39894,8 +39990,8 @@ var runtime = createSteamBaseRuntime(spec, {
39894
39990
  });
39895
39991
  function createServer() {
39896
39992
  const server = new McpServer({
39897
- name: "steam-tools-mcp",
39898
- version: "0.2.0",
39993
+ name: PACKAGE_NAME,
39994
+ version: PACKAGE_VERSION,
39899
39995
  websiteUrl: "https://partner.steamgames.com/doc/webapi"
39900
39996
  });
39901
39997
  registerOverviewTools(server, runtime);