@cyanheads/mcp-ts-core 0.10.8 → 0.10.10
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/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/README.md +1 -10
- package/biome.json +1 -1
- package/changelog/0.10.x/0.10.10.md +39 -0
- package/changelog/0.10.x/0.10.9.md +16 -0
- package/dist/utils/parsing/yamlParser.d.ts +1 -1
- package/dist/utils/parsing/yamlParser.d.ts.map +1 -1
- package/dist/utils/parsing/yamlParser.js +5 -2
- package/dist/utils/parsing/yamlParser.js.map +1 -1
- package/package.json +16 -19
- package/scripts/check-dependency-specifiers.ts +190 -0
- package/scripts/devcheck.ts +21 -3
- package/scripts/lint-packaging.ts +152 -0
- package/skills/api-utils/references/parsing.md +1 -1
- package/skills/polish-docs-meta/SKILL.md +7 -5
- package/skills/polish-docs-meta/references/readme.md +0 -14
- package/templates/devcheck.config.json +3 -0
- package/dist/logs/combined.log +0 -8
- package/dist/logs/error.log +0 -4
- package/dist/logs/interactions.log +0 -0
package/AGENTS.md
CHANGED
package/CLAUDE.md
CHANGED
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
<div align="center">
|
|
7
7
|
|
|
8
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/changelog.mdx)
|
|
9
9
|
|
|
10
10
|
[](https://modelcontextprotocol.io/) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
@@ -301,15 +301,6 @@ bun run lint:mcp # validate MCP definitions against spec
|
|
|
301
301
|
bun run test:all # vitest: unit + Workers pool + integration
|
|
302
302
|
```
|
|
303
303
|
|
|
304
|
-
## Contributing
|
|
305
|
-
|
|
306
|
-
Issues and pull requests welcome. Run checks before submitting:
|
|
307
|
-
|
|
308
|
-
```bash
|
|
309
|
-
bun run devcheck
|
|
310
|
-
bun run test:all
|
|
311
|
-
```
|
|
312
|
-
|
|
313
304
|
## License
|
|
314
305
|
|
|
315
306
|
Apache 2.0 — see [LICENSE](./LICENSE).
|
package/biome.json
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "js-yaml v4 → v5 migration: YamlParser uses YAML11_SCHEMA to preserve YAML 1.1 load semantics after v5 defaults to CORE_SCHEMA, namespace import replaces the removed default export, @types/js-yaml dropped (v5 self-types). Breaking: the js-yaml peer dependency moves ^4 → ^5. Plus biome 2.5.1 and a dependency refresh."
|
|
3
|
+
breaking: true
|
|
4
|
+
security: false
|
|
5
|
+
agent-notes: |
|
|
6
|
+
The js-yaml peer dependency moved ^4 → ^5 (major). A consumer server that
|
|
7
|
+
installs js-yaml directly to use the Tier-3 YamlParser must bump it to ^5. No
|
|
8
|
+
consumer code change is required — the framework now loads with YAML11_SCHEMA,
|
|
9
|
+
preserving the prior YAML 1.1 semantics, so parsing behavior is unchanged.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# 0.10.10 — 2026-06-30
|
|
13
|
+
|
|
14
|
+
## Changed
|
|
15
|
+
|
|
16
|
+
- **`YamlParser.parse`** — js-yaml v5 removed `DEFAULT_SCHEMA` and defaults `load()` to the stricter YAML 1.2 `CORE_SCHEMA`; the parser now passes `YAML11_SCHEMA` to preserve the prior YAML 1.1 load semantics (`yes`/`no`/`on`/`off` → boolean, `!!timestamp` → `Date`) that LLM-generated YAML relies on.
|
|
17
|
+
- **`scripts/fetch-openapi-spec.ts`** — switched to `import * as yaml from 'js-yaml'`; v5 dropped the default export.
|
|
18
|
+
- **`biome.json`** — `$schema` `2.5.0` → `2.5.1`, tracking the `@biomejs/biome` bump.
|
|
19
|
+
|
|
20
|
+
## Removed
|
|
21
|
+
|
|
22
|
+
- **`@types/js-yaml`** — js-yaml v5 ships its own type declarations, so the separate `@types` package is gone.
|
|
23
|
+
- **`js-yaml` resolution pin** — dropped the `resolutions`/`overrides` `js-yaml: ^4.2.0` entry; the v5 upgrade resolves the dependency tree without it.
|
|
24
|
+
|
|
25
|
+
### Dependency bumps
|
|
26
|
+
|
|
27
|
+
- `js-yaml` `^4.2.0` → `^5.2.0` (peer `^4.1.1` → `^5.2.0`)
|
|
28
|
+
- `@hono/node-server` `^2.0.5` → `^2.0.6`
|
|
29
|
+
- `hono` `^4.12.26` → `^4.12.27`
|
|
30
|
+
- `@biomejs/biome` `2.5.0` → `2.5.1`
|
|
31
|
+
- `@cloudflare/vitest-pool-workers` `^0.16.18` → `^0.16.20`
|
|
32
|
+
- `@cloudflare/workers-types` `4.20260620.1` → `4.20260630.1`
|
|
33
|
+
- `@supabase/supabase-js` `^2.108.2` → `^2.109.0`
|
|
34
|
+
- `@types/node` `26.0.0` → `26.0.1`
|
|
35
|
+
- `defuddle` `^0.19.0` → `^0.19.1`
|
|
36
|
+
- `node-cron` `^4.4.1` → `^4.5.0`
|
|
37
|
+
- `openai` `^6.44.0` → `^6.45.0`
|
|
38
|
+
- `repomix` `^1.15.0` → `^1.16.0`
|
|
39
|
+
- `vite` `8.0.16` → `8.1.0`
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "devcheck gains two static guards: floating dependency specifiers (latest/*/dist-tags) in package.json + bun.lock workspaces, and plugin marketplace manifest correctness (descriptions + scoped/unscoped identity)"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.10.9 — 2026-06-20
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`check-dependency-specifiers` devcheck step** — new `scripts/check-dependency-specifiers.ts`, registered in `ALL_CHECKS` (`Dependency Specifiers`, flag `--no-dep-specifiers`), hard-fails on floating specifiers in the manifests we control: `package.json`'s four dependency sections and `bun.lock`'s `workspaces` specifier maps (never the `packages` section, which records third-party nested declarations that legitimately float). `latest` fails in every section; `*` and the pre-release dist-tags `next`/`beta`/`canary`/`rc` fail in `dependencies`/`devDependencies` but are allowed in `peerDependencies`/`optionalDependencies` ("any host version"). Catches the case where `bun update --latest` writes a literal `latest` dist-tag into the lock's workspace map — correct at write time, so it clears every other gate, then lets the next `bun install` re-resolve the dep past the `package.json` range and past any intentional hold. Static and local (no network, no git), so it runs in the default and `--fast` passes; ships to consumers via `package.json` `files:`. ([#246](https://github.com/cyanheads/mcp-ts-core/issues/246))
|
|
12
|
+
- **Plugin marketplace manifest checks in `lint:packaging`** — `scripts/lint-packaging.ts` gains check 10, validating `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, and `.codex-plugin/mcp.json` when present: non-empty descriptions, display fields (`name`, server key, `interface.displayName`) carrying the **unscoped** machine name, and the `npx -y` install arg carrying the **full** `package.json` name. An unscoped install arg for a scoped package is a guaranteed install 404 — the high-value catch. Gated by a new `devcheck.config.json` `packaging.pluginManifests` flag (default on; surfaced in `templates/devcheck.config.json`); each manifest is skipped cleanly when absent, so HTTP-only and non-plugin consumers are unaffected. The `devcheck` "Packaging" gate, previously skipped unless `manifest.json` existed, now also runs when any plugin manifest is present. `skills/polish-docs-meta` step 10 documents the automated subset. ([#240](https://github.com/cyanheads/mcp-ts-core/issues/240))
|
|
13
|
+
|
|
14
|
+
## Changed
|
|
15
|
+
|
|
16
|
+
- **Dependency:** `@cloudflare/workers-types` 4.20260619.1 → 4.20260620.1
|
|
@@ -13,7 +13,7 @@ export declare class YamlParser {
|
|
|
13
13
|
* This method is async because `js-yaml` is loaded lazily on first call.
|
|
14
14
|
* If the input begins with a `<think>...</think>` block, that block is stripped
|
|
15
15
|
* and its content logged before parsing the remainder. Uses `js-yaml`'s
|
|
16
|
-
* `
|
|
16
|
+
* `YAML11_SCHEMA` (YAML 1.1 load semantics) for parsing.
|
|
17
17
|
*
|
|
18
18
|
* @template T - The expected type of the parsed result. Defaults to `unknown`.
|
|
19
19
|
* @param yamlString - The YAML string to parse. May be prefixed with a `<think>` block.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yamlParser.d.ts","sourceRoot":"","sources":["../../../src/utils/parsing/yamlParser.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,cAAc,EAAyB,MAAM,oCAAoC,CAAC;AAQhG;;;;;;GAMG;AACH,qBAAa,UAAU;IACrB;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,KAAK,CAAC,CAAC,GAAG,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"yamlParser.d.ts","sourceRoot":"","sources":["../../../src/utils/parsing/yamlParser.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,cAAc,EAAyB,MAAM,oCAAoC,CAAC;AAQhG;;;;;;GAMG;AACH,qBAAa,UAAU;IACrB;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,KAAK,CAAC,CAAC,GAAG,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;CA4DnF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,UAAU,YAAmB,CAAC"}
|
|
@@ -27,7 +27,7 @@ export class YamlParser {
|
|
|
27
27
|
* This method is async because `js-yaml` is loaded lazily on first call.
|
|
28
28
|
* If the input begins with a `<think>...</think>` block, that block is stripped
|
|
29
29
|
* and its content logged before parsing the remainder. Uses `js-yaml`'s
|
|
30
|
-
* `
|
|
30
|
+
* `YAML11_SCHEMA` (YAML 1.1 load semantics) for parsing.
|
|
31
31
|
*
|
|
32
32
|
* @template T - The expected type of the parsed result. Defaults to `unknown`.
|
|
33
33
|
* @param yamlString - The YAML string to parse. May be prefixed with a `<think>` block.
|
|
@@ -75,7 +75,10 @@ export class YamlParser {
|
|
|
75
75
|
}
|
|
76
76
|
try {
|
|
77
77
|
const yaml = await getYaml();
|
|
78
|
-
|
|
78
|
+
// js-yaml v5 removed DEFAULT_SCHEMA and defaults load() to the stricter 1.2
|
|
79
|
+
// CORE_SCHEMA; YAML11_SCHEMA preserves the prior 1.1 load semantics
|
|
80
|
+
// (yes/no/on/off → boolean, !!timestamp → Date) that LLM YAML output relies on.
|
|
81
|
+
return yaml.load(stringToParse, { schema: yaml.YAML11_SCHEMA });
|
|
79
82
|
}
|
|
80
83
|
catch (e) {
|
|
81
84
|
const error = e instanceof Error ? e : new Error(String(e));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yamlParser.js","sourceRoot":"","sources":["../../../src/utils/parsing/yamlParser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAuB,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAChG,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,OAAO,GAAG,UAAU,CACxB,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,EACvB,wDAAwD,CACzD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,OAAO,UAAU;IACrB;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,KAAK,CAAC,KAAK,CAAc,UAAkB,EAAE,OAAwB;QACnE,IAAI,aAAa,GAAG,UAAU,CAAC;QAC/B,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAEhD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;YAC5C,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAEpC,MAAM,UAAU,GACd,OAAO;gBACP,qBAAqB,CAAC,oBAAoB,CAAC;oBACzC,SAAS,EAAE,uBAAuB;iBACnC,CAAC,CAAC;YACL,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,CAAC,KAAK,CAAC,wCAAwC,EAAE;oBACrD,GAAG,UAAU;oBACb,YAAY;iBACb,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE,UAAU,CAAC,CAAC;YAChE,CAAC;YACD,aAAa,GAAG,YAAY,CAAC;QAC/B,CAAC;QAED,aAAa,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC;QAErC,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,eAAe,CACnB,iEAAiE,EACjE,OAAO,CACR,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"yamlParser.js","sourceRoot":"","sources":["../../../src/utils/parsing/yamlParser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAuB,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAChG,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,OAAO,GAAG,UAAU,CACxB,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,EACvB,wDAAwD,CACzD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,OAAO,UAAU;IACrB;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,KAAK,CAAC,KAAK,CAAc,UAAkB,EAAE,OAAwB;QACnE,IAAI,aAAa,GAAG,UAAU,CAAC;QAC/B,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAEhD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;YAC5C,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAEpC,MAAM,UAAU,GACd,OAAO;gBACP,qBAAqB,CAAC,oBAAoB,CAAC;oBACzC,SAAS,EAAE,uBAAuB;iBACnC,CAAC,CAAC;YACL,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,CAAC,KAAK,CAAC,wCAAwC,EAAE;oBACrD,GAAG,UAAU;oBACb,YAAY;iBACb,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE,UAAU,CAAC,CAAC;YAChE,CAAC;YACD,aAAa,GAAG,YAAY,CAAC;QAC/B,CAAC;QAED,aAAa,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC;QAErC,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,eAAe,CACnB,iEAAiE,EACjE,OAAO,CACR,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAC;YAC7B,4EAA4E;YAC5E,oEAAoE;YACpE,gFAAgF;YAChF,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,CAAM,CAAC;QACvE,CAAC;QAAC,OAAO,CAAU,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5D,MAAM,eAAe,GACnB,OAAO;gBACP,qBAAqB,CAAC,oBAAoB,CAAC;oBACzC,SAAS,EAAE,uBAAuB;iBACnC,CAAC,CAAC;YACL,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE;gBAC5C,GAAG,eAAe;gBAClB,YAAY,EAAE,KAAK,CAAC,OAAO;gBAC3B,gBAAgB,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC;aAClD,CAAC,CAAC;YAEH,MAAM,eAAe,CAAC,yBAAyB,KAAK,CAAC,OAAO,EAAE,EAAE;gBAC9D,GAAG,OAAO;gBACV,qBAAqB,EACnB,aAAa,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7E,QAAQ,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC/D,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/mcp-ts-core",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.10",
|
|
4
4
|
"mcpName": "io.github.cyanheads/mcp-ts-core",
|
|
5
5
|
"description": "Agent-native TypeScript framework for building MCP servers. Declarative definitions with auth, multi-backend storage, OpenTelemetry, and first-class support for Bun/Node/Cloudflare Workers.",
|
|
6
6
|
"main": "dist/core/index.js",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"dist/",
|
|
11
11
|
"scripts/build-changelog.ts",
|
|
12
12
|
"scripts/build.ts",
|
|
13
|
+
"scripts/check-dependency-specifiers.ts",
|
|
13
14
|
"scripts/check-docs-sync.ts",
|
|
14
15
|
"scripts/check-framework-antipatterns.ts",
|
|
15
16
|
"scripts/check-skill-versions.ts",
|
|
@@ -192,13 +193,10 @@
|
|
|
192
193
|
"release:github": "bun run scripts/release-github.ts",
|
|
193
194
|
"publish-mcp": "mcp-publisher login github -token \"$(security find-generic-password -a \"$USER\" -s mcp-publisher-github-pat -w)\" && mcp-publisher publish"
|
|
194
195
|
},
|
|
195
|
-
"resolutions": {
|
|
196
|
-
"js-yaml": "^4.2.0"
|
|
197
|
-
},
|
|
198
196
|
"devDependencies": {
|
|
199
|
-
"@biomejs/biome": "2.5.
|
|
200
|
-
"@cloudflare/vitest-pool-workers": "^0.16.
|
|
201
|
-
"@cloudflare/workers-types": "4.
|
|
197
|
+
"@biomejs/biome": "2.5.1",
|
|
198
|
+
"@cloudflare/vitest-pool-workers": "^0.16.20",
|
|
199
|
+
"@cloudflare/workers-types": "4.20260630.1",
|
|
202
200
|
"@duckdb/node-api": "^1.5.4-r.1",
|
|
203
201
|
"@hono/otel": "^1.1.2",
|
|
204
202
|
"@opentelemetry/exporter-metrics-otlp-http": "^0.219.0",
|
|
@@ -210,10 +208,9 @@
|
|
|
210
208
|
"@opentelemetry/sdk-node": "^0.219.0",
|
|
211
209
|
"@opentelemetry/sdk-trace-node": "^2.8.0",
|
|
212
210
|
"@opentelemetry/semantic-conventions": "^1.41.1",
|
|
213
|
-
"@supabase/supabase-js": "^2.
|
|
211
|
+
"@supabase/supabase-js": "^2.109.0",
|
|
214
212
|
"@types/bun": "^1.3.14",
|
|
215
|
-
"@types/
|
|
216
|
-
"@types/node": "26.0.0",
|
|
213
|
+
"@types/node": "26.0.1",
|
|
217
214
|
"@types/papaparse": "^5.5.2",
|
|
218
215
|
"@types/sanitize-html": "^2.16.1",
|
|
219
216
|
"@types/validator": "^13.15.10",
|
|
@@ -223,29 +220,29 @@
|
|
|
223
220
|
"bun-types": "^1.3.14",
|
|
224
221
|
"chrono-node": "^2.9.1",
|
|
225
222
|
"clipboardy": "^5.3.1",
|
|
226
|
-
"defuddle": "^0.19.
|
|
223
|
+
"defuddle": "^0.19.1",
|
|
227
224
|
"depcheck": "^1.4.7",
|
|
228
225
|
"diff": "^9.0.0",
|
|
229
226
|
"execa": "^9.6.1",
|
|
230
227
|
"fast-check": "^4.8.0",
|
|
231
228
|
"fast-xml-parser": "^5.9.3",
|
|
232
229
|
"ignore": "^7.0.5",
|
|
233
|
-
"js-yaml": "^
|
|
230
|
+
"js-yaml": "^5.2.0",
|
|
234
231
|
"linkedom": "^0.18.12",
|
|
235
|
-
"node-cron": "^4.
|
|
236
|
-
"openai": "^6.
|
|
232
|
+
"node-cron": "^4.5.0",
|
|
233
|
+
"openai": "^6.45.0",
|
|
237
234
|
"papaparse": "^5.5.4",
|
|
238
235
|
"partial-json": "^0.1.7",
|
|
239
236
|
"pdf-lib": "^1.17.1",
|
|
240
237
|
"pino-pretty": "^13.1.3",
|
|
241
|
-
"repomix": "^1.
|
|
238
|
+
"repomix": "^1.16.0",
|
|
242
239
|
"sanitize-html": "^2.17.5",
|
|
243
240
|
"tsc-alias": "^1.8.17",
|
|
244
241
|
"typedoc": "^0.28.19",
|
|
245
242
|
"typescript": "^6.0.3",
|
|
246
243
|
"unpdf": "^1.6.2",
|
|
247
244
|
"validator": "^13.15.35",
|
|
248
|
-
"vite": "8.0
|
|
245
|
+
"vite": "8.1.0",
|
|
249
246
|
"vitest": "^4.1.9"
|
|
250
247
|
},
|
|
251
248
|
"keywords": [
|
|
@@ -298,12 +295,12 @@
|
|
|
298
295
|
},
|
|
299
296
|
"dependencies": {
|
|
300
297
|
"@hono/mcp": "^0.3.0",
|
|
301
|
-
"@hono/node-server": "^2.0.
|
|
298
|
+
"@hono/node-server": "^2.0.6",
|
|
302
299
|
"@modelcontextprotocol/ext-apps": "^1.7.4",
|
|
303
300
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
304
301
|
"@opentelemetry/api": "^1.9.1",
|
|
305
302
|
"dotenv": "^17.4.2",
|
|
306
|
-
"hono": "^4.12.
|
|
303
|
+
"hono": "^4.12.27",
|
|
307
304
|
"jose": "^6.2.3",
|
|
308
305
|
"pino": "^10.3.1",
|
|
309
306
|
"zod": "^4.4.3"
|
|
@@ -328,7 +325,7 @@
|
|
|
328
325
|
"fast-check": "^4.0.0",
|
|
329
326
|
"vitest": ">=4.0.0",
|
|
330
327
|
"fast-xml-parser": "^5.8.0",
|
|
331
|
-
"js-yaml": "^
|
|
328
|
+
"js-yaml": "^5.2.0",
|
|
332
329
|
"linkedom": "^0.18.12",
|
|
333
330
|
"node-cron": "^4.2.1",
|
|
334
331
|
"openai": "^6.34.0",
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Rejects floating dependency specifiers — `latest`, `*`, and
|
|
4
|
+
* pre-release dist-tags (`next`/`beta`/`canary`/`rc`) — in the manifests we
|
|
5
|
+
* control: `package.json`'s dependency sections and `bun.lock`'s `workspaces`
|
|
6
|
+
* specifier maps. `bun update --latest` bumps `package.json` to a caret range
|
|
7
|
+
* but writes the literal `latest` dist-tag into the lock's workspace map; the
|
|
8
|
+
* concrete resolution is correct at write time, so it clears every other gate
|
|
9
|
+
* and ships unnoticed, letting the next `bun install` re-resolve the dep past
|
|
10
|
+
* the `package.json` range and past any intentional version hold (#246).
|
|
11
|
+
*
|
|
12
|
+
* Section-specific rules — `peer`/`optional` may legitimately float to "any
|
|
13
|
+
* host version", so `*` and dist-tags are allowed there; `latest` never is:
|
|
14
|
+
*
|
|
15
|
+
* | specifier | dependencies / devDependencies | peer / optional |
|
|
16
|
+
* |---------------------------|:------------------------------:|:---------------:|
|
|
17
|
+
* | latest | fail | fail |
|
|
18
|
+
* | * | fail | allow |
|
|
19
|
+
* | next / beta / canary / rc | fail | allow |
|
|
20
|
+
*
|
|
21
|
+
* Only the `workspaces` section of `bun.lock` is inspected — the `packages`
|
|
22
|
+
* section records third-party packages' own nested declarations, which can
|
|
23
|
+
* legitimately be `latest`/`*` (e.g. a vendored `"@edge-runtime/vm": "*"`), so
|
|
24
|
+
* a naive whole-file grep false-positives on those. The lock is JSONC (trailing
|
|
25
|
+
* commas), so it is normalized before parsing.
|
|
26
|
+
*
|
|
27
|
+
* Shipped to consumers via `package.json` `files:` because `devcheck` invokes
|
|
28
|
+
* it. Runs standalone (`bun run scripts/check-dependency-specifiers.ts`) and as
|
|
29
|
+
* a devcheck step; no network, no git — runs in the default and `--fast` passes.
|
|
30
|
+
*
|
|
31
|
+
* @module scripts/check-dependency-specifiers
|
|
32
|
+
*/
|
|
33
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
34
|
+
import { resolve } from 'node:path';
|
|
35
|
+
import process from 'node:process';
|
|
36
|
+
|
|
37
|
+
const ROOT = resolve('.');
|
|
38
|
+
|
|
39
|
+
const DEP_SECTIONS = [
|
|
40
|
+
'dependencies',
|
|
41
|
+
'devDependencies',
|
|
42
|
+
'peerDependencies',
|
|
43
|
+
'optionalDependencies',
|
|
44
|
+
] as const;
|
|
45
|
+
type DepSection = (typeof DEP_SECTIONS)[number];
|
|
46
|
+
|
|
47
|
+
/** Sections where a host project legitimately accepts "any version" (`*`/dist-tag). */
|
|
48
|
+
const HOST_SECTIONS = new Set<DepSection>(['peerDependencies', 'optionalDependencies']);
|
|
49
|
+
|
|
50
|
+
/** Pre-release dist-tags that float like `latest`, narrower in reach. */
|
|
51
|
+
const FLOATING_DIST_TAGS = new Set(['next', 'beta', 'canary', 'rc']);
|
|
52
|
+
|
|
53
|
+
interface Offender {
|
|
54
|
+
location: string;
|
|
55
|
+
name: string;
|
|
56
|
+
specifier: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type DepMap = Record<string, string>;
|
|
60
|
+
type SectionedManifest = Partial<Record<DepSection, DepMap>>;
|
|
61
|
+
|
|
62
|
+
/** A specifier floats if it can re-resolve to a different version over time. */
|
|
63
|
+
function isFloating(specifier: string, section: DepSection): boolean {
|
|
64
|
+
const spec = specifier.trim();
|
|
65
|
+
if (spec === 'latest') return true; // never reproducible, in any section
|
|
66
|
+
if (HOST_SECTIONS.has(section)) return false; // `*` / dist-tags are legitimate here
|
|
67
|
+
return spec === '*' || FLOATING_DIST_TAGS.has(spec);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Scan the four dependency sections of one manifest object. */
|
|
71
|
+
function scanManifest(manifest: SectionedManifest, label: string): Offender[] {
|
|
72
|
+
const offenders: Offender[] = [];
|
|
73
|
+
for (const section of DEP_SECTIONS) {
|
|
74
|
+
const deps = manifest[section];
|
|
75
|
+
if (!deps || typeof deps !== 'object') continue;
|
|
76
|
+
for (const [name, specifier] of Object.entries(deps)) {
|
|
77
|
+
if (typeof specifier === 'string' && isFloating(specifier, section)) {
|
|
78
|
+
offenders.push({ name, specifier, location: `${label} ${section}` });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return offenders;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Minimal JSONC → object parse: strips `//` and block comments and trailing
|
|
87
|
+
* commas while preserving string contents verbatim, then `JSON.parse`s.
|
|
88
|
+
* `bun.lock` is emitted with trailing commas, which strict `JSON.parse` rejects.
|
|
89
|
+
*/
|
|
90
|
+
function parseJsonc<T>(text: string): T {
|
|
91
|
+
let out = '';
|
|
92
|
+
let i = 0;
|
|
93
|
+
const n = text.length;
|
|
94
|
+
while (i < n) {
|
|
95
|
+
const ch = text[i] as string;
|
|
96
|
+
if (ch === '"') {
|
|
97
|
+
// Copy a string literal verbatim, honoring escapes.
|
|
98
|
+
out += ch;
|
|
99
|
+
i++;
|
|
100
|
+
while (i < n) {
|
|
101
|
+
const c = text[i] as string;
|
|
102
|
+
out += c;
|
|
103
|
+
if (c === '\\') {
|
|
104
|
+
out += text[i + 1] ?? '';
|
|
105
|
+
i += 2;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
i++;
|
|
109
|
+
if (c === '"') break;
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (ch === '/' && text[i + 1] === '/') {
|
|
114
|
+
const nl = text.indexOf('\n', i);
|
|
115
|
+
if (nl === -1) break;
|
|
116
|
+
i = nl;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (ch === '/' && text[i + 1] === '*') {
|
|
120
|
+
const end = text.indexOf('*/', i + 2);
|
|
121
|
+
i = end === -1 ? n : end + 2;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (ch === ',') {
|
|
125
|
+
// Drop the comma when the next significant char closes an object/array.
|
|
126
|
+
let j = i + 1;
|
|
127
|
+
while (j < n && /\s/.test(text[j] as string)) j++;
|
|
128
|
+
if (text[j] === '}' || text[j] === ']') {
|
|
129
|
+
i++;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
out += ch;
|
|
134
|
+
i++;
|
|
135
|
+
}
|
|
136
|
+
return JSON.parse(out) as T;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const offenders: Offender[] = [];
|
|
140
|
+
|
|
141
|
+
// ── package.json (all four dependency sections) ──
|
|
142
|
+
const pkgPath = resolve(ROOT, 'package.json');
|
|
143
|
+
if (existsSync(pkgPath)) {
|
|
144
|
+
try {
|
|
145
|
+
offenders.push(
|
|
146
|
+
...scanManifest(
|
|
147
|
+
JSON.parse(readFileSync(pkgPath, 'utf-8')) as SectionedManifest,
|
|
148
|
+
'package.json',
|
|
149
|
+
),
|
|
150
|
+
);
|
|
151
|
+
} catch (err) {
|
|
152
|
+
console.error(`Failed to parse package.json: ${err instanceof Error ? err.message : err}`);
|
|
153
|
+
process.exit(2);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ── bun.lock (workspaces specifier maps only — never the `packages` section) ──
|
|
158
|
+
const lockPath = resolve(ROOT, 'bun.lock');
|
|
159
|
+
if (existsSync(lockPath)) {
|
|
160
|
+
try {
|
|
161
|
+
const lock = parseJsonc<{ workspaces?: Record<string, SectionedManifest> }>(
|
|
162
|
+
readFileSync(lockPath, 'utf-8'),
|
|
163
|
+
);
|
|
164
|
+
for (const [ws, manifest] of Object.entries(lock.workspaces ?? {})) {
|
|
165
|
+
const label = ws === '' ? 'bun.lock workspaces[root]' : `bun.lock workspaces["${ws}"]`;
|
|
166
|
+
offenders.push(...scanManifest(manifest, label));
|
|
167
|
+
}
|
|
168
|
+
} catch (err) {
|
|
169
|
+
console.error(`Failed to parse bun.lock: ${err instanceof Error ? err.message : err}`);
|
|
170
|
+
process.exit(2);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (offenders.length === 0) {
|
|
175
|
+
console.log('No floating dependency specifiers found.');
|
|
176
|
+
process.exit(0);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const lines = [
|
|
180
|
+
`${offenders.length} floating dependency specifier(s) found:`,
|
|
181
|
+
'',
|
|
182
|
+
...offenders.map((o) => ` - ${o.name} → ${o.specifier} (${o.location})`),
|
|
183
|
+
'',
|
|
184
|
+
'Fix: pin to a concrete semver range (e.g. ^1.2.3). A `latest`/`*`/dist-tag',
|
|
185
|
+
'specifier lets `bun install` re-resolve the dep past the package.json range',
|
|
186
|
+
'and past any intentional hold. After `bun update --latest`, run a plain',
|
|
187
|
+
'`bun install` to reconcile bun.lock to the package.json ranges.',
|
|
188
|
+
];
|
|
189
|
+
console.error(lines.join('\n'));
|
|
190
|
+
process.exit(1);
|
package/scripts/devcheck.ts
CHANGED
|
@@ -437,10 +437,16 @@ const ALL_CHECKS: Check[] = [
|
|
|
437
437
|
flag: '--no-packaging',
|
|
438
438
|
canFix: false,
|
|
439
439
|
// Validates env var alignment between manifest.json (MCPB bundle) and
|
|
440
|
-
// server.json (MCP Registry)
|
|
441
|
-
//
|
|
440
|
+
// server.json (MCP Registry), plus plugin marketplace manifests (#240).
|
|
441
|
+
// Runs when manifest.json OR any plugin manifest is present; skipped cleanly
|
|
442
|
+
// when none exist — consumers on an HTTP-only deploy are unaffected.
|
|
442
443
|
getCommand: () => {
|
|
443
|
-
|
|
444
|
+
const hasManifest = existsSync(path.join(ROOT_DIR, 'manifest.json'));
|
|
445
|
+
const hasPluginManifest =
|
|
446
|
+
existsSync(path.join(ROOT_DIR, '.claude-plugin/plugin.json')) ||
|
|
447
|
+
existsSync(path.join(ROOT_DIR, '.codex-plugin/plugin.json')) ||
|
|
448
|
+
existsSync(path.join(ROOT_DIR, '.codex-plugin/mcp.json'));
|
|
449
|
+
if (!hasManifest && !hasPluginManifest) return null;
|
|
444
450
|
return ['bun', 'run', 'scripts/lint-packaging.ts'];
|
|
445
451
|
},
|
|
446
452
|
tip: (c) =>
|
|
@@ -458,6 +464,18 @@ const ALL_CHECKS: Check[] = [
|
|
|
458
464
|
tip: (c) =>
|
|
459
465
|
`Remove the flagged SDK-coupling shortcut. See ${c.bold('scripts/check-framework-antipatterns.ts')} for rule rationale.`,
|
|
460
466
|
},
|
|
467
|
+
{
|
|
468
|
+
name: 'Dependency Specifiers',
|
|
469
|
+
flag: '--no-dep-specifiers',
|
|
470
|
+
canFix: false,
|
|
471
|
+
// Rejects floating specifiers (latest/*/dist-tags) in package.json + the
|
|
472
|
+
// bun.lock workspaces map (#246). Static and local — no network, no git —
|
|
473
|
+
// so it runs in the default and --fast passes. Shipped to consumers via
|
|
474
|
+
// package.json `files:`; reads package.json/bun.lock directly, no guard.
|
|
475
|
+
getCommand: () => ['bun', 'run', 'scripts/check-dependency-specifiers.ts'],
|
|
476
|
+
tip: (c) =>
|
|
477
|
+
`Pin the flagged dep to a concrete range; after ${c.bold('bun update --latest')} run a plain ${c.bold('bun install')} to reconcile ${c.bold('bun.lock')}.`,
|
|
478
|
+
},
|
|
461
479
|
{
|
|
462
480
|
name: 'Open-Indexed Interfaces',
|
|
463
481
|
flag: '--no-open-index',
|
|
@@ -32,6 +32,15 @@
|
|
|
32
32
|
* `createWorkerHandler()` (src/index.ts, src/worker.ts) and manifest
|
|
33
33
|
* `display_name` must equal the unscoped package name; a partial
|
|
34
34
|
* `name`/`title` pair warns without failing (issue #231).
|
|
35
|
+
* 10. Plugin marketplace manifests (`.claude-plugin/plugin.json`,
|
|
36
|
+
* `.codex-plugin/plugin.json`, `.codex-plugin/mcp.json`): non-empty
|
|
37
|
+
* descriptions, and identity/install correctness — display fields
|
|
38
|
+
* (`name`, server key, `interface.displayName`) carry the unscoped machine
|
|
39
|
+
* name while the `npx -y` install arg carries the full `package.json`
|
|
40
|
+
* name (scoped if scoped). An unscoped install arg for a scoped package
|
|
41
|
+
* is a guaranteed install 404. Gated by `devcheck.config.json`
|
|
42
|
+
* `packaging.pluginManifests` (default on); each manifest is skipped
|
|
43
|
+
* cleanly when absent (issue #240).
|
|
35
44
|
*
|
|
36
45
|
* Every check skips cleanly when its input is absent — consumers who deleted
|
|
37
46
|
* `manifest.json` for an HTTP-only deploy, or who haven't built a bundle,
|
|
@@ -374,6 +383,128 @@ export function checkManifestIdentity(manifest: Manifest, unscopedName: string):
|
|
|
374
383
|
return [];
|
|
375
384
|
}
|
|
376
385
|
|
|
386
|
+
/** Parsed plugin marketplace manifests; an absent manifest is `undefined`. */
|
|
387
|
+
export interface PluginManifestInputs {
|
|
388
|
+
claudePlugin?: unknown;
|
|
389
|
+
codexMcp?: unknown;
|
|
390
|
+
codexPlugin?: unknown;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const isRecord = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null;
|
|
394
|
+
const isNonEmptyString = (v: unknown): boolean => typeof v === 'string' && v.trim().length > 0;
|
|
395
|
+
|
|
396
|
+
/** The `npx -y <pkg>` install target is the arg after the `-y` flag (args[1]). */
|
|
397
|
+
function installArg(entry: Record<string, unknown>): unknown {
|
|
398
|
+
return Array.isArray(entry.args) ? entry.args[1] : undefined;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Check 10: plugin marketplace manifests. Display fields (`name`, server key,
|
|
403
|
+
* `interface.displayName`) must equal the unscoped machine name; the install
|
|
404
|
+
* arg must equal the full `package.json` name (the real `npx` target). Empty
|
|
405
|
+
* descriptions ship blank marketplace cards. Each manifest is validated only
|
|
406
|
+
* when present, so HTTP-only and non-plugin consumers are unaffected. The
|
|
407
|
+
* caller gates the whole check on `packaging.pluginManifests`.
|
|
408
|
+
*/
|
|
409
|
+
export function checkPluginManifests(
|
|
410
|
+
inputs: PluginManifestInputs,
|
|
411
|
+
unscopedName: string,
|
|
412
|
+
fullName: string,
|
|
413
|
+
): string[] {
|
|
414
|
+
const errors: string[] = [];
|
|
415
|
+
const optOut =
|
|
416
|
+
'(or set "packaging": { "pluginManifests": false } in devcheck.config.json to opt out)';
|
|
417
|
+
|
|
418
|
+
// ── .claude-plugin/plugin.json ──
|
|
419
|
+
const claude = inputs.claudePlugin;
|
|
420
|
+
if (isRecord(claude)) {
|
|
421
|
+
const f = '.claude-plugin/plugin.json';
|
|
422
|
+
if (!isNonEmptyString(claude.description)) {
|
|
423
|
+
errors.push(`${f} "description" is empty — populate it ${optOut}`);
|
|
424
|
+
}
|
|
425
|
+
if (claude.name !== unscopedName) {
|
|
426
|
+
errors.push(
|
|
427
|
+
`${f} "name" is "${String(claude.name)}" — must equal the unscoped package name "${unscopedName}"`,
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
const servers = claude.mcpServers;
|
|
431
|
+
if (isRecord(servers)) {
|
|
432
|
+
if (!(unscopedName in servers)) {
|
|
433
|
+
errors.push(
|
|
434
|
+
`${f} mcpServers has no "${unscopedName}" entry — the server key must be the unscoped package name`,
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
const entry = servers[unscopedName];
|
|
438
|
+
if (isRecord(entry) && installArg(entry) !== fullName) {
|
|
439
|
+
errors.push(
|
|
440
|
+
`${f} mcpServers["${unscopedName}"] install arg is "${String(installArg(entry))}" — ` +
|
|
441
|
+
`must be the full package name "${fullName}" (the npx -y target; an unscoped arg for a scoped package 404s)`,
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// ── .codex-plugin/plugin.json ──
|
|
448
|
+
const codex = inputs.codexPlugin;
|
|
449
|
+
if (isRecord(codex)) {
|
|
450
|
+
const f = '.codex-plugin/plugin.json';
|
|
451
|
+
if (!isNonEmptyString(codex.description)) {
|
|
452
|
+
errors.push(`${f} "description" is empty — populate it ${optOut}`);
|
|
453
|
+
}
|
|
454
|
+
if (codex.name !== unscopedName) {
|
|
455
|
+
errors.push(
|
|
456
|
+
`${f} "name" is "${String(codex.name)}" — must equal the unscoped package name "${unscopedName}"`,
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
const iface = codex.interface;
|
|
460
|
+
if (isRecord(iface)) {
|
|
461
|
+
if (iface.displayName !== unscopedName) {
|
|
462
|
+
errors.push(
|
|
463
|
+
`${f} interface.displayName is "${String(iface.displayName)}" — must equal the unscoped package name "${unscopedName}"`,
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
if (!isNonEmptyString(iface.shortDescription)) {
|
|
467
|
+
errors.push(`${f} interface.shortDescription is empty — populate it ${optOut}`);
|
|
468
|
+
}
|
|
469
|
+
if (!isNonEmptyString(iface.longDescription)) {
|
|
470
|
+
errors.push(`${f} interface.longDescription is empty — populate it ${optOut}`);
|
|
471
|
+
}
|
|
472
|
+
} else {
|
|
473
|
+
errors.push(
|
|
474
|
+
`${f} is missing the "interface" object (displayName / shortDescription / longDescription)`,
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ── .codex-plugin/mcp.json ──
|
|
480
|
+
const codexMcp = inputs.codexMcp;
|
|
481
|
+
if (isRecord(codexMcp)) {
|
|
482
|
+
const f = '.codex-plugin/mcp.json';
|
|
483
|
+
if (!(unscopedName in codexMcp)) {
|
|
484
|
+
errors.push(
|
|
485
|
+
`${f} has no "${unscopedName}" server entry — the server key must be the unscoped package name`,
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
const entry = codexMcp[unscopedName];
|
|
489
|
+
if (isRecord(entry) && installArg(entry) !== fullName) {
|
|
490
|
+
errors.push(
|
|
491
|
+
`${f} "${unscopedName}" install arg is "${String(installArg(entry))}" — ` +
|
|
492
|
+
`must be the full package name "${fullName}" (the npx -y target)`,
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return errors;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/** Read `packaging.pluginManifests` from devcheck.config.json; default on. */
|
|
501
|
+
function pluginManifestsEnabled(): boolean {
|
|
502
|
+
const cfg = tryReadJson<{ packaging?: { pluginManifests?: boolean } }>(
|
|
503
|
+
resolve('devcheck.config.json'),
|
|
504
|
+
);
|
|
505
|
+
return cfg?.packaging?.pluginManifests ?? true;
|
|
506
|
+
}
|
|
507
|
+
|
|
377
508
|
async function main(): Promise<void> {
|
|
378
509
|
const errors: string[] = [];
|
|
379
510
|
const warnings: string[] = [];
|
|
@@ -496,6 +627,27 @@ async function main(): Promise<void> {
|
|
|
496
627
|
}
|
|
497
628
|
}
|
|
498
629
|
|
|
630
|
+
// ── Plugin marketplace manifests (check 10) ──
|
|
631
|
+
if (unscopedName && pkg?.name) {
|
|
632
|
+
if (pluginManifestsEnabled()) {
|
|
633
|
+
errors.push(
|
|
634
|
+
...checkPluginManifests(
|
|
635
|
+
{
|
|
636
|
+
claudePlugin: tryReadJson(resolve('.claude-plugin/plugin.json')),
|
|
637
|
+
codexPlugin: tryReadJson(resolve('.codex-plugin/plugin.json')),
|
|
638
|
+
codexMcp: tryReadJson(resolve('.codex-plugin/mcp.json')),
|
|
639
|
+
},
|
|
640
|
+
unscopedName,
|
|
641
|
+
pkg.name,
|
|
642
|
+
),
|
|
643
|
+
);
|
|
644
|
+
} else {
|
|
645
|
+
notes.push(
|
|
646
|
+
'Plugin-manifest checks disabled via devcheck.config.json packaging.pluginManifests.',
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
499
651
|
for (const note of notes) console.log(note);
|
|
500
652
|
for (const warning of warnings) console.warn(` ⚠ ${warning}`);
|
|
501
653
|
|
|
@@ -23,7 +23,7 @@ All parsers are **Tier 3** — lazy-load their peer dependency on first call. Al
|
|
|
23
23
|
|:-------|:----------|
|
|
24
24
|
| `parse` | `<T = unknown>(yamlString, context?) -> Promise<T>` |
|
|
25
25
|
|
|
26
|
-
Uses `js-yaml` `
|
|
26
|
+
Uses `js-yaml` (v5) `YAML11_SCHEMA` (YAML 1.1 load semantics). Throws `ConfigurationError` if dep missing, `ValidationError` on empty/malformed input.
|
|
27
27
|
|
|
28
28
|
```ts
|
|
29
29
|
const config = await yamlParser.parse<ServerConfig>(yamlString);
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Finalize documentation and project metadata for a ship-ready MCP server. Use after implementation is complete, tests pass, and devcheck is clean. Safe to run at any stage — each step checks current state and only acts on what still needs work.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.9"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -169,20 +169,22 @@ Never hand-edit `CHANGELOG.md` when using this pattern — it's a build artifact
|
|
|
169
169
|
|
|
170
170
|
### 10. Plugin Metadata (Codex / Claude Code)
|
|
171
171
|
|
|
172
|
+
`lint:packaging` (run by `devcheck`) now enforces the high-value subset automatically when these manifests are present: non-empty descriptions, and identity/install correctness — display fields (`name`, server key, `interface.displayName`) must be the **unscoped** machine name, while the `npx -y` install arg must be the full `package.json` `name` (scoped if scoped). Opt out per project with `"packaging": { "pluginManifests": false }` in `devcheck.config.json`. The checks below cover the fields the gate doesn't (version / repository / license sync, category, env vars).
|
|
173
|
+
|
|
172
174
|
If `.codex-plugin/plugin.json` exists, verify it's populated and in sync with `package.json` and `server.json`:
|
|
173
175
|
|
|
174
|
-
- `name`
|
|
176
|
+
- `name` is the unscoped `package.json` `name` (display identity is the machine name on every surface)
|
|
175
177
|
- `version` matches `package.json` `version`
|
|
176
178
|
- `description` matches `package.json` `description`
|
|
177
179
|
- `repository` matches `package.json` `repository.url`
|
|
178
180
|
- `license` matches `package.json` `license`
|
|
179
|
-
- `interface.displayName`
|
|
181
|
+
- `interface.displayName` is the unscoped `package.json` `name`
|
|
180
182
|
- `interface.shortDescription` matches `package.json` `description`
|
|
181
183
|
- `interface.category` is set to a meaningful category
|
|
182
184
|
|
|
183
|
-
If `.codex-plugin/mcp.json` exists, verify the server
|
|
185
|
+
If `.codex-plugin/mcp.json` exists, verify the server-name key is the unscoped `package.json` `name`, the `npx -y` install arg is the full `package.json` `name`, and env vars include any required API keys from the server config schema.
|
|
184
186
|
|
|
185
|
-
If `.claude-plugin/plugin.json` exists, apply the same checks: `name
|
|
187
|
+
If `.claude-plugin/plugin.json` exists, apply the same checks: `name` (unscoped), `version`, `description`, `repository`, `license` from `package.json`. Verify the inline `mcpServers` entry key is the unscoped name, its `npx -y` install arg is the full `package.json` `name`, and env vars include any required API keys.
|
|
186
188
|
|
|
187
189
|
### 11. MCPB Bundling Artifacts
|
|
188
190
|
|
|
@@ -21,7 +21,6 @@ Framework badge ← solo spotlight row — `Built on @cy
|
|
|
21
21
|
## Running the server ← dev, production, Workers/Docker
|
|
22
22
|
## Project structure ← directory/purpose table
|
|
23
23
|
## Development guide ← link to CLAUDE.md/AGENTS.md, key rules
|
|
24
|
-
## Contributing ← brief
|
|
25
24
|
## License ← one line
|
|
26
25
|
```
|
|
27
26
|
|
|
@@ -484,19 +483,6 @@ See [`CLAUDE.md`/`AGENTS.md`](./CLAUDE.md) for development guidelines and archit
|
|
|
484
483
|
- Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
|
|
485
484
|
```
|
|
486
485
|
|
|
487
|
-
### Contributing
|
|
488
|
-
|
|
489
|
-
```markdown
|
|
490
|
-
## Contributing
|
|
491
|
-
|
|
492
|
-
Issues and pull requests are welcome. Run checks and tests before submitting:
|
|
493
|
-
|
|
494
|
-
\`\`\`sh
|
|
495
|
-
bun run devcheck
|
|
496
|
-
bun run test
|
|
497
|
-
\`\`\`
|
|
498
|
-
```
|
|
499
|
-
|
|
500
486
|
### License
|
|
501
487
|
|
|
502
488
|
One line referencing the LICENSE file.
|
package/dist/logs/combined.log
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
{"level":40,"time":1781913320360,"env":"testing","version":"0.10.8","pid":20168,"transport":"http","requestId":"WSD7Y-6FNIU","timestamp":"2026-06-19T23:55:20.360Z","operation":"TransportManager.start","component":"HttpTransportSetup","msg":"MCP_ALLOWED_ORIGINS is not set — CORS is wildcard for CLI clients; browser Origin headers are restricted to loopback. Set MCP_ALLOWED_ORIGINS for production deployments accepting remote browser origins."}
|
|
2
|
-
{"level":40,"time":1781913322185,"env":"testing","version":"0.10.8","pid":20168,"component":"HttpTransport","requestId":"Y3XD8-YV6FO","timestamp":"2026-06-19T23:55:22.184Z","operation":"HttpRpcRequest","sessionId":"not-a-real-session-1781913322182","msg":"Session validation failed - invalid or hijacked session"}
|
|
3
|
-
{"level":50,"time":1781913329461,"env":"testing","version":"0.0.0-test","pid":20387,"requestId":"5JUEU-0VDJE","timestamp":"2026-06-19T23:55:29.461Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"da08fbd6c9656cf3f0b23b60935dfa84e7923c134e40b457be98bda811ed2efc","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"da08fbd6c9656cf3f0b23b60935dfa84e7923c134e40b457be98bda811ed2efc","toolName":"scoped_echo","requestId":"5JUEU-0VDJE","timestamp":"2026-06-19T23:55:29.461Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (file:///Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:54)\n at withRequiredScopes (file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:283:17\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)"},"stack":"McpError: Insufficient permissions.\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:332:26\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
4
|
-
{"level":50,"time":1781913329472,"env":"testing","version":"0.0.0-test","pid":20387,"requestId":"R7MGX-J7GDG","timestamp":"2026-06-19T23:55:29.472Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"d784c7b4dd86d4f145dee03c30c3a68ae51a8606097461f46ebb719cfd85d619","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"d784c7b4dd86d4f145dee03c30c3a68ae51a8606097461f46ebb719cfd85d619","toolName":"scoped_echo","requestId":"R7MGX-J7GDG","timestamp":"2026-06-19T23:55:29.472Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (file:///Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:54)\n at withRequiredScopes (file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:283:17\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)"},"stack":"McpError: Insufficient permissions.\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:332:26\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
5
|
-
{"level":40,"time":1781913343027,"env":"testing","version":"0.10.8","pid":20630,"transport":"http","requestId":"GF046-IMNJH","timestamp":"2026-06-19T23:55:43.027Z","operation":"TransportManager.start","component":"HttpTransportSetup","msg":"MCP_ALLOWED_ORIGINS is not set — CORS is wildcard for CLI clients; browser Origin headers are restricted to loopback. Set MCP_ALLOWED_ORIGINS for production deployments accepting remote browser origins."}
|
|
6
|
-
{"level":40,"time":1781913344790,"env":"testing","version":"0.10.8","pid":20630,"component":"HttpTransport","requestId":"Z5YGA-X0LTR","timestamp":"2026-06-19T23:55:44.790Z","operation":"HttpRpcRequest","sessionId":"not-a-real-session-1781913344790","msg":"Session validation failed - invalid or hijacked session"}
|
|
7
|
-
{"level":50,"time":1781913345263,"env":"testing","version":"0.0.0-test","pid":20668,"requestId":"4BROD-WQSCR","timestamp":"2026-06-19T23:55:45.262Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"25c684941147a157673abe23c7519112dbe02be9c0262c28478ff1fc436ebf1b","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"25c684941147a157673abe23c7519112dbe02be9c0262c28478ff1fc436ebf1b","toolName":"scoped_echo","requestId":"4BROD-WQSCR","timestamp":"2026-06-19T23:55:45.262Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:283:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:332:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
8
|
-
{"level":50,"time":1781913345270,"env":"testing","version":"0.0.0-test","pid":20668,"requestId":"3OCQP-0V3O2","timestamp":"2026-06-19T23:55:45.270Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"10e3337a2b2dbc0eb5bf700bf8e875b1b829ebb783a65abcf06d9ffbf3006536","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"10e3337a2b2dbc0eb5bf700bf8e875b1b829ebb783a65abcf06d9ffbf3006536","toolName":"scoped_echo","requestId":"3OCQP-0V3O2","timestamp":"2026-06-19T23:55:45.270Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:283:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:332:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
package/dist/logs/error.log
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
{"level":50,"time":1781913329461,"env":"testing","version":"0.0.0-test","pid":20387,"requestId":"5JUEU-0VDJE","timestamp":"2026-06-19T23:55:29.461Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"da08fbd6c9656cf3f0b23b60935dfa84e7923c134e40b457be98bda811ed2efc","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"da08fbd6c9656cf3f0b23b60935dfa84e7923c134e40b457be98bda811ed2efc","toolName":"scoped_echo","requestId":"5JUEU-0VDJE","timestamp":"2026-06-19T23:55:29.461Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (file:///Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:54)\n at withRequiredScopes (file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:283:17\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)"},"stack":"McpError: Insufficient permissions.\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:332:26\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
2
|
-
{"level":50,"time":1781913329472,"env":"testing","version":"0.0.0-test","pid":20387,"requestId":"R7MGX-J7GDG","timestamp":"2026-06-19T23:55:29.472Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"d784c7b4dd86d4f145dee03c30c3a68ae51a8606097461f46ebb719cfd85d619","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"d784c7b4dd86d4f145dee03c30c3a68ae51a8606097461f46ebb719cfd85d619","toolName":"scoped_echo","requestId":"R7MGX-J7GDG","timestamp":"2026-06-19T23:55:29.472Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (file:///Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:54)\n at withRequiredScopes (file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:283:17\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)"},"stack":"McpError: Insufficient permissions.\n at ErrorHandler.handleError (file:///Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:19)\n at file:///Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:332:26\n at McpServer.executeToolHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:233:42)\n at file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43\n at async wrappedHandler (file:///Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js:125:32)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
3
|
-
{"level":50,"time":1781913345263,"env":"testing","version":"0.0.0-test","pid":20668,"requestId":"4BROD-WQSCR","timestamp":"2026-06-19T23:55:45.262Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"25c684941147a157673abe23c7519112dbe02be9c0262c28478ff1fc436ebf1b","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"25c684941147a157673abe23c7519112dbe02be9c0262c28478ff1fc436ebf1b","toolName":"scoped_echo","requestId":"4BROD-WQSCR","timestamp":"2026-06-19T23:55:45.262Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:283:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:332:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
4
|
-
{"level":50,"time":1781913345270,"env":"testing","version":"0.0.0-test","pid":20668,"requestId":"3OCQP-0V3O2","timestamp":"2026-06-19T23:55:45.270Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"10e3337a2b2dbc0eb5bf700bf8e875b1b829ebb783a65abcf06d9ffbf3006536","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"10e3337a2b2dbc0eb5bf700bf8e875b1b829ebb783a65abcf06d9ffbf3006536","toolName":"scoped_echo","requestId":"3OCQP-0V3O2","timestamp":"2026-06-19T23:55:45.270Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:283:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:332:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
File without changes
|