@cyanheads/mcp-ts-core 0.5.3 → 0.6.0
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/CLAUDE.md +41 -2
- package/README.md +1 -1
- package/changelog/0.1.x/0.1.0.md +78 -0
- package/changelog/0.1.x/0.1.1.md +28 -0
- package/changelog/0.1.x/0.1.10.md +32 -0
- package/changelog/0.1.x/0.1.11.md +51 -0
- package/changelog/0.1.x/0.1.12.md +21 -0
- package/changelog/0.1.x/0.1.13.md +16 -0
- package/changelog/0.1.x/0.1.14.md +20 -0
- package/changelog/0.1.x/0.1.15.md +24 -0
- package/changelog/0.1.x/0.1.16.md +17 -0
- package/changelog/0.1.x/0.1.17.md +14 -0
- package/changelog/0.1.x/0.1.18.md +18 -0
- package/changelog/0.1.x/0.1.19.md +19 -0
- package/changelog/0.1.x/0.1.2.md +25 -0
- package/changelog/0.1.x/0.1.20.md +21 -0
- package/changelog/0.1.x/0.1.21.md +17 -0
- package/changelog/0.1.x/0.1.22.md +28 -0
- package/changelog/0.1.x/0.1.23.md +23 -0
- package/changelog/0.1.x/0.1.24.md +17 -0
- package/changelog/0.1.x/0.1.25.md +16 -0
- package/changelog/0.1.x/0.1.26.md +22 -0
- package/changelog/0.1.x/0.1.27.md +30 -0
- package/changelog/0.1.x/0.1.28.md +16 -0
- package/changelog/0.1.x/0.1.29.md +19 -0
- package/changelog/0.1.x/0.1.3.md +22 -0
- package/changelog/0.1.x/0.1.4.md +17 -0
- package/changelog/0.1.x/0.1.5.md +25 -0
- package/changelog/0.1.x/0.1.6.md +26 -0
- package/changelog/0.1.x/0.1.7.md +29 -0
- package/changelog/0.1.x/0.1.8.md +33 -0
- package/changelog/0.1.x/0.1.9.md +19 -0
- package/changelog/0.2.x/0.2.0.md +32 -0
- package/changelog/0.2.x/0.2.1.md +12 -0
- package/changelog/0.2.x/0.2.10.md +38 -0
- package/changelog/0.2.x/0.2.11.md +29 -0
- package/changelog/0.2.x/0.2.12.md +31 -0
- package/changelog/0.2.x/0.2.2.md +19 -0
- package/changelog/0.2.x/0.2.3.md +15 -0
- package/changelog/0.2.x/0.2.4.md +24 -0
- package/changelog/0.2.x/0.2.5.md +27 -0
- package/changelog/0.2.x/0.2.6.md +23 -0
- package/changelog/0.2.x/0.2.7.md +23 -0
- package/changelog/0.2.x/0.2.8.md +12 -0
- package/changelog/0.2.x/0.2.9.md +25 -0
- package/changelog/0.3.x/0.3.0.md +45 -0
- package/changelog/0.3.x/0.3.1.md +16 -0
- package/changelog/0.3.x/0.3.2.md +24 -0
- package/changelog/0.3.x/0.3.3.md +31 -0
- package/changelog/0.3.x/0.3.4.md +31 -0
- package/changelog/0.3.x/0.3.5.md +32 -0
- package/changelog/0.3.x/0.3.6.md +48 -0
- package/changelog/0.3.x/0.3.7.md +23 -0
- package/changelog/0.3.x/0.3.8.md +21 -0
- package/changelog/0.4.x/0.4.0.md +38 -0
- package/changelog/0.4.x/0.4.1.md +31 -0
- package/changelog/0.5.x/0.5.0.md +29 -0
- package/changelog/0.5.x/0.5.1.md +18 -0
- package/changelog/0.5.x/0.5.2.md +38 -0
- package/changelog/0.5.x/0.5.3.md +26 -0
- package/changelog/0.5.x/0.5.4.md +29 -0
- package/changelog/0.6.x/0.6.0.md +39 -0
- package/changelog/unreleased.md +40 -0
- package/dist/cli/init.js +1 -0
- package/dist/cli/init.js.map +1 -1
- package/dist/core/app.d.ts +13 -3
- package/dist/core/app.d.ts.map +1 -1
- package/dist/core/app.js +20 -13
- package/dist/core/app.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/serverManifest.d.ts +237 -0
- package/dist/core/serverManifest.d.ts.map +1 -0
- package/dist/core/serverManifest.js +310 -0
- package/dist/core/serverManifest.js.map +1 -0
- package/dist/core/worker.d.ts.map +1 -1
- package/dist/core/worker.js +2 -2
- package/dist/core/worker.js.map +1 -1
- package/dist/linter/rules/landing-rules.d.ts +15 -0
- package/dist/linter/rules/landing-rules.d.ts.map +1 -0
- package/dist/linter/rules/landing-rules.js +125 -0
- package/dist/linter/rules/landing-rules.js.map +1 -0
- package/dist/linter/types.d.ts +5 -2
- package/dist/linter/types.d.ts.map +1 -1
- package/dist/linter/validate.d.ts.map +1 -1
- package/dist/linter/validate.js +26 -2
- package/dist/linter/validate.js.map +1 -1
- package/dist/mcp-server/transports/http/httpTransport.d.ts +4 -3
- package/dist/mcp-server/transports/http/httpTransport.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/httpTransport.js +47 -26
- package/dist/mcp-server/transports/http/httpTransport.js.map +1 -1
- package/dist/mcp-server/transports/http/httpTypes.d.ts +0 -12
- package/dist/mcp-server/transports/http/httpTypes.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/landing-page.d.ts +48 -0
- package/dist/mcp-server/transports/http/landing-page.d.ts.map +1 -0
- package/dist/mcp-server/transports/http/landing-page.js +912 -0
- package/dist/mcp-server/transports/http/landing-page.js.map +1 -0
- package/dist/mcp-server/transports/http/serverCard.d.ts +67 -0
- package/dist/mcp-server/transports/http/serverCard.d.ts.map +1 -0
- package/dist/mcp-server/transports/http/serverCard.js +91 -0
- package/dist/mcp-server/transports/http/serverCard.js.map +1 -0
- package/dist/mcp-server/transports/manager.d.ts +3 -3
- package/dist/mcp-server/transports/manager.d.ts.map +1 -1
- package/dist/mcp-server/transports/manager.js +4 -4
- package/dist/mcp-server/transports/manager.js.map +1 -1
- package/dist/utils/formatting/html.d.ts +76 -0
- package/dist/utils/formatting/html.d.ts.map +1 -0
- package/dist/utils/formatting/html.js +111 -0
- package/dist/utils/formatting/html.js.map +1 -0
- package/dist/utils/formatting/index.d.ts +1 -0
- package/dist/utils/formatting/index.d.ts.map +1 -1
- package/dist/utils/formatting/index.js +1 -0
- package/dist/utils/formatting/index.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 +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +5 -1
- package/scripts/build-changelog.ts +222 -0
- package/scripts/devcheck.ts +19 -4
- package/scripts/tree.ts +3 -0
- package/skills/add-app-tool/SKILL.md +2 -4
- package/skills/add-prompt/SKILL.md +2 -4
- package/skills/add-resource/SKILL.md +2 -4
- package/skills/add-service/SKILL.md +2 -4
- package/skills/add-tool/SKILL.md +6 -5
- package/skills/api-context/SKILL.md +2 -2
- package/skills/api-linter/SKILL.md +391 -0
- package/skills/api-services/SKILL.md +1 -1
- package/skills/api-services/references/graph.md +1 -1
- package/skills/api-utils/SKILL.md +1 -1
- package/skills/api-utils/references/parsing.md +1 -1
- package/skills/api-utils/references/security.md +1 -1
- package/skills/design-mcp-server/SKILL.md +2 -2
- package/skills/maintenance/SKILL.md +12 -11
- package/skills/polish-docs-meta/SKILL.md +24 -9
- package/skills/release/SKILL.md +21 -7
- package/skills/setup/SKILL.md +4 -8
- package/templates/AGENTS.md +23 -1
- package/templates/CLAUDE.md +23 -1
- package/templates/changelog/unreleased.md +40 -0
- package/templates/package.json +3 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Generate CHANGELOG.md as a navigation index from per-version files.
|
|
4
|
+
*
|
|
5
|
+
* Source of truth: `changelog/<major.minor>.x/<version>.md` — each file opens with
|
|
6
|
+
* YAML frontmatter declaring:
|
|
7
|
+
* • summary (required) — ≤250-char headline, no markdown, one line
|
|
8
|
+
* • breaking (optional) — `true` flags releases with breaking changes
|
|
9
|
+
*
|
|
10
|
+
* The rollup is a thin **index**, not a copy of bodies — each entry is just a
|
|
11
|
+
* clickable header + one-line summary. Full content stays in the per-version files.
|
|
12
|
+
*
|
|
13
|
+
* Rendered rollup entry:
|
|
14
|
+
* ## [X.Y.Z](changelog/N.N.x/X.Y.Z.md) — YYYY-MM-DD · ⚠️ Breaking
|
|
15
|
+
*
|
|
16
|
+
* <summary>
|
|
17
|
+
*
|
|
18
|
+
* (The `· ⚠️ Breaking` badge only appears when `breaking: true`.)
|
|
19
|
+
*
|
|
20
|
+
* Modes:
|
|
21
|
+
* • default → regenerate CHANGELOG.md
|
|
22
|
+
* • --check → exit 1 if CHANGELOG.md differs from what would be generated
|
|
23
|
+
*
|
|
24
|
+
* Missing `summary`: warning (not failure) — the entry renders header-only.
|
|
25
|
+
* Summary > 250 chars, or malformed `breaking`: hard error.
|
|
26
|
+
*
|
|
27
|
+
* @module scripts/build-changelog
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
31
|
+
import { resolve } from 'node:path';
|
|
32
|
+
import process from 'node:process';
|
|
33
|
+
|
|
34
|
+
const CHANGELOG_DIR = resolve('changelog');
|
|
35
|
+
const CHANGELOG_PATH = resolve('CHANGELOG.md');
|
|
36
|
+
const EXCLUDED_FILES = new Set(['unreleased.md', 'README.md']);
|
|
37
|
+
const SERIES_PATTERN = /^\d+\.\d+\.x$/;
|
|
38
|
+
const SUMMARY_MAX_LENGTH = 250;
|
|
39
|
+
|
|
40
|
+
const HEADER = `# Changelog
|
|
41
|
+
|
|
42
|
+
All notable changes to this project. Each entry links to its full per-version file in [changelog/](changelog/).
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
interface VersionEntry {
|
|
46
|
+
path: string;
|
|
47
|
+
series: string;
|
|
48
|
+
version: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface Frontmatter {
|
|
52
|
+
breaking: boolean;
|
|
53
|
+
summary: string | null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Semver descending compare. Final releases rank above their prereleases
|
|
58
|
+
* (`0.6.0 > 0.6.0-rc.1 > 0.6.0-beta.1`). Prereleases compared lexicographically.
|
|
59
|
+
*/
|
|
60
|
+
function compareSemverDesc(a: string, b: string): number {
|
|
61
|
+
const parse = (v: string): [number[], string | null] => {
|
|
62
|
+
const [base, pre = null] = v.split('-', 2) as [string, string | undefined];
|
|
63
|
+
return [base.split('.').map(Number), pre ?? null];
|
|
64
|
+
};
|
|
65
|
+
const [aParts, aPre] = parse(a);
|
|
66
|
+
const [bParts, bPre] = parse(b);
|
|
67
|
+
for (let i = 0; i < 3; i++) {
|
|
68
|
+
const diff = (bParts[i] ?? 0) - (aParts[i] ?? 0);
|
|
69
|
+
if (diff !== 0) return diff;
|
|
70
|
+
}
|
|
71
|
+
if (aPre === bPre) return 0;
|
|
72
|
+
if (aPre === null) return -1;
|
|
73
|
+
if (bPre === null) return 1;
|
|
74
|
+
return bPre.localeCompare(aPre);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Parse minimal YAML frontmatter. Only recognizes `summary` and `breaking` —
|
|
79
|
+
* other keys are ignored, so the format stays extensible without touching the
|
|
80
|
+
* parser. Throws on malformed values we actually care about.
|
|
81
|
+
*/
|
|
82
|
+
function parseFrontmatter(content: string, fileLabel: string): Frontmatter {
|
|
83
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n?/);
|
|
84
|
+
if (!match) return { summary: null, breaking: false };
|
|
85
|
+
|
|
86
|
+
const block = match[1] as string;
|
|
87
|
+
|
|
88
|
+
// summary: quoted or bare, single line
|
|
89
|
+
let summary: string | null = null;
|
|
90
|
+
const summaryMatch = block.match(/^summary:\s*(.*)$/m);
|
|
91
|
+
if (summaryMatch) {
|
|
92
|
+
let raw = (summaryMatch[1] ?? '').trim();
|
|
93
|
+
if ((raw.startsWith('"') && raw.endsWith('"')) || (raw.startsWith("'") && raw.endsWith("'"))) {
|
|
94
|
+
raw = raw.slice(1, -1);
|
|
95
|
+
}
|
|
96
|
+
summary = raw.length > 0 ? raw : null;
|
|
97
|
+
}
|
|
98
|
+
if (summary !== null && summary.length > SUMMARY_MAX_LENGTH) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`${fileLabel}: summary is ${summary.length} chars, exceeds cap of ${SUMMARY_MAX_LENGTH}. Keep it tight — headline, not paragraph.`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// breaking: must be literal true/false if present
|
|
105
|
+
let breaking = false;
|
|
106
|
+
const breakingMatch = block.match(/^breaking:\s*(\S+)\s*$/m);
|
|
107
|
+
if (breakingMatch) {
|
|
108
|
+
const val = breakingMatch[1];
|
|
109
|
+
if (val !== 'true' && val !== 'false') {
|
|
110
|
+
throw new Error(`${fileLabel}: breaking must be 'true' or 'false', got '${val}'.`);
|
|
111
|
+
}
|
|
112
|
+
breaking = val === 'true';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return { summary, breaking };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Extract the release date from the H1 heading. */
|
|
119
|
+
function extractDate(body: string, fileLabel: string): string {
|
|
120
|
+
const match = body.match(/^#\s+\S+\s+[—–-]\s+(\d{4}-\d{2}-\d{2})/m);
|
|
121
|
+
if (!match) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`${fileLabel}: H1 heading missing or malformed. Expected '# <version> — YYYY-MM-DD'.`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return match[1] as string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function renderEntry(entry: VersionEntry, fm: Frontmatter, date: string): string {
|
|
130
|
+
const link = `changelog/${entry.series}/${entry.version}.md`;
|
|
131
|
+
const breakingBadge = fm.breaking ? ' · ⚠️ Breaking' : '';
|
|
132
|
+
const header = `## [${entry.version}](${link}) — ${date}${breakingBadge}`;
|
|
133
|
+
if (fm.summary) {
|
|
134
|
+
return `${header}\n\n${fm.summary}\n`;
|
|
135
|
+
}
|
|
136
|
+
return `${header}\n`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function collectVersionFiles(): VersionEntry[] {
|
|
140
|
+
const entries: VersionEntry[] = [];
|
|
141
|
+
for (const entry of readdirSync(CHANGELOG_DIR, { withFileTypes: true })) {
|
|
142
|
+
if (!entry.isDirectory() || !SERIES_PATTERN.test(entry.name)) continue;
|
|
143
|
+
const seriesDir = resolve(CHANGELOG_DIR, entry.name);
|
|
144
|
+
for (const file of readdirSync(seriesDir)) {
|
|
145
|
+
if (!file.endsWith('.md') || EXCLUDED_FILES.has(file)) continue;
|
|
146
|
+
entries.push({
|
|
147
|
+
version: file.replace(/\.md$/, ''),
|
|
148
|
+
series: entry.name,
|
|
149
|
+
path: resolve(seriesDir, file),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return entries.sort((a, b) => compareSemverDesc(a.version, b.version));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function buildRollup(): { content: string; missingSummary: string[] } {
|
|
157
|
+
const entries = collectVersionFiles();
|
|
158
|
+
|
|
159
|
+
if (entries.length === 0) {
|
|
160
|
+
throw new Error(`No per-version changelog files found under ${CHANGELOG_DIR}/<major.minor>.x/`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const missingSummary: string[] = [];
|
|
164
|
+
const sections: string[] = [];
|
|
165
|
+
|
|
166
|
+
for (const entry of entries) {
|
|
167
|
+
const fileLabel = `changelog/${entry.series}/${entry.version}.md`;
|
|
168
|
+
const content = readFileSync(entry.path, 'utf-8');
|
|
169
|
+
const fm = parseFrontmatter(content, fileLabel);
|
|
170
|
+
const date = extractDate(content, fileLabel);
|
|
171
|
+
|
|
172
|
+
if (!fm.summary) {
|
|
173
|
+
missingSummary.push(fileLabel);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
sections.push(renderEntry(entry, fm, date));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
content: `${HEADER}\n${sections.join('\n')}`,
|
|
181
|
+
missingSummary,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function reportMissingSummaries(missing: string[]): void {
|
|
186
|
+
if (missing.length === 0) return;
|
|
187
|
+
const shown = missing.slice(0, 10);
|
|
188
|
+
const extra = missing.length - shown.length;
|
|
189
|
+
console.warn(`\nWarning: ${missing.length} file(s) missing 'summary' frontmatter:`);
|
|
190
|
+
for (const file of shown) console.warn(` - ${file}`);
|
|
191
|
+
if (extra > 0) console.warn(` ... and ${extra} more`);
|
|
192
|
+
console.warn(`\nBackfill these — see CLAUDE.md § Changelog for the frontmatter format.`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function main(): void {
|
|
196
|
+
const checkOnly = process.argv.includes('--check');
|
|
197
|
+
const { content: generated, missingSummary } = buildRollup();
|
|
198
|
+
|
|
199
|
+
if (checkOnly) {
|
|
200
|
+
let existing = '';
|
|
201
|
+
try {
|
|
202
|
+
existing = readFileSync(CHANGELOG_PATH, 'utf-8');
|
|
203
|
+
} catch {
|
|
204
|
+
// missing file counts as drift
|
|
205
|
+
}
|
|
206
|
+
if (existing === generated) {
|
|
207
|
+
console.log('CHANGELOG.md is in sync with changelog/ directory.');
|
|
208
|
+
reportMissingSummaries(missingSummary);
|
|
209
|
+
process.exit(0);
|
|
210
|
+
}
|
|
211
|
+
console.error('CHANGELOG.md is out of sync with changelog/ directory.');
|
|
212
|
+
console.error('Fix: run `bun run changelog:build` to regenerate.');
|
|
213
|
+
reportMissingSummaries(missingSummary);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
writeFileSync(CHANGELOG_PATH, generated);
|
|
218
|
+
console.log(`Wrote ${CHANGELOG_PATH} (${generated.split('\n').length - 1} lines).`);
|
|
219
|
+
reportMissingSummaries(missingSummary);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
main();
|
package/scripts/devcheck.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { type ChildProcess, spawn, spawnSync } from 'node:child_process';
|
|
3
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
4
|
import * as path from 'node:path';
|
|
5
5
|
import process from 'node:process';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
@@ -67,7 +67,7 @@ const createColor = (open: string, close: string, closeRe: RegExp) => (str: stri
|
|
|
67
67
|
return open + `${str}`.replace(closeRe, close + open) + close;
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
-
const esc = (code: string) => new RegExp(code.replace(
|
|
70
|
+
const esc = (code: string) => new RegExp(code.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
|
|
71
71
|
const c = {
|
|
72
72
|
bold: createColor('\x1b[1m', '\x1b[22m', esc('\x1b[22m')),
|
|
73
73
|
dim: createColor('\x1b[2m', '\x1b[22m', esc('\x1b[22m')),
|
|
@@ -411,7 +411,7 @@ const ALL_CHECKS: Check[] = [
|
|
|
411
411
|
canFix: false,
|
|
412
412
|
getCommand: () => ['bun', 'run', 'scripts/lint-mcp.ts'],
|
|
413
413
|
tip: (c) =>
|
|
414
|
-
`Fix definition errors
|
|
414
|
+
`Fix definition errors above — each diagnostic links to its rule in ${c.bold('skills/api-linter/SKILL.md')}.`,
|
|
415
415
|
},
|
|
416
416
|
{
|
|
417
417
|
name: 'Docs Sync',
|
|
@@ -421,6 +421,21 @@ const ALL_CHECKS: Check[] = [
|
|
|
421
421
|
tip: (c) =>
|
|
422
422
|
`Edit both files together, or run ${c.bold('cp CLAUDE.md AGENTS.md')} (or reverse) to resync.`,
|
|
423
423
|
},
|
|
424
|
+
{
|
|
425
|
+
name: 'Changelog Sync',
|
|
426
|
+
flag: '--no-changelog-sync',
|
|
427
|
+
canFix: false,
|
|
428
|
+
// --check exits non-zero if CHANGELOG.md drifts from changelog/*.md.
|
|
429
|
+
// Skipped cleanly when neither CHANGELOG.md nor changelog/ exists (non-mcp-ts-core projects).
|
|
430
|
+
getCommand: () => {
|
|
431
|
+
const hasChangelog = existsSync(path.join(ROOT_DIR, 'changelog'));
|
|
432
|
+
const hasRollup = existsSync(path.join(ROOT_DIR, 'CHANGELOG.md'));
|
|
433
|
+
if (!hasChangelog && !hasRollup) return null;
|
|
434
|
+
return ['bun', 'run', 'scripts/build-changelog.ts', '--check'];
|
|
435
|
+
},
|
|
436
|
+
tip: (c) =>
|
|
437
|
+
`Edit the per-version file in ${c.bold('changelog/')} and run ${c.bold('bun run changelog:build')} to regenerate ${c.bold('CHANGELOG.md')}.`,
|
|
438
|
+
},
|
|
424
439
|
{
|
|
425
440
|
name: 'Biome',
|
|
426
441
|
flag: '--no-lint',
|
|
@@ -562,7 +577,7 @@ const ALL_CHECKS: Check[] = [
|
|
|
562
577
|
return unexpected.length === 0;
|
|
563
578
|
},
|
|
564
579
|
tip: (c) =>
|
|
565
|
-
`Run ${c.bold(`${PM_CMD} update`)} to upgrade
|
|
580
|
+
`Run ${c.bold(`${PM_CMD} update`)} to upgrade; the ${c.bold('maintenance')} skill then investigates changelogs and adopts upstream changes. Configure allowlist in ${c.bold('devcheck.config.json')}.`,
|
|
566
581
|
},
|
|
567
582
|
];
|
|
568
583
|
|
package/scripts/tree.ts
CHANGED
|
@@ -39,6 +39,9 @@ const DEFAULT_IGNORE_PATTERNS: string[] = [
|
|
|
39
39
|
'coverage',
|
|
40
40
|
'logs',
|
|
41
41
|
'.husky/_',
|
|
42
|
+
// Directory-based changelog — keep series dirs visible (0.1.x/, 0.5.x/) for structural
|
|
43
|
+
// orientation, but collapse out the per-version files. Top-level unreleased.md stays.
|
|
44
|
+
'changelog/*/*.md',
|
|
42
45
|
];
|
|
43
46
|
|
|
44
47
|
interface ParsedArgs {
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Scaffold an MCP App tool + UI resource pair. Use when the user asks to add a tool with interactive UI, create an MCP App, or build a visual/interactive tool.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.3"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -18,9 +18,7 @@ MCP Apps extend the standard tool pattern with an interactive HTML UI rendered i
|
|
|
18
18
|
|
|
19
19
|
Both builders are exported from `@cyanheads/mcp-ts-core`. They handle `_meta.ui.resourceUri`, the compat key (`ui/resourceUri`), and the correct MIME type (`text/html;profile=mcp-app`) automatically.
|
|
20
20
|
|
|
21
|
-
For the full API, Context interface, and error codes, read
|
|
22
|
-
|
|
23
|
-
node_modules/@cyanheads/mcp-ts-core/CLAUDE.md
|
|
21
|
+
For the full API, Context interface, and error codes, read `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md`.
|
|
24
22
|
|
|
25
23
|
## Steps
|
|
26
24
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Scaffold a new MCP prompt template. Use when the user asks to add a prompt, create a reusable message template, or define a prompt for LLM interactions.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.2"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -15,9 +15,7 @@ Prompts use the `prompt()` builder from `@cyanheads/mcp-ts-core`. Each prompt li
|
|
|
15
15
|
|
|
16
16
|
Prompts are pure message templates — no `Context`, no auth, no side effects.
|
|
17
17
|
|
|
18
|
-
For the full `prompt()` API, read
|
|
19
|
-
|
|
20
|
-
node_modules/@cyanheads/mcp-ts-core/CLAUDE.md
|
|
18
|
+
For the full `prompt()` API, read `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md`.
|
|
21
19
|
|
|
22
20
|
## Steps
|
|
23
21
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Scaffold a new MCP resource definition. Use when the user asks to add a resource, expose data via URI, or create a readable endpoint.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.3"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -15,9 +15,7 @@ Resources use the `resource()` builder from `@cyanheads/mcp-ts-core`. Each resou
|
|
|
15
15
|
|
|
16
16
|
**Tool coverage.** Not all MCP clients expose resources — many are tool-only (Claude Code, Cursor, most chat UIs). Before adding a resource, verify the same data is reachable via the tool surface — either through a dedicated tool, included in another tool's output, or bundled into a broader tool. A resource whose data has no tool path is invisible to a large share of agents.
|
|
17
17
|
|
|
18
|
-
For the full `resource()` API, pagination utilities, and `Context` interface, read
|
|
19
|
-
|
|
20
|
-
node_modules/@cyanheads/mcp-ts-core/CLAUDE.md
|
|
18
|
+
For the full `resource()` API, pagination utilities, and `Context` interface, read `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md`.
|
|
21
19
|
|
|
22
20
|
## Steps
|
|
23
21
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Scaffold a new service integration. Use when the user asks to add a service, integrate an external API, or create a reusable domain module with its own initialization and state.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.3"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -15,9 +15,7 @@ Services use the init/accessor pattern: initialized once in `createApp`'s `setup
|
|
|
15
15
|
|
|
16
16
|
Service methods receive `Context` for correlated logging (`ctx.log`) and tenant-scoped storage (`ctx.state`). Convention: `ctx.elicit` and `ctx.sample` should only be called from tool handlers, not from services.
|
|
17
17
|
|
|
18
|
-
For the full service pattern, `CoreServices`, and `Context` interface, read
|
|
19
|
-
|
|
20
|
-
node_modules/@cyanheads/mcp-ts-core/CLAUDE.md
|
|
18
|
+
For the full service pattern, `CoreServices`, and `Context` interface, read `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md`.
|
|
21
19
|
|
|
22
20
|
## Steps
|
|
23
21
|
|
package/skills/add-tool/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Scaffold a new MCP tool definition. Use when the user asks to add a tool, create a new tool, or implement a new capability for the server.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.7"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -13,9 +13,7 @@ metadata:
|
|
|
13
13
|
|
|
14
14
|
Tools use the `tool()` builder from `@cyanheads/mcp-ts-core`. Each tool lives in `src/mcp-server/tools/definitions/` with a `.tool.ts` suffix and is registered into `createApp()` in `src/index.ts`. Some larger repos later add `definitions/index.ts` barrels; match the pattern already used by the project you're editing.
|
|
15
15
|
|
|
16
|
-
For the full `tool()` API, `Context` interface, and error codes, read
|
|
17
|
-
|
|
18
|
-
node_modules/@cyanheads/mcp-ts-core/CLAUDE.md
|
|
16
|
+
For the full `tool()` API, `Context` interface, and error codes, read `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md`.
|
|
19
17
|
|
|
20
18
|
## Steps
|
|
21
19
|
|
|
@@ -164,6 +162,8 @@ async handler(input, ctx) {
|
|
|
164
162
|
},
|
|
165
163
|
```
|
|
166
164
|
|
|
165
|
+
**Note on the `try/catch`:** this is the deliberate exception to the "logic throws, framework catches" rule. Per-item isolation is the whole point of partial-success batch tools — one failed item must not abort the batch, and the framework's partial-success telemetry (below) depends on seeing a populated `failed` array. Don't remove it to conform to the handler-level rule.
|
|
166
|
+
|
|
167
167
|
Single-item tools don't need this — they either succeed or throw. The partial success question only arises with array inputs.
|
|
168
168
|
|
|
169
169
|
**Telemetry:** The framework automatically detects this pattern — when a handler result contains a non-empty `failed` array, the span gets `mcp.tool.partial_success`, `mcp.tool.batch.succeeded_count`, and `mcp.tool.batch.failed_count` attributes. No manual instrumentation needed.
|
|
@@ -242,7 +242,8 @@ import { serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
|
|
|
242
242
|
throw serviceUnavailable(`arXiv API returned HTTP ${status}. Retry in a few seconds.`);
|
|
243
243
|
|
|
244
244
|
// Structured hint for programmatic recovery
|
|
245
|
-
|
|
245
|
+
import { invalidParams } from '@cyanheads/mcp-ts-core/errors';
|
|
246
|
+
throw invalidParams(
|
|
246
247
|
`Date range exceeds 90-day API limit. Narrow the range or split into multiple queries.`,
|
|
247
248
|
{ maxDays: 90, requestedDays: daysBetween },
|
|
248
249
|
);
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.elicit`, `ctx.sample`, `ctx.progress`), and when to use each.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.1"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -181,7 +181,7 @@ if (ctx.elicit) {
|
|
|
181
181
|
await produceOutput(result.content?.format as string, result.content?.includeHeaders as boolean);
|
|
182
182
|
} else {
|
|
183
183
|
// 'decline' or 'cancel' — user opted out
|
|
184
|
-
throw
|
|
184
|
+
throw invalidRequest('User declined input');
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
```
|