@jaypie/mcp 0.8.152 → 0.8.154

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.
Files changed (42) hide show
  1. package/README.md +34 -0
  2. package/dist/createMcpServer.d.ts +5 -0
  3. package/dist/createMcpServer.js +3 -2
  4. package/dist/createMcpServer.js.map +1 -1
  5. package/dist/http/index.d.ts +92 -0
  6. package/dist/http/index.js +3 -0
  7. package/dist/http/index.js.map +1 -0
  8. package/dist/http/mcpHttpHandler.d.ts +35 -0
  9. package/dist/http/mcpHttpHandler.js +99 -0
  10. package/dist/http/mcpHttpHandler.js.map +1 -0
  11. package/dist/http/rpc.d.ts +70 -0
  12. package/dist/http/rpc.js +262 -0
  13. package/dist/http/rpc.js.map +1 -0
  14. package/dist/http/tools.d.ts +16 -0
  15. package/dist/http/tools.js +23 -0
  16. package/dist/http/tools.js.map +1 -0
  17. package/dist/index.d.ts +10 -0
  18. package/dist/mcpExpressHandler.d.ts +5 -0
  19. package/dist/mcpExpressHandler.js +2 -2
  20. package/dist/mcpExpressHandler.js.map +1 -1
  21. package/dist/suites/docs/index.js +1 -1
  22. package/package.json +27 -3
  23. package/release-notes/constructs/1.2.82.md +16 -0
  24. package/release-notes/constructs/1.2.83.md +21 -0
  25. package/release-notes/datadog/1.2.8.md +19 -0
  26. package/release-notes/dynamodb/0.6.11.md +13 -0
  27. package/release-notes/express/1.2.37.md +34 -0
  28. package/release-notes/fabric/0.3.12.md +20 -0
  29. package/release-notes/jaypie/1.2.101.md +12 -0
  30. package/release-notes/mcp/0.8.153.md +37 -0
  31. package/release-notes/mcp/0.8.154.md +11 -0
  32. package/release-notes/testkit/1.2.73.md +15 -0
  33. package/skills/apigateway.md +1 -1
  34. package/skills/aws.md +2 -1
  35. package/skills/cdk.md +5 -5
  36. package/skills/datadog.md +15 -2
  37. package/skills/dynamodb.md +3 -7
  38. package/skills/express.md +56 -2
  39. package/skills/fabric.md +14 -1
  40. package/skills/lambda.md +3 -3
  41. package/skills/mcp.md +79 -1
  42. package/skills/streaming.md +3 -1
package/skills/express.md CHANGED
@@ -60,6 +60,32 @@ expressHandler(handler, {
60
60
  });
61
61
  ```
62
62
 
63
+ ### Response Rules
64
+
65
+ The return value becomes the response. Do not call `res.end()`, `res.json()`, or `res.send()` inside the handler; `expressHandler` logs a warning ("Illegal call to res.end()") and replays the call.
66
+
67
+ | Return | Response |
68
+ |--------|----------|
69
+ | Object or JSON string | JSON body with the status (200 unless `res.status()` was called) |
70
+ | Non-JSON string | Text body with the status |
71
+ | `true` | 201, no body |
72
+ | No body (`undefined`, `null`, other falsy) | 204, no body |
73
+ | No body after `res.status(code)` | `code`, no body |
74
+
75
+ Set an explicit status and return nothing to answer with a bodiless non-204 status, such as 202 for MCP notifications:
76
+
77
+ ```typescript
78
+ app.post("/mcp", expressHandler(async (req, res) => {
79
+ if (isNotification(req.body)) {
80
+ res.status(202);
81
+ return; // 202 with no body, no warning
82
+ }
83
+ return answer(req.body);
84
+ }));
85
+ ```
86
+
87
+ `expressStreamHandler` flushes headers before the handler runs, so a status set inside a stream handler does not reach the client.
88
+
63
89
  ### Request Logging
64
90
 
65
91
  `expressHandler` logs a summary of every request. Two options control what that
@@ -131,6 +157,22 @@ import { createLambdaStreamHandler } from "@jaypie/express";
131
157
  export const handler = createLambdaStreamHandler(app);
132
158
  ```
133
159
 
160
+ `createLambdaStreamHandler` wraps with `awslambda.streamifyResponse` only when the Lambda runtime global exists. Elsewhere (unit tests, local servers) it returns the unwrapped `(event, responseStream, context)` handler, so importing the streaming entry point never throws. Without `awslambda.HttpResponseStream`, the body writes straight to the provided stream with no status/headers prelude.
161
+
162
+ ### Options
163
+
164
+ Both factories accept an optional second argument:
165
+
166
+ | Option | Default | Effect |
167
+ |--------|---------|--------|
168
+ | `name` | `"createLambdaHandler"` / `"createLambdaStreamHandler"` | Label on unhandled adapter error output (`[name] Unhandled error:`) |
169
+
170
+ ```typescript
171
+ export const handler = createLambdaStreamHandler(app, { name: "streamApi" });
172
+ ```
173
+
174
+ Lifecycle options such as `format`, `secrets`, `setup`, and `validate` belong on each route's `expressHandler` / `expressStreamHandler`, not the adapter.
175
+
134
176
  ### Cookies
135
177
 
136
178
  Both adapters emit every `Set-Cookie` value separately, so cookie-session auth (multiple cookies per response, chunked session cookies) works unchanged:
@@ -158,6 +200,17 @@ app.get("/callback", (req, res) => {
158
200
 
159
201
  Libraries that defer header or cookie writes this way work unchanged: `express-openid-connect` (session cookie), `express-session`, `morgan`, `compression`. `res.flushHeaders()` and `res._implicitHeader()` both route through the same path.
160
202
 
203
+ ### Node request contract (`rawHeaders`)
204
+
205
+ The adapter request exposes `rawHeaders` as Node's flat `[name, value, name, value]` list, built from the same lowercased event headers as `req.headers` (multi-value headers repeat the name once per value), plus `rawTrailers: []` and `trailers: {}`. When the event carries no `host` header, `host` falls back to `requestContext.domainName` (v1 and v2) in both `headers` and `rawHeaders`. Libraries that convert a Node request into a Web Standard `Request` read these directly, so `@hono/node-server` and the MCP SDK `StreamableHTTPServerTransport` work under both adapters:
206
+
207
+ ```typescript
208
+ import { mcpExpressHandler } from "@jaypie/mcp";
209
+
210
+ app.use("/mcp", await mcpExpressHandler({ enableSessions: false }));
211
+ export const handler = createLambdaHandler(app);
212
+ ```
213
+
161
214
  ### LLM Observability auto-flush
162
215
 
163
216
  `createLambdaHandler` and `createLambdaStreamHandler` call `flushLlmObs()` from `@jaypie/datadog` in their `finally` block, so buffered Datadog LLM Obs spans flush before the Lambda freezes — even when the Express app errors. No-op unless `DD_LLMOBS_ENABLED` is truthy; never affects the response. No per-handler flush code is required.
@@ -246,8 +299,8 @@ Deploy with `@jaypie/constructs`:
246
299
  import { JaypieLambda } from "@jaypie/constructs";
247
300
 
248
301
  new JaypieLambda(this, "Api", {
249
- entry: "src/lambda.ts",
250
- handler: "handler",
302
+ code: "../api/dist",
303
+ handler: "index.handler",
251
304
  environment: {
252
305
  PROJECT_ENV: "production",
253
306
  },
@@ -326,3 +379,4 @@ new JaypieLambda(this, "Api", {
326
379
  ## See Also
327
380
 
328
381
  - **`skill("streaming")`** - Full guide to `expressStreamHandler` and `createLambdaStreamHandler`
382
+ - **`skill("mcp")`** - `mcpHttpHandler` serves a `ServiceSuite` as MCP streamable HTTP from an Express route
package/skills/fabric.md CHANGED
@@ -200,11 +200,24 @@ import { createMcpServerFromSuite } from "@jaypie/fabric/mcp";
200
200
 
201
201
  const server = createMcpServerFromSuite(suite, {
202
202
  name: "users-api", // Optional override
203
+ services: ["greet"], // Optional allowlist of service aliases
203
204
  version: "1.0.0",
204
205
  });
205
- // All suite services now available as MCP tools
206
+ // Allowlisted suite services now available as MCP tools
206
207
  ```
207
208
 
209
+ `services` narrows the tools; omit it to register every service. The same
210
+ selection is available to any transport:
211
+
212
+ ```typescript
213
+ import { selectServiceFunctions } from "@jaypie/fabric";
214
+
215
+ const tools = selectServiceFunctions(suite, { services: ["greet"] });
216
+ ```
217
+
218
+ To serve a suite as MCP streamable HTTP from Lambda without the MCP SDK, use
219
+ `mcpHttpHandler` from `@jaypie/mcp/http` (see `skill("mcp")`).
220
+
208
221
  ## Input Validation
209
222
 
210
223
  Fabric validates inputs automatically:
package/skills/lambda.md CHANGED
@@ -159,13 +159,13 @@ Deploy with `@jaypie/constructs`:
159
159
  import { JaypieLambda } from "@jaypie/constructs";
160
160
 
161
161
  new JaypieLambda(this, "Handler", {
162
- entry: "src/handler.ts",
163
- handler: "handler",
162
+ code: "../api/dist",
163
+ handler: "index.handler",
164
164
  secrets: ["MY_SECRET"],
165
165
  });
166
166
  ```
167
167
 
168
- **Build order**: When using `code: "../package/dist"` instead of `entry`, ensure the package has a `build` script and builds before CDK synth in CI/CD. New packages need to be added to `build:core-deps` in root `package.json` if they're build-time dependencies. See `skill("cicd")` for details.
168
+ **Build order**: When using `code: "../package/dist"`, ensure the package has a `build` script and builds before CDK synth in CI/CD. New packages need to be added to `build:core-deps` in root `package.json` if they're build-time dependencies. See `skill("cicd")` for details.
169
169
 
170
170
  ## ESM Deployment
171
171
 
package/skills/mcp.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Jaypie MCP server tools reference
3
- related: mcp-datadog, debugging
3
+ related: mcp-datadog, debugging, express, fabric, streaming
4
4
  ---
5
5
 
6
6
  # Jaypie MCP
@@ -53,6 +53,84 @@ datadog("monitors", { status: ["Alert", "Warn"] })
53
53
  | `metrics` | Query timeseries metrics |
54
54
  | `rum` | Search RUM events |
55
55
 
56
+ ## Serving MCP over HTTP
57
+
58
+ ### Streamable HTTP without the SDK (`@jaypie/mcp/http`)
59
+
60
+ `mcpHttpHandler` serves a fabric `ServiceSuite` as MCP streamable HTTP from an Express route or a Jaypie Lambda. It implements stateless JSON-RPC 2.0 directly and never loads `@modelcontextprotocol/sdk`, whose Node transport fails against `LambdaRequest`.
61
+
62
+ ```typescript
63
+ import express from "express";
64
+ import { createLambdaStreamHandler } from "@jaypie/express";
65
+ import { mcpHttpHandler } from "@jaypie/mcp/http";
66
+
67
+ const app = express();
68
+ app.use(express.json());
69
+ app.all(
70
+ "/mcp",
71
+ mcpHttpHandler({
72
+ secrets: ["PROJECT_API_KEY"],
73
+ services: ["skill", "version", "release_notes"],
74
+ setup: requireApiKey,
75
+ }),
76
+ );
77
+
78
+ export const handler = createLambdaStreamHandler(app);
79
+ ```
80
+
81
+ | Request | Response |
82
+ |---------|----------|
83
+ | POST, `Accept` absent or prefers `application/json` | 200 JSON (one response, or an array for a batch) |
84
+ | POST, `Accept` prefers `text/event-stream` | 200 SSE via `expressStreamHandler({ format: "sse" })`, one `event: message` per response |
85
+ | POST carrying only notifications | 202, no body |
86
+ | GET, DELETE, any other method | 405 with `Allow: POST` |
87
+
88
+ Methods: `initialize` (echoes a supported `protocolVersion`, else `2025-06-18`), `notifications/initialized`, `notifications/cancelled`, `ping`, `tools/list`, `tools/call`. Errors: `-32700` unparseable body, `-32600` invalid request or empty batch, `-32601` unknown method, `-32602` unknown tool or non-object arguments. A tool that throws answers `isError: true`; a Jaypie error surfaces its message, any other error answers "The tool call failed".
89
+
90
+ | Option | Default | Description |
91
+ |--------|---------|-------------|
92
+ | `suite` | Jaypie MCP suite (loaded on first request) | `ServiceSuite` served as tools |
93
+ | `services` | all | Allowlist of service aliases exposed as tools |
94
+ | `name` | `suite.name` | `serverInfo.name` reported by `initialize` |
95
+ | `version` | `suite.version` | `serverInfo.version` reported by `initialize` |
96
+ | `chaos`, `locals`, `secrets`, `setup`, `teardown`, `unavailable`, `validate`, ... | | Passed to `expressHandler` and `expressStreamHandler` |
97
+
98
+ `handleMcpRpcBody(body, options)` and `handleMcpRpc(request, options)` expose the JSON-RPC layer for other transports. `@jaypie/express` and `@jaypie/logger` are optional peer dependencies required by this entry point; `jaypie` provides both.
99
+
100
+ #### Lambda Deployment
101
+
102
+ - **Streaming**: `createLambdaStreamHandler(app)` behind a Function URL with `RESPONSE_STREAM` invoke mode (for example `JaypieDistribution` with `streaming: true`). Both JSON and SSE responses work.
103
+ - **Buffered**: `createLambdaHandler(app)` behind API Gateway or a buffered Function URL. JSON responses work; SSE responses arrive when the request completes.
104
+ - The endpoint is stateless: no sessions, no `Mcp-Session-Id`, no server-initiated GET stream. Every POST stands alone, which suits Lambda.
105
+ - Put authentication in `setup` or `validate` so it runs for both JSON and SSE requests.
106
+
107
+ ### SDK Transport (`mcpExpressHandler`)
108
+
109
+ `mcpExpressHandler` from `@jaypie/mcp` wraps the SDK's `StreamableHTTPServerTransport` with optional sessions. It suits a long-running Express server, not Lambda.
110
+
111
+ ```typescript
112
+ import express from "express";
113
+ import { mcpExpressHandler } from "@jaypie/mcp";
114
+
115
+ const app = express();
116
+ app.use(express.json());
117
+ app.use("/mcp", await mcpExpressHandler({ services: ["skill"], version: "1.0.0" }));
118
+ ```
119
+
120
+ ## Filtering Tools
121
+
122
+ Every server factory accepts `services`, an allowlist of service aliases. Omit it to expose every tool; an empty array exposes none; unknown aliases are ignored. All share `selectServiceFunctions` from `@jaypie/fabric`.
123
+
124
+ ```typescript
125
+ import { createMcpServer } from "@jaypie/mcp";
126
+ import { mcpHttpHandler } from "@jaypie/mcp/http";
127
+ import { createMcpServerFromSuite } from "@jaypie/fabric/mcp";
128
+
129
+ createMcpServer({ services: ["skill", "version"] }); // stdio / SDK
130
+ createMcpServerFromSuite(suite, { services: ["greet"] }); // any suite, SDK
131
+ mcpHttpHandler({ services: ["skill", "release_notes"] }); // no SDK
132
+ ```
133
+
56
134
  ## Environment Variables
57
135
 
58
136
  ### Datadog Tools
@@ -14,6 +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
18
 
18
19
  ## Stream Formats
19
20
 
@@ -82,7 +83,8 @@ export const handler = lambdaStreamHandler(async (event, context) => {
82
83
  import { JaypieLambda, JaypieDistribution } from "@jaypie/constructs";
83
84
 
84
85
  const streamingLambda = new JaypieLambda(this, "Stream", {
85
- entry: "src/stream.ts",
86
+ code: "../api/dist",
87
+ handler: "index.handler",
86
88
  });
87
89
 
88
90
  new JaypieDistribution(this, "Api", {