@modootoday/pmcp 0.3.0 → 0.5.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 +57 -2
- package/dist/{catalog-BiPLcbLu.d.ts → catalog-5v7jrGwP.d.ts} +12 -1
- package/dist/{chunk-SPWHSHOC.js → chunk-JOQTOVTJ.js} +451 -80
- package/dist/{chunk-MBAXK4IZ.js → chunk-PUGLIJYM.js} +62 -3
- package/dist/{chunk-QJS3NYLT.js → chunk-X4LYYULW.js} +2 -2
- package/dist/cli.js +67 -17
- package/dist/http.d.ts +34 -0
- package/dist/http.js +65 -0
- package/dist/index.d.ts +74 -195
- package/dist/index.js +25 -3
- package/dist/server-Dm71qZgM.d.ts +207 -0
- package/dist/stdio.d.ts +1 -1
- package/dist/stdio.js +2 -2
- package/package.json +8 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
normaliseIntent
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JOQTOVTJ.js";
|
|
4
4
|
|
|
5
5
|
// src/cache.ts
|
|
6
6
|
async function openDatabase(path) {
|
|
@@ -126,9 +126,68 @@ async function openIntentCache(options) {
|
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
// src/config.ts
|
|
130
|
+
import { existsSync, readFileSync } from "fs";
|
|
131
|
+
import { dirname, join, resolve } from "path";
|
|
132
|
+
import { parse } from "smol-toml";
|
|
133
|
+
var CONFIG_FILE = "pmcp.toml";
|
|
134
|
+
var ConfigError = class extends Error {
|
|
135
|
+
constructor(path, message) {
|
|
136
|
+
super(`${path}: ${message}`);
|
|
137
|
+
this.name = "ConfigError";
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
var PATH_KEYS = ["roots", "workspaces", "packages", "marketplaces"];
|
|
141
|
+
var CATALOG_KEYS = /* @__PURE__ */ new Set([...PATH_KEYS, "scopes"]);
|
|
142
|
+
function findConfig(start) {
|
|
143
|
+
let dir = resolve(start);
|
|
144
|
+
for (; ; ) {
|
|
145
|
+
const candidate = join(dir, CONFIG_FILE);
|
|
146
|
+
if (existsSync(candidate)) return candidate;
|
|
147
|
+
const parent = dirname(dir);
|
|
148
|
+
if (parent === dir) return null;
|
|
149
|
+
dir = parent;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function stringList(path, key, value) {
|
|
153
|
+
if (!Array.isArray(value) || !value.every((v) => typeof v === "string")) {
|
|
154
|
+
throw new ConfigError(path, `[catalog] ${key} must be a list of strings`);
|
|
155
|
+
}
|
|
156
|
+
return value;
|
|
157
|
+
}
|
|
158
|
+
function readConfig(path) {
|
|
159
|
+
const absolute = resolve(path);
|
|
160
|
+
const dir = dirname(absolute);
|
|
161
|
+
let raw;
|
|
162
|
+
try {
|
|
163
|
+
raw = parse(readFileSync(absolute, "utf8"));
|
|
164
|
+
} catch (error) {
|
|
165
|
+
const reason = error instanceof Error ? error.message : "unreadable";
|
|
166
|
+
throw new ConfigError(absolute, reason.split("\n")[0] ?? reason);
|
|
167
|
+
}
|
|
168
|
+
const section = raw["catalog"];
|
|
169
|
+
if (section === void 0) return { path: absolute, dir, catalog: {}, raw };
|
|
170
|
+
if (section === null || typeof section !== "object" || Array.isArray(section)) {
|
|
171
|
+
throw new ConfigError(absolute, "[catalog] must be a table");
|
|
172
|
+
}
|
|
173
|
+
const catalog = {};
|
|
174
|
+
for (const [key, value] of Object.entries(section)) {
|
|
175
|
+
if (!CATALOG_KEYS.has(key)) {
|
|
176
|
+
throw new ConfigError(absolute, `unknown key [catalog] ${key}`);
|
|
177
|
+
}
|
|
178
|
+
const list = stringList(absolute, key, value);
|
|
179
|
+
catalog[key] = key === "scopes" ? list : list.map((p) => resolve(dir, p));
|
|
180
|
+
}
|
|
181
|
+
return { path: absolute, dir, catalog, raw };
|
|
182
|
+
}
|
|
183
|
+
|
|
129
184
|
export {
|
|
130
185
|
openDatabase,
|
|
131
186
|
DEFAULT_INTENT_CACHE_LIMIT,
|
|
132
|
-
openIntentCache
|
|
187
|
+
openIntentCache,
|
|
188
|
+
CONFIG_FILE,
|
|
189
|
+
ConfigError,
|
|
190
|
+
findConfig,
|
|
191
|
+
readConfig
|
|
133
192
|
};
|
|
134
|
-
//# sourceMappingURL=chunk-
|
|
193
|
+
//# sourceMappingURL=chunk-PUGLIJYM.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createSkillServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JOQTOVTJ.js";
|
|
4
4
|
|
|
5
5
|
// src/stdio.ts
|
|
6
6
|
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
@@ -35,4 +35,4 @@ export {
|
|
|
35
35
|
serve,
|
|
36
36
|
main
|
|
37
37
|
};
|
|
38
|
-
//# sourceMappingURL=chunk-
|
|
38
|
+
//# sourceMappingURL=chunk-X4LYYULW.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
ConfigError,
|
|
4
|
+
findConfig,
|
|
5
|
+
openDatabase,
|
|
6
|
+
readConfig
|
|
7
|
+
} from "./chunk-PUGLIJYM.js";
|
|
5
8
|
import {
|
|
6
9
|
serve
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-X4LYYULW.js";
|
|
8
11
|
import {
|
|
9
12
|
readCatalog
|
|
10
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-JOQTOVTJ.js";
|
|
11
14
|
|
|
12
15
|
// src/cli/command.ts
|
|
13
16
|
var ArgumentError = class extends Error {
|
|
@@ -320,17 +323,48 @@ var CATALOG_OPTIONS = [
|
|
|
320
323
|
describe: "A workspace root, or a glob of them: each root and every member its workspaces globs name. Repeatable.",
|
|
321
324
|
repeat: true,
|
|
322
325
|
placeholder: "<dir>"
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
name: "config",
|
|
329
|
+
describe: "A pmcp.toml to read instead of the nearest one at or above the working directory.",
|
|
330
|
+
placeholder: "<file>"
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
name: "no-config",
|
|
334
|
+
describe: "Ignore pmcp.toml; only the flags given count.",
|
|
335
|
+
boolean: true
|
|
323
336
|
}
|
|
324
337
|
];
|
|
338
|
+
function configFrom(context) {
|
|
339
|
+
if (context.args.flags.has("no-config")) return null;
|
|
340
|
+
const named = one(context.args, "config");
|
|
341
|
+
const path = named ?? findConfig(context.cwd);
|
|
342
|
+
if (path === null) return null;
|
|
343
|
+
try {
|
|
344
|
+
return readConfig(path);
|
|
345
|
+
} catch (error) {
|
|
346
|
+
if (error instanceof ConfigError) throw new ArgumentError(error.message);
|
|
347
|
+
if (named !== void 0) throw new ArgumentError(`cannot read ${named}`);
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
325
351
|
function rootsFrom(context) {
|
|
326
352
|
const given = many(context.args, "root");
|
|
327
|
-
|
|
353
|
+
if (given.length > 0) return given;
|
|
354
|
+
const config = configFrom(context);
|
|
355
|
+
if (config?.catalog.roots !== void 0) return config.catalog.roots;
|
|
356
|
+
return [`${config?.dir ?? context.cwd}/node_modules`];
|
|
328
357
|
}
|
|
329
358
|
function catalogOptionsFrom(context, onReject) {
|
|
330
|
-
const
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
359
|
+
const config = configFrom(context)?.catalog ?? {};
|
|
360
|
+
const pick = (flag, fromFile) => {
|
|
361
|
+
const given = many(context.args, flag);
|
|
362
|
+
return given.length > 0 ? given : fromFile ?? [];
|
|
363
|
+
};
|
|
364
|
+
const scopes = pick("scope", config.scopes);
|
|
365
|
+
const marketplaces = pick("marketplace", config.marketplaces);
|
|
366
|
+
const packages = pick("package", config.packages);
|
|
367
|
+
const workspaces = pick("workspace", config.workspaces);
|
|
334
368
|
return {
|
|
335
369
|
roots: rootsFrom(context),
|
|
336
370
|
...scopes.length > 0 ? { scopes } : {},
|
|
@@ -449,7 +483,7 @@ var indexCommand = {
|
|
|
449
483
|
var listCommand = {
|
|
450
484
|
name: "list",
|
|
451
485
|
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]",
|
|
486
|
+
usage: "pmcp list [--root <dir>] [--scope <prefix>] [--marketplace <dir>] [--package <dir>] [--workspace <dir|glob>] [--config <file> | --no-config] [--json]",
|
|
453
487
|
options: [
|
|
454
488
|
...CATALOG_OPTIONS,
|
|
455
489
|
{ name: "json", describe: "Emit JSON rather than a table.", boolean: true }
|
|
@@ -474,12 +508,28 @@ var listCommand = {
|
|
|
474
508
|
return 1;
|
|
475
509
|
}
|
|
476
510
|
context.ui.table(
|
|
477
|
-
entries.map((entry2) =>
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
511
|
+
entries.map((entry2) => {
|
|
512
|
+
const tags = [entry2.kind, entry2.tier].filter(Boolean).join(", ");
|
|
513
|
+
return [
|
|
514
|
+
tags === "" ? entry2.name : `${entry2.name} [${tags}]`,
|
|
515
|
+
entry2.description
|
|
516
|
+
];
|
|
517
|
+
})
|
|
518
|
+
);
|
|
519
|
+
const counts = /* @__PURE__ */ new Map();
|
|
520
|
+
for (const entry2 of entries) {
|
|
521
|
+
const kind = entry2.kind ?? "skill";
|
|
522
|
+
counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
523
|
+
}
|
|
524
|
+
const plural = {
|
|
525
|
+
skill: "skills",
|
|
526
|
+
agent: "agents",
|
|
527
|
+
hook: "hooks",
|
|
528
|
+
mcp: "MCP servers"
|
|
529
|
+
};
|
|
530
|
+
context.ui.success(
|
|
531
|
+
[...counts].map(([kind, n]) => `${n} ${plural[kind] ?? kind}`).join(", ")
|
|
481
532
|
);
|
|
482
|
-
context.ui.success(`${entries.length} skills`);
|
|
483
533
|
return rejected.length === 0 ? 0 : 1;
|
|
484
534
|
}
|
|
485
535
|
};
|
|
@@ -488,7 +538,7 @@ var listCommand = {
|
|
|
488
538
|
var serveCommand = {
|
|
489
539
|
name: "serve",
|
|
490
540
|
describe: "Run the MCP server on stdio (the default with no command)",
|
|
491
|
-
usage: "pmcp serve [--root <dir>] [--scope <prefix>] [--marketplace <dir>] [--package <dir>] [--workspace <dir>]",
|
|
541
|
+
usage: "pmcp serve [--root <dir>] [--scope <prefix>] [--marketplace <dir>] [--package <dir>] [--workspace <dir>] [--config <file> | --no-config]",
|
|
492
542
|
options: [...CATALOG_OPTIONS],
|
|
493
543
|
run(context) {
|
|
494
544
|
serve(catalogOptionsFrom(context));
|
|
@@ -2085,7 +2135,7 @@ function dispatch(argv, options = {}) {
|
|
|
2085
2135
|
}
|
|
2086
2136
|
return run(command, rest, { ui, env, cwd });
|
|
2087
2137
|
}
|
|
2088
|
-
var VERSION = "0.
|
|
2138
|
+
var VERSION = "0.5.0";
|
|
2089
2139
|
async function run(command, argv, context) {
|
|
2090
2140
|
try {
|
|
2091
2141
|
return await command.run({
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { S as SkillEntry } from './catalog-5v7jrGwP.js';
|
|
2
|
+
import { S as SkillServerOptions } from './server-Dm71qZgM.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The catalog over streamable HTTP, for a hosted skills server.
|
|
6
|
+
*
|
|
7
|
+
* The host owns identity: it passes authorize(), which turns a request into a
|
|
8
|
+
* caller or undefined. Anonymous requests are refused; each caller sees only the
|
|
9
|
+
* tiers tiersFor() grants. The catalog is read once and filtered per request.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
interface SkillCaller {
|
|
13
|
+
/** Stable id of the signed-in user, recorded by the host, never by pmcp. */
|
|
14
|
+
readonly id: string;
|
|
15
|
+
/** Whatever the host's token carried; passed to tiersFor unchanged. */
|
|
16
|
+
readonly claims?: Readonly<Record<string, unknown>>;
|
|
17
|
+
}
|
|
18
|
+
interface SkillHttpOptions extends SkillServerOptions {
|
|
19
|
+
readonly authorize: (request: Request) => SkillCaller | undefined | Promise<SkillCaller | undefined>;
|
|
20
|
+
/** Tiers a caller may list and load. Package skills without a tier count as open. */
|
|
21
|
+
readonly tiersFor?: (caller: SkillCaller) => readonly string[];
|
|
22
|
+
/** Sent in WWW-Authenticate on a refused request, e.g. a resource metadata URL. */
|
|
23
|
+
readonly challenge?: string;
|
|
24
|
+
}
|
|
25
|
+
declare const DEFAULT_TIERS: readonly string[];
|
|
26
|
+
declare function visibleTo(entries: readonly SkillEntry[], tiers: readonly string[]): SkillEntry[];
|
|
27
|
+
interface SkillHttpHandler {
|
|
28
|
+
fetch(request: Request): Promise<Response>;
|
|
29
|
+
/** Re-read the catalog, for a host that deploys new skill releases in place. */
|
|
30
|
+
reload(): void;
|
|
31
|
+
}
|
|
32
|
+
declare function createSkillHttpHandler(options: SkillHttpOptions): SkillHttpHandler;
|
|
33
|
+
|
|
34
|
+
export { DEFAULT_TIERS, type SkillCaller, type SkillHttpHandler, type SkillHttpOptions, createSkillHttpHandler, visibleTo };
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createSkillServer,
|
|
3
|
+
readCatalog
|
|
4
|
+
} from "./chunk-JOQTOVTJ.js";
|
|
5
|
+
|
|
6
|
+
// src/http.ts
|
|
7
|
+
import {
|
|
8
|
+
createMcpHandler
|
|
9
|
+
} from "@modelcontextprotocol/server";
|
|
10
|
+
var DEFAULT_TIERS = ["open", "free"];
|
|
11
|
+
function visibleTo(entries, tiers) {
|
|
12
|
+
const allowed = new Set(tiers);
|
|
13
|
+
return entries.filter((entry) => allowed.has(entry.tier ?? "open"));
|
|
14
|
+
}
|
|
15
|
+
function createSkillHttpHandler(options) {
|
|
16
|
+
const load = options.loadCatalog ?? readCatalog;
|
|
17
|
+
let all = load(options);
|
|
18
|
+
const tiersFor = options.tiersFor ?? (() => DEFAULT_TIERS);
|
|
19
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
20
|
+
const handlerFor = (tiers) => {
|
|
21
|
+
const key = [...tiers].sort().join(",");
|
|
22
|
+
const cached = handlers.get(key);
|
|
23
|
+
if (cached) return cached;
|
|
24
|
+
const handler = createMcpHandler(
|
|
25
|
+
() => createSkillServer({
|
|
26
|
+
...options,
|
|
27
|
+
loadCatalog: () => visibleTo(all, tiers)
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
handlers.set(key, handler);
|
|
31
|
+
return handler;
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
async fetch(request) {
|
|
35
|
+
const caller = await options.authorize(request);
|
|
36
|
+
if (!caller) {
|
|
37
|
+
return new Response(JSON.stringify({ error: "unauthorized" }), {
|
|
38
|
+
status: 401,
|
|
39
|
+
headers: {
|
|
40
|
+
"content-type": "application/json",
|
|
41
|
+
"www-authenticate": options.challenge ?? "Bearer"
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return handlerFor(tiersFor(caller)).fetch(request, {
|
|
46
|
+
authInfo: {
|
|
47
|
+
token: "",
|
|
48
|
+
clientId: caller.id,
|
|
49
|
+
scopes: [],
|
|
50
|
+
extra: { ...caller.claims }
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
reload() {
|
|
55
|
+
all = load(options);
|
|
56
|
+
handlers.clear();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export {
|
|
61
|
+
DEFAULT_TIERS,
|
|
62
|
+
createSkillHttpHandler,
|
|
63
|
+
visibleTo
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=http.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,36 +1,9 @@
|
|
|
1
|
-
import { S as
|
|
2
|
-
export {
|
|
1
|
+
import { I as IntentCache, S as SkillServerOptions, a as SkillTools } from './server-Dm71qZgM.js';
|
|
2
|
+
export { C as CATALOG_PAGE_MAX, b as CATALOG_PAGE_SIZE, c as CallResponse, d as CatalogItem, e as CatalogResponse, D as DEFAULT_FIND_LIMIT, f as DESCRIBE_FILE_LIMIT, E as Embedder, F as FindOptions, g as FindResult, h as IntentCacheStats, K as KindFilter, M as Match, R as READ_BUDGET_BYTES, i as Ranking, j as ReadResult, k as SkillFilters, l as cosine, m as createSkillTools, n as find, o as lexicalScore, p as normaliseIntent } from './server-Dm71qZgM.js';
|
|
3
|
+
import { S as SkillEntry } from './catalog-5v7jrGwP.js';
|
|
4
|
+
export { a as ASSET_KINDS, A as AssetKind, C as CatalogOptions, M as MAX_SKILL_DEPTH, b as SKIPPED_DIRS, d as displayPath, k as kindOf, r as readBody, c as readCatalog, e as readFrontmatter, w as withoutBody } from './catalog-5v7jrGwP.js';
|
|
3
5
|
import { McpServer } from '@modelcontextprotocol/server';
|
|
4
6
|
|
|
5
|
-
/**
|
|
6
|
-
* The intent key and the cache contract.
|
|
7
|
-
*
|
|
8
|
-
* Separate from the sqlite implementation so that ranking, which needs the key
|
|
9
|
-
* and the interface, does not pull a bun-only module into a consumer running
|
|
10
|
-
* under node. Only `cache.ts` requires bun.
|
|
11
|
-
*/
|
|
12
|
-
interface IntentCacheStats {
|
|
13
|
-
readonly hits: number;
|
|
14
|
-
readonly misses: number;
|
|
15
|
-
/** Rows found for the key but written by a different model. */
|
|
16
|
-
readonly mismatched: number;
|
|
17
|
-
readonly rows: number;
|
|
18
|
-
/** Which sqlite builtin answered, or absent when there is no cache. */
|
|
19
|
-
readonly provider?: string;
|
|
20
|
-
}
|
|
21
|
-
interface IntentCache {
|
|
22
|
-
/** The cached vector for an intent, or null when nothing usable is stored. */
|
|
23
|
-
get(intent: string): Float32Array | null;
|
|
24
|
-
put(intent: string, vector: Float32Array): void;
|
|
25
|
-
stats(): IntentCacheStats;
|
|
26
|
-
close(): void;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Same question, differently typed. Case, punctuation and spacing only — the
|
|
30
|
-
* key has to survive a caller writing "How do I wire X?" and "how do i wire x".
|
|
31
|
-
*/
|
|
32
|
-
declare function normaliseIntent(intent: string): string;
|
|
33
|
-
|
|
34
7
|
/**
|
|
35
8
|
* Query vectors, kept on disk between sessions.
|
|
36
9
|
*
|
|
@@ -64,70 +37,14 @@ declare const DEFAULT_INTENT_CACHE_LIMIT = 4096;
|
|
|
64
37
|
declare function openIntentCache(options: IntentCacheOptions): Promise<IntentCache | null>;
|
|
65
38
|
|
|
66
39
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* from a working one.
|
|
72
|
-
*/
|
|
73
|
-
|
|
74
|
-
type Ranking = "semantic" | "lexical";
|
|
75
|
-
interface Match {
|
|
76
|
-
readonly name: string;
|
|
77
|
-
readonly package: string;
|
|
78
|
-
readonly description: string;
|
|
79
|
-
readonly score: number;
|
|
80
|
-
}
|
|
81
|
-
interface FindResult {
|
|
82
|
-
readonly ranking: Ranking;
|
|
83
|
-
readonly matches: readonly Match[];
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* The encoder, supplied by the caller. There is no default and no bundled
|
|
87
|
-
* model: which model runs decides what the scores mean, so it is the consuming
|
|
88
|
-
* repository's declaration rather than this package's.
|
|
89
|
-
*/
|
|
90
|
-
interface Embedder {
|
|
91
|
-
readonly modelId: string;
|
|
92
|
-
readonly dims: number;
|
|
93
|
-
embed(text: string): Promise<Float32Array>;
|
|
94
|
-
}
|
|
95
|
-
interface FindOptions {
|
|
96
|
-
readonly entries: readonly SkillEntry[];
|
|
97
|
-
readonly intent: string;
|
|
98
|
-
readonly limit?: number;
|
|
99
|
-
readonly embedder?: Embedder;
|
|
100
|
-
readonly cache?: IntentCache;
|
|
101
|
-
/** Precomputed description vectors, keyed by skill name. */
|
|
102
|
-
readonly vectors?: ReadonlyMap<string, Float32Array>;
|
|
103
|
-
}
|
|
104
|
-
declare const DEFAULT_FIND_LIMIT = 5;
|
|
105
|
-
/**
|
|
106
|
-
* Token overlap and bigram Dice, averaged. Deterministic and dependency-free:
|
|
107
|
-
* this is the answer when there is no model, so it must never be the thing
|
|
108
|
-
* that fails.
|
|
109
|
-
*/
|
|
110
|
-
declare function lexicalScore(intent: string, description: string): number;
|
|
111
|
-
/** Cosine over two equal-width vectors. Zero when either has no magnitude. */
|
|
112
|
-
declare function cosine(a: Float32Array, b: Float32Array): number;
|
|
113
|
-
/**
|
|
114
|
-
* Ranked matches, and which ranking produced them.
|
|
115
|
-
*
|
|
116
|
-
* Returns rather than loads: a search verb that also acts cannot be used to
|
|
117
|
-
* look, and the whole reason this server exists is that looking should be
|
|
118
|
-
* cheap.
|
|
119
|
-
*/
|
|
120
|
-
declare function find(options: FindOptions): Promise<FindResult>;
|
|
121
|
-
|
|
122
|
-
/**
|
|
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.
|
|
40
|
+
* Assets laid out as an agent plugin marketplace: a directory holding
|
|
41
|
+
* `.claude-plugin/marketplace.json`, whose plugins keep skills, commands,
|
|
42
|
+
* agents, hooks and MCP servers in the plugin format's own places. No
|
|
43
|
+
* package.json is involved, which is why the node_modules walk never saw them.
|
|
127
44
|
*/
|
|
128
45
|
|
|
129
46
|
type MetadataValue = string | readonly string[];
|
|
130
|
-
/** Why
|
|
47
|
+
/** Why an asset was left out rather than served. */
|
|
131
48
|
interface Rejection {
|
|
132
49
|
readonly path: string;
|
|
133
50
|
readonly reason: string;
|
|
@@ -139,117 +56,48 @@ interface Rejection {
|
|
|
139
56
|
declare function readMetadata(source: string): Record<string, MetadataValue>;
|
|
140
57
|
/** The raw frontmatter text between the fences, for skill_describe. */
|
|
141
58
|
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
|
-
*/
|
|
59
|
+
/** Every asset one marketplace lists. */
|
|
146
60
|
declare function readMarketplace(dir: string, onReject?: (rejection: Rejection) => void): SkillEntry[];
|
|
61
|
+
/** The directory or single file an entry's describe and read may open. */
|
|
62
|
+
declare const assetRoot: (entry: Pick<SkillEntry, "root" | "path">) => string;
|
|
147
63
|
|
|
148
64
|
/**
|
|
149
|
-
*
|
|
65
|
+
* `pmcp.toml`, the project's one record of where its catalog lives.
|
|
150
66
|
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*/
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
interface SkillFilters {
|
|
164
|
-
readonly tier?: string;
|
|
165
|
-
readonly package?: string;
|
|
166
|
-
readonly level?: string;
|
|
167
|
-
readonly domain?: string;
|
|
168
|
-
readonly medium?: string;
|
|
67
|
+
* Paths in it are relative to the file, so the same file works from any
|
|
68
|
+
* directory a host starts pmcp in. Only `[catalog]` is read here; the other
|
|
69
|
+
* sections belong to the projector and are kept as given.
|
|
70
|
+
*/
|
|
71
|
+
declare const CONFIG_FILE = "pmcp.toml";
|
|
72
|
+
/** What `[catalog]` may say. Each mirrors the repeatable CLI flag of the same meaning. */
|
|
73
|
+
interface CatalogSection {
|
|
74
|
+
readonly roots?: readonly string[];
|
|
75
|
+
readonly scopes?: readonly string[];
|
|
76
|
+
readonly workspaces?: readonly string[];
|
|
77
|
+
readonly packages?: readonly string[];
|
|
78
|
+
readonly marketplaces?: readonly string[];
|
|
169
79
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
readonly name: string;
|
|
80
|
+
interface ProjectConfig {
|
|
81
|
+
/** Absolute path of the file read. */
|
|
173
82
|
readonly path: string;
|
|
174
|
-
|
|
175
|
-
readonly
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
readonly
|
|
180
|
-
readonly detail: string;
|
|
181
|
-
};
|
|
182
|
-
interface SkillServerOptions extends CatalogOptions {
|
|
183
|
-
readonly embedder?: Embedder;
|
|
184
|
-
readonly cache?: IntentCache;
|
|
185
|
-
readonly vectors?: ReadonlyMap<string, Float32Array>;
|
|
186
|
-
/** Injected for tests. Reads the catalog off disk otherwise. */
|
|
187
|
-
readonly loadCatalog?: (options: CatalogOptions) => SkillEntry[];
|
|
83
|
+
/** Its directory; every relative path in the file resolves against it. */
|
|
84
|
+
readonly dir: string;
|
|
85
|
+
/** `[catalog]` with paths already absolute. */
|
|
86
|
+
readonly catalog: CatalogSection;
|
|
87
|
+
/** The whole document, for sections this module does not interpret. */
|
|
88
|
+
readonly raw: Readonly<Record<string, unknown>>;
|
|
188
89
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
readonly page: number;
|
|
192
|
-
readonly totalPages: number;
|
|
193
|
-
readonly hasNext: boolean;
|
|
194
|
-
readonly packages: readonly {
|
|
195
|
-
readonly package: string;
|
|
196
|
-
readonly skills: readonly {
|
|
197
|
-
readonly name: string;
|
|
198
|
-
readonly description: string;
|
|
199
|
-
readonly tier?: string;
|
|
200
|
-
}[];
|
|
201
|
-
}[];
|
|
202
|
-
}
|
|
203
|
-
interface CallResponse {
|
|
204
|
-
readonly name: string;
|
|
205
|
-
readonly body: string;
|
|
90
|
+
declare class ConfigError extends Error {
|
|
91
|
+
constructor(path: string, message: string);
|
|
206
92
|
}
|
|
93
|
+
/** The nearest pmcp.toml at or above start, or null when there is none. */
|
|
94
|
+
declare function findConfig(start: string): string | null;
|
|
207
95
|
/**
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
* and so a consumer that wants them from a hook or a script is not made to
|
|
212
|
-
* speak a protocol to get them.
|
|
96
|
+
* Reads and checks one file. An unknown key under `[catalog]` is an error
|
|
97
|
+
* rather than ignored, for the same reason an unknown flag is: a misspelt
|
|
98
|
+
* `workspace = [...]` must not look like it worked.
|
|
213
99
|
*/
|
|
214
|
-
declare function
|
|
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;
|
|
224
|
-
/** Ranked names. Returns; never loads a body. */
|
|
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;
|
|
246
|
-
/** One body, at the moment it is needed. */
|
|
247
|
-
call(name: string): CallResponse | null;
|
|
248
|
-
/** Drops the memoised catalog, so the next call re-reads the tree. */
|
|
249
|
-
refresh(): void;
|
|
250
|
-
entries: () => Omit<SkillEntry, "path">[];
|
|
251
|
-
};
|
|
252
|
-
type SkillTools = ReturnType<typeof createSkillTools>;
|
|
100
|
+
declare function readConfig(path: string): ProjectConfig;
|
|
253
101
|
|
|
254
102
|
/**
|
|
255
103
|
* The five tools, spoken over MCP.
|
|
@@ -270,11 +118,42 @@ declare const SERVER_NAME = "pmcp";
|
|
|
270
118
|
*/
|
|
271
119
|
declare function registerSkillTools(server: McpServer, tools: SkillTools): McpServer;
|
|
272
120
|
/**
|
|
273
|
-
* A server with the five tools registered.
|
|
121
|
+
* A server with the five tools and the Skills extension registered.
|
|
274
122
|
*
|
|
275
123
|
* The catalog is read on first use rather than here, so a host that connects
|
|
276
124
|
* and never asks pays nothing for the walk.
|
|
277
125
|
*/
|
|
278
126
|
declare function createSkillServer(options: SkillServerOptions): McpServer;
|
|
279
127
|
|
|
280
|
-
|
|
128
|
+
/**
|
|
129
|
+
* The MCP Skills extension (SEP-2640): `skills/list`, `skills/get` and the
|
|
130
|
+
* skill files as `skill://pmcp/<name>/<file>` resources, so a host that speaks
|
|
131
|
+
* the extension loads skills natively instead of through the skill_* tools.
|
|
132
|
+
*/
|
|
133
|
+
|
|
134
|
+
declare const SKILLS_EXTENSION = "io.modelcontextprotocol/skills";
|
|
135
|
+
interface SkillResource {
|
|
136
|
+
readonly uri: string;
|
|
137
|
+
readonly digest: string;
|
|
138
|
+
readonly size: number;
|
|
139
|
+
}
|
|
140
|
+
interface SkillExtensionEntry {
|
|
141
|
+
readonly uri: string;
|
|
142
|
+
readonly frontmatter: Readonly<Record<string, unknown>>;
|
|
143
|
+
readonly resources: readonly SkillResource[];
|
|
144
|
+
}
|
|
145
|
+
declare const skillUri: (entry: Pick<SkillEntry, "name">, file?: string) => string;
|
|
146
|
+
/**
|
|
147
|
+
* The extension's entry for one catalog skill, or null when the skill cannot
|
|
148
|
+
* be served under SEP-2640: not a directory with SKILL.md, a frontmatter name
|
|
149
|
+
* that is missing, invalid or unlike its path, or over the per-skill limits.
|
|
150
|
+
* Such a skill stays reachable through the skill_* tools.
|
|
151
|
+
*/
|
|
152
|
+
declare function extensionEntry(entry: SkillEntry): SkillExtensionEntry | null;
|
|
153
|
+
/**
|
|
154
|
+
* Declares the extension and its methods on a server, over the skills the
|
|
155
|
+
* catalog function returns. Must run before the server connects.
|
|
156
|
+
*/
|
|
157
|
+
declare function registerSkillsExtension(server: McpServer, skills: () => readonly SkillEntry[]): void;
|
|
158
|
+
|
|
159
|
+
export { CONFIG_FILE, type CatalogSection, ConfigError, DEFAULT_INTENT_CACHE_LIMIT, IntentCache, type IntentCacheOptions, type MetadataValue, type ProjectConfig, type Rejection, SERVER_NAME, SKILLS_EXTENSION, SkillEntry, type SkillExtensionEntry, type SkillResource, SkillServerOptions, SkillTools, assetRoot, createSkillServer, extensionEntry, findConfig, openIntentCache, readConfig, readFrontmatterText, readMarketplace, readMetadata, registerSkillTools, registerSkillsExtension, skillUri };
|