@modootoday/pmcp 0.1.4 → 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.
- package/README.md +5 -2
- package/dist/catalog-BiPLcbLu.d.ts +86 -0
- package/dist/{chunk-SLTUWTII.js → chunk-MBAXK4IZ.js} +2 -2
- package/dist/{chunk-AJNIMNCV.js → chunk-QJS3NYLT.js} +13 -8
- package/dist/chunk-SPWHSHOC.js +800 -0
- package/dist/cli.js +60 -17
- package/dist/index.d.ts +96 -79
- package/dist/index.js +16 -2
- package/dist/stdio.d.ts +4 -1
- package/dist/stdio.js +2 -2
- package/package.json +3 -2
- package/skills/package-skill-catalog/SKILL.md +10 -8
- package/dist/chunk-N3SHKKWG.js +0 -363
package/dist/cli.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
openDatabase
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MBAXK4IZ.js";
|
|
5
5
|
import {
|
|
6
6
|
serve
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-QJS3NYLT.js";
|
|
8
8
|
import {
|
|
9
9
|
readCatalog
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-SPWHSHOC.js";
|
|
11
11
|
|
|
12
12
|
// src/cli/command.ts
|
|
13
13
|
var ArgumentError = class extends Error {
|
|
@@ -302,16 +302,46 @@ var CATALOG_OPTIONS = [
|
|
|
302
302
|
describe: "Only packages whose names start with this. Repeatable.",
|
|
303
303
|
repeat: true,
|
|
304
304
|
placeholder: "<prefix>"
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: "marketplace",
|
|
308
|
+
describe: "A plugin marketplace directory (holds .claude-plugin/marketplace.json). Repeatable.",
|
|
309
|
+
repeat: true,
|
|
310
|
+
placeholder: "<dir>"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: "package",
|
|
314
|
+
describe: "A package directory read directly, for one no node_modules links; without a package.json its folder name is the package name. Repeatable.",
|
|
315
|
+
repeat: true,
|
|
316
|
+
placeholder: "<dir>"
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
name: "workspace",
|
|
320
|
+
describe: "A workspace root, or a glob of them: each root and every member its workspaces globs name. Repeatable.",
|
|
321
|
+
repeat: true,
|
|
322
|
+
placeholder: "<dir>"
|
|
305
323
|
}
|
|
306
324
|
];
|
|
307
325
|
function rootsFrom(context) {
|
|
308
326
|
const given = many(context.args, "root");
|
|
309
327
|
return given.length > 0 ? given : [`${context.cwd}/node_modules`];
|
|
310
328
|
}
|
|
311
|
-
function
|
|
329
|
+
function catalogOptionsFrom(context, onReject) {
|
|
312
330
|
const scopes = many(context.args, "scope");
|
|
313
|
-
const
|
|
314
|
-
|
|
331
|
+
const marketplaces = many(context.args, "marketplace");
|
|
332
|
+
const packages = many(context.args, "package");
|
|
333
|
+
const workspaces = many(context.args, "workspace");
|
|
334
|
+
return {
|
|
335
|
+
roots: rootsFrom(context),
|
|
336
|
+
...scopes.length > 0 ? { scopes } : {},
|
|
337
|
+
...marketplaces.length > 0 ? { marketplaces } : {},
|
|
338
|
+
...packages.length > 0 ? { packages } : {},
|
|
339
|
+
...workspaces.length > 0 ? { workspaces } : {},
|
|
340
|
+
...onReject ? { onReject } : {}
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function catalogFrom(context, onReject) {
|
|
344
|
+
return readCatalog(catalogOptionsFrom(context, onReject));
|
|
315
345
|
}
|
|
316
346
|
|
|
317
347
|
// src/commands/reindex.ts
|
|
@@ -418,26 +448,39 @@ var indexCommand = {
|
|
|
418
448
|
// src/commands/list.ts
|
|
419
449
|
var listCommand = {
|
|
420
450
|
name: "list",
|
|
421
|
-
describe: "List the skills discovered in node_modules",
|
|
422
|
-
usage: "pmcp list [--root <dir>] [--scope <prefix>] [--json]",
|
|
451
|
+
describe: "List the skills discovered in node_modules and marketplaces",
|
|
452
|
+
usage: "pmcp list [--root <dir>] [--scope <prefix>] [--marketplace <dir>] [--package <dir>] [--workspace <dir|glob>] [--json]",
|
|
423
453
|
options: [
|
|
424
454
|
...CATALOG_OPTIONS,
|
|
425
455
|
{ name: "json", describe: "Emit JSON rather than a table.", boolean: true }
|
|
426
456
|
],
|
|
427
457
|
run(context) {
|
|
428
|
-
const
|
|
458
|
+
const rejected = [];
|
|
459
|
+
const entries = catalogFrom(
|
|
460
|
+
context,
|
|
461
|
+
(rejection) => rejected.push(rejection)
|
|
462
|
+
);
|
|
429
463
|
if (context.args.flags.has("json")) {
|
|
430
|
-
context.ui.data(
|
|
431
|
-
|
|
432
|
-
|
|
464
|
+
context.ui.data(
|
|
465
|
+
`${JSON.stringify({ skills: entries, rejected }, null, 2)}
|
|
466
|
+
`
|
|
467
|
+
);
|
|
468
|
+
return entries.length > 0 && rejected.length === 0 ? 0 : 1;
|
|
433
469
|
}
|
|
470
|
+
for (const { path, reason } of rejected)
|
|
471
|
+
context.ui.error("skipped", `${path}: ${reason}`);
|
|
434
472
|
if (entries.length === 0) {
|
|
435
473
|
context.ui.error("no skills found", rootsFrom(context).join(" "));
|
|
436
474
|
return 1;
|
|
437
475
|
}
|
|
438
|
-
context.ui.table(
|
|
476
|
+
context.ui.table(
|
|
477
|
+
entries.map((entry2) => [
|
|
478
|
+
entry2.tier === void 0 ? entry2.name : `${entry2.name} [${entry2.tier}]`,
|
|
479
|
+
entry2.description
|
|
480
|
+
])
|
|
481
|
+
);
|
|
439
482
|
context.ui.success(`${entries.length} skills`);
|
|
440
|
-
return 0;
|
|
483
|
+
return rejected.length === 0 ? 0 : 1;
|
|
441
484
|
}
|
|
442
485
|
};
|
|
443
486
|
|
|
@@ -445,10 +488,10 @@ var listCommand = {
|
|
|
445
488
|
var serveCommand = {
|
|
446
489
|
name: "serve",
|
|
447
490
|
describe: "Run the MCP server on stdio (the default with no command)",
|
|
448
|
-
usage: "pmcp serve [--root <dir>] [--scope <prefix>]",
|
|
491
|
+
usage: "pmcp serve [--root <dir>] [--scope <prefix>] [--marketplace <dir>] [--package <dir>] [--workspace <dir>]",
|
|
449
492
|
options: [...CATALOG_OPTIONS],
|
|
450
493
|
run(context) {
|
|
451
|
-
serve(
|
|
494
|
+
serve(catalogOptionsFrom(context));
|
|
452
495
|
return 0;
|
|
453
496
|
}
|
|
454
497
|
};
|
|
@@ -2042,7 +2085,7 @@ function dispatch(argv, options = {}) {
|
|
|
2042
2085
|
}
|
|
2043
2086
|
return run(command, rest, { ui, env, cwd });
|
|
2044
2087
|
}
|
|
2045
|
-
var VERSION = "0.
|
|
2088
|
+
var VERSION = "0.3.0";
|
|
2046
2089
|
async function run(command, argv, context) {
|
|
2047
2090
|
try {
|
|
2048
2091
|
return await command.run({
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { S as SkillEntry, C as CatalogOptions } from './catalog-BiPLcbLu.js';
|
|
2
|
+
export { M as MAX_SKILL_DEPTH, a as SKIPPED_DIRS, d as displayPath, r as readBody, b as readCatalog, c as readFrontmatter, w as withoutBody } from './catalog-BiPLcbLu.js';
|
|
1
3
|
import { McpServer } from '@modelcontextprotocol/server';
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -61,74 +63,6 @@ declare const DEFAULT_INTENT_CACHE_LIMIT = 4096;
|
|
|
61
63
|
*/
|
|
62
64
|
declare function openIntentCache(options: IntentCacheOptions): Promise<IntentCache | null>;
|
|
63
65
|
|
|
64
|
-
/**
|
|
65
|
-
* The skills the installed packages brought with them.
|
|
66
|
-
*
|
|
67
|
-
* Derived from `node_modules` at read time rather than from a list somebody
|
|
68
|
-
* maintains: installing a package is how its skill arrives and uninstalling it
|
|
69
|
-
* is how the skill leaves, so the catalog is always true of the project.
|
|
70
|
-
*/
|
|
71
|
-
interface SkillEntry {
|
|
72
|
-
/** `<package>/<slug>`. Unique, and stable while the package is installed. */
|
|
73
|
-
readonly name: string;
|
|
74
|
-
readonly package: string;
|
|
75
|
-
readonly slug: string;
|
|
76
|
-
/** Frontmatter description. The only text `find` ranks against. */
|
|
77
|
-
readonly description: string;
|
|
78
|
-
/** Absolute path to the SKILL.md. `call` reads the body from here. */
|
|
79
|
-
readonly path: string;
|
|
80
|
-
}
|
|
81
|
-
interface CatalogOptions {
|
|
82
|
-
/** Directories to walk. Each is a `node_modules` root, not a package root. */
|
|
83
|
-
readonly roots: readonly string[];
|
|
84
|
-
/** Package name prefixes to consider. Empty means every package. */
|
|
85
|
-
readonly scopes?: readonly string[];
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Directories a walk does not enter. Kept as short as the evidence allows: a
|
|
89
|
-
* name here is a package whose skill will never be found, and the cost of one
|
|
90
|
-
* extra readdir is far below the cost of missing a skill. Measured 20260906 --
|
|
91
|
-
* `lib` was on this list and hid playwright's `lib/skill/SKILL.md`, which is
|
|
92
|
-
* the most widely installed skill in the real world.
|
|
93
|
-
*/
|
|
94
|
-
declare const SKIPPED_DIRS: ReadonlySet<string>;
|
|
95
|
-
/**
|
|
96
|
-
* How deep below a package root a SKILL.md is looked for. Three reaches every
|
|
97
|
-
* convention in use (skills, .agent/skills, .claude/skills, .gemini/skills,
|
|
98
|
-
* .codex/skills); four was measured to find nothing more.
|
|
99
|
-
*/
|
|
100
|
-
declare const MAX_SKILL_DEPTH = 3;
|
|
101
|
-
/**
|
|
102
|
-
* Reads `name` and `description` out of YAML frontmatter without a parser.
|
|
103
|
-
*
|
|
104
|
-
* A parser is a dependency this server does not otherwise need, and the two
|
|
105
|
-
* fields it wants are flat scalars on their own line. Anything more nested is
|
|
106
|
-
* not read, which is the honest limit rather than a partial YAML.
|
|
107
|
-
*/
|
|
108
|
-
declare function readFrontmatter(source: string): Record<string, string>;
|
|
109
|
-
/**
|
|
110
|
-
* The body after the frontmatter, or the whole file when there is none.
|
|
111
|
-
*
|
|
112
|
-
* The slice starts at the newline terminating the closing `---`, and a blank
|
|
113
|
-
* line after it is separator rather than content, so both are dropped.
|
|
114
|
-
*/
|
|
115
|
-
declare function readBody(source: string): string;
|
|
116
|
-
/**
|
|
117
|
-
* Every skill reachable from the given roots, sorted by name.
|
|
118
|
-
*
|
|
119
|
-
* A package with no skills directory contributes nothing and costs one
|
|
120
|
-
* `existsSync` per convention. A skill with no description is skipped rather
|
|
121
|
-
* than listed:
|
|
122
|
-
* `find` ranks descriptions, so one without a description can only ever be
|
|
123
|
-
* found by already knowing its name, and listing it would spend a session's
|
|
124
|
-
* tokens on an entry that cannot answer.
|
|
125
|
-
*/
|
|
126
|
-
declare function readCatalog(options: CatalogOptions): SkillEntry[];
|
|
127
|
-
/** A catalog entry without its body, which is what `catalog` returns. */
|
|
128
|
-
declare function withoutBody({ path, ...rest }: SkillEntry): Omit<SkillEntry, "path">;
|
|
129
|
-
/** Path relative to a root, for a message a reader can act on. */
|
|
130
|
-
declare function displayPath(root: string, entry: SkillEntry): string;
|
|
131
|
-
|
|
132
66
|
/**
|
|
133
67
|
* Ranking an intent against skill descriptions.
|
|
134
68
|
*
|
|
@@ -186,13 +120,65 @@ declare function cosine(a: Float32Array, b: Float32Array): number;
|
|
|
186
120
|
declare function find(options: FindOptions): Promise<FindResult>;
|
|
187
121
|
|
|
188
122
|
/**
|
|
189
|
-
*
|
|
123
|
+
* Skills laid out as an agent plugin marketplace: a directory holding
|
|
124
|
+
* `.claude-plugin/marketplace.json`, whose plugins keep skills at
|
|
125
|
+
* `skills/<skill>/SKILL.md`. No package.json is involved, which is why the
|
|
126
|
+
* node_modules walk never saw them.
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
type MetadataValue = string | readonly string[];
|
|
130
|
+
/** Why a skill was left out rather than served. */
|
|
131
|
+
interface Rejection {
|
|
132
|
+
readonly path: string;
|
|
133
|
+
readonly reason: string;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The `metadata:` block of a frontmatter, one level deep: scalars and inline
|
|
137
|
+
* lists. Deeper nesting is skipped rather than half-read.
|
|
138
|
+
*/
|
|
139
|
+
declare function readMetadata(source: string): Record<string, MetadataValue>;
|
|
140
|
+
/** The raw frontmatter text between the fences, for skill_describe. */
|
|
141
|
+
declare function readFrontmatterText(source: string): string;
|
|
142
|
+
/**
|
|
143
|
+
* Every skill one marketplace lists. A skill whose own tier disagrees with the
|
|
144
|
+
* marketplace's is rejected: a mislabelled tier is how a private skill leaks.
|
|
145
|
+
*/
|
|
146
|
+
declare function readMarketplace(dir: string, onReject?: (rejection: Rejection) => void): SkillEntry[];
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The five tools, and nothing else.
|
|
190
150
|
*
|
|
191
151
|
* The point of the server is that a session's startup cost does not grow with
|
|
192
|
-
* the number of skills installed: the host lists
|
|
152
|
+
* the number of skills installed: the host lists five tools whatever the
|
|
193
153
|
* catalog holds, and a body is read only when one is asked for.
|
|
194
154
|
*/
|
|
195
155
|
|
|
156
|
+
/** Largest file skill_read returns. A skill needing more should split it. */
|
|
157
|
+
declare const READ_BUDGET_BYTES: number;
|
|
158
|
+
/** How many files skill_describe lists before saying the rest were cut. */
|
|
159
|
+
declare const DESCRIBE_FILE_LIMIT = 200;
|
|
160
|
+
declare const CATALOG_PAGE_SIZE = 8;
|
|
161
|
+
declare const CATALOG_PAGE_MAX = 20;
|
|
162
|
+
/** Equality filters over catalog fields. Unset keys match everything. */
|
|
163
|
+
interface SkillFilters {
|
|
164
|
+
readonly tier?: string;
|
|
165
|
+
readonly package?: string;
|
|
166
|
+
readonly level?: string;
|
|
167
|
+
readonly domain?: string;
|
|
168
|
+
readonly medium?: string;
|
|
169
|
+
}
|
|
170
|
+
type ReadResult = {
|
|
171
|
+
readonly ok: true;
|
|
172
|
+
readonly name: string;
|
|
173
|
+
readonly path: string;
|
|
174
|
+
readonly bytes: number;
|
|
175
|
+
readonly sha256: string;
|
|
176
|
+
readonly text: string;
|
|
177
|
+
} | {
|
|
178
|
+
readonly ok: false;
|
|
179
|
+
readonly error: string;
|
|
180
|
+
readonly detail: string;
|
|
181
|
+
};
|
|
196
182
|
interface SkillServerOptions extends CatalogOptions {
|
|
197
183
|
readonly embedder?: Embedder;
|
|
198
184
|
readonly cache?: IntentCache;
|
|
@@ -202,11 +188,15 @@ interface SkillServerOptions extends CatalogOptions {
|
|
|
202
188
|
}
|
|
203
189
|
interface CatalogResponse {
|
|
204
190
|
readonly count: number;
|
|
191
|
+
readonly page: number;
|
|
192
|
+
readonly totalPages: number;
|
|
193
|
+
readonly hasNext: boolean;
|
|
205
194
|
readonly packages: readonly {
|
|
206
195
|
readonly package: string;
|
|
207
196
|
readonly skills: readonly {
|
|
208
197
|
readonly name: string;
|
|
209
198
|
readonly description: string;
|
|
199
|
+
readonly tier?: string;
|
|
210
200
|
}[];
|
|
211
201
|
}[];
|
|
212
202
|
}
|
|
@@ -222,10 +212,37 @@ interface CallResponse {
|
|
|
222
212
|
* speak a protocol to get them.
|
|
223
213
|
*/
|
|
224
214
|
declare function createSkillTools(options: SkillServerOptions): {
|
|
225
|
-
/**
|
|
226
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Grouped, and without bodies. The cheap overview and the search's miss
|
|
217
|
+
* path. Paged by package so a large catalog never arrives in one answer.
|
|
218
|
+
*/
|
|
219
|
+
catalog(request?: {
|
|
220
|
+
page?: number;
|
|
221
|
+
pageSize?: number;
|
|
222
|
+
tier?: string;
|
|
223
|
+
}): CatalogResponse;
|
|
227
224
|
/** Ranked names. Returns; never loads a body. */
|
|
228
|
-
find(intent: string, limit?: number): Promise<FindResult>;
|
|
225
|
+
find(intent: string, limit?: number, filters?: SkillFilters): Promise<FindResult>;
|
|
226
|
+
/** Every property of one skill and the files it carries, without bodies. */
|
|
227
|
+
describe(name: string): {
|
|
228
|
+
name: string;
|
|
229
|
+
package: string;
|
|
230
|
+
description: string;
|
|
231
|
+
tier: string | undefined;
|
|
232
|
+
metadata: Readonly<Record<string, string | readonly string[]>>;
|
|
233
|
+
frontmatter: string;
|
|
234
|
+
files: {
|
|
235
|
+
path: string;
|
|
236
|
+
bytes: number;
|
|
237
|
+
sha256: string;
|
|
238
|
+
}[];
|
|
239
|
+
filesTruncated: boolean;
|
|
240
|
+
} | null;
|
|
241
|
+
/**
|
|
242
|
+
* One file inside a skill's directory. Paths that leave the directory,
|
|
243
|
+
* directly or through a link, are refused; nothing is ever executed.
|
|
244
|
+
*/
|
|
245
|
+
read(name: string, path?: string): ReadResult;
|
|
229
246
|
/** One body, at the moment it is needed. */
|
|
230
247
|
call(name: string): CallResponse | null;
|
|
231
248
|
/** Drops the memoised catalog, so the next call re-reads the tree. */
|
|
@@ -235,29 +252,29 @@ declare function createSkillTools(options: SkillServerOptions): {
|
|
|
235
252
|
type SkillTools = ReturnType<typeof createSkillTools>;
|
|
236
253
|
|
|
237
254
|
/**
|
|
238
|
-
* The
|
|
255
|
+
* The five tools, spoken over MCP.
|
|
239
256
|
*
|
|
240
257
|
* Thin on purpose: every decision is in `createSkillTools`, and this file only
|
|
241
258
|
* names the tools and shapes their arguments. A consumer that wants the same
|
|
242
|
-
*
|
|
243
|
-
*
|
|
259
|
+
* operations from a hook or a script uses the tools directly and never speaks
|
|
260
|
+
* a protocol.
|
|
244
261
|
*/
|
|
245
262
|
|
|
246
263
|
/** Reported to the host on connect. */
|
|
247
264
|
declare const SERVER_NAME = "pmcp";
|
|
248
265
|
/**
|
|
249
|
-
* Registers the
|
|
266
|
+
* Registers the five tools on a server.
|
|
250
267
|
*
|
|
251
268
|
* Exported separately from the stdio entry so a consumer already running an MCP
|
|
252
269
|
* server can add these to it rather than starting a second process.
|
|
253
270
|
*/
|
|
254
271
|
declare function registerSkillTools(server: McpServer, tools: SkillTools): McpServer;
|
|
255
272
|
/**
|
|
256
|
-
* A server with the
|
|
273
|
+
* A server with the five tools registered.
|
|
257
274
|
*
|
|
258
275
|
* The catalog is read on first use rather than here, so a host that connects
|
|
259
276
|
* and never asks pays nothing for the walk.
|
|
260
277
|
*/
|
|
261
278
|
declare function createSkillServer(options: SkillServerOptions): McpServer;
|
|
262
279
|
|
|
263
|
-
export { type CallResponse,
|
|
280
|
+
export { CATALOG_PAGE_MAX, CATALOG_PAGE_SIZE, type CallResponse, CatalogOptions, type CatalogResponse, DEFAULT_FIND_LIMIT, DEFAULT_INTENT_CACHE_LIMIT, DESCRIBE_FILE_LIMIT, type Embedder, type FindOptions, type FindResult, type IntentCache, type IntentCacheOptions, type IntentCacheStats, type Match, type MetadataValue, READ_BUDGET_BYTES, type Ranking, type ReadResult, type Rejection, SERVER_NAME, SkillEntry, type SkillFilters, type SkillServerOptions, type SkillTools, cosine, createSkillServer, createSkillTools, find, lexicalScore, normaliseIntent, openIntentCache, readFrontmatterText, readMarketplace, readMetadata, registerSkillTools };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_INTENT_CACHE_LIMIT,
|
|
3
3
|
openIntentCache
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MBAXK4IZ.js";
|
|
5
5
|
import {
|
|
6
|
+
CATALOG_PAGE_MAX,
|
|
7
|
+
CATALOG_PAGE_SIZE,
|
|
6
8
|
DEFAULT_FIND_LIMIT,
|
|
9
|
+
DESCRIBE_FILE_LIMIT,
|
|
7
10
|
MAX_SKILL_DEPTH,
|
|
11
|
+
READ_BUDGET_BYTES,
|
|
8
12
|
SERVER_NAME,
|
|
9
13
|
SKIPPED_DIRS,
|
|
10
14
|
cosine,
|
|
@@ -17,13 +21,20 @@ import {
|
|
|
17
21
|
readBody,
|
|
18
22
|
readCatalog,
|
|
19
23
|
readFrontmatter,
|
|
24
|
+
readFrontmatterText,
|
|
25
|
+
readMarketplace,
|
|
26
|
+
readMetadata,
|
|
20
27
|
registerSkillTools,
|
|
21
28
|
withoutBody
|
|
22
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-SPWHSHOC.js";
|
|
23
30
|
export {
|
|
31
|
+
CATALOG_PAGE_MAX,
|
|
32
|
+
CATALOG_PAGE_SIZE,
|
|
24
33
|
DEFAULT_FIND_LIMIT,
|
|
25
34
|
DEFAULT_INTENT_CACHE_LIMIT,
|
|
35
|
+
DESCRIBE_FILE_LIMIT,
|
|
26
36
|
MAX_SKILL_DEPTH,
|
|
37
|
+
READ_BUDGET_BYTES,
|
|
27
38
|
SERVER_NAME,
|
|
28
39
|
SKIPPED_DIRS,
|
|
29
40
|
cosine,
|
|
@@ -37,6 +48,9 @@ export {
|
|
|
37
48
|
readBody,
|
|
38
49
|
readCatalog,
|
|
39
50
|
readFrontmatter,
|
|
51
|
+
readFrontmatterText,
|
|
52
|
+
readMarketplace,
|
|
53
|
+
readMetadata,
|
|
40
54
|
registerSkillTools,
|
|
41
55
|
withoutBody
|
|
42
56
|
};
|
package/dist/stdio.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { C as CatalogOptions } from './catalog-BiPLcbLu.js';
|
|
3
|
+
|
|
2
4
|
/**
|
|
3
5
|
* The stdio entry a host launches.
|
|
4
6
|
*
|
|
@@ -6,9 +8,10 @@
|
|
|
6
8
|
* the dependency tree is its data source, and from an npx cache it is not a
|
|
7
9
|
* sibling of the consumer's packages and would find no skills at all.
|
|
8
10
|
*/
|
|
11
|
+
|
|
9
12
|
/** `node_modules` roots to walk, from argv or from the working directory. */
|
|
10
13
|
declare function resolveRoots(argv: readonly string[], cwd: string): string[];
|
|
11
|
-
declare function serve(
|
|
14
|
+
declare function serve(catalog: CatalogOptions): void;
|
|
12
15
|
declare function main(argv?: readonly string[]): void;
|
|
13
16
|
|
|
14
17
|
export { main, resolveRoots, serve };
|
package/dist/stdio.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modootoday/pmcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "Elastic-2.0",
|
|
5
5
|
"author": "modootoday",
|
|
6
6
|
"description": "An MCP server that finds the skills your installed packages already ship. Reads SKILL.md out of node_modules by shape rather than by path, so it sees the skills, .agent, .claude, .gemini and .codex conventions alike, and keeps session startup flat as the number of installed skills grows.",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
],
|
|
58
58
|
"scripts": {
|
|
59
59
|
"build": "tsup",
|
|
60
|
-
"typecheck": "
|
|
60
|
+
"typecheck": "tsgo --noEmit --incremental --tsBuildInfoFile ./dist/.tsbuildinfo --singleThreaded && tsgo -p tsconfig.tests.json --noEmit --singleThreaded",
|
|
61
61
|
"test": "vitest run",
|
|
62
62
|
"clean": "rimraf dist",
|
|
63
63
|
"catalog:refresh": "node scripts/refresh-catalog.mjs",
|
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@types/node": "^22.15.21",
|
|
86
86
|
"@types/semver": "7.8.0",
|
|
87
|
+
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
87
88
|
"prettier": "^3.6.2",
|
|
88
89
|
"rimraf": "^6.0.1",
|
|
89
90
|
"tsup": "^8.5.1",
|
|
@@ -5,9 +5,9 @@ description: A project has grown past a hundred dependencies and nobody knows wh
|
|
|
5
5
|
|
|
6
6
|
# pmcp
|
|
7
7
|
|
|
8
|
-
An MCP server that reads `SKILL.md` files out of `node_modules` and
|
|
9
|
-
as
|
|
10
|
-
whole catalog.
|
|
8
|
+
An MCP server that reads `SKILL.md` files out of `node_modules` and plugin
|
|
9
|
+
marketplaces and serves them as five tools, so a session pays for the one skill
|
|
10
|
+
it opens rather than for the whole catalog.
|
|
11
11
|
|
|
12
12
|
## When to reach for it
|
|
13
13
|
|
|
@@ -108,11 +108,13 @@ discovery will and will not walk.
|
|
|
108
108
|
|
|
109
109
|
### Tools on the wire
|
|
110
110
|
|
|
111
|
-
| Tool
|
|
112
|
-
|
|
|
113
|
-
| `skill_catalog`
|
|
114
|
-
| `skill_find`
|
|
115
|
-
| `
|
|
111
|
+
| Tool | Argument | Returns |
|
|
112
|
+
| ---------------- | ------------------------------------------------- | ------------------------------------ |
|
|
113
|
+
| `skill_catalog` | optional `page`, `pageSize`, `tier` | each skill's name, description, tier |
|
|
114
|
+
| `skill_find` | `intent`, a sentence; optional `limit`, `filters` | ranked matches |
|
|
115
|
+
| `skill_describe` | `name` | metadata and files, no body |
|
|
116
|
+
| `skill_call` | `name` | that skill's body |
|
|
117
|
+
| `skill_read` | `name`, optional `path` | one file inside the skill, as text |
|
|
116
118
|
|
|
117
119
|
## Worked examples
|
|
118
120
|
|