pi-mcp-extension 1.10.0 → 1.10.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
@@ -3,6 +3,7 @@
3
3
  [![npm version](https://img.shields.io/npm/v/pi-mcp-extension.svg)](https://www.npmjs.com/package/pi-mcp-extension)
4
4
  [![license](https://img.shields.io/npm/l/pi-mcp-extension.svg)](https://github.com/irahardianto/pi-mcp-extension/blob/main/LICENSE)
5
5
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/irahardianto/pi-mcp-extension/badge)](https://scorecard.dev/viewer/?uri=github.com/irahardianto/pi-mcp-extension)
6
+ [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/15659/badge)](https://www.bestpractices.dev/projects/15659)
6
7
 
7
8
  **Connect [Pi](https://pi.dev) to any MCP server.** Supabase, DeepSource, Playwright, Context7, filesystem, databases — if it speaks [MCP](https://modelcontextprotocol.io), Pi can use it.
8
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mcp-extension",
3
- "version": "1.10.0",
3
+ "version": "1.10.2",
4
4
  "description": "MCP (Model Context Protocol) client extension for the Pi coding agent — connect Pi to any MCP server",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@modelcontextprotocol/sdk": "^1.32.1",
37
- "zod": "^4.6.5"
37
+ "zod": "^4.6.7"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "@earendil-works/pi-ai": "*",
package/src/config.ts CHANGED
@@ -16,6 +16,7 @@ import { join } from "node:path";
16
16
  import { z } from "zod";
17
17
  import { callbackServerConfigFromRedirectUrl } from "./callback-server.js";
18
18
  import { McpError } from "./errors.js";
19
+ import { stripJsonComments } from "./jsonc.js";
19
20
 
20
21
  // ─── Zod Schemas ──────────────────────────────────────────────────────────────
21
22
 
@@ -227,80 +228,6 @@ export interface LoadConfigOptions {
227
228
 
228
229
  // ─── Loader ───────────────────────────────────────────────────────────────────
229
230
 
230
- /**
231
- * Strip `//` line comments and `/* ... *\/` block comments outside of double-quoted
232
- * JSON strings in a single character-by-character pass.
233
- *
234
- * Preserves `\n` and `\r` characters so line numbers in `JSON.parse` error messages
235
- * remain accurate. Unterminated `/*` block comments are left intact so `JSON.parse`
236
- * fails with a syntax error rather than silently truncating the document.
237
- */
238
- export function stripJsonComments(text: string): string {
239
- let out = "";
240
- let inString = false;
241
- let i = 0;
242
-
243
- while (i < text.length) {
244
- const ch = text[i]!;
245
-
246
- if (inString) {
247
- if (ch === "\\") {
248
- out += ch;
249
- if (i + 1 < text.length) {
250
- out += text[i + 1]!;
251
- i += 2;
252
- } else {
253
- i += 1;
254
- }
255
- continue;
256
- }
257
- if (ch === '"') {
258
- inString = false;
259
- }
260
- out += ch;
261
- i += 1;
262
- continue;
263
- }
264
-
265
- if (ch === '"') {
266
- inString = true;
267
- out += ch;
268
- i += 1;
269
- continue;
270
- }
271
-
272
- if (ch === "/" && text[i + 1] === "/") {
273
- i += 2;
274
- while (i < text.length && text[i] !== "\n") {
275
- if (text[i] === "\r" && text[i + 1] === "\n") break;
276
- i += 1;
277
- }
278
- continue;
279
- }
280
-
281
- if (ch === "/" && text[i + 1] === "*") {
282
- const closeIdx = text.indexOf("*/", i + 2);
283
- if (closeIdx === -1) {
284
- // WHY: Leave unterminated block comments intact so JSON.parse fails with a
285
- // SyntaxError rather than silently truncating the rest of the file.
286
- out += text.slice(i);
287
- break;
288
- }
289
- for (let j = i; j < closeIdx + 2; j += 1) {
290
- const commentCh = text[j]!;
291
- out += commentCh === "\n" || commentCh === "\r" ? commentCh : " ";
292
- }
293
- i = closeIdx + 2;
294
- continue;
295
- }
296
-
297
- out += ch;
298
- i += 1;
299
- }
300
-
301
- return out;
302
- }
303
-
304
231
  async function readJsonFile(path: string): Promise<unknown> {
305
232
  let text: string;
306
233
  try {
package/src/jsonc.ts ADDED
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Pure JSON-with-comments (JSONC) text utilities. This module has no `src/`
3
+ * dependencies of its own and is safe for `scripts/` and `tests/` to import: it
4
+ * does not load `zod`, the MCP error types, or the OAuth/callback modules, so
5
+ * non-runtime consumers stay free of runtime MCP imports.
6
+ *
7
+ * The only behaviour this module performs is `stripJsonComments` — a single-pass
8
+ * character scan that removes `//` line comments and `/* ... *\/` block comments
9
+ * while leaving the rest of the document byte-equivalent. Comments inside
10
+ * double-quoted strings are preserved verbatim, so a JSON value such as
11
+ * `"https://example.com/foo"` is never accidentally truncated. Trailing commas
12
+ * (the other common JSONC extension) are **not** handled here — none of the
13
+ * files this helper feeds today uses them, and an explicit `JSON.parse` failure
14
+ * on a misplaced comma is a more honest signal than silent acceptance.
15
+ */
16
+
17
+ /**
18
+ * Strip `//` line comments and `/* ... *\/` block comments outside of double-quoted
19
+ * JSON strings in a single character-by-character pass.
20
+ *
21
+ * Preserves `\n` and `\r` characters so line numbers in `JSON.parse` error messages
22
+ * remain accurate. Unterminated `/*` block comments are left intact so `JSON.parse`
23
+ * fails with a syntax error rather than silently truncating the document.
24
+ *
25
+ * WHY: this module exists so `scripts/ruleset-audit.ts` and `tests/repo-policy.test.ts`
26
+ * can read `.github/rulesets/*.json` with the same JSONC tolerance the runtime uses
27
+ * for `mcp.json`. A future maintainer can add `// WHY: …` style comments to the
28
+ * ruleset files (mirroring the convention used throughout `src/`) without breaking the
29
+ * ruleset audit or policy tests.
30
+ */
31
+ export function stripJsonComments(text: string): string {
32
+ let out = "";
33
+ let inString = false;
34
+ let i = 0;
35
+
36
+ while (i < text.length) {
37
+ const ch = text[i]!;
38
+
39
+ if (inString) {
40
+ if (ch === "\\") {
41
+ out += ch;
42
+ if (i + 1 < text.length) {
43
+ out += text[i + 1]!;
44
+ i += 2;
45
+ } else {
46
+ i += 1;
47
+ }
48
+ continue;
49
+ }
50
+ if (ch === '"') {
51
+ inString = false;
52
+ }
53
+ out += ch;
54
+ i += 1;
55
+ continue;
56
+ }
57
+
58
+ if (ch === '"') {
59
+ inString = true;
60
+ out += ch;
61
+ i += 1;
62
+ continue;
63
+ }
64
+
65
+ if (ch === "/" && text[i + 1] === "/") {
66
+ i += 2;
67
+ while (i < text.length && text[i] !== "\n") {
68
+ if (text[i] === "\r" && text[i + 1] === "\n") break;
69
+ i += 1;
70
+ }
71
+ continue;
72
+ }
73
+
74
+ if (ch === "/" && text[i + 1] === "*") {
75
+ const closeIdx = text.indexOf("*/", i + 2);
76
+ if (closeIdx === -1) {
77
+ // WHY: Leave unterminated block comments intact so JSON.parse fails with a
78
+ // SyntaxError rather than silently truncating the rest of the file.
79
+ out += text.slice(i);
80
+ break;
81
+ }
82
+ for (let j = i; j < closeIdx + 2; j += 1) {
83
+ const commentCh = text[j]!;
84
+ out += commentCh === "\n" || commentCh === "\r" ? commentCh : " ";
85
+ }
86
+ i = closeIdx + 2;
87
+ continue;
88
+ }
89
+
90
+ out += ch;
91
+ i += 1;
92
+ }
93
+
94
+ return out;
95
+ }