@jaypie/mcp 0.1.5 → 0.1.7
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/LICENSE.txt +21 -0
- package/dist/createMcpServer.d.ts +6 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +109 -8
- package/dist/index.js.map +1 -1
- package/dist/mcpExpressHandler.d.ts +4 -3
- package/package.json +8 -3
- package/prompts/Jaypie_Fabricator.md +164 -0
- package/prompts/Jaypie_Init_Monorepo_Project.md +1 -1
- package/prompts/templates/project-monorepo/gitignore +11 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Finlayson Studio, LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export interface CreateMcpServerOptions {
|
|
3
|
+
version?: string;
|
|
4
|
+
verbose?: boolean;
|
|
5
|
+
}
|
|
2
6
|
/**
|
|
3
7
|
* Creates and configures an MCP server instance with Jaypie tools
|
|
4
|
-
* @param
|
|
8
|
+
* @param options - Configuration options (or legacy version string)
|
|
5
9
|
* @returns Configured MCP server instance
|
|
6
10
|
*/
|
|
7
|
-
export declare function createMcpServer(
|
|
11
|
+
export declare function createMcpServer(options?: CreateMcpServerOptions | string): McpServer;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -13,6 +13,19 @@ import { randomUUID } from 'node:crypto';
|
|
|
13
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
14
|
const __dirname = path.dirname(__filename);
|
|
15
15
|
const PROMPTS_PATH = path.join(__dirname, "..", "prompts");
|
|
16
|
+
// Logger utility
|
|
17
|
+
function createLogger(verbose) {
|
|
18
|
+
return {
|
|
19
|
+
info: (message, ...args) => {
|
|
20
|
+
if (verbose) {
|
|
21
|
+
console.error(`[jaypie-mcp] ${message}`, ...args);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
error: (message, ...args) => {
|
|
25
|
+
console.error(`[jaypie-mcp ERROR] ${message}`, ...args);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
16
29
|
async function parseMarkdownFile(filePath) {
|
|
17
30
|
try {
|
|
18
31
|
const content = await fs.readFile(filePath, "utf-8");
|
|
@@ -49,22 +62,33 @@ function formatPromptListItem(prompt) {
|
|
|
49
62
|
}
|
|
50
63
|
/**
|
|
51
64
|
* Creates and configures an MCP server instance with Jaypie tools
|
|
52
|
-
* @param
|
|
65
|
+
* @param options - Configuration options (or legacy version string)
|
|
53
66
|
* @returns Configured MCP server instance
|
|
54
67
|
*/
|
|
55
|
-
function createMcpServer(
|
|
68
|
+
function createMcpServer(options = {}) {
|
|
69
|
+
// Support legacy signature: createMcpServer(version: string)
|
|
70
|
+
const config = typeof options === "string" ? { version: options } : options;
|
|
71
|
+
const { version = "0.0.0", verbose = false } = config;
|
|
72
|
+
const log = createLogger(verbose);
|
|
73
|
+
log.info("Creating MCP server instance");
|
|
74
|
+
log.info(`Prompts directory: ${PROMPTS_PATH}`);
|
|
56
75
|
const server = new McpServer({
|
|
57
76
|
name: "jaypie",
|
|
58
77
|
version,
|
|
59
78
|
}, {
|
|
60
79
|
capabilities: {},
|
|
61
80
|
});
|
|
81
|
+
log.info("Registering tools...");
|
|
62
82
|
server.tool("list_prompts", "Returns a bulleted list of all .md files in the prompts directory with their descriptions and requirements", {}, async () => {
|
|
83
|
+
log.info("Tool called: list_prompts");
|
|
84
|
+
log.info(`Reading directory: ${PROMPTS_PATH}`);
|
|
63
85
|
try {
|
|
64
86
|
const files = await fs.readdir(PROMPTS_PATH);
|
|
65
87
|
const mdFiles = files.filter((file) => file.endsWith(".md"));
|
|
88
|
+
log.info(`Found ${mdFiles.length} .md files`);
|
|
66
89
|
const prompts = await Promise.all(mdFiles.map((file) => parseMarkdownFile(path.join(PROMPTS_PATH, file))));
|
|
67
90
|
const formattedList = prompts.map(formatPromptListItem).join("\n");
|
|
91
|
+
log.info("Successfully listed prompts");
|
|
68
92
|
return {
|
|
69
93
|
content: [
|
|
70
94
|
{
|
|
@@ -75,6 +99,7 @@ function createMcpServer(version = "0.0.0") {
|
|
|
75
99
|
};
|
|
76
100
|
}
|
|
77
101
|
catch (error) {
|
|
102
|
+
log.error("Error listing prompts:", error);
|
|
78
103
|
return {
|
|
79
104
|
content: [
|
|
80
105
|
{
|
|
@@ -85,14 +110,18 @@ function createMcpServer(version = "0.0.0") {
|
|
|
85
110
|
};
|
|
86
111
|
}
|
|
87
112
|
});
|
|
113
|
+
log.info("Registered tool: list_prompts");
|
|
88
114
|
server.tool("read_prompt", "Returns the contents of a specified prompt file", {
|
|
89
115
|
filename: z
|
|
90
116
|
.string()
|
|
91
117
|
.describe("The name of the prompt file to read (e.g., example_prompt.md)"),
|
|
92
118
|
}, async ({ filename }) => {
|
|
119
|
+
log.info(`Tool called: read_prompt (filename: ${filename})`);
|
|
93
120
|
try {
|
|
94
121
|
const filePath = path.join(PROMPTS_PATH, filename);
|
|
122
|
+
log.info(`Reading file: ${filePath}`);
|
|
95
123
|
const content = await fs.readFile(filePath, "utf-8");
|
|
124
|
+
log.info(`Successfully read ${filename} (${content.length} bytes)`);
|
|
96
125
|
return {
|
|
97
126
|
content: [
|
|
98
127
|
{
|
|
@@ -104,6 +133,7 @@ function createMcpServer(version = "0.0.0") {
|
|
|
104
133
|
}
|
|
105
134
|
catch (error) {
|
|
106
135
|
if (error.code === "ENOENT") {
|
|
136
|
+
log.error(`File not found: ${filename}`);
|
|
107
137
|
return {
|
|
108
138
|
content: [
|
|
109
139
|
{
|
|
@@ -113,6 +143,7 @@ function createMcpServer(version = "0.0.0") {
|
|
|
113
143
|
],
|
|
114
144
|
};
|
|
115
145
|
}
|
|
146
|
+
log.error("Error reading prompt file:", error);
|
|
116
147
|
return {
|
|
117
148
|
content: [
|
|
118
149
|
{
|
|
@@ -123,6 +154,8 @@ function createMcpServer(version = "0.0.0") {
|
|
|
123
154
|
};
|
|
124
155
|
}
|
|
125
156
|
});
|
|
157
|
+
log.info("Registered tool: read_prompt");
|
|
158
|
+
log.info("MCP server configuration complete");
|
|
126
159
|
return server;
|
|
127
160
|
}
|
|
128
161
|
|
|
@@ -132,7 +165,7 @@ const DEFAULT_VERSION = "0.0.0";
|
|
|
132
165
|
* Creates an Express handler for the Jaypie MCP server using HTTP transport
|
|
133
166
|
*
|
|
134
167
|
* @param options - Configuration options for the handler
|
|
135
|
-
* @returns Express middleware function
|
|
168
|
+
* @returns Promise that resolves to an Express middleware function
|
|
136
169
|
*
|
|
137
170
|
* @example
|
|
138
171
|
* ```typescript
|
|
@@ -142,7 +175,8 @@ const DEFAULT_VERSION = "0.0.0";
|
|
|
142
175
|
* const app = express();
|
|
143
176
|
* app.use(express.json());
|
|
144
177
|
*
|
|
145
|
-
*
|
|
178
|
+
* // Note: mcpExpressHandler is now async
|
|
179
|
+
* app.use('/mcp', await mcpExpressHandler({
|
|
146
180
|
* version: '1.0.0',
|
|
147
181
|
* enableSessions: true
|
|
148
182
|
* }));
|
|
@@ -152,14 +186,14 @@ const DEFAULT_VERSION = "0.0.0";
|
|
|
152
186
|
* });
|
|
153
187
|
* ```
|
|
154
188
|
*/
|
|
155
|
-
function mcpExpressHandler(options = {}) {
|
|
189
|
+
async function mcpExpressHandler(options = {}) {
|
|
156
190
|
const { version = DEFAULT_VERSION, enableSessions = true, sessionIdGenerator = () => randomUUID(), enableJsonResponse = false, } = options;
|
|
157
191
|
const server = createMcpServer(version);
|
|
158
192
|
const transport = new StreamableHTTPServerTransport({
|
|
159
193
|
sessionIdGenerator: enableSessions ? sessionIdGenerator : undefined,
|
|
160
194
|
enableJsonResponse,
|
|
161
195
|
});
|
|
162
|
-
server.connect(transport);
|
|
196
|
+
await server.connect(transport);
|
|
163
197
|
return async (req, res) => {
|
|
164
198
|
try {
|
|
165
199
|
await transport.handleRequest(req, res, req.body);
|
|
@@ -177,10 +211,74 @@ function mcpExpressHandler(options = {}) {
|
|
|
177
211
|
|
|
178
212
|
// Version will be injected during build
|
|
179
213
|
const version = "0.0.0";
|
|
214
|
+
// Parse command-line arguments
|
|
215
|
+
const args = process.argv.slice(2);
|
|
216
|
+
const verbose = args.includes("--verbose") || args.includes("-v");
|
|
217
|
+
// Logger for verbose mode (uses stderr to not interfere with JSON-RPC on stdout)
|
|
218
|
+
const log = {
|
|
219
|
+
info: (message, ...args) => {
|
|
220
|
+
if (verbose) {
|
|
221
|
+
console.error(`[jaypie-mcp] ${message}`, ...args);
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
error: (message, ...args) => {
|
|
225
|
+
console.error(`[jaypie-mcp ERROR] ${message}`, ...args);
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
let server = null;
|
|
229
|
+
let isShuttingDown = false;
|
|
230
|
+
async function gracefulShutdown(exitCode = 0) {
|
|
231
|
+
if (isShuttingDown) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
isShuttingDown = true;
|
|
235
|
+
log.info("Shutting down gracefully...");
|
|
236
|
+
try {
|
|
237
|
+
if (server) {
|
|
238
|
+
await server.close();
|
|
239
|
+
log.info("Server closed successfully");
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
log.error("Error during shutdown:", error);
|
|
244
|
+
}
|
|
245
|
+
finally {
|
|
246
|
+
process.exit(exitCode);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
180
249
|
async function main() {
|
|
181
|
-
|
|
250
|
+
log.info("Starting Jaypie MCP server...");
|
|
251
|
+
log.info(`Version: ${version}`);
|
|
252
|
+
log.info(`Node version: ${process.version}`);
|
|
253
|
+
log.info(`Verbose mode: ${verbose ? "enabled" : "disabled"}`);
|
|
254
|
+
server = createMcpServer({ version, verbose });
|
|
255
|
+
log.info("MCP server created successfully");
|
|
182
256
|
const transport = new StdioServerTransport();
|
|
183
257
|
await server.connect(transport);
|
|
258
|
+
log.info("Connected to stdio transport");
|
|
259
|
+
log.info("Server is ready to accept requests");
|
|
260
|
+
// Handle process termination signals
|
|
261
|
+
process.on("SIGINT", () => {
|
|
262
|
+
log.info("Received SIGINT signal");
|
|
263
|
+
gracefulShutdown(0);
|
|
264
|
+
});
|
|
265
|
+
process.on("SIGTERM", () => {
|
|
266
|
+
log.info("Received SIGTERM signal");
|
|
267
|
+
gracefulShutdown(0);
|
|
268
|
+
});
|
|
269
|
+
// Handle stdio stream errors (but let transport handle normal stdin end/close)
|
|
270
|
+
process.stdin.on("error", (error) => {
|
|
271
|
+
if (error.message?.includes("EPIPE") || error.message?.includes("EOF")) {
|
|
272
|
+
log.info("stdin closed");
|
|
273
|
+
gracefulShutdown(0);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
process.stdout.on("error", (error) => {
|
|
277
|
+
if (error.message?.includes("EPIPE")) {
|
|
278
|
+
log.info("stdout pipe broken");
|
|
279
|
+
gracefulShutdown(0);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
184
282
|
// Server is running on stdio
|
|
185
283
|
}
|
|
186
284
|
// Only run main() if this module is executed directly (not imported)
|
|
@@ -189,7 +287,10 @@ if (process.argv[1]) {
|
|
|
189
287
|
const realPath = realpathSync(process.argv[1]);
|
|
190
288
|
const realPathAsUrl = pathToFileURL(realPath).href;
|
|
191
289
|
if (import.meta.url === realPathAsUrl) {
|
|
192
|
-
main()
|
|
290
|
+
main().catch((error) => {
|
|
291
|
+
log.error("Fatal error:", error);
|
|
292
|
+
process.exit(1);
|
|
293
|
+
});
|
|
193
294
|
}
|
|
194
295
|
}
|
|
195
296
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/createMcpServer.ts","../src/mcpExpressHandler.ts","../src/index.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport matter from \"gray-matter\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\nconst PROMPTS_PATH = path.join(__dirname, \"..\", \"prompts\");\n\ninterface FrontMatter {\n description?: string;\n include?: string;\n globs?: string;\n}\n\nasync function parseMarkdownFile(filePath: string): Promise<{\n filename: string;\n description?: string;\n include?: string;\n}> {\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n const filename = path.basename(filePath);\n\n if (content.startsWith(\"---\")) {\n const parsed = matter(content);\n const frontMatter = parsed.data as FrontMatter;\n return {\n filename,\n description: frontMatter.description,\n include: frontMatter.include || frontMatter.globs,\n };\n }\n\n return { filename };\n } catch {\n return { filename: path.basename(filePath) };\n }\n}\n\nfunction formatPromptListItem(prompt: {\n filename: string;\n description?: string;\n include?: string;\n}): string {\n const { filename, description, include } = prompt;\n\n if (description && include) {\n return `* ${filename}: ${description} - Required for ${include}`;\n } else if (description) {\n return `* ${filename}: ${description}`;\n } else if (include) {\n return `* ${filename} - Required for ${include}`;\n } else {\n return `* ${filename}`;\n }\n}\n\n/**\n * Creates and configures an MCP server instance with Jaypie tools\n * @param version - The version string for the server\n * @returns Configured MCP server instance\n */\nexport function createMcpServer(version: string = \"0.0.0\"): McpServer {\n const server = new McpServer(\n {\n name: \"jaypie\",\n version,\n },\n {\n capabilities: {},\n },\n );\n\n server.tool(\n \"list_prompts\",\n \"Returns a bulleted list of all .md files in the prompts directory with their descriptions and requirements\",\n {},\n async () => {\n try {\n const files = await fs.readdir(PROMPTS_PATH);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const prompts = await Promise.all(\n mdFiles.map((file) =>\n parseMarkdownFile(path.join(PROMPTS_PATH, file)),\n ),\n );\n\n const formattedList = prompts.map(formatPromptListItem).join(\"\\n\");\n\n return {\n content: [\n {\n type: \"text\" as const,\n text:\n formattedList || \"No .md files found in the prompts directory.\",\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Error listing prompts: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n ],\n };\n }\n },\n );\n\n server.tool(\n \"read_prompt\",\n \"Returns the contents of a specified prompt file\",\n {\n filename: z\n .string()\n .describe(\n \"The name of the prompt file to read (e.g., example_prompt.md)\",\n ),\n },\n async ({ filename }) => {\n try {\n const filePath = path.join(PROMPTS_PATH, filename);\n const content = await fs.readFile(filePath, \"utf-8\");\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: content,\n },\n ],\n };\n } catch (error) {\n if ((error as { code?: string }).code === \"ENOENT\") {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Error: Prompt file \"${filename}\" not found in prompts directory`,\n },\n ],\n };\n }\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Error reading prompt file: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n ],\n };\n }\n },\n );\n\n return server;\n}\n","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 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 * app.use('/mcp', 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 function mcpExpressHandler(\n options: McpExpressHandlerOptions = {},\n): (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 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","#!/usr/bin/env node\nimport { realpathSync } from \"node:fs\";\nimport { pathToFileURL } from \"node:url\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { createMcpServer } from \"./createMcpServer.js\";\n\n// Version will be injected during build\nconst version = \"0.0.0\";\n\nasync function main() {\n const server = createMcpServer(version);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n // Server is running on stdio\n}\n\n// Only run main() if this module is executed directly (not imported)\n// This handles npx and symlinks in node_modules/.bin correctly\nif (process.argv[1]) {\n const realPath = realpathSync(process.argv[1]);\n const realPathAsUrl = pathToFileURL(realPath).href;\n if (import.meta.url === realPathAsUrl) {\n main();\n }\n}\n\nexport { createMcpServer } from \"./createMcpServer.js\";\nexport {\n mcpExpressHandler,\n type McpExpressHandlerOptions,\n} from \"./mcpExpressHandler.js\";\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/createMcpServer.ts","../src/mcpExpressHandler.ts","../src/index.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport matter from \"gray-matter\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\nconst PROMPTS_PATH = path.join(__dirname, \"..\", \"prompts\");\n\nexport interface CreateMcpServerOptions {\n version?: string;\n verbose?: boolean;\n}\n\n// Logger utility\nfunction createLogger(verbose: boolean) {\n return {\n info: (message: string, ...args: unknown[]) => {\n if (verbose) {\n console.error(`[jaypie-mcp] ${message}`, ...args);\n }\n },\n error: (message: string, ...args: unknown[]) => {\n console.error(`[jaypie-mcp ERROR] ${message}`, ...args);\n },\n };\n}\n\ninterface FrontMatter {\n description?: string;\n include?: string;\n globs?: string;\n}\n\nasync function parseMarkdownFile(filePath: string): Promise<{\n filename: string;\n description?: string;\n include?: string;\n}> {\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n const filename = path.basename(filePath);\n\n if (content.startsWith(\"---\")) {\n const parsed = matter(content);\n const frontMatter = parsed.data as FrontMatter;\n return {\n filename,\n description: frontMatter.description,\n include: frontMatter.include || frontMatter.globs,\n };\n }\n\n return { filename };\n } catch {\n return { filename: path.basename(filePath) };\n }\n}\n\nfunction formatPromptListItem(prompt: {\n filename: string;\n description?: string;\n include?: string;\n}): string {\n const { filename, description, include } = prompt;\n\n if (description && include) {\n return `* ${filename}: ${description} - Required for ${include}`;\n } else if (description) {\n return `* ${filename}: ${description}`;\n } else if (include) {\n return `* ${filename} - Required for ${include}`;\n } else {\n return `* ${filename}`;\n }\n}\n\n/**\n * Creates and configures an MCP server instance with Jaypie tools\n * @param options - Configuration options (or legacy version string)\n * @returns Configured MCP server instance\n */\nexport function createMcpServer(\n options: CreateMcpServerOptions | string = {},\n): McpServer {\n // Support legacy signature: createMcpServer(version: string)\n const config: CreateMcpServerOptions =\n typeof options === \"string\" ? { version: options } : options;\n\n const { version = \"0.0.0\", verbose = false } = config;\n\n const log = createLogger(verbose);\n\n log.info(\"Creating MCP server instance\");\n log.info(`Prompts directory: ${PROMPTS_PATH}`);\n\n const server = new McpServer(\n {\n name: \"jaypie\",\n version,\n },\n {\n capabilities: {},\n },\n );\n\n log.info(\"Registering tools...\");\n\n server.tool(\n \"list_prompts\",\n \"Returns a bulleted list of all .md files in the prompts directory with their descriptions and requirements\",\n {},\n async () => {\n log.info(\"Tool called: list_prompts\");\n log.info(`Reading directory: ${PROMPTS_PATH}`);\n\n try {\n const files = await fs.readdir(PROMPTS_PATH);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n log.info(`Found ${mdFiles.length} .md files`);\n\n const prompts = await Promise.all(\n mdFiles.map((file) =>\n parseMarkdownFile(path.join(PROMPTS_PATH, file)),\n ),\n );\n\n const formattedList = prompts.map(formatPromptListItem).join(\"\\n\");\n\n log.info(\"Successfully listed prompts\");\n\n return {\n content: [\n {\n type: \"text\" as const,\n text:\n formattedList || \"No .md files found in the prompts directory.\",\n },\n ],\n };\n } catch (error) {\n log.error(\"Error listing prompts:\", error);\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Error listing prompts: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n ],\n };\n }\n },\n );\n\n log.info(\"Registered tool: list_prompts\");\n\n server.tool(\n \"read_prompt\",\n \"Returns the contents of a specified prompt file\",\n {\n filename: z\n .string()\n .describe(\n \"The name of the prompt file to read (e.g., example_prompt.md)\",\n ),\n },\n async ({ filename }) => {\n log.info(`Tool called: read_prompt (filename: ${filename})`);\n\n try {\n const filePath = path.join(PROMPTS_PATH, filename);\n\n log.info(`Reading file: ${filePath}`);\n\n const content = await fs.readFile(filePath, \"utf-8\");\n\n log.info(`Successfully read ${filename} (${content.length} bytes)`);\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: content,\n },\n ],\n };\n } catch (error) {\n if ((error as { code?: string }).code === \"ENOENT\") {\n log.error(`File not found: ${filename}`);\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Error: Prompt file \"${filename}\" not found in prompts directory`,\n },\n ],\n };\n }\n\n log.error(\"Error reading prompt file:\", error);\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Error reading prompt file: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n ],\n };\n }\n },\n );\n\n log.info(\"Registered tool: read_prompt\");\n log.info(\"MCP server configuration complete\");\n\n return server;\n}\n","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","#!/usr/bin/env node\nimport { realpathSync } from \"node:fs\";\nimport { pathToFileURL } from \"node:url\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { createMcpServer } from \"./createMcpServer.js\";\n\n// Version will be injected during build\nconst version = \"0.0.0\";\n\n// Parse command-line arguments\nconst args = process.argv.slice(2);\nconst verbose = args.includes(\"--verbose\") || args.includes(\"-v\");\n\n// Logger for verbose mode (uses stderr to not interfere with JSON-RPC on stdout)\nconst log = {\n info: (message: string, ...args: unknown[]) => {\n if (verbose) {\n console.error(`[jaypie-mcp] ${message}`, ...args);\n }\n },\n error: (message: string, ...args: unknown[]) => {\n console.error(`[jaypie-mcp ERROR] ${message}`, ...args);\n },\n};\n\nlet server: McpServer | null = null;\nlet isShuttingDown = false;\n\nasync function gracefulShutdown(exitCode = 0) {\n if (isShuttingDown) {\n return;\n }\n isShuttingDown = true;\n\n log.info(\"Shutting down gracefully...\");\n\n try {\n if (server) {\n await server.close();\n log.info(\"Server closed successfully\");\n }\n } catch (error) {\n log.error(\"Error during shutdown:\", error);\n } finally {\n process.exit(exitCode);\n }\n}\n\nasync function main() {\n log.info(\"Starting Jaypie MCP server...\");\n log.info(`Version: ${version}`);\n log.info(`Node version: ${process.version}`);\n log.info(`Verbose mode: ${verbose ? \"enabled\" : \"disabled\"}`);\n\n server = createMcpServer({ version, verbose });\n\n log.info(\"MCP server created successfully\");\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n log.info(\"Connected to stdio transport\");\n log.info(\"Server is ready to accept requests\");\n\n // Handle process termination signals\n process.on(\"SIGINT\", () => {\n log.info(\"Received SIGINT signal\");\n gracefulShutdown(0);\n });\n process.on(\"SIGTERM\", () => {\n log.info(\"Received SIGTERM signal\");\n gracefulShutdown(0);\n });\n\n // Handle stdio stream errors (but let transport handle normal stdin end/close)\n process.stdin.on(\"error\", (error) => {\n if (error.message?.includes(\"EPIPE\") || error.message?.includes(\"EOF\")) {\n log.info(\"stdin closed\");\n gracefulShutdown(0);\n }\n });\n\n process.stdout.on(\"error\", (error) => {\n if (error.message?.includes(\"EPIPE\")) {\n log.info(\"stdout pipe broken\");\n gracefulShutdown(0);\n }\n });\n\n // Server is running on stdio\n}\n\n// Only run main() if this module is executed directly (not imported)\n// This handles npx and symlinks in node_modules/.bin correctly\nif (process.argv[1]) {\n const realPath = realpathSync(process.argv[1]);\n const realPathAsUrl = pathToFileURL(realPath).href;\n if (import.meta.url === realPathAsUrl) {\n main().catch((error) => {\n log.error(\"Fatal error:\", error);\n process.exit(1);\n });\n }\n}\n\nexport { createMcpServer } from \"./createMcpServer.js\";\nexport type { CreateMcpServerOptions } from \"./createMcpServer.js\";\nexport {\n mcpExpressHandler,\n type McpExpressHandlerOptions,\n} from \"./mcpExpressHandler.js\";\n"],"names":[],"mappings":";;;;;;;;;;;;AAOA,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;AACjD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;AAC1C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC;AAO1D;AACA,SAAS,YAAY,CAAC,OAAgB,EAAA;IACpC,OAAO;AACL,QAAA,IAAI,EAAE,CAAC,OAAe,EAAE,GAAG,IAAe,KAAI;YAC5C,IAAI,OAAO,EAAE;gBACX,OAAO,CAAC,KAAK,CAAC,CAAA,aAAA,EAAgB,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC;YACnD;QACF,CAAC;AACD,QAAA,KAAK,EAAE,CAAC,OAAe,EAAE,GAAG,IAAe,KAAI;YAC7C,OAAO,CAAC,KAAK,CAAC,CAAA,mBAAA,EAAsB,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC;QACzD,CAAC;KACF;AACH;AAQA,eAAe,iBAAiB,CAAC,QAAgB,EAAA;AAK/C,IAAA,IAAI;QACF,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;QACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;AAExC,QAAA,IAAI,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE;AAC7B,YAAA,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;AAC9B,YAAA,MAAM,WAAW,GAAG,MAAM,CAAC,IAAmB;YAC9C,OAAO;gBACL,QAAQ;gBACR,WAAW,EAAE,WAAW,CAAC,WAAW;AACpC,gBAAA,OAAO,EAAE,WAAW,CAAC,OAAO,IAAI,WAAW,CAAC,KAAK;aAClD;QACH;QAEA,OAAO,EAAE,QAAQ,EAAE;IACrB;AAAE,IAAA,MAAM;QACN,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;IAC9C;AACF;AAEA,SAAS,oBAAoB,CAAC,MAI7B,EAAA;IACC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,MAAM;AAEjD,IAAA,IAAI,WAAW,IAAI,OAAO,EAAE;AAC1B,QAAA,OAAO,KAAK,QAAQ,CAAA,EAAA,EAAK,WAAW,CAAA,gBAAA,EAAmB,OAAO,EAAE;IAClE;SAAO,IAAI,WAAW,EAAE;AACtB,QAAA,OAAO,CAAA,EAAA,EAAK,QAAQ,CAAA,EAAA,EAAK,WAAW,EAAE;IACxC;SAAO,IAAI,OAAO,EAAE;AAClB,QAAA,OAAO,CAAA,EAAA,EAAK,QAAQ,CAAA,gBAAA,EAAmB,OAAO,EAAE;IAClD;SAAO;QACL,OAAO,CAAA,EAAA,EAAK,QAAQ,CAAA,CAAE;IACxB;AACF;AAEA;;;;AAIG;AACG,SAAU,eAAe,CAC7B,OAAA,GAA2C,EAAE,EAAA;;AAG7C,IAAA,MAAM,MAAM,GACV,OAAO,OAAO,KAAK,QAAQ,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO;IAE9D,MAAM,EAAE,OAAO,GAAG,OAAO,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,MAAM;AAErD,IAAA,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC;AAEjC,IAAA,GAAG,CAAC,IAAI,CAAC,8BAA8B,CAAC;AACxC,IAAA,GAAG,CAAC,IAAI,CAAC,sBAAsB,YAAY,CAAA,CAAE,CAAC;AAE9C,IAAA,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B;AACE,QAAA,IAAI,EAAE,QAAQ;QACd,OAAO;KACR,EACD;AACE,QAAA,YAAY,EAAE,EAAE;AACjB,KAAA,CACF;AAED,IAAA,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC;IAEhC,MAAM,CAAC,IAAI,CACT,cAAc,EACd,4GAA4G,EAC5G,EAAE,EACF,YAAW;AACT,QAAA,GAAG,CAAC,IAAI,CAAC,2BAA2B,CAAC;AACrC,QAAA,GAAG,CAAC,IAAI,CAAC,sBAAsB,YAAY,CAAA,CAAE,CAAC;AAE9C,QAAA,IAAI;YACF,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC;AAC5C,YAAA,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAE5D,GAAG,CAAC,IAAI,CAAC,CAAA,MAAA,EAAS,OAAO,CAAC,MAAM,CAAA,UAAA,CAAY,CAAC;AAE7C,YAAA,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,KACf,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,CACjD,CACF;AAED,YAAA,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;AAElE,YAAA,GAAG,CAAC,IAAI,CAAC,6BAA6B,CAAC;YAEvC,OAAO;AACL,gBAAA,OAAO,EAAE;AACP,oBAAA;AACE,wBAAA,IAAI,EAAE,MAAe;wBACrB,IAAI,EACF,aAAa,IAAI,8CAA8C;AAClE,qBAAA;AACF,iBAAA;aACF;QACH;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,GAAG,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC;YAE1C,OAAO;AACL,gBAAA,OAAO,EAAE;AACP,oBAAA;AACE,wBAAA,IAAI,EAAE,MAAe;AACrB,wBAAA,IAAI,EAAE,CAAA,uBAAA,EAA0B,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,eAAe,CAAA,CAAE;AAC3F,qBAAA;AACF,iBAAA;aACF;QACH;AACF,IAAA,CAAC,CACF;AAED,IAAA,GAAG,CAAC,IAAI,CAAC,+BAA+B,CAAC;AAEzC,IAAA,MAAM,CAAC,IAAI,CACT,aAAa,EACb,iDAAiD,EACjD;AACE,QAAA,QAAQ,EAAE;AACP,aAAA,MAAM;aACN,QAAQ,CACP,+DAA+D,CAChE;AACJ,KAAA,EACD,OAAO,EAAE,QAAQ,EAAE,KAAI;AACrB,QAAA,GAAG,CAAC,IAAI,CAAC,uCAAuC,QAAQ,CAAA,CAAA,CAAG,CAAC;AAE5D,QAAA,IAAI;YACF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC;AAElD,YAAA,GAAG,CAAC,IAAI,CAAC,iBAAiB,QAAQ,CAAA,CAAE,CAAC;YAErC,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;YAEpD,GAAG,CAAC,IAAI,CAAC,CAAA,kBAAA,EAAqB,QAAQ,CAAA,EAAA,EAAK,OAAO,CAAC,MAAM,CAAA,OAAA,CAAS,CAAC;YAEnE,OAAO;AACL,gBAAA,OAAO,EAAE;AACP,oBAAA;AACE,wBAAA,IAAI,EAAE,MAAe;AACrB,wBAAA,IAAI,EAAE,OAAO;AACd,qBAAA;AACF,iBAAA;aACF;QACH;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,IAAK,KAA2B,CAAC,IAAI,KAAK,QAAQ,EAAE;AAClD,gBAAA,GAAG,CAAC,KAAK,CAAC,mBAAmB,QAAQ,CAAA,CAAE,CAAC;gBAExC,OAAO;AACL,oBAAA,OAAO,EAAE;AACP,wBAAA;AACE,4BAAA,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,CAAA,oBAAA,EAAuB,QAAQ,CAAA,gCAAA,CAAkC;AACxE,yBAAA;AACF,qBAAA;iBACF;YACH;AAEA,YAAA,GAAG,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC;YAE9C,OAAO;AACL,gBAAA,OAAO,EAAE;AACP,oBAAA;AACE,wBAAA,IAAI,EAAE,MAAe;AACrB,wBAAA,IAAI,EAAE,CAAA,2BAAA,EAA8B,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,eAAe,CAAA,CAAE;AAC/F,qBAAA;AACF,iBAAA;aACF;QACH;AACF,IAAA,CAAC,CACF;AAED,IAAA,GAAG,CAAC,IAAI,CAAC,8BAA8B,CAAC;AACxC,IAAA,GAAG,CAAC,IAAI,CAAC,mCAAmC,CAAC;AAE7C,IAAA,OAAO,MAAM;AACf;;ACzNA;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;;ACjFA;AACA,MAAM,OAAO,GAAG,OAAO;AAEvB;AACA,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AAClC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;AAEjE;AACA,MAAM,GAAG,GAAG;AACV,IAAA,IAAI,EAAE,CAAC,OAAe,EAAE,GAAG,IAAe,KAAI;QAC5C,IAAI,OAAO,EAAE;YACX,OAAO,CAAC,KAAK,CAAC,CAAA,aAAA,EAAgB,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC;QACnD;IACF,CAAC;AACD,IAAA,KAAK,EAAE,CAAC,OAAe,EAAE,GAAG,IAAe,KAAI;QAC7C,OAAO,CAAC,KAAK,CAAC,CAAA,mBAAA,EAAsB,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC;IACzD,CAAC;CACF;AAED,IAAI,MAAM,GAAqB,IAAI;AACnC,IAAI,cAAc,GAAG,KAAK;AAE1B,eAAe,gBAAgB,CAAC,QAAQ,GAAG,CAAC,EAAA;IAC1C,IAAI,cAAc,EAAE;QAClB;IACF;IACA,cAAc,GAAG,IAAI;AAErB,IAAA,GAAG,CAAC,IAAI,CAAC,6BAA6B,CAAC;AAEvC,IAAA,IAAI;QACF,IAAI,MAAM,EAAE;AACV,YAAA,MAAM,MAAM,CAAC,KAAK,EAAE;AACpB,YAAA,GAAG,CAAC,IAAI,CAAC,4BAA4B,CAAC;QACxC;IACF;IAAE,OAAO,KAAK,EAAE;AACd,QAAA,GAAG,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC;IAC5C;YAAU;AACR,QAAA,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;IACxB;AACF;AAEA,eAAe,IAAI,GAAA;AACjB,IAAA,GAAG,CAAC,IAAI,CAAC,+BAA+B,CAAC;AACzC,IAAA,GAAG,CAAC,IAAI,CAAC,YAAY,OAAO,CAAA,CAAE,CAAC;IAC/B,GAAG,CAAC,IAAI,CAAC,CAAA,cAAA,EAAiB,OAAO,CAAC,OAAO,CAAA,CAAE,CAAC;AAC5C,IAAA,GAAG,CAAC,IAAI,CAAC,CAAA,cAAA,EAAiB,OAAO,GAAG,SAAS,GAAG,UAAU,CAAA,CAAE,CAAC;IAE7D,MAAM,GAAG,eAAe,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAE9C,IAAA,GAAG,CAAC,IAAI,CAAC,iCAAiC,CAAC;AAE3C,IAAA,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE;AAC5C,IAAA,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;AAE/B,IAAA,GAAG,CAAC,IAAI,CAAC,8BAA8B,CAAC;AACxC,IAAA,GAAG,CAAC,IAAI,CAAC,oCAAoC,CAAC;;AAG9C,IAAA,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAK;AACxB,QAAA,GAAG,CAAC,IAAI,CAAC,wBAAwB,CAAC;QAClC,gBAAgB,CAAC,CAAC,CAAC;AACrB,IAAA,CAAC,CAAC;AACF,IAAA,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAK;AACzB,QAAA,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC;QACnC,gBAAgB,CAAC,CAAC,CAAC;AACrB,IAAA,CAAC,CAAC;;IAGF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,KAAI;AAClC,QAAA,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE;AACtE,YAAA,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC;YACxB,gBAAgB,CAAC,CAAC,CAAC;QACrB;AACF,IAAA,CAAC,CAAC;IAEF,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,KAAI;QACnC,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE;AACpC,YAAA,GAAG,CAAC,IAAI,CAAC,oBAAoB,CAAC;YAC9B,gBAAgB,CAAC,CAAC,CAAC;QACrB;AACF,IAAA,CAAC,CAAC;;AAGJ;AAEA;AACA;AACA,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;IACnB,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,aAAa,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI;IAClD,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,aAAa,EAAE;AACrC,QAAA,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,KAAI;AACrB,YAAA,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC;AAChC,YAAA,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AACjB,QAAA,CAAC,CAAC;IACJ;AACF;;;;"}
|
|
@@ -26,7 +26,7 @@ export interface McpExpressHandlerOptions {
|
|
|
26
26
|
* Creates an Express handler for the Jaypie MCP server using HTTP transport
|
|
27
27
|
*
|
|
28
28
|
* @param options - Configuration options for the handler
|
|
29
|
-
* @returns Express middleware function
|
|
29
|
+
* @returns Promise that resolves to an Express middleware function
|
|
30
30
|
*
|
|
31
31
|
* @example
|
|
32
32
|
* ```typescript
|
|
@@ -36,7 +36,8 @@ export interface McpExpressHandlerOptions {
|
|
|
36
36
|
* const app = express();
|
|
37
37
|
* app.use(express.json());
|
|
38
38
|
*
|
|
39
|
-
*
|
|
39
|
+
* // Note: mcpExpressHandler is now async
|
|
40
|
+
* app.use('/mcp', await mcpExpressHandler({
|
|
40
41
|
* version: '1.0.0',
|
|
41
42
|
* enableSessions: true
|
|
42
43
|
* }));
|
|
@@ -46,4 +47,4 @@ export interface McpExpressHandlerOptions {
|
|
|
46
47
|
* });
|
|
47
48
|
* ```
|
|
48
49
|
*/
|
|
49
|
-
export declare function mcpExpressHandler(options?: McpExpressHandlerOptions): (req: Request, res: Response) => Promise<void
|
|
50
|
+
export declare function mcpExpressHandler(options?: McpExpressHandlerOptions): Promise<(req: Request, res: Response) => Promise<void>>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jaypie/mcp",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Jaypie MCP
|
|
3
|
+
"version": "0.1.7",
|
|
4
|
+
"description": "Jaypie MCP",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/finlaysonstudio/jaypie"
|
|
8
|
+
},
|
|
5
9
|
"license": "MIT",
|
|
6
10
|
"author": "Finlayson Studio",
|
|
7
11
|
"type": "module",
|
|
@@ -41,5 +45,6 @@
|
|
|
41
45
|
},
|
|
42
46
|
"publishConfig": {
|
|
43
47
|
"access": "public"
|
|
44
|
-
}
|
|
48
|
+
},
|
|
49
|
+
"gitHead": "14683b303359f95dc63e58998959fddae3c90e97"
|
|
45
50
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: coding style resource, helpful when stuck on lint errors
|
|
3
|
+
globs: packages/fabricator/**
|
|
4
|
+
---
|
|
5
|
+
# Brief Guide to @jaypie/fabricator
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
`@jaypie/fabricator` is a test data generation library built on top of `@faker-js/faker` that provides deterministic, seeded data generation with additional utilities for randomness and complex data patterns.
|
|
10
|
+
|
|
11
|
+
## Core Concepts
|
|
12
|
+
|
|
13
|
+
### 1. The Fabricator Class
|
|
14
|
+
|
|
15
|
+
The `Fabricator` class wraps faker.js and provides:
|
|
16
|
+
- **Deterministic seeding**: Pass a string, number, or UUID to get reproducible test data
|
|
17
|
+
- **Direct faker access**: All faker modules are proxied for convenience
|
|
18
|
+
- **Enhanced random number generation**: Built-in `random()` function with advanced options
|
|
19
|
+
- **Custom generators**: Specialized methods like `words()` and `generate.person()`
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { fabricator } from "@jaypie/fabricator";
|
|
23
|
+
|
|
24
|
+
// Create unseeded (random) fabricator
|
|
25
|
+
const fab = fabricator();
|
|
26
|
+
|
|
27
|
+
// Create seeded (deterministic) fabricator
|
|
28
|
+
const seededFab = fabricator("my-seed");
|
|
29
|
+
const uuidFab = fabricator("550e8400-e29b-41d4-a716-446655440000");
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 2. Deterministic Seeding
|
|
33
|
+
|
|
34
|
+
Any string or number can be used as a seed. The same seed always produces the same output:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
const fab1 = fabricator("test-seed");
|
|
38
|
+
const fab2 = fabricator("test-seed");
|
|
39
|
+
|
|
40
|
+
fab1.faker.person.firstName(); // "Jordan"
|
|
41
|
+
fab2.faker.person.firstName(); // "Jordan" (same!)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**UUID Seeding**: UUIDs are converted to numeric arrays using `uuidToBytes` for efficient seeding.
|
|
45
|
+
|
|
46
|
+
### 3. Random Number Generation
|
|
47
|
+
|
|
48
|
+
The `random()` function provides advanced randomness with multiple distribution types:
|
|
49
|
+
|
|
50
|
+
**Basic Usage:**
|
|
51
|
+
```typescript
|
|
52
|
+
fab.random(); // 0-1 float
|
|
53
|
+
fab.random({ min: 1, max: 10 }); // 1-10 float
|
|
54
|
+
fab.random({ min: 1, max: 10, integer: true }); // 1-10 integer
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Advanced Options:**
|
|
58
|
+
- `min/max`: Bounds (defaults: 0-1)
|
|
59
|
+
- `integer`: Return integers (default: false)
|
|
60
|
+
- `mean/stddev`: Normal distribution
|
|
61
|
+
- `precision`: Decimal places
|
|
62
|
+
- `currency`: Shorthand for precision: 2
|
|
63
|
+
- `start`: Add offset to result
|
|
64
|
+
- `seed`: Override the fabricator's seed for this call only
|
|
65
|
+
|
|
66
|
+
**Smart Defaults:**
|
|
67
|
+
- If only `min` is set, `max` defaults to `min * 2`
|
|
68
|
+
- Normal distribution with `mean/stddev` can optionally be clamped by `min/max`
|
|
69
|
+
|
|
70
|
+
### 4. Faker Integration
|
|
71
|
+
|
|
72
|
+
All faker modules are directly accessible through getters:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
fab.person.firstName();
|
|
76
|
+
fab.internet.email();
|
|
77
|
+
fab.location.city();
|
|
78
|
+
fab.company.name();
|
|
79
|
+
// ... all faker modules available
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 5. Custom Methods
|
|
83
|
+
|
|
84
|
+
#### `words()`: Two-Word Combinations
|
|
85
|
+
Generates one of three patterns randomly:
|
|
86
|
+
- adjective + noun
|
|
87
|
+
- adjective + verb
|
|
88
|
+
- noun + verb
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
fab.words(); // "happy dog" or "quick run" or "mountain climb"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### `generate.person(id?)`: Complex Person Generator
|
|
95
|
+
Creates realistic person data with probabilistic variations:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const person = fab.generate.person();
|
|
99
|
+
// {
|
|
100
|
+
// id: "uuid",
|
|
101
|
+
// firstName: "Jordan",
|
|
102
|
+
// middleName: "Alex" | undefined,
|
|
103
|
+
// lastName: "Smith" | "Smith-Jones",
|
|
104
|
+
// fullName: "Jordan Smith" | "Jordan Alex Smith"
|
|
105
|
+
// }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Probabilistic Rules** (using `CHANCE` constants):
|
|
109
|
+
- **firstName**: 2.1% chance (RARE) to be a lastName instead
|
|
110
|
+
- **middleName**:
|
|
111
|
+
- 14.6% chance (UNCOMMON) to be missing
|
|
112
|
+
- 2.1% chance (RARE) to be a lastName
|
|
113
|
+
- 0.307% chance (EPIC) to have two middle names
|
|
114
|
+
- **lastName**: 2.1% chance (RARE) to be hyphenated
|
|
115
|
+
- **fullName**: 14.6% chance (UNCOMMON) to include middle name
|
|
116
|
+
|
|
117
|
+
**Independent Seeding**: Pass an optional UUID to get deterministic person data that's independent of the parent fabricator's seed:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const person1 = fab1.generate.person("same-uuid");
|
|
121
|
+
const person2 = fab2.generate.person("same-uuid");
|
|
122
|
+
// person1 === person2, regardless of fab1/fab2 seeds
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 6. Probability Constants
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
export const CHANCE = {
|
|
129
|
+
UNCOMMON: 0.146, // ~14.6%
|
|
130
|
+
RARE: 0.021, // ~2.1%
|
|
131
|
+
EPIC: 0.00307, // ~0.3%
|
|
132
|
+
LEGENDARY: 0.000441 // ~0.04%
|
|
133
|
+
};
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Use these for consistent probability calculations across your test data.
|
|
137
|
+
|
|
138
|
+
## Architecture
|
|
139
|
+
|
|
140
|
+
### Internal Utilities
|
|
141
|
+
|
|
142
|
+
- **`numericSeed()`**: Converts strings to numeric seeds using cyrb53 hash
|
|
143
|
+
- **`uuidToBytes()`**: Converts UUIDs to byte arrays for seeding
|
|
144
|
+
- **`uuidToNumber()`**: Converts UUIDs to single numbers (fallback)
|
|
145
|
+
- **`numericSeedArray()`**: Smart converter that detects UUIDs and uses appropriate conversion
|
|
146
|
+
|
|
147
|
+
## Key Design Patterns
|
|
148
|
+
|
|
149
|
+
1. **Subfaker Pattern**: Complex generators create independent seeded faker instances to ensure deterministic output regardless of parent state
|
|
150
|
+
2. **Probabilistic Variations**: Use float rolls with CHANCE constants for realistic data variety
|
|
151
|
+
3. **Proxy Access**: All faker modules exposed through getters for ergonomic API
|
|
152
|
+
4. **Seed Flexibility**: Accepts strings, numbers, or UUIDs as seeds
|
|
153
|
+
|
|
154
|
+
## Usage in @entpayco/fabricator
|
|
155
|
+
|
|
156
|
+
Your wrapper package should:
|
|
157
|
+
- Extend or compose `Fabricator` from `@jaypie/fabricator`
|
|
158
|
+
- Add domain-specific generators (payments, accounts, transactions, etc.)
|
|
159
|
+
- Follow the same patterns: seeding, probabilistic variations, subfaker for complex objects
|
|
160
|
+
- Maintain the same function signature standards (single param, config object, or optional config)
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
Ready to build domain-specific test data generators on this foundation!
|
|
@@ -35,7 +35,7 @@ npm install --save-dev @jaypie/eslint @jaypie/testkit eslint rimraf sort-package
|
|
|
35
35
|
## Context
|
|
36
36
|
|
|
37
37
|
prompts/Jaypie_Ideal_Project_Structure.md
|
|
38
|
-
prompts/templates/project-monorepo
|
|
38
|
+
prompts/templates/project-monorepo/gitignore (rename to .gitignore when copying)
|
|
39
39
|
prompts/templates/project-monorepo/.vscode/settings.json
|
|
40
40
|
prompts/templates/project-monorepo/eslint.config.mjs
|
|
41
41
|
prompts/templates/project-monorepo/package.json
|