@lpm-registry/mcp-server 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,8 +4,8 @@ import { errorResponse, parseName, textResponse } from "../format.js"
4
4
 
5
5
  /**
6
6
  * Get the correct CLI install command for an LPM package.
7
- * Returns `lpm add` for source packages (components, blocks, MCP servers, Swift)
8
- * or `lpm install` for dependency packages (installed to node_modules).
7
+ * Returns `lpm add` for source packages (components, blocks, MCP servers)
8
+ * or `lpm install` for managed dependencies (JS to node_modules, Swift to Package.swift).
9
9
  *
10
10
  * @param {{ name: string, version?: string }} params
11
11
  * @param {{ cache: import('../cache.js').MemoryCache, getToken: () => Promise<string|null>, getBaseUrl: () => string }} ctx
@@ -33,11 +33,11 @@ export async function getInstallCommand({ name, version }, ctx) {
33
33
  const result = await registryGet(`/${fullName}`, token, baseUrl)
34
34
 
35
35
  if (!result.ok) {
36
- // Fallback: return `lpm add` as the default command when metadata is unavailable
36
+ // Fallback: return `lpm install` as the default command when metadata is unavailable
37
37
  if (result.status === 404) {
38
38
  return errorResponse(`Package ${fullName} not found.`)
39
39
  }
40
- const response = textResponse(`lpm add ${fullName}${versionSuffix}`)
40
+ const response = textResponse(`lpm install ${fullName}${versionSuffix}`)
41
41
  return response
42
42
  }
43
43
 
@@ -45,13 +45,11 @@ export async function getInstallCommand({ name, version }, ctx) {
45
45
  const ecosystem = data.ecosystem || "js"
46
46
  const packageType = data.packageType || "package"
47
47
 
48
- // Source-based packages use `lpm add` (extracts files into the project)
49
- // Only plain JS packages without a special type use `lpm install` (node_modules)
48
+ // Source packages (components, blocks, templates) use `lpm add`
49
+ // Everything else uses `lpm install` (JS → node_modules, Swift → Package.swift)
50
+ const sourceTypes = new Set(["component", "block", "template", "mcp-server"])
50
51
  const useAdd =
51
- ecosystem !== "js" ||
52
- packageType !== "package" ||
53
- data.lpmSource ||
54
- data.hasLpmConfig
52
+ sourceTypes.has(packageType) || data.lpmSource || data.hasLpmConfig
55
53
 
56
54
  const command = useAdd
57
55
  ? `lpm add ${fullName}${versionSuffix}`
@@ -59,7 +57,9 @@ export async function getInstallCommand({ name, version }, ctx) {
59
57
 
60
58
  const explanation = useAdd
61
59
  ? "Extracts source files into your project for customization."
62
- : "Installs as a dependency in node_modules (like npm install)."
60
+ : ecosystem === "swift"
61
+ ? "Installs as a managed dependency via SE-0292 (edits Package.swift)."
62
+ : "Installs as a dependency in node_modules (like npm install)."
63
63
 
64
64
  const response = textResponse(
65
65
  JSON.stringify(
@@ -1,14 +1,13 @@
1
1
  import { runCli } from "../cli.js"
2
- import { errorResponse, parseName, textResponse } from "../format.js"
2
+ import { cliResponse, errorResponse, parseName } from "../format.js"
3
3
 
4
4
  /**
5
5
  * Install an LPM package as a dependency via the CLI.
6
6
  * Installs to node_modules like npm install.
7
7
  *
8
8
  * @param {{ name: string, version?: string }} params
9
- * @param {{ getToken: () => Promise<string|null> }} ctx
10
9
  */
11
- export async function install({ name, version }, ctx) {
10
+ export async function install({ name, version }) {
12
11
  let owner, pkgName
13
12
  try {
14
13
  const parsed = parseName(name)
@@ -18,13 +17,6 @@ export async function install({ name, version }, ctx) {
18
17
  return errorResponse(err.message)
19
18
  }
20
19
 
21
- const token = await ctx.getToken()
22
- if (!token) {
23
- return errorResponse(
24
- "Authentication required. Set LPM_TOKEN environment variable or run `lpm login`.",
25
- )
26
- }
27
-
28
20
  let pkgRef = `@lpm.dev/${owner}.${pkgName}`
29
21
  if (version) pkgRef += `@${version}`
30
22
 
@@ -32,20 +24,5 @@ export async function install({ name, version }, ctx) {
32
24
 
33
25
  const result = await runCli(args)
34
26
 
35
- if (!result.success) {
36
- return errorResponse(result.error || "Failed to install package.")
37
- }
38
-
39
- const data = result.data
40
- const summary = {
41
- success: true,
42
- packages: data.packages || [{ name: pkgRef }],
43
- npmOutput: data.npmOutput || "",
44
- }
45
-
46
- if (data.warnings?.length > 0) {
47
- summary.warnings = data.warnings
48
- }
49
-
50
- return textResponse(JSON.stringify(summary, null, 2))
27
+ return cliResponse(result)
51
28
  }
@@ -0,0 +1,80 @@
1
+ import { registryGet } from "../api.js"
2
+ import { CACHE_TTL } from "../constants.js"
3
+ import { errorResponse, parseName, textResponse } from "../format.js"
4
+
5
+ /**
6
+ * Get marketplace information for an LPM package.
7
+ * Returns pricing, licensing model, seat management details, and purchase status.
8
+ *
9
+ * @param {{ name: string }} params
10
+ * @param {{ cache: import('../cache.js').MemoryCache, getToken: () => Promise<string|null>, getBaseUrl: () => string }} ctx
11
+ */
12
+ export async function marketplaceInfo({ name }, ctx) {
13
+ let owner, pkgName
14
+ try {
15
+ const parsed = parseName(name)
16
+ owner = parsed.owner
17
+ pkgName = parsed.name
18
+ } catch (err) {
19
+ return errorResponse(err.message)
20
+ }
21
+
22
+ const fullName = `@lpm.dev/${owner}.${pkgName}`
23
+ const token = await ctx.getToken()
24
+ const cacheKey = `marketplace-info:${owner}.${pkgName}:auth=${!!token}`
25
+
26
+ const cached = ctx.cache.get(cacheKey)
27
+ if (cached) return cached
28
+
29
+ const baseUrl = ctx.getBaseUrl()
30
+ const result = await registryGet(`/${fullName}`, token, baseUrl)
31
+
32
+ if (!result.ok) {
33
+ if (result.status === 404) {
34
+ return errorResponse(`Package ${fullName} not found.`)
35
+ }
36
+ return errorResponse(
37
+ result.data?.error || `Request failed (${result.status})`,
38
+ )
39
+ }
40
+
41
+ const data = result.data
42
+ const distributionMode = data.distributionMode || "private"
43
+
44
+ if (distributionMode !== "marketplace") {
45
+ const response = textResponse(
46
+ JSON.stringify(
47
+ {
48
+ name: fullName,
49
+ distributionMode,
50
+ isMarketplace: false,
51
+ message: `This package uses ${distributionMode} distribution, not marketplace.`,
52
+ },
53
+ null,
54
+ 2,
55
+ ),
56
+ )
57
+ ctx.cache.set(cacheKey, response, CACHE_TTL.SHORT)
58
+ return response
59
+ }
60
+
61
+ const summary = {
62
+ name: fullName,
63
+ isMarketplace: true,
64
+ distributionMode: "marketplace",
65
+ description: data.description || "",
66
+ // Pricing
67
+ pricing: data.marketplace?.pricing || null,
68
+ licenseType: data.marketplace?.licenseType || null,
69
+ // Seat management
70
+ seats: data.marketplace?.seats || null,
71
+ // Access info for the current user
72
+ accessInfo: data.accessInfo || null,
73
+ // Latest version
74
+ latestVersion: data["dist-tags"]?.latest || null,
75
+ }
76
+
77
+ const response = textResponse(JSON.stringify(summary, null, 2))
78
+ ctx.cache.set(cacheKey, response, CACHE_TTL.SHORT)
79
+ return response
80
+ }
@@ -12,20 +12,24 @@ const ADD_TYPES = new Set(["component", "block", "template", "mcp-server"])
12
12
  * @returns {{ command: string, description: string }}
13
13
  */
14
14
  export function getInstallMethod(packageType, ecosystem) {
15
- if (ecosystem === "swift" || ecosystem === "xcframework") {
15
+ // Source-extract types always use `lpm add`
16
+ if (ADD_TYPES.has(packageType)) {
16
17
  return {
17
18
  command: "lpm add",
18
- description: "Extracts Swift source files into your project",
19
+ description: "Extracts source files into your project for customization",
19
20
  }
20
21
  }
21
22
 
22
- if (ADD_TYPES.has(packageType)) {
23
+ // Swift/XCFramework use `lpm install` (SE-0292 registry mode)
24
+ if (ecosystem === "swift" || ecosystem === "xcframework") {
23
25
  return {
24
- command: "lpm add",
25
- description: "Extracts source files into your project for customization",
26
+ command: "lpm install",
27
+ description:
28
+ "Installs as a managed dependency via SE-0292 (edits Package.swift)",
26
29
  }
27
30
  }
28
31
 
32
+ // JS dependencies/libraries use `lpm install`
29
33
  if (packageType === "dependency" || packageType === "library") {
30
34
  return {
31
35
  command: "lpm install",
@@ -34,10 +38,10 @@ export function getInstallMethod(packageType, ecosystem) {
34
38
  }
35
39
  }
36
40
 
37
- // Default: lpm add for source packages, lpm install for generic packages
41
+ // Default: lpm install for managed dependencies
38
42
  return {
39
- command: "lpm add",
40
- description: "Extracts source files into your project for customization",
43
+ command: "lpm install",
44
+ description: "Installs as a managed dependency",
41
45
  }
42
46
  }
43
47
 
@@ -10,10 +10,10 @@ import { errorResponse, textResponse } from "../format.js"
10
10
  * - Explore path (/packages/explore): when any structured filter is active
11
11
  *
12
12
  * @param {object} params
13
- * @param {string} [params.query] - Search text (required unless category provided)
13
+ * @param {string} [params.query] - Search text (optional with a structured filter)
14
14
  * @param {string} [params.category] - Category slug filter
15
15
  * @param {string} [params.ecosystem] - Ecosystem filter (js, swift, xcframework)
16
- * @param {string} [params.distribution] - Distribution mode filter (marketplace, pool, private)
16
+ * @param {string} [params.distribution] - Distribution mode filter (free, marketplace, pool, private)
17
17
  * @param {string} [params.packageType] - Package type filter
18
18
  * @param {string} [params.sort] - Sort order (newest, popular, name)
19
19
  * @param {boolean} [params.hasTypes] - TypeScript types filter
@@ -36,18 +36,6 @@ export async function search(params, ctx) {
36
36
  minNodeVersion,
37
37
  } = params
38
38
 
39
- // Validation: at least query or category required
40
- if (!query?.trim() && !category) {
41
- return errorResponse(ERROR_MESSAGES.searchNoParams)
42
- }
43
- if (query && query.trim().length < 2) {
44
- return errorResponse("Search query must be at least 2 characters.")
45
- }
46
-
47
- const token = await ctx.getToken()
48
- const baseUrl = ctx.getBaseUrl()
49
-
50
- // Determine which path to use
51
39
  const hasStructuredFilters =
52
40
  category ||
53
41
  distribution ||
@@ -56,7 +44,18 @@ export async function search(params, ctx) {
56
44
  hasTypes ||
57
45
  moduleType ||
58
46
  license ||
59
- minNodeVersion
47
+ minNodeVersion ||
48
+ (!query?.trim() && params.ecosystem)
49
+
50
+ if (!query?.trim() && !hasStructuredFilters) {
51
+ return errorResponse(ERROR_MESSAGES.searchNoParams)
52
+ }
53
+ if (query && query.trim().length < 2) {
54
+ return errorResponse("Search query must be at least 2 characters.")
55
+ }
56
+
57
+ const token = await ctx.getToken()
58
+ const baseUrl = ctx.getBaseUrl()
60
59
 
61
60
  // Build deterministic cache key from all params
62
61
  const cacheKey = buildCacheKey(params)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lpm-registry/mcp-server",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server for the LPM package registry — exposes package info, quality reports, name checks, pool stats, and marketplace search as AI tools",
5
5
  "author": "LPM <hello@lpm.dev> (https://lpm.dev)",
6
6
  "repository": {