@universal-mcp-toolkit/server-npm-registry 0.1.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.
@@ -0,0 +1,33 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/json",
3
+ "name": "npm-registry",
4
+ "title": "NPM Registry MCP Server",
5
+ "description": "Search and inspect npm registry packages, versions, and dist-tags.",
6
+ "version": "0.1.0",
7
+ "packageName": "@universal-mcp-toolkit/server-npm-registry",
8
+ "homepage": "https://github.com/universal-mcp-toolkit/universal-mcp-toolkit",
9
+ "transports": [
10
+ "stdio",
11
+ "sse"
12
+ ],
13
+ "authentication": {
14
+ "mode": "environment-variables",
15
+ "required": []
16
+ },
17
+ "capabilities": {
18
+ "tools": true,
19
+ "resources": true,
20
+ "prompts": true
21
+ },
22
+ "tools": [
23
+ "search_packages",
24
+ "get_package_metadata",
25
+ "list_package_versions"
26
+ ],
27
+ "resources": [
28
+ "package"
29
+ ],
30
+ "prompts": [
31
+ "release-plan"
32
+ ]
33
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 universal-mcp-toolkit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,70 @@
1
+ import * as _universal_mcp_toolkit_core from '@universal-mcp-toolkit/core';
2
+ import { ToolkitServer, ToolkitServerMetadata } from '@universal-mcp-toolkit/core';
3
+ import { z } from 'zod';
4
+
5
+ declare const metadata: ToolkitServerMetadata;
6
+ declare const serverCard: _universal_mcp_toolkit_core.ToolkitServerCard;
7
+ declare const searchResultSchema: z.ZodObject<{
8
+ name: z.ZodString;
9
+ version: z.ZodString;
10
+ description: z.ZodNullable<z.ZodString>;
11
+ keywords: z.ZodArray<z.ZodString>;
12
+ score: z.ZodNumber;
13
+ homepage: z.ZodNullable<z.ZodString>;
14
+ npmUrl: z.ZodNullable<z.ZodString>;
15
+ repositoryUrl: z.ZodNullable<z.ZodString>;
16
+ }, z.core.$strip>;
17
+ declare const maintainerSchema: z.ZodObject<{
18
+ name: z.ZodString;
19
+ email: z.ZodNullable<z.ZodString>;
20
+ }, z.core.$strip>;
21
+ declare const versionInfoSchema: z.ZodObject<{
22
+ version: z.ZodString;
23
+ publishedAt: z.ZodNullable<z.ZodString>;
24
+ deprecated: z.ZodBoolean;
25
+ unpackedSize: z.ZodNullable<z.ZodNumber>;
26
+ }, z.core.$strip>;
27
+ declare const packageMetadataSchema: z.ZodObject<{
28
+ name: z.ZodString;
29
+ description: z.ZodNullable<z.ZodString>;
30
+ latestVersion: z.ZodNullable<z.ZodString>;
31
+ homepage: z.ZodNullable<z.ZodString>;
32
+ repositoryUrl: z.ZodNullable<z.ZodString>;
33
+ license: z.ZodNullable<z.ZodString>;
34
+ distTags: z.ZodRecord<z.ZodString, z.ZodString>;
35
+ maintainers: z.ZodArray<z.ZodObject<{
36
+ name: z.ZodString;
37
+ email: z.ZodNullable<z.ZodString>;
38
+ }, z.core.$strip>>;
39
+ versions: z.ZodArray<z.ZodObject<{
40
+ version: z.ZodString;
41
+ publishedAt: z.ZodNullable<z.ZodString>;
42
+ deprecated: z.ZodBoolean;
43
+ unpackedSize: z.ZodNullable<z.ZodNumber>;
44
+ }, z.core.$strip>>;
45
+ }, z.core.$strip>;
46
+ type NpmSearchResult = z.infer<typeof searchResultSchema>;
47
+ type NpmMaintainer = z.infer<typeof maintainerSchema>;
48
+ type NpmVersionInfo = z.infer<typeof versionInfoSchema>;
49
+ type NpmPackageMetadata = z.infer<typeof packageMetadataSchema>;
50
+ interface NpmRegistryClient {
51
+ searchPackages(input: {
52
+ query: string;
53
+ limit: number;
54
+ from: number;
55
+ }): Promise<ReadonlyArray<NpmSearchResult>>;
56
+ getPackageMetadata(packageName: string): Promise<NpmPackageMetadata>;
57
+ }
58
+ interface CreateNpmRegistryServerOptions {
59
+ client?: NpmRegistryClient;
60
+ env?: NodeJS.ProcessEnv;
61
+ fetch?: typeof fetch;
62
+ }
63
+ declare class NpmRegistryServer extends ToolkitServer {
64
+ private readonly client;
65
+ constructor(client: NpmRegistryClient);
66
+ }
67
+ declare function createServer(options?: CreateNpmRegistryServerOptions): Promise<NpmRegistryServer>;
68
+ declare function main(argv?: readonly string[]): Promise<void>;
69
+
70
+ export { type CreateNpmRegistryServerOptions, type NpmMaintainer, type NpmPackageMetadata, type NpmRegistryClient, NpmRegistryServer, type NpmSearchResult, type NpmVersionInfo, createServer, main, metadata, serverCard };
package/dist/index.js ADDED
@@ -0,0 +1,359 @@
1
+ // src/index.ts
2
+ import {
3
+ createServerCard,
4
+ defineTool,
5
+ ExternalServiceError,
6
+ loadEnv,
7
+ parseRuntimeOptions,
8
+ runToolkitServer,
9
+ ToolkitServer
10
+ } from "@universal-mcp-toolkit/core";
11
+ import { resolve } from "path";
12
+ import { fileURLToPath } from "url";
13
+ import { z } from "zod";
14
+ var toolNames = ["search_packages", "get_package_metadata", "list_package_versions"];
15
+ var resourceNames = ["package"];
16
+ var promptNames = ["release-plan"];
17
+ var metadata = {
18
+ id: "npm-registry",
19
+ title: "NPM Registry MCP Server",
20
+ description: "Search and inspect npm registry packages, versions, and dist-tags.",
21
+ version: "0.1.0",
22
+ packageName: "@universal-mcp-toolkit/server-npm-registry",
23
+ homepage: "https://github.com/universal-mcp-toolkit/universal-mcp-toolkit",
24
+ repositoryUrl: "https://github.com/universal-mcp-toolkit/universal-mcp-toolkit",
25
+ documentationUrl: "https://github.com/universal-mcp-toolkit/universal-mcp-toolkit/tree/main/servers/npm-registry",
26
+ envVarNames: [],
27
+ transports: ["stdio", "sse"],
28
+ toolNames,
29
+ resourceNames,
30
+ promptNames
31
+ };
32
+ var serverCard = createServerCard(metadata);
33
+ var envShape = {
34
+ NPM_REGISTRY_BASE_URL: z.string().url().optional(),
35
+ NPM_SEARCH_BASE_URL: z.string().url().optional()
36
+ };
37
+ var searchResultSchema = z.object({
38
+ name: z.string(),
39
+ version: z.string(),
40
+ description: z.string().nullable(),
41
+ keywords: z.array(z.string()),
42
+ score: z.number(),
43
+ homepage: z.string().nullable(),
44
+ npmUrl: z.string().nullable(),
45
+ repositoryUrl: z.string().nullable()
46
+ });
47
+ var maintainerSchema = z.object({
48
+ name: z.string(),
49
+ email: z.string().nullable()
50
+ });
51
+ var versionInfoSchema = z.object({
52
+ version: z.string(),
53
+ publishedAt: z.string().nullable(),
54
+ deprecated: z.boolean(),
55
+ unpackedSize: z.number().nonnegative().nullable()
56
+ });
57
+ var packageMetadataSchema = z.object({
58
+ name: z.string(),
59
+ description: z.string().nullable(),
60
+ latestVersion: z.string().nullable(),
61
+ homepage: z.string().nullable(),
62
+ repositoryUrl: z.string().nullable(),
63
+ license: z.string().nullable(),
64
+ distTags: z.record(z.string(), z.string()),
65
+ maintainers: z.array(maintainerSchema),
66
+ versions: z.array(versionInfoSchema)
67
+ });
68
+ var searchResponseSchema = z.object({
69
+ objects: z.array(
70
+ z.object({
71
+ package: z.object({
72
+ name: z.string(),
73
+ version: z.string(),
74
+ description: z.string().nullable().optional(),
75
+ keywords: z.array(z.string()).optional(),
76
+ links: z.object({
77
+ homepage: z.string().nullable().optional(),
78
+ npm: z.string().nullable().optional(),
79
+ repository: z.string().nullable().optional()
80
+ }).optional()
81
+ }),
82
+ score: z.object({
83
+ final: z.number()
84
+ }).optional()
85
+ })
86
+ )
87
+ });
88
+ var rawVersionSchema = z.object({
89
+ dist: z.object({
90
+ unpackedSize: z.number().nonnegative().optional()
91
+ }).optional(),
92
+ deprecated: z.string().optional()
93
+ });
94
+ var packageResponseSchema = z.object({
95
+ name: z.string(),
96
+ description: z.string().nullable().optional(),
97
+ homepage: z.string().nullable().optional(),
98
+ license: z.string().nullable().optional(),
99
+ repository: z.object({
100
+ url: z.string().nullable().optional()
101
+ }).optional(),
102
+ "dist-tags": z.record(z.string(), z.string()).optional(),
103
+ maintainers: z.array(
104
+ z.object({
105
+ name: z.string(),
106
+ email: z.string().nullable().optional()
107
+ })
108
+ ).optional(),
109
+ versions: z.record(z.string(), rawVersionSchema).optional(),
110
+ time: z.record(z.string(), z.string()).optional()
111
+ });
112
+ function resolveEnv(source = process.env) {
113
+ return loadEnv(envShape, source);
114
+ }
115
+ function toNullableString(value) {
116
+ return typeof value === "string" && value.length > 0 ? value : null;
117
+ }
118
+ function encodePackageName(packageName) {
119
+ return encodeURIComponent(packageName);
120
+ }
121
+ function getTemplateVariable(variables, name) {
122
+ const value = variables[name];
123
+ return Array.isArray(value) ? value[0] ?? "" : value ?? "";
124
+ }
125
+ var FetchNpmRegistryClient = class {
126
+ registryBaseUrl;
127
+ searchBaseUrl;
128
+ fetchImpl;
129
+ constructor(options) {
130
+ this.registryBaseUrl = (options.registryBaseUrl ?? "https://registry.npmjs.org").replace(/\/+$/, "");
131
+ this.searchBaseUrl = (options.searchBaseUrl ?? "https://registry.npmjs.org/-/v1").replace(/\/+$/, "");
132
+ this.fetchImpl = options.fetch;
133
+ }
134
+ async searchPackages(input) {
135
+ const url = new URL(`${this.searchBaseUrl}/search`);
136
+ url.searchParams.set("text", input.query);
137
+ url.searchParams.set("size", String(input.limit));
138
+ url.searchParams.set("from", String(input.from));
139
+ const payload = await this.requestJson(url, searchResponseSchema);
140
+ return payload.objects.map((entry) => ({
141
+ name: entry.package.name,
142
+ version: entry.package.version,
143
+ description: toNullableString(entry.package.description),
144
+ keywords: entry.package.keywords ?? [],
145
+ score: entry.score?.final ?? 0,
146
+ homepage: toNullableString(entry.package.links?.homepage),
147
+ npmUrl: toNullableString(entry.package.links?.npm),
148
+ repositoryUrl: toNullableString(entry.package.links?.repository)
149
+ }));
150
+ }
151
+ async getPackageMetadata(packageName) {
152
+ const payload = await this.requestJson(
153
+ new URL(`${this.registryBaseUrl}/${encodePackageName(packageName)}`),
154
+ packageResponseSchema
155
+ );
156
+ const times = payload.time ?? {};
157
+ const versions = Object.entries(payload.versions ?? {}).map(([version, detail]) => ({
158
+ version,
159
+ publishedAt: toNullableString(times[version]),
160
+ deprecated: typeof detail.deprecated === "string" && detail.deprecated.length > 0,
161
+ unpackedSize: detail.dist?.unpackedSize ?? null
162
+ })).sort((left, right) => {
163
+ const leftTime = left.publishedAt === null ? 0 : Date.parse(left.publishedAt);
164
+ const rightTime = right.publishedAt === null ? 0 : Date.parse(right.publishedAt);
165
+ return rightTime - leftTime;
166
+ });
167
+ return {
168
+ name: payload.name,
169
+ description: toNullableString(payload.description),
170
+ latestVersion: toNullableString(payload["dist-tags"]?.latest),
171
+ homepage: toNullableString(payload.homepage),
172
+ repositoryUrl: toNullableString(payload.repository?.url),
173
+ license: toNullableString(payload.license),
174
+ distTags: payload["dist-tags"] ?? {},
175
+ maintainers: (payload.maintainers ?? []).map((maintainer) => ({
176
+ name: maintainer.name,
177
+ email: toNullableString(maintainer.email)
178
+ })),
179
+ versions
180
+ };
181
+ }
182
+ async requestJson(url, schema) {
183
+ const response = await this.fetchImpl(url, {
184
+ method: "GET",
185
+ headers: {
186
+ accept: "application/json"
187
+ }
188
+ });
189
+ if (!response.ok) {
190
+ const body = await response.text();
191
+ throw new ExternalServiceError(`npm registry request failed with status ${response.status}.`, {
192
+ statusCode: response.status,
193
+ details: body
194
+ });
195
+ }
196
+ const payload = await response.json();
197
+ const parsed = schema.safeParse(payload);
198
+ if (!parsed.success) {
199
+ throw new ExternalServiceError("npm registry returned an unexpected response shape.", {
200
+ details: parsed.error.flatten()
201
+ });
202
+ }
203
+ return parsed.data;
204
+ }
205
+ };
206
+ var NpmRegistryServer = class extends ToolkitServer {
207
+ client;
208
+ constructor(client) {
209
+ super(metadata);
210
+ this.client = client;
211
+ this.registerTool(
212
+ defineTool({
213
+ name: "search_packages",
214
+ title: "Search npm packages",
215
+ description: "Search the npm registry for packages matching a text query.",
216
+ inputSchema: {
217
+ query: z.string().trim().min(1),
218
+ limit: z.number().int().min(1).max(50).default(10),
219
+ from: z.number().int().min(0).max(500).default(0)
220
+ },
221
+ outputSchema: {
222
+ results: z.array(searchResultSchema),
223
+ returned: z.number().int()
224
+ },
225
+ handler: async ({ query, limit, from }, context) => {
226
+ await context.log("info", `Searching npm registry for ${query}`);
227
+ const results = await this.client.searchPackages({ query, limit, from });
228
+ return {
229
+ results: [...results],
230
+ returned: results.length
231
+ };
232
+ },
233
+ renderText: ({ results, returned }) => {
234
+ if (returned === 0) {
235
+ return "No npm packages found.";
236
+ }
237
+ return results.map((result) => `${result.name}@${result.version}`).join("\n");
238
+ }
239
+ })
240
+ );
241
+ this.registerTool(
242
+ defineTool({
243
+ name: "get_package_metadata",
244
+ title: "Get package metadata",
245
+ description: "Fetch npm registry metadata for a specific package.",
246
+ inputSchema: {
247
+ packageName: z.string().trim().min(1)
248
+ },
249
+ outputSchema: {
250
+ package: packageMetadataSchema
251
+ },
252
+ handler: async ({ packageName }, context) => {
253
+ await context.log("info", `Fetching npm metadata for ${packageName}`);
254
+ return {
255
+ package: await this.client.getPackageMetadata(packageName)
256
+ };
257
+ },
258
+ renderText: ({ package: pkg }) => `${pkg.name} latest=${pkg.latestVersion ?? "unknown"}`
259
+ })
260
+ );
261
+ this.registerTool(
262
+ defineTool({
263
+ name: "list_package_versions",
264
+ title: "List package versions",
265
+ description: "List recent versions and dist-tags for a package in the npm registry.",
266
+ inputSchema: {
267
+ packageName: z.string().trim().min(1),
268
+ limit: z.number().int().min(1).max(100).default(20)
269
+ },
270
+ outputSchema: {
271
+ packageName: z.string(),
272
+ distTags: z.record(z.string(), z.string()),
273
+ versions: z.array(versionInfoSchema)
274
+ },
275
+ handler: async ({ packageName, limit }, context) => {
276
+ await context.log("info", `Listing npm versions for ${packageName}`);
277
+ const pkg = await this.client.getPackageMetadata(packageName);
278
+ return {
279
+ packageName: pkg.name,
280
+ distTags: pkg.distTags,
281
+ versions: pkg.versions.slice(0, limit)
282
+ };
283
+ },
284
+ renderText: ({ packageName, versions }) => `${packageName}: ${versions.map((version) => version.version).join(", ")}`
285
+ })
286
+ );
287
+ this.registerTemplateResource(
288
+ "package",
289
+ "npm://package/{packageName}",
290
+ {
291
+ title: "Package metadata",
292
+ description: "Metadata resource for an npm package.",
293
+ mimeType: "application/json"
294
+ },
295
+ async (uri, variables) => {
296
+ const packageName = getTemplateVariable(variables, "packageName");
297
+ const pkg = await this.client.getPackageMetadata(packageName);
298
+ return this.createJsonResource(uri.toString(), pkg);
299
+ }
300
+ );
301
+ this.registerPrompt(
302
+ "release-plan",
303
+ {
304
+ title: "Release plan prompt",
305
+ description: "Draft a release plan for publishing or promoting an npm package.",
306
+ argsSchema: {
307
+ packageName: z.string().trim().min(1),
308
+ targetVersion: z.string().trim().min(1),
309
+ currentVersion: z.string().trim().min(1).optional(),
310
+ goal: z.string().trim().min(1)
311
+ }
312
+ },
313
+ async ({ packageName, targetVersion, currentVersion, goal }) => this.createTextPrompt(
314
+ [
315
+ `Create a release plan for ${packageName} targeting version ${targetVersion}.`,
316
+ currentVersion ? `Current published version: ${currentVersion}.` : "Assume the current version needs verification.",
317
+ `Primary goal: ${goal}.`,
318
+ "Cover changelog scope, semver impact, dist-tags, validation, rollback, and communication steps."
319
+ ].join(" ")
320
+ )
321
+ );
322
+ }
323
+ };
324
+ async function createServer(options = {}) {
325
+ if (options.client) {
326
+ return new NpmRegistryServer(options.client);
327
+ }
328
+ const env = resolveEnv(options.env);
329
+ const client = new FetchNpmRegistryClient({
330
+ fetch: options.fetch ?? globalThis.fetch,
331
+ ...env.NPM_REGISTRY_BASE_URL ? { registryBaseUrl: env.NPM_REGISTRY_BASE_URL } : {},
332
+ ...env.NPM_SEARCH_BASE_URL ? { searchBaseUrl: env.NPM_SEARCH_BASE_URL } : {}
333
+ });
334
+ return new NpmRegistryServer(client);
335
+ }
336
+ function isMainModule(metaUrl) {
337
+ const entry = process.argv[1];
338
+ return typeof entry === "string" && fileURLToPath(metaUrl) === resolve(entry);
339
+ }
340
+ async function main(argv = process.argv.slice(2)) {
341
+ const runtimeOptions = parseRuntimeOptions(argv);
342
+ await runToolkitServer(
343
+ {
344
+ createServer: () => createServer(),
345
+ serverCard
346
+ },
347
+ runtimeOptions
348
+ );
349
+ }
350
+ if (isMainModule(import.meta.url)) {
351
+ await main();
352
+ }
353
+ export {
354
+ NpmRegistryServer,
355
+ createServer,
356
+ main,
357
+ metadata,
358
+ serverCard
359
+ };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@universal-mcp-toolkit/server-npm-registry",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Package metadata, search, and version tools for the npm registry.",
6
+ "license": "MIT",
7
+ "bin": {
8
+ "server-npm-registry": "./dist/index.js",
9
+ "umt-npm-registry": "./dist/index.js"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/Markgatcha/universal-mcp-toolkit.git"
14
+ },
15
+ "homepage": "https://github.com/Markgatcha/universal-mcp-toolkit#readme",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ ".well-known"
26
+ ],
27
+ "keywords": [
28
+ "mcp",
29
+ "model-context-protocol",
30
+ "ai",
31
+ "developer-tools",
32
+ "typescript",
33
+ "npm",
34
+ "registry",
35
+ "packages",
36
+ "versions"
37
+ ],
38
+ "dependencies": {
39
+ "@universal-mcp-toolkit/core": "0.1.0",
40
+ "zod": "^4.3.6"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "scripts": {
46
+ "build": "tsup src/index.ts --format esm --dts --clean",
47
+ "dev": "tsx watch src/index.ts",
48
+ "lint": "tsc --noEmit",
49
+ "typecheck": "tsc --noEmit",
50
+ "test": "vitest run --passWithNoTests",
51
+ "clean": "rimraf dist"
52
+ }
53
+ }