@cyanheads/mcp-ts-core 0.13.7 → 0.13.9
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 +19 -15
- package/CLAUDE.md +19 -15
- package/README.md +4 -2
- package/changelog/0.13.x/0.13.8.md +101 -0
- package/changelog/0.13.x/0.13.9.md +113 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +39 -9
- package/dist/config/index.js.map +1 -1
- package/dist/core/app.d.ts +6 -3
- package/dist/core/app.d.ts.map +1 -1
- package/dist/core/app.js +20 -6
- package/dist/core/app.js.map +1 -1
- package/dist/core/context.d.ts +25 -1
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +13 -3
- package/dist/core/context.js.map +1 -1
- package/dist/core/serverManifest.d.ts +6 -0
- package/dist/core/serverManifest.d.ts.map +1 -1
- package/dist/core/serverManifest.js +6 -0
- package/dist/core/serverManifest.js.map +1 -1
- package/dist/core/worker.d.ts +2 -0
- package/dist/core/worker.d.ts.map +1 -1
- package/dist/core/worker.js +2 -0
- package/dist/core/worker.js.map +1 -1
- package/dist/linter/rules/enrichment-rules.d.ts +3 -2
- package/dist/linter/rules/enrichment-rules.d.ts.map +1 -1
- package/dist/linter/rules/enrichment-rules.js +9 -2
- package/dist/linter/rules/enrichment-rules.js.map +1 -1
- package/dist/linter/rules/handler-body-rules.d.ts.map +1 -1
- package/dist/linter/rules/handler-body-rules.js +10 -4
- package/dist/linter/rules/handler-body-rules.js.map +1 -1
- package/dist/linter/rules/schema-rules.d.ts +5 -0
- package/dist/linter/rules/schema-rules.d.ts.map +1 -1
- package/dist/linter/rules/schema-rules.js +44 -17
- package/dist/linter/rules/schema-rules.js.map +1 -1
- package/dist/linter/rules/tool-rules.d.ts +2 -1
- package/dist/linter/rules/tool-rules.d.ts.map +1 -1
- package/dist/linter/rules/tool-rules.js +36 -1
- package/dist/linter/rules/tool-rules.js.map +1 -1
- package/dist/mcp-server/inputRequired.d.ts +14 -5
- package/dist/mcp-server/inputRequired.d.ts.map +1 -1
- package/dist/mcp-server/inputRequired.js +15 -8
- package/dist/mcp-server/inputRequired.js.map +1 -1
- package/dist/mcp-server/outputContract.d.ts +33 -0
- package/dist/mcp-server/outputContract.d.ts.map +1 -0
- package/dist/mcp-server/outputContract.js +43 -0
- package/dist/mcp-server/outputContract.js.map +1 -0
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +10 -2
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
- package/dist/mcp-server/tools/utils/inputPrevalidation.d.ts +163 -40
- package/dist/mcp-server/tools/utils/inputPrevalidation.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/inputPrevalidation.js +330 -114
- package/dist/mcp-server/tools/utils/inputPrevalidation.js.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +39 -15
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js +361 -93
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
- package/dist/mcp-server/transports/auth/lib/authUtils.js +4 -1
- package/dist/mcp-server/transports/auth/lib/authUtils.js.map +1 -1
- package/dist/mcp-server/transports/auth/strategies/jwtStrategy.d.ts.map +1 -1
- package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js +1 -1
- package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js.map +1 -1
- package/dist/mcp-server/transports/auth/strategies/oauthStrategy.d.ts.map +1 -1
- package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js +2 -5
- package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js.map +1 -1
- package/dist/mcp-server/transports/http/httpTransport.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/httpTransport.js +65 -9
- package/dist/mcp-server/transports/http/httpTransport.js.map +1 -1
- package/dist/mcp-server/transports/http/sessionStore.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/sessionStore.js +2 -2
- package/dist/mcp-server/transports/http/sessionStore.js.map +1 -1
- package/dist/mcp-server/transports/stdio/stdioTransport.d.ts +9 -5
- package/dist/mcp-server/transports/stdio/stdioTransport.d.ts.map +1 -1
- package/dist/mcp-server/transports/stdio/stdioTransport.js +9 -5
- package/dist/mcp-server/transports/stdio/stdioTransport.js.map +1 -1
- package/dist/services/canvas/core/CanvasRegistry.d.ts +6 -2
- package/dist/services/canvas/core/CanvasRegistry.d.ts.map +1 -1
- package/dist/services/canvas/core/CanvasRegistry.js +8 -4
- package/dist/services/canvas/core/CanvasRegistry.js.map +1 -1
- package/dist/services/canvas/core/DataCanvas.d.ts.map +1 -1
- package/dist/services/canvas/core/DataCanvas.js +7 -5
- package/dist/services/canvas/core/DataCanvas.js.map +1 -1
- package/dist/services/canvas/core/canvasFactory.d.ts.map +1 -1
- package/dist/services/canvas/core/canvasFactory.js +2 -2
- package/dist/services/canvas/core/canvasFactory.js.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts +82 -18
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +645 -344
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js.map +1 -1
- package/dist/services/canvas/providers/duckdb/exportWriter.d.ts +11 -7
- package/dist/services/canvas/providers/duckdb/exportWriter.d.ts.map +1 -1
- package/dist/services/canvas/providers/duckdb/exportWriter.js +19 -16
- package/dist/services/canvas/providers/duckdb/exportWriter.js.map +1 -1
- package/dist/services/llm/providers/openrouter.provider.js +1 -1
- package/dist/services/llm/providers/openrouter.provider.js.map +1 -1
- package/dist/services/mirror/core/defineMirror.d.ts +1 -0
- package/dist/services/mirror/core/defineMirror.d.ts.map +1 -1
- package/dist/services/mirror/core/defineMirror.js +1 -0
- package/dist/services/mirror/core/defineMirror.js.map +1 -1
- package/dist/services/speech/providers/elevenlabs.provider.js +3 -3
- package/dist/services/speech/providers/elevenlabs.provider.js.map +1 -1
- package/dist/services/speech/providers/whisper.provider.d.ts.map +1 -1
- package/dist/services/speech/providers/whisper.provider.js +5 -5
- package/dist/services/speech/providers/whisper.provider.js.map +1 -1
- package/dist/storage/core/StorageService.d.ts.map +1 -1
- package/dist/storage/core/StorageService.js +3 -6
- package/dist/storage/core/StorageService.js.map +1 -1
- package/dist/storage/core/storageFactory.d.ts.map +1 -1
- package/dist/storage/core/storageFactory.js +12 -15
- package/dist/storage/core/storageFactory.js.map +1 -1
- package/dist/storage/core/storageValidation.d.ts +13 -13
- package/dist/storage/core/storageValidation.d.ts.map +1 -1
- package/dist/storage/core/storageValidation.js +49 -125
- package/dist/storage/core/storageValidation.js.map +1 -1
- package/dist/storage/providers/cloudflare/d1Provider.d.ts.map +1 -1
- package/dist/storage/providers/cloudflare/d1Provider.js +5 -3
- package/dist/storage/providers/cloudflare/d1Provider.js.map +1 -1
- package/dist/storage/providers/cloudflare/kvProvider.d.ts.map +1 -1
- package/dist/storage/providers/cloudflare/kvProvider.js +1 -1
- package/dist/storage/providers/cloudflare/kvProvider.js.map +1 -1
- package/dist/storage/providers/cloudflare/r2Provider.d.ts.map +1 -1
- package/dist/storage/providers/cloudflare/r2Provider.js +3 -3
- package/dist/storage/providers/cloudflare/r2Provider.js.map +1 -1
- package/dist/storage/providers/fileSystem/fileSystemProvider.js +4 -4
- package/dist/storage/providers/fileSystem/fileSystemProvider.js.map +1 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.d.ts +1 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.d.ts.map +1 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.js +6 -5
- package/dist/storage/providers/inMemory/inMemoryProvider.js.map +1 -1
- package/dist/testing/fuzz.d.ts.map +1 -1
- package/dist/testing/fuzz.js +7 -1
- package/dist/testing/fuzz.js.map +1 -1
- package/dist/testing/index.d.ts +15 -2
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +51 -6
- package/dist/testing/index.js.map +1 -1
- package/dist/types-global/errors.d.ts +7 -4
- package/dist/types-global/errors.d.ts.map +1 -1
- package/dist/types-global/errors.js.map +1 -1
- package/dist/utils/formatting/codeSpan.d.ts +27 -0
- package/dist/utils/formatting/codeSpan.d.ts.map +1 -0
- package/dist/utils/formatting/codeSpan.js +42 -0
- package/dist/utils/formatting/codeSpan.js.map +1 -0
- package/dist/utils/formatting/diffFormatter.d.ts.map +1 -1
- package/dist/utils/formatting/diffFormatter.js +7 -15
- package/dist/utils/formatting/diffFormatter.js.map +1 -1
- package/dist/utils/formatting/markdownBuilder.d.ts +12 -5
- package/dist/utils/formatting/markdownBuilder.d.ts.map +1 -1
- package/dist/utils/formatting/markdownBuilder.js +14 -2
- package/dist/utils/formatting/markdownBuilder.js.map +1 -1
- package/dist/utils/formatting/tableFormatter.d.ts.map +1 -1
- package/dist/utils/formatting/tableFormatter.js +5 -9
- package/dist/utils/formatting/tableFormatter.js.map +1 -1
- package/dist/utils/formatting/treeFormatter.d.ts.map +1 -1
- package/dist/utils/formatting/treeFormatter.js +5 -9
- package/dist/utils/formatting/treeFormatter.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/internal/error-handler/errorHandler.d.ts +17 -10
- package/dist/utils/internal/error-handler/errorHandler.d.ts.map +1 -1
- package/dist/utils/internal/error-handler/errorHandler.js +47 -26
- package/dist/utils/internal/error-handler/errorHandler.js.map +1 -1
- package/dist/utils/internal/error-handler/mappings.d.ts +17 -1
- package/dist/utils/internal/error-handler/mappings.d.ts.map +1 -1
- package/dist/utils/internal/error-handler/mappings.js +22 -1
- package/dist/utils/internal/error-handler/mappings.js.map +1 -1
- package/dist/utils/internal/error-handler/types.d.ts +2 -0
- package/dist/utils/internal/error-handler/types.d.ts.map +1 -1
- package/dist/utils/internal/logger.d.ts +75 -3
- package/dist/utils/internal/logger.d.ts.map +1 -1
- package/dist/utils/internal/logger.js +181 -52
- package/dist/utils/internal/logger.js.map +1 -1
- package/dist/utils/internal/performance.d.ts +11 -0
- package/dist/utils/internal/performance.d.ts.map +1 -1
- package/dist/utils/internal/performance.js +46 -12
- package/dist/utils/internal/performance.js.map +1 -1
- package/dist/utils/network/fetchWithTimeout.d.ts +11 -5
- package/dist/utils/network/fetchWithTimeout.d.ts.map +1 -1
- package/dist/utils/network/fetchWithTimeout.js +50 -23
- package/dist/utils/network/fetchWithTimeout.js.map +1 -1
- package/dist/utils/network/pacer.d.ts +38 -5
- package/dist/utils/network/pacer.d.ts.map +1 -1
- package/dist/utils/network/pacer.js +87 -25
- package/dist/utils/network/pacer.js.map +1 -1
- package/dist/utils/network/retry.d.ts +16 -8
- package/dist/utils/network/retry.d.ts.map +1 -1
- package/dist/utils/network/retry.js +19 -8
- package/dist/utils/network/retry.js.map +1 -1
- package/dist/utils/overflow/outlineOnOverflow.d.ts +18 -2
- package/dist/utils/overflow/outlineOnOverflow.d.ts.map +1 -1
- package/dist/utils/overflow/outlineOnOverflow.js +28 -3
- package/dist/utils/overflow/outlineOnOverflow.js.map +1 -1
- package/dist/utils/pagination/pagination.d.ts +3 -1
- package/dist/utils/pagination/pagination.d.ts.map +1 -1
- package/dist/utils/pagination/pagination.js +10 -2
- package/dist/utils/pagination/pagination.js.map +1 -1
- package/dist/utils/parsing/csvParser.d.ts.map +1 -1
- package/dist/utils/parsing/csvParser.js +4 -2
- package/dist/utils/parsing/csvParser.js.map +1 -1
- package/dist/utils/parsing/htmlExtractor.js +1 -1
- package/dist/utils/parsing/htmlExtractor.js.map +1 -1
- package/dist/utils/parsing/jsonParser.d.ts.map +1 -1
- package/dist/utils/parsing/jsonParser.js +3 -1
- package/dist/utils/parsing/jsonParser.js.map +1 -1
- package/dist/utils/parsing/xmlParser.d.ts.map +1 -1
- package/dist/utils/parsing/xmlParser.js +3 -1
- package/dist/utils/parsing/xmlParser.js.map +1 -1
- package/dist/utils/parsing/yamlParser.d.ts.map +1 -1
- package/dist/utils/parsing/yamlParser.js +3 -1
- package/dist/utils/parsing/yamlParser.js.map +1 -1
- package/dist/utils/security/idGenerator.d.ts.map +1 -1
- package/dist/utils/security/idGenerator.js +20 -4
- package/dist/utils/security/idGenerator.js.map +1 -1
- package/dist/utils/security/sanitization.d.ts +31 -0
- package/dist/utils/security/sanitization.d.ts.map +1 -1
- package/dist/utils/security/sanitization.js +98 -11
- package/dist/utils/security/sanitization.js.map +1 -1
- package/dist/utils/telemetry/attributes.d.ts +21 -2
- package/dist/utils/telemetry/attributes.d.ts.map +1 -1
- package/dist/utils/telemetry/attributes.js +21 -2
- package/dist/utils/telemetry/attributes.js.map +1 -1
- package/dist/utils/telemetry/instrumentation.d.ts +9 -3
- package/dist/utils/telemetry/instrumentation.d.ts.map +1 -1
- package/dist/utils/telemetry/instrumentation.js +85 -13
- package/dist/utils/telemetry/instrumentation.js.map +1 -1
- package/framework-skills/add-app-tool/SKILL.md +3 -3
- package/framework-skills/add-export/SKILL.md +5 -16
- package/framework-skills/add-prompt/SKILL.md +7 -3
- package/framework-skills/add-resource/SKILL.md +7 -5
- package/framework-skills/add-tool/SKILL.md +12 -10
- package/framework-skills/api-auth/SKILL.md +4 -2
- package/framework-skills/api-canvas/SKILL.md +19 -10
- package/framework-skills/api-config/SKILL.md +9 -6
- package/framework-skills/api-context/SKILL.md +16 -5
- package/framework-skills/api-errors/SKILL.md +23 -17
- package/framework-skills/api-linter/SKILL.md +32 -9
- package/framework-skills/api-mirror/SKILL.md +2 -1
- package/framework-skills/api-telemetry/SKILL.md +34 -14
- package/framework-skills/api-testing/SKILL.md +5 -3
- package/framework-skills/api-utils/SKILL.md +10 -10
- package/framework-skills/api-utils/references/formatting.md +1 -1
- package/framework-skills/api-utils/references/parsing.md +2 -2
- package/framework-skills/api-utils/references/security.md +6 -4
- package/framework-skills/design-mcp-server/SKILL.md +2 -2
- package/framework-skills/field-test/SKILL.md +4 -4
- package/framework-skills/git-wrapup/SKILL.md +12 -7
- package/framework-skills/maintenance/SKILL.md +2 -2
- package/framework-skills/orchestrations/SKILL.md +7 -6
- package/framework-skills/orchestrations/workflows/field-test-fix.md +9 -19
- package/framework-skills/orchestrations/workflows/fix-wrapup-release.md +7 -7
- package/framework-skills/orchestrations/workflows/greenfield-build.md +8 -5
- package/framework-skills/orchestrations/workflows/maintenance-release.md +8 -8
- package/framework-skills/polish-docs-meta/SKILL.md +4 -4
- package/framework-skills/polish-docs-meta/references/readme.md +1 -0
- package/framework-skills/release-and-publish/SKILL.md +7 -5
- package/framework-skills/release-pr-review/SKILL.md +37 -23
- package/framework-skills/report-issue-framework/SKILL.md +7 -5
- package/framework-skills/report-issue-local/SKILL.md +8 -6
- package/framework-skills/security-pass/SKILL.md +8 -8
- package/framework-skills/techniques/SKILL.md +1 -1
- package/framework-skills/techniques/references/outline-on-overflow.md +12 -7
- package/package.json +20 -5
- package/scripts/check-skill-versions.ts +103 -22
- package/scripts/devcheck.ts +11 -9
- package/scripts/lint-mcp.ts +87 -27
- package/scripts/lint-packaging.ts +99 -1
- package/scripts/release-github.ts +117 -5
- package/templates/.env.example +4 -0
- package/templates/Dockerfile +26 -6
- package/templates/_.mcpbignore +2 -0
- package/templates/package.json +1 -0
package/scripts/lint-mcp.ts
CHANGED
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
* 3. Extracts exported definitions by duck-typing (has name/handler/input etc.)
|
|
13
13
|
* 4. Feeds them into `validateDefinitions()`
|
|
14
14
|
*
|
|
15
|
+
* A definition file whose `import()` rejects, and a `server.json` that exists
|
|
16
|
+
* but does not parse, are errors (`definition-import-failed`,
|
|
17
|
+
* `server-json-parse`): what they declare cannot be checked, so the run fails
|
|
18
|
+
* instead of passing without them. The remaining files are still linted.
|
|
19
|
+
*
|
|
15
20
|
* Runtime-agnostic: works with bun, tsx, and Node.js (via ts-node/esm).
|
|
16
21
|
*
|
|
17
22
|
* Rule knobs come from the project's `devcheck.config.json` `lint` block, so one
|
|
@@ -20,7 +25,7 @@
|
|
|
20
25
|
* @module scripts/lint-mcp
|
|
21
26
|
*/
|
|
22
27
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
23
|
-
import { join, resolve } from 'node:path';
|
|
28
|
+
import { join, relative, resolve } from 'node:path';
|
|
24
29
|
import { fileURLToPath } from 'node:url';
|
|
25
30
|
|
|
26
31
|
// ---------------------------------------------------------------------------
|
|
@@ -100,19 +105,64 @@ function discoverFiles(): string[] {
|
|
|
100
105
|
return [...new Set(files)];
|
|
101
106
|
}
|
|
102
107
|
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Load failures
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
/** Where the rule reference lives — the breadcrumb `validateDefinitions()` appends too. */
|
|
113
|
+
const SKILL_REFERENCE_PATH = 'framework-skills/api-linter/SKILL.md';
|
|
114
|
+
|
|
115
|
+
/** A file the CLI could not load, shaped like the diagnostics it is printed beside. */
|
|
116
|
+
interface LoadFailure {
|
|
117
|
+
message: string;
|
|
118
|
+
rule: 'definition-import-failed' | 'server-json-parse';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The text of a thrown value. An `AggregateError` keeps its causes in `errors`
|
|
123
|
+
* — Bun rejects a definition that fails to transpile with one whose own message
|
|
124
|
+
* only counts them ("2 errors building …") — so each cause is appended.
|
|
125
|
+
*/
|
|
126
|
+
function describeError(err: unknown): string {
|
|
127
|
+
if (!(err instanceof Error)) return String(err);
|
|
128
|
+
const message = err.message || err.name;
|
|
129
|
+
if (!(err instanceof AggregateError) || err.errors.length === 0) return message;
|
|
130
|
+
return `${message} (${err.errors.map(describeError).join('; ')})`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function loadFailure(rule: LoadFailure['rule'], file: string, err: unknown): LoadFailure {
|
|
134
|
+
const anchor = rule === 'server-json-parse' ? 'server-json-rules' : rule;
|
|
135
|
+
return {
|
|
136
|
+
rule,
|
|
137
|
+
message: `${file}: ${describeError(err)}\nSee: ${SKILL_REFERENCE_PATH}#${anchor}`,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
103
141
|
// ---------------------------------------------------------------------------
|
|
104
142
|
// Main
|
|
105
143
|
// ---------------------------------------------------------------------------
|
|
106
144
|
|
|
107
|
-
/**
|
|
108
|
-
|
|
145
|
+
/** A parsed JSON file, or the error an existing one failed to read or parse with. */
|
|
146
|
+
type JsonRead = { ok: true; value: unknown } | { ok: false; error: unknown };
|
|
147
|
+
|
|
148
|
+
/** Reads and parses a JSON file. `undefined` when the file does not exist. */
|
|
149
|
+
function readJson(path: string): JsonRead | undefined {
|
|
150
|
+
if (!existsSync(path)) return;
|
|
109
151
|
try {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
152
|
+
return { ok: true, value: JSON.parse(readFileSync(path, 'utf-8')) };
|
|
153
|
+
} catch (error) {
|
|
154
|
+
return { ok: false, error };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Try to read and parse a JSON file. Returns undefined when absent, and warns when unparseable. */
|
|
159
|
+
function tryReadJson(path: string): unknown {
|
|
160
|
+
const read = readJson(path);
|
|
161
|
+
if (read?.ok === false) {
|
|
162
|
+
console.warn(`Warning: Failed to parse ${path}: ${describeError(read.error)}`);
|
|
114
163
|
return;
|
|
115
164
|
}
|
|
165
|
+
return read?.value;
|
|
116
166
|
}
|
|
117
167
|
|
|
118
168
|
/** The `lint` block of `devcheck.config.json`, as far as this script reads it. */
|
|
@@ -149,12 +199,17 @@ export function readLintOptions(configPath = resolve('devcheck.config.json')): L
|
|
|
149
199
|
|
|
150
200
|
async function main(): Promise<void> {
|
|
151
201
|
const files = discoverFiles();
|
|
202
|
+
const failures: LoadFailure[] = [];
|
|
152
203
|
|
|
153
204
|
// Discover server.json and package.json at project root
|
|
154
|
-
const
|
|
205
|
+
const serverJsonRead = readJson(resolve('server.json'));
|
|
206
|
+
if (serverJsonRead?.ok === false) {
|
|
207
|
+
failures.push(loadFailure('server-json-parse', 'server.json', serverJsonRead.error));
|
|
208
|
+
}
|
|
209
|
+
const serverJson = serverJsonRead?.ok ? serverJsonRead.value : undefined;
|
|
155
210
|
const packageJson = tryReadJson(resolve('package.json')) as { version?: string } | undefined;
|
|
156
211
|
|
|
157
|
-
if (files.length === 0 && serverJson == null) {
|
|
212
|
+
if (files.length === 0 && serverJson == null && failures.length === 0) {
|
|
158
213
|
console.log('No MCP definition files or server.json found. Skipping lint.');
|
|
159
214
|
process.exit(0);
|
|
160
215
|
}
|
|
@@ -162,36 +217,42 @@ async function main(): Promise<void> {
|
|
|
162
217
|
const tools: unknown[] = [];
|
|
163
218
|
const resources: unknown[] = [];
|
|
164
219
|
const prompts: unknown[] = [];
|
|
220
|
+
let failedImports = 0;
|
|
165
221
|
|
|
166
222
|
for (const file of files) {
|
|
223
|
+
let mod: Record<string, unknown>;
|
|
167
224
|
try {
|
|
168
|
-
|
|
169
|
-
for (const exported of Object.values(mod)) {
|
|
170
|
-
if (isToolLike(exported)) tools.push(exported);
|
|
171
|
-
else if (isResourceLike(exported)) resources.push(exported);
|
|
172
|
-
else if (isPromptLike(exported)) prompts.push(exported);
|
|
173
|
-
}
|
|
225
|
+
mod = await import(file);
|
|
174
226
|
} catch (err) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
227
|
+
failures.push(loadFailure('definition-import-failed', relative(process.cwd(), file), err));
|
|
228
|
+
failedImports++;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
for (const exported of Object.values(mod)) {
|
|
232
|
+
if (isToolLike(exported)) tools.push(exported);
|
|
233
|
+
else if (isResourceLike(exported)) resources.push(exported);
|
|
234
|
+
else if (isPromptLike(exported)) prompts.push(exported);
|
|
178
235
|
}
|
|
179
236
|
}
|
|
180
237
|
|
|
181
238
|
const defTotal = tools.length + resources.length + prompts.length;
|
|
182
|
-
if (defTotal === 0 && serverJson == null) {
|
|
239
|
+
if (defTotal === 0 && serverJson == null && failures.length === 0) {
|
|
183
240
|
console.log(`Scanned ${files.length} files but found no definitions. Skipping lint.`);
|
|
184
241
|
process.exit(0);
|
|
185
242
|
}
|
|
186
243
|
|
|
187
244
|
const parts: string[] = [];
|
|
188
|
-
if (defTotal > 0) {
|
|
245
|
+
if (defTotal > 0 || failedImports > 0) {
|
|
246
|
+
const fileCount =
|
|
247
|
+
failedImports === 0
|
|
248
|
+
? `${files.length}`
|
|
249
|
+
: `${files.length - failedImports} of ${files.length}`;
|
|
189
250
|
parts.push(
|
|
190
|
-
`${tools.length} tool(s), ${resources.length} resource(s), ${prompts.length} prompt(s) from ${
|
|
251
|
+
`${tools.length} tool(s), ${resources.length} resource(s), ${prompts.length} prompt(s) from ${fileCount} file(s)`,
|
|
191
252
|
);
|
|
192
253
|
}
|
|
193
254
|
if (serverJson != null) parts.push('server.json');
|
|
194
|
-
console.log(`Linting ${parts.join(' + ')}...`);
|
|
255
|
+
if (parts.length > 0) console.log(`Linting ${parts.join(' + ')}...`);
|
|
195
256
|
|
|
196
257
|
const report = validateDefinitions({
|
|
197
258
|
tools,
|
|
@@ -202,14 +263,15 @@ async function main(): Promise<void> {
|
|
|
202
263
|
...readLintOptions(),
|
|
203
264
|
});
|
|
204
265
|
|
|
266
|
+
const errors = [...failures, ...report.errors];
|
|
205
267
|
for (const w of report.warnings) {
|
|
206
268
|
console.warn(` ⚠ [${w.rule}] ${w.message}`);
|
|
207
269
|
}
|
|
208
|
-
for (const e of
|
|
270
|
+
for (const e of errors) {
|
|
209
271
|
console.error(` ✗ [${e.rule}] ${e.message}`);
|
|
210
272
|
}
|
|
211
273
|
|
|
212
|
-
if (
|
|
274
|
+
if (errors.length === 0) {
|
|
213
275
|
if (report.warnings.length > 0) {
|
|
214
276
|
console.log(`\nPassed with ${report.warnings.length} warning(s).`);
|
|
215
277
|
} else {
|
|
@@ -217,9 +279,7 @@ async function main(): Promise<void> {
|
|
|
217
279
|
}
|
|
218
280
|
process.exit(0);
|
|
219
281
|
} else {
|
|
220
|
-
console.error(
|
|
221
|
-
`\nFailed: ${report.errors.length} error(s), ${report.warnings.length} warning(s).`,
|
|
222
|
-
);
|
|
282
|
+
console.error(`\nFailed: ${errors.length} error(s), ${report.warnings.length} warning(s).`);
|
|
223
283
|
process.exit(1);
|
|
224
284
|
}
|
|
225
285
|
}
|
|
@@ -59,6 +59,22 @@
|
|
|
59
59
|
* package's headline version on GitHub and npmjs.com and ships in the
|
|
60
60
|
* tarball, so a half-finished bump is publicly visible. Skipped when the
|
|
61
61
|
* README, the badge, or the package version is absent (issue #418).
|
|
62
|
+
* 13. npm `files` excludes the built bundle: when `manifest.json` exists and
|
|
63
|
+
* `package.json` `files` covers `dist/` wholesale, it must also carry
|
|
64
|
+
* `"!dist/*.mcpb"`. The `bundle` script writes the `.mcpb` into `dist/`,
|
|
65
|
+
* so without the entry a release that bundles before publishing ships the
|
|
66
|
+
* server and its production dependencies inside the npm tarball
|
|
67
|
+
* (issue #469). Skipped when `manifest.json` or `files` is absent.
|
|
68
|
+
* 14. manifest.json version parity: `version` must equal `package.json`'s, the
|
|
69
|
+
* same rule check 10 applies to the plugin manifests — the bundle's install
|
|
70
|
+
* dialog shows it. Skipped when `manifest.json` or the package version is
|
|
71
|
+
* absent.
|
|
72
|
+
* 15. Dockerfile build platform: every stage that runs `bun run build` must
|
|
73
|
+
* start `FROM --platform=$BUILDPLATFORM`. Without it the non-native leg of
|
|
74
|
+
* a multi-arch `docker buildx` build runs under QEMU, where bun >= 1.4
|
|
75
|
+
* aborts inside the build and no image publishes for either architecture —
|
|
76
|
+
* and the image publishes last, after npm and the MCP Registry. Skipped
|
|
77
|
+
* when there is no `Dockerfile` or no stage builds.
|
|
62
78
|
*
|
|
63
79
|
* Every check skips cleanly when its input is absent — consumers who deleted
|
|
64
80
|
* `manifest.json` for an HTTP-only deploy, or who haven't built a bundle,
|
|
@@ -97,6 +113,7 @@ interface Manifest {
|
|
|
97
113
|
name?: string;
|
|
98
114
|
server?: { mcp_config?: { args?: unknown[]; env?: Record<string, string> } };
|
|
99
115
|
user_config?: Record<string, ManifestUserConfigEntry>;
|
|
116
|
+
version?: unknown;
|
|
100
117
|
}
|
|
101
118
|
|
|
102
119
|
const USER_CONFIG_REF = /^\$\{user_config\.([\w-]+)\}$/;
|
|
@@ -742,6 +759,76 @@ export function checkReadmeVersionBadge(readme: string, packageVersion?: string)
|
|
|
742
759
|
];
|
|
743
760
|
}
|
|
744
761
|
|
|
762
|
+
/** The `files` entry that keeps the `bundle` script's `dist/<name>.mcpb` out of the npm tarball. */
|
|
763
|
+
const BUNDLE_EXCLUSION = '!dist/*.mcpb';
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Check 13: a `files` allowlist that covers `dist/` wholesale must exclude the
|
|
767
|
+
* `.mcpb` the `bundle` script writes there. npm and Bun both honor `!` entries
|
|
768
|
+
* in `files`, and both ignore `.npmignore` once `files` is set, so the entry is
|
|
769
|
+
* the only place the exclusion can live. The caller runs this only when
|
|
770
|
+
* `manifest.json` exists — a project without one never builds a bundle.
|
|
771
|
+
*/
|
|
772
|
+
export function checkBundleExcludedFromFiles(files: unknown): string[] {
|
|
773
|
+
if (!Array.isArray(files)) return [];
|
|
774
|
+
const entries = files.filter((entry): entry is string => typeof entry === 'string');
|
|
775
|
+
|
|
776
|
+
const coversDist = entries.some(
|
|
777
|
+
(entry) => entry.replace(/^\.\//, '').replace(/\/(?:\*\*(?:\/\*)?|\*)?$/, '') === 'dist',
|
|
778
|
+
);
|
|
779
|
+
if (!coversDist) return [];
|
|
780
|
+
if (entries.includes(BUNDLE_EXCLUSION) || entries.includes('!dist/**/*.mcpb')) return [];
|
|
781
|
+
|
|
782
|
+
return [
|
|
783
|
+
`package.json "files" covers dist/ without "${BUNDLE_EXCLUSION}" — the bundle script writes ` +
|
|
784
|
+
`dist/<name>.mcpb, so a release that bundles before publishing ships it in the npm tarball; ` +
|
|
785
|
+
`add "${BUNDLE_EXCLUSION}" to "files"`,
|
|
786
|
+
];
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* Check 14: manifest.json `version` must equal `package.json`'s. Skipped when
|
|
791
|
+
* the package version is absent — the same fail-safe as checks 10 and 12.
|
|
792
|
+
*/
|
|
793
|
+
export function checkManifestVersion(manifest: Manifest, packageVersion?: string): string[] {
|
|
794
|
+
if (!packageVersion || manifest.version === packageVersion) return [];
|
|
795
|
+
return [
|
|
796
|
+
manifest.version === undefined
|
|
797
|
+
? `manifest.json has no "version" — must declare the package.json version "${packageVersion}"`
|
|
798
|
+
: `manifest.json "version" is "${String(manifest.version)}" — must equal the package.json version "${packageVersion}"`,
|
|
799
|
+
];
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const DOCKERFILE_FROM = /^\s*FROM\s/i;
|
|
803
|
+
const DOCKERFILE_BUILD_PLATFORM = /--platform=\$\{?BUILDPLATFORM\}?(?:\s|$)/;
|
|
804
|
+
const DOCKERFILE_BUILD_STEP = /\bbun run (?:re)?build\b/;
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Check 15: every Dockerfile stage that runs `bun run build` must be pinned to
|
|
808
|
+
* the build platform. Stages are split at `FROM` lines; comment lines are
|
|
809
|
+
* ignored so a note mentioning the build does not count as one.
|
|
810
|
+
*/
|
|
811
|
+
export function checkDockerfileBuildPlatform(dockerfile: string): string[] {
|
|
812
|
+
const stages: { from: string; line: number; builds: boolean }[] = [];
|
|
813
|
+
for (const [index, line] of dockerfile.split('\n').entries()) {
|
|
814
|
+
if (DOCKERFILE_FROM.test(line)) {
|
|
815
|
+
stages.push({ from: line.trim(), line: index + 1, builds: false });
|
|
816
|
+
} else if (!line.trimStart().startsWith('#') && DOCKERFILE_BUILD_STEP.test(line)) {
|
|
817
|
+
const stage = stages.at(-1);
|
|
818
|
+
if (stage) stage.builds = true;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
return stages
|
|
823
|
+
.filter((stage) => stage.builds && !DOCKERFILE_BUILD_PLATFORM.test(stage.from))
|
|
824
|
+
.map(
|
|
825
|
+
(stage) =>
|
|
826
|
+
`Dockerfile:${stage.line} "${stage.from}" runs \`bun run build\` without --platform=$BUILDPLATFORM — ` +
|
|
827
|
+
`a multi-arch buildx build then runs it under QEMU, where bun aborts and no image publishes; ` +
|
|
828
|
+
`start the build stage with "FROM --platform=$BUILDPLATFORM" and copy dist/ into a separate runtime stage`,
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
|
|
745
832
|
/** Read `packaging.pluginManifests` from devcheck.config.json; default on. */
|
|
746
833
|
function pluginManifestsEnabled(): boolean {
|
|
747
834
|
const cfg = tryReadJson<{ packaging?: { pluginManifests?: boolean } }>(
|
|
@@ -755,7 +842,9 @@ async function main(): Promise<void> {
|
|
|
755
842
|
const warnings: string[] = [];
|
|
756
843
|
const notes: string[] = [];
|
|
757
844
|
|
|
758
|
-
const pkg = tryReadJson<{ name?: string; version?: string }>(
|
|
845
|
+
const pkg = tryReadJson<{ files?: unknown; name?: string; version?: string }>(
|
|
846
|
+
resolve('package.json'),
|
|
847
|
+
);
|
|
759
848
|
const unscopedName = pkg?.name?.split('/').pop();
|
|
760
849
|
|
|
761
850
|
// ── Manifest-dependent checks (1–4 + manifest identity) ──
|
|
@@ -826,6 +915,9 @@ async function main(): Promise<void> {
|
|
|
826
915
|
if (unscopedName) {
|
|
827
916
|
errors.push(...checkManifestIdentity(manifest, unscopedName));
|
|
828
917
|
}
|
|
918
|
+
|
|
919
|
+
errors.push(...checkManifestVersion(manifest, pkg?.version));
|
|
920
|
+
errors.push(...checkBundleExcludedFromFiles(pkg?.files));
|
|
829
921
|
} else {
|
|
830
922
|
notes.push('No manifest.json — skipping manifest/server.json alignment checks.');
|
|
831
923
|
}
|
|
@@ -874,6 +966,12 @@ async function main(): Promise<void> {
|
|
|
874
966
|
}
|
|
875
967
|
}
|
|
876
968
|
|
|
969
|
+
// ── Dockerfile build platform (check 15) ──
|
|
970
|
+
const dockerfilePath = resolve('Dockerfile');
|
|
971
|
+
if (existsSync(dockerfilePath)) {
|
|
972
|
+
errors.push(...checkDockerfileBuildPlatform(readFileSync(dockerfilePath, 'utf-8')));
|
|
973
|
+
}
|
|
974
|
+
|
|
877
975
|
// ── README version badge (check 12) ──
|
|
878
976
|
const readmePath = resolve('README.md');
|
|
879
977
|
if (existsSync(readmePath)) {
|
|
@@ -18,6 +18,15 @@
|
|
|
18
18
|
* The framework itself has no `manifest.json`/`.mcpb`, so the attach path is
|
|
19
19
|
* skipped here but scaffolded servers that do have a manifest get the full flow.
|
|
20
20
|
*
|
|
21
|
+
* Before any `gh` call it validates the tag — `--notes-from-tag` publishes the
|
|
22
|
+
* message verbatim as the release body, so a malformed tag becomes a malformed
|
|
23
|
+
* public release. `--check` runs only that validation, for use right after
|
|
24
|
+
* `git tag -a` and before the tag is pushed. The rules are the ones the
|
|
25
|
+
* `release-and-publish` skill states for the annotation: an annotated tag, a
|
|
26
|
+
* subject of at most 72 characters with no version and no `;`, flat bullets
|
|
27
|
+
* with no section headers, no signature block leaked into the body, and the
|
|
28
|
+
* `[CHANGELOG v<version>](…)` link as the final line.
|
|
29
|
+
*
|
|
21
30
|
* @module scripts/release-github
|
|
22
31
|
*
|
|
23
32
|
* @example
|
|
@@ -25,6 +34,10 @@
|
|
|
25
34
|
* // bun run release:github
|
|
26
35
|
*
|
|
27
36
|
* @example
|
|
37
|
+
* // Validate the tag annotation only (exit 1 on a violation, no gh calls):
|
|
38
|
+
* // bun run release:github -- --check
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
28
41
|
* // Dry-run — print the command that would be executed without running it:
|
|
29
42
|
* // bun run release:github -- --dry-run
|
|
30
43
|
*/
|
|
@@ -33,8 +46,86 @@ import { spawnSync } from 'node:child_process';
|
|
|
33
46
|
import { existsSync, readFileSync } from 'node:fs';
|
|
34
47
|
import { resolve } from 'node:path';
|
|
35
48
|
import process from 'node:process';
|
|
49
|
+
import { fileURLToPath } from 'node:url';
|
|
36
50
|
|
|
37
51
|
const DRY_RUN = process.argv.includes('--dry-run');
|
|
52
|
+
const CHECK_ONLY = process.argv.includes('--check');
|
|
53
|
+
|
|
54
|
+
/** A tag subject longer than this reads as a digest, not a release title. */
|
|
55
|
+
const MAX_SUBJECT_LENGTH = 72;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Section headers belong in the changelog entry, never in the tag body: a
|
|
59
|
+
* markdown heading, a Keep a Changelog section name, or any other line that is
|
|
60
|
+
* not a bullet and ends in a colon (`Dependency bumps:`, `Highlights:`).
|
|
61
|
+
*/
|
|
62
|
+
const SECTION_HEADER =
|
|
63
|
+
/^(?:#{1,6}\s.*|(?:Added|Changed|Deprecated|Removed|Fixed|Security|Dependencies)\s*:?|[^\s\-*+[].*:)$/;
|
|
64
|
+
|
|
65
|
+
/** The parts of an annotated tag that become the GitHub Release title and body. */
|
|
66
|
+
export interface TagMessage {
|
|
67
|
+
body: string;
|
|
68
|
+
/** `git for-each-ref %(objecttype)` — `tag` for an annotated tag, `commit` for a lightweight one. */
|
|
69
|
+
objectType: string;
|
|
70
|
+
subject: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Validates a tag annotation against the release-body rules. Returns one
|
|
75
|
+
* message per violation; an empty array means the tag is publishable.
|
|
76
|
+
*/
|
|
77
|
+
export function checkTagMessage(tag: TagMessage, version: string): string[] {
|
|
78
|
+
if (tag.objectType !== 'tag') {
|
|
79
|
+
return [
|
|
80
|
+
`v${version} is a lightweight tag — recreate it annotated: git tag -a v${version} -F <file>`,
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const errors: string[] = [];
|
|
85
|
+
const { subject } = tag;
|
|
86
|
+
if (subject.length > MAX_SUBJECT_LENGTH) {
|
|
87
|
+
errors.push(
|
|
88
|
+
`subject is ${subject.length} characters — keep it one short theme (≤${MAX_SUBJECT_LENGTH}); the bullets carry the digest`,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
if (subject.includes(version)) {
|
|
92
|
+
errors.push(
|
|
93
|
+
`subject contains the version "${version}" — GitHub prepends "v${version}:" to the title`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
if (subject.includes(';')) {
|
|
97
|
+
errors.push('subject contains ";" — one theme, not a list of changes');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (tag.body.includes('-----BEGIN')) {
|
|
101
|
+
errors.push(
|
|
102
|
+
'body contains a signature block — the signature did not parse (usually --cleanup=verbatim); recreate the tag with --cleanup=whitespace before it is pushed',
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const lines = tag.body
|
|
107
|
+
.split('\n')
|
|
108
|
+
.map((line) => line.trim())
|
|
109
|
+
.filter((line) => line.length > 0);
|
|
110
|
+
const headers = lines.filter((line) => SECTION_HEADER.test(line));
|
|
111
|
+
if (headers.length > 0) {
|
|
112
|
+
errors.push(
|
|
113
|
+
`body has section headers (${headers.map((h) => `"${h}"`).join(', ')}) — flat bullets only; sections belong in the changelog entry`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const escaped = version.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
118
|
+
const changelogLink = new RegExp(
|
|
119
|
+
`^\\[CHANGELOG v${escaped}\\]\\(\\S+/changelog/\\d+\\.\\d+\\.x/${escaped}\\.md\\)`,
|
|
120
|
+
);
|
|
121
|
+
if (!changelogLink.test(lines.at(-1) ?? '')) {
|
|
122
|
+
errors.push(
|
|
123
|
+
`final line is not the changelog link — end the body with "[CHANGELOG v${version}](https://github.com/<OWNER>/<REPO>/blob/main/changelog/<major.minor>.x/${version}.md)"`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return errors;
|
|
128
|
+
}
|
|
38
129
|
|
|
39
130
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
40
131
|
|
|
@@ -107,15 +198,34 @@ function main(): void {
|
|
|
107
198
|
if (!subject) {
|
|
108
199
|
console.error(
|
|
109
200
|
`Tag ${tag} not found locally or has no subject line. ` +
|
|
110
|
-
`Create the annotated tag first: git tag -a ${tag} -
|
|
201
|
+
`Create the annotated tag first: git tag -a ${tag} -F <file>`,
|
|
111
202
|
);
|
|
112
203
|
process.exit(1);
|
|
113
204
|
}
|
|
114
205
|
|
|
206
|
+
// 3. Validate the annotation — it becomes the public release body verbatim
|
|
207
|
+
const errors = checkTagMessage(
|
|
208
|
+
{
|
|
209
|
+
subject,
|
|
210
|
+
body: run('git', ['for-each-ref', `refs/tags/${tag}`, '--format=%(contents:body)']),
|
|
211
|
+
objectType: run('git', ['for-each-ref', `refs/tags/${tag}`, '--format=%(objecttype)']),
|
|
212
|
+
},
|
|
213
|
+
version,
|
|
214
|
+
);
|
|
215
|
+
if (errors.length > 0) {
|
|
216
|
+
console.error(`Tag ${tag} is not publishable:`);
|
|
217
|
+
for (const error of errors) console.error(` ✗ ${error}`);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
if (CHECK_ONLY) {
|
|
221
|
+
console.log(`Tag ${tag} OK.`);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
115
225
|
const title = `${tag}: ${subject}`;
|
|
116
226
|
const hasMcpb = existsSync('manifest.json');
|
|
117
227
|
|
|
118
|
-
//
|
|
228
|
+
// 4. Build the gh release create command
|
|
119
229
|
const createArgs = [
|
|
120
230
|
'release',
|
|
121
231
|
'create',
|
|
@@ -152,7 +262,7 @@ function main(): void {
|
|
|
152
262
|
console.log(' asset: dist/*.mcpb');
|
|
153
263
|
}
|
|
154
264
|
|
|
155
|
-
//
|
|
265
|
+
// 5. Try to create the release
|
|
156
266
|
const createResult = gh(createArgs, { required: false });
|
|
157
267
|
|
|
158
268
|
if (!createResult.startsWith('__ERROR__:')) {
|
|
@@ -170,7 +280,7 @@ function main(): void {
|
|
|
170
280
|
process.exit(1);
|
|
171
281
|
}
|
|
172
282
|
|
|
173
|
-
//
|
|
283
|
+
// 6. Release already exists — repair: upload asset (if applicable) and set title.
|
|
174
284
|
console.log(`Release ${tag} already exists. Repairing…`);
|
|
175
285
|
|
|
176
286
|
if (hasMcpb) {
|
|
@@ -184,4 +294,6 @@ function main(): void {
|
|
|
184
294
|
console.log(`Release ${tag} repaired.`);
|
|
185
295
|
}
|
|
186
296
|
|
|
187
|
-
|
|
297
|
+
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
298
|
+
main();
|
|
299
|
+
}
|
package/templates/.env.example
CHANGED
|
@@ -31,12 +31,16 @@ MCP_SESSION_MODE=stateless # stateful | stateless | auto. Set here, not
|
|
|
31
31
|
# MCP_LOG_LEVEL=info # debug | info | notice | warning | error
|
|
32
32
|
# MCP_LOG_RATE_LIMIT_THRESHOLD=10 # Max emissions per level+message per window; 0 disables
|
|
33
33
|
# MCP_LOG_RATE_LIMIT_WINDOW_MS=60000
|
|
34
|
+
# LOG_TOOL_FAILURE_PAYLOADS=false # Log failed tool calls' arguments + result (key-name redaction only;
|
|
35
|
+
# secrets in free-form values are kept). Reaches stderr, files, and OTLP
|
|
36
|
+
# LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES=16384 # Per-payload cap, UTF-8 bytes
|
|
34
37
|
|
|
35
38
|
# ── Telemetry ─────────────────────────────────────────────────────────
|
|
36
39
|
# OTEL_ENABLED=false # Enable OpenTelemetry (default: false)
|
|
37
40
|
# OTEL_EXPORTER_OTLP_ENDPOINT= # OTLP base URL (e.g., http://localhost:4318); traces → /v1/traces, metrics → /v1/metrics
|
|
38
41
|
# OTEL_EXPORTER_OTLP_TRACES_ENDPOINT= # Overrides the base for traces; used as-is
|
|
39
42
|
# OTEL_EXPORTER_OTLP_METRICS_ENDPOINT= # Overrides the base for metrics; used as-is
|
|
43
|
+
# OTEL_EXPORTER_OTLP_LOGS_ENDPOINT= # Opt-in log export (e.g., http://localhost:4318/v1/logs); the base never enables it
|
|
40
44
|
|
|
41
45
|
# ── Server-specific ──────────────────────────────────────────────────
|
|
42
46
|
# Add your server's environment variables below
|
package/templates/Dockerfile
CHANGED
|
@@ -56,8 +56,15 @@ LABEL org.opencontainers.image.licenses="Apache-2.0"
|
|
|
56
56
|
LABEL org.opencontainers.image.version="${APP_VERSION}"
|
|
57
57
|
LABEL org.opencontainers.image.source=""
|
|
58
58
|
|
|
59
|
-
# Copy dependency manifests
|
|
60
|
-
|
|
59
|
+
# Copy dependency manifests. `bunfig.toml` rides along so every install below
|
|
60
|
+
# passes its release-age gate and security scanner, as a local install does.
|
|
61
|
+
COPY package.json bun.lock bunfig.toml ./
|
|
62
|
+
|
|
63
|
+
# The scanner bunfig.toml names is a devDependency, and Bun installs a missing
|
|
64
|
+
# scanner through the same production-filtered install, which omits it and
|
|
65
|
+
# aborts. Seed it from the build stage's full install instead. Remove this line
|
|
66
|
+
# together with the scanner if bunfig.toml stops naming one.
|
|
67
|
+
COPY --from=build /usr/src/app/node_modules/@socketsecurity/bun-security-scanner ./node_modules/@socketsecurity/bun-security-scanner
|
|
61
68
|
|
|
62
69
|
# Install only production dependencies, ignoring any lifecycle scripts (like 'prepare')
|
|
63
70
|
# that are not needed in the final production image.
|
|
@@ -72,19 +79,33 @@ RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
|
72
79
|
# Conditionally install OpenTelemetry optional peer dependencies (Tier 3).
|
|
73
80
|
# Installed by default. Omit them for a leaner image at build time
|
|
74
81
|
# with: docker build --build-arg OTEL_ENABLED=false
|
|
82
|
+
# Each package is requested at the range the installed framework declares in
|
|
83
|
+
# `peerDependencies`, so the resolution stays inside the framework's tested
|
|
84
|
+
# peer range; a name with no declared range fails the build.
|
|
75
85
|
ARG OTEL_ENABLED=true
|
|
76
86
|
RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
77
87
|
if [ "$OTEL_ENABLED" = "true" ]; then \
|
|
78
|
-
bun
|
|
79
|
-
|
|
88
|
+
specs=$(bun -e ' \
|
|
89
|
+
const { peerDependencies: peers } = await Bun.file("node_modules/@cyanheads/mcp-ts-core/package.json").json(); \
|
|
90
|
+
const names = process.argv.slice(1); \
|
|
91
|
+
const missing = names.filter((name) => !peers?.[name]); \
|
|
92
|
+
if (missing.length > 0) throw new Error(`no peerDependencies range for ${missing.join(", ")}`); \
|
|
93
|
+
console.log(names.map((name) => `${name}@${peers[name]}`).join(" ")); \
|
|
94
|
+
' \
|
|
95
|
+
@hono/otel \
|
|
96
|
+
@opentelemetry/api-logs \
|
|
97
|
+
@opentelemetry/exporter-logs-otlp-http \
|
|
80
98
|
@opentelemetry/exporter-metrics-otlp-http \
|
|
81
99
|
@opentelemetry/exporter-trace-otlp-http \
|
|
100
|
+
@opentelemetry/instrumentation-http \
|
|
82
101
|
@opentelemetry/instrumentation-pino \
|
|
83
102
|
@opentelemetry/resources \
|
|
103
|
+
@opentelemetry/sdk-logs \
|
|
84
104
|
@opentelemetry/sdk-metrics \
|
|
85
105
|
@opentelemetry/sdk-node \
|
|
86
106
|
@opentelemetry/sdk-trace-node \
|
|
87
|
-
@opentelemetry/semantic-conventions
|
|
107
|
+
@opentelemetry/semantic-conventions) \
|
|
108
|
+
&& bun add --omit=dev --omit=peer --ignore-scripts $specs; \
|
|
88
109
|
fi
|
|
89
110
|
|
|
90
111
|
# Copy the compiled application code from the build stage
|
|
@@ -128,7 +149,6 @@ ENV MCP_TRANSPORT_TYPE="http"
|
|
|
128
149
|
ENV MCP_SESSION_MODE="stateless"
|
|
129
150
|
ENV MCP_LOG_LEVEL="info"
|
|
130
151
|
ENV LOGS_DIR="/var/log/{{PACKAGE_NAME}}"
|
|
131
|
-
ENV MCP_FORCE_CONSOLE_LOGGING="true"
|
|
132
152
|
|
|
133
153
|
# Expose the port the server listens on
|
|
134
154
|
EXPOSE ${MCP_HTTP_PORT}
|
package/templates/_.mcpbignore
CHANGED