@jaypie/mcp 0.8.154 → 0.8.155

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,6 +28,22 @@ export const handler = createLambdaStreamHandler(app);
28
28
 
29
29
  `mcpHttpHandler` answers JSON or server-sent events by `Accept` header, 202 for notifications, and 405 for GET and DELETE. Pass `suite` to serve any fabric `ServiceSuite`. Lifecycle options (`secrets`, `setup`, `validate`, ...) pass through to `expressHandler`. Requires `@jaypie/express` and `@jaypie/logger` (optional peers, included in `jaypie`).
30
30
 
31
+ ### Bundling
32
+
33
+ A bundler (esbuild) leaves out the markdown that `skill` and `release_notes` read. Copy it beside the bundle at build time:
34
+
35
+ ```typescript
36
+ import { cpSync } from "node:fs";
37
+ import { join } from "node:path";
38
+ import { getMcpAssetPaths, MCP_ASSET_DIRECTORY } from "@jaypie/mcp/assets";
39
+
40
+ const assets = getMcpAssetPaths();
41
+ cpSync(assets.releaseNotes, join("dist", MCP_ASSET_DIRECTORY.RELEASE_NOTES), { recursive: true });
42
+ cpSync(assets.skills, join("dist", MCP_ASSET_DIRECTORY.SKILLS), { recursive: true });
43
+ ```
44
+
45
+ `MCP_BUILTIN_SKILLS_PATH` and `MCP_RELEASE_NOTES_PATH` point at other locations.
46
+
31
47
  ### Filtering Tools
32
48
 
33
49
  `createMcpServer`, `mcpExpressHandler`, and `mcpHttpHandler` accept `services`, an allowlist of tool names:
@@ -0,0 +1,34 @@
1
+ declare const MCP_ASSET_DIRECTORY: {
2
+ readonly RELEASE_NOTES: "release-notes";
3
+ readonly SKILLS: "skills";
4
+ };
5
+ interface McpAssetPaths {
6
+ /** Absolute path of the release notes directory read by `release_notes` */
7
+ releaseNotes: string;
8
+ /** Absolute path of the Jaypie skills directory read by `skill` */
9
+ skills: string;
10
+ }
11
+ /**
12
+ * Absolute paths of the markdown directories `skill` and `release_notes` read
13
+ * at runtime
14
+ *
15
+ * A bundler (esbuild) inlines the code but not the markdown. Copy each
16
+ * directory beside the bundle under its own name (`skills/`,
17
+ * `release-notes/`) and the docs suite finds it without configuration. Call
18
+ * this from a build script, never from bundled code.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * import { cpSync } from "node:fs";
23
+ * import { join } from "node:path";
24
+ * import { getMcpAssetPaths, MCP_ASSET_DIRECTORY } from "@jaypie/mcp/assets";
25
+ *
26
+ * const assets = getMcpAssetPaths();
27
+ * cpSync(assets.skills, join("dist", MCP_ASSET_DIRECTORY.SKILLS), { recursive: true });
28
+ * cpSync(assets.releaseNotes, join("dist", MCP_ASSET_DIRECTORY.RELEASE_NOTES), { recursive: true });
29
+ * ```
30
+ */
31
+ declare function getMcpAssetPaths(): McpAssetPaths;
32
+
33
+ export { MCP_ASSET_DIRECTORY, getMcpAssetPaths };
34
+ export type { McpAssetPaths };
package/dist/assets.js ADDED
@@ -0,0 +1,47 @@
1
+ import * as path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+
4
+ // Locate the markdown that ships inside @jaypie/mcp, for build scripts that bundle it
5
+ //
6
+ //
7
+ // Constants
8
+ //
9
+ const MCP_ASSET_DIRECTORY = {
10
+ RELEASE_NOTES: "release-notes",
11
+ SKILLS: "skills",
12
+ };
13
+ //
14
+ //
15
+ // Main
16
+ //
17
+ /**
18
+ * Absolute paths of the markdown directories `skill` and `release_notes` read
19
+ * at runtime
20
+ *
21
+ * A bundler (esbuild) inlines the code but not the markdown. Copy each
22
+ * directory beside the bundle under its own name (`skills/`,
23
+ * `release-notes/`) and the docs suite finds it without configuration. Call
24
+ * this from a build script, never from bundled code.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * import { cpSync } from "node:fs";
29
+ * import { join } from "node:path";
30
+ * import { getMcpAssetPaths, MCP_ASSET_DIRECTORY } from "@jaypie/mcp/assets";
31
+ *
32
+ * const assets = getMcpAssetPaths();
33
+ * cpSync(assets.skills, join("dist", MCP_ASSET_DIRECTORY.SKILLS), { recursive: true });
34
+ * cpSync(assets.releaseNotes, join("dist", MCP_ASSET_DIRECTORY.RELEASE_NOTES), { recursive: true });
35
+ * ```
36
+ */
37
+ function getMcpAssetPaths() {
38
+ // src/assets.ts and dist/assets.js both sit one level below the package root
39
+ const packageRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), "..");
40
+ return {
41
+ releaseNotes: path.join(packageRoot, MCP_ASSET_DIRECTORY.RELEASE_NOTES),
42
+ skills: path.join(packageRoot, MCP_ASSET_DIRECTORY.SKILLS),
43
+ };
44
+ }
45
+
46
+ export { MCP_ASSET_DIRECTORY, getMcpAssetPaths };
47
+ //# sourceMappingURL=assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets.js","sources":["../src/assets.ts"],"sourcesContent":["// Locate the markdown that ships inside @jaypie/mcp, for build scripts that bundle it\n\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n//\n//\n// Constants\n//\n\nexport const MCP_ASSET_DIRECTORY = {\n RELEASE_NOTES: \"release-notes\",\n SKILLS: \"skills\",\n} as const;\n\n//\n//\n// Types\n//\n\nexport interface McpAssetPaths {\n /** Absolute path of the release notes directory read by `release_notes` */\n releaseNotes: string;\n /** Absolute path of the Jaypie skills directory read by `skill` */\n skills: string;\n}\n\n//\n//\n// Main\n//\n\n/**\n * Absolute paths of the markdown directories `skill` and `release_notes` read\n * at runtime\n *\n * A bundler (esbuild) inlines the code but not the markdown. Copy each\n * directory beside the bundle under its own name (`skills/`,\n * `release-notes/`) and the docs suite finds it without configuration. Call\n * this from a build script, never from bundled code.\n *\n * @example\n * ```typescript\n * import { cpSync } from \"node:fs\";\n * import { join } from \"node:path\";\n * import { getMcpAssetPaths, MCP_ASSET_DIRECTORY } from \"@jaypie/mcp/assets\";\n *\n * const assets = getMcpAssetPaths();\n * cpSync(assets.skills, join(\"dist\", MCP_ASSET_DIRECTORY.SKILLS), { recursive: true });\n * cpSync(assets.releaseNotes, join(\"dist\", MCP_ASSET_DIRECTORY.RELEASE_NOTES), { recursive: true });\n * ```\n */\nexport function getMcpAssetPaths(): McpAssetPaths {\n // src/assets.ts and dist/assets.js both sit one level below the package root\n const packageRoot = path.join(\n path.dirname(fileURLToPath(import.meta.url)),\n \"..\",\n );\n return {\n releaseNotes: path.join(packageRoot, MCP_ASSET_DIRECTORY.RELEASE_NOTES),\n skills: path.join(packageRoot, MCP_ASSET_DIRECTORY.SKILLS),\n };\n}\n"],"names":[],"mappings":";;;AAAA;AAKA;AACA;AACA;AACA;AAEO,MAAM,mBAAmB,GAAG;AACjC,IAAA,aAAa,EAAE,eAAe;AAC9B,IAAA,MAAM,EAAE,QAAQ;;AAelB;AACA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;;;AAmBG;SACa,gBAAgB,GAAA;;IAE9B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAC3B,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAC5C,IAAI,CACL;IACD,OAAO;QACL,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,aAAa,CAAC;QACvE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,MAAM,CAAC;KAC3D;AACH;;;;"}
@@ -0,0 +1 @@
1
+ export declare const RELEASE_NOTES_HELP = "# Release Notes\n\nBrowse Jaypie package release notes.\n\n## Commands\n\n| Command | Description | Required Parameters |\n|---------|-------------|---------------------|\n| `list` | List available release notes | - |\n| `read` | Read a specific release note | `package`, `version` |\n\n## Options for `list`\n\n| Option | Description |\n|--------|-------------|\n| `package` | Filter by package name (e.g., \"mcp\", \"jaypie\") |\n| `since_version` | Only versions newer than this (e.g., \"1.0.0\") |\n\n## Examples\n\n```\nrelease_notes(\"list\")\nrelease_notes(\"list\", { package: \"mcp\" })\nrelease_notes(\"list\", { package: \"jaypie\", since_version: \"2.0.0\" })\nrelease_notes(\"read\", { package: \"mcp\", version: \"0.4.2\" })\n```\n\n## Available Packages\n\nRelease notes are available for all Jaypie packages including:\n- `jaypie` - Main package\n- `mcp` - MCP server\n- `fabric` - Service handlers and adapters\n- `llm` - LLM provider interface\n- And more...\n\nUse `release_notes(\"list\")` to see all available packages and versions.\n";
@@ -0,0 +1,42 @@
1
+ // Inlined so bundled builds (esbuild, Lambda) need no shipped file
2
+ const RELEASE_NOTES_HELP = `# Release Notes
3
+
4
+ Browse Jaypie package release notes.
5
+
6
+ ## Commands
7
+
8
+ | Command | Description | Required Parameters |
9
+ |---------|-------------|---------------------|
10
+ | \`list\` | List available release notes | - |
11
+ | \`read\` | Read a specific release note | \`package\`, \`version\` |
12
+
13
+ ## Options for \`list\`
14
+
15
+ | Option | Description |
16
+ |--------|-------------|
17
+ | \`package\` | Filter by package name (e.g., "mcp", "jaypie") |
18
+ | \`since_version\` | Only versions newer than this (e.g., "1.0.0") |
19
+
20
+ ## Examples
21
+
22
+ \`\`\`
23
+ release_notes("list")
24
+ release_notes("list", { package: "mcp" })
25
+ release_notes("list", { package: "jaypie", since_version: "2.0.0" })
26
+ release_notes("read", { package: "mcp", version: "0.4.2" })
27
+ \`\`\`
28
+
29
+ ## Available Packages
30
+
31
+ Release notes are available for all Jaypie packages including:
32
+ - \`jaypie\` - Main package
33
+ - \`mcp\` - MCP server
34
+ - \`fabric\` - Service handlers and adapters
35
+ - \`llm\` - LLM provider interface
36
+ - And more...
37
+
38
+ Use \`release_notes("list")\` to see all available packages and versions.
39
+ `;
40
+
41
+ export { RELEASE_NOTES_HELP };
42
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sources":["../../../src/suites/docs/help.ts"],"sourcesContent":["// Inlined so bundled builds (esbuild, Lambda) need no shipped file\n\nexport const RELEASE_NOTES_HELP = `# Release Notes\n\nBrowse Jaypie package release notes.\n\n## Commands\n\n| Command | Description | Required Parameters |\n|---------|-------------|---------------------|\n| \\`list\\` | List available release notes | - |\n| \\`read\\` | Read a specific release note | \\`package\\`, \\`version\\` |\n\n## Options for \\`list\\`\n\n| Option | Description |\n|--------|-------------|\n| \\`package\\` | Filter by package name (e.g., \"mcp\", \"jaypie\") |\n| \\`since_version\\` | Only versions newer than this (e.g., \"1.0.0\") |\n\n## Examples\n\n\\`\\`\\`\nrelease_notes(\"list\")\nrelease_notes(\"list\", { package: \"mcp\" })\nrelease_notes(\"list\", { package: \"jaypie\", since_version: \"2.0.0\" })\nrelease_notes(\"read\", { package: \"mcp\", version: \"0.4.2\" })\n\\`\\`\\`\n\n## Available Packages\n\nRelease notes are available for all Jaypie packages including:\n- \\`jaypie\\` - Main package\n- \\`mcp\\` - MCP server\n- \\`fabric\\` - Service handlers and adapters\n- \\`llm\\` - LLM provider interface\n- And more...\n\nUse \\`release_notes(\"list\")\\` to see all available packages and versions.\n`;\n"],"names":[],"mappings":"AAAA;AAEO,MAAM,kBAAkB,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -2,26 +2,97 @@ import { BadRequestError } from '@jaypie/errors';
2
2
  import { fabricService } from '@jaypie/fabric';
3
3
  import { parseFrontmatter } from '@jaypie/kit';
4
4
  import { createMarkdownStore, createLayeredStore, createSkillService } from '@jaypie/tildeskill';
5
+ import { existsSync, readdirSync } from 'node:fs';
5
6
  import * as fs from 'node:fs/promises';
6
7
  import * as path from 'node:path';
7
8
  import { fileURLToPath } from 'node:url';
8
9
  import { gt } from 'semver';
10
+ import { getMcpAssetPaths, MCP_ASSET_DIRECTORY } from '../../assets.js';
11
+ import { RELEASE_NOTES_HELP } from './help.js';
9
12
 
10
13
  /**
11
14
  * Docs Suite - Documentation services (skill, version, release_notes)
12
15
  */
13
- const BUILD_VERSION_STRING = "@jaypie/mcp@0.8.154#f76841e4"
16
+ const BUILD_VERSION_STRING = "@jaypie/mcp@0.8.155#dee71f8c"
14
17
  ;
15
- const __filename$1 = fileURLToPath(import.meta.url);
16
- const __dirname$1 = path.dirname(__filename$1);
17
- // From dist/suites/docs/, go up 3 levels to package root where skills/ and release-notes/ live
18
- // Environment variables allow overriding paths when bundled (e.g., esbuild Lambda)
19
- const RELEASE_NOTES_PATH = process.env.MCP_RELEASE_NOTES_PATH ||
20
- path.join(__dirname$1, "..", "..", "..", "release-notes");
18
+ // =============================================================================
19
+ // ASSET PATHS
20
+ // =============================================================================
21
+ const ENV_BUILTIN_SKILLS_PATH = "MCP_BUILTIN_SKILLS_PATH";
22
+ const ENV_RELEASE_NOTES_PATH = "MCP_RELEASE_NOTES_PATH";
23
+ const MARKDOWN_EXTENSION = ".md";
24
+ const WARNING_PREFIX = "[@jaypie/mcp]";
25
+ // A bundler (esbuild) collapses every module into one file, so this module's
26
+ // directory is the bundle directory rather than dist/suites/docs/
27
+ const MODULE_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
28
+ const PACKAGE_ASSET_PATHS = getMcpAssetPaths();
29
+ /**
30
+ * An explicit environment path wins outright. Otherwise the package copy wins,
31
+ * then a copy beside the bundle. Returns the package path when neither exists.
32
+ */
33
+ function resolveAssetDirectory({ bundled, envPath, packaged, }) {
34
+ if (envPath)
35
+ return envPath;
36
+ return ([packaged, bundled].find((candidate) => existsSync(candidate)) ?? packaged);
37
+ }
38
+ function hasEntries({ directory, isEntry, }) {
39
+ try {
40
+ return readdirSync(directory, { withFileTypes: true }).some(isEntry);
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ }
46
+ /**
47
+ * Warn once, on first use, when an asset directory is missing or empty. Waiting
48
+ * for first use keeps servers that never call the service quiet.
49
+ */
50
+ function createAssetCheck({ directory, envName, isEntry, name, }) {
51
+ let checked = false;
52
+ return () => {
53
+ if (checked)
54
+ return;
55
+ checked = true;
56
+ if (hasEntries({ directory, isEntry }))
57
+ return;
58
+ // stderr keeps the stdio transport's stdout clean and still reaches CloudWatch
59
+ // eslint-disable-next-line no-console
60
+ console.warn(`${WARNING_PREFIX} No ${name} found in "${directory}". Copy the ${name} directory from getMcpAssetPaths() in @jaypie/mcp/assets beside the bundle or set ${envName}.`);
61
+ };
62
+ }
63
+ /** Run `check` before any property of `target` is read */
64
+ function withAssetCheck({ check, target, }) {
65
+ return new Proxy(target, {
66
+ get(object, property, receiver) {
67
+ check();
68
+ return Reflect.get(object, property, receiver);
69
+ },
70
+ });
71
+ }
72
+ const RELEASE_NOTES_PATH = resolveAssetDirectory({
73
+ bundled: path.join(MODULE_DIRECTORY, MCP_ASSET_DIRECTORY.RELEASE_NOTES),
74
+ envPath: process.env[ENV_RELEASE_NOTES_PATH],
75
+ packaged: PACKAGE_ASSET_PATHS.releaseNotes,
76
+ });
21
77
  // Bundled Jaypie skills ship inside the @jaypie/mcp package. MCP_BUILTIN_SKILLS_PATH
22
- // lets bundlers (esbuild, Lambda) relocate them without disabling the built-in layer.
23
- const BUILTIN_SKILLS_PATH = process.env.MCP_BUILTIN_SKILLS_PATH ||
24
- path.join(__dirname$1, "..", "..", "..", "skills");
78
+ // relocates them without disabling the built-in layer.
79
+ const BUILTIN_SKILLS_PATH = resolveAssetDirectory({
80
+ bundled: path.join(MODULE_DIRECTORY, MCP_ASSET_DIRECTORY.SKILLS),
81
+ envPath: process.env[ENV_BUILTIN_SKILLS_PATH],
82
+ packaged: PACKAGE_ASSET_PATHS.skills,
83
+ });
84
+ const checkReleaseNoteAssets = createAssetCheck({
85
+ directory: RELEASE_NOTES_PATH,
86
+ envName: ENV_RELEASE_NOTES_PATH,
87
+ isEntry: (entry) => entry.isDirectory(),
88
+ name: MCP_ASSET_DIRECTORY.RELEASE_NOTES,
89
+ });
90
+ const checkSkillAssets = createAssetCheck({
91
+ directory: BUILTIN_SKILLS_PATH,
92
+ envName: ENV_BUILTIN_SKILLS_PATH,
93
+ isEntry: (entry) => entry.isFile() && entry.name.endsWith(MARKDOWN_EXTENSION),
94
+ name: MCP_ASSET_DIRECTORY.SKILLS,
95
+ });
25
96
  const LOCAL_SKILLS_NAMESPACE = "local";
26
97
  const JAYPIE_SKILLS_NAMESPACE = "jaypie";
27
98
  const LAYER_SEPARATOR = ":";
@@ -37,7 +108,10 @@ if (process.env.MCP_SKILLS_PATH) {
37
108
  }
38
109
  skillLayers.push({
39
110
  namespace: JAYPIE_SKILLS_NAMESPACE,
40
- store: createMarkdownStore({ path: BUILTIN_SKILLS_PATH }),
111
+ store: withAssetCheck({
112
+ check: checkSkillAssets,
113
+ target: createMarkdownStore({ path: BUILTIN_SKILLS_PATH }),
114
+ }),
41
115
  });
42
116
  const skillStore = createLayeredStore({
43
117
  layers: skillLayers,
@@ -122,12 +196,6 @@ const versionService = fabricService({
122
196
  input: {},
123
197
  service: async () => BUILD_VERSION_STRING,
124
198
  });
125
- // =============================================================================
126
- // RELEASE NOTES SERVICE
127
- // =============================================================================
128
- async function getReleaseNotesHelp() {
129
- return fs.readFile(path.join(__dirname$1, "release-notes", "help.md"), "utf-8");
130
- }
131
199
  const releaseNotesService = fabricService({
132
200
  alias: "release_notes",
133
201
  description: "Browse Jaypie package release notes. Commands: list, read. Call with no args for help.",
@@ -145,8 +213,9 @@ const releaseNotesService = fabricService({
145
213
  },
146
214
  service: async ({ command, input: params, }) => {
147
215
  if (!command || command === "help") {
148
- return getReleaseNotesHelp();
216
+ return RELEASE_NOTES_HELP;
149
217
  }
218
+ checkReleaseNoteAssets();
150
219
  const p = params || {};
151
220
  switch (command) {
152
221
  case "list": {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/suites/docs/index.ts"],"sourcesContent":["/**\n * Docs Suite - Documentation services (skill, version, release_notes)\n */\nimport { BadRequestError } from \"@jaypie/errors\";\nimport { fabricService } from \"@jaypie/fabric\";\nimport { parseFrontmatter } from \"@jaypie/kit\";\nimport {\n createLayeredStore,\n createMarkdownStore,\n createSkillService,\n type LayeredStoreLayer,\n} from \"@jaypie/tildeskill\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { gt } from \"semver\";\n\n// Build-time constants\ndeclare const __BUILD_VERSION_STRING__: string;\nconst BUILD_VERSION_STRING =\n typeof __BUILD_VERSION_STRING__ !== \"undefined\"\n ? __BUILD_VERSION_STRING__\n : \"@jaypie/mcp@0.0.0\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n// From dist/suites/docs/, go up 3 levels to package root where skills/ and release-notes/ live\n// Environment variables allow overriding paths when bundled (e.g., esbuild Lambda)\nconst RELEASE_NOTES_PATH =\n process.env.MCP_RELEASE_NOTES_PATH ||\n path.join(__dirname, \"..\", \"..\", \"..\", \"release-notes\");\n\n// Bundled Jaypie skills ship inside the @jaypie/mcp package. MCP_BUILTIN_SKILLS_PATH\n// lets bundlers (esbuild, Lambda) relocate them without disabling the built-in layer.\nconst BUILTIN_SKILLS_PATH =\n process.env.MCP_BUILTIN_SKILLS_PATH ||\n path.join(__dirname, \"..\", \"..\", \"..\", \"skills\");\n\nconst LOCAL_SKILLS_NAMESPACE = \"local\";\nconst JAYPIE_SKILLS_NAMESPACE = \"jaypie\";\nconst LAYER_SEPARATOR = \":\";\n\n// Skill layers resolved in order: a client's MCP_SKILLS_PATH layers on top of\n// the built-in Jaypie skill pack so `skill(\"aws\")` prefers the client's copy\n// while still exposing bundled Jaypie docs under the `jaypie:` namespace.\nconst skillLayers: LayeredStoreLayer[] = [];\n\nif (process.env.MCP_SKILLS_PATH) {\n skillLayers.push({\n namespace: LOCAL_SKILLS_NAMESPACE,\n store: createMarkdownStore({ path: process.env.MCP_SKILLS_PATH }),\n });\n}\n\nskillLayers.push({\n namespace: JAYPIE_SKILLS_NAMESPACE,\n store: createMarkdownStore({ path: BUILTIN_SKILLS_PATH }),\n});\n\nconst skillStore = createLayeredStore({\n layers: skillLayers,\n separator: LAYER_SEPARATOR,\n});\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\ninterface ReleaseNoteFrontMatter {\n date?: string;\n summary?: string;\n version?: string;\n}\n\nasync function parseReleaseNoteFile(filePath: string): Promise<{\n date?: string;\n filename: string;\n summary?: string;\n version?: string;\n}> {\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n const filename = path.basename(filePath, \".md\");\n\n if (content.startsWith(\"---\")) {\n const frontMatter =\n parseFrontmatter<ReleaseNoteFrontMatter>(content).data;\n return {\n date: frontMatter.date,\n filename,\n summary: frontMatter.summary,\n version: frontMatter.version || filename,\n };\n }\n\n return { filename, version: filename };\n } catch {\n return { filename: path.basename(filePath, \".md\") };\n }\n}\n\nfunction formatReleaseNoteListItem(note: {\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n}): string {\n const { date, packageName, summary, version } = note;\n const parts = [`* ${packageName}@${version}`];\n\n if (date) {\n parts.push(`(${date})`);\n }\n\n if (summary) {\n parts.push(`- ${summary}`);\n }\n\n return parts.join(\" \");\n}\n\nasync function getPackageReleaseNotes(packageName: string): Promise<\n Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n }>\n> {\n const packageDir = path.join(RELEASE_NOTES_PATH, packageName);\n try {\n const files = await fs.readdir(packageDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const notes = await Promise.all(\n mdFiles.map(async (file) => {\n const parsed = await parseReleaseNoteFile(path.join(packageDir, file));\n return { ...parsed, packageName };\n }),\n );\n\n return notes.sort((a, b) => {\n if (!a.version || !b.version) return 0;\n try {\n return gt(a.version, b.version) ? -1 : 1;\n } catch {\n return b.version.localeCompare(a.version);\n }\n });\n } catch {\n return [];\n }\n}\n\nfunction filterReleaseNotesSince(\n notes: Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n }>,\n sinceVersion: string,\n): Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n}> {\n return notes.filter((note) => {\n if (!note.version) return false;\n try {\n return gt(note.version, sinceVersion);\n } catch {\n return false;\n }\n });\n}\n\n// =============================================================================\n// SKILL SERVICE\n// =============================================================================\n\nexport const skillService = createSkillService(skillStore);\n\n// =============================================================================\n// VERSION SERVICE\n// =============================================================================\n\nexport const versionService = fabricService({\n alias: \"version\",\n description: `Prints the current version and hash, \\`${BUILD_VERSION_STRING}\\``,\n input: {},\n service: async () => BUILD_VERSION_STRING,\n});\n\n// =============================================================================\n// RELEASE NOTES SERVICE\n// =============================================================================\n\nasync function getReleaseNotesHelp(): Promise<string> {\n return fs.readFile(path.join(__dirname, \"release-notes\", \"help.md\"), \"utf-8\");\n}\n\ninterface ReleaseNotesInput {\n package?: string;\n since_version?: string;\n version?: string;\n}\n\nexport const releaseNotesService = fabricService({\n alias: \"release_notes\",\n description:\n \"Browse Jaypie package release notes. Commands: list, read. Call with no args for help.\",\n input: {\n command: {\n description: \"Command to execute (omit for help)\",\n required: false,\n type: String,\n },\n input: {\n description: \"Command parameters\",\n required: false,\n type: Object,\n },\n },\n service: async ({\n command,\n input: params,\n }: {\n command?: string;\n input?: ReleaseNotesInput;\n }) => {\n if (!command || command === \"help\") {\n return getReleaseNotesHelp();\n }\n\n const p = params || {};\n\n switch (command) {\n case \"list\": {\n const entries = await fs.readdir(RELEASE_NOTES_PATH, {\n withFileTypes: true,\n });\n const packageDirs = entries\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name);\n const packagesToList = p.package\n ? packageDirs.filter((pkg) => pkg === p.package)\n : packageDirs;\n\n if (packagesToList.length === 0 && p.package) {\n return `No release notes found for package \"${p.package}\".`;\n }\n\n const allNotes = await Promise.all(\n packagesToList.map((pkg) => getPackageReleaseNotes(pkg)),\n );\n let flatNotes = allNotes.flat();\n\n if (p.since_version) {\n flatNotes = filterReleaseNotesSince(flatNotes, p.since_version);\n }\n\n if (flatNotes.length === 0) {\n const filterDesc = p.since_version\n ? ` newer than ${p.since_version}`\n : \"\";\n return `No release notes found${filterDesc}.`;\n }\n\n return flatNotes.map(formatReleaseNoteListItem).join(\"\\n\");\n }\n\n case \"read\": {\n if (!p.package) throw new BadRequestError(\"package is required\");\n if (!p.version) throw new BadRequestError(\"version is required\");\n const filePath = path.join(\n RELEASE_NOTES_PATH,\n p.package,\n `${p.version}.md`,\n );\n return fs.readFile(filePath, \"utf-8\");\n }\n\n default:\n throw new BadRequestError(\n `Unknown command: ${command}. Use release_notes() for help.`,\n );\n }\n },\n});\n"],"names":["__filename","__dirname"],"mappings":";;;;;;;;;AAAA;;AAEG;AAiBH,MAAM,oBAAoB,GAEpB;IACmB;AAEzB,MAAMA,YAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;AACjD,MAAMC,WAAS,GAAG,IAAI,CAAC,OAAO,CAACD,YAAU,CAAC;AAC1C;AACA;AACA,MAAM,kBAAkB,GACtB,OAAO,CAAC,GAAG,CAAC,sBAAsB;AAClC,IAAA,IAAI,CAAC,IAAI,CAACC,WAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,eAAe,CAAC;AAEzD;AACA;AACA,MAAM,mBAAmB,GACvB,OAAO,CAAC,GAAG,CAAC,uBAAuB;AACnC,IAAA,IAAI,CAAC,IAAI,CAACA,WAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC;AAElD,MAAM,sBAAsB,GAAG,OAAO;AACtC,MAAM,uBAAuB,GAAG,QAAQ;AACxC,MAAM,eAAe,GAAG,GAAG;AAE3B;AACA;AACA;AACA,MAAM,WAAW,GAAwB,EAAE;AAE3C,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE;IAC/B,WAAW,CAAC,IAAI,CAAC;AACf,QAAA,SAAS,EAAE,sBAAsB;AACjC,QAAA,KAAK,EAAE,mBAAmB,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC;AAClE,KAAA,CAAC;AACJ;AAEA,WAAW,CAAC,IAAI,CAAC;AACf,IAAA,SAAS,EAAE,uBAAuB;IAClC,KAAK,EAAE,mBAAmB,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC;AAC1D,CAAA,CAAC;AAEF,MAAM,UAAU,GAAG,kBAAkB,CAAC;AACpC,IAAA,MAAM,EAAE,WAAW;AACnB,IAAA,SAAS,EAAE,eAAe;AAC3B,CAAA,CAAC;AAYF,eAAe,oBAAoB,CAAC,QAAgB,EAAA;AAMlD,IAAA,IAAI;QACF,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;QACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;AAE/C,QAAA,IAAI,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE;YAC7B,MAAM,WAAW,GACf,gBAAgB,CAAyB,OAAO,CAAC,CAAC,IAAI;YACxD,OAAO;gBACL,IAAI,EAAE,WAAW,CAAC,IAAI;gBACtB,QAAQ;gBACR,OAAO,EAAE,WAAW,CAAC,OAAO;AAC5B,gBAAA,OAAO,EAAE,WAAW,CAAC,OAAO,IAAI,QAAQ;aACzC;QACH;AAEA,QAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE;IACxC;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE;IACrD;AACF;AAEA,SAAS,yBAAyB,CAAC,IAMlC,EAAA;IACC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI;IACpD,MAAM,KAAK,GAAG,CAAC,CAAA,EAAA,EAAK,WAAW,CAAA,CAAA,EAAI,OAAO,CAAA,CAAE,CAAC;IAE7C,IAAI,IAAI,EAAE;AACR,QAAA,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAA,CAAA,CAAG,CAAC;IACzB;IAEA,IAAI,OAAO,EAAE;AACX,QAAA,KAAK,CAAC,IAAI,CAAC,KAAK,OAAO,CAAA,CAAE,CAAC;IAC5B;AAEA,IAAA,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;AACxB;AAEA,eAAe,sBAAsB,CAAC,WAAmB,EAAA;IASvD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC;AAC7D,IAAA,IAAI;QACF,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC;AAC1C,QAAA,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAE5D,QAAA,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAC7B,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,KAAI;AACzB,YAAA,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AACtE,YAAA,OAAO,EAAE,GAAG,MAAM,EAAE,WAAW,EAAE;QACnC,CAAC,CAAC,CACH;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAI;YACzB,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO;AAAE,gBAAA,OAAO,CAAC;AACtC,YAAA,IAAI;AACF,gBAAA,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;YAC1C;AAAE,YAAA,MAAM;gBACN,OAAO,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC;YAC3C;AACF,QAAA,CAAC,CAAC;IACJ;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,EAAE;IACX;AACF;AAEA,SAAS,uBAAuB,CAC9B,KAME,EACF,YAAoB,EAAA;AAQpB,IAAA,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,KAAI;QAC3B,IAAI,CAAC,IAAI,CAAC,OAAO;AAAE,YAAA,OAAO,KAAK;AAC/B,QAAA,IAAI;YACF,OAAO,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC;QACvC;AAAE,QAAA,MAAM;AACN,YAAA,OAAO,KAAK;QACd;AACF,IAAA,CAAC,CAAC;AACJ;AAEA;AACA;AACA;MAEa,YAAY,GAAG,kBAAkB,CAAC,UAAU;AAEzD;AACA;AACA;AAEO,MAAM,cAAc,GAAG,aAAa,CAAC;AAC1C,IAAA,KAAK,EAAE,SAAS;IAChB,WAAW,EAAE,CAAA,uCAAA,EAA0C,oBAAoB,CAAA,EAAA,CAAI;AAC/E,IAAA,KAAK,EAAE,EAAE;AACT,IAAA,OAAO,EAAE,YAAY,oBAAoB;AAC1C,CAAA;AAED;AACA;AACA;AAEA,eAAe,mBAAmB,GAAA;AAChC,IAAA,OAAO,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAACA,WAAS,EAAE,eAAe,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC;AAC/E;AAQO,MAAM,mBAAmB,GAAG,aAAa,CAAC;AAC/C,IAAA,KAAK,EAAE,eAAe;AACtB,IAAA,WAAW,EACT,wFAAwF;AAC1F,IAAA,KAAK,EAAE;AACL,QAAA,OAAO,EAAE;AACP,YAAA,WAAW,EAAE,oCAAoC;AACjD,YAAA,QAAQ,EAAE,KAAK;AACf,YAAA,IAAI,EAAE,MAAM;AACb,SAAA;AACD,QAAA,KAAK,EAAE;AACL,YAAA,WAAW,EAAE,oBAAoB;AACjC,YAAA,QAAQ,EAAE,KAAK;AACf,YAAA,IAAI,EAAE,MAAM;AACb,SAAA;AACF,KAAA;IACD,OAAO,EAAE,OAAO,EACd,OAAO,EACP,KAAK,EAAE,MAAM,GAId,KAAI;AACH,QAAA,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,MAAM,EAAE;YAClC,OAAO,mBAAmB,EAAE;QAC9B;AAEA,QAAA,MAAM,CAAC,GAAG,MAAM,IAAI,EAAE;QAEtB,QAAQ,OAAO;YACb,KAAK,MAAM,EAAE;gBACX,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,kBAAkB,EAAE;AACnD,oBAAA,aAAa,EAAE,IAAI;AACpB,iBAAA,CAAC;gBACF,MAAM,WAAW,GAAG;qBACjB,MAAM,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,WAAW,EAAE;qBACrC,GAAG,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,CAAC;AAC7B,gBAAA,MAAM,cAAc,GAAG,CAAC,CAAC;AACvB,sBAAE,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC,OAAO;sBAC7C,WAAW;gBAEf,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE;AAC5C,oBAAA,OAAO,CAAA,oCAAA,EAAuC,CAAC,CAAC,OAAO,IAAI;gBAC7D;gBAEA,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,sBAAsB,CAAC,GAAG,CAAC,CAAC,CACzD;AACD,gBAAA,IAAI,SAAS,GAAG,QAAQ,CAAC,IAAI,EAAE;AAE/B,gBAAA,IAAI,CAAC,CAAC,aAAa,EAAE;oBACnB,SAAS,GAAG,uBAAuB,CAAC,SAAS,EAAE,CAAC,CAAC,aAAa,CAAC;gBACjE;AAEA,gBAAA,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE;AAC1B,oBAAA,MAAM,UAAU,GAAG,CAAC,CAAC;AACnB,0BAAE,CAAA,YAAA,EAAe,CAAC,CAAC,aAAa,CAAA;0BAC9B,EAAE;oBACN,OAAO,CAAA,sBAAA,EAAyB,UAAU,CAAA,CAAA,CAAG;gBAC/C;gBAEA,OAAO,SAAS,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YAC5D;YAEA,KAAK,MAAM,EAAE;gBACX,IAAI,CAAC,CAAC,CAAC,OAAO;AAAE,oBAAA,MAAM,IAAI,eAAe,CAAC,qBAAqB,CAAC;gBAChE,IAAI,CAAC,CAAC,CAAC,OAAO;AAAE,oBAAA,MAAM,IAAI,eAAe,CAAC,qBAAqB,CAAC;AAChE,gBAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CACxB,kBAAkB,EAClB,CAAC,CAAC,OAAO,EACT,CAAA,EAAG,CAAC,CAAC,OAAO,CAAA,GAAA,CAAK,CAClB;gBACD,OAAO,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;YACvC;AAEA,YAAA;AACE,gBAAA,MAAM,IAAI,eAAe,CACvB,oBAAoB,OAAO,CAAA,+BAAA,CAAiC,CAC7D;;IAEP,CAAC;AACF,CAAA;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/suites/docs/index.ts"],"sourcesContent":["/**\n * Docs Suite - Documentation services (skill, version, release_notes)\n */\nimport { BadRequestError } from \"@jaypie/errors\";\nimport { fabricService } from \"@jaypie/fabric\";\nimport { parseFrontmatter } from \"@jaypie/kit\";\nimport {\n createLayeredStore,\n createMarkdownStore,\n createSkillService,\n type LayeredStoreLayer,\n} from \"@jaypie/tildeskill\";\nimport { existsSync, readdirSync, type Dirent } from \"node:fs\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { gt } from \"semver\";\n\nimport { getMcpAssetPaths, MCP_ASSET_DIRECTORY } from \"../../assets.js\";\nimport { RELEASE_NOTES_HELP } from \"./help.js\";\n\n// Build-time constants\ndeclare const __BUILD_VERSION_STRING__: string;\nconst BUILD_VERSION_STRING =\n typeof __BUILD_VERSION_STRING__ !== \"undefined\"\n ? __BUILD_VERSION_STRING__\n : \"@jaypie/mcp@0.0.0\";\n\n// =============================================================================\n// ASSET PATHS\n// =============================================================================\n\nconst ENV_BUILTIN_SKILLS_PATH = \"MCP_BUILTIN_SKILLS_PATH\";\nconst ENV_RELEASE_NOTES_PATH = \"MCP_RELEASE_NOTES_PATH\";\nconst MARKDOWN_EXTENSION = \".md\";\nconst WARNING_PREFIX = \"[@jaypie/mcp]\";\n\n// A bundler (esbuild) collapses every module into one file, so this module's\n// directory is the bundle directory rather than dist/suites/docs/\nconst MODULE_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));\nconst PACKAGE_ASSET_PATHS = getMcpAssetPaths();\n\n/**\n * An explicit environment path wins outright. Otherwise the package copy wins,\n * then a copy beside the bundle. Returns the package path when neither exists.\n */\nfunction resolveAssetDirectory({\n bundled,\n envPath,\n packaged,\n}: {\n bundled: string;\n envPath?: string;\n packaged: string;\n}): string {\n if (envPath) return envPath;\n return (\n [packaged, bundled].find((candidate) => existsSync(candidate)) ?? packaged\n );\n}\n\nfunction hasEntries({\n directory,\n isEntry,\n}: {\n directory: string;\n isEntry: (entry: Dirent) => boolean;\n}): boolean {\n try {\n return readdirSync(directory, { withFileTypes: true }).some(isEntry);\n } catch {\n return false;\n }\n}\n\n/**\n * Warn once, on first use, when an asset directory is missing or empty. Waiting\n * for first use keeps servers that never call the service quiet.\n */\nfunction createAssetCheck({\n directory,\n envName,\n isEntry,\n name,\n}: {\n directory: string;\n envName: string;\n isEntry: (entry: Dirent) => boolean;\n name: string;\n}): () => void {\n let checked = false;\n return () => {\n if (checked) return;\n checked = true;\n if (hasEntries({ directory, isEntry })) return;\n // stderr keeps the stdio transport's stdout clean and still reaches CloudWatch\n // eslint-disable-next-line no-console\n console.warn(\n `${WARNING_PREFIX} No ${name} found in \"${directory}\". Copy the ${name} directory from getMcpAssetPaths() in @jaypie/mcp/assets beside the bundle or set ${envName}.`,\n );\n };\n}\n\n/** Run `check` before any property of `target` is read */\nfunction withAssetCheck<T extends object>({\n check,\n target,\n}: {\n check: () => void;\n target: T;\n}): T {\n return new Proxy(target, {\n get(object, property, receiver) {\n check();\n return Reflect.get(object, property, receiver);\n },\n });\n}\n\nconst RELEASE_NOTES_PATH = resolveAssetDirectory({\n bundled: path.join(MODULE_DIRECTORY, MCP_ASSET_DIRECTORY.RELEASE_NOTES),\n envPath: process.env[ENV_RELEASE_NOTES_PATH],\n packaged: PACKAGE_ASSET_PATHS.releaseNotes,\n});\n\n// Bundled Jaypie skills ship inside the @jaypie/mcp package. MCP_BUILTIN_SKILLS_PATH\n// relocates them without disabling the built-in layer.\nconst BUILTIN_SKILLS_PATH = resolveAssetDirectory({\n bundled: path.join(MODULE_DIRECTORY, MCP_ASSET_DIRECTORY.SKILLS),\n envPath: process.env[ENV_BUILTIN_SKILLS_PATH],\n packaged: PACKAGE_ASSET_PATHS.skills,\n});\n\nconst checkReleaseNoteAssets = createAssetCheck({\n directory: RELEASE_NOTES_PATH,\n envName: ENV_RELEASE_NOTES_PATH,\n isEntry: (entry) => entry.isDirectory(),\n name: MCP_ASSET_DIRECTORY.RELEASE_NOTES,\n});\n\nconst checkSkillAssets = createAssetCheck({\n directory: BUILTIN_SKILLS_PATH,\n envName: ENV_BUILTIN_SKILLS_PATH,\n isEntry: (entry) => entry.isFile() && entry.name.endsWith(MARKDOWN_EXTENSION),\n name: MCP_ASSET_DIRECTORY.SKILLS,\n});\n\nconst LOCAL_SKILLS_NAMESPACE = \"local\";\nconst JAYPIE_SKILLS_NAMESPACE = \"jaypie\";\nconst LAYER_SEPARATOR = \":\";\n\n// Skill layers resolved in order: a client's MCP_SKILLS_PATH layers on top of\n// the built-in Jaypie skill pack so `skill(\"aws\")` prefers the client's copy\n// while still exposing bundled Jaypie docs under the `jaypie:` namespace.\nconst skillLayers: LayeredStoreLayer[] = [];\n\nif (process.env.MCP_SKILLS_PATH) {\n skillLayers.push({\n namespace: LOCAL_SKILLS_NAMESPACE,\n store: createMarkdownStore({ path: process.env.MCP_SKILLS_PATH }),\n });\n}\n\nskillLayers.push({\n namespace: JAYPIE_SKILLS_NAMESPACE,\n store: withAssetCheck({\n check: checkSkillAssets,\n target: createMarkdownStore({ path: BUILTIN_SKILLS_PATH }),\n }),\n});\n\nconst skillStore = createLayeredStore({\n layers: skillLayers,\n separator: LAYER_SEPARATOR,\n});\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\ninterface ReleaseNoteFrontMatter {\n date?: string;\n summary?: string;\n version?: string;\n}\n\nasync function parseReleaseNoteFile(filePath: string): Promise<{\n date?: string;\n filename: string;\n summary?: string;\n version?: string;\n}> {\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n const filename = path.basename(filePath, \".md\");\n\n if (content.startsWith(\"---\")) {\n const frontMatter =\n parseFrontmatter<ReleaseNoteFrontMatter>(content).data;\n return {\n date: frontMatter.date,\n filename,\n summary: frontMatter.summary,\n version: frontMatter.version || filename,\n };\n }\n\n return { filename, version: filename };\n } catch {\n return { filename: path.basename(filePath, \".md\") };\n }\n}\n\nfunction formatReleaseNoteListItem(note: {\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n}): string {\n const { date, packageName, summary, version } = note;\n const parts = [`* ${packageName}@${version}`];\n\n if (date) {\n parts.push(`(${date})`);\n }\n\n if (summary) {\n parts.push(`- ${summary}`);\n }\n\n return parts.join(\" \");\n}\n\nasync function getPackageReleaseNotes(packageName: string): Promise<\n Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n }>\n> {\n const packageDir = path.join(RELEASE_NOTES_PATH, packageName);\n try {\n const files = await fs.readdir(packageDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const notes = await Promise.all(\n mdFiles.map(async (file) => {\n const parsed = await parseReleaseNoteFile(path.join(packageDir, file));\n return { ...parsed, packageName };\n }),\n );\n\n return notes.sort((a, b) => {\n if (!a.version || !b.version) return 0;\n try {\n return gt(a.version, b.version) ? -1 : 1;\n } catch {\n return b.version.localeCompare(a.version);\n }\n });\n } catch {\n return [];\n }\n}\n\nfunction filterReleaseNotesSince(\n notes: Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n }>,\n sinceVersion: string,\n): Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n}> {\n return notes.filter((note) => {\n if (!note.version) return false;\n try {\n return gt(note.version, sinceVersion);\n } catch {\n return false;\n }\n });\n}\n\n// =============================================================================\n// SKILL SERVICE\n// =============================================================================\n\nexport const skillService = createSkillService(skillStore);\n\n// =============================================================================\n// VERSION SERVICE\n// =============================================================================\n\nexport const versionService = fabricService({\n alias: \"version\",\n description: `Prints the current version and hash, \\`${BUILD_VERSION_STRING}\\``,\n input: {},\n service: async () => BUILD_VERSION_STRING,\n});\n\n// =============================================================================\n// RELEASE NOTES SERVICE\n// =============================================================================\n\ninterface ReleaseNotesInput {\n package?: string;\n since_version?: string;\n version?: string;\n}\n\nexport const releaseNotesService = fabricService({\n alias: \"release_notes\",\n description:\n \"Browse Jaypie package release notes. Commands: list, read. Call with no args for help.\",\n input: {\n command: {\n description: \"Command to execute (omit for help)\",\n required: false,\n type: String,\n },\n input: {\n description: \"Command parameters\",\n required: false,\n type: Object,\n },\n },\n service: async ({\n command,\n input: params,\n }: {\n command?: string;\n input?: ReleaseNotesInput;\n }) => {\n if (!command || command === \"help\") {\n return RELEASE_NOTES_HELP;\n }\n\n checkReleaseNoteAssets();\n const p = params || {};\n\n switch (command) {\n case \"list\": {\n const entries = await fs.readdir(RELEASE_NOTES_PATH, {\n withFileTypes: true,\n });\n const packageDirs = entries\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name);\n const packagesToList = p.package\n ? packageDirs.filter((pkg) => pkg === p.package)\n : packageDirs;\n\n if (packagesToList.length === 0 && p.package) {\n return `No release notes found for package \"${p.package}\".`;\n }\n\n const allNotes = await Promise.all(\n packagesToList.map((pkg) => getPackageReleaseNotes(pkg)),\n );\n let flatNotes = allNotes.flat();\n\n if (p.since_version) {\n flatNotes = filterReleaseNotesSince(flatNotes, p.since_version);\n }\n\n if (flatNotes.length === 0) {\n const filterDesc = p.since_version\n ? ` newer than ${p.since_version}`\n : \"\";\n return `No release notes found${filterDesc}.`;\n }\n\n return flatNotes.map(formatReleaseNoteListItem).join(\"\\n\");\n }\n\n case \"read\": {\n if (!p.package) throw new BadRequestError(\"package is required\");\n if (!p.version) throw new BadRequestError(\"version is required\");\n const filePath = path.join(\n RELEASE_NOTES_PATH,\n p.package,\n `${p.version}.md`,\n );\n return fs.readFile(filePath, \"utf-8\");\n }\n\n default:\n throw new BadRequestError(\n `Unknown command: ${command}. Use release_notes() for help.`,\n );\n }\n },\n});\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaypie/mcp",
3
- "version": "0.8.154",
3
+ "version": "0.8.155",
4
4
  "description": "Jaypie MCP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,6 +16,12 @@
16
16
  "default": "./dist/index.js"
17
17
  }
18
18
  },
19
+ "./assets": {
20
+ "import": {
21
+ "types": "./dist/assets.d.ts",
22
+ "default": "./dist/assets.js"
23
+ }
24
+ },
19
25
  "./http": {
20
26
  "import": {
21
27
  "types": "./dist/http/index.d.ts",
@@ -66,9 +72,9 @@
66
72
  "@rollup/plugin-typescript": "^12.3.0",
67
73
  "@types/express": "^5.0.3",
68
74
  "@types/semver": "^7.7.1",
75
+ "esbuild": "^0.28.2",
69
76
  "express": "^5.1.0",
70
77
  "rollup": "^4.53.3",
71
- "rollup-plugin-copy": "^3.5.0",
72
78
  "rollup-plugin-dts": "^6.1.1",
73
79
  "typescript": "^5.9.3"
74
80
  },
@@ -0,0 +1,24 @@
1
+ ---
2
+ version: 0.8.155
3
+ date: 2026-09-12
4
+ summary: Bundled builds serve skills and release notes, warn when either is missing, and add @jaypie/mcp/assets
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **Bundled builds** find `skills/` and `release-notes/` beside the bundle when
10
+ the package copy is absent. `MCP_BUILTIN_SKILLS_PATH` and
11
+ `MCP_RELEASE_NOTES_PATH` still win when set (#543).
12
+ - **`release_notes` help** is inlined, so a bundle no longer needs
13
+ `suites/docs/release-notes/help.md`.
14
+ - **Warning** logged to stderr on the first `skill` or `release_notes` call
15
+ when its directory is missing or empty, naming the directory and variable.
16
+ - **`@jaypie/mcp/assets`** exports `getMcpAssetPaths()` and
17
+ `MCP_ASSET_DIRECTORY` for build scripts that copy the markdown beside a
18
+ bundle.
19
+ - **`mcp` skill** documents the bundling copy step; the `express` skill's
20
+ esbuild banner aliases its imports to avoid duplicate declarations.
21
+ - **`github` skill** documents the repository standard: the `main` ruleset,
22
+ environment classes and deployment branch policies, and secrets and variables
23
+ layered across organization, repository, and environment scopes. Linked from
24
+ `cicd`, `cicd-environments`, `infrastructure`, and `skills`.
package/skills/agents.md CHANGED
@@ -34,7 +34,7 @@ Complete stack styles, techniques, and traditions.
34
34
 
35
35
  Contents: index, releasenotes
36
36
  Development: apikey, documentation, errors, llm, logs, mdaml, mocks, monorepo, repokit, style, subpackages, tests, tools
37
- Infrastructure: apigateway, aws, cdk, cicd, datadog, dns, dynamodb, email, express, lambda, migrations, ports, secrets, sqs, streaming, variables, waf, web, websockets
37
+ Infrastructure: apigateway, aws, cdk, cicd, datadog, dns, dynamodb, email, express, github, lambda, migrations, ports, secrets, sqs, streaming, variables, waf, web, websockets
38
38
  Patterns: api, fabric, handlers, models, repository, services, vocabulary
39
39
  Recipes: recipe-api-server
40
40
  Meta: issues, jaypie, mcp, skills
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: GitHub Environments configuration for CDK deployments
3
- related: cicd, cicd-actions, cicd-deploy, variables
3
+ related: cicd, cicd-actions, cicd-deploy, github, variables
4
4
  ---
5
5
 
6
6
  # GitHub Environments Configuration
package/skills/cicd.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: GitHub Actions CI/CD workflows
3
- related: cicd-actions, cicd-deploy, cicd-environments, cdk, tests
3
+ related: cicd-actions, cicd-deploy, cicd-environments, cdk, github, tests
4
4
  ---
5
5
 
6
6
  # CI/CD with GitHub Actions
@@ -14,6 +14,7 @@ Jaypie projects use GitHub Actions for continuous integration and deployment.
14
14
  | `cicd-actions` | Reusable composite actions for workflows |
15
15
  | `cicd-deploy` | CDK deployment workflows (sandbox, production) |
16
16
  | `cicd-environments` | GitHub Environments configuration |
17
+ | `github` | Repository rulesets, environments, and secret and variable scopes |
17
18
 
18
19
  ## Action Versions
19
20
 
package/skills/express.md CHANGED
@@ -337,12 +337,12 @@ await build({
337
337
  ...builtinModules.map(m => `node:${m}`),
338
338
  ],
339
339
  banner: {
340
- js: `import { createRequire } from "module";
341
- import { fileURLToPath } from "url";
342
- import { dirname } from "path";
343
- const require = createRequire(import.meta.url);
344
- const __filename = fileURLToPath(import.meta.url);
345
- const __dirname = dirname(__filename);`,
340
+ js: `import { createRequire as __bannerCreateRequire } from "node:module";
341
+ import { fileURLToPath as __bannerFileURLToPath } from "node:url";
342
+ import { dirname as __bannerDirname } from "node:path";
343
+ const require = __bannerCreateRequire(import.meta.url);
344
+ const __filename = __bannerFileURLToPath(import.meta.url);
345
+ const __dirname = __bannerDirname(__filename);`,
346
346
  },
347
347
  });
348
348
  ```
@@ -365,6 +365,8 @@ Node.js always treats `.mjs` files as ESM, regardless of `package.json`. No need
365
365
 
366
366
  Some dependencies use `require()` even when bundled. The banner provides CommonJS shims for `require`, `__filename`, and `__dirname`.
367
367
 
368
+ Alias every banner import. esbuild hoists bundled imports such as `import { fileURLToPath } from "node:url"` into the same top-level scope as the banner, and a shared name fails at load with `Identifier 'fileURLToPath' has already been declared`.
369
+
368
370
  ### CDK Integration
369
371
 
370
372
  Reference the `.mjs` file in your CDK stack:
@@ -0,0 +1,280 @@
1
+ ---
2
+ description: GitHub repository standard for branch rulesets, environments, and layered secrets and variables
3
+ related: cicd, cicd-actions, cicd-deploy, cicd-environments, secrets, variables
4
+ ---
5
+
6
+ # GitHub Repository Standard
7
+
8
+ Configuration for a Jaypie repository on GitHub. Values are layered in three scopes:
9
+
10
+ | Scope | Holds |
11
+ |-------|-------|
12
+ | Organization | Values shared by every repository: region, sponsor, log levels, per-account AWS references, CI credentials, shared integrations |
13
+ | Repository | Project identity and defaults for the lower environments |
14
+ | Environment | Per-environment deployment targets and overrides, most heavily in production-class environments |
15
+
16
+ Resolution order for `vars.*` and `secrets.*` is **environment → repository → organization**. A name defined at a narrower scope shadows the broader one for any job that targets that environment. Jobs with no `environment:` resolve repository, then organization.
17
+
18
+ ## Repository Settings
19
+
20
+ | Setting | Value |
21
+ |---------|-------|
22
+ | Default branch | `main` |
23
+ | Merge methods | merge, squash, and rebase allowed |
24
+ | Delete branch on merge | off |
25
+ | Auto-merge | off |
26
+ | Allowed actions | all |
27
+
28
+ ## Branch Protection
29
+
30
+ Protect `main` with a repository ruleset named `main`.
31
+
32
+ | Field | Value |
33
+ |-------|-------|
34
+ | Target | `branch` |
35
+ | Enforcement | `active` |
36
+ | Include | `~DEFAULT_BRANCH`, `refs/heads/main` |
37
+ | Bypass actors | Organization admins and the repository admin role (`RepositoryRole` 5), or a development team, `always` |
38
+
39
+ | Rule | Setting |
40
+ |------|---------|
41
+ | `deletion` | blocked |
42
+ | `non_fast_forward` | blocked |
43
+ | `pull_request` | required; stale reviews dismissed on push; extra approval for unattributed changes |
44
+ | `required_status_checks` | job names from the check workflow; strict (branch up to date) |
45
+
46
+ Required status check contexts are the `name:` of jobs in the check workflow. Matrix jobs append the matrix value (`Test (24)`).
47
+
48
+ ```json
49
+ {
50
+ "name": "main",
51
+ "target": "branch",
52
+ "enforcement": "active",
53
+ "conditions": {
54
+ "ref_name": { "include": ["~DEFAULT_BRANCH", "refs/heads/main"], "exclude": [] }
55
+ },
56
+ "bypass_actors": [
57
+ { "actor_id": null, "actor_type": "OrganizationAdmin", "bypass_mode": "always" },
58
+ { "actor_id": 5, "actor_type": "RepositoryRole", "bypass_mode": "always" }
59
+ ],
60
+ "rules": [
61
+ { "type": "deletion" },
62
+ { "type": "non_fast_forward" },
63
+ {
64
+ "type": "pull_request",
65
+ "parameters": {
66
+ "allowed_merge_methods": ["merge", "squash", "rebase"],
67
+ "dismiss_stale_reviews_on_push": true,
68
+ "require_code_owner_review": false,
69
+ "require_extra_approval_for_unattributed_changes": true,
70
+ "require_last_push_approval": false,
71
+ "required_approving_review_count": 0,
72
+ "required_review_thread_resolution": false
73
+ }
74
+ },
75
+ {
76
+ "type": "required_status_checks",
77
+ "parameters": {
78
+ "do_not_enforce_on_create": false,
79
+ "strict_required_status_checks_policy": true,
80
+ "required_status_checks": [
81
+ { "context": "Lint", "integration_id": 15368 },
82
+ { "context": "Typecheck", "integration_id": 15368 },
83
+ { "context": "Test (24)", "integration_id": 15368 }
84
+ ]
85
+ }
86
+ }
87
+ ]
88
+ }
89
+ ```
90
+
91
+ `integration_id` 15368 is GitHub Actions.
92
+
93
+ ```bash
94
+ gh api repos/<org>/<repo>/rulesets --method POST --input ruleset.json
95
+ gh api repos/<org>/<repo>/rules/branches/main # effective rules, including org rulesets
96
+ ```
97
+
98
+ ## Environments
99
+
100
+ Environment names are lowercase and match `PROJECT_ENV`.
101
+
102
+ | Environment | Class | Protection | Deployment refs |
103
+ |-------------|-------|------------|-----------------|
104
+ | `sandbox` | lower | none | any branch or tag |
105
+ | `development` | lower | none | any branch or tag |
106
+ | `test` | production-class | branch policy | custom |
107
+ | `demo` | production-class | branch policy | `branch/*`, `feat/*`, `fix/*`, `main`; tags `demo-*`, `v0.*`, `v1.*` |
108
+ | `production` | production-class | branch policy | `main`; tags `production-*`, `v0.*`, `v1.*.*` |
109
+
110
+ Production may instead use the "protected branches only" policy (`protected_branches: true`), which admits `main` through its ruleset.
111
+
112
+ ```bash
113
+ # Custom deployment policy
114
+ gh api repos/<org>/<repo>/environments/production --method PUT \
115
+ -F "deployment_branch_policy[protected_branches]=false" \
116
+ -F "deployment_branch_policy[custom_branch_policies]=true"
117
+ gh api repos/<org>/<repo>/environments/production/deployment-branch-policies \
118
+ --method POST -f name=main -f type=branch
119
+ gh api repos/<org>/<repo>/environments/production/deployment-branch-policies \
120
+ --method POST -f name='production-*' -f type=tag
121
+ ```
122
+
123
+ ## Organization
124
+
125
+ Values every repository can assume.
126
+
127
+ ### Variables
128
+
129
+ | Variable | Example | Purpose |
130
+ |----------|---------|---------|
131
+ | `AWS_REGION` | `us-east-1` | Deployment region |
132
+ | `LOG_LEVEL` | `trace` | Application log level default |
133
+ | `MODULE_LOG_LEVEL` | `warn` | Dependency log level default |
134
+ | `PROJECT_SPONSOR` | `<org>` | Sponsor segment of stack names |
135
+ | `ACCOUNT_AWS_ROLE_ARN` | sandbox account role | Default account deploy role |
136
+ | `ACCOUNT_DATADOG_API_KEY_ARN` | sandbox account secret | Default account Datadog key secret |
137
+ | `ACCOUNT_<ENV>_AWS_ROLE_ARN` | per-account role | Deploy role for the account hosting `<ENV>` |
138
+ | `ACCOUNT_<ENV>_DATADOG_API_KEY_ARN` | per-account secret | Datadog key secret in the account hosting `<ENV>` |
139
+ | `ACCOUNT_<ENV>_<SERVICE>_*` | `ACCOUNT_DEVELOPMENT_AUTH0_DOMAIN` | Other per-account references |
140
+ | Shared public identifiers | `HUBSPOT_PORTAL_ID`, `SLACK_TEAM_ID`, `CLOUDFLARE_TURNSTILE_SITE` | Integrations common to the organization |
141
+
142
+ `ACCOUNT_*` variables are the organization record of each AWS account. `<ENV>` is `SANDBOX`, `DEVELOPMENT`, `TEST`, `DEMO`, or `PRODUCTION`; environments sharing an account carry the same values. Unprefixed `ACCOUNT_*` values point at the sandbox account.
143
+
144
+ ### Secrets
145
+
146
+ | Secret | Purpose |
147
+ |--------|---------|
148
+ | `CICD_<PROVIDER>_API_KEY` | LLM provider keys for CI test jobs (`CICD_ANTHROPIC_API_KEY`, `CICD_OPENAI_API_KEY`, ...) |
149
+ | `DATADOG_CICD_API_KEY`, `DATADOG_CICD_APP_KEY` | Datadog CI visibility |
150
+ | `NPM_TOKEN` | Private package install |
151
+ | `SLACK_WEBHOOK_<CHANNEL>` | Notification channels (`SLACK_WEBHOOK_NONPRODUCTION_DEPLOYMENT`, `SLACK_WEBHOOK_PRODUCTION_DEPLOYMENT`) |
152
+ | Shared service credentials | Licenses and tokens used across repositories (`NUXT_UI_PRO_LICENSE`, `POSTMAN_API_KEY`, `SLACK_BOT_TOKEN`) |
153
+
154
+ The `CICD_` prefix separates keys consumed by CI from runtime keys deployed into applications (`ANTHROPIC_API_KEY`).
155
+
156
+ ## Repository
157
+
158
+ Project identity and the defaults that lower environments inherit.
159
+
160
+ ### Variables
161
+
162
+ | Variable | Example | Purpose |
163
+ |----------|---------|---------|
164
+ | `PROJECT_KEY` | `mockpay` | Project identifier |
165
+ | `PROJECT_SERVICE` | `mockpay` | Service identifier |
166
+ | `AWS_HOSTED_ZONE` | `example.com` | Apex zone |
167
+ | `AWS_SUBDOMAIN` | `evaluations` | Subdomain within the zone |
168
+ | Lower-environment service config | `AUTH0_CLIENT_ID`, `AUTH0_DOMAIN` | Development tenant used unless an environment overrides |
169
+ | Public client identifiers | `DATADOG_APPLICATION_ID`, `DATADOG_CLIENT_TOKEN`, Turnstile site keys | Browser-exposed values |
170
+ | Feature flags | `LLM_EXCHANGE_ENABLED` | Project-wide toggles |
171
+
172
+ A repository variable may shadow an organization default for the whole project (`MODULE_LOG_LEVEL`).
173
+
174
+ ### Secrets
175
+
176
+ | Secret | Purpose |
177
+ |--------|---------|
178
+ | Lower-environment service secrets | `AUTH0_CLIENT_SECRET` for the development tenant |
179
+ | Project integration credentials | Third-party keys specific to the project |
180
+
181
+ ## Environment
182
+
183
+ Deployment targets live on every environment. Overrides concentrate on production-class environments.
184
+
185
+ ### Variables on Every Environment
186
+
187
+ | Variable | Example | Purpose |
188
+ |----------|---------|---------|
189
+ | `AWS_ROLE_ARN` | `ACCOUNT_<ENV>_AWS_ROLE_ARN` value | OIDC deploy role |
190
+ | `DATADOG_API_KEY_ARN` | `ACCOUNT_<ENV>_DATADOG_API_KEY_ARN` value | Datadog key secret in the target account |
191
+ | `PROJECT_ENV` | `production` | Environment identifier; matches the environment name |
192
+ | `PROJECT_NONCE` | `ba342b91` | 8 hex characters, unique per environment |
193
+
194
+ Workflows may also read `vars.ACCOUNT_<ENV>_AWS_ROLE_ARN` from the organization directly in a workflow-level `env:` block, one workflow per environment.
195
+
196
+ ### Overrides
197
+
198
+ | Override | Scope | Example |
199
+ |----------|-------|---------|
200
+ | Service tenant | production-class | `AUTH0_CLIENT_ID`, `AUTH0_DOMAIN`, `AUTH0_CLIENT_SECRET` for the production tenant |
201
+ | Hosted zone | per environment | `AWS_HOSTED_ZONE` = `sandbox.example.com` |
202
+ | Runtime keys | production | `ANTHROPIC_API_KEY` shadowing the repository key |
203
+ | Environment-only resources | per environment | Additional role ARNs, bastion IDs, vendor endpoints |
204
+
205
+ ## Values in Secrets or Variables
206
+
207
+ | Kind | Scope type |
208
+ |------|------------|
209
+ | Credentials, signing keys, private keys | secret |
210
+ | AWS ARNs, including Secrets Manager ARNs | variable |
211
+ | Public client identifiers (Auth0 client ID, Datadog RUM client token, Turnstile site key) | variable |
212
+ | Secret material the application reads at runtime | AWS Secrets Manager, referenced by a `*_ARN` variable |
213
+
214
+ ## Workflows
215
+
216
+ ### Environment Selection
217
+
218
+ | Pattern | Shape |
219
+ |---------|-------|
220
+ | One workflow per environment | `deploy-env-<env>.yml` with literal `environment: <env>` |
221
+ | Reusable deploy | `deploy.yml` on `workflow_call` with `environment: ${{ inputs.environment }}`; callers pass `secrets: inherit` |
222
+ | Manual | `workflow_dispatch` with a `choice` input listing environments |
223
+
224
+ ### Triggers
225
+
226
+ | Environment | Branches | Tags |
227
+ |-------------|----------|------|
228
+ | `sandbox` | `branch/*`, `claude/*`, `feat/*`, `fix/*`, `sandbox/*` | `sandbox-*` |
229
+ | `development` | `main`, `development/*` | `development-*` |
230
+ | `production` | `main` | `production-*` |
231
+
232
+ Deploy workflows set `concurrency: deploy-env-<env>` with `cancel-in-progress: false`. Production deploy jobs declare `needs: [lint, test]`.
233
+
234
+ ### Deploy Job
235
+
236
+ ```yaml
237
+ jobs:
238
+ deploy:
239
+ environment: production
240
+ permissions:
241
+ contents: read
242
+ id-token: write
243
+ runs-on: ubuntu-latest
244
+ steps:
245
+ - uses: ./.github/actions/setup-environment
246
+ with:
247
+ aws-region: ${{ vars.AWS_REGION }}
248
+ hosted-zone: ${{ vars.AWS_HOSTED_ZONE }}
249
+ log-level: ${{ vars.LOG_LEVEL }}
250
+ module-log-level: ${{ vars.MODULE_LOG_LEVEL }}
251
+ project-env: ${{ vars.PROJECT_ENV }}
252
+ project-key: ${{ vars.PROJECT_KEY }}
253
+ project-nonce: ${{ vars.PROJECT_NONCE }}
254
+ project-sponsor: ${{ vars.PROJECT_SPONSOR }}
255
+ - uses: ./.github/actions/configure-aws
256
+ with:
257
+ aws-region: ${{ vars.AWS_REGION }}
258
+ role-arn: ${{ vars.AWS_ROLE_ARN }}
259
+ ```
260
+
261
+ - Deploy jobs declare `permissions` with `contents: read` and `id-token: write`
262
+ - Composite actions under `.github/actions` receive every value through `inputs` and never reference `vars.*` or `secrets.*`
263
+ - `setup-environment` supplies fallback defaults when an input is empty
264
+ - The OIDC subject for an environment job is `repo:<org>/<repo>:environment:<env>` (see ~cicd-environments for the trust policy)
265
+
266
+ ## Audit
267
+
268
+ ```bash
269
+ gh api repos/<org>/<repo>/rulesets
270
+ gh api repos/<org>/<repo>/environments --jq '.environments[]|{name,protection_rules,deployment_branch_policy}'
271
+ gh api repos/<org>/<repo>/environments/<env>/deployment-branch-policies
272
+ gh api repos/<org>/<repo>/actions/variables
273
+ gh api repos/<org>/<repo>/actions/secrets
274
+ gh api repos/<org>/<repo>/environments/<env>/variables
275
+ gh api repos/<org>/<repo>/environments/<env>/secrets
276
+ gh api "repos/<org>/<repo>/actions/organization-variables?per_page=100"
277
+ gh api "repos/<org>/<repo>/actions/organization-secrets?per_page=100"
278
+ ```
279
+
280
+ Organization-scoped listings (`orgs/<org>/actions/*`, `orgs/<org>/rulesets`) require the `admin:org` token scope. The `repos/.../organization-*` endpoints list what the repository can read without it and paginate at 10.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: AWS, CDK, CI/CD, and observability
3
- related: aws, cdk, cicd, cicd-actions, cicd-deploy, cicd-environments, datadog, dns, dynamodb, email, secrets, variables, web, websockets
3
+ related: aws, cdk, cicd, cicd-actions, cicd-deploy, cicd-environments, datadog, dns, dynamodb, email, github, secrets, variables, web, websockets
4
4
  ---
5
5
 
6
6
  # Infrastructure
@@ -21,6 +21,7 @@ Cloud infrastructure and deployment patterns.
21
21
  | `dns` | DNS and domain configuration |
22
22
  | `dynamodb` | DynamoDB patterns and queries |
23
23
  | `email` | SES inbound email receiving |
24
+ | `github` | GitHub rulesets, environments, and layered secrets and variables |
24
25
  | `migrations` | DynamoDB migration custom resources |
25
26
  | `secrets` | AWS Secrets Manager |
26
27
  | `variables` | Environment variables reference |
package/skills/mcp.md CHANGED
@@ -104,6 +104,36 @@ Methods: `initialize` (echoes a supported `protocolVersion`, else `2025-06-18`),
104
104
  - The endpoint is stateless: no sessions, no `Mcp-Session-Id`, no server-initiated GET stream. Every POST stands alone, which suits Lambda.
105
105
  - Put authentication in `setup` or `validate` so it runs for both JSON and SSE requests.
106
106
 
107
+ #### Bundling
108
+
109
+ A bundler (esbuild) inlines the code but not the markdown that `skill` and `release_notes` read. Without that markdown, `skill("index")` answers only its heading, and the first `skill` or `release_notes` call logs a `[@jaypie/mcp]` warning to stderr naming the missing directory.
110
+
111
+ Copy both directories beside the bundle at build time. `getMcpAssetPaths` from `@jaypie/mcp/assets` returns their absolute paths in the installed package and loads nothing else.
112
+
113
+ ```javascript
114
+ // esbuild.config.mjs (build options from skill("express"))
115
+ import { cpSync } from "node:fs";
116
+ import { join } from "node:path";
117
+ import { build } from "esbuild";
118
+ import { getMcpAssetPaths, MCP_ASSET_DIRECTORY } from "@jaypie/mcp/assets";
119
+
120
+ const OUT_DIR = "dist";
121
+
122
+ await build({ bundle: true, format: "esm", outfile: join(OUT_DIR, "index.mjs"), platform: "node" });
123
+
124
+ const assets = getMcpAssetPaths();
125
+ cpSync(assets.releaseNotes, join(OUT_DIR, MCP_ASSET_DIRECTORY.RELEASE_NOTES), { recursive: true });
126
+ cpSync(assets.skills, join(OUT_DIR, MCP_ASSET_DIRECTORY.SKILLS), { recursive: true });
127
+ ```
128
+
129
+ Each directory resolves to the first match: its environment variable, the installed package, then the directory beside the bundle.
130
+
131
+ | Variable | Description |
132
+ |----------|-------------|
133
+ | `MCP_BUILTIN_SKILLS_PATH` | Jaypie skills directory (namespace `jaypie`) |
134
+ | `MCP_RELEASE_NOTES_PATH` | Release notes directory |
135
+ | `MCP_SKILLS_PATH` | Client skills directory layered over the Jaypie skills (namespace `local`) |
136
+
107
137
  ### SDK Transport (`mcpExpressHandler`)
108
138
 
109
139
  `mcpExpressHandler` from `@jaypie/mcp` wraps the SDK's `StreamableHTTPServerTransport` with optional sessions. It suits a long-running Express server, not Lambda.
package/skills/skills.md CHANGED
@@ -17,7 +17,7 @@ Look up skills by alias: `mcp__jaypie__skill(alias)`
17
17
  |----------|--------|
18
18
  | contents | index, releasenotes |
19
19
  | development | apikey, documentation, errors, llm, logs, mdaml, mocks, monorepo, repokit, style, subpackages, tests, tools |
20
- | infrastructure | apigateway, aws, cdk, cicd, datadog, dns, dynamodb, email, express, lambda, migrations, ports, secrets, sqs, streaming, variables, waf, web, websockets |
20
+ | infrastructure | apigateway, aws, cdk, cicd, datadog, dns, dynamodb, email, express, github, lambda, migrations, ports, secrets, sqs, streaming, variables, waf, web, websockets |
21
21
  | patterns | api, fabric, handlers, models, repository, services, vocabulary |
22
22
  | recipes | recipe-api-server |
23
23
  | meta | issues, jaypie, mcp, skills |
@@ -14,7 +14,7 @@ Jaypie provides response streaming for real-time data delivery. Three handler pa
14
14
  | `lambdaStreamHandler` | `@jaypie/lambda` | Pure Lambda Function URL streaming |
15
15
  | `expressStreamHandler` | `@jaypie/express` | Express routes with SSE |
16
16
  | `createLambdaStreamHandler` | `@jaypie/express` | Express app on Lambda with streaming |
17
- | `mcpHttpHandler` | `@jaypie/mcp/http` | MCP streamable HTTP (JSON or SSE) over a `ServiceSuite`; see `skill("mcp")` |
17
+ | `mcpHttpHandler` | `@jaypie/mcp/http` | MCP streamable HTTP (JSON or SSE) over a `ServiceSuite`; bundles copy the skill and release note markdown, see `skill("mcp")` |
18
18
 
19
19
  ## Stream Formats
20
20
 
@@ -166,8 +166,9 @@ await layered.put({ alias: "local:new", content: "# New" }); // must be qualifie
166
166
 
167
167
  The MCP server itself uses `createLayeredStore` to place `MCP_SKILLS_PATH`
168
168
  (the client's local library, namespace `local`) over the bundled Jaypie
169
- skills (namespace `jaypie`). Set `MCP_BUILTIN_SKILLS_PATH` if a bundler
170
- needs to relocate the Jaypie base layer.
169
+ skills (namespace `jaypie`). A bundled build finds the Jaypie base layer in
170
+ `skills/` beside the bundle, or at `MCP_BUILTIN_SKILLS_PATH`; see
171
+ `skill("mcp")` for the copy step.
171
172
 
172
173
  ## Plural/Singular Fallback
173
174
 
@@ -1,37 +0,0 @@
1
- # Release Notes
2
-
3
- Browse Jaypie package release notes.
4
-
5
- ## Commands
6
-
7
- | Command | Description | Required Parameters |
8
- |---------|-------------|---------------------|
9
- | `list` | List available release notes | - |
10
- | `read` | Read a specific release note | `package`, `version` |
11
-
12
- ## Options for `list`
13
-
14
- | Option | Description |
15
- |--------|-------------|
16
- | `package` | Filter by package name (e.g., "mcp", "jaypie") |
17
- | `since_version` | Only versions newer than this (e.g., "1.0.0") |
18
-
19
- ## Examples
20
-
21
- ```
22
- release_notes("list")
23
- release_notes("list", { package: "mcp" })
24
- release_notes("list", { package: "jaypie", since_version: "2.0.0" })
25
- release_notes("read", { package: "mcp", version: "0.4.2" })
26
- ```
27
-
28
- ## Available Packages
29
-
30
- Release notes are available for all Jaypie packages including:
31
- - `jaypie` - Main package
32
- - `mcp` - MCP server
33
- - `fabric` - Service handlers and adapters
34
- - `llm` - LLM provider interface
35
- - And more...
36
-
37
- Use `release_notes("list")` to see all available packages and versions.