@modootoday/pmcp 0.8.0 → 0.8.2

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 CHANGED
@@ -189,7 +189,19 @@ const skills = createSkillHttpHandler({
189
189
  export default { fetch: (request: Request) => skills.fetch(request) };
190
190
  ```
191
191
 
192
- Put Origin and Host validation in front of it when it is mounted bare.
192
+ The handler checks requests before they reach MCP:
193
+
194
+ | Option | Default | Effect |
195
+ | ----------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
196
+ | `allowedOrigins` | none | A request with an `Origin` other than the server's own gets 403. Listed origins are served with CORS headers, and their preflight is answered. `"*"` admits any origin. Requests without `Origin` are not from a browser page and pass. |
197
+ | `publicOrigins` | none | The server's own origins as browsers see them, when a proxy ends TLS and the request URL is `http://`. Without it, a same-site page behind such a proxy gets 403. |
198
+ | `allowedHosts` | any | When set, a `Host` outside the list gets 403 (DNS rebinding). |
199
+ | `maxRequestBytes` | 1 MiB | A larger body gets 413; a declared length is refused before reading. |
200
+ | `rateLimit` | off | `{ perMinute }` per caller id; past it, 429 with `retry-after`. |
201
+
202
+ An MCP config (`.mcp.json`) is never served as written: every `env` and `headers` value is
203
+ replaced with `<redacted>`, whichever entry reaches the file, and listed digests describe
204
+ the redacted bytes.
193
205
 
194
206
  Pass `semantic: { cacheDir, localOnly: true }` to rank by meaning: the handler encodes
195
207
  the catalog at start with `@huggingface/transformers` and the multilingual model, so an
package/SECURITY.md CHANGED
@@ -20,6 +20,11 @@ Inspect the plan with `--dry-run`; non-interactive application requires `--yes`.
20
20
  The CLI disables dependency install scripts for skill-package installation and
21
21
  checks catalog integrity against registry metadata before applying a plan.
22
22
 
23
+ The server returns skill files as text or base64 and never runs them. An MCP config
24
+ (`.mcp.json`) is served with every `env` and `headers` value redacted. The HTTP handler
25
+ refuses browser origins it was not given, and caps request size; host names and a
26
+ per-caller rate limit are options. Authentication and call records belong to the host.
27
+
23
28
  ## Supported versions
24
29
 
25
30
  Security fixes are released on the latest published version. Include the pmcp
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  openDatabase
3
- } from "./chunk-OPDVUHTK.js";
3
+ } from "./chunk-IRQTGYHA.js";
4
4
 
5
5
  // src/embed.ts
6
6
  var DEFAULT_MODEL_ID = "Xenova/multilingual-e5-small";
@@ -157,4 +157,4 @@ export {
157
157
  encodeEntries,
158
158
  openEntryVectors
159
159
  };
160
- //# sourceMappingURL=chunk-WSTCBVXW.js.map
160
+ //# sourceMappingURL=chunk-3JNWCUHA.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  normaliseIntent
3
- } from "./chunk-ZDV75HZN.js";
3
+ } from "./chunk-UGRHZHHC.js";
4
4
 
5
5
  // src/cache.ts
6
6
  async function openDatabase(path) {
@@ -131,4 +131,4 @@ export {
131
131
  DEFAULT_INTENT_CACHE_LIMIT,
132
132
  openIntentCache
133
133
  };
134
- //# sourceMappingURL=chunk-OPDVUHTK.js.map
134
+ //# sourceMappingURL=chunk-IRQTGYHA.js.map
@@ -2,7 +2,7 @@ import {
2
2
  frontmatterObject,
3
3
  readCatalog,
4
4
  specProblems
5
- } from "./chunk-ZDV75HZN.js";
5
+ } from "./chunk-UGRHZHHC.js";
6
6
 
7
7
  // src/config.ts
8
8
  import { existsSync, readFileSync } from "fs";
@@ -1139,4 +1139,4 @@ export {
1139
1139
  validateSkillDir,
1140
1140
  validateCatalog
1141
1141
  };
1142
- //# sourceMappingURL=chunk-M6LBYJDS.js.map
1142
+ //# sourceMappingURL=chunk-O2P5AIHX.js.map
@@ -624,16 +624,49 @@ function registerSkillPrompts(server, tools) {
624
624
  // src/files/list.ts
625
625
  import { createHash } from "crypto";
626
626
  import { readFileSync as readFileSync3, readdirSync as readdirSync3, statSync } from "fs";
627
- import { basename as basename3, join as join3, relative as relative2, sep } from "path";
627
+ import { basename as basename4, join as join3, relative as relative2, sep } from "path";
628
+
629
+ // src/files/redact.ts
630
+ import { basename as basename3 } from "path";
631
+ var REDACTED = "<redacted>";
632
+ var SECRET_MAPS = /* @__PURE__ */ new Set(["env", "headers"]);
633
+ function redactValue(value) {
634
+ if (Array.isArray(value)) return value.map(redactValue);
635
+ if (value === null || typeof value !== "object") return value;
636
+ const out = {};
637
+ for (const [key, inner] of Object.entries(value)) {
638
+ const isSecretMap = SECRET_MAPS.has(key.toLowerCase()) && inner !== null && typeof inner === "object" && !Array.isArray(inner);
639
+ out[key] = isSecretMap ? Object.fromEntries(Object.keys(inner).map((name) => [name, REDACTED])) : redactValue(inner);
640
+ }
641
+ return out;
642
+ }
643
+ function redactMcpConfig(text) {
644
+ let parsed;
645
+ try {
646
+ parsed = JSON.parse(text);
647
+ } catch {
648
+ return `${JSON.stringify({ redacted: REDACTED, reason: "not valid JSON" }, null, 2)}
649
+ `;
650
+ }
651
+ return `${JSON.stringify(redactValue(parsed), null, 2)}
652
+ `;
653
+ }
654
+ var isMcpConfig = (path) => basename3(path) === ".mcp.json";
655
+ function servedBytes(path, raw) {
656
+ if (!isMcpConfig(path)) return raw;
657
+ return Buffer.from(redactMcpConfig(raw.toString("utf8")), "utf8");
658
+ }
659
+
660
+ // src/files/list.ts
628
661
  var FILE_LIST_LIMIT = 200;
629
662
  var sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
630
663
  function describe(path, full) {
631
- const bytes = readFileSync3(full);
664
+ const bytes = servedBytes(full, readFileSync3(full));
632
665
  return { path, bytes: bytes.length, sha256: sha256(bytes) };
633
666
  }
634
667
  function listSkillFiles(root, limit = FILE_LIST_LIMIT) {
635
668
  if (statSync(root).isFile()) {
636
- return { files: [describe(basename3(root), root)], truncated: false };
669
+ return { files: [describe(basename4(root), root)], truncated: false };
637
670
  }
638
671
  const files = [];
639
672
  const stack = [root];
@@ -803,7 +836,7 @@ function readSkillFile(entry, wanted, budget = READ_BUDGET_BYTES) {
803
836
  detail: `${size} bytes > ${budget}`
804
837
  };
805
838
  }
806
- const bytes = readFileSync4(resolved.target);
839
+ const bytes = servedBytes(resolved.target, readFileSync4(resolved.target));
807
840
  const { kind: kind2, mimeType } = classify(resolved.path, bytes);
808
841
  return {
809
842
  ok: true,
@@ -1130,7 +1163,9 @@ function createSkillTools(options) {
1130
1163
  const own = ownFile(entry);
1131
1164
  return {
1132
1165
  name: entry.name,
1133
- body: readBody(readFileSync6(entry.path, "utf8")),
1166
+ body: readBody(
1167
+ servedBytes(entry.path, readFileSync6(entry.path)).toString("utf8")
1168
+ ),
1134
1169
  files: bundle.files.filter((file) => file.path !== own),
1135
1170
  filesTruncated: bundle.truncated
1136
1171
  };
@@ -1150,7 +1185,7 @@ function createSkillTools(options) {
1150
1185
  // src/extension/entry.ts
1151
1186
  import { createHash as createHash2 } from "crypto";
1152
1187
  import { readFileSync as readFileSync7, readdirSync as readdirSync4, statSync as statSync6 } from "fs";
1153
- import { basename as basename4, dirname as dirname5, join as join5, relative as relative4, sep as sep3 } from "path";
1188
+ import { basename as basename5, dirname as dirname5, join as join5, relative as relative4, sep as sep3 } from "path";
1154
1189
  var MAX_RESOURCES = 512;
1155
1190
  var MAX_SKILL_BYTES = 16 * 1024 * 1024;
1156
1191
  var digests = /* @__PURE__ */ new Map();
@@ -1159,8 +1194,9 @@ function facts(path) {
1159
1194
  const key = `${stat.mtimeMs}:${stat.size}`;
1160
1195
  const cached = digests.get(path);
1161
1196
  if (cached?.key === key) return cached;
1162
- const digest = `sha256:${createHash2("sha256").update(readFileSync7(path)).digest("hex")}`;
1163
- const fresh = { key, digest, size: stat.size };
1197
+ const bytes = servedBytes(path, readFileSync7(path));
1198
+ const digest = `sha256:${createHash2("sha256").update(bytes).digest("hex")}`;
1199
+ const fresh = { key, digest, size: bytes.length };
1164
1200
  digests.set(path, fresh);
1165
1201
  return fresh;
1166
1202
  }
@@ -1180,11 +1216,11 @@ function filesUnder(root) {
1180
1216
  return found.sort();
1181
1217
  }
1182
1218
  function extensionEntry(entry) {
1183
- if (kindOf(entry) !== "skill" || basename4(entry.path) !== "SKILL.md")
1219
+ if (kindOf(entry) !== "skill" || basename5(entry.path) !== "SKILL.md")
1184
1220
  return null;
1185
1221
  const frontmatter = frontmatterObject(readFileSync7(entry.path, "utf8"));
1186
1222
  const root = dirname5(entry.path);
1187
- if (specProblems(frontmatter, basename4(root)).length > 0) return null;
1223
+ if (specProblems(frontmatter, basename5(root)).length > 0) return null;
1188
1224
  if (frontmatter?.["name"] !== entry.slug) return null;
1189
1225
  const files = filesUnder(root);
1190
1226
  if (files === null) return null;
@@ -1554,7 +1590,7 @@ function registerReadTool(server, tools) {
1554
1590
 
1555
1591
  // src/mcp.ts
1556
1592
  var SERVER_NAME = "pmcp";
1557
- var SERVER_VERSION = "0.8.0";
1593
+ var SERVER_VERSION = "0.8.2";
1558
1594
  function registerSkillTools(server, tools) {
1559
1595
  registerCatalogTool(server, tools);
1560
1596
  registerFindTool(server, tools);
@@ -1625,4 +1661,4 @@ export {
1625
1661
  registerSkillTools,
1626
1662
  createSkillServer
1627
1663
  };
1628
- //# sourceMappingURL=chunk-ZDV75HZN.js.map
1664
+ //# sourceMappingURL=chunk-UGRHZHHC.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createSkillServer
3
- } from "./chunk-ZDV75HZN.js";
3
+ } from "./chunk-UGRHZHHC.js";
4
4
 
5
5
  // src/stdio.ts
6
6
  import { serveStdio } from "@modelcontextprotocol/server/stdio";
@@ -38,4 +38,4 @@ export {
38
38
  serve,
39
39
  main
40
40
  };
41
- //# sourceMappingURL=chunk-LUBYOET7.js.map
41
+ //# sourceMappingURL=chunk-UW7HTILA.js.map
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  loadEmbedder,
4
4
  openEntryVectors
5
- } from "./chunk-WSTCBVXW.js";
5
+ } from "./chunk-3JNWCUHA.js";
6
6
  import {
7
7
  ConfigError,
8
8
  ProjectError,
@@ -17,15 +17,15 @@ import {
17
17
  splitFrontmatter,
18
18
  validateCatalog,
19
19
  validateSkillDir
20
- } from "./chunk-M6LBYJDS.js";
21
- import "./chunk-OPDVUHTK.js";
20
+ } from "./chunk-O2P5AIHX.js";
21
+ import "./chunk-IRQTGYHA.js";
22
22
  import {
23
23
  serve
24
- } from "./chunk-LUBYOET7.js";
24
+ } from "./chunk-UW7HTILA.js";
25
25
  import {
26
26
  kindOf,
27
27
  readCatalog
28
- } from "./chunk-ZDV75HZN.js";
28
+ } from "./chunk-UGRHZHHC.js";
29
29
 
30
30
  // src/cli/command.ts
31
31
  var ArgumentError = class extends Error {
@@ -2347,7 +2347,7 @@ function dispatch(argv, options = {}) {
2347
2347
  }
2348
2348
  return run(command, rest, { ui, env, cwd });
2349
2349
  }
2350
- var VERSION = "0.8.0";
2350
+ var VERSION = "0.8.2";
2351
2351
  async function run(command, argv, context) {
2352
2352
  try {
2353
2353
  return await command.run({
package/dist/http.d.ts CHANGED
@@ -37,6 +37,19 @@ interface LoadEmbedderOptions {
37
37
  */
38
38
  declare function loadEmbedder(modelId?: string, options?: LoadEmbedderOptions): Promise<Embedder | null>;
39
39
 
40
+ /**
41
+ * Checks a hosted skills server makes before a request reaches MCP: which browser origins
42
+ * and Host names may call it, how large a request may be, and how often one caller may call.
43
+ * Requests without an Origin header are not from a browser page and are not origin-checked.
44
+ */
45
+ declare const DEFAULT_MAX_REQUEST_BYTES: number;
46
+ interface RateLimitOptions {
47
+ /** Requests one caller may make in a minute. */
48
+ readonly perMinute: number;
49
+ /** Injected by tests. */
50
+ readonly now?: () => number;
51
+ }
52
+
40
53
  /**
41
54
  * The catalog over streamable HTTP, for a hosted skills server.
42
55
  *
@@ -75,6 +88,16 @@ interface SkillHttpOptions extends SkillServerOptions {
75
88
  * here, so "private" unless the host knows every caller sees the same list.
76
89
  */
77
90
  readonly cacheScope?: "public" | "private";
91
+ /** Browser origins, besides the server's own, whose pages may call; others get 403. "*" admits any. */
92
+ readonly allowedOrigins?: readonly string[];
93
+ /** The server's own public origins when a proxy ends TLS, e.g. "https://skills.example.com". */
94
+ readonly publicOrigins?: readonly string[];
95
+ /** Host header values the server answers to; unset means any. */
96
+ readonly allowedHosts?: readonly string[];
97
+ /** Largest request body accepted; larger ones get 413. */
98
+ readonly maxRequestBytes?: number;
99
+ /** Per-caller request ceiling; unset means the host limits rate itself. */
100
+ readonly rateLimit?: RateLimitOptions;
78
101
  }
79
102
  declare const DEFAULT_TIERS: readonly string[];
80
103
  declare function visibleTo(entries: readonly SkillEntry[], tiers: readonly string[]): SkillEntry[];
@@ -87,4 +110,4 @@ interface SkillHttpHandler {
87
110
  }
88
111
  declare function createSkillHttpHandler(options: SkillHttpOptions): SkillHttpHandler;
89
112
 
90
- export { DEFAULT_MODEL_ID, DEFAULT_TIERS, Embedder, type SkillCaller, type SkillHttpHandler, type SkillHttpOptions, type SkillHttpSemanticOptions, createSkillHttpHandler, loadEmbedder, visibleTo };
113
+ export { DEFAULT_MAX_REQUEST_BYTES, DEFAULT_MODEL_ID, DEFAULT_TIERS, Embedder, type RateLimitOptions, type SkillCaller, type SkillHttpHandler, type SkillHttpOptions, type SkillHttpSemanticOptions, createSkillHttpHandler, loadEmbedder, visibleTo };
package/dist/http.js CHANGED
@@ -2,17 +2,124 @@ import {
2
2
  DEFAULT_MODEL_ID,
3
3
  encodeEntries,
4
4
  loadEmbedder
5
- } from "./chunk-WSTCBVXW.js";
6
- import "./chunk-OPDVUHTK.js";
5
+ } from "./chunk-3JNWCUHA.js";
6
+ import "./chunk-IRQTGYHA.js";
7
7
  import {
8
8
  createSkillServer,
9
9
  readCatalog
10
- } from "./chunk-ZDV75HZN.js";
10
+ } from "./chunk-UGRHZHHC.js";
11
11
 
12
12
  // src/http.ts
13
13
  import {
14
14
  createMcpHandler
15
15
  } from "@modelcontextprotocol/server";
16
+
17
+ // src/http-guard.ts
18
+ var DEFAULT_MAX_REQUEST_BYTES = 1024 * 1024;
19
+ var RATE_WINDOW_MS = 6e4;
20
+ var CORS_ALLOW_HEADERS = "authorization, content-type, accept, mcp-session-id, mcp-protocol-version, last-event-id";
21
+ var json = (status, body, headers = {}) => new Response(JSON.stringify(body), {
22
+ status,
23
+ headers: { "content-type": "application/json", ...headers }
24
+ });
25
+ function checkOrigin(request, allowedOrigins = [], publicOrigins = []) {
26
+ const origin = request.headers.get("origin");
27
+ if (origin === null) return { kind: "none" };
28
+ if (origin === new URL(request.url).origin || publicOrigins.includes(origin)) {
29
+ return { kind: "same" };
30
+ }
31
+ if (allowedOrigins.includes(origin) || allowedOrigins.includes("*")) {
32
+ return { kind: "allowed", origin };
33
+ }
34
+ return {
35
+ kind: "refused",
36
+ response: json(403, { error: "origin_not_allowed" })
37
+ };
38
+ }
39
+ function checkHost(request, allowedHosts) {
40
+ if (!allowedHosts) return void 0;
41
+ const host = request.headers.get("host") ?? new URL(request.url).host;
42
+ return allowedHosts.includes(host) ? void 0 : json(403, { error: "host_not_allowed" });
43
+ }
44
+ function corsHeaders(origin) {
45
+ return {
46
+ "access-control-allow-origin": origin,
47
+ "access-control-expose-headers": "mcp-session-id, www-authenticate",
48
+ vary: "Origin"
49
+ };
50
+ }
51
+ function preflight(origin) {
52
+ return new Response(null, {
53
+ status: 204,
54
+ headers: {
55
+ ...corsHeaders(origin),
56
+ "access-control-allow-methods": "GET, POST, DELETE, OPTIONS",
57
+ "access-control-allow-headers": CORS_ALLOW_HEADERS,
58
+ "access-control-max-age": "600"
59
+ }
60
+ });
61
+ }
62
+ function withHeaders(response, headers) {
63
+ const merged = new Headers(response.headers);
64
+ for (const [name, value] of Object.entries(headers)) merged.set(name, value);
65
+ return new Response(response.body, {
66
+ status: response.status,
67
+ statusText: response.statusText,
68
+ headers: merged
69
+ });
70
+ }
71
+ var tooLarge = (max) => json(413, { error: "request_too_large", limit: max });
72
+ async function capBody(request, max) {
73
+ const declared = Number(request.headers.get("content-length") ?? NaN);
74
+ if (Number.isFinite(declared) && declared > max) return tooLarge(max);
75
+ if (!request.body) return request;
76
+ const reader = request.body.getReader();
77
+ const chunks = [];
78
+ let total = 0;
79
+ for (; ; ) {
80
+ const { done, value } = await reader.read();
81
+ if (done) break;
82
+ total += value.byteLength;
83
+ if (total > max) {
84
+ await reader.cancel();
85
+ return tooLarge(max);
86
+ }
87
+ chunks.push(value);
88
+ }
89
+ const body = new Uint8Array(total);
90
+ let offset = 0;
91
+ for (const chunk of chunks) {
92
+ body.set(chunk, offset);
93
+ offset += chunk.byteLength;
94
+ }
95
+ return new Request(request.url, {
96
+ method: request.method,
97
+ headers: request.headers,
98
+ body
99
+ });
100
+ }
101
+ function createRateLimiter(options) {
102
+ const now = options.now ?? Date.now;
103
+ const windows = /* @__PURE__ */ new Map();
104
+ return (callerId) => {
105
+ const t = now();
106
+ const window = windows.get(callerId);
107
+ if (!window || t - window.start >= RATE_WINDOW_MS) {
108
+ windows.set(callerId, { start: t, count: 1 });
109
+ return void 0;
110
+ }
111
+ window.count += 1;
112
+ if (window.count <= options.perMinute) return void 0;
113
+ const retry = Math.ceil((window.start + RATE_WINDOW_MS - t) / 1e3);
114
+ return json(
115
+ 429,
116
+ { error: "rate_limited" },
117
+ { "retry-after": String(Math.max(retry, 1)) }
118
+ );
119
+ };
120
+ }
121
+
122
+ // src/http.ts
16
123
  var DEFAULT_TIERS = ["open", "free"];
17
124
  function visibleTo(entries, tiers) {
18
125
  const allowed = new Set(tiers);
@@ -57,26 +164,47 @@ function createSkillHttpHandler(options) {
57
164
  handlers.set(key, handler);
58
165
  return handler;
59
166
  };
60
- return {
61
- async fetch(request) {
62
- const caller = await options.authorize(request);
63
- if (!caller) {
64
- return new Response(JSON.stringify({ error: "unauthorized" }), {
65
- status: 401,
66
- headers: {
67
- "content-type": "application/json",
68
- "www-authenticate": options.challenge ?? "Bearer"
69
- }
70
- });
71
- }
72
- return handlerFor(tiersFor(caller)).fetch(request, {
73
- authInfo: {
74
- token: "",
75
- clientId: caller.id,
76
- scopes: [],
77
- extra: { ...caller.claims }
167
+ const limit = options.rateLimit ? createRateLimiter(options.rateLimit) : void 0;
168
+ const serve = async (request) => {
169
+ const capped = await capBody(
170
+ request,
171
+ options.maxRequestBytes ?? DEFAULT_MAX_REQUEST_BYTES
172
+ );
173
+ if (capped instanceof Response) return capped;
174
+ const caller = await options.authorize(capped);
175
+ if (!caller) {
176
+ return new Response(JSON.stringify({ error: "unauthorized" }), {
177
+ status: 401,
178
+ headers: {
179
+ "content-type": "application/json",
180
+ "www-authenticate": options.challenge ?? "Bearer"
78
181
  }
79
182
  });
183
+ }
184
+ const limited = limit?.(caller.id);
185
+ if (limited) return limited;
186
+ return handlerFor(tiersFor(caller)).fetch(capped, {
187
+ authInfo: {
188
+ token: "",
189
+ clientId: caller.id,
190
+ scopes: [],
191
+ extra: { ...caller.claims }
192
+ }
193
+ });
194
+ };
195
+ return {
196
+ async fetch(request) {
197
+ const wrongHost = checkHost(request, options.allowedHosts);
198
+ if (wrongHost) return wrongHost;
199
+ const origin = checkOrigin(
200
+ request,
201
+ options.allowedOrigins,
202
+ options.publicOrigins
203
+ );
204
+ if (origin.kind === "refused") return origin.response;
205
+ if (origin.kind !== "allowed") return serve(request);
206
+ if (request.method === "OPTIONS") return preflight(origin.origin);
207
+ return withHeaders(await serve(request), corsHeaders(origin.origin));
80
208
  },
81
209
  reload() {
82
210
  all = load(options);
@@ -88,6 +216,7 @@ function createSkillHttpHandler(options) {
88
216
  };
89
217
  }
90
218
  export {
219
+ DEFAULT_MAX_REQUEST_BYTES,
91
220
  DEFAULT_MODEL_ID,
92
221
  DEFAULT_TIERS,
93
222
  createSkillHttpHandler,
package/dist/index.d.ts CHANGED
@@ -304,7 +304,7 @@ declare function resolveSkillFileUri(uri: string, entries: readonly SkillEntry[]
304
304
 
305
305
  /** Reported to the host on connect. */
306
306
  declare const SERVER_NAME = "pmcp";
307
- declare const SERVER_VERSION = "0.8.0";
307
+ declare const SERVER_VERSION = "0.8.2";
308
308
  /**
309
309
  * Registers the skill_* tools on a server.
310
310
  *
package/dist/index.js CHANGED
@@ -14,11 +14,11 @@ import {
14
14
  readSpec,
15
15
  validateCatalog,
16
16
  validateSkillDir
17
- } from "./chunk-M6LBYJDS.js";
17
+ } from "./chunk-O2P5AIHX.js";
18
18
  import {
19
19
  DEFAULT_INTENT_CACHE_LIMIT,
20
20
  openIntentCache
21
- } from "./chunk-OPDVUHTK.js";
21
+ } from "./chunk-IRQTGYHA.js";
22
22
  import {
23
23
  ASSET_KINDS,
24
24
  CATALOG_PAGE_MAX,
@@ -70,7 +70,7 @@ import {
70
70
  skillFileUri,
71
71
  specProblems,
72
72
  withoutBody
73
- } from "./chunk-ZDV75HZN.js";
73
+ } from "./chunk-UGRHZHHC.js";
74
74
  export {
75
75
  ASSET_KINDS,
76
76
  CATALOG_PAGE_MAX,
package/dist/stdio.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  main,
4
4
  resolveRoots,
5
5
  serve
6
- } from "./chunk-LUBYOET7.js";
7
- import "./chunk-ZDV75HZN.js";
6
+ } from "./chunk-UW7HTILA.js";
7
+ import "./chunk-UGRHZHHC.js";
8
8
  export {
9
9
  main,
10
10
  resolveRoots,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modootoday/pmcp",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
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.",