@deplens/mcp 0.1.3 → 0.1.5

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 (2) hide show
  1. package/package.json +2 -2
  2. package/src/core/inspect.mjs +176 -72
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deplens/mcp",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "main": "./src/server.mjs",
6
6
  "bin": {
@@ -17,7 +17,7 @@
17
17
  "node": ">=18"
18
18
  },
19
19
  "dependencies": {
20
- "@deplens/core": "0.1.2",
20
+ "@deplens/core": "0.1.5",
21
21
  "@modelcontextprotocol/sdk": "^1.25.1"
22
22
  },
23
23
  "publishConfig": {
@@ -135,7 +135,34 @@ async function resolveTargetModule(target, cwd, resolveFrom) {
135
135
  return { resolved: null, resolveCwd: baseDir, resolver: null }
136
136
  }
137
137
 
138
- function resolvePackageInfo(basePkg, require) {
138
+ function findPackageJsonFromPath(startPath) {
139
+ if (!startPath) return null
140
+ let dir = path.dirname(startPath)
141
+ for (let i = 0; i < 10; i++) {
142
+ const candidate = path.join(dir, "package.json")
143
+ if (fs.existsSync(candidate)) return candidate
144
+ const parent = path.dirname(dir)
145
+ if (parent === dir) break
146
+ dir = parent
147
+ }
148
+ return null
149
+ }
150
+
151
+ function findPackageJsonInNodeModules(startDir, basePkg) {
152
+ if (!startDir || !basePkg) return null
153
+ const segments = basePkg.split("/")
154
+ let dir = path.resolve(startDir)
155
+ while (true) {
156
+ const candidate = path.join(dir, "node_modules", ...segments, "package.json")
157
+ if (fs.existsSync(candidate)) return candidate
158
+ const parent = path.dirname(dir)
159
+ if (parent === dir) break
160
+ dir = parent
161
+ }
162
+ return null
163
+ }
164
+
165
+ function resolvePackageInfo(basePkg, require, resolveFrom, resolvedPath) {
139
166
  let pkgPath
140
167
  let pkgDir
141
168
  try {
@@ -157,6 +184,30 @@ function resolvePackageInfo(basePkg, require) {
157
184
  } catch (err) {}
158
185
  }
159
186
 
187
+ if (!pkgPath && resolveFrom && basePkg) {
188
+ const fallback = findPackageJsonInNodeModules(resolveFrom, basePkg)
189
+ if (fallback) {
190
+ pkgPath = fallback
191
+ pkgDir = path.dirname(fallback)
192
+ }
193
+ }
194
+
195
+ if (!pkgPath && resolvedPath) {
196
+ const fallback = findPackageJsonFromPath(resolvedPath)
197
+ if (fallback) {
198
+ pkgPath = fallback
199
+ pkgDir = path.dirname(fallback)
200
+ }
201
+ }
202
+
203
+ if (resolveFrom && basePkg) {
204
+ const rootCandidate = findPackageJsonInNodeModules(resolveFrom, basePkg)
205
+ if (rootCandidate && rootCandidate !== pkgPath) {
206
+ pkgPath = rootCandidate
207
+ pkgDir = path.dirname(rootCandidate)
208
+ }
209
+ }
210
+
160
211
  if (!pkgPath || !fs.existsSync(pkgPath)) return null
161
212
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
162
213
  return { pkg, pkgPath, pkgDir }
@@ -267,6 +318,29 @@ function resolveTypesFile(pkg, pkgDir, subpath) {
267
318
  }
268
319
  }
269
320
 
321
+ if (!fs.existsSync(dtsPath)) {
322
+ const altDir = path.dirname(dtsPath)
323
+ const altCandidates = [
324
+ "types.d.ts",
325
+ "types.d.mts",
326
+ "types.d.cts",
327
+ "index.d.ts",
328
+ "index.d.mts",
329
+ "index.d.cts",
330
+ ]
331
+ for (const candidate of altCandidates) {
332
+ const altPath = path.join(altDir, candidate)
333
+ if (fs.existsSync(altPath)) {
334
+ dtsPath = altPath
335
+ typesFile = path.relative(pkgDir, altPath)
336
+ if (!source || source === "exports" || source === "package") {
337
+ source = "fallback"
338
+ }
339
+ break
340
+ }
341
+ }
342
+ }
343
+
270
344
  return { typesFile, dtsPath, source }
271
345
  }
272
346
 
@@ -585,6 +659,12 @@ export async function runInspect(options) {
585
659
  const resolution = await resolveTargetModule(target, baseCwd, resolveFrom)
586
660
  const resolveCwd = resolution.resolveCwd || baseCwd
587
661
  const require = createRequire(resolveCwd ? path.join(resolveCwd, "noop.js") : import.meta.url)
662
+ const resolvedPath = resolution.resolved
663
+ const entrypointPath =
664
+ typeof resolvedPath === "string" && resolvedPath.startsWith("file://")
665
+ ? fileURLToPath(resolvedPath)
666
+ : resolvedPath
667
+ const entrypointExists = entrypointPath ? fs.existsSync(entrypointPath) : false
588
668
 
589
669
  const flags = []
590
670
  if (filter) flags.push(`Filtro: "${filter}"`)
@@ -606,7 +686,9 @@ export async function runInspect(options) {
606
686
 
607
687
  const basePkg = getPackageName(target)
608
688
  const subpath = getPackageSubpath(target)
609
- const pkgInfo = basePkg ? resolvePackageInfo(basePkg, require) : null
689
+ const pkgInfo = basePkg
690
+ ? resolvePackageInfo(basePkg, require, resolveFrom || baseCwd || process.cwd(), entrypointPath)
691
+ : null
610
692
  const pkg = pkgInfo?.pkg
611
693
  const pkgDir = pkgInfo?.pkgDir
612
694
 
@@ -673,9 +755,18 @@ export async function runInspect(options) {
673
755
  typeInfoRaw = parseDtsFile(dtsPath, null)
674
756
  }
675
757
 
676
- const { module: moduleNamespace } = await loadModuleExports(resolution.resolved, require, pkg)
677
- const moduleDescriptors = Object.getOwnPropertyDescriptors(moduleNamespace)
678
- const allExports = Object.keys(moduleDescriptors)
758
+ let moduleNamespace = {}
759
+ let moduleDescriptors = {}
760
+ let allExports = []
761
+ const runtimeAvailable = Boolean(entrypointExists)
762
+ if (!runtimeAvailable) {
763
+ log(`\n⚠️ Entrypoint not found on disk; runtime exports skipped.`)
764
+ } else {
765
+ const { module: loadedNamespace } = await loadModuleExports(entrypointPath, require, pkg)
766
+ moduleNamespace = loadedNamespace
767
+ moduleDescriptors = Object.getOwnPropertyDescriptors(moduleNamespace)
768
+ allExports = Object.keys(moduleDescriptors)
769
+ }
679
770
 
680
771
  // Lógica de Filtro
681
772
  let finalList = allExports
@@ -700,67 +791,74 @@ export async function runInspect(options) {
700
791
  constants: [],
701
792
  }
702
793
 
703
- for (const key of finalList) {
704
- const descriptor = moduleDescriptors[key]
705
- if (!descriptor) continue
706
- if (descriptor.get || descriptor.set) {
707
- categorized.objects.push(key)
708
- continue
709
- }
710
- const value = descriptor.value
711
- const type = typeof value
794
+ if (runtimeAvailable) {
795
+ for (const key of finalList) {
796
+ const descriptor = moduleDescriptors[key]
797
+ if (!descriptor) continue
798
+ if (descriptor.get || descriptor.set) {
799
+ categorized.objects.push(key)
800
+ continue
801
+ }
802
+ const value = descriptor.value
803
+ const type = typeof value
712
804
 
713
- if (type === "function") {
714
- // Distinguir class vs function
715
- if (isProbablyClass(value)) {
716
- categorized.classes.push(key)
805
+ if (type === "function") {
806
+ // Distinguir class vs function
807
+ if (isProbablyClass(value)) {
808
+ categorized.classes.push(key)
809
+ } else {
810
+ categorized.functions.push(key)
811
+ }
812
+ } else if (type === "object" && value !== null) {
813
+ categorized.objects.push(key)
814
+ } else if (type === "string" || type === "number" || type === "boolean") {
815
+ categorized.constants.push(key)
717
816
  } else {
718
- categorized.functions.push(key)
817
+ categorized.primitives.push(key)
719
818
  }
720
- } else if (type === "object" && value !== null) {
721
- categorized.objects.push(key)
722
- } else if (type === "string" || type === "number" || type === "boolean") {
723
- categorized.constants.push(key)
724
- } else {
725
- categorized.primitives.push(key)
726
819
  }
727
- }
728
-
729
- // Prefer class from type info when available
730
- if (typeInfoRaw && Object.keys(typeInfoRaw.classes).length > 0) {
731
- const classNames = new Set(Object.keys(typeInfoRaw.classes))
732
- categorized.functions = categorized.functions.filter((name) => {
733
- if (classNames.has(name)) {
734
- categorized.classes.push(name)
735
- return false
736
- }
737
- return true
738
- })
739
- }
740
820
 
741
- // Apply kind filter if specified
742
- if (kindFilter && kindFilter.length > 0) {
743
- const kindMap = {
744
- function: "functions",
745
- class: "classes",
746
- object: "objects",
747
- constant: "constants",
821
+ // Prefer class from type info when available
822
+ if (typeInfoRaw && Object.keys(typeInfoRaw.classes).length > 0) {
823
+ const classNames = new Set(Object.keys(typeInfoRaw.classes))
824
+ categorized.functions = categorized.functions.filter((name) => {
825
+ if (classNames.has(name)) {
826
+ categorized.classes.push(name)
827
+ return false
828
+ }
829
+ return true
830
+ })
748
831
  }
749
832
 
750
- // Keep only the requested kinds
751
- for (const [key, value] of Object.entries(categorized)) {
752
- const shouldKeep = Object.entries(kindMap).some(([kind, catKey]) => kindFilter.includes(kind) && catKey === key)
753
- if (!shouldKeep) {
754
- categorized[key] = []
833
+ // Apply kind filter if specified
834
+ if (kindFilter && kindFilter.length > 0) {
835
+ const kindMap = {
836
+ function: "functions",
837
+ class: "classes",
838
+ object: "objects",
839
+ constant: "constants",
840
+ }
841
+
842
+ // Keep only the requested kinds
843
+ for (const [key, value] of Object.entries(categorized)) {
844
+ const shouldKeep = Object.entries(kindMap).some(
845
+ ([kind, catKey]) => kindFilter.includes(kind) && catKey === key,
846
+ )
847
+ if (!shouldKeep) {
848
+ categorized[key] = []
849
+ }
755
850
  }
756
- }
757
851
 
758
- // Update finalList to only include filtered kinds
759
- finalList = [...categorized.functions, ...categorized.classes, ...categorized.objects, ...categorized.constants]
852
+ // Update finalList to only include filtered kinds
853
+ finalList = [...categorized.functions, ...categorized.classes, ...categorized.objects, ...categorized.constants]
854
+ }
760
855
  }
761
856
 
762
857
  // Mostrar exports categorizados
763
858
  if (jsdocOutput !== "only") {
859
+ if (!runtimeAvailable) {
860
+ log(`\nℹ️ Runtime exports unavailable. Use --types to inspect type exports.`)
861
+ }
764
862
  log(`\n🔑 Exports Encontrados (${finalList.length} total):`)
765
863
 
766
864
  if (categorized.functions.length > 0) {
@@ -808,7 +906,9 @@ export async function runInspect(options) {
808
906
 
809
907
  // === MELHORIA 5: Mostrar assinaturas de funções ===
810
908
  if (jsdocOutput !== "only") {
811
- if (!showTypes && categorized.functions.length > 0 && categorized.functions.length <= 15) {
909
+ if (!runtimeAvailable) {
910
+ // Skip runtime-only signature/default export hints when entrypoint is missing
911
+ } else if (!showTypes && categorized.functions.length > 0 && categorized.functions.length <= 15) {
812
912
  log(`\n✍️ Function Signatures:`)
813
913
  for (const fname of categorized.functions) {
814
914
  const descriptor = moduleDescriptors[fname]
@@ -822,13 +922,15 @@ export async function runInspect(options) {
822
922
  }
823
923
 
824
924
  // Default export handling
825
- const defaultDescriptor = moduleDescriptors.default
826
- if (defaultDescriptor && (!filter || "default".includes(filter))) {
827
- const defaultValue = defaultDescriptor.get || defaultDescriptor.set ? undefined : defaultDescriptor.value
828
- const defaultType = typeof defaultValue
829
- log(`\n📦 Default Export: ${defaultType}`)
830
- if (defaultType === "function" && defaultValue && defaultValue.length !== undefined) {
831
- log(` Parameters: ${defaultValue.length}`)
925
+ if (runtimeAvailable) {
926
+ const defaultDescriptor = moduleDescriptors.default
927
+ if (defaultDescriptor && (!filter || "default".includes(filter))) {
928
+ const defaultValue = defaultDescriptor.get || defaultDescriptor.set ? undefined : defaultDescriptor.value
929
+ const defaultType = typeof defaultValue
930
+ log(`\n📦 Default Export: ${defaultType}`)
931
+ if (defaultType === "function" && defaultValue && defaultValue.length !== undefined) {
932
+ log(` Parameters: ${defaultValue.length}`)
933
+ }
832
934
  }
833
935
  }
834
936
  }
@@ -995,17 +1097,19 @@ export async function runInspect(options) {
995
1097
  ...Object.keys(typeInfo.enums),
996
1098
  ...Object.keys(typeInfo.namespaces),
997
1099
  ])
998
- const runtimeNames = new Set(allExports)
999
- const runtimeOnly = [...runtimeNames].filter((name) => !typeExportNames.has(name))
1000
- const typesOnly = [...typeExportNames].filter((name) => !runtimeNames.has(name))
1001
-
1002
- if (runtimeOnly.length > 0 || typesOnly.length > 0) {
1003
- log(`\n ⚖️ Runtime/Types Mismatch:`)
1004
- if (runtimeOnly.length > 0) {
1005
- log(` Runtime only: ${runtimeOnly.slice(0, 10).join(", ")}`)
1006
- }
1007
- if (typesOnly.length > 0) {
1008
- log(` Types only: ${typesOnly.slice(0, 10).join(", ")}`)
1100
+ if (runtimeAvailable) {
1101
+ const runtimeNames = new Set(allExports)
1102
+ const runtimeOnly = [...runtimeNames].filter((name) => !typeExportNames.has(name))
1103
+ const typesOnly = [...typeExportNames].filter((name) => !runtimeNames.has(name))
1104
+
1105
+ if (runtimeOnly.length > 0 || typesOnly.length > 0) {
1106
+ log(`\n ⚖️ Runtime/Types Mismatch:`)
1107
+ if (runtimeOnly.length > 0) {
1108
+ log(` Runtime only: ${runtimeOnly.slice(0, 10).join(", ")}`)
1109
+ }
1110
+ if (typesOnly.length > 0) {
1111
+ log(` Types only: ${typesOnly.slice(0, 10).join(", ")}`)
1112
+ }
1009
1113
  }
1010
1114
  }
1011
1115