@jaypie/mcp 0.1.9 → 0.1.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaypie/mcp",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Jaypie MCP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -37,7 +37,7 @@
37
37
  "@modelcontextprotocol/sdk": "^1.17.0",
38
38
  "commander": "^14.0.0",
39
39
  "gray-matter": "^4.0.3",
40
- "zod": "^3.23.8"
40
+ "zod": "^4.1.13"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/express": "^5.0.3",
@@ -46,5 +46,5 @@
46
46
  "publishConfig": {
47
47
  "access": "public"
48
48
  },
49
- "gitHead": "65cd25e8b34237ab6ae4d494a3e429d20352b43e"
49
+ "gitHead": "45fe048c6687a807ed52bbc0b25963f45450f9c9"
50
50
  }
@@ -0,0 +1,388 @@
1
+ ---
2
+ description: Complete guide to using Jaypie Express features including expressHandler, CORS, lifecycle hooks, and pre-built routes
3
+ globs: packages/express/**
4
+ ---
5
+
6
+ # Jaypie Express Package
7
+
8
+ Jaypie provides Express utilities through `@jaypie/express` (also available via `jaypie`). The primary export is `expressHandler`, a wrapper that adds error handling, logging, lifecycle hooks, and response formatting to Express route handlers.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ npm install jaypie
14
+ # or
15
+ npm install @jaypie/express
16
+ ```
17
+
18
+ ## expressHandler
19
+
20
+ The core of Jaypie Express. Wraps route handlers with error handling, logging, and lifecycle management.
21
+
22
+ ### Basic Usage
23
+
24
+ ```typescript
25
+ import { expressHandler } from "jaypie";
26
+ import type { Request, Response } from "express";
27
+
28
+ const myRoute = expressHandler(async (req: Request, res: Response) => {
29
+ return { message: "Hello, World!" };
30
+ });
31
+
32
+ // Use in Express
33
+ app.get("/hello", myRoute);
34
+ ```
35
+
36
+ ### Return Value Handling
37
+
38
+ expressHandler automatically formats responses:
39
+
40
+ | Return Value | HTTP Status | Response |
41
+ |--------------|-------------|----------|
42
+ | Object | 200 | JSON body |
43
+ | Array | 200 | JSON body |
44
+ | String (JSON) | 200 | Parsed JSON |
45
+ | String (other) | 200 | Text body |
46
+ | `true` | 201 Created | Empty |
47
+ | `null`, `undefined`, `false` | 204 No Content | Empty |
48
+ | Object with `.json()` method | 200 | Result of `.json()` |
49
+
50
+ ### Options
51
+
52
+ ```typescript
53
+ import { expressHandler } from "jaypie";
54
+ import type { ExpressHandlerOptions } from "jaypie";
55
+
56
+ const options: ExpressHandlerOptions = {
57
+ name: "myHandler", // Handler name for logging
58
+ chaos: "low", // Chaos testing level
59
+ unavailable: false, // Return 503 if true
60
+ setup: [], // Setup function(s)
61
+ teardown: [], // Teardown function(s)
62
+ validate: [], // Validation function(s)
63
+ locals: {}, // Values to set on req.locals
64
+ };
65
+
66
+ const handler = expressHandler(async (req, res) => {
67
+ return { success: true };
68
+ }, options);
69
+
70
+ // Alternative: options first
71
+ const handler2 = expressHandler(options, async (req, res) => {
72
+ return { success: true };
73
+ });
74
+ ```
75
+
76
+ ## Lifecycle Hooks
77
+
78
+ ### Setup Functions
79
+
80
+ Run before the main handler. Use for initialization, authentication checks, or setting up request context.
81
+
82
+ ```typescript
83
+ import { expressHandler } from "jaypie";
84
+ import type { JaypieHandlerSetup } from "jaypie";
85
+
86
+ const authenticateUser: JaypieHandlerSetup = async (req, res) => {
87
+ const token = req.headers.authorization;
88
+ // Validate token, throw UnauthorizedError if invalid
89
+ };
90
+
91
+ const loadTenant: JaypieHandlerSetup = async (req, res) => {
92
+ req.locals.tenant = await getTenant(req.params.tenantId);
93
+ };
94
+
95
+ const handler = expressHandler(
96
+ async (req, res) => {
97
+ // req.locals.tenant is available here
98
+ return { tenant: req.locals.tenant };
99
+ },
100
+ {
101
+ setup: [authenticateUser, loadTenant],
102
+ }
103
+ );
104
+ ```
105
+
106
+ ### Teardown Functions
107
+
108
+ Run after the main handler completes (success or error). Use for cleanup.
109
+
110
+ ```typescript
111
+ import type { JaypieHandlerTeardown } from "jaypie";
112
+
113
+ const closeConnection: JaypieHandlerTeardown = async (req, res) => {
114
+ await req.locals.dbConnection?.close();
115
+ };
116
+
117
+ const handler = expressHandler(
118
+ async (req, res) => {
119
+ req.locals.dbConnection = await openConnection();
120
+ return await doWork(req.locals.dbConnection);
121
+ },
122
+ {
123
+ teardown: closeConnection,
124
+ }
125
+ );
126
+ ```
127
+
128
+ ### Validation Functions
129
+
130
+ Run before the main handler. Return `true` to continue or `false`/throw to reject.
131
+
132
+ ```typescript
133
+ import { ForbiddenError } from "jaypie";
134
+ import type { JaypieHandlerValidate } from "jaypie";
135
+
136
+ const requireAdmin: JaypieHandlerValidate = (req, res) => {
137
+ if (!req.locals.user?.isAdmin) {
138
+ throw new ForbiddenError();
139
+ }
140
+ return true;
141
+ };
142
+
143
+ const validateBody: JaypieHandlerValidate = (req, res) => {
144
+ return req.body?.email && req.body?.name;
145
+ };
146
+
147
+ const handler = expressHandler(
148
+ async (req, res) => {
149
+ // Only runs if user is admin and body is valid
150
+ return { success: true };
151
+ },
152
+ {
153
+ validate: [requireAdmin, validateBody],
154
+ }
155
+ );
156
+ ```
157
+
158
+ ### Locals
159
+
160
+ Set values on `req.locals`. Values can be static or functions that receive `(req, res)`.
161
+
162
+ ```typescript
163
+ import type { ExpressHandlerLocals } from "jaypie";
164
+
165
+ const getUser: ExpressHandlerLocals = async (req, res) => {
166
+ return await User.findById(req.params.userId);
167
+ };
168
+
169
+ const handler = expressHandler(
170
+ async (req, res) => {
171
+ // Access via req.locals
172
+ console.log(req.locals.apiVersion); // "v1"
173
+ console.log(req.locals.user); // User object
174
+ return req.locals.user;
175
+ },
176
+ {
177
+ locals: {
178
+ apiVersion: "v1", // Static value
179
+ user: getUser, // Function called during setup
180
+ },
181
+ }
182
+ );
183
+ ```
184
+
185
+ ## CORS Helper
186
+
187
+ Configure CORS middleware with Jaypie conventions.
188
+
189
+ ```typescript
190
+ import { cors } from "jaypie";
191
+ import type { CorsConfig } from "jaypie";
192
+
193
+ // Default: uses BASE_URL or PROJECT_BASE_URL env vars
194
+ app.use(cors());
195
+
196
+ // Wildcard origin
197
+ app.use(cors({ origin: "*" }));
198
+
199
+ // Specific origin
200
+ app.use(cors({ origin: "https://example.com" }));
201
+
202
+ // Custom configuration
203
+ const corsConfig: CorsConfig = {
204
+ origin: "https://api.example.com",
205
+ // Additional cors options
206
+ };
207
+ app.use(cors(corsConfig));
208
+ ```
209
+
210
+ Environment variables:
211
+ - `BASE_URL` or `PROJECT_BASE_URL`: Default allowed origin
212
+ - `PROJECT_ENV=sandbox` or `PROJECT_SANDBOX_MODE=true`: Allows localhost
213
+
214
+ ## Pre-built Routes
215
+
216
+ Ready-to-use route handlers for common responses.
217
+
218
+ ```typescript
219
+ import {
220
+ badRequestRoute, // 400 Bad Request
221
+ echoRoute, // 200 with request echo
222
+ forbiddenRoute, // 403 Forbidden
223
+ goneRoute, // 410 Gone
224
+ methodNotAllowedRoute, // 405 Method Not Allowed
225
+ noContentRoute, // 204 No Content
226
+ notFoundRoute, // 404 Not Found
227
+ notImplementedRoute, // 501 Not Implemented
228
+ } from "jaypie";
229
+
230
+ // Use as catch-all or placeholder routes
231
+ app.all("/deprecated/*", goneRoute);
232
+ app.use("*", notFoundRoute);
233
+
234
+ // Echo route for debugging
235
+ app.get("/debug/echo", echoRoute);
236
+ ```
237
+
238
+ ## HTTP Code Handler
239
+
240
+ Create custom HTTP status code handlers.
241
+
242
+ ```typescript
243
+ import { expressHttpCodeHandler, HTTP } from "jaypie";
244
+
245
+ // Returns 200 OK with empty body
246
+ const okRoute = expressHttpCodeHandler(HTTP.CODE.OK);
247
+
248
+ // Returns 202 Accepted
249
+ const acceptedRoute = expressHttpCodeHandler(202, { name: "accepted" });
250
+
251
+ app.post("/jobs", acceptedRoute);
252
+ ```
253
+
254
+ ## Error Handling
255
+
256
+ Throw Jaypie errors for proper HTTP responses.
257
+
258
+ ```typescript
259
+ import {
260
+ expressHandler,
261
+ BadRequestError,
262
+ NotFoundError,
263
+ UnauthorizedError,
264
+ ForbiddenError,
265
+ InternalError,
266
+ log,
267
+ } from "jaypie";
268
+
269
+ const handler = expressHandler(async (req, res) => {
270
+ const item = await findItem(req.params.id);
271
+
272
+ if (!item) {
273
+ log.warn("Item not found");
274
+ throw new NotFoundError();
275
+ }
276
+
277
+ if (!canAccess(req.user, item)) {
278
+ throw new ForbiddenError();
279
+ }
280
+
281
+ return item;
282
+ });
283
+ ```
284
+
285
+ Errors return JSON:API compliant error responses:
286
+
287
+ ```json
288
+ {
289
+ "errors": [{
290
+ "status": 404,
291
+ "title": "Not Found",
292
+ "detail": "The requested resource was not found"
293
+ }]
294
+ }
295
+ ```
296
+
297
+ ## TypeScript Types
298
+
299
+ All lifecycle function types are exported for type safety:
300
+
301
+ ```typescript
302
+ import type {
303
+ ExpressHandlerOptions,
304
+ ExpressHandlerLocals,
305
+ JaypieHandlerSetup,
306
+ JaypieHandlerTeardown,
307
+ JaypieHandlerValidate,
308
+ CorsConfig,
309
+ } from "jaypie";
310
+ ```
311
+
312
+ ## Complete Example
313
+
314
+ ```typescript
315
+ import express from "express";
316
+ import {
317
+ cors,
318
+ expressHandler,
319
+ notFoundRoute,
320
+ NotFoundError,
321
+ ForbiddenError,
322
+ log,
323
+ } from "jaypie";
324
+ import type {
325
+ JaypieHandlerSetup,
326
+ JaypieHandlerValidate,
327
+ ExpressHandlerLocals,
328
+ } from "jaypie";
329
+
330
+ const app = express();
331
+ app.use(express.json());
332
+ app.use(cors());
333
+
334
+ // Lifecycle functions
335
+ const authenticate: JaypieHandlerSetup = async (req, res) => {
336
+ const token = req.headers.authorization?.replace("Bearer ", "");
337
+ if (!token) throw new UnauthorizedError();
338
+ req.locals.user = await verifyToken(token);
339
+ };
340
+
341
+ const requireOwner: JaypieHandlerValidate = (req, res) => {
342
+ return req.locals.resource?.ownerId === req.locals.user?.id;
343
+ };
344
+
345
+ const loadResource: ExpressHandlerLocals = async (req, res) => {
346
+ const resource = await Resource.findById(req.params.id);
347
+ if (!resource) throw new NotFoundError();
348
+ return resource;
349
+ };
350
+
351
+ // Route handler
352
+ const updateResource = expressHandler(
353
+ async (req, res) => {
354
+ const { resource, user } = req.locals;
355
+
356
+ resource.name = req.body.name;
357
+ resource.updatedBy = user.id;
358
+ await resource.save();
359
+
360
+ log.trace("Resource updated");
361
+ return resource;
362
+ },
363
+ {
364
+ name: "updateResource",
365
+ setup: authenticate,
366
+ validate: requireOwner,
367
+ locals: {
368
+ resource: loadResource,
369
+ },
370
+ }
371
+ );
372
+
373
+ app.put("/resources/:id", updateResource);
374
+ app.use("*", notFoundRoute);
375
+
376
+ export default app;
377
+ ```
378
+
379
+ ## Response Headers
380
+
381
+ expressHandler automatically sets:
382
+ - `X-Powered-By: @jaypie/express`
383
+ - `X-Project-Handler: {name}` (if name option provided)
384
+ - `X-Project-Invocation: {uuid}` (request tracking ID)
385
+
386
+ ## Datadog Integration
387
+
388
+ When Datadog environment variables are configured, expressHandler automatically submits metrics for each request including status code and path.