@jaypie/mcp 0.3.2 → 0.3.4

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.
@@ -0,0 +1,140 @@
1
+ ---
2
+ description: DynamoDB patterns and queries
3
+ related: aws, cdk, models
4
+ ---
5
+
6
+ # DynamoDB Patterns
7
+
8
+ Best practices for DynamoDB with Jaypie applications.
9
+
10
+ ## MCP DynamoDB Tools
11
+
12
+ ```
13
+ aws_dynamodb_describe_table - Get table schema and indexes
14
+ aws_dynamodb_query - Query by partition key (efficient)
15
+ aws_dynamodb_scan - Full table scan (use sparingly)
16
+ aws_dynamodb_get_item - Get single item by key
17
+ ```
18
+
19
+ ## Key Design
20
+
21
+ ### Single Table Design
22
+
23
+ Use prefixed keys for multiple entity types:
24
+
25
+ ```typescript
26
+ // User entity
27
+ {
28
+ pk: "USER#123",
29
+ sk: "PROFILE",
30
+ name: "John Doe",
31
+ email: "john@example.com"
32
+ }
33
+
34
+ // User's orders
35
+ {
36
+ pk: "USER#123",
37
+ sk: "ORDER#2024-01-15#abc",
38
+ total: 99.99,
39
+ status: "completed"
40
+ }
41
+ ```
42
+
43
+ ### Access Patterns
44
+
45
+ | Access Pattern | Key Condition |
46
+ |---------------|---------------|
47
+ | Get user profile | pk = USER#123, sk = PROFILE |
48
+ | List user orders | pk = USER#123, sk begins_with ORDER# |
49
+ | Get specific order | pk = USER#123, sk = ORDER#2024-01-15#abc |
50
+
51
+ ## Query Examples
52
+
53
+ ### Query via MCP
54
+
55
+ ```
56
+ # Get user profile
57
+ aws_dynamodb_get_item --tableName "MyTable" --key '{"pk":{"S":"USER#123"},"sk":{"S":"PROFILE"}}'
58
+
59
+ # List user orders
60
+ aws_dynamodb_query --tableName "MyTable" \
61
+ --keyConditionExpression "pk = :pk AND begins_with(sk, :prefix)" \
62
+ --expressionAttributeValues '{":pk":{"S":"USER#123"},":prefix":{"S":"ORDER#"}}'
63
+ ```
64
+
65
+ ### Query in Code
66
+
67
+ ```typescript
68
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
69
+ import { QueryCommand } from "@aws-sdk/lib-dynamodb";
70
+
71
+ const client = new DynamoDBClient({});
72
+
73
+ const result = await client.send(new QueryCommand({
74
+ TableName: process.env.CDK_ENV_TABLE,
75
+ KeyConditionExpression: "pk = :pk AND begins_with(sk, :prefix)",
76
+ ExpressionAttributeValues: {
77
+ ":pk": "USER#123",
78
+ ":prefix": "ORDER#",
79
+ },
80
+ ScanIndexForward: false, // Newest first
81
+ Limit: 10,
82
+ }));
83
+ ```
84
+
85
+ ## GSI Patterns
86
+
87
+ ### By-Status Index
88
+
89
+ ```typescript
90
+ // GSI for querying by status across all users
91
+ {
92
+ pk: "USER#123",
93
+ sk: "ORDER#2024-01-15#abc",
94
+ gsi1pk: "ORDER#pending",
95
+ gsi1sk: "2024-01-15T10:00:00Z",
96
+ }
97
+ ```
98
+
99
+ Query pending orders:
100
+
101
+ ```
102
+ aws_dynamodb_query --tableName "MyTable" --indexName "gsi1" \
103
+ --keyConditionExpression "gsi1pk = :status" \
104
+ --expressionAttributeValues '{":status":{"S":"ORDER#pending"}}'
105
+ ```
106
+
107
+ ## CDK Table Definition
108
+
109
+ ```typescript
110
+ import { JaypieTable } from "@jaypie/constructs";
111
+
112
+ const table = new JaypieTable(this, "DataTable", {
113
+ partitionKey: { name: "pk", type: AttributeType.STRING },
114
+ sortKey: { name: "sk", type: AttributeType.STRING },
115
+ globalSecondaryIndexes: [
116
+ {
117
+ indexName: "gsi1",
118
+ partitionKey: { name: "gsi1pk", type: AttributeType.STRING },
119
+ sortKey: { name: "gsi1sk", type: AttributeType.STRING },
120
+ },
121
+ ],
122
+ });
123
+ ```
124
+
125
+ ## Local Development
126
+
127
+ ```bash
128
+ # Start local DynamoDB
129
+ docker run -p 8000:8000 amazon/dynamodb-local
130
+
131
+ # Create table
132
+ AWS_ACCESS_KEY_ID=local AWS_SECRET_ACCESS_KEY=local \
133
+ aws dynamodb create-table \
134
+ --table-name MyTable \
135
+ --attribute-definitions AttributeName=pk,AttributeType=S AttributeName=sk,AttributeType=S \
136
+ --key-schema AttributeName=pk,KeyType=HASH AttributeName=sk,KeyType=RANGE \
137
+ --billing-mode PAY_PER_REQUEST \
138
+ --endpoint-url http://127.0.0.1:8000
139
+ ```
140
+
@@ -0,0 +1,142 @@
1
+ ---
2
+ description: Error handling with @jaypie/errors
3
+ related: debugging, logs, tests
4
+ ---
5
+
6
+ # Error Handling
7
+
8
+ Jaypie provides structured error types for consistent error handling.
9
+
10
+ ## Core Principle
11
+
12
+ **Never throw vanilla `Error`**. Use Jaypie error types:
13
+
14
+ ```typescript
15
+ import { ConfigurationError, NotFoundError } from "jaypie";
16
+
17
+ // BAD
18
+ throw new Error("Missing API key");
19
+
20
+ // GOOD
21
+ throw new ConfigurationError("Missing API key");
22
+ ```
23
+
24
+ ## Error Types
25
+
26
+ | Error | HTTP Status | Use When |
27
+ |-------|-------------|----------|
28
+ | BadRequestError | 400 | Invalid input from client |
29
+ | UnauthorizedError | 401 | Authentication required |
30
+ | ForbiddenError | 403 | Authenticated but not permitted |
31
+ | NotFoundError | 404 | Resource doesn't exist |
32
+ | ConfigurationError | 500 | Missing or invalid config |
33
+ | InternalError | 500 | Unexpected server error |
34
+
35
+ ## Usage Examples
36
+
37
+ ### Bad Request
38
+
39
+ ```typescript
40
+ import { BadRequestError } from "jaypie";
41
+
42
+ if (!request.body.email) {
43
+ throw new BadRequestError("Email is required");
44
+ }
45
+
46
+ if (!isValidEmail(request.body.email)) {
47
+ throw new BadRequestError("Invalid email format");
48
+ }
49
+ ```
50
+
51
+ ### Not Found
52
+
53
+ ```typescript
54
+ import { NotFoundError } from "jaypie";
55
+
56
+ const user = await User.findById(userId);
57
+ if (!user) {
58
+ throw new NotFoundError(`User ${userId} not found`);
59
+ }
60
+ ```
61
+
62
+ ### Configuration Error
63
+
64
+ ```typescript
65
+ import { ConfigurationError } from "jaypie";
66
+
67
+ if (!process.env.API_KEY) {
68
+ throw new ConfigurationError("API_KEY environment variable is required");
69
+ }
70
+ ```
71
+
72
+ ### Unauthorized vs Forbidden
73
+
74
+ ```typescript
75
+ import { UnauthorizedError, ForbiddenError } from "jaypie";
76
+
77
+ // No credentials provided
78
+ if (!token) {
79
+ throw new UnauthorizedError("Authentication required");
80
+ }
81
+
82
+ // Credentials provided but insufficient permission
83
+ if (!user.hasRole("admin")) {
84
+ throw new ForbiddenError("Admin access required");
85
+ }
86
+ ```
87
+
88
+ ## Error Context
89
+
90
+ Add context to errors for debugging:
91
+
92
+ ```typescript
93
+ import { NotFoundError } from "jaypie";
94
+
95
+ throw new NotFoundError("User not found", {
96
+ context: {
97
+ userId,
98
+ requestId: req.id,
99
+ searchedTables: ["users", "legacy_users"],
100
+ },
101
+ });
102
+ ```
103
+
104
+ ## Error Handling in Handlers
105
+
106
+ Jaypie handlers automatically catch and format errors:
107
+
108
+ ```typescript
109
+ import { lambdaHandler } from "@jaypie/lambda";
110
+ import { NotFoundError } from "jaypie";
111
+
112
+ export const handler = lambdaHandler(async (event) => {
113
+ const item = await getItem(event.id);
114
+ if (!item) {
115
+ throw new NotFoundError("Item not found");
116
+ // Returns: { statusCode: 404, body: { error: "Item not found" } }
117
+ }
118
+ return item;
119
+ });
120
+ ```
121
+
122
+ ## Testing Errors
123
+
124
+ ```typescript
125
+ import { expect, it } from "vitest";
126
+ import { NotFoundError } from "jaypie";
127
+
128
+ it("throws NotFoundError when user missing", async () => {
129
+ await expect(getUser("invalid-id"))
130
+ .rejects
131
+ .toThrow(NotFoundError);
132
+ });
133
+
134
+ it("includes context in error", async () => {
135
+ try {
136
+ await getUser("invalid-id");
137
+ } catch (error) {
138
+ expect(error.context.userId).toBe("invalid-id");
139
+ }
140
+ });
141
+ ```
142
+
@@ -0,0 +1,164 @@
1
+ ---
2
+ description: Fabric service patterns and adapters
3
+ related: services, errors, tools
4
+ ---
5
+
6
+ # Fabric Services
7
+
8
+ Fabric provides a unified service pattern that works across CLI, Lambda, LLM tools, and MCP.
9
+
10
+ ## Core Concept
11
+
12
+ Define a service once, deploy it anywhere:
13
+
14
+ ```typescript
15
+ import { fabricService } from "@jaypie/fabric";
16
+
17
+ const greetService = fabricService({
18
+ alias: "greet",
19
+ description: "Greet a user by name",
20
+ input: {
21
+ name: {
22
+ type: String,
23
+ required: true,
24
+ description: "Name to greet",
25
+ },
26
+ },
27
+ service: async ({ name }) => {
28
+ return `Hello, ${name}!`;
29
+ },
30
+ });
31
+ ```
32
+
33
+ ## Adapters
34
+
35
+ ### Lambda Handler
36
+
37
+ ```typescript
38
+ import { fabricLambdaHandler } from "@jaypie/fabric";
39
+
40
+ export const handler = fabricLambdaHandler(greetService);
41
+ // Invoked via Lambda with { name: "World" }
42
+ ```
43
+
44
+ ### CLI Command
45
+
46
+ ```typescript
47
+ import { fabricCommand } from "@jaypie/fabric";
48
+
49
+ const program = new Command();
50
+ program.addCommand(fabricCommand(greetService));
51
+ // $ cli greet --name World
52
+ ```
53
+
54
+ ### MCP Tool
55
+
56
+ ```typescript
57
+ import { fabricMcpTool } from "@jaypie/fabric";
58
+
59
+ server.tool(...fabricMcpTool(greetService));
60
+ // Available as MCP tool "greet"
61
+ ```
62
+
63
+ ### LLM Tool
64
+
65
+ ```typescript
66
+ import { fabricLlmTool } from "@jaypie/fabric";
67
+
68
+ const tools = [fabricLlmTool(greetService)];
69
+ // Available to LLM as function call
70
+ ```
71
+
72
+ ## Service Suites
73
+
74
+ Group related services:
75
+
76
+ ```typescript
77
+ import { createServiceSuite, fabricService } from "@jaypie/fabric";
78
+
79
+ const userService = fabricService({
80
+ alias: "user_get",
81
+ description: "Get user by ID",
82
+ input: { id: { type: String, required: true } },
83
+ service: async ({ id }) => User.findById(id),
84
+ });
85
+
86
+ const userListService = fabricService({
87
+ alias: "user_list",
88
+ description: "List all users",
89
+ input: {},
90
+ service: async () => User.find(),
91
+ });
92
+
93
+ const suite = createServiceSuite({
94
+ name: "users",
95
+ version: "1.0.0",
96
+ });
97
+
98
+ suite.register(userService, "users");
99
+ suite.register(userListService, "users");
100
+ ```
101
+
102
+ ## Input Validation
103
+
104
+ Fabric validates inputs automatically:
105
+
106
+ ```typescript
107
+ const service = fabricService({
108
+ input: {
109
+ email: {
110
+ type: String,
111
+ required: true,
112
+ description: "User email address",
113
+ },
114
+ count: {
115
+ type: Number,
116
+ required: false,
117
+ description: "Number of results",
118
+ },
119
+ status: {
120
+ type: ["active", "inactive"] as const,
121
+ required: false,
122
+ description: "Filter by status",
123
+ },
124
+ },
125
+ service: async ({ email, count, status }) => {
126
+ // email: string (validated)
127
+ // count: number | undefined
128
+ // status: "active" | "inactive" | undefined
129
+ },
130
+ });
131
+ ```
132
+
133
+ ## Error Handling
134
+
135
+ Fabric services use Jaypie errors:
136
+
137
+ ```typescript
138
+ import { fabricService } from "@jaypie/fabric";
139
+ import { NotFoundError, BadRequestError } from "jaypie";
140
+
141
+ const service = fabricService({
142
+ alias: "user_get",
143
+ input: { id: { type: String, required: true } },
144
+ service: async ({ id }) => {
145
+ if (!isValidId(id)) {
146
+ throw new BadRequestError("Invalid user ID format");
147
+ }
148
+ const user = await User.findById(id);
149
+ if (!user) {
150
+ throw new NotFoundError(`User ${id} not found`);
151
+ }
152
+ return user;
153
+ },
154
+ });
155
+ ```
156
+
157
+ ## Best Practices
158
+
159
+ 1. **Single Responsibility**: Each service does one thing
160
+ 2. **Descriptive Aliases**: Use `noun_verb` format (`user_get`, `order_create`)
161
+ 3. **Clear Descriptions**: Write for AI tools that need context
162
+ 4. **Input Documentation**: Describe what each input expects
163
+ 5. **Return Types**: Return JSON-serializable data
164
+
@@ -0,0 +1,7 @@
1
+ ---
2
+ description: Skill directory listing
3
+ ---
4
+
5
+ # Jaypie Skills
6
+
7
+ Query the Jaypie MCP `skill` tool with one of the following alias keywords `mcp__jaypie__skill(alias: String)`:
@@ -0,0 +1,100 @@
1
+ ---
2
+ description: Jaypie overview and core concepts
3
+ ---
4
+
5
+ # Introduction to Jaypie
6
+
7
+ Jaypie is a complete stack framework for building multi-environment cloud applications on AWS using TypeScript/Node.js.
8
+
9
+ ## Core Packages
10
+
11
+ ### Main Package: `jaypie`
12
+
13
+ The main package provides:
14
+ - **Secrets**: AWS Secrets Manager integration
15
+ - **Errors**: Structured error types (ConfigurationError, etc.)
16
+ - **Events**: Event parsing for Lambda handlers
17
+ - **Lifecycle**: Handler lifecycle management
18
+ - **Logging**: Structured logging with context
19
+ - **Queues**: SQS messaging utilities
20
+
21
+ ```typescript
22
+ import {
23
+ getSecret,
24
+ log,
25
+ ConfigurationError,
26
+ HTTP,
27
+ PROJECT
28
+ } from "jaypie";
29
+ ```
30
+
31
+ ### Infrastructure: `@jaypie/constructs`
32
+
33
+ CDK constructs for AWS infrastructure:
34
+ - Lambda functions with best practices
35
+ - SQS queues with DLQ
36
+ - S3 buckets with encryption
37
+ - CloudFront distributions
38
+ - Secrets Manager secrets
39
+
40
+ ### Testing: `@jaypie/testkit`
41
+
42
+ Testing utilities and mocks:
43
+ - Mock implementations for all Jaypie functions
44
+ - Vitest configuration helpers
45
+ - Test data fabrication
46
+
47
+ ## Project Structure
48
+
49
+ Jaypie projects use npm workspaces:
50
+
51
+ ```
52
+ project/
53
+ ├── packages/ # npm packages
54
+ │ ├── api/ # Express API
55
+ │ └── lib/ # Shared library
56
+ ├── stacks/ # CDK infrastructure
57
+ │ └── cdk/ # CDK app
58
+ └── package.json # Root workspace config
59
+ ```
60
+
61
+ ## Key Conventions
62
+
63
+ ### Environment Variables
64
+
65
+ | Variable | Purpose |
66
+ |----------|---------|
67
+ | `PROJECT_ENV` | Environment: local, sandbox, production |
68
+ | `PROJECT_KEY` | Project identifier for logging |
69
+ | `PROJECT_NONCE` | Unique resource identifier |
70
+ | `LOG_LEVEL` | Log level: trace, debug, info, warn, error |
71
+
72
+ ### Error Handling
73
+
74
+ Never throw vanilla `Error`. Use Jaypie errors:
75
+
76
+ ```typescript
77
+ import { ConfigurationError, NotFoundError } from "jaypie";
78
+
79
+ if (!config.required) {
80
+ throw new ConfigurationError("Missing required config");
81
+ }
82
+ ```
83
+
84
+ ### Logging
85
+
86
+ Use structured logging with context:
87
+
88
+ ```typescript
89
+ import { log } from "jaypie";
90
+
91
+ log.info("Processing request", { userId, action });
92
+ log.error("Request failed", { error: error.message });
93
+ ```
94
+
95
+ ## Next Steps
96
+
97
+ - `skill("style")` - Code style conventions
98
+ - `skill("errors")` - Error handling patterns
99
+ - `skill("tests")` - Testing with Vitest
100
+ - `skill("cdk")` - Infrastructure with CDK
@@ -0,0 +1,97 @@
1
+ ---
2
+ description: Legacy libraries and deprecated patterns
3
+ related: dynamodb, fabric, mocks
4
+ ---
5
+
6
+ # Legacy Patterns
7
+
8
+ Deprecated libraries and patterns that may exist in older Jaypie projects.
9
+
10
+ ## @jaypie/core (Deprecated)
11
+
12
+ Migrated to `@jaypie/kit`. Update imports:
13
+
14
+ ```typescript
15
+ // Old
16
+ import { someUtil } from "@jaypie/core";
17
+
18
+ // New
19
+ import { someUtil } from "@jaypie/kit";
20
+ ```
21
+
22
+ ## @jaypie/mongoose (Legacy)
23
+
24
+ MongoDB/Mongoose integration. Consider DynamoDB for new projects.
25
+
26
+ ### Connection
27
+
28
+ ```typescript
29
+ import { connectMongoose } from "@jaypie/mongoose";
30
+
31
+ export const handler = async (event) => {
32
+ await connectMongoose();
33
+ // Connection cached for Lambda warm starts
34
+ };
35
+ ```
36
+
37
+ ### Models
38
+
39
+ ```typescript
40
+ import mongoose, { Schema, Document } from "mongoose";
41
+
42
+ interface IUser extends Document {
43
+ email: string;
44
+ name: string;
45
+ }
46
+
47
+ const userSchema = new Schema<IUser>({
48
+ email: { type: String, required: true },
49
+ name: { type: String, required: true },
50
+ }, { timestamps: true });
51
+
52
+ export const User = mongoose.model<IUser>("User", userSchema);
53
+ ```
54
+
55
+ ### Mocking
56
+
57
+ ```typescript
58
+ vi.mock("@jaypie/mongoose", async () => {
59
+ const { mockMongoose } = await import("@jaypie/testkit");
60
+ return mockMongoose(vi);
61
+ });
62
+ ```
63
+
64
+ ## Migration Guides
65
+
66
+ ### Mongoose to DynamoDB
67
+
68
+ 1. Define DynamoDB item types (see `skill("dynamodb")`)
69
+ 2. Update data access patterns for single-table design
70
+ 3. Replace Mongoose queries with DynamoDB operations
71
+ 4. Update tests to mock DynamoDB instead of Mongoose
72
+
73
+ ### Core to Kit
74
+
75
+ Direct replacement - same exports, new package name:
76
+
77
+ ```bash
78
+ npm uninstall @jaypie/core
79
+ npm install @jaypie/kit
80
+ ```
81
+
82
+ Update imports throughout codebase.
83
+
84
+ ## When to Keep Legacy
85
+
86
+ Keep legacy patterns when:
87
+ - Existing production system is stable
88
+ - Migration cost outweighs benefits
89
+ - Team expertise is in legacy stack
90
+ - Database has significant existing data
91
+
92
+ Migrate when:
93
+ - Starting new features
94
+ - Performance issues arise
95
+ - Scaling requirements change
96
+ - Simplifying architecture
97
+