@jaypie/mcp 0.8.157 → 0.8.159

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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Skill/vocabulary storage with pluggable backends (pre-1.0)
3
- related: fabric, mcp
3
+ related: dynamodb, fabric, mcp
4
4
  ---
5
5
 
6
6
  # @jaypie/tildeskill
@@ -14,6 +14,8 @@ This package provides a storage abstraction for skill/vocabulary documents with
14
14
  - Loading skills from markdown files with YAML frontmatter
15
15
  - In-memory storage for testing
16
16
  - Layered composition of multiple stores with namespace prefixes
17
+ - DynamoDB storage through the `@jaypie/tildeskill/dynamodb` subpath
18
+ - Store-to-store sync with `syncSkills`
17
19
  - Consistent alias normalization and validation
18
20
  - Filtering by namespace and tags
19
21
  - Searching across alias, name, description, content, and tags
@@ -46,6 +48,7 @@ interface ListFilter {
46
48
  }
47
49
 
48
50
  interface SkillStore {
51
+ delete(alias: string): Promise<boolean>;
49
52
  find(alias: string): Promise<SkillRecord | null>;
50
53
  get(alias: string): Promise<SkillRecord | null>;
51
54
  getByNickname(nickname: string): Promise<SkillRecord[]>;
@@ -87,6 +90,71 @@ const store = createMemoryStore([
87
90
  const skill = await store.get("test");
88
91
  ```
89
92
 
93
+ ## DynamoDB Store
94
+
95
+ ```typescript
96
+ import { initClient } from "@jaypie/dynamodb";
97
+ import { createSkillService } from "@jaypie/tildeskill";
98
+ import { createDynamoDbStore } from "@jaypie/tildeskill/dynamodb";
99
+
100
+ initClient();
101
+ const store = createDynamoDbStore({ category: "jaypie" });
102
+ const skillService = createSkillService(store);
103
+ ```
104
+
105
+ `@jaypie/dynamodb` is an optional peer dependency. Only the
106
+ `@jaypie/tildeskill/dynamodb` subpath imports it, so markdown-only consumers
107
+ never load it. The store loads it with a dynamic import on first use, so a
108
+ CJS consumer shares the host's initialized client. Call `initClient()` before
109
+ using the store.
110
+
111
+ - Records are `skill` entities at APEX scope. `category` is the store
112
+ namespace; `alias` is unqualified.
113
+ - Ids are deterministic: `uuidv5("<category>:<alias>", SKILL_NAMESPACE)`.
114
+ `SKILL_NAMESPACE` is exported from the subpath and never changes.
115
+ - `metadata.hash` holds `hashSkill(record)` (sha256) for change detection.
116
+ - `includes`, `nicknames`, and `related` are stored in `metadata`, since
117
+ `related` is reserved for entity references.
118
+ - `list`, `getByNickname`, and `search` query `indexModelCategory`.
119
+ `createDynamoDbStore` calls `registerSkillModel()`; declare the index in CDK
120
+ by calling `registerSkillModel()` before `getAllRegisteredIndexes()`.
121
+ - `find` keeps the plural/singular fallback.
122
+ - `delete` soft deletes with `deleteEntity`. Reads skip archived and deleted
123
+ entities, and a later `put` restores the same id.
124
+
125
+ ## Syncing Stores
126
+
127
+ ```typescript
128
+ import { createMarkdownStore, syncSkills } from "@jaypie/tildeskill";
129
+
130
+ const result = await syncSkills({
131
+ from: createMarkdownStore({ path: "./skills" }),
132
+ to: store,
133
+ });
134
+ // { added: ["aws"], removed: ["retired"], unchanged: ["tests"], updated: [] }
135
+ ```
136
+
137
+ `syncSkills` makes `to` match `from`. It puts records missing from `to`
138
+ (`added`) or whose `hashSkill` differs (`updated`), skips records whose hash
139
+ matches (`unchanged`), and deletes records missing from `from` (`removed`).
140
+ Each list holds aliases sorted alphabetically. An empty `from` with a
141
+ non-empty `to` throws `ConfigurationError` before writing, since a markdown
142
+ store pointed at a missing directory lists nothing. Pass
143
+ `allowEmptySource: true` to empty `to` on purpose.
144
+
145
+ ## Deleting Records
146
+
147
+ Every store implements `delete(alias)`. It matches the exact alias (no
148
+ plural/singular fallback) and returns `true` when a record was removed and
149
+ `false` when none existed.
150
+
151
+ | Store | `delete` behavior |
152
+ |-------|-------------------|
153
+ | memory | Removes the record from the map |
154
+ | markdown | Removes `<alias>.md`; throws `BadRequestError` for an invalid alias |
155
+ | layered | Requires a namespace-qualified alias and delegates to that layer; throws `ConfigurationError` when unqualified |
156
+ | dynamodb | Soft deletes the entity; reads skip it and `put` restores it |
157
+
90
158
  ## Skill Service Factory
91
159
 
92
160
  ```typescript
@@ -102,6 +170,8 @@ const skillService = createSkillService(store);
102
170
  await skillService({ alias: "aws" }); // → skill content (with expandIncludes)
103
171
  await skillService({ alias: "index" }); // → formatted listing
104
172
  await skillService(); // → same as "index"
173
+ await skillService({ alias: "missing" }); // throws NotFoundError
174
+ await skillService({ alias: "../bad" }); // throws BadRequestError
105
175
 
106
176
  // Use with fabricTool for Llm.operate toolkits
107
177
  import { fabricTool } from "@jaypie/fabric/llm";
@@ -159,11 +229,16 @@ const layered = createLayeredStore({
159
229
 
160
230
  await layered.get("aws"); // → { alias: "local:aws", ... }
161
231
  await layered.get("jaypie:aws"); // → { alias: "jaypie:aws", ... }
162
- await layered.find("skills"); // per-layer plural fallback
232
+ await layered.find("skills"); // exact alias in any layer, then fallback
163
233
  await layered.list(); // prefixed aliases from every layer
164
234
  await layered.put({ alias: "local:new", content: "# New" }); // must be qualified
165
235
  ```
166
236
 
237
+ `find` checks every layer for an exact alias before it tries plural/singular
238
+ fallback in any layer. With `local:test` and `jaypie:tests`, `find("tests")`
239
+ returns `jaypie:tests`, so a local skill cannot shadow a Jaypie skill under a
240
+ different spelling. Namespace-qualified aliases search only their own layer.
241
+
167
242
  The MCP server itself uses `createLayeredStore` to place `MCP_SKILLS_PATH`
168
243
  (the client's local library, namespace `local`) over the bundled Jaypie
169
244
  skills (namespace `jaypie`). A bundled build finds the Jaypie base layer in
@@ -13,7 +13,7 @@ Configuration variables used in Jaypie applications.
13
13
  |----------|-------------|--------|
14
14
  | `PROJECT_ENV` | Environment identifier; scopes physical resource names | local, sandbox, kitchen, lab, studio, production |
15
15
  | `PROJECT_KEY` | Project name for logging | e.g., my-api |
16
- | `PROJECT_NONCE` | Unique resource suffix; scopes physical resource names | e.g., dev, staging, prod |
16
+ | `PROJECT_NONCE` | 8 lowercase hex characters, unique per environment; scopes physical resource names | e.g., ba342b91 |
17
17
  | `PROJECT_SPONSOR` | Sponsor segment of the generated stack name and the `sponsor` tag | e.g., finlaysonstudio |
18
18
  | `PROJECT_CHAOS` | Chaos engineering mode | none, partial, full |
19
19
  | `PROJECT_SALT` | Secret salt for HMAC hashing (API keys) | Generated by JaypieEnvSecret |
@@ -23,6 +23,8 @@ Configuration variables used in Jaypie applications.
23
23
 
24
24
  `PROJECT_ENV` and `PROJECT_NONCE` together scope physical AWS resource names to avoid collisions when multiple stacks deploy to the same account. Any explicitly named resource (ECR repos, log groups, ECS task families, EventBridge rules, etc.) must include both values. See `skill("cdk")` for the full naming convention.
25
25
 
26
+ Generate `PROJECT_NONCE` once per environment with `openssl rand -hex 4` and store it as a GitHub environment variable (see `skill("github")`). A word or branch name repeats across projects and accounts, and S3 bucket names are global.
27
+
26
28
  ### Stack Naming
27
29
 
28
30
  `JaypieStack` generates `cdk-{PROJECT_SPONSOR}-{PROJECT_KEY}-{PROJECT_ENV}-{PROJECT_NONCE}[-{key}]`. Every segment must be set in CI or the name contains `undefined`, and a stack name is immutable. `skill("cicd-actions")` sets all four in `setup-environment`.