@cyanheads/mcp-ts-core 0.13.6 → 0.13.8
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 +5 -5
- package/CLAUDE.md +5 -5
- package/README.md +57 -52
- package/biome.json +1 -1
- package/changelog/0.13.x/0.13.7.md +77 -0
- package/changelog/0.13.x/0.13.8.md +101 -0
- package/config/tsconfig.base.json +2 -2
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +61 -11
- package/dist/config/index.js.map +1 -1
- package/dist/core/app.d.ts.map +1 -1
- package/dist/core/app.js +35 -6
- package/dist/core/app.js.map +1 -1
- package/dist/core/context.d.ts +9 -1
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +17 -16
- package/dist/core/context.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 +9 -1
- 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/mcp-server/handlerContext.d.ts +6 -0
- package/dist/mcp-server/handlerContext.d.ts.map +1 -1
- package/dist/mcp-server/handlerContext.js +3 -0
- package/dist/mcp-server/handlerContext.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/prompts/prompt-registration.d.ts.map +1 -1
- package/dist/mcp-server/prompts/prompt-registration.js +6 -3
- package/dist/mcp-server/prompts/prompt-registration.js.map +1 -1
- 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/toolHandlerFactory.d.ts +16 -5
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js +70 -14
- 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/httpErrorHandler.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/httpErrorHandler.js +15 -5
- package/dist/mcp-server/transports/http/httpErrorHandler.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/services/canvas/core/CanvasRegistry.js +1 -1
- 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.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +25 -16
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.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/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/IStorageProvider.d.ts +5 -2
- package/dist/storage/core/IStorageProvider.d.ts.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/providerHelpers.d.ts +29 -8
- package/dist/storage/core/providerHelpers.d.ts.map +1 -1
- package/dist/storage/core/providerHelpers.js +49 -11
- package/dist/storage/core/providerHelpers.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 +9 -7
- package/dist/storage/providers/cloudflare/d1Provider.js.map +1 -1
- package/dist/storage/providers/cloudflare/kvProvider.d.ts +2 -0
- package/dist/storage/providers/cloudflare/kvProvider.d.ts.map +1 -1
- package/dist/storage/providers/cloudflare/kvProvider.js +12 -10
- 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 +11 -8
- package/dist/storage/providers/cloudflare/r2Provider.js.map +1 -1
- package/dist/storage/providers/fileSystem/fileSystemProvider.d.ts +1 -0
- package/dist/storage/providers/fileSystem/fileSystemProvider.d.ts.map +1 -1
- package/dist/storage/providers/fileSystem/fileSystemProvider.js +14 -12
- package/dist/storage/providers/fileSystem/fileSystemProvider.js.map +1 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.d.ts +6 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.d.ts.map +1 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.js +15 -10
- package/dist/storage/providers/inMemory/inMemoryProvider.js.map +1 -1
- package/dist/storage/providers/supabase/supabaseProvider.d.ts.map +1 -1
- package/dist/storage/providers/supabase/supabaseProvider.js +5 -1
- package/dist/storage/providers/supabase/supabaseProvider.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 +21 -6
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +57 -10
- 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/partialResult.d.ts +28 -2
- package/dist/utils/formatting/partialResult.d.ts.map +1 -1
- package/dist/utils/formatting/partialResult.js +46 -2
- package/dist/utils/formatting/partialResult.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/internal/error-handler/errorHandler.d.ts +21 -8
- package/dist/utils/internal/error-handler/errorHandler.d.ts.map +1 -1
- package/dist/utils/internal/error-handler/errorHandler.js +70 -38
- package/dist/utils/internal/error-handler/errorHandler.js.map +1 -1
- package/dist/utils/internal/error-handler/mappings.d.ts +18 -1
- package/dist/utils/internal/error-handler/mappings.d.ts.map +1 -1
- package/dist/utils/internal/error-handler/mappings.js +23 -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 +16 -1
- package/dist/utils/internal/performance.d.ts.map +1 -1
- package/dist/utils/internal/performance.js +59 -20
- 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/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 +46 -15
- package/dist/utils/security/sanitization.d.ts.map +1 -1
- package/dist/utils/security/sanitization.js +203 -96
- package/dist/utils/security/sanitization.js.map +1 -1
- package/dist/utils/telemetry/attributes.d.ts +16 -1
- package/dist/utils/telemetry/attributes.d.ts.map +1 -1
- package/dist/utils/telemetry/attributes.js +16 -1
- package/dist/utils/telemetry/attributes.js.map +1 -1
- package/dist/utils/telemetry/instrumentation.d.ts +13 -3
- package/dist/utils/telemetry/instrumentation.d.ts.map +1 -1
- package/dist/utils/telemetry/instrumentation.js +104 -17
- package/dist/utils/telemetry/instrumentation.js.map +1 -1
- package/framework-skills/add-app-tool/SKILL.md +12 -18
- package/framework-skills/add-prompt/SKILL.md +3 -1
- package/framework-skills/add-provider/SKILL.md +14 -4
- package/framework-skills/add-resource/SKILL.md +3 -3
- package/framework-skills/add-tool/SKILL.md +22 -7
- package/framework-skills/api-auth/SKILL.md +3 -1
- package/framework-skills/api-canvas/SKILL.md +4 -4
- package/framework-skills/api-config/SKILL.md +9 -5
- package/framework-skills/api-context/SKILL.md +10 -7
- package/framework-skills/api-errors/SKILL.md +20 -11
- package/framework-skills/api-linter/SKILL.md +14 -10
- package/framework-skills/api-telemetry/SKILL.md +38 -13
- package/framework-skills/api-testing/SKILL.md +25 -15
- 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 +13 -10
- package/framework-skills/code-simplifier/SKILL.md +31 -18
- package/framework-skills/design-mcp-server/SKILL.md +62 -35
- package/framework-skills/git-wrapup/SKILL.md +19 -10
- package/framework-skills/maintenance/SKILL.md +3 -3
- package/framework-skills/orchestrations/SKILL.md +1 -1
- package/framework-skills/orchestrations/workflows/greenfield-build.md +15 -8
- package/framework-skills/polish-docs-meta/SKILL.md +2 -2
- package/framework-skills/polish-docs-meta/references/package-meta.md +1 -1
- package/framework-skills/polish-docs-meta/references/readme.md +4 -3
- package/framework-skills/release-and-publish/SKILL.md +7 -5
- package/framework-skills/release-pr-review/SKILL.md +18 -1
- package/framework-skills/report-issue-framework/SKILL.md +2 -2
- package/framework-skills/report-issue-local/SKILL.md +3 -3
- package/framework-skills/security-pass/SKILL.md +11 -3
- package/framework-skills/techniques/SKILL.md +1 -1
- package/framework-skills/techniques/references/outline-on-overflow.md +12 -7
- package/framework-skills/tool-defs-analysis/SKILL.md +3 -3
- package/package.json +30 -36
- package/scripts/check-skill-versions.ts +103 -22
- package/scripts/devcheck.ts +4 -3
- package/scripts/lint-packaging.ts +38 -1
- package/templates/.env.example +7 -1
- package/templates/.github/ISSUE_TEMPLATE/feature_request.yml +1 -1
- package/templates/AGENTS.md +2 -2
- package/templates/CLAUDE.md +2 -2
- package/templates/Dockerfile +30 -10
- package/templates/package.json +4 -3
- package/templates/src/mcp-server/prompts/definitions/echo.prompt.ts +2 -4
- package/templates/src/mcp-server/resources/definitions/echo-app-ui.app-resource.ts +51 -14
- package/templates/src/mcp-server/resources/definitions/echo.resource.ts +1 -1
- package/templates/src/mcp-server/tools/definitions/echo-app.app-tool.ts +2 -3
- package/templates/src/mcp-server/tools/definitions/echo.tool.ts +1 -1
- package/dist/utils/telemetry/index.d.ts +0 -12
- package/dist/utils/telemetry/index.d.ts.map +0 -1
- package/dist/utils/telemetry/index.js +0 -12
- package/dist/utils/telemetry/index.js.map +0 -1
|
@@ -20,10 +20,19 @@
|
|
|
20
20
|
*
|
|
21
21
|
* A bare name (`add-tool`) and the file path (`add-tool/SKILL.md`) both match.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* In the framework repo, a skill whose version already moved since the last `v*`
|
|
24
|
+
* release tag — or that is new since it — is covered: a later body edit in the
|
|
25
|
+
* same cycle shares that bump instead of needing another.
|
|
26
|
+
*
|
|
27
|
+
* The inverse also holds: a skill moves at most one step per release. In the
|
|
28
|
+
* framework repo itself, each skill's version is compared with its version at the
|
|
29
|
+
* last `v*` release tag, and anything past the next minor (`1.4` → `1.5`) or the
|
|
30
|
+
* next major (`1.4` → `2.0`) is a violation — repeated edits in one cycle share a
|
|
31
|
+
* single bump. Consumer repos skip this: a skill sync can legitimately jump
|
|
32
|
+
* several framework releases at once.
|
|
33
|
+
*
|
|
25
34
|
* Severity mirrors `check-skills-sync.ts` — exits 1, demoted to a warning by
|
|
26
|
-
* devcheck. New skills (no
|
|
35
|
+
* devcheck. New skills (no prior version) and non-git trees are skipped.
|
|
27
36
|
*
|
|
28
37
|
* Runs standalone (`bun run scripts/check-skill-versions.ts`) and as a devcheck step.
|
|
29
38
|
*
|
|
@@ -36,6 +45,7 @@ import process from 'node:process';
|
|
|
36
45
|
|
|
37
46
|
const ROOT = resolve('.');
|
|
38
47
|
const SKILL_MD_RE = /^framework-skills\/[^/]+\/SKILL\.md$/;
|
|
48
|
+
const FRAMEWORK_PACKAGE = '@cyanheads/mcp-ts-core';
|
|
39
49
|
|
|
40
50
|
interface DevcheckConfig {
|
|
41
51
|
skillVersions?: { ignore?: string[] };
|
|
@@ -60,10 +70,10 @@ function isIgnored(relPath: string, patterns: string[]): boolean {
|
|
|
60
70
|
);
|
|
61
71
|
}
|
|
62
72
|
|
|
63
|
-
/** Skill `SKILL.md` files that differ from `
|
|
64
|
-
function changedSkillFiles(): string[] {
|
|
65
|
-
const result = spawnSync('git', ['diff', '--name-only',
|
|
66
|
-
if (result.status !== 0) return []; // not a git repo / no
|
|
73
|
+
/** Skill `SKILL.md` files that differ from `ref` in the working tree (staged + unstaged). */
|
|
74
|
+
function changedSkillFiles(ref: string): string[] {
|
|
75
|
+
const result = spawnSync('git', ['diff', '--name-only', ref, '--'], { encoding: 'utf-8' });
|
|
76
|
+
if (result.status !== 0) return []; // not a git repo / no such ref
|
|
67
77
|
return result.stdout
|
|
68
78
|
.trim()
|
|
69
79
|
.split('\n')
|
|
@@ -71,18 +81,50 @@ function changedSkillFiles(): string[] {
|
|
|
71
81
|
}
|
|
72
82
|
|
|
73
83
|
/**
|
|
74
|
-
* Content of a path at `
|
|
75
|
-
* A tree renamed from the pre-0.13 `skills/` reads its
|
|
84
|
+
* Content of a path at `ref`, or null when it didn't exist there (new file).
|
|
85
|
+
* A tree renamed from the pre-0.13 `skills/` reads its old copy from the old
|
|
76
86
|
* path, so the release that carries the rename still checks every body edit.
|
|
77
87
|
*/
|
|
78
|
-
function
|
|
79
|
-
const show = (p: string) => spawnSync('git', ['show',
|
|
88
|
+
function contentAt(ref: string, relPath: string): string | null {
|
|
89
|
+
const show = (p: string) => spawnSync('git', ['show', `${ref}:${p}`], { encoding: 'utf-8' });
|
|
80
90
|
const result = show(relPath);
|
|
81
91
|
if (result.status === 0) return result.stdout;
|
|
82
92
|
const legacy = show(relPath.replace(/^framework-skills\//, 'skills/'));
|
|
83
93
|
return legacy.status === 0 ? legacy.stdout : null;
|
|
84
94
|
}
|
|
85
95
|
|
|
96
|
+
/** True when this tree is the framework itself, where skill versions are authored. */
|
|
97
|
+
function isFrameworkRepo(): boolean {
|
|
98
|
+
try {
|
|
99
|
+
const pkg = JSON.parse(readFileSync(resolve(ROOT, 'package.json'), 'utf-8')) as {
|
|
100
|
+
name?: string;
|
|
101
|
+
};
|
|
102
|
+
return pkg.name === FRAMEWORK_PACKAGE;
|
|
103
|
+
} catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The latest `v*` release tag reachable from `HEAD`, or null when there is none. */
|
|
109
|
+
function lastReleaseTag(): string | null {
|
|
110
|
+
const result = spawnSync('git', ['describe', '--tags', '--abbrev=0', '--match', 'v*'], {
|
|
111
|
+
encoding: 'utf-8',
|
|
112
|
+
});
|
|
113
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** True when `to` is `from`, its next minor, or its next major at `.0`. */
|
|
117
|
+
function withinOneStep(from: string, to: string): boolean {
|
|
118
|
+
const [fromMajor, fromMinor] = from.split('.').map(Number);
|
|
119
|
+
const [toMajor, toMinor] = to.split('.').map(Number);
|
|
120
|
+
if ([fromMajor, fromMinor, toMajor, toMinor].some((n) => n === undefined || Number.isNaN(n))) {
|
|
121
|
+
return true; // not `X.Y` — out of this check's scope
|
|
122
|
+
}
|
|
123
|
+
if (from === to) return true;
|
|
124
|
+
if (toMajor === fromMajor) return toMinor === (fromMinor as number) + 1;
|
|
125
|
+
return toMajor === (fromMajor as number) + 1 && toMinor === 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
86
128
|
/** `metadata.version` from skill frontmatter, or null when absent/unparseable. */
|
|
87
129
|
function extractVersion(content: string): string | null {
|
|
88
130
|
const block = content.match(/^---\n([\s\S]*?)\n---/)?.[1];
|
|
@@ -108,11 +150,24 @@ if (!existsSync(resolve(ROOT, 'framework-skills'))) {
|
|
|
108
150
|
}
|
|
109
151
|
|
|
110
152
|
const ignore = loadIgnorePatterns();
|
|
111
|
-
const
|
|
153
|
+
const tag = isFrameworkRepo() ? lastReleaseTag() : null;
|
|
112
154
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
155
|
+
/**
|
|
156
|
+
* True when the skill's current version already differs from its version at the
|
|
157
|
+
* release tag — or the skill is new since it — so this cycle's one step is taken
|
|
158
|
+
* and a further body edit shares it rather than needing another bump.
|
|
159
|
+
*/
|
|
160
|
+
function bumpedThisRelease(file: string, version: string | null): boolean {
|
|
161
|
+
if (tag === null) return false;
|
|
162
|
+
const released = contentAt(tag, file);
|
|
163
|
+
if (released === null) return true;
|
|
164
|
+
const releasedVersion = extractVersion(released);
|
|
165
|
+
return releasedVersion !== null && releasedVersion !== version;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const missing: { file: string; version: string }[] = [];
|
|
169
|
+
for (const file of changedSkillFiles('HEAD').filter((f) => !isIgnored(f, ignore))) {
|
|
170
|
+
const oldContent = contentAt('HEAD', file);
|
|
116
171
|
if (oldContent === null) continue; // new skill — no prior version to compare
|
|
117
172
|
if (!existsSync(resolve(ROOT, file))) continue; // deleted in worktree — no body to compare, can't violate
|
|
118
173
|
const newContent = readFileSync(resolve(ROOT, file), 'utf-8');
|
|
@@ -121,25 +176,51 @@ for (const file of changed) {
|
|
|
121
176
|
|
|
122
177
|
const oldVersion = extractVersion(oldContent);
|
|
123
178
|
const newVersion = extractVersion(newContent);
|
|
124
|
-
if (oldVersion !== null && oldVersion === newVersion) {
|
|
125
|
-
|
|
179
|
+
if (oldVersion !== null && oldVersion === newVersion && !bumpedThisRelease(file, newVersion)) {
|
|
180
|
+
missing.push({ file, version: oldVersion });
|
|
126
181
|
}
|
|
127
182
|
}
|
|
128
183
|
|
|
129
|
-
|
|
184
|
+
const overshot: { file: string; tag: string; released: string; version: string }[] = [];
|
|
185
|
+
if (tag !== null) {
|
|
186
|
+
for (const file of changedSkillFiles(tag)) {
|
|
187
|
+
const released = contentAt(tag, file);
|
|
188
|
+
if (released === null || !existsSync(resolve(ROOT, file))) continue;
|
|
189
|
+
const releasedVersion = extractVersion(released);
|
|
190
|
+
const version = extractVersion(readFileSync(resolve(ROOT, file), 'utf-8'));
|
|
191
|
+
if (releasedVersion === null || version === null) continue;
|
|
192
|
+
if (!withinOneStep(releasedVersion, version)) {
|
|
193
|
+
overshot.push({ file, tag, released: releasedVersion, version });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const count = missing.length + overshot.length;
|
|
199
|
+
if (count === 0) {
|
|
130
200
|
console.log('Skill versions are in step with body changes.');
|
|
131
201
|
process.exit(0);
|
|
132
202
|
}
|
|
133
203
|
|
|
134
204
|
const lines = [
|
|
135
|
-
`${
|
|
205
|
+
`${count} skill${count === 1 ? '' : 's'} out of step with the versioning policy:`,
|
|
136
206
|
'',
|
|
137
207
|
];
|
|
138
|
-
for (const v of
|
|
208
|
+
for (const v of missing) {
|
|
139
209
|
lines.push(` - ${v.file} body changed but metadata.version is still "${v.version}"`);
|
|
140
210
|
}
|
|
211
|
+
for (const v of overshot) {
|
|
212
|
+
lines.push(
|
|
213
|
+
` - ${v.file} is "${v.version}", more than one step past "${v.released}" at ${v.tag}`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
141
216
|
lines.push('');
|
|
142
|
-
|
|
143
|
-
lines.push('
|
|
217
|
+
if (missing.length > 0) {
|
|
218
|
+
lines.push('Fix: bump metadata.version in the SKILL.md frontmatter, or add the skill to');
|
|
219
|
+
lines.push(' devcheck.config.json `skillVersions.ignore` for the typo/whitespace carve-out.');
|
|
220
|
+
}
|
|
221
|
+
if (overshot.length > 0) {
|
|
222
|
+
lines.push('Fix: set metadata.version to one step past the release tag — edits in one');
|
|
223
|
+
lines.push(' release cycle share a single bump.');
|
|
224
|
+
}
|
|
144
225
|
console.log(lines.join('\n'));
|
|
145
226
|
process.exit(1);
|
package/scripts/devcheck.ts
CHANGED
|
@@ -811,7 +811,8 @@ const ALL_CHECKS: Check[] = [
|
|
|
811
811
|
flag: '--no-skill-versions',
|
|
812
812
|
canFix: false,
|
|
813
813
|
// Flags framework-skills/<name>/SKILL.md body changes (vs HEAD) that lack a metadata.version
|
|
814
|
-
// bump (#99)
|
|
814
|
+
// bump (#99), and, in the framework repo, a skill bumped more than one step past the last
|
|
815
|
+
// release tag. Skipped when framework-skills/ is absent. Drift is demoted to a warning via
|
|
815
816
|
// isSuccess — the typo/whitespace carve-out lives in devcheck.config.json
|
|
816
817
|
// `skillVersions.ignore`.
|
|
817
818
|
getCommand: () => {
|
|
@@ -821,11 +822,11 @@ const ALL_CHECKS: Check[] = [
|
|
|
821
822
|
isSuccess: (result) => {
|
|
822
823
|
if (result.exitCode === 0) return true;
|
|
823
824
|
const firstLine =
|
|
824
|
-
result.stdout.split('\n')[0]?.trim() || 'Skill
|
|
825
|
+
result.stdout.split('\n')[0]?.trim() || 'Skill versions are out of step with the policy.';
|
|
825
826
|
return { success: true, warning: firstLine };
|
|
826
827
|
},
|
|
827
828
|
tip: (c) =>
|
|
828
|
-
`Bump ${c.bold('metadata.version')} in the changed ${c.bold('SKILL.md')}, or add it to ${c.bold('devcheck.config.json')} ${c.bold('skillVersions.ignore')}.`,
|
|
829
|
+
`Bump ${c.bold('metadata.version')} once per release in the changed ${c.bold('SKILL.md')}, or add it to ${c.bold('devcheck.config.json')} ${c.bold('skillVersions.ignore')}.`,
|
|
829
830
|
},
|
|
830
831
|
{
|
|
831
832
|
name: 'Changelog Sync',
|
|
@@ -59,6 +59,12 @@
|
|
|
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.
|
|
62
68
|
*
|
|
63
69
|
* Every check skips cleanly when its input is absent — consumers who deleted
|
|
64
70
|
* `manifest.json` for an HTTP-only deploy, or who haven't built a bundle,
|
|
@@ -742,6 +748,33 @@ export function checkReadmeVersionBadge(readme: string, packageVersion?: string)
|
|
|
742
748
|
];
|
|
743
749
|
}
|
|
744
750
|
|
|
751
|
+
/** The `files` entry that keeps the `bundle` script's `dist/<name>.mcpb` out of the npm tarball. */
|
|
752
|
+
const BUNDLE_EXCLUSION = '!dist/*.mcpb';
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Check 13: a `files` allowlist that covers `dist/` wholesale must exclude the
|
|
756
|
+
* `.mcpb` the `bundle` script writes there. npm and Bun both honor `!` entries
|
|
757
|
+
* in `files`, and both ignore `.npmignore` once `files` is set, so the entry is
|
|
758
|
+
* the only place the exclusion can live. The caller runs this only when
|
|
759
|
+
* `manifest.json` exists — a project without one never builds a bundle.
|
|
760
|
+
*/
|
|
761
|
+
export function checkBundleExcludedFromFiles(files: unknown): string[] {
|
|
762
|
+
if (!Array.isArray(files)) return [];
|
|
763
|
+
const entries = files.filter((entry): entry is string => typeof entry === 'string');
|
|
764
|
+
|
|
765
|
+
const coversDist = entries.some(
|
|
766
|
+
(entry) => entry.replace(/^\.\//, '').replace(/\/(?:\*\*(?:\/\*)?|\*)?$/, '') === 'dist',
|
|
767
|
+
);
|
|
768
|
+
if (!coversDist) return [];
|
|
769
|
+
if (entries.includes(BUNDLE_EXCLUSION) || entries.includes('!dist/**/*.mcpb')) return [];
|
|
770
|
+
|
|
771
|
+
return [
|
|
772
|
+
`package.json "files" covers dist/ without "${BUNDLE_EXCLUSION}" — the bundle script writes ` +
|
|
773
|
+
`dist/<name>.mcpb, so a release that bundles before publishing ships it in the npm tarball; ` +
|
|
774
|
+
`add "${BUNDLE_EXCLUSION}" to "files"`,
|
|
775
|
+
];
|
|
776
|
+
}
|
|
777
|
+
|
|
745
778
|
/** Read `packaging.pluginManifests` from devcheck.config.json; default on. */
|
|
746
779
|
function pluginManifestsEnabled(): boolean {
|
|
747
780
|
const cfg = tryReadJson<{ packaging?: { pluginManifests?: boolean } }>(
|
|
@@ -755,7 +788,9 @@ async function main(): Promise<void> {
|
|
|
755
788
|
const warnings: string[] = [];
|
|
756
789
|
const notes: string[] = [];
|
|
757
790
|
|
|
758
|
-
const pkg = tryReadJson<{ name?: string; version?: string }>(
|
|
791
|
+
const pkg = tryReadJson<{ files?: unknown; name?: string; version?: string }>(
|
|
792
|
+
resolve('package.json'),
|
|
793
|
+
);
|
|
759
794
|
const unscopedName = pkg?.name?.split('/').pop();
|
|
760
795
|
|
|
761
796
|
// ── Manifest-dependent checks (1–4 + manifest identity) ──
|
|
@@ -826,6 +861,8 @@ async function main(): Promise<void> {
|
|
|
826
861
|
if (unscopedName) {
|
|
827
862
|
errors.push(...checkManifestIdentity(manifest, unscopedName));
|
|
828
863
|
}
|
|
864
|
+
|
|
865
|
+
errors.push(...checkBundleExcludedFromFiles(pkg?.files));
|
|
829
866
|
} else {
|
|
830
867
|
notes.push('No manifest.json — skipping manifest/server.json alignment checks.');
|
|
831
868
|
}
|
package/templates/.env.example
CHANGED
|
@@ -31,10 +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
|
-
# OTEL_EXPORTER_OTLP_ENDPOINT= # OTLP
|
|
40
|
+
# OTEL_EXPORTER_OTLP_ENDPOINT= # OTLP base URL (e.g., http://localhost:4318); traces → /v1/traces, metrics → /v1/metrics
|
|
41
|
+
# OTEL_EXPORTER_OTLP_TRACES_ENDPOINT= # Overrides the base for traces; used as-is
|
|
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
|
|
38
44
|
|
|
39
45
|
# ── Server-specific ──────────────────────────────────────────────────
|
|
40
46
|
# Add your server's environment variables below
|
|
@@ -10,7 +10,7 @@ body:
|
|
|
10
10
|
**Secondary labels.** `enhancement` is applied automatically — also add one or more of the following in the sidebar if they apply:
|
|
11
11
|
- `performance` — improves memory, CPU, latency, or resource usage
|
|
12
12
|
- `security` — hardens the security posture
|
|
13
|
-
- `breaking-change` — will break public API
|
|
13
|
+
- `breaking-change` — will break public API or an existing tool contract
|
|
14
14
|
- `surplus-token-idea` — worth exploring when token budget allows
|
|
15
15
|
|
|
16
16
|
- type: textarea
|
package/templates/AGENTS.md
CHANGED
|
@@ -67,7 +67,7 @@ export const searchItems = tool('search_items', {
|
|
|
67
67
|
annotations: { readOnlyHint: true },
|
|
68
68
|
input: z.object({
|
|
69
69
|
query: z.string().describe('Search terms'),
|
|
70
|
-
limit: z.number().default(10).describe('Max results'),
|
|
70
|
+
limit: z.number().int().min(1).max(100).default(10).describe('Max results (1–100)'),
|
|
71
71
|
}),
|
|
72
72
|
output: z.object({
|
|
73
73
|
items: z.array(z.object({
|
|
@@ -199,7 +199,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
199
199
|
| Property | Description |
|
|
200
200
|
|:---------|:------------|
|
|
201
201
|
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino **and** `notifications/message` to the client, so treat it as client-visible. |
|
|
202
|
-
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
202
|
+
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any JSON-serializable value; reads return its JSON form (a `Date` comes back as an ISO string). |
|
|
203
203
|
| `ctx.requestInput` | Suspend and ask the caller for more input — `return ctx.requestInput({ inputRequests: { key: inputRequired.elicit({ message, requestedSchema }) } })`. Never returns; the handler is re-entered with the answers. Always present. |
|
|
204
204
|
| `ctx.inputs` | Reader over a retried request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped`. Empty on the first round. |
|
|
205
205
|
| `ctx.enrich` | Success-path agent context (empty-result notices, query echo, pagination totals) — `ctx.enrich(...)` or `.notice()` / `.total()` / `.echo()` / `.truncated()`. Reaches `structuredContent` and `content[]`; lands only when the definition declares an `enrichment` block (no-op otherwise). |
|
package/templates/CLAUDE.md
CHANGED
|
@@ -67,7 +67,7 @@ export const searchItems = tool('search_items', {
|
|
|
67
67
|
annotations: { readOnlyHint: true },
|
|
68
68
|
input: z.object({
|
|
69
69
|
query: z.string().describe('Search terms'),
|
|
70
|
-
limit: z.number().default(10).describe('Max results'),
|
|
70
|
+
limit: z.number().int().min(1).max(100).default(10).describe('Max results (1–100)'),
|
|
71
71
|
}),
|
|
72
72
|
output: z.object({
|
|
73
73
|
items: z.array(z.object({
|
|
@@ -199,7 +199,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
199
199
|
| Property | Description |
|
|
200
200
|
|:---------|:------------|
|
|
201
201
|
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino **and** `notifications/message` to the client, so treat it as client-visible. |
|
|
202
|
-
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
202
|
+
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any JSON-serializable value; reads return its JSON form (a `Date` comes back as an ISO string). |
|
|
203
203
|
| `ctx.requestInput` | Suspend and ask the caller for more input — `return ctx.requestInput({ inputRequests: { key: inputRequired.elicit({ message, requestedSchema }) } })`. Never returns; the handler is re-entered with the answers. Always present. |
|
|
204
204
|
| `ctx.inputs` | Reader over a retried request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped`. Empty on the first round. |
|
|
205
205
|
| `ctx.enrich` | Success-path agent context (empty-result notices, query echo, pagination totals) — `ctx.enrich(...)` or `.notice()` / `.total()` / `.echo()` / `.truncated()`. Reaches `structuredContent` and `content[]`; lands only when the definition declares an `enrichment` block (no-op otherwise). |
|
package/templates/Dockerfile
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# output. A stage that compiles a native addon needs the target-arch toolchain
|
|
15
15
|
# and cannot cross-compile this way — drop the flag there.
|
|
16
16
|
# ==============================================================================
|
|
17
|
-
FROM --platform=$BUILDPLATFORM oven/bun:1.4.
|
|
17
|
+
FROM --platform=$BUILDPLATFORM oven/bun:1.4.2 AS build
|
|
18
18
|
|
|
19
19
|
WORKDIR /usr/src/app
|
|
20
20
|
|
|
@@ -40,7 +40,7 @@ RUN bun run build
|
|
|
40
40
|
# application. It uses a slim base image and only includes production
|
|
41
41
|
# dependencies and build artifacts.
|
|
42
42
|
# ==============================================================================
|
|
43
|
-
FROM oven/bun:1.4.
|
|
43
|
+
FROM oven/bun:1.4.2-slim AS production
|
|
44
44
|
|
|
45
45
|
WORKDIR /usr/src/app
|
|
46
46
|
|
|
@@ -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.
|
|
@@ -70,21 +77,35 @@ RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
|
70
77
|
bun install --production --omit=peer --frozen-lockfile --ignore-scripts
|
|
71
78
|
|
|
72
79
|
# Conditionally install OpenTelemetry optional peer dependencies (Tier 3).
|
|
73
|
-
#
|
|
74
|
-
# with: docker build --build-arg OTEL_ENABLED=
|
|
80
|
+
# Installed by default. Omit them for a leaner image at build time
|
|
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/package.json
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"files": [
|
|
13
13
|
"changelog/",
|
|
14
14
|
"dist/",
|
|
15
|
+
"!dist/*.mcpb",
|
|
15
16
|
"README.md",
|
|
16
17
|
"LICENSE",
|
|
17
18
|
"CLAUDE.md",
|
|
@@ -52,7 +53,7 @@
|
|
|
52
53
|
"url": ""
|
|
53
54
|
},
|
|
54
55
|
"license": "Apache-2.0",
|
|
55
|
-
"packageManager": "bun@1.4.
|
|
56
|
+
"packageManager": "bun@1.4.2",
|
|
56
57
|
"engines": {
|
|
57
58
|
"bun": ">=1.4.0",
|
|
58
59
|
"node": ">=24.0.0"
|
|
@@ -67,8 +68,8 @@
|
|
|
67
68
|
},
|
|
68
69
|
"devDependencies": {
|
|
69
70
|
"@biomejs/biome": "2.5.14",
|
|
70
|
-
"@socketsecurity/bun-security-scanner": "^1.1.
|
|
71
|
-
"@types/node": "26.
|
|
71
|
+
"@socketsecurity/bun-security-scanner": "^1.1.3",
|
|
72
|
+
"@types/node": "26.6.2",
|
|
72
73
|
"@vitest/coverage-istanbul": "4.1.11",
|
|
73
74
|
"depcheck": "^1.4.7",
|
|
74
75
|
"fast-check": "^4.9.0",
|
|
@@ -8,11 +8,9 @@ import { prompt, z } from '@cyanheads/mcp-ts-core';
|
|
|
8
8
|
// Prompts are pure message templates — no Context, no auth, no side effects.
|
|
9
9
|
// They generate conversation messages that clients can use to start interactions.
|
|
10
10
|
export const echoPrompt = prompt('template_echo_message', {
|
|
11
|
-
description: '
|
|
11
|
+
description: 'Generate a simple echo message. Replace this with your first real prompt.',
|
|
12
12
|
args: z.object({
|
|
13
13
|
message: z.string().describe('The message to echo.'),
|
|
14
14
|
}),
|
|
15
|
-
generate: (args) => [
|
|
16
|
-
{ role: 'user', content: { type: 'text', text: `Echo: ${args.message}` } },
|
|
17
|
-
],
|
|
15
|
+
generate: (args) => [{ role: 'user', content: { type: 'text', text: `Echo: ${args.message}` } }],
|
|
18
16
|
});
|
|
@@ -18,31 +18,69 @@ const APP_HTML = `<!DOCTYPE html>
|
|
|
18
18
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
19
19
|
<title>Echo App</title>
|
|
20
20
|
<style>
|
|
21
|
+
/*
|
|
22
|
+
* Local tokens are the pre-connect baseline: light by default, dark when the
|
|
23
|
+
* OS prefers it, and pinned by the data-theme attribute applyDocumentTheme sets.
|
|
24
|
+
* Every rule reads the host's variable first (installed by applyHostStyleVariables)
|
|
25
|
+
* and falls back to the local token until host context arrives.
|
|
26
|
+
*/
|
|
27
|
+
:root {
|
|
28
|
+
color-scheme: light dark;
|
|
29
|
+
--bg: #ffffff; --surface: #f8fafc; --fg: #0f172a; --muted: #64748b;
|
|
30
|
+
--border: #e2e8f0; --accent: #2563eb; --accent-fg: #ffffff;
|
|
31
|
+
}
|
|
32
|
+
@media (prefers-color-scheme: dark) {
|
|
33
|
+
:root:not([data-theme="light"]) {
|
|
34
|
+
--bg: #0f172a; --surface: #1e293b; --fg: #e2e8f0; --muted: #94a3b8;
|
|
35
|
+
--border: #334155; --accent: #3b82f6; --accent-fg: #ffffff;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
:root[data-theme="dark"] {
|
|
39
|
+
--bg: #0f172a; --surface: #1e293b; --fg: #e2e8f0; --muted: #94a3b8;
|
|
40
|
+
--border: #334155; --accent: #3b82f6; --accent-fg: #ffffff;
|
|
41
|
+
}
|
|
21
42
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
22
43
|
body {
|
|
23
|
-
font-family: system-ui, -apple-system, sans-serif;
|
|
24
|
-
background:
|
|
44
|
+
font-family: var(--font-sans, system-ui, -apple-system, sans-serif);
|
|
45
|
+
background: var(--color-background-primary, var(--bg));
|
|
46
|
+
color: var(--color-text-primary, var(--fg));
|
|
25
47
|
padding: 1.5rem; min-height: 100vh;
|
|
26
48
|
display: flex; flex-direction: column; align-items: center; gap: 1rem;
|
|
27
49
|
}
|
|
28
|
-
h1 {
|
|
50
|
+
h1 {
|
|
51
|
+
font-size: var(--font-heading-sm-size, 1.25rem);
|
|
52
|
+
font-weight: var(--font-weight-semibold, 600);
|
|
53
|
+
}
|
|
29
54
|
.card {
|
|
30
|
-
background:
|
|
55
|
+
background: var(--color-background-secondary, var(--surface));
|
|
56
|
+
border: 1px solid var(--color-border-primary, var(--border));
|
|
57
|
+
border-radius: var(--border-radius-lg, 0.5rem);
|
|
31
58
|
padding: 1rem 1.25rem; width: 100%; max-width: 480px;
|
|
32
59
|
}
|
|
33
|
-
.label {
|
|
34
|
-
|
|
60
|
+
.label {
|
|
61
|
+
font-size: var(--font-text-xs-size, 0.75rem);
|
|
62
|
+
color: var(--color-text-secondary, var(--muted));
|
|
63
|
+
text-transform: uppercase; letter-spacing: 0.05em;
|
|
64
|
+
}
|
|
65
|
+
.value { font-size: var(--font-text-md-size, 1rem); margin-top: 0.25rem; word-break: break-word; }
|
|
35
66
|
.controls { display: flex; gap: 0.5rem; width: 100%; max-width: 480px; }
|
|
36
67
|
input[type="text"] {
|
|
37
|
-
flex: 1; padding: 0.5rem 0.75rem;
|
|
38
|
-
|
|
68
|
+
flex: 1; padding: 0.5rem 0.75rem;
|
|
69
|
+
background: var(--color-background-secondary, var(--surface));
|
|
70
|
+
border: 1px solid var(--color-border-primary, var(--border));
|
|
71
|
+
border-radius: var(--border-radius-md, 0.375rem);
|
|
72
|
+
color: var(--color-text-primary, var(--fg));
|
|
73
|
+
font: inherit; font-size: var(--font-text-sm-size, 0.875rem); outline: none;
|
|
39
74
|
}
|
|
40
|
-
input[type="text"]:focus { border-color:
|
|
75
|
+
input[type="text"]:focus { border-color: var(--color-ring-primary, var(--accent)); }
|
|
41
76
|
button {
|
|
42
|
-
padding: 0.5rem 1rem;
|
|
43
|
-
|
|
77
|
+
padding: 0.5rem 1rem; border: none;
|
|
78
|
+
background: var(--color-background-inverse, var(--accent));
|
|
79
|
+
color: var(--color-text-inverse, var(--accent-fg));
|
|
80
|
+
border-radius: var(--border-radius-md, 0.375rem);
|
|
81
|
+
font: inherit; font-size: var(--font-text-sm-size, 0.875rem); cursor: pointer;
|
|
44
82
|
}
|
|
45
|
-
button:hover {
|
|
83
|
+
button:hover { opacity: 0.9; }
|
|
46
84
|
button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
47
85
|
</style>
|
|
48
86
|
</head>
|
|
@@ -138,8 +176,7 @@ export const echoAppUiResource = appResource('ui://template-echo-app/app.html',
|
|
|
138
176
|
name: 'echo-app-ui',
|
|
139
177
|
title: 'Echo App UI',
|
|
140
178
|
description:
|
|
141
|
-
'Interactive HTML app for the echo app tool. Displayed as a sandboxed iframe '
|
|
142
|
-
'by MCP Apps-capable hosts.',
|
|
179
|
+
'Interactive HTML app for the echo app tool. Displayed as a sandboxed iframe by MCP Apps-capable hosts.',
|
|
143
180
|
params: ParamsSchema,
|
|
144
181
|
auth: ['resource:echo-app-ui:read'],
|
|
145
182
|
_meta: {
|
|
@@ -9,7 +9,7 @@ import { resource, z } from '@cyanheads/mcp-ts-core';
|
|
|
9
9
|
// Like tools, prefix names with your server name: template-echo, tasks-status, etc.
|
|
10
10
|
export const echoResource = resource('echo://{message}', {
|
|
11
11
|
name: 'template-echo-resource',
|
|
12
|
-
description: '
|
|
12
|
+
description: 'Echo a message from the URI. Replace this with your first real resource.',
|
|
13
13
|
mimeType: 'application/json',
|
|
14
14
|
params: z.object({
|
|
15
15
|
message: z.string().describe('The message to echo back.'),
|
|
@@ -15,15 +15,14 @@ export const echoAppTool = appTool('template_echo_app', {
|
|
|
15
15
|
resourceUri: UI_RESOURCE_URI,
|
|
16
16
|
title: 'Echo App',
|
|
17
17
|
description:
|
|
18
|
-
'
|
|
19
|
-
'the extension render an HTML interface; others receive a text fallback.',
|
|
18
|
+
'Echo a message back with an interactive UI. Hosts that support MCP Apps render an HTML interface; other hosts receive a text fallback.',
|
|
20
19
|
annotations: { readOnlyHint: true },
|
|
21
20
|
input: z.object({
|
|
22
21
|
message: z.string().describe('The message to echo.'),
|
|
23
22
|
}),
|
|
24
23
|
output: z.object({
|
|
25
24
|
message: z.string().describe('The echoed message.'),
|
|
26
|
-
timestamp: z.
|
|
25
|
+
timestamp: z.iso.datetime().describe('ISO 8601 timestamp of the echo.'),
|
|
27
26
|
}),
|
|
28
27
|
|
|
29
28
|
handler(input, ctx) {
|
|
@@ -9,7 +9,7 @@ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
|
9
9
|
// Tool names are snake_case, prefixed with your server name to avoid collisions across servers.
|
|
10
10
|
// e.g. for a "tasks" server: tasks_fetch_list, tasks_create_item.
|
|
11
11
|
export const echoTool = tool('template_echo_message', {
|
|
12
|
-
description: '
|
|
12
|
+
description: 'Echo a message back. Replace this with your first real tool.',
|
|
13
13
|
annotations: { readOnlyHint: true },
|
|
14
14
|
input: z.object({
|
|
15
15
|
message: z.string().describe('The message to echo back.'),
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Barrel for telemetry utilities.
|
|
3
|
-
* Provides OpenTelemetry instrumentation, MCP attribute keys, trace helpers,
|
|
4
|
-
* and metrics creation utilities for observability.
|
|
5
|
-
*
|
|
6
|
-
* @module src/utils/telemetry
|
|
7
|
-
*/
|
|
8
|
-
export * from './attributes.js';
|
|
9
|
-
export * from './instrumentation.js';
|
|
10
|
-
export * from './metrics.js';
|
|
11
|
-
export * from './trace.js';
|
|
12
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/telemetry/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Barrel for telemetry utilities.
|
|
3
|
-
* Provides OpenTelemetry instrumentation, MCP attribute keys, trace helpers,
|
|
4
|
-
* and metrics creation utilities for observability.
|
|
5
|
-
*
|
|
6
|
-
* @module src/utils/telemetry
|
|
7
|
-
*/
|
|
8
|
-
export * from './attributes.js';
|
|
9
|
-
export * from './instrumentation.js';
|
|
10
|
-
export * from './metrics.js';
|
|
11
|
-
export * from './trace.js';
|
|
12
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/telemetry/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
|