@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc.js","sources":["../../src/http/rpc.ts"],"sourcesContent":["import { isJaypieError } from \"@jaypie/errors\";\nimport type { ServiceSuite } from \"@jaypie/fabric\";\nimport { inputToJsonSchema } from \"@jaypie/fabric/llm\";\nimport { log } from \"@jaypie/logger\";\n\nimport { findTool, selectTools } from \"./tools.js\";\n\n//\n//\n// Constants\n//\n\nexport const JSON_RPC_ERROR = {\n INTERNAL: -32603,\n INVALID_PARAMS: -32602,\n INVALID_REQUEST: -32600,\n METHOD_NOT_FOUND: -32601,\n PARSE: -32700,\n} as const;\n\nexport const JSON_RPC_VERSION = \"2.0\";\n\nexport const MCP_METHOD = {\n CALL_TOOL: \"tools/call\",\n CANCELLED: \"notifications/cancelled\",\n INITIALIZE: \"initialize\",\n INITIALIZED: \"notifications/initialized\",\n LIST_TOOLS: \"tools/list\",\n PING: \"ping\",\n} as const;\n\n/** Latest protocol version; answered when a client requests an unknown one */\nexport const MCP_PROTOCOL_VERSION = \"2025-06-18\";\n\nexport const MCP_SUPPORTED_PROTOCOL_VERSIONS: readonly string[] = [\n MCP_PROTOCOL_VERSION,\n \"2025-03-26\",\n \"2024-11-05\",\n];\n\nconst MESSAGE = {\n EMPTY_BATCH: \"An empty batch is not a request\",\n INVALID_ARGUMENTS: \"Tool arguments must be an object\",\n INVALID_REQUEST: \"The request is not a JSON-RPC request\",\n PARSE: \"The body is not JSON-RPC\",\n TOOL_FAILED: \"The tool call failed\",\n UNKNOWN_METHOD: \"Unknown method\",\n UNKNOWN_TOOL: \"Unknown tool\",\n} as const;\n\n//\n//\n// Types\n//\n\nexport type JsonRpcId = number | string | null;\n\nexport interface JsonRpcRequest {\n id?: JsonRpcId;\n jsonrpc?: string;\n method?: string;\n params?: Record<string, unknown>;\n}\n\nexport interface JsonRpcResponse {\n error?: { code: number; data?: unknown; message: string };\n id: JsonRpcId;\n jsonrpc: typeof JSON_RPC_VERSION;\n result?: unknown;\n}\n\nexport interface McpRpcOptions {\n /** Server name reported by `initialize`. Defaults to the suite name */\n name?: string;\n /** Allowlist of service aliases exposed as tools. Omit to expose all */\n services?: string[];\n /** Suite served as tools. Defaults to the Jaypie MCP suite */\n suite?: ServiceSuite;\n /** Server version reported by `initialize`. Defaults to the suite version */\n version?: string;\n}\n\n//\n//\n// Helpers\n//\n\nlet defaultSuite: Promise<ServiceSuite> | undefined;\n\n/** Load the Jaypie suite only when no suite is supplied */\nfunction loadDefaultSuite(): Promise<ServiceSuite> {\n if (!defaultSuite) {\n defaultSuite = import(\"../suite.js\")\n .then((module) => module.suite)\n .catch((error: unknown) => {\n defaultSuite = undefined;\n throw error;\n });\n }\n return defaultSuite;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction jsonRpcError({\n code,\n id = null,\n message,\n}: {\n code: number;\n id?: JsonRpcId;\n message: string;\n}): JsonRpcResponse {\n return { error: { code, message }, id, jsonrpc: JSON_RPC_VERSION };\n}\n\nfunction jsonRpcResult({\n id,\n result,\n}: {\n id: JsonRpcId;\n result: unknown;\n}): JsonRpcResponse {\n return { id, jsonrpc: JSON_RPC_VERSION, result };\n}\n\nfunction textContent(value: unknown): { text: string; type: \"text\" }[] {\n let text: string;\n if (value === undefined || value === null) {\n text = \"\";\n } else if (typeof value === \"string\") {\n text = value;\n } else {\n text = JSON.stringify(value, null, 2);\n }\n return [{ text, type: \"text\" }];\n}\n\n/** A notification carries no id and expects no response */\nexport function isNotification(request: unknown): boolean {\n return isRecord(request) && (request.id === undefined || request.id === null);\n}\n\n/**\n * Normalize a raw body: a Buffer or string is parsed as JSON; anything else is\n * taken as already parsed. Returns `undefined` when there is no parseable body.\n */\nexport function parseRpcBody(body: unknown): unknown {\n const raw = Buffer.isBuffer(body) ? body.toString(\"utf8\") : body;\n if (typeof raw !== \"string\") {\n return raw;\n }\n try {\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n}\n\n/** Does the body carry anything that must be answered? */\nexport function expectsResponse(body: unknown): boolean {\n const parsed = parseRpcBody(body);\n if (Array.isArray(parsed)) {\n return (\n parsed.length === 0 || parsed.some((entry) => !isNotification(entry))\n );\n }\n return !isNotification(parsed);\n}\n\nasync function callTool({\n id,\n params,\n services,\n suite,\n}: {\n id: JsonRpcId;\n params: Record<string, unknown>;\n services?: string[];\n suite: ServiceSuite;\n}): Promise<JsonRpcResponse> {\n const name = typeof params.name === \"string\" ? params.name : \"\";\n const tool = findTool(name, { services, suite });\n if (!tool) {\n return jsonRpcError({\n code: JSON_RPC_ERROR.INVALID_PARAMS,\n id,\n message: `${MESSAGE.UNKNOWN_TOOL}: ${name}`,\n });\n }\n\n const args = params.arguments ?? {};\n if (!isRecord(args)) {\n return jsonRpcError({\n code: JSON_RPC_ERROR.INVALID_PARAMS,\n id,\n message: MESSAGE.INVALID_ARGUMENTS,\n });\n }\n\n try {\n const result = await tool(args);\n log.trace(`[mcp] Tool call complete: ${name}`);\n return jsonRpcResult({ id, result: { content: textContent(result) } });\n } catch (error) {\n let message: string = MESSAGE.TOOL_FAILED;\n if (isJaypieError(error) && error instanceof Error) {\n message = error.message;\n log.debug(`[mcp] Tool call rejected: ${name}`);\n } else {\n log.warn(`[mcp] Tool call failed: ${name}`);\n }\n log.var({\n mcpToolError: {\n message: error instanceof Error ? error.message : String(error),\n tool: name,\n },\n });\n return jsonRpcResult({\n id,\n result: { content: textContent(message), isError: true },\n });\n }\n}\n\n//\n//\n// Main\n//\n\n/**\n * Answer one JSON-RPC request against a suite\n *\n * Returns `null` for a notification, which the transport answers with 202 and\n * no body.\n */\nexport async function handleMcpRpc(\n request: JsonRpcRequest,\n options: McpRpcOptions = {},\n): Promise<JsonRpcResponse | null> {\n const id = request.id ?? null;\n const notification = isNotification(request);\n\n if (typeof request.method !== \"string\") {\n if (notification) {\n return null;\n }\n return jsonRpcError({\n code: JSON_RPC_ERROR.INVALID_REQUEST,\n id,\n message: MESSAGE.INVALID_REQUEST,\n });\n }\n\n const suite = options.suite ?? (await loadDefaultSuite());\n const { name = suite.name, services, version = suite.version } = options;\n const params = isRecord(request.params) ? request.params : {};\n\n log.trace(`[mcp] ${request.method}`);\n\n switch (request.method) {\n case MCP_METHOD.INITIALIZE: {\n const requested = params.protocolVersion;\n const protocolVersion =\n typeof requested === \"string\" &&\n MCP_SUPPORTED_PROTOCOL_VERSIONS.includes(requested)\n ? requested\n : MCP_PROTOCOL_VERSION;\n return jsonRpcResult({\n id,\n result: {\n capabilities: { tools: {} },\n protocolVersion,\n serverInfo: { name, version },\n },\n });\n }\n\n case MCP_METHOD.CANCELLED:\n case MCP_METHOD.INITIALIZED:\n return null;\n\n case MCP_METHOD.PING:\n return jsonRpcResult({ id, result: {} });\n\n case MCP_METHOD.LIST_TOOLS:\n return jsonRpcResult({\n id,\n result: {\n tools: selectTools({ services, suite }).map((tool) => ({\n description: tool.description ?? \"\",\n inputSchema: inputToJsonSchema(tool.input),\n name: String(tool.alias),\n })),\n },\n });\n\n case MCP_METHOD.CALL_TOOL:\n return callTool({ id, params, services, suite });\n\n default:\n if (notification) {\n return null;\n }\n return jsonRpcError({\n code: JSON_RPC_ERROR.METHOD_NOT_FOUND,\n id,\n message: `${MESSAGE.UNKNOWN_METHOD}: ${request.method}`,\n });\n }\n}\n\n/**\n * Answer a body that may be one request or a batch\n *\n * Accepts a parsed body, a JSON string, or a Buffer. Returns `null` when\n * nothing needs an answer.\n */\nexport async function handleMcpRpcBody(\n body: unknown,\n options: McpRpcOptions = {},\n): Promise<JsonRpcResponse | JsonRpcResponse[] | null> {\n const parsed = parseRpcBody(body);\n\n if (parsed === undefined) {\n return jsonRpcError({ code: JSON_RPC_ERROR.PARSE, message: MESSAGE.PARSE });\n }\n\n if (Array.isArray(parsed)) {\n if (parsed.length === 0) {\n return jsonRpcError({\n code: JSON_RPC_ERROR.INVALID_REQUEST,\n message: MESSAGE.EMPTY_BATCH,\n });\n }\n const responses = await Promise.all(\n parsed.map((entry) =>\n isRecord(entry)\n ? handleMcpRpc(entry as JsonRpcRequest, options)\n : jsonRpcError({\n code: JSON_RPC_ERROR.INVALID_REQUEST,\n message: MESSAGE.INVALID_REQUEST,\n }),\n ),\n );\n const answered = responses.filter(\n (response): response is JsonRpcResponse => response !== null,\n );\n return answered.length > 0 ? answered : null;\n }\n\n if (!isRecord(parsed)) {\n return jsonRpcError({\n code: JSON_RPC_ERROR.INVALID_REQUEST,\n message: MESSAGE.INVALID_REQUEST,\n });\n }\n\n return handleMcpRpc(parsed as JsonRpcRequest, options);\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Service, ServiceSuite } from "@jaypie/fabric";
2
+ export interface SelectToolsOptions {
3
+ /** Allowlist of service aliases. Omit to select every service */
4
+ services?: string[];
5
+ suite: ServiceSuite;
6
+ }
7
+ /**
8
+ * Select the services a suite exposes as MCP tools
9
+ *
10
+ * Delegates to `selectServiceFunctions` from `@jaypie/fabric`, the same
11
+ * selection `createMcpServerFromSuite` applies, so every transport narrows
12
+ * tools identically.
13
+ */
14
+ export declare function selectTools({ services, suite, }: SelectToolsOptions): Service[];
15
+ /** Find one exposed tool by name; tools outside the allowlist are absent */
16
+ export declare function findTool(name: string, options: SelectToolsOptions): Service | undefined;
@@ -0,0 +1,23 @@
1
+ import { selectServiceFunctions } from '@jaypie/fabric';
2
+
3
+ //
4
+ //
5
+ // Main
6
+ //
7
+ /**
8
+ * Select the services a suite exposes as MCP tools
9
+ *
10
+ * Delegates to `selectServiceFunctions` from `@jaypie/fabric`, the same
11
+ * selection `createMcpServerFromSuite` applies, so every transport narrows
12
+ * tools identically.
13
+ */
14
+ function selectTools({ services, suite, }) {
15
+ return selectServiceFunctions(suite, { services });
16
+ }
17
+ /** Find one exposed tool by name; tools outside the allowlist are absent */
18
+ function findTool(name, options) {
19
+ return selectTools(options).find((tool) => tool.alias === name);
20
+ }
21
+
22
+ export { findTool, selectTools };
23
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sources":["../../src/http/tools.ts"],"sourcesContent":["import { selectServiceFunctions } from \"@jaypie/fabric\";\nimport type { Service, ServiceSuite } from \"@jaypie/fabric\";\n\n//\n//\n// Types\n//\n\nexport interface SelectToolsOptions {\n /** Allowlist of service aliases. Omit to select every service */\n services?: string[];\n suite: ServiceSuite;\n}\n\n//\n//\n// Main\n//\n\n/**\n * Select the services a suite exposes as MCP tools\n *\n * Delegates to `selectServiceFunctions` from `@jaypie/fabric`, the same\n * selection `createMcpServerFromSuite` applies, so every transport narrows\n * tools identically.\n */\nexport function selectTools({\n services,\n suite,\n}: SelectToolsOptions): Service[] {\n return selectServiceFunctions(suite, { services });\n}\n\n/** Find one exposed tool by name; tools outside the allowlist are absent */\nexport function findTool(\n name: string,\n options: SelectToolsOptions,\n): Service | undefined {\n return selectTools(options).find((tool) => tool.alias === name);\n}\n"],"names":[],"mappings":";;AAcA;AACA;AACA;AACA;AAEA;;;;;;AAMG;SACa,WAAW,CAAC,EAC1B,QAAQ,EACR,KAAK,GACc,EAAA;IACnB,OAAO,sBAAsB,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC;AACpD;AAEA;AACM,SAAU,QAAQ,CACtB,IAAY,EACZ,OAA2B,EAAA;AAE3B,IAAA,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC;AACjE;;;;"}
package/dist/index.d.ts CHANGED
@@ -3,6 +3,11 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { Request, Response } from 'express';
4
4
 
5
5
  interface CreateMcpServerOptions {
6
+ /**
7
+ * Allowlist of tool names (service aliases) to expose. Omit to expose every
8
+ * tool; an empty array exposes none.
9
+ */
10
+ services?: string[];
6
11
  version?: string;
7
12
  verbose?: boolean;
8
13
  }
@@ -32,6 +37,11 @@ interface McpExpressHandlerOptions {
32
37
  * Default: true
33
38
  */
34
39
  enableSessions?: boolean;
40
+ /**
41
+ * Allowlist of tool names (service aliases) to expose. Omit to expose every
42
+ * tool
43
+ */
44
+ services?: string[];
35
45
  /**
36
46
  * Custom session ID generator function
37
47
  */
@@ -12,6 +12,11 @@ export interface McpExpressHandlerOptions {
12
12
  * Default: true
13
13
  */
14
14
  enableSessions?: boolean;
15
+ /**
16
+ * Allowlist of tool names (service aliases) to expose. Omit to expose every
17
+ * tool
18
+ */
19
+ services?: string[];
15
20
  /**
16
21
  * Custom session ID generator function
17
22
  */
@@ -30,8 +30,8 @@ const DEFAULT_VERSION = "0.0.0";
30
30
  * ```
31
31
  */
32
32
  async function mcpExpressHandler(options = {}) {
33
- const { version = DEFAULT_VERSION, enableSessions = true, sessionIdGenerator = () => randomUUID(), enableJsonResponse = false, } = options;
34
- const server = createMcpServer(version);
33
+ const { version = DEFAULT_VERSION, enableSessions = true, sessionIdGenerator = () => randomUUID(), enableJsonResponse = false, services, } = options;
34
+ const server = createMcpServer({ services, version });
35
35
  const transport = new StreamableHTTPServerTransport({
36
36
  sessionIdGenerator: enableSessions ? sessionIdGenerator : undefined,
37
37
  enableJsonResponse,
@@ -1 +1 @@
1
- {"version":3,"file":"mcpExpressHandler.js","sources":["../src/mcpExpressHandler.ts"],"sourcesContent":["import { Request, Response } from \"express\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { randomUUID } from \"node:crypto\";\nimport { createMcpServer } from \"./createMcpServer.js\";\n\n// Version will be injected during build\nconst DEFAULT_VERSION = \"0.0.0\";\n\n/**\n * Options for configuring the MCP Express handler\n */\nexport interface McpExpressHandlerOptions {\n /**\n * Version string for the MCP server\n */\n version?: string;\n /**\n * Whether to enable session management (stateful mode)\n * Default: true\n */\n enableSessions?: boolean;\n /**\n * Custom session ID generator function\n */\n sessionIdGenerator?: () => string;\n /**\n * Enable JSON responses instead of SSE streaming\n * Default: false\n */\n enableJsonResponse?: boolean;\n}\n\n/**\n * Creates an Express handler for the Jaypie MCP server using HTTP transport\n *\n * @param options - Configuration options for the handler\n * @returns Promise that resolves to an Express middleware function\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { mcpExpressHandler } from '@jaypie/mcp';\n *\n * const app = express();\n * app.use(express.json());\n *\n * // Note: mcpExpressHandler is now async\n * app.use('/mcp', await mcpExpressHandler({\n * version: '1.0.0',\n * enableSessions: true\n * }));\n *\n * app.listen(3000, () => {\n * console.log('MCP server running on http://localhost:3000/mcp');\n * });\n * ```\n */\nexport async function mcpExpressHandler(\n options: McpExpressHandlerOptions = {},\n): Promise<(req: Request, res: Response) => Promise<void>> {\n const {\n version = DEFAULT_VERSION,\n enableSessions = true,\n sessionIdGenerator = () => randomUUID(),\n enableJsonResponse = false,\n } = options;\n\n const server = createMcpServer(version);\n\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: enableSessions ? sessionIdGenerator : undefined,\n enableJsonResponse,\n });\n\n await server.connect(transport);\n\n return async (req: Request, res: Response): Promise<void> => {\n try {\n await transport.handleRequest(req, res, req.body);\n } catch (error) {\n if (!res.headersSent) {\n res.status(500).json({\n error: \"Internal server error\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n };\n}\n"],"names":[],"mappings":";;;;AAKA;AACA,MAAM,eAAe,GAAG,OAAO;AA0B/B;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;AACI,eAAe,iBAAiB,CACrC,UAAoC,EAAE,EAAA;IAEtC,MAAM,EACJ,OAAO,GAAG,eAAe,EACzB,cAAc,GAAG,IAAI,EACrB,kBAAkB,GAAG,MAAM,UAAU,EAAE,EACvC,kBAAkB,GAAG,KAAK,GAC3B,GAAG,OAAO;AAEX,IAAA,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC;AAEvC,IAAA,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;QAClD,kBAAkB,EAAE,cAAc,GAAG,kBAAkB,GAAG,SAAS;QACnE,kBAAkB;AACnB,KAAA,CAAC;AAEF,IAAA,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;AAE/B,IAAA,OAAO,OAAO,GAAY,EAAE,GAAa,KAAmB;AAC1D,QAAA,IAAI;AACF,YAAA,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC;QACnD;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE;AACpB,gBAAA,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;AACnB,oBAAA,KAAK,EAAE,uBAAuB;AAC9B,oBAAA,OAAO,EAAE,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,eAAe;AAClE,iBAAA,CAAC;YACJ;QACF;AACF,IAAA,CAAC;AACH;;;;"}
1
+ {"version":3,"file":"mcpExpressHandler.js","sources":["../src/mcpExpressHandler.ts"],"sourcesContent":["import { Request, Response } from \"express\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { randomUUID } from \"node:crypto\";\nimport { createMcpServer } from \"./createMcpServer.js\";\n\n// Version will be injected during build\nconst DEFAULT_VERSION = \"0.0.0\";\n\n/**\n * Options for configuring the MCP Express handler\n */\nexport interface McpExpressHandlerOptions {\n /**\n * Version string for the MCP server\n */\n version?: string;\n /**\n * Whether to enable session management (stateful mode)\n * Default: true\n */\n enableSessions?: boolean;\n /**\n * Allowlist of tool names (service aliases) to expose. Omit to expose every\n * tool\n */\n services?: string[];\n /**\n * Custom session ID generator function\n */\n sessionIdGenerator?: () => string;\n /**\n * Enable JSON responses instead of SSE streaming\n * Default: false\n */\n enableJsonResponse?: boolean;\n}\n\n/**\n * Creates an Express handler for the Jaypie MCP server using HTTP transport\n *\n * @param options - Configuration options for the handler\n * @returns Promise that resolves to an Express middleware function\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { mcpExpressHandler } from '@jaypie/mcp';\n *\n * const app = express();\n * app.use(express.json());\n *\n * // Note: mcpExpressHandler is now async\n * app.use('/mcp', await mcpExpressHandler({\n * version: '1.0.0',\n * enableSessions: true\n * }));\n *\n * app.listen(3000, () => {\n * console.log('MCP server running on http://localhost:3000/mcp');\n * });\n * ```\n */\nexport async function mcpExpressHandler(\n options: McpExpressHandlerOptions = {},\n): Promise<(req: Request, res: Response) => Promise<void>> {\n const {\n version = DEFAULT_VERSION,\n enableSessions = true,\n sessionIdGenerator = () => randomUUID(),\n enableJsonResponse = false,\n services,\n } = options;\n\n const server = createMcpServer({ services, version });\n\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: enableSessions ? sessionIdGenerator : undefined,\n enableJsonResponse,\n });\n\n await server.connect(transport);\n\n return async (req: Request, res: Response): Promise<void> => {\n try {\n await transport.handleRequest(req, res, req.body);\n } catch (error) {\n if (!res.headersSent) {\n res.status(500).json({\n error: \"Internal server error\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }\n };\n}\n"],"names":[],"mappings":";;;;AAKA;AACA,MAAM,eAAe,GAAG,OAAO;AA+B/B;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;AACI,eAAe,iBAAiB,CACrC,UAAoC,EAAE,EAAA;IAEtC,MAAM,EACJ,OAAO,GAAG,eAAe,EACzB,cAAc,GAAG,IAAI,EACrB,kBAAkB,GAAG,MAAM,UAAU,EAAE,EACvC,kBAAkB,GAAG,KAAK,EAC1B,QAAQ,GACT,GAAG,OAAO;IAEX,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAErD,IAAA,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;QAClD,kBAAkB,EAAE,cAAc,GAAG,kBAAkB,GAAG,SAAS;QACnE,kBAAkB;AACnB,KAAA,CAAC;AAEF,IAAA,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;AAE/B,IAAA,OAAO,OAAO,GAAY,EAAE,GAAa,KAAmB;AAC1D,QAAA,IAAI;AACF,YAAA,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC;QACnD;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE;AACpB,gBAAA,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;AACnB,oBAAA,KAAK,EAAE,uBAAuB;AAC9B,oBAAA,OAAO,EAAE,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,eAAe;AAClE,iBAAA,CAAC;YACJ;QACF;AACF,IAAA,CAAC;AACH;;;;"}
@@ -10,7 +10,7 @@ import { gt } from 'semver';
10
10
  /**
11
11
  * Docs Suite - Documentation services (skill, version, release_notes)
12
12
  */
13
- const BUILD_VERSION_STRING = "@jaypie/mcp@0.8.152#29c7932c"
13
+ const BUILD_VERSION_STRING = "@jaypie/mcp@0.8.154#f76841e4"
14
14
  ;
15
15
  const __filename$1 = fileURLToPath(import.meta.url);
16
16
  const __dirname$1 = path.dirname(__filename$1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaypie/mcp",
3
- "version": "0.8.152",
3
+ "version": "0.8.154",
4
4
  "description": "Jaypie MCP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,6 +16,12 @@
16
16
  "default": "./dist/index.js"
17
17
  }
18
18
  },
19
+ "./http": {
20
+ "import": {
21
+ "types": "./dist/http/index.d.ts",
22
+ "default": "./dist/http/index.js"
23
+ }
24
+ },
19
25
  "./suite": {
20
26
  "import": {
21
27
  "types": "./dist/suite.d.ts",
@@ -43,9 +49,9 @@
43
49
  "typecheck": "tsc --noEmit"
44
50
  },
45
51
  "dependencies": {
46
- "@jaypie/datadog": "^1.2.7",
52
+ "@jaypie/datadog": "^1.2.8",
47
53
  "@jaypie/errors": "^1.2.6",
48
- "@jaypie/fabric": "^0.3.3",
54
+ "@jaypie/fabric": "^0.3.12",
49
55
  "@jaypie/kit": "^1.2.12",
50
56
  "@jaypie/tildeskill": "^0.3.3",
51
57
  "@modelcontextprotocol/sdk": "^1.29.0",
@@ -54,6 +60,8 @@
54
60
  "zod": "^4.1.13"
55
61
  },
56
62
  "devDependencies": {
63
+ "@jaypie/express": "^1.2.37",
64
+ "@jaypie/logger": "^1.2.24",
57
65
  "@rollup/plugin-replace": "^6.0.3",
58
66
  "@rollup/plugin-typescript": "^12.3.0",
59
67
  "@types/express": "^5.0.3",
@@ -64,6 +72,22 @@
64
72
  "rollup-plugin-dts": "^6.1.1",
65
73
  "typescript": "^5.9.3"
66
74
  },
75
+ "peerDependencies": {
76
+ "@jaypie/express": "^1.2.37",
77
+ "@jaypie/logger": "^1.2.24",
78
+ "express": ">=4.0.0"
79
+ },
80
+ "peerDependenciesMeta": {
81
+ "@jaypie/express": {
82
+ "optional": true
83
+ },
84
+ "@jaypie/logger": {
85
+ "optional": true
86
+ },
87
+ "express": {
88
+ "optional": true
89
+ }
90
+ },
67
91
  "publishConfig": {
68
92
  "access": "public"
69
93
  }
@@ -0,0 +1,16 @@
1
+ ---
2
+ version: 1.2.82
3
+ date: 2026-09-12
4
+ summary: JaypieDistributionProps JSDoc states the actual roleTag, host, and zone defaults
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **`roleTag`** (#536) documents `@default CDK.ROLE.API`, matching the
10
+ constructor. It previously claimed `CDK.ROLE.HOSTING`.
11
+ - **`host`** documents that `CDK_ENV_API_HOST_NAME` is checked first, then
12
+ `CDK_ENV_API_SUBDOMAIN` merged with `CDK_ENV_API_HOSTED_ZONE || CDK_ENV_HOSTED_ZONE`.
13
+ - **`zone`** documents `@default CDK_ENV_HOSTED_ZONE`. The code does not read
14
+ `CDK_ENV_API_HOSTED_ZONE` for the zone.
15
+
16
+ No runtime behavior changed.
@@ -0,0 +1,21 @@
1
+ ---
2
+ version: 1.2.83
3
+ date: 2026-09-12
4
+ summary: JaypieDistribution resolves zone from CDK_ENV_API_HOSTED_ZONE before CDK_ENV_HOSTED_ZONE
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **`JaypieDistribution` `zone`** falls back to
10
+ `CDK_ENV_API_HOSTED_ZONE || CDK_ENV_HOSTED_ZONE`, matching `host` and
11
+ `JaypieCertificate`. Previously `zone` read only `CDK_ENV_HOSTED_ZONE`, so a
12
+ stack setting only `CDK_ENV_API_HOSTED_ZONE` resolved a host but created no
13
+ certificate or DNS records.
14
+
15
+ ## Upgrade
16
+
17
+ A stack that sets both variables to different zones now places the certificate
18
+ and DNS records in the `CDK_ENV_API_HOSTED_ZONE` zone. Pass `zone` explicitly to
19
+ keep the previous zone.
20
+
21
+ Jaypie 2 consolidates on `CDK_ENV_HOSTED_ZONE` alone.
@@ -0,0 +1,19 @@
1
+ ---
2
+ version: 1.2.8
3
+ date: 2026-09-12
4
+ summary: hasDatadogEnv recognizes secret references; metric submission rejects with BadGatewayError
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **`hasDatadogEnv` recognizes every key source** (#513). `DD_API_KEY` and the
10
+ `SECRET_<NAME>` / `<NAME>_SECRET` references that `getEnvSecret` resolves now
11
+ count as present. A key held only in Secrets Manager (for example
12
+ `DATADOG_API_KEY_SECRET`) turns on `expressHandler` response metrics.
13
+ Detection reads variable names only and fetches nothing.
14
+ - **Jaypie errors from metric submission** (#516). A failed Datadog response or
15
+ connection error rejects with `BadGatewayError` rather than a vanilla `Error`.
16
+ `submitMetric`, `submitMetricSet`, and `submitDistribution` still catch and
17
+ return `false`.
18
+ - Tests cover a Datadog 403 through `datadogService` returning
19
+ `success: false` with a readable error.
@@ -0,0 +1,13 @@
1
+ ---
2
+ version: 0.6.11
3
+ date: 2026-09-12
4
+ summary: updateEntity removes a row from a sparse index when its source field is deleted
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **Sparse indexes can be exited** (#533). Through `@jaypie/fabric` 0.3.12,
10
+ `indexEntity` drops a stale index key (for example `indexModelCategory` and
11
+ `indexModelCategorySk`) when its source field is absent. `updateEntity` writes
12
+ the entity with a full PUT, so deleting `category` and updating removes the
13
+ row from `indexModelCategory`.
@@ -0,0 +1,34 @@
1
+ ---
2
+ version: 1.2.37
3
+ date: 2026-09-12
4
+ summary: LambdaRequest provides rawHeaders; createLambdaStreamHandler runs outside Lambda and honors name
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **`rawHeaders`, `rawTrailers`, `trailers` on `LambdaRequest`** (#532).
10
+ `rawHeaders` is the flat `[name, value, ...]` list Node's `IncomingMessage`
11
+ guarantees, repeating a name per value for multi-value headers.
12
+ `@hono/node-server`-based handlers, including `mcpExpressHandler`, now work
13
+ under `createLambdaHandler`.
14
+ - **`host` fallback**. When an event carries no `host` header,
15
+ `requestContext.domainName` supplies it.
16
+ - **`createLambdaStreamHandler` guards `awslambda`** (#534). Outside the Lambda
17
+ runtime it returns the plain `(event, responseStream, context)` handler rather
18
+ than throwing `ReferenceError` at import. Response header writes are guarded
19
+ the same way.
20
+ - **`name` option** labels unhandled error logs in both `createLambdaHandler`
21
+ and `createLambdaStreamHandler`.
22
+ - **Explicit status on an empty return** (#539). A controller that calls
23
+ `res.status(code)` and returns nothing responds with that code and an empty
24
+ body, instead of 204 or an "Illegal call to res.end()" warning. Without an
25
+ explicit `res.status()`, an empty return is still 204. `expressStreamHandler`
26
+ flushes headers before the controller runs, so the rule does not apply there.
27
+
28
+ ```typescript
29
+ expressHandler(async (req, res) => {
30
+ res.status(202);
31
+ });
32
+ ```
33
+
34
+ - Tests cover `logBody` and `sensitiveHeaders` on `expressStreamHandler` (#515).
@@ -0,0 +1,20 @@
1
+ ---
2
+ version: 0.3.12
3
+ date: 2026-09-12
4
+ summary: populateIndexKeys removes stale sparse index keys; createMcpServerFromSuite accepts a services allowlist
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **`services` allowlist** (#539). `createMcpServerFromSuite` accepts
10
+ `services?: string[]` to expose a subset of a suite. The new
11
+ `selectServiceFunctions(suite, { services })` export holds the shared
12
+ selection logic.
13
+
14
+ - **Stale sparse index keys are removed** (#533). When a partition key source
15
+ field is missing, `populateIndexKeys` deletes the index key and its `Sk`
16
+ companion instead of preserving whatever the entity carried in. When only a
17
+ sort key source field is missing, it deletes the `Sk` attribute.
18
+ - An index with no partition key value no longer gets an orphan `Sk` attribute
19
+ on a new entity.
20
+ - The input entity is not mutated.
@@ -0,0 +1,12 @@
1
+ ---
2
+ version: 1.2.101
3
+ date: 2026-09-12
4
+ summary: Pulls @jaypie/express 1.2.37 and @jaypie/datadog 1.2.8
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **`@jaypie/express` 1.2.37**: `LambdaRequest` provides `rawHeaders` (#532);
10
+ `createLambdaStreamHandler` runs outside Lambda and honors `name` (#534).
11
+ - **`@jaypie/datadog` 1.2.8**: `hasDatadogEnv` recognizes secret references
12
+ (#513); metric submission rejects with `BadGatewayError` (#516).
@@ -0,0 +1,37 @@
1
+ ---
2
+ version: 0.8.153
3
+ date: 2026-09-12
4
+ summary: SDK-free streamable HTTP handler at @jaypie/mcp/http and a services allowlist for MCP servers
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **`@jaypie/mcp/http`** (#539). `mcpHttpHandler` serves any fabric
10
+ `ServiceSuite` as MCP streamable HTTP without `@modelcontextprotocol/sdk`.
11
+ Stateless JSON-RPC 2.0 on one endpoint: `initialize`, `ping`, `tools/list`,
12
+ `tools/call`, notifications, and batches. The `Accept` header selects a
13
+ buffered JSON body or an SSE stream. Notification-only requests answer 202
14
+ with no body and no response warning; GET and DELETE answer 405. Importing the entry loads no SDK modules.
15
+
16
+ ```typescript
17
+ import { mcpHttpHandler } from "@jaypie/mcp/http";
18
+
19
+ app.all("/mcp", mcpHttpHandler({ services: ["skill", "version"] }));
20
+ export const handler = createLambdaStreamHandler(app);
21
+ ```
22
+
23
+ Deploy behind a `RESPONSE_STREAM` function URL for streamed responses.
24
+ - **`services` allowlist**. `createMcpServer`, `mcpExpressHandler`, and
25
+ `mcpHttpHandler` accept `services?: string[]`. Omitted exposes every service;
26
+ `[]` exposes none; unknown names are ignored.
27
+ - **Documentation** (#535, #537, #538). `EXAMPLE.md` lists the four router tools
28
+ and awaits `mcpExpressHandler`. Skills show `JaypieLambda` with `code` and
29
+ `handler`, drop the claim that `jaypie` re-exports `@jaypie/dynamodb`, document
30
+ sparse index removal, the Lambda adapter `host` fallback and `name` option, and
31
+ every Datadog key variable.
32
+
33
+ ## Dependencies
34
+
35
+ `@jaypie/express`, `@jaypie/logger`, and `express` are optional peer
36
+ dependencies, required only by `@jaypie/mcp/http`. The CLI does not install
37
+ them.
@@ -0,0 +1,11 @@
1
+ ---
2
+ version: 0.8.154
3
+ date: 2026-09-12
4
+ summary: apigateway skill documents the JaypieDistribution zone fallback
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **`apigateway` skill** lists the `JaypieDistribution` zone env fallback as
10
+ `CDK_ENV_API_HOSTED_ZONE` → `CDK_ENV_HOSTED_ZONE`, matching `@jaypie/constructs`
11
+ 1.2.83.
@@ -0,0 +1,15 @@
1
+ ---
2
+ version: 1.2.73
3
+ date: 2026-09-12
4
+ summary: Mock ExpressHandlerOptions and ExpressStreamHandlerOptions derive from @jaypie/express
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **Express mock option types follow the real types** (#515).
10
+ `ExpressHandlerOptions` and `ExpressStreamHandlerOptions` derive from
11
+ `@jaypie/express`, so `chaos`, `fabric`, `logBody`, `name`, `scrub`,
12
+ `secrets`, `sensitiveHeaders`, `unavailable`, `contentType`, and `format` are
13
+ typed. `locals`, `setup`, `teardown`, and `validate` keep their mock shapes.
14
+ - The import is type-only; `./mock` still loads without `@jaypie/express`
15
+ installed.
@@ -209,7 +209,7 @@ Two things to plan for:
209
209
  |---------|------------------|-------------------|
210
210
  | Origin | API Gateway REST | CloudFront → Function URL |
211
211
  | Host env fallback | `CDK_ENV_API_HOST_NAME` → `CDK_ENV_API_SUBDOMAIN`+`CDK_ENV_API_HOSTED_ZONE` | Same, plus falls back to `CDK_ENV_HOSTED_ZONE` |
212
- | Zone env fallback | `CDK_ENV_API_HOSTED_ZONE` | `CDK_ENV_HOSTED_ZONE` |
212
+ | Zone env fallback | `CDK_ENV_API_HOSTED_ZONE` | `CDK_ENV_API_HOSTED_ZONE` → `CDK_ENV_HOSTED_ZONE` |
213
213
  | DNS | A record only | A + AAAA records |
214
214
  | WAF | Not built in | Opt-in (`waf: true` to enable) |
215
215
  | Security headers | Not built in | Enabled by default (`securityHeaders: false` to disable) |
package/skills/aws.md CHANGED
@@ -235,7 +235,8 @@ Lambda functions automatically receive credentials via IAM role. Use CDK to gran
235
235
  import { JaypieLambda } from "@jaypie/constructs";
236
236
 
237
237
  const handler = new JaypieLambda(this, "Handler", {
238
- entry: "src/handler.ts",
238
+ code: "../api/dist",
239
+ handler: "index.handler",
239
240
  });
240
241
 
241
242
  // Grant S3 access
package/skills/cdk.md CHANGED
@@ -23,8 +23,8 @@ Lambda function with Datadog tracing, logging, and error handling:
23
23
  import { JaypieLambda } from "@jaypie/constructs";
24
24
 
25
25
  const handler = new JaypieLambda(this, "ApiHandler", {
26
- entry: "src/handler.ts",
27
- handler: "handler",
26
+ code: "../api/dist",
27
+ handler: "index.handler",
28
28
  environment: {
29
29
  PROJECT_ENV: "production",
30
30
  PROJECT_KEY: "my-api",
@@ -36,7 +36,7 @@ const handler = new JaypieLambda(this, "ApiHandler", {
36
36
 
37
37
  #### Pre-built Code
38
38
 
39
- When using `code` instead of `entry` for pre-built bundles:
39
+ `code` accepts a pre-built bundle directory (or a `lambda.Code`); `handler` is `<file>.<export>` within it:
40
40
 
41
41
  ```typescript
42
42
  new JaypieLambda(this, "Api", {
@@ -222,8 +222,8 @@ import { JaypieDynamoDb, JaypieLambda } from "@jaypie/constructs";
222
222
  const table = new JaypieDynamoDb(this, "myApp");
223
223
 
224
224
  const handler = new JaypieLambda(this, "ApiHandler", {
225
- entry: "src/handler.ts",
226
- handler: "handler",
225
+ code: "../api/dist",
226
+ handler: "index.handler",
227
227
  tables: [table],
228
228
  });
229
229
  ```
package/skills/datadog.md CHANGED
@@ -19,7 +19,8 @@ Enable Datadog tracing in CDK:
19
19
  import { JaypieLambda } from "@jaypie/constructs";
20
20
 
21
21
  const handler = new JaypieLambda(this, "Handler", {
22
- entry: "src/handler.ts",
22
+ code: "../api/dist",
23
+ handler: "index.handler",
23
24
  datadogApiKeyArn: process.env.CDK_ENV_DATADOG_API_KEY_ARN,
24
25
  environment: {
25
26
  DD_ENV: "production",
@@ -142,9 +143,21 @@ reference never has to be written into `process.env` first.
142
143
  |----------|---------|
143
144
  | `DATADOG_API_KEY` / `DD_API_KEY` | Plain API key |
144
145
  | `SECRET_DATADOG_API_KEY` / `DATADOG_API_KEY_ARN` / `DD_API_KEY_SECRET_ARN` | Secrets Manager ARN, resolved with `getSecret` |
145
- | `DATADOG_API_KEY_SECRET` | Secrets Manager reference, resolved with `getEnvSecret` |
146
+ | `DATADOG_API_KEY_SECRET` / `SECRET_DD_API_KEY` / `DD_API_KEY_SECRET` | Secrets Manager reference, resolved with `getEnvSecret` |
146
147
  | `DATADOG_APP_KEY` / `DATADOG_APPLICATION_KEY` / `DD_APP_KEY` / `DD_APPLICATION_KEY` | Application key, required by the query service |
147
148
 
149
+ Each application key name also accepts a `SECRET_<NAME>` or `<NAME>_SECRET`
150
+ reference. Precedence for the API key: an explicit `apiSecret`, then the ARN
151
+ variables, then an explicit `apiKey`, then `DATADOG_API_KEY` and `DD_API_KEY`
152
+ through `getEnvSecret`. The ARN variables stay on `getSecret` because
153
+ `getEnvSecret` reads a secret reference only when `AWS_SESSION_TOKEN` is set.
154
+
155
+ `hasDatadogEnv()` recognizes every one of these API key sources, references
156
+ included, without resolving them. `expressHandler` gates its response metric on
157
+ it, so a key held only in Secrets Manager still reports. On Lambda,
158
+ `JaypieLambda` maps `datadogApiKeyArn`, `DATADOG_API_KEY_ARN`, or `CDK_ENV_DATADOG_API_KEY_ARN` to
159
+ `DD_API_KEY_SECRET_ARN`.
160
+
148
161
  ### LLM Observability primitives
149
162
 
150
163
  Bundler-safe access to the runtime `dd-trace` LLM Obs singleton. `dd-trace` is resolved at runtime via a computed module specifier (never bundled), so on Lambda these reach the layer-initialized singleton — a bundled copy would be a different instance whose buffer never ships.
@@ -5,7 +5,7 @@ related: apikey, aws, cdk, models, vocabulary
5
5
 
6
6
  # DynamoDB Patterns
7
7
 
8
- Jaypie provides `@jaypie/dynamodb` for single-table DynamoDB with entity operations, GSI-based queries, hierarchical scoping, and soft delete. Access through the main `jaypie` package or directly.
8
+ Jaypie provides `@jaypie/dynamodb` for single-table DynamoDB with entity operations, GSI-based queries, hierarchical scoping, and soft delete. Import it directly; the `jaypie` umbrella package does not re-export it.
9
9
 
10
10
  ## Key Design
11
11
 
@@ -41,12 +41,6 @@ import {
41
41
  } from "@jaypie/dynamodb";
42
42
  ```
43
43
 
44
- Or through the main package:
45
-
46
- ```typescript
47
- import { APEX, initClient, createEntity, queryByScope } from "jaypie";
48
- ```
49
-
50
44
  ### Client Initialization
51
45
 
52
46
  Call `initClient()` once at startup. In Lambda, use the handler's `setup` option:
@@ -288,6 +282,8 @@ All GSIs use a composite sort key of `scope#updatedAt` (stored as `{indexName}Sk
288
282
  | `indexModelType` | `{model}#{type}` (sparse) | `indexModelTypeSk` = `{scope}#{updatedAt}` | Type filtering | You need to filter by type |
289
283
  | `indexModelXid` | `{model}#{xid}` (sparse) | `indexModelXidSk` = `{scope}#{updatedAt}` | External ID lookup | You need cross-system ID lookups |
290
284
 
285
+ Sparse indexes are entered and exited by their source field. Every write re-indexes the entity: when a source field (e.g., `category`) is absent, the index key (`indexModelCategory`) and its sort key (`indexModelCategorySk`) are removed from the item, so deleting the field and calling `updateEntity` takes the row out of the index.
286
+
291
287
  ```typescript
292
288
  import { fabricIndex, registerModel } from "@jaypie/fabric";
293
289