@jaypie/mcp 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/createMcpServer.js +34 -0
- package/dist/createMcpServer.js.map +1 -0
- package/dist/index.js +3 -92
- package/dist/index.js.map +1 -1
- package/dist/mcpExpressHandler.js +56 -0
- package/dist/mcpExpressHandler.js.map +1 -0
- package/dist/suite.d.ts +12 -0
- package/dist/suite.js +23 -2426
- package/dist/suite.js.map +1 -1
- package/dist/suites/aws/aws.js +328 -0
- package/dist/suites/aws/aws.js.map +1 -0
- package/dist/suites/aws/help.md +42 -0
- package/dist/suites/aws/index.d.ts +102 -0
- package/dist/suites/aws/index.js +258 -0
- package/dist/suites/aws/index.js.map +1 -0
- package/dist/suites/datadog/datadog.js +828 -0
- package/dist/suites/datadog/datadog.js.map +1 -0
- package/dist/suites/datadog/help.md +39 -0
- package/dist/suites/datadog/index.d.ts +24 -0
- package/dist/suites/datadog/index.js +156 -0
- package/dist/suites/datadog/index.js.map +1 -0
- package/dist/suites/docs/index.d.ts +14 -0
- package/dist/suites/docs/index.js +246 -0
- package/dist/suites/docs/index.js.map +1 -0
- package/dist/suites/docs/release-notes/help.md +37 -0
- package/dist/suites/llm/help.md +46 -0
- package/dist/suites/llm/index.d.ts +23 -0
- package/dist/suites/llm/index.js +65 -0
- package/dist/suites/llm/index.js.map +1 -0
- package/dist/suites/llm/llm.js +96 -0
- package/dist/suites/llm/llm.js.map +1 -0
- package/package.json +4 -3
- package/release-notes/constructs/1.2.18.md +12 -0
- package/release-notes/dynamodb/0.4.0.md +44 -0
- package/release-notes/fabric/0.2.0.md +77 -0
- package/release-notes/llm/1.2.5.md +25 -0
- package/release-notes/llm/1.2.6.md +55 -0
- package/release-notes/mcp/0.5.0.md +70 -0
- package/release-notes/testkit/1.2.17.md +30 -0
- package/skills/agents.md +18 -8
- package/skills/aws.md +232 -60
- package/skills/contents.md +15 -0
- package/skills/datadog.md +98 -64
- package/skills/development.md +19 -0
- package/skills/documentation.md +27 -0
- package/skills/dynamodb.md +37 -28
- package/skills/fabric.md +2 -2
- package/skills/infrastructure.md +21 -0
- package/skills/issues.md +55 -0
- package/skills/llm.md +381 -0
- package/skills/meta.md +17 -0
- package/skills/patterns.md +17 -0
- package/skills/secrets.md +74 -18
- package/skills/skills.md +22 -0
- package/skills/style.md +1 -24
- package/skills/tools-aws.md +166 -0
- package/skills/tools-datadog.md +199 -0
- package/skills/tools-dynamodb.md +168 -0
- package/skills/tools.md +58 -80
- package/skills/vocabulary.md +160 -0
- package/skills/topics.md +0 -116
- package/skills/writing.md +0 -153
- /package/dist/{aws.d.ts → suites/aws/aws.d.ts} +0 -0
- /package/dist/{datadog.d.ts → suites/datadog/datadog.d.ts} +0 -0
- /package/dist/{llm.d.ts → suites/llm/llm.d.ts} +0 -0
package/skills/llm.md
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Using @jaypie/llm for unified LLM access
|
|
3
|
+
related: fabric, tools, tests
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @jaypie/llm
|
|
7
|
+
|
|
8
|
+
Unified interface for calling LLM providers with multi-turn conversations, tool calling, streaming, and structured output.
|
|
9
|
+
|
|
10
|
+
## Quick Start
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import Llm from "@jaypie/llm";
|
|
14
|
+
|
|
15
|
+
// Auto-detect provider from model name
|
|
16
|
+
const response = await Llm.operate("What is 2+2?", { model: "claude-sonnet-4" });
|
|
17
|
+
console.log(response.content); // "4"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Providers and Models
|
|
21
|
+
|
|
22
|
+
| Provider | Match Keywords | Default Model |
|
|
23
|
+
|----------|----------------|---------------|
|
|
24
|
+
| OpenAI | "openai", "gpt", /^o\d/ | gpt-5.1 |
|
|
25
|
+
| Anthropic | "anthropic", "claude", "haiku", "opus", "sonnet" | claude-sonnet-4-5 |
|
|
26
|
+
| Gemini | "gemini", "google" | gemini-3-pro-preview |
|
|
27
|
+
| OpenRouter | "openrouter" | z-ai/glm-4.7 |
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// Provider auto-detected from model
|
|
31
|
+
await Llm.operate(input, { model: "gpt-5.1" }); // OpenAI
|
|
32
|
+
await Llm.operate(input, { model: "claude-opus-4" }); // Anthropic
|
|
33
|
+
await Llm.operate(input, { model: "gemini-3" }); // Gemini
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Core Methods
|
|
37
|
+
|
|
38
|
+
### operate() - Multi-turn with Tools
|
|
39
|
+
|
|
40
|
+
The primary method for complex interactions:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const response = await Llm.operate(input, {
|
|
44
|
+
model: "gpt-5.1",
|
|
45
|
+
system: "You are a helpful assistant",
|
|
46
|
+
tools: toolkit,
|
|
47
|
+
turns: 5, // Allow up to 5 conversation turns
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Response structure
|
|
51
|
+
response.content; // string | object (structured output)
|
|
52
|
+
response.history; // LlmHistory - for follow-up calls
|
|
53
|
+
response.usage; // Token usage per turn
|
|
54
|
+
response.reasoning; // Extended thinking (if available)
|
|
55
|
+
response.status; // "completed" | "incomplete" | "in_progress"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### send() - Simple Completions
|
|
59
|
+
|
|
60
|
+
For single-shot text completions:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
const response = await Llm.send("Explain REST APIs", {
|
|
64
|
+
model: "claude-sonnet-4",
|
|
65
|
+
system: "You are a technical writer",
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### stream() - Streaming Responses
|
|
70
|
+
|
|
71
|
+
For real-time output:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
for await (const chunk of Llm.stream("Tell me a story", { model: "gpt-5.1" })) {
|
|
75
|
+
switch (chunk.type) {
|
|
76
|
+
case "text":
|
|
77
|
+
process.stdout.write(chunk.content);
|
|
78
|
+
break;
|
|
79
|
+
case "tool_call":
|
|
80
|
+
console.log(`Tool: ${chunk.toolCall.name}`);
|
|
81
|
+
break;
|
|
82
|
+
case "done":
|
|
83
|
+
console.log("Usage:", chunk.usage);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Tool Calling
|
|
90
|
+
|
|
91
|
+
### Define Tools with Toolkit
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import Llm, { Toolkit } from "@jaypie/llm";
|
|
95
|
+
|
|
96
|
+
const toolkit = new Toolkit([
|
|
97
|
+
{
|
|
98
|
+
name: "get_weather",
|
|
99
|
+
description: "Get current weather for a city",
|
|
100
|
+
type: "function",
|
|
101
|
+
parameters: {
|
|
102
|
+
type: "object",
|
|
103
|
+
properties: {
|
|
104
|
+
city: { type: "string", description: "City name" },
|
|
105
|
+
},
|
|
106
|
+
required: ["city"],
|
|
107
|
+
},
|
|
108
|
+
call: async ({ city }) => {
|
|
109
|
+
// Actual implementation
|
|
110
|
+
return { temp: 72, conditions: "sunny" };
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
const response = await Llm.operate("What's the weather in NYC?", {
|
|
116
|
+
model: "gpt-5.1",
|
|
117
|
+
tools: toolkit,
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Built-in Tools
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import Llm, { tools, JaypieToolkit } from "@jaypie/llm";
|
|
125
|
+
|
|
126
|
+
// Use individual tools
|
|
127
|
+
const response = await Llm.operate("Roll 2d6", {
|
|
128
|
+
model: "gpt-5.1",
|
|
129
|
+
tools, // Includes: random, roll, time, weather
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Or use the pre-configured toolkit
|
|
133
|
+
const toolkit = new JaypieToolkit();
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Fabric Services as Tools
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { fabricLlmTool } from "@jaypie/fabric";
|
|
140
|
+
|
|
141
|
+
const greetTool = fabricLlmTool(greetService);
|
|
142
|
+
const toolkit = new Toolkit([greetTool]);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Structured Output
|
|
146
|
+
|
|
147
|
+
### Natural Schema
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
const result = await Llm.operate("Extract contact info from: John Doe, john@example.com, 555-1234", {
|
|
151
|
+
model: "gpt-5.1",
|
|
152
|
+
format: {
|
|
153
|
+
name: String,
|
|
154
|
+
email: String,
|
|
155
|
+
phone: String,
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
// result.content = { name: "John Doe", email: "john@example.com", phone: "555-1234" }
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Zod Schema
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { z } from "zod";
|
|
165
|
+
|
|
166
|
+
const PersonSchema = z.object({
|
|
167
|
+
name: z.string(),
|
|
168
|
+
age: z.number(),
|
|
169
|
+
hobbies: z.array(z.string()),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const result = await Llm.operate("Parse: Alice is 30 and likes hiking and reading", {
|
|
173
|
+
model: "gpt-5.1",
|
|
174
|
+
format: PersonSchema,
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Conversation History
|
|
179
|
+
|
|
180
|
+
Continue conversations across calls:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
const first = await Llm.operate("My name is Alice", { model: "gpt-5.1" });
|
|
184
|
+
|
|
185
|
+
const second = await Llm.operate("What's my name?", {
|
|
186
|
+
model: "gpt-5.1",
|
|
187
|
+
history: first.history,
|
|
188
|
+
});
|
|
189
|
+
// second.content = "Your name is Alice"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Input with Files and Images
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
const response = await Llm.operate([
|
|
196
|
+
"Analyze these documents",
|
|
197
|
+
{ file: "report.pdf", bucket: "my-bucket" },
|
|
198
|
+
{ image: "chart.png" },
|
|
199
|
+
], {
|
|
200
|
+
model: "claude-sonnet-4",
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Placeholders and Data
|
|
205
|
+
|
|
206
|
+
Substitute data into prompts:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
const response = await Llm.operate("Summarize the article about {{topic}}", {
|
|
210
|
+
model: "gpt-5.1",
|
|
211
|
+
data: { topic: "climate change" },
|
|
212
|
+
placeholders: { input: true },
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Lifecycle Hooks
|
|
217
|
+
|
|
218
|
+
Monitor and react to LLM operations:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
const response = await Llm.operate(input, {
|
|
222
|
+
model: "gpt-5.1",
|
|
223
|
+
hooks: {
|
|
224
|
+
beforeEachModelRequest: ({ providerRequest }) => {
|
|
225
|
+
console.log("Sending request...");
|
|
226
|
+
},
|
|
227
|
+
afterEachModelResponse: ({ content, usage }) => {
|
|
228
|
+
console.log(`Tokens: ${usage.total}`);
|
|
229
|
+
},
|
|
230
|
+
beforeEachTool: ({ toolName, args }) => {
|
|
231
|
+
console.log(`Calling ${toolName} with`, args);
|
|
232
|
+
},
|
|
233
|
+
afterEachTool: ({ result, toolName }) => {
|
|
234
|
+
console.log(`${toolName} returned:`, result);
|
|
235
|
+
},
|
|
236
|
+
onToolError: ({ error, toolName }) => {
|
|
237
|
+
console.error(`${toolName} failed:`, error);
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Fallback Providers
|
|
244
|
+
|
|
245
|
+
Configure a chain of fallback providers that automatically retry failed calls when the primary provider fails:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
// Instance-level configuration
|
|
249
|
+
const llm = new Llm("anthropic", {
|
|
250
|
+
model: "claude-sonnet-4",
|
|
251
|
+
fallback: [
|
|
252
|
+
{ provider: "openai", model: "gpt-4o" },
|
|
253
|
+
{ provider: "gemini", model: "gemini-2.0-flash" },
|
|
254
|
+
],
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// Per-call override
|
|
258
|
+
const response = await llm.operate(input, {
|
|
259
|
+
fallback: [{ provider: "openai", model: "gpt-4o" }],
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Disable fallback for specific call
|
|
263
|
+
const response = await llm.operate(input, { fallback: false });
|
|
264
|
+
|
|
265
|
+
// Static method with fallback
|
|
266
|
+
const response = await Llm.operate(input, {
|
|
267
|
+
model: "claude-sonnet-4",
|
|
268
|
+
fallback: [{ provider: "openai", model: "gpt-4o" }],
|
|
269
|
+
});
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Fallback Response Metadata
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
response.provider; // Which provider handled the request
|
|
276
|
+
response.fallbackUsed; // true if a fallback was used
|
|
277
|
+
response.fallbackAttempts; // Number of providers tried (1 = primary only)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Instance Usage
|
|
281
|
+
|
|
282
|
+
For repeated calls with same configuration:
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
const llm = new Llm("anthropic", {
|
|
286
|
+
model: "claude-sonnet-4",
|
|
287
|
+
system: "You are a code reviewer",
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const review1 = await llm.operate(code1);
|
|
291
|
+
const review2 = await llm.operate(code2);
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Environment Variables
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
OPENAI_API_KEY # Required for OpenAI
|
|
298
|
+
ANTHROPIC_API_KEY # Required for Anthropic
|
|
299
|
+
GOOGLE_API_KEY # Required for Gemini
|
|
300
|
+
OPENROUTER_API_KEY # Required for OpenRouter
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Keys are resolved via `getEnvSecret()` which supports AWS Secrets Manager.
|
|
304
|
+
|
|
305
|
+
## Error Handling
|
|
306
|
+
|
|
307
|
+
The package auto-retries rate limits and transient errors:
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
try {
|
|
311
|
+
const response = await Llm.operate(input, { model: "gpt-5.1" });
|
|
312
|
+
if (response.status !== "completed") {
|
|
313
|
+
console.log("Incomplete:", response.error);
|
|
314
|
+
}
|
|
315
|
+
} catch (error) {
|
|
316
|
+
// Unrecoverable errors (auth, validation) throw
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Testing Pattern
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
import { describe, expect, it, vi } from "vitest";
|
|
324
|
+
import Llm, { Toolkit } from "@jaypie/llm";
|
|
325
|
+
|
|
326
|
+
describe("LLM Integration", () => {
|
|
327
|
+
it("calls tool and returns response", async () => {
|
|
328
|
+
const mockTool = {
|
|
329
|
+
name: "calculate",
|
|
330
|
+
description: "Perform calculation",
|
|
331
|
+
type: "function",
|
|
332
|
+
parameters: { type: "object", properties: { expr: { type: "string" } } },
|
|
333
|
+
call: vi.fn().mockResolvedValue("42"),
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const toolkit = new Toolkit([mockTool]);
|
|
337
|
+
const response = await Llm.operate("Calculate 6*7", {
|
|
338
|
+
model: "gpt-5.1",
|
|
339
|
+
tools: toolkit,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
expect(mockTool.call).toHaveBeenCalled();
|
|
343
|
+
expect(response.status).toBe("completed");
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Best Practices
|
|
349
|
+
|
|
350
|
+
1. **Use operate() over send()** - More flexible, handles tools and structure
|
|
351
|
+
2. **Set turns appropriately** - Default is 1; increase for tool-heavy workflows
|
|
352
|
+
3. **Provide clear tool descriptions** - LLMs use these to decide which tool to call
|
|
353
|
+
4. **Use structured output** - More reliable than parsing free text
|
|
354
|
+
5. **Track usage** - Monitor `response.usage` for cost management
|
|
355
|
+
6. **Handle incomplete status** - Check `response.status` for tool errors or limits
|
|
356
|
+
7. **Configure fallbacks** - Set up fallback providers for resilience against outages
|
|
357
|
+
|
|
358
|
+
## Common Options
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
interface LlmOperateOptions {
|
|
362
|
+
data?: Record<string, any>; // Placeholder substitution data
|
|
363
|
+
fallback?: LlmFallbackConfig[] | false; // Fallback provider chain
|
|
364
|
+
format?: JsonObject | ZodType; // Structured output schema
|
|
365
|
+
history?: LlmHistory; // Previous conversation
|
|
366
|
+
hooks?: LlmHooks; // Lifecycle callbacks
|
|
367
|
+
instructions?: string; // Additional instructions
|
|
368
|
+
model?: string; // Model override
|
|
369
|
+
system?: string; // System prompt
|
|
370
|
+
tools?: LlmTool[] | Toolkit; // Available tools
|
|
371
|
+
turns?: boolean | number; // Max conversation turns
|
|
372
|
+
user?: string; // User ID for logging
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
interface LlmFallbackConfig {
|
|
376
|
+
provider: string; // Provider name (e.g., "openai", "anthropic", "gemini")
|
|
377
|
+
model?: string; // Model to use (optional, uses provider default)
|
|
378
|
+
apiKey?: string; // API key (optional, uses environment variable)
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
package/skills/meta.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: About Jaypie, tools, and contributing
|
|
3
|
+
related: issues, jaypie, skills, tools
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Meta
|
|
7
|
+
|
|
8
|
+
About Jaypie and this skill system.
|
|
9
|
+
|
|
10
|
+
## Skills
|
|
11
|
+
|
|
12
|
+
| Alias | Description |
|
|
13
|
+
|-------|-------------|
|
|
14
|
+
| `issues` | Submitting issues to Jaypie |
|
|
15
|
+
| `jaypie` | Jaypie overview and core concepts |
|
|
16
|
+
| `skills` | About the skill system |
|
|
17
|
+
| `tools` | Available MCP tools reference |
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Architecture, models, and service patterns
|
|
3
|
+
related: fabric, models, services, vocabulary
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Patterns
|
|
7
|
+
|
|
8
|
+
Architectural patterns and conventions.
|
|
9
|
+
|
|
10
|
+
## Skills
|
|
11
|
+
|
|
12
|
+
| Alias | Description |
|
|
13
|
+
|-------|-------------|
|
|
14
|
+
| `fabric` | Service handlers and adapters |
|
|
15
|
+
| `models` | Data models and type definitions |
|
|
16
|
+
| `services` | Service layer architecture |
|
|
17
|
+
| `vocabulary` | Naming conventions and ontology |
|
package/skills/secrets.md
CHANGED
|
@@ -5,26 +5,52 @@ related: aws, cdk, variables
|
|
|
5
5
|
|
|
6
6
|
# Secret Management
|
|
7
7
|
|
|
8
|
-
Jaypie uses AWS Secrets Manager for secure credential storage.
|
|
8
|
+
Jaypie uses AWS Secrets Manager for secure credential storage, with environment-aware resolution that works seamlessly in both local development and Lambda.
|
|
9
9
|
|
|
10
10
|
## Basic Usage
|
|
11
11
|
|
|
12
|
+
Use `getEnvSecret` for environment-aware secret resolution:
|
|
13
|
+
|
|
12
14
|
```typescript
|
|
13
|
-
import {
|
|
15
|
+
import { getEnvSecret } from "jaypie";
|
|
14
16
|
|
|
15
|
-
const apiKey = await
|
|
16
|
-
const dbUri = await
|
|
17
|
+
const apiKey = await getEnvSecret("ANTHROPIC_API_KEY");
|
|
18
|
+
const dbUri = await getEnvSecret("MONGODB_URI");
|
|
17
19
|
```
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
### How getEnvSecret Works
|
|
22
|
+
|
|
23
|
+
`getEnvSecret` checks environment variables in order:
|
|
24
|
+
|
|
25
|
+
1. `SECRET_{name}` - If found, fetches from AWS Secrets Manager
|
|
26
|
+
2. `{name}_SECRET` - If found, fetches from AWS Secrets Manager
|
|
27
|
+
3. `{name}` - Returns direct value without AWS call
|
|
28
|
+
|
|
29
|
+
This allows the same code to work locally (with direct env values) and in Lambda (with secret references).
|
|
30
|
+
|
|
31
|
+
## Loading Multiple Secrets
|
|
32
|
+
|
|
33
|
+
Use `loadEnvSecrets` during handler initialization:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { loadEnvSecrets } from "jaypie";
|
|
37
|
+
|
|
38
|
+
// Load secrets and set in process.env
|
|
39
|
+
await loadEnvSecrets("ANTHROPIC_API_KEY", "OPENAI_API_KEY", "MONGODB_URI");
|
|
40
|
+
|
|
41
|
+
// Now available as process.env.ANTHROPIC_API_KEY, etc.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## CDK Configuration
|
|
20
45
|
|
|
21
46
|
Reference secrets via environment variables in CDK:
|
|
22
47
|
|
|
23
48
|
```typescript
|
|
24
49
|
const handler = new JaypieLambda(this, "Handler", {
|
|
25
50
|
environment: {
|
|
26
|
-
|
|
27
|
-
|
|
51
|
+
// SECRET_ prefix triggers AWS Secrets Manager fetch
|
|
52
|
+
SECRET_MONGODB_URI: "my-project/mongodb-uri",
|
|
53
|
+
SECRET_API_KEY: "my-project/third-party-api-key",
|
|
28
54
|
},
|
|
29
55
|
});
|
|
30
56
|
```
|
|
@@ -32,10 +58,23 @@ const handler = new JaypieLambda(this, "Handler", {
|
|
|
32
58
|
In code:
|
|
33
59
|
|
|
34
60
|
```typescript
|
|
35
|
-
|
|
36
|
-
const mongoUri = await
|
|
61
|
+
// getEnvSecret sees SECRET_MONGODB_URI and fetches from Secrets Manager
|
|
62
|
+
const mongoUri = await getEnvSecret("MONGODB_URI");
|
|
37
63
|
```
|
|
38
64
|
|
|
65
|
+
## Direct Secret Access
|
|
66
|
+
|
|
67
|
+
Use `getSecret` when you need to fetch by exact AWS secret name:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { getSecret } from "jaypie";
|
|
71
|
+
|
|
72
|
+
// Fetch by exact AWS secret name
|
|
73
|
+
const secret = await getSecret("my-project/production/api-key");
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Note: `getSecret` requires `AWS_SESSION_TOKEN` and always calls Secrets Manager. Prefer `getEnvSecret` for typical use cases.
|
|
77
|
+
|
|
39
78
|
## Creating Secrets
|
|
40
79
|
|
|
41
80
|
### Via CDK
|
|
@@ -86,7 +125,7 @@ aws secretsmanager create-secret \
|
|
|
86
125
|
Retrieve in code:
|
|
87
126
|
|
|
88
127
|
```typescript
|
|
89
|
-
const credentialsJson = await
|
|
128
|
+
const credentialsJson = await getEnvSecret("DB_CREDENTIALS");
|
|
90
129
|
const credentials = JSON.parse(credentialsJson);
|
|
91
130
|
```
|
|
92
131
|
|
|
@@ -96,10 +135,10 @@ Secrets are cached by default to reduce API calls:
|
|
|
96
135
|
|
|
97
136
|
```typescript
|
|
98
137
|
// First call: fetches from Secrets Manager
|
|
99
|
-
const key1 = await
|
|
138
|
+
const key1 = await getEnvSecret("API_KEY");
|
|
100
139
|
|
|
101
140
|
// Second call: returns cached value
|
|
102
|
-
const key2 = await
|
|
141
|
+
const key2 = await getEnvSecret("API_KEY");
|
|
103
142
|
```
|
|
104
143
|
|
|
105
144
|
Cache is scoped to Lambda execution context (warm starts reuse cache).
|
|
@@ -125,19 +164,16 @@ secret.addRotationSchedule("Rotation", {
|
|
|
125
164
|
|
|
126
165
|
## Local Development
|
|
127
166
|
|
|
128
|
-
For local development,
|
|
167
|
+
For local development, set environment variables directly:
|
|
129
168
|
|
|
130
169
|
```bash
|
|
131
170
|
# .env.local (not committed)
|
|
171
|
+
ANTHROPIC_API_KEY=sk-ant-test123
|
|
132
172
|
MONGODB_URI=mongodb://localhost:27017/dev
|
|
133
173
|
API_KEY=test_key_123
|
|
134
174
|
```
|
|
135
175
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
const mongoUri = process.env.MONGODB_URI || await getSecret(process.env.SECRET_MONGODB_URI);
|
|
140
|
-
```
|
|
176
|
+
`getEnvSecret` automatically returns these values without AWS calls since there's no `SECRET_` prefix.
|
|
141
177
|
|
|
142
178
|
## IAM Permissions
|
|
143
179
|
|
|
@@ -153,3 +189,23 @@ lambdaFunction.addToRolePolicy(new PolicyStatement({
|
|
|
153
189
|
}));
|
|
154
190
|
```
|
|
155
191
|
|
|
192
|
+
## Testing
|
|
193
|
+
|
|
194
|
+
Mock secret functions in tests:
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
import { getEnvSecret } from "@jaypie/testkit/mock";
|
|
198
|
+
import { vi } from "vitest";
|
|
199
|
+
|
|
200
|
+
vi.mock("@jaypie/aws");
|
|
201
|
+
|
|
202
|
+
describe("Handler", () => {
|
|
203
|
+
it("uses API key from secrets", async () => {
|
|
204
|
+
vi.mocked(getEnvSecret).mockResolvedValue("test-api-key");
|
|
205
|
+
|
|
206
|
+
const result = await handler();
|
|
207
|
+
|
|
208
|
+
expect(getEnvSecret).toHaveBeenCalledWith("API_KEY");
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
```
|
package/skills/skills.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: About the Jaypie skill system
|
|
3
|
+
related: agents, index
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Jaypie Skills
|
|
7
|
+
|
|
8
|
+
- Instruct Jaypie styles, techniques, and traditions
|
|
9
|
+
- Written for agents
|
|
10
|
+
- Balance brevity and rigor
|
|
11
|
+
|
|
12
|
+
Look up skills by alias: `mcp__jaypie__skill(alias)`
|
|
13
|
+
|
|
14
|
+
## Categories
|
|
15
|
+
|
|
16
|
+
| Category | Skills |
|
|
17
|
+
|----------|--------|
|
|
18
|
+
| contents | index, releasenotes |
|
|
19
|
+
| development | documentation, errors, logs, mocks, style, tests |
|
|
20
|
+
| infrastructure | aws, cdk, cicd, datadog, dns, dynamodb, secrets, variables |
|
|
21
|
+
| patterns | fabric, models, services, vocabulary |
|
|
22
|
+
| meta | issues, jaypie, skills, tools |
|
package/skills/style.md
CHANGED
|
@@ -144,28 +144,6 @@ throw new NotFoundError("User not found", {
|
|
|
144
144
|
});
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
## Avoid Over-Engineering
|
|
148
|
-
|
|
149
|
-
### No Premature Abstraction
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
// BAD - Unnecessary abstraction for one use
|
|
153
|
-
const formatName = (name) => name.toUpperCase();
|
|
154
|
-
const processUser = (user) => ({ ...user, name: formatName(user.name) });
|
|
155
|
-
|
|
156
|
-
// GOOD - Simple and direct
|
|
157
|
-
const processUser = (user) => ({ ...user, name: user.name.toUpperCase() });
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### Minimal Changes
|
|
161
|
-
|
|
162
|
-
Only modify what's requested:
|
|
163
|
-
|
|
164
|
-
- Bug fix? Fix the bug, nothing else.
|
|
165
|
-
- Add feature? Add only that feature.
|
|
166
|
-
- Don't add docstrings to unchanged code.
|
|
167
|
-
- Don't refactor surrounding code.
|
|
168
|
-
|
|
169
147
|
## Naming Conventions
|
|
170
148
|
|
|
171
149
|
| Type | Convention | Example |
|
|
@@ -173,7 +151,7 @@ Only modify what's requested:
|
|
|
173
151
|
| Functions | camelCase | `getUser`, `createOrder` |
|
|
174
152
|
| Classes | PascalCase | `UserService`, `OrderModel` |
|
|
175
153
|
| Constants | SCREAMING_SNAKE | `MAX_RETRIES`, `API_URL` |
|
|
176
|
-
| Files |
|
|
154
|
+
| Files | camelCase | `userService.ts`, `orderModel.ts` |
|
|
177
155
|
| Types/Interfaces | PascalCase | `UserInput`, `IUserService` |
|
|
178
156
|
|
|
179
157
|
## Lint Rules
|
|
@@ -187,4 +165,3 @@ export default [...jaypie];
|
|
|
187
165
|
```
|
|
188
166
|
|
|
189
167
|
Always run `npm run format` before committing.
|
|
190
|
-
|