@jaypie/mcp 0.8.154 → 0.8.156
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 +16 -0
- package/dist/assets.d.ts +34 -0
- package/dist/assets.js +47 -0
- package/dist/assets.js.map +1 -0
- package/dist/suites/docs/help.d.ts +1 -0
- package/dist/suites/docs/help.js +42 -0
- package/dist/suites/docs/help.js.map +1 -0
- package/dist/suites/docs/index.js +87 -18
- package/dist/suites/docs/index.js.map +1 -1
- package/package.json +8 -2
- package/release-notes/constructs/1.2.84.md +19 -0
- package/release-notes/mcp/0.8.155.md +24 -0
- package/release-notes/mcp/0.8.156.md +11 -0
- package/skills/agents.md +1 -1
- package/skills/cdk.md +4 -2
- package/skills/cicd-environments.md +1 -1
- package/skills/cicd.md +2 -1
- package/skills/dynamodb.md +2 -0
- package/skills/express.md +8 -6
- package/skills/github.md +280 -0
- package/skills/infrastructure.md +2 -1
- package/skills/mcp.md +30 -0
- package/skills/migrations.md +1 -1
- package/skills/skills.md +1 -1
- package/skills/streaming.md +1 -1
- package/skills/tildeskill.md +3 -2
- package/dist/suites/docs/release-notes/help.md +0 -37
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:
|
package/dist/assets.d.ts
ADDED
|
@@ -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.
|
|
16
|
+
const BUILD_VERSION_STRING = "@jaypie/mcp@0.8.156#2f9f851b"
|
|
14
17
|
;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
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
|
-
//
|
|
23
|
-
const BUILTIN_SKILLS_PATH =
|
|
24
|
-
path.join(
|
|
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:
|
|
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
|
|
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":"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"}
|
|
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.
|
|
3
|
+
"version": "0.8.156",
|
|
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,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.2.84
|
|
3
|
+
date: 2026-09-13
|
|
4
|
+
summary: JaypieLambda and JaypieMigration grant Query and Scan on table indexes CDK does not declare
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Changes
|
|
8
|
+
|
|
9
|
+
- **`JaypieLambda` `tables`** grants `dynamodb:Query` and `dynamodb:Scan` on
|
|
10
|
+
`${tableArn}/index/*` for every table. CDK adds index ARNs only for indexes
|
|
11
|
+
declared in CDK, so a Lambda could not query a GSI created by a migration or
|
|
12
|
+
owned by an imported table (#546).
|
|
13
|
+
- **`JaypieMigration`** inherits the grant, so a migration can query an index an
|
|
14
|
+
earlier migration created in the same deploy.
|
|
15
|
+
|
|
16
|
+
## Upgrade
|
|
17
|
+
|
|
18
|
+
Remove workarounds that add `dynamodb:Query` or `dynamodb:Scan` on
|
|
19
|
+
`${tableArn}/index/*` to a `JaypieLambda` or `JaypieMigration` role.
|
|
@@ -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`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 0.8.156
|
|
3
|
+
date: 2026-09-13
|
|
4
|
+
summary: Skills document index Query and Scan grants for tables passed to JaypieLambda and JaypieMigration
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Changes
|
|
8
|
+
|
|
9
|
+
- **`cdk`, `dynamodb`, and `migrations` skills** document that tables passed
|
|
10
|
+
via `tables` get `Query` and `Scan` on `${tableArn}/index/*`, covering
|
|
11
|
+
indexes created by migrations that CDK never declares (#546).
|
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
|
package/skills/cdk.md
CHANGED
|
@@ -232,8 +232,10 @@ const handler = new JaypieLambda(this, "ApiHandler", {
|
|
|
232
232
|
|
|
233
233
|
| Tables Count | Permissions | Environment |
|
|
234
234
|
|-------------|-------------|-------------|
|
|
235
|
-
| 1 table | `grantReadWriteData()` | `DYNAMODB_TABLE_NAME` set automatically |
|
|
236
|
-
| 2+ tables | `grantReadWriteData()` for each | No auto env var — set `CDK_ENV_TABLE` manually |
|
|
235
|
+
| 1 table | `grantReadWriteData()` plus `Query`/`Scan` on `index/*` | `DYNAMODB_TABLE_NAME` set automatically |
|
|
236
|
+
| 2+ tables | `grantReadWriteData()` plus `Query`/`Scan` on `index/*` for each | No auto env var — set `CDK_ENV_TABLE` manually |
|
|
237
|
+
|
|
238
|
+
CDK grants index ARNs only for indexes declared in CDK. The explicit `index/*` grant covers GSIs created by migrations (`UpdateTable`) and indexes on imported tables.
|
|
237
239
|
|
|
238
240
|
Single table example — at runtime, use `process.env.DYNAMODB_TABLE_NAME`:
|
|
239
241
|
|
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/dynamodb.md
CHANGED
|
@@ -272,6 +272,8 @@ GSIs are defined using `fabricIndex()` from `@jaypie/fabric`. **Do not create al
|
|
|
272
272
|
|
|
273
273
|
**Important:** DynamoDB allows only **one GSI to be added per deployment**. If you need multiple GSIs, add them sequentially across separate deploys. For production tables, the AWS CLI is often better suited for adding GSIs than CDK (which may try to replace the table).
|
|
274
274
|
|
|
275
|
+
**Grants:** CDK adds `${tableArn}/index/*` to grants only for indexes declared in CDK. GSIs created by migrations or the CLI are invisible to CDK, so a role needs `Query` and `Scan` on the index ARN explicitly. `JaypieLambda` and `JaypieMigration` grant this for every table in `tables`; hand-rolled roles must add it.
|
|
276
|
+
|
|
275
277
|
All GSIs use a composite sort key of `scope#updatedAt` (stored as `{indexName}Sk`). Queries use `begins_with` on the sk to filter by scope; omitting scope lists across all scopes.
|
|
276
278
|
|
|
277
279
|
| GSI Name | Partition Key Pattern | Sort Key | Purpose | Add When |
|
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 =
|
|
344
|
-
const __filename =
|
|
345
|
-
const __dirname =
|
|
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:
|
package/skills/github.md
ADDED
|
@@ -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.
|
package/skills/infrastructure.md
CHANGED
|
@@ -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/migrations.md
CHANGED
|
@@ -47,7 +47,7 @@ new JaypieMigration(this, "SeedData", {
|
|
|
47
47
|
- **Role**: Tagged as `CDK.ROLE.PROCESSING`
|
|
48
48
|
- **Execution**: Uses `cr.Provider` with both `onEventHandler` and `isCompleteHandler` pointing to the same Lambda. The `onEventHandler` returns `PhysicalResourceId` immediately; the migration code runs in `isCompleteHandler` invocations, which are polled by Step Functions until `IsComplete: true`. `Delete` requests skip the migration entirely.
|
|
49
49
|
- **Dependencies**: Use `dependencies` to ensure tables and other resources exist before the migration executes
|
|
50
|
-
- **Permissions**: Tables passed via `tables` get data-plane (`grantReadWriteData`) plus control-plane access (`DescribeTable`, `UpdateTable`, `UpdateTimeToLive`, `UpdateContinuousBackups`) scoped to the table ARN and its indexes — migrations that add GSIs, toggle TTL, or change backups work without extra IAM
|
|
50
|
+
- **Permissions**: Tables passed via `tables` get data-plane (`grantReadWriteData`), `Query` and `Scan` on `${tableArn}/index/*`, plus control-plane access (`DescribeTable`, `UpdateTable`, `UpdateTimeToLive`, `UpdateContinuousBackups`) scoped to the table ARN and its indexes — migrations that add GSIs, toggle TTL, or change backups work without extra IAM, and later migrations can query the GSIs earlier ones created
|
|
51
51
|
|
|
52
52
|
## Migration Lambda Handler
|
|
53
53
|
|
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 |
|
package/skills/streaming.md
CHANGED
|
@@ -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
|
|
package/skills/tildeskill.md
CHANGED
|
@@ -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`).
|
|
170
|
-
|
|
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.
|