@agimon-ai/imagine-mcp 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/{commands-Bda9LPMQ.cjs → commands-CZZNZpPm.cjs} +2 -2
- package/dist/commands-CZZNZpPm.cjs.map +1 -0
- package/dist/{commands-c927CRZc.mjs → commands-NFskYAk7.mjs} +2 -2
- package/dist/commands-NFskYAk7.mjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{stdio-CuIrQe3m.mjs → stdio-CQi19ID4.mjs} +2 -2
- package/dist/stdio-CQi19ID4.mjs.map +1 -0
- package/dist/{stdio-qsNJYZZf.cjs → stdio-DSIKRtTS.cjs} +2 -2
- package/dist/stdio-DSIKRtTS.cjs.map +1 -0
- package/package.json +1 -1
- package/dist/commands-Bda9LPMQ.cjs.map +0 -1
- package/dist/commands-c927CRZc.mjs.map +0 -1
- package/dist/stdio-CuIrQe3m.mjs.map +0 -1
- package/dist/stdio-qsNJYZZf.cjs.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const e=require(`./stdio-
|
|
2
|
+
const e=require(`./stdio-DSIKRtTS.cjs`);let t=require(`commander`);const n={version:`0.1.0`},r=[{name:`mcp-serve`,description:`Start MCP server with specified transport`,loader:()=>Promise.resolve().then(()=>require(`./commands-CZZNZpPm.cjs`)).then(e=>e.mcpServeCommand)}];var i=class extends Error{code;recovery;constructor(e,t=`CLI_EXECUTION_FAILED`,n){super(e,n),this.name=`CLIExecutionError`,this.code=t,this.recovery=n?.recovery}},a=class extends Error{code=`COMMAND_LOAD_ERROR`;recovery;commandName;constructor(e,t,n){super(`Failed to load command '${e}': ${t}`,n),this.name=`CommandLoadError`,this.commandName=e,this.recovery=`Check that the command module exists and exports the expected command. Run with --help to see available commands.`}};function o(e){let n=new t.Command(e.name).description(e.description).allowUnknownOption(!0).allowExcessArguments(!0);return n.action(async()=>{let n;try{n=await e.loader()}catch(t){throw t instanceof a?t:new a(e.name,t instanceof Error?t.message:String(t),{cause:t})}let r=process.argv.slice(2);await new t.Command().addCommand(n).parseAsync([`node`,`imagine-mcp`,...r])}),n}async function s(){let e=process.argv[2]??`unknown`;try{let e=new t.Command;e.name(`imagine`).description(`MCP server for AI image generation and manipulation`).version(n.version);for(let t of r)e.addCommand(o(t));await e.parseAsync(process.argv)}catch(t){if(t instanceof i)process.stderr.write(`Error [${t.code}] ${JSON.stringify({command:e,recovery:t.recovery,message:t.message,cause:t.cause instanceof Error?t.cause.message:t.cause})}\n`),t.recovery&&process.stderr.write(`Recovery: ${t.recovery}\n`),t.cause&&process.stderr.write(`Caused by: ${t.cause instanceof Error?t.cause.message:String(t.cause)}\n`);else if(t instanceof a)process.stderr.write(`Error [${t.code}] ${JSON.stringify({command:e,recovery:t.recovery,message:t.message})}\n`),t.recovery&&process.stderr.write(`Recovery: ${t.recovery}\n`),t.cause&&process.stderr.write(`Caused by: ${t.cause instanceof Error?t.cause.message:String(t.cause)}\n`);else{let n=t.code??`CLI_EXECUTION_FAILED`;process.stderr.write(`Error [${n}] ${JSON.stringify({command:e,message:t instanceof Error?t.message:String(t)})}\n`),process.stderr.write(`Recovery: Run with --help for usage information.
|
|
3
3
|
`),t instanceof Error&&t.cause&&process.stderr.write(`Caused by: ${t.cause instanceof Error?t.cause.message:String(t.cause)}\n`)}process.exit(1)}}s(),exports.CLIExecutionError=i,exports.CommandLoadError=a,exports.commandDefinitions=r,exports.createLazyCommand=o;
|
|
4
4
|
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as e}from"commander";const t={version:`0.1.0`},n=[{name:`mcp-serve`,description:`Start MCP server with specified transport`,loader:()=>import(`./commands-
|
|
2
|
+
import{Command as e}from"commander";const t={version:`0.1.0`},n=[{name:`mcp-serve`,description:`Start MCP server with specified transport`,loader:()=>import(`./commands-NFskYAk7.mjs`).then(e=>e.mcpServeCommand)}];var r=class extends Error{code;recovery;constructor(e,t=`CLI_EXECUTION_FAILED`,n){super(e,n),this.name=`CLIExecutionError`,this.code=t,this.recovery=n?.recovery}},i=class extends Error{code=`COMMAND_LOAD_ERROR`;recovery;commandName;constructor(e,t,n){super(`Failed to load command '${e}': ${t}`,n),this.name=`CommandLoadError`,this.commandName=e,this.recovery=`Check that the command module exists and exports the expected command. Run with --help to see available commands.`}};function a(t){let n=new e(t.name).description(t.description).allowUnknownOption(!0).allowExcessArguments(!0);return n.action(async()=>{let n;try{n=await t.loader()}catch(e){throw e instanceof i?e:new i(t.name,e instanceof Error?e.message:String(e),{cause:e})}let r=process.argv.slice(2);await new e().addCommand(n).parseAsync([`node`,`imagine-mcp`,...r])}),n}async function o(){let o=process.argv[2]??`unknown`;try{let r=new e;r.name(`imagine`).description(`MCP server for AI image generation and manipulation`).version(t.version);for(let e of n)r.addCommand(a(e));await r.parseAsync(process.argv)}catch(e){if(e instanceof r)process.stderr.write(`Error [${e.code}] ${JSON.stringify({command:o,recovery:e.recovery,message:e.message,cause:e.cause instanceof Error?e.cause.message:e.cause})}\n`),e.recovery&&process.stderr.write(`Recovery: ${e.recovery}\n`),e.cause&&process.stderr.write(`Caused by: ${e.cause instanceof Error?e.cause.message:String(e.cause)}\n`);else if(e instanceof i)process.stderr.write(`Error [${e.code}] ${JSON.stringify({command:o,recovery:e.recovery,message:e.message})}\n`),e.recovery&&process.stderr.write(`Recovery: ${e.recovery}\n`),e.cause&&process.stderr.write(`Caused by: ${e.cause instanceof Error?e.cause.message:String(e.cause)}\n`);else{let t=e.code??`CLI_EXECUTION_FAILED`;process.stderr.write(`Error [${t}] ${JSON.stringify({command:o,message:e instanceof Error?e.message:String(e)})}\n`),process.stderr.write(`Recovery: Run with --help for usage information.
|
|
3
3
|
`),e instanceof Error&&e.cause&&process.stderr.write(`Caused by: ${e.cause instanceof Error?e.cause.message:String(e.cause)}\n`)}process.exit(1)}}o();export{r as CLIExecutionError,i as CommandLoadError,n as commandDefinitions,a as createLazyCommand};
|
|
4
4
|
//# sourceMappingURL=cli.mjs.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`./stdio-
|
|
2
|
-
//# sourceMappingURL=commands-
|
|
1
|
+
const e=require(`./stdio-DSIKRtTS.cjs`);let t=require(`commander`),n=function(e){return e.STDIO=`stdio`,e.HTTP=`http`,e.SSE=`sse`,e}({});async function r(e){await e.start();let t=async t=>{process.stderr.write(`\nReceived ${t}, shutting down gracefully...\n`);try{await e.stop(),process.exit(0)}catch(e){process.stderr.write(`Error during shutdown: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}};process.on(`SIGINT`,()=>t(`SIGINT`)),process.on(`SIGTERM`,()=>t(`SIGTERM`))}const i=new t.Command(`mcp-serve`).description(`Start MCP server with specified transport`).option(`-t, --type <type>`,`Transport type: stdio, http, or sse`,`stdio`).option(`-p, --port <port>`,`Port to listen on (http/sse only)`,e=>parseInt(e,10),3e3).option(`--host <host>`,`Host to bind to (http/sse only)`,`localhost`).action(async t=>{try{let i=t.type.toLowerCase();i===`stdio`?await r(new e.t(e.i())):i===`http`?await r(new e.r(()=>e.i(),{mode:n.HTTP,port:t.port||Number(process.env.MCP_PORT)||3e3,host:t.host||process.env.MCP_HOST||`localhost`})):i===`sse`?await r(new e.n(()=>e.i(),{mode:n.SSE,port:t.port||Number(process.env.MCP_PORT)||3e3,host:t.host||process.env.MCP_HOST||`localhost`})):(process.stderr.write(`Unknown transport type: ${i}. Use: stdio, http, or sse\n`),process.exit(1))}catch(e){process.stderr.write(`Failed to start MCP server: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}});exports.mcpServeCommand=i;
|
|
2
|
+
//# sourceMappingURL=commands-CZZNZpPm.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands-CZZNZpPm.cjs","names":["Command","StdioTransportHandler","createServer","HttpTransportHandler","SseTransportHandler"],"sources":["../src/types/index.ts","../src/commands/mcp-serve.ts"],"sourcesContent":["/**\n * Shared TypeScript Types\n *\n * DESIGN PATTERNS:\n * - Type-first development\n * - Interface segregation\n *\n * CODING STANDARDS:\n * - Export all shared types from this file\n * - Use descriptive names for types and interfaces\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Tool definition for MCP\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, unknown>;\n required?: string[];\n additionalProperties?: boolean;\n };\n}\n\n/**\n * Base tool interface following MCP SDK patterns\n */\nexport interface Tool<TInput = unknown> {\n getDefinition(): ToolDefinition;\n execute(input: TInput): Promise<CallToolResult>;\n}\n\n/**\n * Transport mode types\n */\nexport enum TransportMode {\n STDIO = 'stdio',\n HTTP = 'http',\n SSE = 'sse',\n}\n\n/**\n * Transport configuration options\n */\nexport interface TransportConfig {\n mode: TransportMode;\n port?: number;\n host?: string;\n}\n\n/**\n * Base interface for all transport handlers\n */\nexport interface TransportHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\n/**\n * HTTP transport specific types\n */\nexport interface HttpTransportHandler extends TransportHandler {\n getPort(): number;\n getHost(): string;\n}\n","/**\n * MCP Serve Command\n *\n * DESIGN PATTERNS:\n * - Command pattern with Commander for CLI argument parsing\n * - Transport abstraction pattern for flexible deployment (stdio, HTTP, SSE)\n * - Factory pattern for creating transport handlers\n * - Graceful shutdown pattern with signal handling\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Implement proper error handling with try-catch blocks\n * - Handle process signals for graceful shutdown\n * - Provide clear CLI options and help messages\n *\n * AVOID:\n * - Hardcoded configuration values (use CLI options or environment variables)\n * - Missing error handling for transport startup\n * - Not cleaning up resources on shutdown\n */\n\nimport { Command } from 'commander';\nimport { createServer } from '../server';\nimport { HttpTransportHandler } from '../transports/http';\nimport { SseTransportHandler } from '../transports/sse';\nimport { StdioTransportHandler } from '../transports/stdio';\nimport { type TransportConfig, TransportMode } from '../types';\n\ninterface LifecycleHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\n/**\n * Start MCP server with given transport handler\n */\nasync function startServer(handler: LifecycleHandler) {\n await handler.start();\n\n // Handle graceful shutdown\n const shutdown = async (signal: string) => {\n process.stderr.write(`\\nReceived ${signal}, shutting down gracefully...\\n`);\n try {\n await handler.stop();\n process.exit(0);\n } catch (error) {\n process.stderr.write(`Error during shutdown: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n };\n\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n}\n\n/**\n * MCP Serve command\n */\nexport const mcpServeCommand = new Command('mcp-serve')\n .description('Start MCP server with specified transport')\n .option('-t, --type <type>', 'Transport type: stdio, http, or sse', 'stdio')\n .option('-p, --port <port>', 'Port to listen on (http/sse only)', (val) => parseInt(val, 10), 3000)\n .option('--host <host>', 'Host to bind to (http/sse only)', 'localhost')\n .action(async (options) => {\n try {\n const transportType = options.type.toLowerCase();\n\n if (transportType === 'stdio') {\n const server = createServer();\n const handler = new StdioTransportHandler(server);\n await startServer(handler);\n } else if (transportType === 'http') {\n // For HTTP, pass a factory function to create new server instances per session\n const config: TransportConfig = {\n mode: TransportMode.HTTP,\n port: options.port || Number(process.env.MCP_PORT) || 3000,\n host: options.host || process.env.MCP_HOST || 'localhost',\n };\n const handler = new HttpTransportHandler(() => createServer(), config);\n await startServer(handler);\n } else if (transportType === 'sse') {\n // For SSE, pass a factory function to create new server instances per connection\n const config: TransportConfig = {\n mode: TransportMode.SSE,\n port: options.port || Number(process.env.MCP_PORT) || 3000,\n host: options.host || process.env.MCP_HOST || 'localhost',\n };\n const handler = new SseTransportHandler(() => createServer(), config);\n await startServer(handler);\n } else {\n process.stderr.write(`Unknown transport type: ${transportType}. Use: stdio, http, or sse\\n`);\n process.exit(1);\n }\n } catch (error) {\n process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n });\n"],"mappings":"mEAuCY,EAAA,SAAA,EAAL,OACL,GAAA,MAAA,QACA,EAAA,KAAA,OACA,EAAA,IAAA,aCNF,eAAe,EAAY,EAA2B,CACpD,MAAM,EAAQ,OAAO,CAGrB,IAAM,EAAW,KAAO,IAAmB,CACzC,QAAQ,OAAO,MAAM,cAAc,EAAO,iCAAiC,CAC3E,GAAI,CACF,MAAM,EAAQ,MAAM,CACpB,QAAQ,KAAK,EAAE,OACR,EAAO,CACd,QAAQ,OAAO,MAAM,0BAA0B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAAC,IAAI,CAC1G,QAAQ,KAAK,EAAE,GAInB,QAAQ,GAAG,aAAgB,EAAS,SAAS,CAAC,CAC9C,QAAQ,GAAG,cAAiB,EAAS,UAAU,CAAC,CAMlD,MAAa,EAAkB,IAAIA,EAAAA,QAAQ,YAAY,CACpD,YAAY,4CAA4C,CACxD,OAAO,oBAAqB,sCAAuC,QAAQ,CAC3E,OAAO,oBAAqB,oCAAsC,GAAQ,SAAS,EAAK,GAAG,CAAE,IAAK,CAClG,OAAO,gBAAiB,kCAAmC,YAAY,CACvE,OAAO,KAAO,IAAY,CACzB,GAAI,CACF,IAAM,EAAgB,EAAQ,KAAK,aAAa,CAE5C,IAAkB,QAGpB,MAAM,EADU,IAAIC,EAAAA,EADLC,EAAAA,GAAc,CACoB,CACvB,CACjB,IAAkB,OAQ3B,MAAM,EADU,IAAIC,EAAAA,MAA2BD,EAAAA,GAAc,CAL7B,CAC9B,KAAM,EAAc,KACpB,KAAM,EAAQ,MAAQ,OAAO,QAAQ,IAAI,SAAS,EAAI,IACtD,KAAM,EAAQ,MAAQ,QAAQ,IAAI,UAAY,YAC/C,CACqE,CAC5C,CACjB,IAAkB,MAQ3B,MAAM,EADU,IAAIE,EAAAA,MAA0BF,EAAAA,GAAc,CAL5B,CAC9B,KAAM,EAAc,IACpB,KAAM,EAAQ,MAAQ,OAAO,QAAQ,IAAI,SAAS,EAAI,IACtD,KAAM,EAAQ,MAAQ,QAAQ,IAAI,UAAY,YAC/C,CACoE,CAC3C,EAE1B,QAAQ,OAAO,MAAM,2BAA2B,EAAc,8BAA8B,CAC5F,QAAQ,KAAK,EAAE,QAEV,EAAO,CACd,QAAQ,OAAO,MAAM,+BAA+B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAAC,IAAI,CAC/G,QAAQ,KAAK,EAAE,GAEjB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{i as e,n as t,r as n,t as r}from"./stdio-
|
|
2
|
-
//# sourceMappingURL=commands-
|
|
1
|
+
import{i as e,n as t,r as n,t as r}from"./stdio-CQi19ID4.mjs";import{Command as i}from"commander";let a=function(e){return e.STDIO=`stdio`,e.HTTP=`http`,e.SSE=`sse`,e}({});async function o(e){await e.start();let t=async t=>{process.stderr.write(`\nReceived ${t}, shutting down gracefully...\n`);try{await e.stop(),process.exit(0)}catch(e){process.stderr.write(`Error during shutdown: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}};process.on(`SIGINT`,()=>t(`SIGINT`)),process.on(`SIGTERM`,()=>t(`SIGTERM`))}const s=new i(`mcp-serve`).description(`Start MCP server with specified transport`).option(`-t, --type <type>`,`Transport type: stdio, http, or sse`,`stdio`).option(`-p, --port <port>`,`Port to listen on (http/sse only)`,e=>parseInt(e,10),3e3).option(`--host <host>`,`Host to bind to (http/sse only)`,`localhost`).action(async i=>{try{let s=i.type.toLowerCase();s===`stdio`?await o(new r(e())):s===`http`?await o(new n(()=>e(),{mode:a.HTTP,port:i.port||Number(process.env.MCP_PORT)||3e3,host:i.host||process.env.MCP_HOST||`localhost`})):s===`sse`?await o(new t(()=>e(),{mode:a.SSE,port:i.port||Number(process.env.MCP_PORT)||3e3,host:i.host||process.env.MCP_HOST||`localhost`})):(process.stderr.write(`Unknown transport type: ${s}. Use: stdio, http, or sse\n`),process.exit(1))}catch(e){process.stderr.write(`Failed to start MCP server: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}});export{s as mcpServeCommand};
|
|
2
|
+
//# sourceMappingURL=commands-NFskYAk7.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands-NFskYAk7.mjs","names":[],"sources":["../src/types/index.ts","../src/commands/mcp-serve.ts"],"sourcesContent":["/**\n * Shared TypeScript Types\n *\n * DESIGN PATTERNS:\n * - Type-first development\n * - Interface segregation\n *\n * CODING STANDARDS:\n * - Export all shared types from this file\n * - Use descriptive names for types and interfaces\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Tool definition for MCP\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, unknown>;\n required?: string[];\n additionalProperties?: boolean;\n };\n}\n\n/**\n * Base tool interface following MCP SDK patterns\n */\nexport interface Tool<TInput = unknown> {\n getDefinition(): ToolDefinition;\n execute(input: TInput): Promise<CallToolResult>;\n}\n\n/**\n * Transport mode types\n */\nexport enum TransportMode {\n STDIO = 'stdio',\n HTTP = 'http',\n SSE = 'sse',\n}\n\n/**\n * Transport configuration options\n */\nexport interface TransportConfig {\n mode: TransportMode;\n port?: number;\n host?: string;\n}\n\n/**\n * Base interface for all transport handlers\n */\nexport interface TransportHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\n/**\n * HTTP transport specific types\n */\nexport interface HttpTransportHandler extends TransportHandler {\n getPort(): number;\n getHost(): string;\n}\n","/**\n * MCP Serve Command\n *\n * DESIGN PATTERNS:\n * - Command pattern with Commander for CLI argument parsing\n * - Transport abstraction pattern for flexible deployment (stdio, HTTP, SSE)\n * - Factory pattern for creating transport handlers\n * - Graceful shutdown pattern with signal handling\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Implement proper error handling with try-catch blocks\n * - Handle process signals for graceful shutdown\n * - Provide clear CLI options and help messages\n *\n * AVOID:\n * - Hardcoded configuration values (use CLI options or environment variables)\n * - Missing error handling for transport startup\n * - Not cleaning up resources on shutdown\n */\n\nimport { Command } from 'commander';\nimport { createServer } from '../server';\nimport { HttpTransportHandler } from '../transports/http';\nimport { SseTransportHandler } from '../transports/sse';\nimport { StdioTransportHandler } from '../transports/stdio';\nimport { type TransportConfig, TransportMode } from '../types';\n\ninterface LifecycleHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\n/**\n * Start MCP server with given transport handler\n */\nasync function startServer(handler: LifecycleHandler) {\n await handler.start();\n\n // Handle graceful shutdown\n const shutdown = async (signal: string) => {\n process.stderr.write(`\\nReceived ${signal}, shutting down gracefully...\\n`);\n try {\n await handler.stop();\n process.exit(0);\n } catch (error) {\n process.stderr.write(`Error during shutdown: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n };\n\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n}\n\n/**\n * MCP Serve command\n */\nexport const mcpServeCommand = new Command('mcp-serve')\n .description('Start MCP server with specified transport')\n .option('-t, --type <type>', 'Transport type: stdio, http, or sse', 'stdio')\n .option('-p, --port <port>', 'Port to listen on (http/sse only)', (val) => parseInt(val, 10), 3000)\n .option('--host <host>', 'Host to bind to (http/sse only)', 'localhost')\n .action(async (options) => {\n try {\n const transportType = options.type.toLowerCase();\n\n if (transportType === 'stdio') {\n const server = createServer();\n const handler = new StdioTransportHandler(server);\n await startServer(handler);\n } else if (transportType === 'http') {\n // For HTTP, pass a factory function to create new server instances per session\n const config: TransportConfig = {\n mode: TransportMode.HTTP,\n port: options.port || Number(process.env.MCP_PORT) || 3000,\n host: options.host || process.env.MCP_HOST || 'localhost',\n };\n const handler = new HttpTransportHandler(() => createServer(), config);\n await startServer(handler);\n } else if (transportType === 'sse') {\n // For SSE, pass a factory function to create new server instances per connection\n const config: TransportConfig = {\n mode: TransportMode.SSE,\n port: options.port || Number(process.env.MCP_PORT) || 3000,\n host: options.host || process.env.MCP_HOST || 'localhost',\n };\n const handler = new SseTransportHandler(() => createServer(), config);\n await startServer(handler);\n } else {\n process.stderr.write(`Unknown transport type: ${transportType}. Use: stdio, http, or sse\\n`);\n process.exit(1);\n }\n } catch (error) {\n process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n });\n"],"mappings":"kGAuCA,IAAY,EAAA,SAAA,EAAL,OACL,GAAA,MAAA,QACA,EAAA,KAAA,OACA,EAAA,IAAA,aCNF,eAAe,EAAY,EAA2B,CACpD,MAAM,EAAQ,OAAO,CAGrB,IAAM,EAAW,KAAO,IAAmB,CACzC,QAAQ,OAAO,MAAM,cAAc,EAAO,iCAAiC,CAC3E,GAAI,CACF,MAAM,EAAQ,MAAM,CACpB,QAAQ,KAAK,EAAE,OACR,EAAO,CACd,QAAQ,OAAO,MAAM,0BAA0B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAAC,IAAI,CAC1G,QAAQ,KAAK,EAAE,GAInB,QAAQ,GAAG,aAAgB,EAAS,SAAS,CAAC,CAC9C,QAAQ,GAAG,cAAiB,EAAS,UAAU,CAAC,CAMlD,MAAa,EAAkB,IAAI,EAAQ,YAAY,CACpD,YAAY,4CAA4C,CACxD,OAAO,oBAAqB,sCAAuC,QAAQ,CAC3E,OAAO,oBAAqB,oCAAsC,GAAQ,SAAS,EAAK,GAAG,CAAE,IAAK,CAClG,OAAO,gBAAiB,kCAAmC,YAAY,CACvE,OAAO,KAAO,IAAY,CACzB,GAAI,CACF,IAAM,EAAgB,EAAQ,KAAK,aAAa,CAE5C,IAAkB,QAGpB,MAAM,EADU,IAAI,EADL,GAAc,CACoB,CACvB,CACjB,IAAkB,OAQ3B,MAAM,EADU,IAAI,MAA2B,GAAc,CAL7B,CAC9B,KAAM,EAAc,KACpB,KAAM,EAAQ,MAAQ,OAAO,QAAQ,IAAI,SAAS,EAAI,IACtD,KAAM,EAAQ,MAAQ,QAAQ,IAAI,UAAY,YAC/C,CACqE,CAC5C,CACjB,IAAkB,MAQ3B,MAAM,EADU,IAAI,MAA0B,GAAc,CAL5B,CAC9B,KAAM,EAAc,IACpB,KAAM,EAAQ,MAAQ,OAAO,QAAQ,IAAI,SAAS,EAAI,IACtD,KAAM,EAAQ,MAAQ,QAAQ,IAAI,UAAY,YAC/C,CACoE,CAC3C,EAE1B,QAAQ,OAAO,MAAM,2BAA2B,EAAc,8BAA8B,CAC5F,QAAQ,KAAK,EAAE,QAEV,EAAO,CACd,QAAQ,OAAO,MAAM,+BAA+B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAAC,IAAI,CAC/G,QAAQ,KAAK,EAAE,GAEjB"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=require(`./stdio-
|
|
1
|
+
const e=require(`./stdio-DSIKRtTS.cjs`);exports.HttpTransportHandler=e.r,exports.ReadImageTool=e.o,exports.SseTransportHandler=e.n,exports.StdioTransportHandler=e.t,exports.UnsplashSearchTool=e.a,exports.createServer=e.i;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,i as t,n,o as r,r as i,t as a}from"./stdio-
|
|
1
|
+
import{a as e,i as t,n,o as r,r as i,t as a}from"./stdio-CQi19ID4.mjs";export{i as HttpTransportHandler,r as ReadImageTool,n as SseTransportHandler,a as StdioTransportHandler,e as UnsplashSearchTool,t as createServer};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Server as e}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as t,ErrorCode as n,ListToolsRequestSchema as r,McpError as i,isInitializeRequest as a}from"@modelcontextprotocol/sdk/types.js";import{createHash as o,randomUUID as s}from"node:crypto";import{constants as c}from"node:fs";import{access as l,readFile as u}from"node:fs/promises";import{basename as d,extname as f,resolve as p}from"node:path";import m from"node-fetch";import{createApi as h}from"unsplash-js";import{z as g}from"zod";import{StreamableHTTPServerTransport as _}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import v from"express";import{SSEServerTransport as y}from"@modelcontextprotocol/sdk/server/sse.js";import{StdioServerTransport as b}from"@modelcontextprotocol/sdk/server/stdio.js";function x(e){let t=e.replace(/\\+ /g,` `);return t=t.replace(/\\+'/g,`'`).replace(/\\+"/g,`"`).replace(/\\+`/g,"`").replace(/\\+\(/g,`(`).replace(/\\+\)/g,`)`).replace(/\\+\[/g,`[`).replace(/\\+\]/g,`]`).replace(/\\+\{/g,`{`).replace(/\\+\}/g,`}`),t}const S=`application/octet-stream`,C={".png":`image/png`,".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".gif":`image/gif`,".webp":`image/webp`,".bmp":`image/bmp`,".svg":`image/svg+xml`,".heic":`image/heic`,".heif":`image/heif`};var w=class{async readImage(e,t={}){let n=await this.resolveImageSource(e),r=o(`sha256`).update(n.data).digest(`hex`),i={source:n.source,sourceType:n.sourceType,mimeType:n.mimeType,sizeBytes:n.sizeBytes,sha256:r};return n.sourceType!==`data-uri`&&(i.fileName=d(n.source)),t.includeBase64&&(i.base64=n.data.toString(`base64`)),i}async resolveImageSource(e){let t=e.trim();if(!t)throw new i(n.InvalidParams,`Image source cannot be empty.`);return t.startsWith(`data:`)?this.resolveDataUri(t):/^https?:\/\//i.test(t)?this.resolveRemoteImage(t):this.resolveFile(t)}async resolveFile(e){let t=p(x(e));try{await l(t,c.F_OK)}catch{throw new i(n.InvalidParams,`Image file not found at path: ${e}`)}let r;try{r=await u(t)}catch(e){throw new i(n.InternalError,`Failed to read image file: ${e instanceof Error?e.message:String(e)}`)}return{source:t,sourceType:`file`,mimeType:C[f(t).toLowerCase()]||S,sizeBytes:r.length,data:r}}async resolveRemoteImage(e){try{let t=await m(e,{redirect:`follow`});if(!t.ok)throw new i(n.InvalidParams,`Failed to fetch image from URL: ${e} (${t.status} ${t.statusText})`);let r=await t.arrayBuffer(),a=Buffer.from(r),o=t.headers.get(`content-type`)?.split(`;`)[0],s=d(new URL(e).pathname||`image`);return{source:e,sourceType:`url`,mimeType:o??C[f(s)]??S,sizeBytes:a.length,data:a}}catch(e){throw e instanceof i?e:new i(n.InternalError,`Failed to download image from URL: ${e instanceof Error?e.message:String(e)}`)}}resolveDataUri(e){let t=e.indexOf(`,`);if(t===-1||t===e.length-1)throw new i(n.InvalidParams,"Invalid data URI format. Expected `data:[<mime>];base64,<payload>`.");let r=e.slice(5,t).toLowerCase(),a=e.slice(t+1),o=r.includes(`base64`),s=r.split(`;`)[0]||S;try{let t=o?Buffer.from(a,`base64`):Buffer.from(decodeURIComponent(a));return{source:e,sourceType:`data-uri`,mimeType:s||S,sizeBytes:t.length,data:t}}catch(e){throw new i(n.InvalidParams,`Failed to decode data URI payload: ${e instanceof Error?e.message:String(e)}`)}}},T=class e{static TOOL_NAME=`read-image`;service=new w;getDefinition(){return{name:e.TOOL_NAME,description:`Read an image from a local path, URL, or data URI and return metadata.`,inputSchema:{type:`object`,properties:{source:{type:`string`,minLength:1,description:`Image source as a local path, HTTP(S) URL, or data URI.`},includeBase64:{type:`boolean`,description:`Whether to include base64-encoded output.`,default:!1}},required:[`source`],additionalProperties:!1}}}async execute(e){try{if(!e||typeof e!=`object`)throw new i(n.InvalidParams,`Tool input must be an object.`);let t=e,r=t.source,a=t.includeBase64;if(!r||typeof r!=`string`||r.trim().length===0)throw new i(n.InvalidParams,`source must be a non-empty string.`);if(a!==void 0&&typeof a!=`boolean`)throw new i(n.InvalidParams,`includeBase64 must be a boolean.`);let o={includeBase64:a},s=await this.service.readImage(r,o);return{content:[{type:`text`,text:JSON.stringify(s,null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};const E=g.object({UPLOAD_SERVICE:g.enum([`s3`,`cloudflare`,`gcloud`]).optional().default(`s3`),S3_BUCKET:g.string().optional(),AWS_ACCESS_KEY_ID:g.string().optional(),AWS_SECRET_ACCESS_KEY:g.string().optional(),S3_REGION:g.string().optional().default(`us-east-1`),S3_ENDPOINT:g.string().url(`S3_ENDPOINT must be a valid URL`).optional(),CLOUDFLARE_R2_BUCKET:g.string().optional(),CLOUDFLARE_R2_ACCESS_KEY_ID:g.string().optional(),CLOUDFLARE_R2_SECRET_ACCESS_KEY:g.string().optional(),CLOUDFLARE_R2_REGION:g.string().optional().default(`auto`),CLOUDFLARE_R2_ENDPOINT:g.string().url(`CLOUDFLARE_R2_ENDPOINT must be a valid URL`).optional(),GCLOUD_BUCKET:g.string().optional(),GCLOUD_PROJECT_ID:g.string().optional(),GCLOUD_CREDENTIALS_PATH:g.string().optional(),UNSPLASH_ACCESS_KEY:g.string().optional()}).refine(e=>e.AWS_ACCESS_KEY_ID||e.AWS_SECRET_ACCESS_KEY?e.AWS_ACCESS_KEY_ID&&e.AWS_SECRET_ACCESS_KEY:!0,{message:`Both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be provided together`,path:[`AWS_ACCESS_KEY_ID`]}),D=class e{static instance;config;constructor(){try{this.config=E.parse(process.env)}catch(e){if(e instanceof g.ZodError){let t=e.issues.map(e=>`${e.path.join(`.`)}: ${e.message}`).join(`; `);throw new i(n.InternalError,`Environment configuration validation failed: ${t}`)}throw e}}static getInstance(){return e.instance||=new e,e.instance}getConfig(){return this.config}getS3Config(){return{bucket:this.config.S3_BUCKET,accessKeyId:this.config.AWS_ACCESS_KEY_ID,secretAccessKey:this.config.AWS_SECRET_ACCESS_KEY,region:this.config.S3_REGION,endpoint:this.config.S3_ENDPOINT}}getCloudflareConfig(){return{bucket:this.config.CLOUDFLARE_R2_BUCKET,accessKeyId:this.config.CLOUDFLARE_R2_ACCESS_KEY_ID,secretAccessKey:this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY,region:this.config.CLOUDFLARE_R2_REGION,endpoint:this.config.CLOUDFLARE_R2_ENDPOINT}}getGCloudConfig(){return{bucket:this.config.GCLOUD_BUCKET,projectId:this.config.GCLOUD_PROJECT_ID,credentialsPath:this.config.GCLOUD_CREDENTIALS_PATH}}getUploadService(){return this.config.UPLOAD_SERVICE}getUnsplashConfig(){return{accessKey:this.config.UNSPLASH_ACCESS_KEY}}validateServiceConfig(e){switch(e){case`s3`:if(!this.config.S3_BUCKET)throw new i(n.InvalidParams,`S3_BUCKET is required for S3 upload service`);break;case`cloudflare`:if(!this.config.CLOUDFLARE_R2_BUCKET)throw new i(n.InvalidParams,`CLOUDFLARE_R2_BUCKET is required for Cloudflare upload service`);if(!this.config.CLOUDFLARE_R2_ACCESS_KEY_ID||!this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY)throw new i(n.InvalidParams,`CLOUDFLARE_R2_ACCESS_KEY_ID and CLOUDFLARE_R2_SECRET_ACCESS_KEY are required for Cloudflare upload service`);if(!this.config.CLOUDFLARE_R2_ENDPOINT)throw new i(n.InvalidParams,`CLOUDFLARE_R2_ENDPOINT is required for Cloudflare upload service`);break;case`gcloud`:if(!this.config.GCLOUD_BUCKET)throw new i(n.InvalidParams,`GCLOUD_BUCKET is required for Google Cloud upload service`);if(!this.config.GCLOUD_PROJECT_ID)throw new i(n.InvalidParams,`GCLOUD_PROJECT_ID is required for Google Cloud upload service`);break}}}.getInstance();var O=class{api;constructor(e){let t=D.getUnsplashConfig(),n=e||t.accessKey;if(!n)throw Error(`Unsplash API key is required. Set UNSPLASH_ACCESS_KEY environment variable.`);this.api=h({accessKey:n})}async searchImages(e){try{let t=await this.api.search.getPhotos({query:e.query,page:e.page||1,perPage:e.perPage||10,orientation:e.orientation,color:e.color});if(t.errors)throw Error(`Unsplash API error: ${t.errors.join(`, `)}`);if(!t.response)throw Error(`No response from Unsplash API`);return t.response.results.map(e=>({id:e.id,description:e.description,alt_description:e.alt_description,urls:{raw:e.urls.raw,full:e.urls.full,regular:e.urls.regular,small:e.urls.small,thumb:e.urls.thumb},links:{html:e.links.html,download:e.links.download},user:{name:e.user.name,username:e.user.username,portfolio_url:e.user.portfolio_url},width:e.width,height:e.height,color:e.color||`#000000`,likes:e.likes}))}catch(e){throw Error(`Failed to search Unsplash images: ${e instanceof Error?e.message:`Unknown error`}
|
|
1
|
+
import{Server as e}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as t,ErrorCode as n,ListToolsRequestSchema as r,McpError as i,isInitializeRequest as a}from"@modelcontextprotocol/sdk/types.js";import{createHash as o,randomUUID as s}from"node:crypto";import{constants as c}from"node:fs";import{access as l,readFile as u}from"node:fs/promises";import{basename as d,extname as f,resolve as p}from"node:path";import m from"node-fetch";import{createApi as h}from"unsplash-js";import{z as g}from"zod";import{StreamableHTTPServerTransport as _}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import v from"express";import{SSEServerTransport as y}from"@modelcontextprotocol/sdk/server/sse.js";import{StdioServerTransport as b}from"@modelcontextprotocol/sdk/server/stdio.js";function x(e){let t=e.replace(/\\+ /g,` `);return t=t.replace(/\\+'/g,`'`).replace(/\\+"/g,`"`).replace(/\\+`/g,"`").replace(/\\+\(/g,`(`).replace(/\\+\)/g,`)`).replace(/\\+\[/g,`[`).replace(/\\+\]/g,`]`).replace(/\\+\{/g,`{`).replace(/\\+\}/g,`}`),t}const S=`application/octet-stream`,C={".png":`image/png`,".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".gif":`image/gif`,".webp":`image/webp`,".bmp":`image/bmp`,".svg":`image/svg+xml`,".heic":`image/heic`,".heif":`image/heif`};var w=class{async readImage(e,t={}){let n=await this.resolveImageSource(e),r=o(`sha256`).update(n.data).digest(`hex`),i={source:n.source,sourceType:n.sourceType,mimeType:n.mimeType,sizeBytes:n.sizeBytes,sha256:r};return n.sourceType!==`data-uri`&&(i.fileName=d(n.source)),t.includeBase64&&(i.base64=n.data.toString(`base64`)),i}async resolveImageSource(e){let t=e.trim();if(!t)throw new i(n.InvalidParams,`Image source cannot be empty.`);return t.startsWith(`data:`)?this.resolveDataUri(t):/^https?:\/\//i.test(t)?this.resolveRemoteImage(t):this.resolveFile(t)}async resolveFile(e){let t=p(x(e));try{await l(t,c.F_OK)}catch{throw new i(n.InvalidParams,`Image file not found at path: ${e}`)}let r;try{r=await u(t)}catch(e){throw new i(n.InternalError,`Failed to read image file: ${e instanceof Error?e.message:String(e)}`)}return{source:t,sourceType:`file`,mimeType:C[f(t).toLowerCase()]||S,sizeBytes:r.length,data:r}}async resolveRemoteImage(e){try{let t=await m(e,{redirect:`follow`});if(!t.ok)throw new i(n.InvalidParams,`Failed to fetch image from URL: ${e} (${t.status} ${t.statusText})`);let r=await t.arrayBuffer(),a=Buffer.from(r),o=t.headers.get(`content-type`)?.split(`;`)[0],s=d(new URL(e).pathname||`image`);return{source:e,sourceType:`url`,mimeType:o??C[f(s)]??S,sizeBytes:a.length,data:a}}catch(e){throw e instanceof i?e:new i(n.InternalError,`Failed to download image from URL: ${e instanceof Error?e.message:String(e)}`)}}resolveDataUri(e){let t=e.indexOf(`,`);if(t===-1||t===e.length-1)throw new i(n.InvalidParams,"Invalid data URI format. Expected `data:[<mime>];base64,<payload>`.");let r=e.slice(5,t).toLowerCase(),a=e.slice(t+1),o=r.includes(`base64`),s=r.split(`;`)[0]||S;try{let t=o?Buffer.from(a,`base64`):Buffer.from(decodeURIComponent(a));return{source:e,sourceType:`data-uri`,mimeType:s||S,sizeBytes:t.length,data:t}}catch(e){throw new i(n.InvalidParams,`Failed to decode data URI payload: ${e instanceof Error?e.message:String(e)}`)}}},T=class e{static TOOL_NAME=`read-image`;service=new w;getDefinition(){return{name:e.TOOL_NAME,description:`Read an image from a local path, URL, or data URI and return metadata.`,inputSchema:{type:`object`,properties:{source:{type:`string`,minLength:1,description:`Image source as a local path, HTTP(S) URL, or data URI.`},includeBase64:{type:`boolean`,description:`Whether to include base64-encoded output.`,default:!1}},required:[`source`],additionalProperties:!1}}}async execute(e){try{if(!e||typeof e!=`object`)throw new i(n.InvalidParams,`Tool input must be an object.`);let t=e,r=t.source,a=t.includeBase64;if(!r||typeof r!=`string`||r.trim().length===0)throw new i(n.InvalidParams,`source must be a non-empty string.`);if(a!==void 0&&typeof a!=`boolean`)throw new i(n.InvalidParams,`includeBase64 must be a boolean.`);let o={includeBase64:a},s=await this.service.readImage(r,o);return{content:[{type:`text`,text:JSON.stringify(s,null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};const E=g.object({UPLOAD_SERVICE:g.enum([`s3`,`cloudflare`,`gcloud`]).optional().default(`s3`),S3_BUCKET:g.string().optional(),AWS_ACCESS_KEY_ID:g.string().optional(),AWS_SECRET_ACCESS_KEY:g.string().optional(),S3_REGION:g.string().optional().default(`us-east-1`),S3_ENDPOINT:g.string().url(`S3_ENDPOINT must be a valid URL`).optional(),CLOUDFLARE_R2_BUCKET:g.string().optional(),CLOUDFLARE_R2_ACCESS_KEY_ID:g.string().optional(),CLOUDFLARE_R2_SECRET_ACCESS_KEY:g.string().optional(),CLOUDFLARE_R2_REGION:g.string().optional().default(`auto`),CLOUDFLARE_R2_ENDPOINT:g.string().url(`CLOUDFLARE_R2_ENDPOINT must be a valid URL`).optional(),GCLOUD_BUCKET:g.string().optional(),GCLOUD_PROJECT_ID:g.string().optional(),GCLOUD_CREDENTIALS_PATH:g.string().optional(),UNSPLASH_ACCESS_KEY:g.string().optional()}).refine(e=>e.AWS_ACCESS_KEY_ID||e.AWS_SECRET_ACCESS_KEY?e.AWS_ACCESS_KEY_ID&&e.AWS_SECRET_ACCESS_KEY:!0,{message:`Both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be provided together`,path:[`AWS_ACCESS_KEY_ID`]}),D=class e{static instance;config;constructor(){try{this.config=E.parse(process.env)}catch(e){if(e instanceof g.ZodError){let t=e.issues.map(e=>`${e.path.join(`.`)}: ${e.message}`).join(`; `);throw new i(n.InternalError,`Environment configuration validation failed: ${t}`)}throw e}}static getInstance(){return e.instance||=new e,e.instance}getConfig(){return this.config}getS3Config(){return{bucket:this.config.S3_BUCKET,accessKeyId:this.config.AWS_ACCESS_KEY_ID,secretAccessKey:this.config.AWS_SECRET_ACCESS_KEY,region:this.config.S3_REGION,endpoint:this.config.S3_ENDPOINT}}getCloudflareConfig(){return{bucket:this.config.CLOUDFLARE_R2_BUCKET,accessKeyId:this.config.CLOUDFLARE_R2_ACCESS_KEY_ID,secretAccessKey:this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY,region:this.config.CLOUDFLARE_R2_REGION,endpoint:this.config.CLOUDFLARE_R2_ENDPOINT}}getGCloudConfig(){return{bucket:this.config.GCLOUD_BUCKET,projectId:this.config.GCLOUD_PROJECT_ID,credentialsPath:this.config.GCLOUD_CREDENTIALS_PATH}}getUploadService(){return this.config.UPLOAD_SERVICE}getUnsplashConfig(){return{accessKey:this.config.UNSPLASH_ACCESS_KEY}}validateServiceConfig(e){switch(e){case`s3`:if(!this.config.S3_BUCKET)throw new i(n.InvalidParams,`S3_BUCKET is required for S3 upload service`);break;case`cloudflare`:if(!this.config.CLOUDFLARE_R2_BUCKET)throw new i(n.InvalidParams,`CLOUDFLARE_R2_BUCKET is required for Cloudflare upload service`);if(!this.config.CLOUDFLARE_R2_ACCESS_KEY_ID||!this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY)throw new i(n.InvalidParams,`CLOUDFLARE_R2_ACCESS_KEY_ID and CLOUDFLARE_R2_SECRET_ACCESS_KEY are required for Cloudflare upload service`);if(!this.config.CLOUDFLARE_R2_ENDPOINT)throw new i(n.InvalidParams,`CLOUDFLARE_R2_ENDPOINT is required for Cloudflare upload service`);break;case`gcloud`:if(!this.config.GCLOUD_BUCKET)throw new i(n.InvalidParams,`GCLOUD_BUCKET is required for Google Cloud upload service`);if(!this.config.GCLOUD_PROJECT_ID)throw new i(n.InvalidParams,`GCLOUD_PROJECT_ID is required for Google Cloud upload service`);break}}}.getInstance();var O=class{api;constructor(e){let t=D.getUnsplashConfig(),n=e||t.accessKey;if(!n)throw Error(`Unsplash API key is required. Set UNSPLASH_ACCESS_KEY environment variable.`);this.api=h({accessKey:n})}async searchImages(e){try{let t=await this.api.search.getPhotos({query:e.query,page:e.page||1,perPage:e.perPage||10,orientation:e.orientation,color:e.color});if(t.errors)throw Error(`Unsplash API error: ${t.errors.join(`, `)}`);if(!t.response)throw Error(`No response from Unsplash API`);return t.response.results.map(e=>({id:e.id,description:e.description,alt_description:e.alt_description,urls:{raw:e.urls.raw,full:e.urls.full,regular:e.urls.regular,small:e.urls.small,thumb:e.urls.thumb},links:{html:e.links.html,download:e.links.download},user:{name:e.user.name,username:e.user.username,portfolio_url:e.user.portfolio_url},width:e.width,height:e.height,color:e.color||`#000000`,likes:e.likes}))}catch(e){throw Error(`Failed to search Unsplash images: ${e instanceof Error?e.message:`Unknown error`}`,{cause:e})}}},k=class e{static TOOL_NAME=`unsplash_search`;service;accessKey;constructor(e){this.accessKey=e,this.service=null}getService(){return this.service||=new O(this.accessKey),this.service}getDefinition(){return{name:e.TOOL_NAME,description:`Search for stock images from Unsplash by keyword and return image URLs with metadata`,inputSchema:{type:`object`,properties:{query:{type:`string`,description:`Search query (e.g., "sunset", "technology", "nature")`},perPage:{type:`number`,description:`Number of results per page (1-30, default: 10)`,minimum:1,maximum:30},page:{type:`number`,description:`Page number for pagination (default: 1)`,minimum:1},orientation:{type:`string`,enum:[`landscape`,`portrait`,`squarish`],description:`Filter by photo orientation`},color:{type:`string`,enum:[`black_and_white`,`black`,`white`,`yellow`,`orange`,`red`,`purple`,`magenta`,`green`,`teal`,`blue`],description:`Filter by photo color`}},required:[`query`],additionalProperties:!1}}}async execute(e){try{let t=await this.getService().searchImages({query:e.query,perPage:e.perPage,page:e.page,orientation:e.orientation,color:e.color});if(t.length===0)return{content:[{type:`text`,text:`No images found for query: "${e.query}"`}]};let n=t.map((e,t)=>`
|
|
2
2
|
📷 **Image ${t+1}**
|
|
3
3
|
- **ID**: ${e.id}
|
|
4
4
|
- **Description**: ${e.alt_description||e.description||`No description`}
|
|
@@ -23,4 +23,4 @@ ${e.user.portfolio_url?`- Photographer Portfolio: ${e.user.portfolio_url}`:``}
|
|
|
23
23
|
|
|
24
24
|
`);return{content:[{type:`text`,text:`Found ${t.length} image(s) for "${e.query}":\n\n${n}`}]}}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};function A(){let n=new e({name:`imagine-mcp`,version:`0.1.0`},{capabilities:{tools:{}}}),i=new k,a=new T;return n.setRequestHandler(r,async()=>({tools:[i.getDefinition(),a.getDefinition()]})),n.setRequestHandler(t,async e=>{let{name:t,arguments:n}=e.params;return t===k.TOOL_NAME?await i.execute(n):t===T.TOOL_NAME?await a.execute(n):{content:[{type:`text`,text:`Unknown tool: ${t}`}],isError:!0}}),n}var j=class{sessions=new Map;getSession(e){return this.sessions.get(e)}setSession(e,t,n){this.sessions.set(e,{transport:t,server:n})}deleteSession(e){let t=this.sessions.get(e);t&&t.server.close(),this.sessions.delete(e)}hasSession(e){return this.sessions.has(e)}clear(){for(let e of this.sessions.values())e.server.close();this.sessions.clear()}},M=class{serverFactory;app;server=null;sessionManager;config;constructor(e,t){this.serverFactory=typeof e==`function`?e:()=>e,this.app=v(),this.sessionManager=new j,this.config={mode:t.mode,port:t.port??3e3,host:t.host??`localhost`},this.setupMiddleware(),this.setupRoutes()}setupMiddleware(){this.app.use(v.json())}setupRoutes(){this.app.post(`/mcp`,async(e,t)=>{await this.handlePostRequest(e,t)}),this.app.get(`/mcp`,async(e,t)=>{await this.handleGetRequest(e,t)}),this.app.delete(`/mcp`,async(e,t)=>{await this.handleDeleteRequest(e,t)}),this.app.get(`/health`,(e,t)=>{t.json({status:`ok`,transport:`http`})})}async handlePostRequest(e,t){let n=e.headers[`mcp-session-id`],r;if(n&&this.sessionManager.hasSession(n))r=this.sessionManager.getSession(n).transport;else if(!n&&a(e.body)){let e=this.serverFactory();r=new _({sessionIdGenerator:()=>s(),enableJsonResponse:!0,onsessioninitialized:t=>{this.sessionManager.setSession(t,r,e)}}),r.onclose=()=>{r.sessionId&&this.sessionManager.deleteSession(r.sessionId)},await e.connect(r)}else{t.status(400).json({jsonrpc:`2.0`,error:{code:-32e3,message:`Bad Request: No valid session ID provided`},id:null});return}await r.handleRequest(e,t,e.body)}async handleGetRequest(e,t){let n=e.headers[`mcp-session-id`];if(!n||!this.sessionManager.hasSession(n)){t.status(400).send(`Invalid or missing session ID`);return}await this.sessionManager.getSession(n).transport.handleRequest(e,t)}async handleDeleteRequest(e,t){let n=e.headers[`mcp-session-id`];if(!n||!this.sessionManager.hasSession(n)){t.status(400).send(`Invalid or missing session ID`);return}await this.sessionManager.getSession(n).transport.handleRequest(e,t),this.sessionManager.deleteSession(n)}async start(){return new Promise((e,t)=>{try{this.server=this.app.listen(this.config.port,this.config.host,()=>{process.stderr.write(`imagine-mcp MCP server started on http://${this.config.host}:${this.config.port}/mcp\n`),process.stderr.write(`Health check: http://${this.config.host}:${this.config.port}/health\n`),e()}),this.server.on(`error`,e=>{t(e)})}catch(e){t(e)}})}async stop(){return new Promise((e,t)=>{this.server?(this.sessionManager.clear(),this.server.close(n=>{n?t(n):(this.server=null,e())})):e()})}getPort(){return this.config.port}getHost(){return this.config.host}},N=class{sessions=new Map;getSession(e){return this.sessions.get(e)?.transport}setSession(e,t,n){this.sessions.set(e,{transport:t,server:n})}deleteSession(e){let t=this.sessions.get(e);t&&t.server.close(),this.sessions.delete(e)}hasSession(e){return this.sessions.has(e)}clear(){for(let e of this.sessions.values())e.server.close();this.sessions.clear()}},P=class{serverFactory;app;server=null;sessionManager;config;constructor(e,t){this.serverFactory=typeof e==`function`?e:()=>e,this.app=v(),this.sessionManager=new N,this.config={mode:t.mode,port:t.port??3e3,host:t.host??`localhost`},this.setupMiddleware(),this.setupRoutes()}setupMiddleware(){this.app.use(v.json())}setupRoutes(){this.app.get(`/sse`,async(e,t)=>{await this.handleSseConnection(e,t)}),this.app.post(`/messages`,async(e,t)=>{await this.handlePostMessage(e,t)}),this.app.get(`/health`,(e,t)=>{t.json({status:`ok`,transport:`sse`})})}async handleSseConnection(e,t){try{let e=this.serverFactory(),n=new y(`/messages`,t);this.sessionManager.setSession(n.sessionId,n,e),t.on(`close`,()=>{this.sessionManager.deleteSession(n.sessionId)}),await e.connect(n),process.stderr.write(`SSE session established: ${n.sessionId}\n`)}catch(e){process.stderr.write(`Error handling SSE connection: ${e instanceof Error?e.message:String(e)}\n`),t.headersSent||t.status(500).send(`Internal Server Error`)}}async handlePostMessage(e,t){let n=e.query.sessionId;if(!n){t.status(400).send(`Missing sessionId query parameter`);return}let r=this.sessionManager.getSession(n);if(!r){t.status(404).send(`No transport found for sessionId`);return}try{await r.handlePostMessage(e,t,e.body)}catch(e){process.stderr.write(`Error handling post message: ${e instanceof Error?e.message:String(e)}\n`),t.headersSent||t.status(500).send(`Internal Server Error`)}}async start(){return new Promise((e,t)=>{try{this.server=this.app.listen(this.config.port,this.config.host,()=>{process.stderr.write(`imagine-mcp MCP server started with SSE transport on http://${this.config.host}:${this.config.port}\n`),process.stderr.write(`SSE endpoint: http://${this.config.host}:${this.config.port}/sse\n`),process.stderr.write(`Messages endpoint: http://${this.config.host}:${this.config.port}/messages\n`),process.stderr.write(`Health check: http://${this.config.host}:${this.config.port}/health\n`),e()}),this.server.on(`error`,e=>{t(e)})}catch(e){t(e)}})}async stop(){return new Promise((e,t)=>{this.server?(this.sessionManager.clear(),this.server.close(n=>{n?t(n):(this.server=null,e())})):e()})}getPort(){return this.config.port}getHost(){return this.config.host}},F=class{server;transport=null;constructor(e){this.server=e}async start(){this.transport=new b,await this.server.connect(this.transport),process.stderr.write(`imagine-mcp MCP server started on stdio
|
|
25
25
|
`)}async stop(){this.transport&&=(await this.transport.close(),null)}};export{k as a,A as i,P as n,T as o,M as r,F as t};
|
|
26
|
-
//# sourceMappingURL=stdio-
|
|
26
|
+
//# sourceMappingURL=stdio-CQi19ID4.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio-CQi19ID4.mjs","names":["MIME_BY_EXTENSION: Record<string, string>","result: ReadImageResult","data: Buffer","options: ReadImageOptions","config","transport: StreamableHTTPServerTransport","sessionId","config"],"sources":["../src/utils.ts","../src/services/ImageReader.ts","../src/tools/ReadImageTool.ts","../src/imagineEnvSchema.ts","../src/config.ts","../src/services/UnsplashService.ts","../src/tools/UnsplashSearchTool.ts","../src/server/index.ts","../src/transports/http.ts","../src/transports/sse.ts","../src/transports/stdio.ts"],"sourcesContent":["import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport fetch from 'node-fetch';\n\nimport { SUPPORTED_INPUT_FORMATS, SUPPORTED_OUTPUT_FORMATS } from './constants';\n\n/**\n * Validates if the provided format is supported for input\n */\nexport function isValidInputFormat(format: string): boolean {\n return SUPPORTED_INPUT_FORMATS.includes(format.toLowerCase());\n}\n\n/**\n * Validates if the provided format is supported for output\n */\nexport function isValidOutputFormat(format: string): boolean {\n return SUPPORTED_OUTPUT_FORMATS.includes(format.toLowerCase());\n}\n\n/**\n * Validates if the provided dimensions are valid\n */\nexport function isValidDimensions(width?: number, height?: number): boolean {\n if (width !== undefined && (width <= 0 || width > 10000)) {\n return false;\n }\n if (height !== undefined && (height <= 0 || height > 10000)) {\n return false;\n }\n return true;\n}\n\n/**\n * Validates if the provided quality is valid\n */\nexport function isValidQuality(quality?: number): boolean {\n if (quality !== undefined && (quality < 1 || quality > 100)) {\n return false;\n }\n return true;\n}\n\n/**\n * Extracts the file extension from a filename\n */\nexport function getFileExtension(filename: string): string {\n const parts = filename.split('.');\n return parts.length > 1 ? parts.pop()!.toLowerCase() : '';\n}\n\n/**\n * Converts a base64 string to a Buffer\n */\nexport function base64ToBuffer(base64: string): Buffer {\n // Remove data URL prefix if present\n const base64Data = base64.replace(/^data:image\\/\\w+;base64,/, '');\n return Buffer.from(base64Data, 'base64');\n}\n\n/**\n * Converts a Buffer to a base64 string\n */\nexport function bufferToBase64(buffer: Buffer, mimeType: string): string {\n return `data:${mimeType};base64,${buffer.toString('base64')}`;\n}\n\n/**\n * Normalizes a file path by handling escaped characters and spaces\n *\n * This function handles cases like 'a\\ name.png' by converting them to 'a name.png'\n */\nexport function normalizeFilePath(filePath: string): string {\n // Replace escaped spaces (\\ ) with actual spaces\n let normalizedPath = filePath.replace(/\\\\+ /g, ' ');\n\n // Replace other common escaped characters\n normalizedPath = normalizedPath\n .replace(/\\\\+'/g, \"'\")\n .replace(/\\\\+\"/g, '\"')\n .replace(/\\\\+`/g, '`')\n .replace(/\\\\+\\(/g, '(')\n .replace(/\\\\+\\)/g, ')')\n .replace(/\\\\+\\[/g, '[')\n .replace(/\\\\+\\]/g, ']')\n .replace(/\\\\+\\{/g, '{')\n .replace(/\\\\+\\}/g, '}');\n\n return normalizedPath;\n}\n\n/**\n * Fetches an image from a URL and returns it as a Buffer\n */\nexport async function fetchImageFromUrl(url: string): Promise<Buffer> {\n try {\n const response = await fetch(url);\n\n // Check if the response is successful\n if (!response.ok) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Failed to fetch image from URL: ${url}, status code: ${response.status}`,\n );\n }\n\n // Check if the content type is an image\n const contentType = response.headers.get('content-type');\n if (!contentType || !contentType.startsWith('image/')) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `URL does not point to an image: ${url}, content-type: ${contentType}`,\n );\n }\n\n // Get the response as an ArrayBuffer and convert to Buffer\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer);\n } catch (error) {\n if (error instanceof McpError) {\n throw error;\n }\n throw new McpError(\n ErrorCode.InternalError,\n `Error fetching image from URL: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n","import { createHash } from 'node:crypto';\nimport { constants } from 'node:fs';\nimport { access, readFile } from 'node:fs/promises';\nimport { basename, extname, resolve } from 'node:path';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport fetch from 'node-fetch';\n\nimport { normalizeFilePath } from '../utils';\n\ninterface ImageSource {\n source: string;\n sourceType: 'data-uri' | 'url' | 'file';\n mimeType: string;\n sizeBytes: number;\n data: Buffer;\n}\n\nexport interface ReadImageResult {\n source: string;\n sourceType: 'data-uri' | 'url' | 'file';\n mimeType: string;\n sizeBytes: number;\n sha256: string;\n fileName?: string;\n base64?: string;\n}\n\nexport interface ReadImageOptions {\n includeBase64?: boolean;\n}\n\nconst DEFAULT_MIME_TYPE = 'application/octet-stream';\nconst MIME_BY_EXTENSION: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.bmp': 'image/bmp',\n '.svg': 'image/svg+xml',\n '.heic': 'image/heic',\n '.heif': 'image/heif',\n};\n\nexport class ImageReader {\n async readImage(source: string, options: ReadImageOptions = {}): Promise<ReadImageResult> {\n const imageSource = await this.resolveImageSource(source);\n const sha256 = createHash('sha256').update(imageSource.data).digest('hex');\n\n const result: ReadImageResult = {\n source: imageSource.source,\n sourceType: imageSource.sourceType,\n mimeType: imageSource.mimeType,\n sizeBytes: imageSource.sizeBytes,\n sha256,\n };\n\n if (imageSource.sourceType !== 'data-uri') {\n result.fileName = basename(imageSource.source);\n }\n\n if (options.includeBase64) {\n result.base64 = imageSource.data.toString('base64');\n }\n\n return result;\n }\n\n private async resolveImageSource(source: string): Promise<ImageSource> {\n const trimmed = source.trim();\n\n if (!trimmed) {\n throw new McpError(ErrorCode.InvalidParams, 'Image source cannot be empty.');\n }\n\n if (trimmed.startsWith('data:')) {\n return this.resolveDataUri(trimmed);\n }\n\n if (/^https?:\\/\\//i.test(trimmed)) {\n return this.resolveRemoteImage(trimmed);\n }\n\n return this.resolveFile(trimmed);\n }\n\n private async resolveFile(filePath: string): Promise<ImageSource> {\n const normalizedPath = resolve(normalizeFilePath(filePath));\n\n try {\n await access(normalizedPath, constants.F_OK);\n } catch {\n throw new McpError(ErrorCode.InvalidParams, `Image file not found at path: ${filePath}`);\n }\n\n let data: Buffer;\n try {\n data = await readFile(normalizedPath);\n } catch (error) {\n throw new McpError(\n ErrorCode.InternalError,\n `Failed to read image file: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n const extension = extname(normalizedPath).toLowerCase();\n const mimeType = MIME_BY_EXTENSION[extension] || DEFAULT_MIME_TYPE;\n\n return {\n source: normalizedPath,\n sourceType: 'file',\n mimeType,\n sizeBytes: data.length,\n data,\n };\n }\n\n private async resolveRemoteImage(url: string): Promise<ImageSource> {\n try {\n const response = await fetch(url, { redirect: 'follow' });\n if (!response.ok) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Failed to fetch image from URL: ${url} (${response.status} ${response.statusText})`,\n );\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const data = Buffer.from(arrayBuffer);\n const contentType = response.headers.get('content-type')?.split(';')[0];\n const fileName = basename(new URL(url).pathname || 'image');\n\n return {\n source: url,\n sourceType: 'url',\n mimeType: contentType ?? MIME_BY_EXTENSION[extname(fileName)] ?? DEFAULT_MIME_TYPE,\n sizeBytes: data.length,\n data,\n };\n } catch (error) {\n if (error instanceof McpError) {\n throw error;\n }\n throw new McpError(\n ErrorCode.InternalError,\n `Failed to download image from URL: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private resolveDataUri(dataUri: string): ImageSource {\n const commaIndex = dataUri.indexOf(',');\n if (commaIndex === -1 || commaIndex === dataUri.length - 1) {\n throw new McpError(\n ErrorCode.InvalidParams,\n 'Invalid data URI format. Expected `data:[<mime>];base64,<payload>`.',\n );\n }\n\n const metadata = dataUri.slice(5, commaIndex).toLowerCase();\n const payload = dataUri.slice(commaIndex + 1);\n const isBase64 = metadata.includes('base64');\n const declaredMimeType = metadata.split(';')[0] || DEFAULT_MIME_TYPE;\n\n try {\n const data = isBase64 ? Buffer.from(payload, 'base64') : Buffer.from(decodeURIComponent(payload));\n return {\n source: dataUri,\n sourceType: 'data-uri',\n mimeType: declaredMimeType || DEFAULT_MIME_TYPE,\n sizeBytes: data.length,\n data,\n };\n } catch (error) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Failed to decode data URI payload: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n}\n","import { CallToolResult, ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { ImageReader, type ReadImageOptions } from '../services/ImageReader';\nimport type { Tool, ToolDefinition } from '../types/index';\n\nexport interface ReadImageToolInput {\n source: string;\n includeBase64?: boolean;\n}\n\nexport class ReadImageTool implements Tool<ReadImageToolInput> {\n static readonly TOOL_NAME = 'read-image';\n\n private service = new ImageReader();\n\n getDefinition(): ToolDefinition {\n return {\n name: ReadImageTool.TOOL_NAME,\n description: 'Read an image from a local path, URL, or data URI and return metadata.',\n inputSchema: {\n type: 'object',\n properties: {\n source: {\n type: 'string',\n minLength: 1,\n description: 'Image source as a local path, HTTP(S) URL, or data URI.',\n },\n includeBase64: {\n type: 'boolean',\n description: 'Whether to include base64-encoded output.',\n default: false,\n },\n },\n required: ['source'],\n additionalProperties: false,\n },\n };\n }\n\n async execute(input: ReadImageToolInput): Promise<CallToolResult> {\n try {\n if (!input || typeof input !== 'object') {\n throw new McpError(ErrorCode.InvalidParams, 'Tool input must be an object.');\n }\n\n const payload = input as ReadImageToolInput & { [key: string]: unknown };\n const source = payload.source;\n const includeBase64 = payload.includeBase64;\n if (!source || typeof source !== 'string' || source.trim().length === 0) {\n throw new McpError(ErrorCode.InvalidParams, 'source must be a non-empty string.');\n }\n if (includeBase64 !== undefined && typeof includeBase64 !== 'boolean') {\n throw new McpError(ErrorCode.InvalidParams, 'includeBase64 must be a boolean.');\n }\n\n const options: ReadImageOptions = {\n includeBase64,\n };\n const result = await this.service.readImage(source, options);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n}\n","import { z } from 'zod';\n\n/**\n * Environment variable schema for imagine-mcp\n *\n * Following Agiflow naming conventions:\n * - Service credentials use provider-specific prefixes\n * - Upload service selection uses UPLOAD_SERVICE\n */\nexport const envSchema = z\n .object({\n // Upload service selection\n UPLOAD_SERVICE: z.enum(['s3', 'cloudflare', 'gcloud']).optional().default('s3'),\n\n // AWS S3 Configuration\n S3_BUCKET: z.string().optional(),\n AWS_ACCESS_KEY_ID: z.string().optional(),\n AWS_SECRET_ACCESS_KEY: z.string().optional(),\n S3_REGION: z.string().optional().default('us-east-1'),\n S3_ENDPOINT: z.string().url('S3_ENDPOINT must be a valid URL').optional(),\n\n // Cloudflare R2 Configuration\n CLOUDFLARE_R2_BUCKET: z.string().optional(),\n CLOUDFLARE_R2_ACCESS_KEY_ID: z.string().optional(),\n CLOUDFLARE_R2_SECRET_ACCESS_KEY: z.string().optional(),\n CLOUDFLARE_R2_REGION: z.string().optional().default('auto'),\n CLOUDFLARE_R2_ENDPOINT: z.string().url('CLOUDFLARE_R2_ENDPOINT must be a valid URL').optional(),\n\n // Google Cloud Storage Configuration\n GCLOUD_BUCKET: z.string().optional(),\n GCLOUD_PROJECT_ID: z.string().optional(),\n GCLOUD_CREDENTIALS_PATH: z.string().optional(),\n\n // Unsplash API Configuration\n UNSPLASH_ACCESS_KEY: z.string().optional(),\n })\n .refine(\n (data) => {\n // If AWS credentials are provided, both must be present\n if (data.AWS_ACCESS_KEY_ID || data.AWS_SECRET_ACCESS_KEY) {\n return data.AWS_ACCESS_KEY_ID && data.AWS_SECRET_ACCESS_KEY;\n }\n return true;\n },\n {\n message: 'Both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be provided together',\n path: ['AWS_ACCESS_KEY_ID'],\n },\n );\n\nexport type EnvConfig = z.infer<typeof envSchema>;\n","import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod';\nimport { type EnvConfig, envSchema } from './imagineEnvSchema';\n\n/**\n * Centralized configuration service for imagine-mcp\n *\n * Validates environment variables at startup and provides\n * type-safe access to configuration throughout the application.\n */\nclass ConfigService {\n private static instance: ConfigService;\n private config: EnvConfig;\n\n private constructor() {\n try {\n this.config = envSchema.parse(process.env);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const errorMessage = error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join('; ');\n throw new McpError(ErrorCode.InternalError, `Environment configuration validation failed: ${errorMessage}`);\n }\n throw error;\n }\n }\n\n /**\n * Get the singleton instance of ConfigService\n */\n public static getInstance(): ConfigService {\n if (!ConfigService.instance) {\n ConfigService.instance = new ConfigService();\n }\n return ConfigService.instance;\n }\n\n /**\n * Get the validated configuration\n */\n public getConfig(): EnvConfig {\n return this.config;\n }\n\n /**\n * Get S3 configuration\n */\n public getS3Config() {\n return {\n bucket: this.config.S3_BUCKET,\n accessKeyId: this.config.AWS_ACCESS_KEY_ID,\n secretAccessKey: this.config.AWS_SECRET_ACCESS_KEY,\n region: this.config.S3_REGION,\n endpoint: this.config.S3_ENDPOINT,\n };\n }\n\n /**\n * Get Cloudflare R2 configuration\n */\n public getCloudflareConfig() {\n return {\n bucket: this.config.CLOUDFLARE_R2_BUCKET,\n accessKeyId: this.config.CLOUDFLARE_R2_ACCESS_KEY_ID,\n secretAccessKey: this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY,\n region: this.config.CLOUDFLARE_R2_REGION,\n endpoint: this.config.CLOUDFLARE_R2_ENDPOINT,\n };\n }\n\n /**\n * Get Google Cloud Storage configuration\n */\n public getGCloudConfig() {\n return {\n bucket: this.config.GCLOUD_BUCKET,\n projectId: this.config.GCLOUD_PROJECT_ID,\n credentialsPath: this.config.GCLOUD_CREDENTIALS_PATH,\n };\n }\n\n /**\n * Get the default upload service\n */\n public getUploadService() {\n return this.config.UPLOAD_SERVICE;\n }\n\n /**\n * Get Unsplash API configuration\n */\n public getUnsplashConfig() {\n return {\n accessKey: this.config.UNSPLASH_ACCESS_KEY,\n };\n }\n\n /**\n * Validate that required credentials exist for a specific service\n */\n public validateServiceConfig(service: 's3' | 'cloudflare' | 'gcloud'): void {\n switch (service) {\n case 's3':\n if (!this.config.S3_BUCKET) {\n throw new McpError(ErrorCode.InvalidParams, 'S3_BUCKET is required for S3 upload service');\n }\n break;\n case 'cloudflare':\n if (!this.config.CLOUDFLARE_R2_BUCKET) {\n throw new McpError(ErrorCode.InvalidParams, 'CLOUDFLARE_R2_BUCKET is required for Cloudflare upload service');\n }\n if (!this.config.CLOUDFLARE_R2_ACCESS_KEY_ID || !this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY) {\n throw new McpError(\n ErrorCode.InvalidParams,\n 'CLOUDFLARE_R2_ACCESS_KEY_ID and CLOUDFLARE_R2_SECRET_ACCESS_KEY are required for Cloudflare upload service',\n );\n }\n if (!this.config.CLOUDFLARE_R2_ENDPOINT) {\n throw new McpError(\n ErrorCode.InvalidParams,\n 'CLOUDFLARE_R2_ENDPOINT is required for Cloudflare upload service',\n );\n }\n break;\n case 'gcloud':\n if (!this.config.GCLOUD_BUCKET) {\n throw new McpError(ErrorCode.InvalidParams, 'GCLOUD_BUCKET is required for Google Cloud upload service');\n }\n if (!this.config.GCLOUD_PROJECT_ID) {\n throw new McpError(ErrorCode.InvalidParams, 'GCLOUD_PROJECT_ID is required for Google Cloud upload service');\n }\n break;\n }\n }\n}\n\n// Export singleton instance\nexport const config = ConfigService.getInstance();\n","/**\n * UnsplashService\n *\n * DESIGN PATTERNS:\n * - Service pattern for business logic encapsulation\n * - Single responsibility principle\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Keep methods focused and well-named\n * - Document complex logic with comments\n *\n * AVOID:\n * - Mixing concerns (keep focused on single domain)\n * - Direct tool implementation (services should be tool-agnostic)\n */\n\nimport { createApi } from 'unsplash-js';\nimport { config } from '../config.js';\n\nexport interface UnsplashImage {\n id: string;\n description: string | null;\n alt_description: string | null;\n urls: {\n raw: string;\n full: string;\n regular: string;\n small: string;\n thumb: string;\n };\n links: {\n html: string;\n download: string;\n };\n user: {\n name: string;\n username: string;\n portfolio_url: string | null;\n };\n width: number;\n height: number;\n color: string;\n likes: number;\n}\n\nexport interface SearchImagesParams {\n query: string;\n perPage?: number;\n page?: number;\n orientation?: 'landscape' | 'portrait' | 'squarish';\n color?:\n | 'black_and_white'\n | 'black'\n | 'white'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'magenta'\n | 'green'\n | 'teal'\n | 'blue';\n}\n\nexport class UnsplashService {\n private api: ReturnType<typeof createApi>;\n\n constructor(accessKey?: string) {\n const unsplashConfig = config.getUnsplashConfig();\n const apiKey = accessKey || unsplashConfig.accessKey;\n\n if (!apiKey) {\n throw new Error('Unsplash API key is required. Set UNSPLASH_ACCESS_KEY environment variable.');\n }\n\n this.api = createApi({\n accessKey: apiKey,\n });\n }\n\n async searchImages(params: SearchImagesParams): Promise<UnsplashImage[]> {\n try {\n const result = await this.api.search.getPhotos({\n query: params.query,\n page: params.page || 1,\n perPage: params.perPage || 10,\n orientation: params.orientation,\n color: params.color,\n });\n\n if (result.errors) {\n throw new Error(`Unsplash API error: ${result.errors.join(', ')}`);\n }\n\n if (!result.response) {\n throw new Error('No response from Unsplash API');\n }\n\n return result.response.results.map((photo) => ({\n id: photo.id,\n description: photo.description,\n alt_description: photo.alt_description,\n urls: {\n raw: photo.urls.raw,\n full: photo.urls.full,\n regular: photo.urls.regular,\n small: photo.urls.small,\n thumb: photo.urls.thumb,\n },\n links: {\n html: photo.links.html,\n download: photo.links.download,\n },\n user: {\n name: photo.user.name,\n username: photo.user.username,\n portfolio_url: photo.user.portfolio_url,\n },\n width: photo.width,\n height: photo.height,\n color: photo.color || '#000000',\n likes: photo.likes,\n }));\n } catch (error) {\n throw new Error(`Failed to search Unsplash images: ${error instanceof Error ? error.message : 'Unknown error'}`, {\n cause: error,\n });\n }\n }\n}\n","/**\n * UnsplashSearchTool\n *\nhttps://github.com/BoomLinkAi/image-worker-mcp * DESIGN PATTERNS:\n * - Tool pattern with getDefinition() and execute() methods\n * - Service delegation for business logic\n * - JSON Schema validation for inputs\n *\n * CODING STANDARDS:\n * - Implement Tool interface from ../types\n * - Use TOOL_NAME constant with snake_case (e.g., 'file_read')\n * - Return CallToolResult with content array\n * - Handle errors with isError flag\n * - Delegate complex logic to services\n *\n * AVOID:\n * - Complex business logic in execute method\n * - Unhandled promise rejections\n * - Missing input validation\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { UnsplashService } from '../services/UnsplashService';\nimport type { Tool, ToolDefinition } from '../types/index';\n\nexport interface UnsplashSearchToolInput {\n query: string;\n perPage?: number;\n page?: number;\n orientation?: 'landscape' | 'portrait' | 'squarish';\n color?:\n | 'black_and_white'\n | 'black'\n | 'white'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'magenta'\n | 'green'\n | 'teal'\n | 'blue';\n}\n\nexport class UnsplashSearchTool implements Tool<UnsplashSearchToolInput> {\n static readonly TOOL_NAME = 'unsplash_search';\n\n private service: UnsplashService | null;\n private readonly accessKey?: string;\n\n constructor(accessKey?: string) {\n this.accessKey = accessKey;\n this.service = null;\n }\n\n private getService(): UnsplashService {\n if (!this.service) {\n this.service = new UnsplashService(this.accessKey);\n }\n return this.service;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: UnsplashSearchTool.TOOL_NAME,\n description: 'Search for stock images from Unsplash by keyword and return image URLs with metadata',\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Search query (e.g., \"sunset\", \"technology\", \"nature\")',\n },\n perPage: {\n type: 'number',\n description: 'Number of results per page (1-30, default: 10)',\n minimum: 1,\n maximum: 30,\n },\n page: {\n type: 'number',\n description: 'Page number for pagination (default: 1)',\n minimum: 1,\n },\n orientation: {\n type: 'string',\n enum: ['landscape', 'portrait', 'squarish'],\n description: 'Filter by photo orientation',\n },\n color: {\n type: 'string',\n enum: [\n 'black_and_white',\n 'black',\n 'white',\n 'yellow',\n 'orange',\n 'red',\n 'purple',\n 'magenta',\n 'green',\n 'teal',\n 'blue',\n ],\n description: 'Filter by photo color',\n },\n },\n required: ['query'],\n additionalProperties: false,\n },\n };\n }\n\n async execute(input: UnsplashSearchToolInput): Promise<CallToolResult> {\n try {\n const images = await this.getService().searchImages({\n query: input.query,\n perPage: input.perPage,\n page: input.page,\n orientation: input.orientation,\n color: input.color,\n });\n\n if (images.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: `No images found for query: \"${input.query}\"`,\n },\n ],\n };\n }\n\n const formattedResults = images\n .map((img, index) => {\n return `\n📷 **Image ${index + 1}**\n- **ID**: ${img.id}\n- **Description**: ${img.alt_description || img.description || 'No description'}\n- **Photographer**: ${img.user.name} (@${img.user.username})\n- **Dimensions**: ${img.width}x${img.height}px\n- **Color**: ${img.color}\n- **Likes**: ${img.likes}\n\n**URLs**:\n- Full: ${img.urls.full}\n- Regular: ${img.urls.regular}\n- Small: ${img.urls.small}\n- Thumbnail: ${img.urls.thumb}\n\n**Links**:\n- View on Unsplash: ${img.links.html}\n- Download: ${img.links.download}\n${img.user.portfolio_url ? `- Photographer Portfolio: ${img.user.portfolio_url}` : ''}\n`.trim();\n })\n .join('\\n\\n' + '-'.repeat(80) + '\\n\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Found ${images.length} image(s) for \"${input.query}\":\\n\\n${formattedResults}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n}\n","/**\n * MCP Server Setup\n *\n * DESIGN PATTERNS:\n * - Factory pattern for server creation\n * - Tool registration pattern\n *\n * CODING STANDARDS:\n * - Register all tools, resources, and prompts here\n * - Keep server setup modular and extensible\n * - Import tools from ../tools/ and register them in the handlers\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { ReadImageTool, type ReadImageToolInput, UnsplashSearchTool, type UnsplashSearchToolInput } from '../tools';\n\nexport function createServer(): Server {\n const server = new Server(\n {\n name: 'imagine-mcp',\n version: '0.1.0',\n },\n {\n capabilities: {\n tools: {},\n },\n },\n );\n\n // Initialize tools\n const unsplashSearchTool = new UnsplashSearchTool();\n const readImageTool = new ReadImageTool();\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [unsplashSearchTool.getDefinition(), readImageTool.getDefinition()],\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n if (name === UnsplashSearchTool.TOOL_NAME) {\n return await unsplashSearchTool.execute(args as unknown as UnsplashSearchToolInput);\n }\n if (name === ReadImageTool.TOOL_NAME) {\n return await readImageTool.execute(args as unknown as ReadImageToolInput);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: `Unknown tool: ${name}`,\n },\n ],\n isError: true,\n };\n });\n\n return server;\n}\n","/**\n * HTTP Transport Handler\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Session management for stateful connections\n * - Streamable HTTP protocol (2025-03-26) with resumability support\n * - Factory pattern for creating MCP server instances per session\n *\n * CODING STANDARDS:\n * - Use async/await for all asynchronous operations\n * - Implement proper session lifecycle management\n * - Handle errors gracefully with appropriate HTTP status codes\n * - Provide health check endpoint for monitoring\n * - Clean up resources on shutdown\n *\n * AVOID:\n * - Sharing MCP server instances across sessions (use factory pattern)\n * - Forgetting to clean up sessions on disconnect\n * - Missing error handling for request processing\n * - Hardcoded configuration (use TransportConfig)\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';\nimport express, { type Request, type Response } from 'express';\nimport type { HttpTransportHandler as IHttpTransportHandler, TransportConfig } from '../types/index.js';\n\n/**\n * Session data for HTTP connections\n */\ninterface HttpSession {\n transport: StreamableHTTPServerTransport;\n server: McpServer;\n}\n\n/**\n * HTTP session manager\n */\nclass HttpFullSessionManager {\n private sessions: Map<string, HttpSession> = new Map();\n\n getSession(sessionId: string): HttpSession | undefined {\n return this.sessions.get(sessionId);\n }\n\n setSession(sessionId: string, transport: StreamableHTTPServerTransport, server: McpServer): void {\n this.sessions.set(sessionId, { transport, server });\n }\n\n deleteSession(sessionId: string): void {\n const session = this.sessions.get(sessionId);\n if (session) {\n session.server.close();\n }\n this.sessions.delete(sessionId);\n }\n\n hasSession(sessionId: string): boolean {\n return this.sessions.has(sessionId);\n }\n\n clear(): void {\n for (const session of this.sessions.values()) {\n session.server.close();\n }\n this.sessions.clear();\n }\n}\n\n/**\n * HTTP transport handler using Streamable HTTP (protocol version 2025-03-26)\n * Provides stateful session management with resumability support\n */\nexport class HttpTransportHandler implements IHttpTransportHandler {\n private serverFactory: () => McpServer;\n private app: express.Application;\n private server: HttpServer | null = null;\n private sessionManager: HttpFullSessionManager;\n private config: Required<TransportConfig>;\n\n constructor(serverFactory: McpServer | (() => McpServer), config: TransportConfig) {\n // Support both a factory function and a direct server instance for backwards compatibility\n this.serverFactory = typeof serverFactory === 'function' ? serverFactory : () => serverFactory;\n this.app = express();\n this.sessionManager = new HttpFullSessionManager();\n this.config = {\n mode: config.mode,\n port: config.port ?? 3000,\n host: config.host ?? 'localhost',\n };\n\n this.setupMiddleware();\n this.setupRoutes();\n }\n\n private setupMiddleware(): void {\n this.app.use(express.json());\n }\n\n private setupRoutes(): void {\n // Handle POST requests for client-to-server communication\n this.app.post('/mcp', async (req: Request, res: Response) => {\n await this.handlePostRequest(req, res);\n });\n\n // Handle GET requests for server-to-client notifications via SSE\n this.app.get('/mcp', async (req: Request, res: Response) => {\n await this.handleGetRequest(req, res);\n });\n\n // Handle DELETE requests for session termination\n this.app.delete('/mcp', async (req: Request, res: Response) => {\n await this.handleDeleteRequest(req, res);\n });\n\n // Health check endpoint\n this.app.get('/health', (_req: Request, res: Response) => {\n res.json({ status: 'ok', transport: 'http' });\n });\n }\n\n private async handlePostRequest(req: Request, res: Response): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n let transport: StreamableHTTPServerTransport;\n\n if (sessionId && this.sessionManager.hasSession(sessionId)) {\n // Reuse existing transport\n const session = this.sessionManager.getSession(sessionId)!;\n transport = session.transport;\n } else if (!sessionId && isInitializeRequest(req.body)) {\n // New initialization request - create new server instance\n const mcpServer = this.serverFactory();\n\n transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n enableJsonResponse: true, // Return JSON instead of SSE for simple request/response\n onsessioninitialized: (sessionId) => {\n this.sessionManager.setSession(sessionId, transport, mcpServer);\n },\n });\n\n // Clean up transport when closed\n transport.onclose = () => {\n if (transport.sessionId) {\n this.sessionManager.deleteSession(transport.sessionId);\n }\n };\n\n // Connect the new MCP server instance to the transport\n await mcpServer.connect(transport);\n } else {\n // Invalid request\n res.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Bad Request: No valid session ID provided',\n },\n id: null,\n });\n return;\n }\n\n // Handle the request\n await transport.handleRequest(req, res, req.body);\n }\n\n private async handleGetRequest(req: Request, res: Response): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n if (!sessionId || !this.sessionManager.hasSession(sessionId)) {\n res.status(400).send('Invalid or missing session ID');\n return;\n }\n\n const session = this.sessionManager.getSession(sessionId)!;\n await session.transport.handleRequest(req, res);\n }\n\n private async handleDeleteRequest(req: Request, res: Response): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n if (!sessionId || !this.sessionManager.hasSession(sessionId)) {\n res.status(400).send('Invalid or missing session ID');\n return;\n }\n\n const session = this.sessionManager.getSession(sessionId)!;\n await session.transport.handleRequest(req, res);\n\n // Clean up session\n this.sessionManager.deleteSession(sessionId);\n }\n\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.server = this.app.listen(this.config.port, this.config.host, () => {\n process.stderr.write(\n `imagine-mcp MCP server started on http://${this.config.host}:${this.config.port}/mcp\\n`,\n );\n process.stderr.write(`Health check: http://${this.config.host}:${this.config.port}/health\\n`);\n resolve();\n });\n\n this.server.on('error', (error: Error) => {\n reject(error);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.server) {\n // Clear all sessions\n this.sessionManager.clear();\n\n this.server.close((err?: Error) => {\n if (err) {\n reject(err);\n } else {\n this.server = null;\n resolve();\n }\n });\n } else {\n resolve();\n }\n });\n }\n\n getPort(): number {\n return this.config.port;\n }\n\n getHost(): string {\n return this.config.host;\n }\n}\n","/**\n * SSE Transport Handler\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Session management for stateful SSE connections\n * - Legacy SSE protocol (2024-11-05) with separate endpoints\n * - Factory pattern for creating MCP server instances per connection\n *\n * CODING STANDARDS:\n * - Use async/await for all asynchronous operations\n * - Implement proper session lifecycle management\n * - Handle connection cleanup on client disconnect\n * - Provide health check endpoint for monitoring\n * - Clean up resources on shutdown\n *\n * AVOID:\n * - Sharing MCP server instances across sessions (use factory pattern)\n * - Forgetting to clean up sessions on disconnect\n * - Missing error handling for connection/message processing\n * - Hardcoded configuration (use TransportConfig)\n */\n\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js';\nimport { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\nimport express, { type Request, type Response } from 'express';\nimport type { HttpTransportHandler as IHttpTransportHandler, TransportConfig } from '../types/index.js';\n\n/**\n * Session data for SSE connections\n */\ninterface SseSession {\n transport: SSEServerTransport;\n server: McpServer;\n}\n\n/**\n * Session manager for SSE transports\n */\nclass SseSessionManager {\n private sessions: Map<string, SseSession> = new Map();\n\n getSession(sessionId: string): SSEServerTransport | undefined {\n return this.sessions.get(sessionId)?.transport;\n }\n\n setSession(sessionId: string, transport: SSEServerTransport, server: McpServer): void {\n this.sessions.set(sessionId, { transport, server });\n }\n\n deleteSession(sessionId: string): void {\n const session = this.sessions.get(sessionId);\n if (session) {\n // Close the server instance\n session.server.close();\n }\n this.sessions.delete(sessionId);\n }\n\n hasSession(sessionId: string): boolean {\n return this.sessions.has(sessionId);\n }\n\n clear(): void {\n // Close all server instances\n for (const session of this.sessions.values()) {\n session.server.close();\n }\n this.sessions.clear();\n }\n}\n\n/**\n * SSE (Server-Sent Events) transport handler\n * Legacy transport for backwards compatibility (protocol version 2024-11-05)\n * Uses separate endpoints: /sse for SSE stream (GET) and /messages for client messages (POST)\n */\nexport class SseTransportHandler implements IHttpTransportHandler {\n private serverFactory: () => McpServer;\n private app: express.Application;\n private server: HttpServer | null = null;\n private sessionManager: SseSessionManager;\n private config: Required<TransportConfig>;\n\n constructor(serverFactory: McpServer | (() => McpServer), config: TransportConfig) {\n // Support both a factory function and a direct server instance for backwards compatibility\n this.serverFactory = typeof serverFactory === 'function' ? serverFactory : () => serverFactory;\n this.app = express();\n this.sessionManager = new SseSessionManager();\n this.config = {\n mode: config.mode,\n port: config.port ?? 3000,\n host: config.host ?? 'localhost',\n };\n\n this.setupMiddleware();\n this.setupRoutes();\n }\n\n private setupMiddleware(): void {\n this.app.use(express.json());\n }\n\n private setupRoutes(): void {\n // SSE endpoint - establishes the SSE stream\n this.app.get('/sse', async (req: Request, res: Response) => {\n await this.handleSseConnection(req, res);\n });\n\n // Messages endpoint - receives client messages\n this.app.post('/messages', async (req: Request, res: Response) => {\n await this.handlePostMessage(req, res);\n });\n\n // Health check endpoint\n this.app.get('/health', (_req: Request, res: Response) => {\n res.json({ status: 'ok', transport: 'sse' });\n });\n }\n\n private async handleSseConnection(_req: Request, res: Response): Promise<void> {\n try {\n // Create a new MCP server instance for this SSE connection\n const mcpServer = this.serverFactory();\n\n // Create SSE transport\n const transport = new SSEServerTransport('/messages', res);\n\n // Store the transport and server\n this.sessionManager.setSession(transport.sessionId, transport, mcpServer);\n\n // Clean up when connection closes\n res.on('close', () => {\n this.sessionManager.deleteSession(transport.sessionId);\n });\n\n // Connect the new server instance to the transport\n await mcpServer.connect(transport);\n\n process.stderr.write(`SSE session established: ${transport.sessionId}\\n`);\n } catch (error) {\n process.stderr.write(\n `Error handling SSE connection: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n if (!res.headersSent) {\n res.status(500).send('Internal Server Error');\n }\n }\n }\n\n private async handlePostMessage(req: Request, res: Response): Promise<void> {\n const sessionId = req.query.sessionId as string;\n\n if (!sessionId) {\n res.status(400).send('Missing sessionId query parameter');\n return;\n }\n\n const transport = this.sessionManager.getSession(sessionId);\n\n if (!transport) {\n res.status(404).send('No transport found for sessionId');\n return;\n }\n\n try {\n await transport.handlePostMessage(req, res, req.body);\n } catch (error) {\n process.stderr.write(`Error handling post message: ${error instanceof Error ? error.message : String(error)}\\n`);\n if (!res.headersSent) {\n res.status(500).send('Internal Server Error');\n }\n }\n }\n\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.server = this.app.listen(this.config.port, this.config.host, () => {\n process.stderr.write(\n `imagine-mcp MCP server started with SSE transport on http://${this.config.host}:${this.config.port}\\n`,\n );\n process.stderr.write(`SSE endpoint: http://${this.config.host}:${this.config.port}/sse\\n`);\n process.stderr.write(`Messages endpoint: http://${this.config.host}:${this.config.port}/messages\\n`);\n process.stderr.write(`Health check: http://${this.config.host}:${this.config.port}/health\\n`);\n resolve();\n });\n\n this.server.on('error', (error: Error) => {\n reject(error);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.server) {\n // Clear all sessions\n this.sessionManager.clear();\n\n this.server.close((err?: Error) => {\n if (err) {\n reject(err);\n } else {\n this.server = null;\n resolve();\n }\n });\n } else {\n resolve();\n }\n });\n }\n\n getPort(): number {\n return this.config.port;\n }\n\n getHost(): string {\n return this.config.host;\n }\n}\n","/**\n * STDIO Transport\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Standard I/O based communication for CLI usage\n *\n * CODING STANDARDS:\n * - Initialize server and transport properly\n * - Handle cleanup on shutdown with stop() method\n * - Use async/await for all operations\n *\n * AVOID:\n * - Forgetting to close transport on shutdown\n * - Missing error handling for connection failures\n */\n\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { TransportHandler } from '../types/index.js';\n\n/**\n * Stdio transport handler for MCP server\n * Used for command-line and direct integrations\n */\nexport class StdioTransportHandler implements TransportHandler {\n private server: Server;\n private transport: StdioServerTransport | null = null;\n\n constructor(server: Server) {\n this.server = server;\n }\n\n async start(): Promise<void> {\n this.transport = new StdioServerTransport();\n await this.server.connect(this.transport);\n process.stderr.write('imagine-mcp MCP server started on stdio\\n');\n }\n\n async stop(): Promise<void> {\n if (this.transport) {\n await this.transport.close();\n this.transport = null;\n }\n }\n}\n"],"mappings":"syBAuEA,SAAgB,EAAkB,EAA0B,CAE1D,IAAI,EAAiB,EAAS,QAAQ,QAAS,IAAI,CAcnD,MAXA,GAAiB,EACd,QAAQ,QAAS,IAAI,CACrB,QAAQ,QAAS,IAAI,CACrB,QAAQ,QAAS,IAAI,CACrB,QAAQ,SAAU,IAAI,CACtB,QAAQ,SAAU,IAAI,CACtB,QAAQ,SAAU,IAAI,CACtB,QAAQ,SAAU,IAAI,CACtB,QAAQ,SAAU,IAAI,CACtB,QAAQ,SAAU,IAAI,CAElB,ECxDT,MAAM,EAAoB,2BACpBA,EAA4C,CAChD,OAAQ,YACR,OAAQ,aACR,QAAS,aACT,OAAQ,YACR,QAAS,aACT,OAAQ,YACR,OAAQ,gBACR,QAAS,aACT,QAAS,aACV,CAED,IAAa,EAAb,KAAyB,CACvB,MAAM,UAAU,EAAgB,EAA4B,EAAE,CAA4B,CACxF,IAAM,EAAc,MAAM,KAAK,mBAAmB,EAAO,CACnD,EAAS,EAAW,SAAS,CAAC,OAAO,EAAY,KAAK,CAAC,OAAO,MAAM,CAEpEC,EAA0B,CAC9B,OAAQ,EAAY,OACpB,WAAY,EAAY,WACxB,SAAU,EAAY,SACtB,UAAW,EAAY,UACvB,SACD,CAUD,OARI,EAAY,aAAe,aAC7B,EAAO,SAAW,EAAS,EAAY,OAAO,EAG5C,EAAQ,gBACV,EAAO,OAAS,EAAY,KAAK,SAAS,SAAS,EAG9C,EAGT,MAAc,mBAAmB,EAAsC,CACrE,IAAM,EAAU,EAAO,MAAM,CAE7B,GAAI,CAAC,EACH,MAAM,IAAI,EAAS,EAAU,cAAe,gCAAgC,CAW9E,OARI,EAAQ,WAAW,QAAQ,CACtB,KAAK,eAAe,EAAQ,CAGjC,gBAAgB,KAAK,EAAQ,CACxB,KAAK,mBAAmB,EAAQ,CAGlC,KAAK,YAAY,EAAQ,CAGlC,MAAc,YAAY,EAAwC,CAChE,IAAM,EAAiB,EAAQ,EAAkB,EAAS,CAAC,CAE3D,GAAI,CACF,MAAM,EAAO,EAAgB,EAAU,KAAK,MACtC,CACN,MAAM,IAAI,EAAS,EAAU,cAAe,iCAAiC,IAAW,CAG1F,IAAIC,EACJ,GAAI,CACF,EAAO,MAAM,EAAS,EAAe,OAC9B,EAAO,CACd,MAAM,IAAI,EACR,EAAU,cACV,8BAA8B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GACrF,CAMH,MAAO,CACL,OAAQ,EACR,WAAY,OACZ,SALe,EADC,EAAQ,EAAe,CAAC,aAAa,GACN,EAM/C,UAAW,EAAK,OAChB,OACD,CAGH,MAAc,mBAAmB,EAAmC,CAClE,GAAI,CACF,IAAM,EAAW,MAAM,EAAM,EAAK,CAAE,SAAU,SAAU,CAAC,CACzD,GAAI,CAAC,EAAS,GACZ,MAAM,IAAI,EACR,EAAU,cACV,mCAAmC,EAAI,IAAI,EAAS,OAAO,GAAG,EAAS,WAAW,GACnF,CAGH,IAAM,EAAc,MAAM,EAAS,aAAa,CAC1C,EAAO,OAAO,KAAK,EAAY,CAC/B,EAAc,EAAS,QAAQ,IAAI,eAAe,EAAE,MAAM,IAAI,CAAC,GAC/D,EAAW,EAAS,IAAI,IAAI,EAAI,CAAC,UAAY,QAAQ,CAE3D,MAAO,CACL,OAAQ,EACR,WAAY,MACZ,SAAU,GAAe,EAAkB,EAAQ,EAAS,GAAK,EACjE,UAAW,EAAK,OAChB,OACD,OACM,EAAO,CAId,MAHI,aAAiB,EACb,EAEF,IAAI,EACR,EAAU,cACV,sCAAsC,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAC7F,EAIL,eAAuB,EAA8B,CACnD,IAAM,EAAa,EAAQ,QAAQ,IAAI,CACvC,GAAI,IAAe,IAAM,IAAe,EAAQ,OAAS,EACvD,MAAM,IAAI,EACR,EAAU,cACV,sEACD,CAGH,IAAM,EAAW,EAAQ,MAAM,EAAG,EAAW,CAAC,aAAa,CACrD,EAAU,EAAQ,MAAM,EAAa,EAAE,CACvC,EAAW,EAAS,SAAS,SAAS,CACtC,EAAmB,EAAS,MAAM,IAAI,CAAC,IAAM,EAEnD,GAAI,CACF,IAAM,EAAO,EAAW,OAAO,KAAK,EAAS,SAAS,CAAG,OAAO,KAAK,mBAAmB,EAAQ,CAAC,CACjG,MAAO,CACL,OAAQ,EACR,WAAY,WACZ,SAAU,GAAoB,EAC9B,UAAW,EAAK,OAChB,OACD,OACM,EAAO,CACd,MAAM,IAAI,EACR,EAAU,cACV,sCAAsC,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAC7F,ICxKM,EAAb,MAAa,CAAkD,CAC7D,OAAgB,UAAY,aAE5B,QAAkB,IAAI,EAEtB,eAAgC,CAC9B,MAAO,CACL,KAAM,EAAc,UACpB,YAAa,yEACb,YAAa,CACX,KAAM,SACN,WAAY,CACV,OAAQ,CACN,KAAM,SACN,UAAW,EACX,YAAa,0DACd,CACD,cAAe,CACb,KAAM,UACN,YAAa,4CACb,QAAS,GACV,CACF,CACD,SAAU,CAAC,SAAS,CACpB,qBAAsB,GACvB,CACF,CAGH,MAAM,QAAQ,EAAoD,CAChE,GAAI,CACF,GAAI,CAAC,GAAS,OAAO,GAAU,SAC7B,MAAM,IAAI,EAAS,EAAU,cAAe,gCAAgC,CAG9E,IAAM,EAAU,EACV,EAAS,EAAQ,OACjB,EAAgB,EAAQ,cAC9B,GAAI,CAAC,GAAU,OAAO,GAAW,UAAY,EAAO,MAAM,CAAC,SAAW,EACpE,MAAM,IAAI,EAAS,EAAU,cAAe,qCAAqC,CAEnF,GAAI,IAAkB,IAAA,IAAa,OAAO,GAAkB,UAC1D,MAAM,IAAI,EAAS,EAAU,cAAe,mCAAmC,CAGjF,IAAMC,EAA4B,CAChC,gBACD,CACK,EAAS,MAAM,KAAK,QAAQ,UAAU,EAAQ,EAAQ,CAE5D,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,KAAK,UAAU,EAAQ,KAAM,EAAE,CACtC,CACF,CACF,OACM,EAAO,CACd,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,UAAU,aAAiB,MAAQ,EAAM,QAAU,kBAC1D,CACF,CACD,QAAS,GACV,ICnEP,MAAa,EAAY,EACtB,OAAO,CAEN,eAAgB,EAAE,KAAK,CAAC,KAAM,aAAc,SAAS,CAAC,CAAC,UAAU,CAAC,QAAQ,KAAK,CAG/E,UAAW,EAAE,QAAQ,CAAC,UAAU,CAChC,kBAAmB,EAAE,QAAQ,CAAC,UAAU,CACxC,sBAAuB,EAAE,QAAQ,CAAC,UAAU,CAC5C,UAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,YAAY,CACrD,YAAa,EAAE,QAAQ,CAAC,IAAI,kCAAkC,CAAC,UAAU,CAGzE,qBAAsB,EAAE,QAAQ,CAAC,UAAU,CAC3C,4BAA6B,EAAE,QAAQ,CAAC,UAAU,CAClD,gCAAiC,EAAE,QAAQ,CAAC,UAAU,CACtD,qBAAsB,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,OAAO,CAC3D,uBAAwB,EAAE,QAAQ,CAAC,IAAI,6CAA6C,CAAC,UAAU,CAG/F,cAAe,EAAE,QAAQ,CAAC,UAAU,CACpC,kBAAmB,EAAE,QAAQ,CAAC,UAAU,CACxC,wBAAyB,EAAE,QAAQ,CAAC,UAAU,CAG9C,oBAAqB,EAAE,QAAQ,CAAC,UAAU,CAC3C,CAAC,CACD,OACE,GAEK,EAAK,mBAAqB,EAAK,sBAC1B,EAAK,mBAAqB,EAAK,sBAEjC,GAET,CACE,QAAS,6EACT,KAAM,CAAC,oBAAoB,CAC5B,CACF,CCwFU,EA9Hb,MAAM,CAAc,CAClB,OAAe,SACf,OAEA,aAAsB,CACpB,GAAI,CACF,KAAK,OAAS,EAAU,MAAM,QAAQ,IAAI,OACnC,EAAO,CACd,GAAI,aAAiB,EAAE,SAAU,CAC/B,IAAM,EAAe,EAAM,OAAO,IAAK,GAAM,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,KAAK,KAAK,CAC5F,MAAM,IAAI,EAAS,EAAU,cAAe,gDAAgD,IAAe,CAE7G,MAAM,GAOV,OAAc,aAA6B,CAIzC,MAHA,CACE,EAAc,WAAW,IAAI,EAExB,EAAc,SAMvB,WAA8B,CAC5B,OAAO,KAAK,OAMd,aAAqB,CACnB,MAAO,CACL,OAAQ,KAAK,OAAO,UACpB,YAAa,KAAK,OAAO,kBACzB,gBAAiB,KAAK,OAAO,sBAC7B,OAAQ,KAAK,OAAO,UACpB,SAAU,KAAK,OAAO,YACvB,CAMH,qBAA6B,CAC3B,MAAO,CACL,OAAQ,KAAK,OAAO,qBACpB,YAAa,KAAK,OAAO,4BACzB,gBAAiB,KAAK,OAAO,gCAC7B,OAAQ,KAAK,OAAO,qBACpB,SAAU,KAAK,OAAO,uBACvB,CAMH,iBAAyB,CACvB,MAAO,CACL,OAAQ,KAAK,OAAO,cACpB,UAAW,KAAK,OAAO,kBACvB,gBAAiB,KAAK,OAAO,wBAC9B,CAMH,kBAA0B,CACxB,OAAO,KAAK,OAAO,eAMrB,mBAA2B,CACzB,MAAO,CACL,UAAW,KAAK,OAAO,oBACxB,CAMH,sBAA6B,EAA+C,CAC1E,OAAQ,EAAR,CACE,IAAK,KACH,GAAI,CAAC,KAAK,OAAO,UACf,MAAM,IAAI,EAAS,EAAU,cAAe,8CAA8C,CAE5F,MACF,IAAK,aACH,GAAI,CAAC,KAAK,OAAO,qBACf,MAAM,IAAI,EAAS,EAAU,cAAe,iEAAiE,CAE/G,GAAI,CAAC,KAAK,OAAO,6BAA+B,CAAC,KAAK,OAAO,gCAC3D,MAAM,IAAI,EACR,EAAU,cACV,6GACD,CAEH,GAAI,CAAC,KAAK,OAAO,uBACf,MAAM,IAAI,EACR,EAAU,cACV,mEACD,CAEH,MACF,IAAK,SACH,GAAI,CAAC,KAAK,OAAO,cACf,MAAM,IAAI,EAAS,EAAU,cAAe,4DAA4D,CAE1G,GAAI,CAAC,KAAK,OAAO,kBACf,MAAM,IAAI,EAAS,EAAU,cAAe,gEAAgE,CAE9G,SAM4B,aAAa,CCtEjD,IAAa,EAAb,KAA6B,CAC3B,IAEA,YAAY,EAAoB,CAC9B,IAAM,EAAiB,EAAO,mBAAmB,CAC3C,EAAS,GAAa,EAAe,UAE3C,GAAI,CAAC,EACH,MAAU,MAAM,8EAA8E,CAGhG,KAAK,IAAM,EAAU,CACnB,UAAW,EACZ,CAAC,CAGJ,MAAM,aAAa,EAAsD,CACvE,GAAI,CACF,IAAM,EAAS,MAAM,KAAK,IAAI,OAAO,UAAU,CAC7C,MAAO,EAAO,MACd,KAAM,EAAO,MAAQ,EACrB,QAAS,EAAO,SAAW,GAC3B,YAAa,EAAO,YACpB,MAAO,EAAO,MACf,CAAC,CAEF,GAAI,EAAO,OACT,MAAU,MAAM,uBAAuB,EAAO,OAAO,KAAK,KAAK,GAAG,CAGpE,GAAI,CAAC,EAAO,SACV,MAAU,MAAM,gCAAgC,CAGlD,OAAO,EAAO,SAAS,QAAQ,IAAK,IAAW,CAC7C,GAAI,EAAM,GACV,YAAa,EAAM,YACnB,gBAAiB,EAAM,gBACvB,KAAM,CACJ,IAAK,EAAM,KAAK,IAChB,KAAM,EAAM,KAAK,KACjB,QAAS,EAAM,KAAK,QACpB,MAAO,EAAM,KAAK,MAClB,MAAO,EAAM,KAAK,MACnB,CACD,MAAO,CACL,KAAM,EAAM,MAAM,KAClB,SAAU,EAAM,MAAM,SACvB,CACD,KAAM,CACJ,KAAM,EAAM,KAAK,KACjB,SAAU,EAAM,KAAK,SACrB,cAAe,EAAM,KAAK,cAC3B,CACD,MAAO,EAAM,MACb,OAAQ,EAAM,OACd,MAAO,EAAM,OAAS,UACtB,MAAO,EAAM,MACd,EAAE,OACI,EAAO,CACd,MAAU,MAAM,qCAAqC,aAAiB,MAAQ,EAAM,QAAU,kBAAmB,CAC/G,MAAO,EACR,CAAC,ICpFK,EAAb,MAAa,CAA4D,CACvE,OAAgB,UAAY,kBAE5B,QACA,UAEA,YAAY,EAAoB,CAC9B,KAAK,UAAY,EACjB,KAAK,QAAU,KAGjB,YAAsC,CAIpC,MAHA,CACE,KAAK,UAAU,IAAI,EAAgB,KAAK,UAAU,CAE7C,KAAK,QAGd,eAAgC,CAC9B,MAAO,CACL,KAAM,EAAmB,UACzB,YAAa,uFACb,YAAa,CACX,KAAM,SACN,WAAY,CACV,MAAO,CACL,KAAM,SACN,YAAa,wDACd,CACD,QAAS,CACP,KAAM,SACN,YAAa,iDACb,QAAS,EACT,QAAS,GACV,CACD,KAAM,CACJ,KAAM,SACN,YAAa,0CACb,QAAS,EACV,CACD,YAAa,CACX,KAAM,SACN,KAAM,CAAC,YAAa,WAAY,WAAW,CAC3C,YAAa,8BACd,CACD,MAAO,CACL,KAAM,SACN,KAAM,CACJ,kBACA,QACA,QACA,SACA,SACA,MACA,SACA,UACA,QACA,OACA,OACD,CACD,YAAa,wBACd,CACF,CACD,SAAU,CAAC,QAAQ,CACnB,qBAAsB,GACvB,CACF,CAGH,MAAM,QAAQ,EAAyD,CACrE,GAAI,CACF,IAAM,EAAS,MAAM,KAAK,YAAY,CAAC,aAAa,CAClD,MAAO,EAAM,MACb,QAAS,EAAM,QACf,KAAM,EAAM,KACZ,YAAa,EAAM,YACnB,MAAO,EAAM,MACd,CAAC,CAEF,GAAI,EAAO,SAAW,EACpB,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,+BAA+B,EAAM,MAAM,GAClD,CACF,CACF,CAGH,IAAM,EAAmB,EACtB,KAAK,EAAK,IACF;aACJ,EAAQ,EAAE;YACX,EAAI,GAAG;qBACE,EAAI,iBAAmB,EAAI,aAAe,iBAAiB;sBAC1D,EAAI,KAAK,KAAK,KAAK,EAAI,KAAK,SAAS;oBACvC,EAAI,MAAM,GAAG,EAAI,OAAO;eAC7B,EAAI,MAAM;eACV,EAAI,MAAM;;;UAGf,EAAI,KAAK,KAAK;aACX,EAAI,KAAK,QAAQ;WACnB,EAAI,KAAK,MAAM;eACX,EAAI,KAAK,MAAM;;;sBAGR,EAAI,MAAM,KAAK;cACvB,EAAI,MAAM,SAAS;EAC/B,EAAI,KAAK,cAAgB,6BAA6B,EAAI,KAAK,gBAAkB,GAAG;EACpF,MAAM,CACE,CACD,KAAK;;EAAS,IAAI,OAAO,GAAG,CAAG;;EAAO,CAEzC,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,SAAS,EAAO,OAAO,iBAAiB,EAAM,MAAM,QAAQ,IACnE,CACF,CACF,OACM,EAAO,CACd,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,UAAU,aAAiB,MAAQ,EAAM,QAAU,kBAC1D,CACF,CACD,QAAS,GACV,IC/JP,SAAgB,GAAuB,CACrC,IAAM,EAAS,IAAI,EACjB,CACE,KAAM,cACN,QAAS,QACV,CACD,CACE,aAAc,CACZ,MAAO,EAAE,CACV,CACF,CACF,CAGK,EAAqB,IAAI,EACzB,EAAgB,IAAI,EA2B1B,OAzBA,EAAO,kBAAkB,EAAwB,UAAa,CAC5D,MAAO,CAAC,EAAmB,eAAe,CAAE,EAAc,eAAe,CAAC,CAC3E,EAAE,CAEH,EAAO,kBAAkB,EAAuB,KAAO,IAAY,CACjE,GAAM,CAAE,OAAM,UAAW,GAAS,EAAQ,OAS1C,OAPI,IAAS,EAAmB,UACvB,MAAM,EAAmB,QAAQ,EAA2C,CAEjF,IAAS,EAAc,UAClB,MAAM,EAAc,QAAQ,EAAsC,CAGpE,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,iBAAiB,IACxB,CACF,CACD,QAAS,GACV,EACD,CAEK,ECjBT,IAAM,EAAN,KAA6B,CAC3B,SAA6C,IAAI,IAEjD,WAAW,EAA4C,CACrD,OAAO,KAAK,SAAS,IAAI,EAAU,CAGrC,WAAW,EAAmB,EAA0C,EAAyB,CAC/F,KAAK,SAAS,IAAI,EAAW,CAAE,YAAW,SAAQ,CAAC,CAGrD,cAAc,EAAyB,CACrC,IAAM,EAAU,KAAK,SAAS,IAAI,EAAU,CACxC,GACF,EAAQ,OAAO,OAAO,CAExB,KAAK,SAAS,OAAO,EAAU,CAGjC,WAAW,EAA4B,CACrC,OAAO,KAAK,SAAS,IAAI,EAAU,CAGrC,OAAc,CACZ,IAAK,IAAM,KAAW,KAAK,SAAS,QAAQ,CAC1C,EAAQ,OAAO,OAAO,CAExB,KAAK,SAAS,OAAO,GAQZ,EAAb,KAAmE,CACjE,cACA,IACA,OAAoC,KACpC,eACA,OAEA,YAAY,EAA8C,EAAyB,CAEjF,KAAK,cAAgB,OAAO,GAAkB,WAAa,MAAsB,EACjF,KAAK,IAAM,GAAS,CACpB,KAAK,eAAiB,IAAI,EAC1B,KAAK,OAAS,CACZ,KAAMC,EAAO,KACb,KAAMA,EAAO,MAAQ,IACrB,KAAMA,EAAO,MAAQ,YACtB,CAED,KAAK,iBAAiB,CACtB,KAAK,aAAa,CAGpB,iBAAgC,CAC9B,KAAK,IAAI,IAAI,EAAQ,MAAM,CAAC,CAG9B,aAA4B,CAE1B,KAAK,IAAI,KAAK,OAAQ,MAAO,EAAc,IAAkB,CAC3D,MAAM,KAAK,kBAAkB,EAAK,EAAI,EACtC,CAGF,KAAK,IAAI,IAAI,OAAQ,MAAO,EAAc,IAAkB,CAC1D,MAAM,KAAK,iBAAiB,EAAK,EAAI,EACrC,CAGF,KAAK,IAAI,OAAO,OAAQ,MAAO,EAAc,IAAkB,CAC7D,MAAM,KAAK,oBAAoB,EAAK,EAAI,EACxC,CAGF,KAAK,IAAI,IAAI,WAAY,EAAe,IAAkB,CACxD,EAAI,KAAK,CAAE,OAAQ,KAAM,UAAW,OAAQ,CAAC,EAC7C,CAGJ,MAAc,kBAAkB,EAAc,EAA8B,CAC1E,IAAM,EAAY,EAAI,QAAQ,kBAC1BC,EAEJ,GAAI,GAAa,KAAK,eAAe,WAAW,EAAU,CAGxD,EADgB,KAAK,eAAe,WAAW,EAAU,CACrC,kBACX,CAAC,GAAa,EAAoB,EAAI,KAAK,CAAE,CAEtD,IAAM,EAAY,KAAK,eAAe,CAEtC,EAAY,IAAI,EAA8B,CAC5C,uBAA0B,GAAY,CACtC,mBAAoB,GACpB,qBAAuB,GAAc,CACnC,KAAK,eAAe,WAAWC,EAAW,EAAW,EAAU,EAElE,CAAC,CAGF,EAAU,YAAgB,CACpB,EAAU,WACZ,KAAK,eAAe,cAAc,EAAU,UAAU,EAK1D,MAAM,EAAU,QAAQ,EAAU,KAC7B,CAEL,EAAI,OAAO,IAAI,CAAC,KAAK,CACnB,QAAS,MACT,MAAO,CACL,KAAM,MACN,QAAS,4CACV,CACD,GAAI,KACL,CAAC,CACF,OAIF,MAAM,EAAU,cAAc,EAAK,EAAK,EAAI,KAAK,CAGnD,MAAc,iBAAiB,EAAc,EAA8B,CACzE,IAAM,EAAY,EAAI,QAAQ,kBAE9B,GAAI,CAAC,GAAa,CAAC,KAAK,eAAe,WAAW,EAAU,CAAE,CAC5D,EAAI,OAAO,IAAI,CAAC,KAAK,gCAAgC,CACrD,OAIF,MADgB,KAAK,eAAe,WAAW,EAAU,CAC3C,UAAU,cAAc,EAAK,EAAI,CAGjD,MAAc,oBAAoB,EAAc,EAA8B,CAC5E,IAAM,EAAY,EAAI,QAAQ,kBAE9B,GAAI,CAAC,GAAa,CAAC,KAAK,eAAe,WAAW,EAAU,CAAE,CAC5D,EAAI,OAAO,IAAI,CAAC,KAAK,gCAAgC,CACrD,OAIF,MADgB,KAAK,eAAe,WAAW,EAAU,CAC3C,UAAU,cAAc,EAAK,EAAI,CAG/C,KAAK,eAAe,cAAc,EAAU,CAG9C,MAAM,OAAuB,CAC3B,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,GAAI,CACF,KAAK,OAAS,KAAK,IAAI,OAAO,KAAK,OAAO,KAAM,KAAK,OAAO,SAAY,CACtE,QAAQ,OAAO,MACb,4CAA4C,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,QAClF,CACD,QAAQ,OAAO,MAAM,wBAAwB,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,WAAW,CAC7F,GAAS,EACT,CAEF,KAAK,OAAO,GAAG,QAAU,GAAiB,CACxC,EAAO,EAAM,EACb,OACK,EAAO,CACd,EAAO,EAAM,GAEf,CAGJ,MAAM,MAAsB,CAC1B,OAAO,IAAI,SAAS,EAAS,IAAW,CAClC,KAAK,QAEP,KAAK,eAAe,OAAO,CAE3B,KAAK,OAAO,MAAO,GAAgB,CAC7B,EACF,EAAO,EAAI,EAEX,KAAK,OAAS,KACd,GAAS,GAEX,EAEF,GAAS,EAEX,CAGJ,SAAkB,CAChB,OAAO,KAAK,OAAO,KAGrB,SAAkB,CAChB,OAAO,KAAK,OAAO,OC3MjB,EAAN,KAAwB,CACtB,SAA4C,IAAI,IAEhD,WAAW,EAAmD,CAC5D,OAAO,KAAK,SAAS,IAAI,EAAU,EAAE,UAGvC,WAAW,EAAmB,EAA+B,EAAyB,CACpF,KAAK,SAAS,IAAI,EAAW,CAAE,YAAW,SAAQ,CAAC,CAGrD,cAAc,EAAyB,CACrC,IAAM,EAAU,KAAK,SAAS,IAAI,EAAU,CACxC,GAEF,EAAQ,OAAO,OAAO,CAExB,KAAK,SAAS,OAAO,EAAU,CAGjC,WAAW,EAA4B,CACrC,OAAO,KAAK,SAAS,IAAI,EAAU,CAGrC,OAAc,CAEZ,IAAK,IAAM,KAAW,KAAK,SAAS,QAAQ,CAC1C,EAAQ,OAAO,OAAO,CAExB,KAAK,SAAS,OAAO,GASZ,EAAb,KAAkE,CAChE,cACA,IACA,OAAoC,KACpC,eACA,OAEA,YAAY,EAA8C,EAAyB,CAEjF,KAAK,cAAgB,OAAO,GAAkB,WAAa,MAAsB,EACjF,KAAK,IAAM,GAAS,CACpB,KAAK,eAAiB,IAAI,EAC1B,KAAK,OAAS,CACZ,KAAMC,EAAO,KACb,KAAMA,EAAO,MAAQ,IACrB,KAAMA,EAAO,MAAQ,YACtB,CAED,KAAK,iBAAiB,CACtB,KAAK,aAAa,CAGpB,iBAAgC,CAC9B,KAAK,IAAI,IAAI,EAAQ,MAAM,CAAC,CAG9B,aAA4B,CAE1B,KAAK,IAAI,IAAI,OAAQ,MAAO,EAAc,IAAkB,CAC1D,MAAM,KAAK,oBAAoB,EAAK,EAAI,EACxC,CAGF,KAAK,IAAI,KAAK,YAAa,MAAO,EAAc,IAAkB,CAChE,MAAM,KAAK,kBAAkB,EAAK,EAAI,EACtC,CAGF,KAAK,IAAI,IAAI,WAAY,EAAe,IAAkB,CACxD,EAAI,KAAK,CAAE,OAAQ,KAAM,UAAW,MAAO,CAAC,EAC5C,CAGJ,MAAc,oBAAoB,EAAe,EAA8B,CAC7E,GAAI,CAEF,IAAM,EAAY,KAAK,eAAe,CAGhC,EAAY,IAAI,EAAmB,YAAa,EAAI,CAG1D,KAAK,eAAe,WAAW,EAAU,UAAW,EAAW,EAAU,CAGzE,EAAI,GAAG,YAAe,CACpB,KAAK,eAAe,cAAc,EAAU,UAAU,EACtD,CAGF,MAAM,EAAU,QAAQ,EAAU,CAElC,QAAQ,OAAO,MAAM,4BAA4B,EAAU,UAAU,IAAI,OAClE,EAAO,CACd,QAAQ,OAAO,MACb,kCAAkC,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAAC,IAC1F,CACI,EAAI,aACP,EAAI,OAAO,IAAI,CAAC,KAAK,wBAAwB,EAKnD,MAAc,kBAAkB,EAAc,EAA8B,CAC1E,IAAM,EAAY,EAAI,MAAM,UAE5B,GAAI,CAAC,EAAW,CACd,EAAI,OAAO,IAAI,CAAC,KAAK,oCAAoC,CACzD,OAGF,IAAM,EAAY,KAAK,eAAe,WAAW,EAAU,CAE3D,GAAI,CAAC,EAAW,CACd,EAAI,OAAO,IAAI,CAAC,KAAK,mCAAmC,CACxD,OAGF,GAAI,CACF,MAAM,EAAU,kBAAkB,EAAK,EAAK,EAAI,KAAK,OAC9C,EAAO,CACd,QAAQ,OAAO,MAAM,gCAAgC,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAAC,IAAI,CAC3G,EAAI,aACP,EAAI,OAAO,IAAI,CAAC,KAAK,wBAAwB,EAKnD,MAAM,OAAuB,CAC3B,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,GAAI,CACF,KAAK,OAAS,KAAK,IAAI,OAAO,KAAK,OAAO,KAAM,KAAK,OAAO,SAAY,CACtE,QAAQ,OAAO,MACb,+DAA+D,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,IACrG,CACD,QAAQ,OAAO,MAAM,wBAAwB,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,QAAQ,CAC1F,QAAQ,OAAO,MAAM,6BAA6B,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,aAAa,CACpG,QAAQ,OAAO,MAAM,wBAAwB,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,WAAW,CAC7F,GAAS,EACT,CAEF,KAAK,OAAO,GAAG,QAAU,GAAiB,CACxC,EAAO,EAAM,EACb,OACK,EAAO,CACd,EAAO,EAAM,GAEf,CAGJ,MAAM,MAAsB,CAC1B,OAAO,IAAI,SAAS,EAAS,IAAW,CAClC,KAAK,QAEP,KAAK,eAAe,OAAO,CAE3B,KAAK,OAAO,MAAO,GAAgB,CAC7B,EACF,EAAO,EAAI,EAEX,KAAK,OAAS,KACd,GAAS,GAEX,EAEF,GAAS,EAEX,CAGJ,SAAkB,CAChB,OAAO,KAAK,OAAO,KAGrB,SAAkB,CAChB,OAAO,KAAK,OAAO,OCtMV,EAAb,KAA+D,CAC7D,OACA,UAAiD,KAEjD,YAAY,EAAgB,CAC1B,KAAK,OAAS,EAGhB,MAAM,OAAuB,CAC3B,KAAK,UAAY,IAAI,EACrB,MAAM,KAAK,OAAO,QAAQ,KAAK,UAAU,CACzC,QAAQ,OAAO,MAAM;EAA4C,CAGnE,MAAM,MAAsB,CAC1B,AAEE,KAAK,aADL,MAAM,KAAK,UAAU,OAAO,CACX"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`@modelcontextprotocol/sdk/server/index.js`),l=require(`@modelcontextprotocol/sdk/types.js`),u=require(`node:crypto`),d=require(`node:fs`),f=require(`node:fs/promises`),p=require(`node:path`),m=require(`node-fetch`);m=s(m);let h=require(`unsplash-js`),g=require(`zod`),_=require(`@modelcontextprotocol/sdk/server/streamableHttp.js`),v=require(`express`);v=s(v);let y=require(`@modelcontextprotocol/sdk/server/sse.js`),b=require(`@modelcontextprotocol/sdk/server/stdio.js`);function x(e){let t=e.replace(/\\+ /g,` `);return t=t.replace(/\\+'/g,`'`).replace(/\\+"/g,`"`).replace(/\\+`/g,"`").replace(/\\+\(/g,`(`).replace(/\\+\)/g,`)`).replace(/\\+\[/g,`[`).replace(/\\+\]/g,`]`).replace(/\\+\{/g,`{`).replace(/\\+\}/g,`}`),t}const S=`application/octet-stream`,C={".png":`image/png`,".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".gif":`image/gif`,".webp":`image/webp`,".bmp":`image/bmp`,".svg":`image/svg+xml`,".heic":`image/heic`,".heif":`image/heif`};var w=class{async readImage(e,t={}){let n=await this.resolveImageSource(e),r=(0,u.createHash)(`sha256`).update(n.data).digest(`hex`),i={source:n.source,sourceType:n.sourceType,mimeType:n.mimeType,sizeBytes:n.sizeBytes,sha256:r};return n.sourceType!==`data-uri`&&(i.fileName=(0,p.basename)(n.source)),t.includeBase64&&(i.base64=n.data.toString(`base64`)),i}async resolveImageSource(e){let t=e.trim();if(!t)throw new l.McpError(l.ErrorCode.InvalidParams,`Image source cannot be empty.`);return t.startsWith(`data:`)?this.resolveDataUri(t):/^https?:\/\//i.test(t)?this.resolveRemoteImage(t):this.resolveFile(t)}async resolveFile(e){let t=(0,p.resolve)(x(e));try{await(0,f.access)(t,d.constants.F_OK)}catch{throw new l.McpError(l.ErrorCode.InvalidParams,`Image file not found at path: ${e}`)}let n;try{n=await(0,f.readFile)(t)}catch(e){throw new l.McpError(l.ErrorCode.InternalError,`Failed to read image file: ${e instanceof Error?e.message:String(e)}`)}return{source:t,sourceType:`file`,mimeType:C[(0,p.extname)(t).toLowerCase()]||S,sizeBytes:n.length,data:n}}async resolveRemoteImage(e){try{let t=await(0,m.default)(e,{redirect:`follow`});if(!t.ok)throw new l.McpError(l.ErrorCode.InvalidParams,`Failed to fetch image from URL: ${e} (${t.status} ${t.statusText})`);let n=await t.arrayBuffer(),r=Buffer.from(n),i=t.headers.get(`content-type`)?.split(`;`)[0],a=(0,p.basename)(new URL(e).pathname||`image`);return{source:e,sourceType:`url`,mimeType:i??C[(0,p.extname)(a)]??S,sizeBytes:r.length,data:r}}catch(e){throw e instanceof l.McpError?e:new l.McpError(l.ErrorCode.InternalError,`Failed to download image from URL: ${e instanceof Error?e.message:String(e)}`)}}resolveDataUri(e){let t=e.indexOf(`,`);if(t===-1||t===e.length-1)throw new l.McpError(l.ErrorCode.InvalidParams,"Invalid data URI format. Expected `data:[<mime>];base64,<payload>`.");let n=e.slice(5,t).toLowerCase(),r=e.slice(t+1),i=n.includes(`base64`),a=n.split(`;`)[0]||S;try{let t=i?Buffer.from(r,`base64`):Buffer.from(decodeURIComponent(r));return{source:e,sourceType:`data-uri`,mimeType:a||S,sizeBytes:t.length,data:t}}catch(e){throw new l.McpError(l.ErrorCode.InvalidParams,`Failed to decode data URI payload: ${e instanceof Error?e.message:String(e)}`)}}},T=class e{static TOOL_NAME=`read-image`;service=new w;getDefinition(){return{name:e.TOOL_NAME,description:`Read an image from a local path, URL, or data URI and return metadata.`,inputSchema:{type:`object`,properties:{source:{type:`string`,minLength:1,description:`Image source as a local path, HTTP(S) URL, or data URI.`},includeBase64:{type:`boolean`,description:`Whether to include base64-encoded output.`,default:!1}},required:[`source`],additionalProperties:!1}}}async execute(e){try{if(!e||typeof e!=`object`)throw new l.McpError(l.ErrorCode.InvalidParams,`Tool input must be an object.`);let t=e,n=t.source,r=t.includeBase64;if(!n||typeof n!=`string`||n.trim().length===0)throw new l.McpError(l.ErrorCode.InvalidParams,`source must be a non-empty string.`);if(r!==void 0&&typeof r!=`boolean`)throw new l.McpError(l.ErrorCode.InvalidParams,`includeBase64 must be a boolean.`);let i={includeBase64:r},a=await this.service.readImage(n,i);return{content:[{type:`text`,text:JSON.stringify(a,null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};const E=g.z.object({UPLOAD_SERVICE:g.z.enum([`s3`,`cloudflare`,`gcloud`]).optional().default(`s3`),S3_BUCKET:g.z.string().optional(),AWS_ACCESS_KEY_ID:g.z.string().optional(),AWS_SECRET_ACCESS_KEY:g.z.string().optional(),S3_REGION:g.z.string().optional().default(`us-east-1`),S3_ENDPOINT:g.z.string().url(`S3_ENDPOINT must be a valid URL`).optional(),CLOUDFLARE_R2_BUCKET:g.z.string().optional(),CLOUDFLARE_R2_ACCESS_KEY_ID:g.z.string().optional(),CLOUDFLARE_R2_SECRET_ACCESS_KEY:g.z.string().optional(),CLOUDFLARE_R2_REGION:g.z.string().optional().default(`auto`),CLOUDFLARE_R2_ENDPOINT:g.z.string().url(`CLOUDFLARE_R2_ENDPOINT must be a valid URL`).optional(),GCLOUD_BUCKET:g.z.string().optional(),GCLOUD_PROJECT_ID:g.z.string().optional(),GCLOUD_CREDENTIALS_PATH:g.z.string().optional(),UNSPLASH_ACCESS_KEY:g.z.string().optional()}).refine(e=>e.AWS_ACCESS_KEY_ID||e.AWS_SECRET_ACCESS_KEY?e.AWS_ACCESS_KEY_ID&&e.AWS_SECRET_ACCESS_KEY:!0,{message:`Both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be provided together`,path:[`AWS_ACCESS_KEY_ID`]});var D=class e{static instance;config;constructor(){try{this.config=E.parse(process.env)}catch(e){if(e instanceof g.z.ZodError){let t=e.issues.map(e=>`${e.path.join(`.`)}: ${e.message}`).join(`; `);throw new l.McpError(l.ErrorCode.InternalError,`Environment configuration validation failed: ${t}`)}throw e}}static getInstance(){return e.instance||=new e,e.instance}getConfig(){return this.config}getS3Config(){return{bucket:this.config.S3_BUCKET,accessKeyId:this.config.AWS_ACCESS_KEY_ID,secretAccessKey:this.config.AWS_SECRET_ACCESS_KEY,region:this.config.S3_REGION,endpoint:this.config.S3_ENDPOINT}}getCloudflareConfig(){return{bucket:this.config.CLOUDFLARE_R2_BUCKET,accessKeyId:this.config.CLOUDFLARE_R2_ACCESS_KEY_ID,secretAccessKey:this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY,region:this.config.CLOUDFLARE_R2_REGION,endpoint:this.config.CLOUDFLARE_R2_ENDPOINT}}getGCloudConfig(){return{bucket:this.config.GCLOUD_BUCKET,projectId:this.config.GCLOUD_PROJECT_ID,credentialsPath:this.config.GCLOUD_CREDENTIALS_PATH}}getUploadService(){return this.config.UPLOAD_SERVICE}getUnsplashConfig(){return{accessKey:this.config.UNSPLASH_ACCESS_KEY}}validateServiceConfig(e){switch(e){case`s3`:if(!this.config.S3_BUCKET)throw new l.McpError(l.ErrorCode.InvalidParams,`S3_BUCKET is required for S3 upload service`);break;case`cloudflare`:if(!this.config.CLOUDFLARE_R2_BUCKET)throw new l.McpError(l.ErrorCode.InvalidParams,`CLOUDFLARE_R2_BUCKET is required for Cloudflare upload service`);if(!this.config.CLOUDFLARE_R2_ACCESS_KEY_ID||!this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY)throw new l.McpError(l.ErrorCode.InvalidParams,`CLOUDFLARE_R2_ACCESS_KEY_ID and CLOUDFLARE_R2_SECRET_ACCESS_KEY are required for Cloudflare upload service`);if(!this.config.CLOUDFLARE_R2_ENDPOINT)throw new l.McpError(l.ErrorCode.InvalidParams,`CLOUDFLARE_R2_ENDPOINT is required for Cloudflare upload service`);break;case`gcloud`:if(!this.config.GCLOUD_BUCKET)throw new l.McpError(l.ErrorCode.InvalidParams,`GCLOUD_BUCKET is required for Google Cloud upload service`);if(!this.config.GCLOUD_PROJECT_ID)throw new l.McpError(l.ErrorCode.InvalidParams,`GCLOUD_PROJECT_ID is required for Google Cloud upload service`);break}}};const O=D.getInstance();var k=class{api;constructor(e){let t=O.getUnsplashConfig(),n=e||t.accessKey;if(!n)throw Error(`Unsplash API key is required. Set UNSPLASH_ACCESS_KEY environment variable.`);this.api=(0,h.createApi)({accessKey:n})}async searchImages(e){try{let t=await this.api.search.getPhotos({query:e.query,page:e.page||1,perPage:e.perPage||10,orientation:e.orientation,color:e.color});if(t.errors)throw Error(`Unsplash API error: ${t.errors.join(`, `)}`);if(!t.response)throw Error(`No response from Unsplash API`);return t.response.results.map(e=>({id:e.id,description:e.description,alt_description:e.alt_description,urls:{raw:e.urls.raw,full:e.urls.full,regular:e.urls.regular,small:e.urls.small,thumb:e.urls.thumb},links:{html:e.links.html,download:e.links.download},user:{name:e.user.name,username:e.user.username,portfolio_url:e.user.portfolio_url},width:e.width,height:e.height,color:e.color||`#000000`,likes:e.likes}))}catch(e){throw Error(`Failed to search Unsplash images: ${e instanceof Error?e.message:`Unknown error`}`)}}},A=class e{static TOOL_NAME=`unsplash_search`;service;accessKey;constructor(e){this.accessKey=e,this.service=null}getService(){return this.service||=new k(this.accessKey),this.service}getDefinition(){return{name:e.TOOL_NAME,description:`Search for stock images from Unsplash by keyword and return image URLs with metadata`,inputSchema:{type:`object`,properties:{query:{type:`string`,description:`Search query (e.g., "sunset", "technology", "nature")`},perPage:{type:`number`,description:`Number of results per page (1-30, default: 10)`,minimum:1,maximum:30},page:{type:`number`,description:`Page number for pagination (default: 1)`,minimum:1},orientation:{type:`string`,enum:[`landscape`,`portrait`,`squarish`],description:`Filter by photo orientation`},color:{type:`string`,enum:[`black_and_white`,`black`,`white`,`yellow`,`orange`,`red`,`purple`,`magenta`,`green`,`teal`,`blue`],description:`Filter by photo color`}},required:[`query`],additionalProperties:!1}}}async execute(e){try{let t=await this.getService().searchImages({query:e.query,perPage:e.perPage,page:e.page,orientation:e.orientation,color:e.color});if(t.length===0)return{content:[{type:`text`,text:`No images found for query: "${e.query}"`}]};let n=t.map((e,t)=>`
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`@modelcontextprotocol/sdk/server/index.js`),l=require(`@modelcontextprotocol/sdk/types.js`),u=require(`node:crypto`),d=require(`node:fs`),f=require(`node:fs/promises`),p=require(`node:path`),m=require(`node-fetch`);m=s(m);let h=require(`unsplash-js`),g=require(`zod`),_=require(`@modelcontextprotocol/sdk/server/streamableHttp.js`),v=require(`express`);v=s(v);let y=require(`@modelcontextprotocol/sdk/server/sse.js`),b=require(`@modelcontextprotocol/sdk/server/stdio.js`);function x(e){let t=e.replace(/\\+ /g,` `);return t=t.replace(/\\+'/g,`'`).replace(/\\+"/g,`"`).replace(/\\+`/g,"`").replace(/\\+\(/g,`(`).replace(/\\+\)/g,`)`).replace(/\\+\[/g,`[`).replace(/\\+\]/g,`]`).replace(/\\+\{/g,`{`).replace(/\\+\}/g,`}`),t}const S=`application/octet-stream`,C={".png":`image/png`,".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".gif":`image/gif`,".webp":`image/webp`,".bmp":`image/bmp`,".svg":`image/svg+xml`,".heic":`image/heic`,".heif":`image/heif`};var w=class{async readImage(e,t={}){let n=await this.resolveImageSource(e),r=(0,u.createHash)(`sha256`).update(n.data).digest(`hex`),i={source:n.source,sourceType:n.sourceType,mimeType:n.mimeType,sizeBytes:n.sizeBytes,sha256:r};return n.sourceType!==`data-uri`&&(i.fileName=(0,p.basename)(n.source)),t.includeBase64&&(i.base64=n.data.toString(`base64`)),i}async resolveImageSource(e){let t=e.trim();if(!t)throw new l.McpError(l.ErrorCode.InvalidParams,`Image source cannot be empty.`);return t.startsWith(`data:`)?this.resolveDataUri(t):/^https?:\/\//i.test(t)?this.resolveRemoteImage(t):this.resolveFile(t)}async resolveFile(e){let t=(0,p.resolve)(x(e));try{await(0,f.access)(t,d.constants.F_OK)}catch{throw new l.McpError(l.ErrorCode.InvalidParams,`Image file not found at path: ${e}`)}let n;try{n=await(0,f.readFile)(t)}catch(e){throw new l.McpError(l.ErrorCode.InternalError,`Failed to read image file: ${e instanceof Error?e.message:String(e)}`)}return{source:t,sourceType:`file`,mimeType:C[(0,p.extname)(t).toLowerCase()]||S,sizeBytes:n.length,data:n}}async resolveRemoteImage(e){try{let t=await(0,m.default)(e,{redirect:`follow`});if(!t.ok)throw new l.McpError(l.ErrorCode.InvalidParams,`Failed to fetch image from URL: ${e} (${t.status} ${t.statusText})`);let n=await t.arrayBuffer(),r=Buffer.from(n),i=t.headers.get(`content-type`)?.split(`;`)[0],a=(0,p.basename)(new URL(e).pathname||`image`);return{source:e,sourceType:`url`,mimeType:i??C[(0,p.extname)(a)]??S,sizeBytes:r.length,data:r}}catch(e){throw e instanceof l.McpError?e:new l.McpError(l.ErrorCode.InternalError,`Failed to download image from URL: ${e instanceof Error?e.message:String(e)}`)}}resolveDataUri(e){let t=e.indexOf(`,`);if(t===-1||t===e.length-1)throw new l.McpError(l.ErrorCode.InvalidParams,"Invalid data URI format. Expected `data:[<mime>];base64,<payload>`.");let n=e.slice(5,t).toLowerCase(),r=e.slice(t+1),i=n.includes(`base64`),a=n.split(`;`)[0]||S;try{let t=i?Buffer.from(r,`base64`):Buffer.from(decodeURIComponent(r));return{source:e,sourceType:`data-uri`,mimeType:a||S,sizeBytes:t.length,data:t}}catch(e){throw new l.McpError(l.ErrorCode.InvalidParams,`Failed to decode data URI payload: ${e instanceof Error?e.message:String(e)}`)}}},T=class e{static TOOL_NAME=`read-image`;service=new w;getDefinition(){return{name:e.TOOL_NAME,description:`Read an image from a local path, URL, or data URI and return metadata.`,inputSchema:{type:`object`,properties:{source:{type:`string`,minLength:1,description:`Image source as a local path, HTTP(S) URL, or data URI.`},includeBase64:{type:`boolean`,description:`Whether to include base64-encoded output.`,default:!1}},required:[`source`],additionalProperties:!1}}}async execute(e){try{if(!e||typeof e!=`object`)throw new l.McpError(l.ErrorCode.InvalidParams,`Tool input must be an object.`);let t=e,n=t.source,r=t.includeBase64;if(!n||typeof n!=`string`||n.trim().length===0)throw new l.McpError(l.ErrorCode.InvalidParams,`source must be a non-empty string.`);if(r!==void 0&&typeof r!=`boolean`)throw new l.McpError(l.ErrorCode.InvalidParams,`includeBase64 must be a boolean.`);let i={includeBase64:r},a=await this.service.readImage(n,i);return{content:[{type:`text`,text:JSON.stringify(a,null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};const E=g.z.object({UPLOAD_SERVICE:g.z.enum([`s3`,`cloudflare`,`gcloud`]).optional().default(`s3`),S3_BUCKET:g.z.string().optional(),AWS_ACCESS_KEY_ID:g.z.string().optional(),AWS_SECRET_ACCESS_KEY:g.z.string().optional(),S3_REGION:g.z.string().optional().default(`us-east-1`),S3_ENDPOINT:g.z.string().url(`S3_ENDPOINT must be a valid URL`).optional(),CLOUDFLARE_R2_BUCKET:g.z.string().optional(),CLOUDFLARE_R2_ACCESS_KEY_ID:g.z.string().optional(),CLOUDFLARE_R2_SECRET_ACCESS_KEY:g.z.string().optional(),CLOUDFLARE_R2_REGION:g.z.string().optional().default(`auto`),CLOUDFLARE_R2_ENDPOINT:g.z.string().url(`CLOUDFLARE_R2_ENDPOINT must be a valid URL`).optional(),GCLOUD_BUCKET:g.z.string().optional(),GCLOUD_PROJECT_ID:g.z.string().optional(),GCLOUD_CREDENTIALS_PATH:g.z.string().optional(),UNSPLASH_ACCESS_KEY:g.z.string().optional()}).refine(e=>e.AWS_ACCESS_KEY_ID||e.AWS_SECRET_ACCESS_KEY?e.AWS_ACCESS_KEY_ID&&e.AWS_SECRET_ACCESS_KEY:!0,{message:`Both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be provided together`,path:[`AWS_ACCESS_KEY_ID`]});var D=class e{static instance;config;constructor(){try{this.config=E.parse(process.env)}catch(e){if(e instanceof g.z.ZodError){let t=e.issues.map(e=>`${e.path.join(`.`)}: ${e.message}`).join(`; `);throw new l.McpError(l.ErrorCode.InternalError,`Environment configuration validation failed: ${t}`)}throw e}}static getInstance(){return e.instance||=new e,e.instance}getConfig(){return this.config}getS3Config(){return{bucket:this.config.S3_BUCKET,accessKeyId:this.config.AWS_ACCESS_KEY_ID,secretAccessKey:this.config.AWS_SECRET_ACCESS_KEY,region:this.config.S3_REGION,endpoint:this.config.S3_ENDPOINT}}getCloudflareConfig(){return{bucket:this.config.CLOUDFLARE_R2_BUCKET,accessKeyId:this.config.CLOUDFLARE_R2_ACCESS_KEY_ID,secretAccessKey:this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY,region:this.config.CLOUDFLARE_R2_REGION,endpoint:this.config.CLOUDFLARE_R2_ENDPOINT}}getGCloudConfig(){return{bucket:this.config.GCLOUD_BUCKET,projectId:this.config.GCLOUD_PROJECT_ID,credentialsPath:this.config.GCLOUD_CREDENTIALS_PATH}}getUploadService(){return this.config.UPLOAD_SERVICE}getUnsplashConfig(){return{accessKey:this.config.UNSPLASH_ACCESS_KEY}}validateServiceConfig(e){switch(e){case`s3`:if(!this.config.S3_BUCKET)throw new l.McpError(l.ErrorCode.InvalidParams,`S3_BUCKET is required for S3 upload service`);break;case`cloudflare`:if(!this.config.CLOUDFLARE_R2_BUCKET)throw new l.McpError(l.ErrorCode.InvalidParams,`CLOUDFLARE_R2_BUCKET is required for Cloudflare upload service`);if(!this.config.CLOUDFLARE_R2_ACCESS_KEY_ID||!this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY)throw new l.McpError(l.ErrorCode.InvalidParams,`CLOUDFLARE_R2_ACCESS_KEY_ID and CLOUDFLARE_R2_SECRET_ACCESS_KEY are required for Cloudflare upload service`);if(!this.config.CLOUDFLARE_R2_ENDPOINT)throw new l.McpError(l.ErrorCode.InvalidParams,`CLOUDFLARE_R2_ENDPOINT is required for Cloudflare upload service`);break;case`gcloud`:if(!this.config.GCLOUD_BUCKET)throw new l.McpError(l.ErrorCode.InvalidParams,`GCLOUD_BUCKET is required for Google Cloud upload service`);if(!this.config.GCLOUD_PROJECT_ID)throw new l.McpError(l.ErrorCode.InvalidParams,`GCLOUD_PROJECT_ID is required for Google Cloud upload service`);break}}};const O=D.getInstance();var k=class{api;constructor(e){let t=O.getUnsplashConfig(),n=e||t.accessKey;if(!n)throw Error(`Unsplash API key is required. Set UNSPLASH_ACCESS_KEY environment variable.`);this.api=(0,h.createApi)({accessKey:n})}async searchImages(e){try{let t=await this.api.search.getPhotos({query:e.query,page:e.page||1,perPage:e.perPage||10,orientation:e.orientation,color:e.color});if(t.errors)throw Error(`Unsplash API error: ${t.errors.join(`, `)}`);if(!t.response)throw Error(`No response from Unsplash API`);return t.response.results.map(e=>({id:e.id,description:e.description,alt_description:e.alt_description,urls:{raw:e.urls.raw,full:e.urls.full,regular:e.urls.regular,small:e.urls.small,thumb:e.urls.thumb},links:{html:e.links.html,download:e.links.download},user:{name:e.user.name,username:e.user.username,portfolio_url:e.user.portfolio_url},width:e.width,height:e.height,color:e.color||`#000000`,likes:e.likes}))}catch(e){throw Error(`Failed to search Unsplash images: ${e instanceof Error?e.message:`Unknown error`}`,{cause:e})}}},A=class e{static TOOL_NAME=`unsplash_search`;service;accessKey;constructor(e){this.accessKey=e,this.service=null}getService(){return this.service||=new k(this.accessKey),this.service}getDefinition(){return{name:e.TOOL_NAME,description:`Search for stock images from Unsplash by keyword and return image URLs with metadata`,inputSchema:{type:`object`,properties:{query:{type:`string`,description:`Search query (e.g., "sunset", "technology", "nature")`},perPage:{type:`number`,description:`Number of results per page (1-30, default: 10)`,minimum:1,maximum:30},page:{type:`number`,description:`Page number for pagination (default: 1)`,minimum:1},orientation:{type:`string`,enum:[`landscape`,`portrait`,`squarish`],description:`Filter by photo orientation`},color:{type:`string`,enum:[`black_and_white`,`black`,`white`,`yellow`,`orange`,`red`,`purple`,`magenta`,`green`,`teal`,`blue`],description:`Filter by photo color`}},required:[`query`],additionalProperties:!1}}}async execute(e){try{let t=await this.getService().searchImages({query:e.query,perPage:e.perPage,page:e.page,orientation:e.orientation,color:e.color});if(t.length===0)return{content:[{type:`text`,text:`No images found for query: "${e.query}"`}]};let n=t.map((e,t)=>`
|
|
2
2
|
📷 **Image ${t+1}**
|
|
3
3
|
- **ID**: ${e.id}
|
|
4
4
|
- **Description**: ${e.alt_description||e.description||`No description`}
|
|
@@ -23,4 +23,4 @@ ${e.user.portfolio_url?`- Photographer Portfolio: ${e.user.portfolio_url}`:``}
|
|
|
23
23
|
|
|
24
24
|
`);return{content:[{type:`text`,text:`Found ${t.length} image(s) for "${e.query}":\n\n${n}`}]}}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};function j(){let e=new c.Server({name:`imagine-mcp`,version:`0.1.0`},{capabilities:{tools:{}}}),t=new A,n=new T;return e.setRequestHandler(l.ListToolsRequestSchema,async()=>({tools:[t.getDefinition(),n.getDefinition()]})),e.setRequestHandler(l.CallToolRequestSchema,async e=>{let{name:r,arguments:i}=e.params;return r===A.TOOL_NAME?await t.execute(i):r===T.TOOL_NAME?await n.execute(i):{content:[{type:`text`,text:`Unknown tool: ${r}`}],isError:!0}}),e}var M=class{sessions=new Map;getSession(e){return this.sessions.get(e)}setSession(e,t,n){this.sessions.set(e,{transport:t,server:n})}deleteSession(e){let t=this.sessions.get(e);t&&t.server.close(),this.sessions.delete(e)}hasSession(e){return this.sessions.has(e)}clear(){for(let e of this.sessions.values())e.server.close();this.sessions.clear()}},N=class{serverFactory;app;server=null;sessionManager;config;constructor(e,t){this.serverFactory=typeof e==`function`?e:()=>e,this.app=(0,v.default)(),this.sessionManager=new M,this.config={mode:t.mode,port:t.port??3e3,host:t.host??`localhost`},this.setupMiddleware(),this.setupRoutes()}setupMiddleware(){this.app.use(v.default.json())}setupRoutes(){this.app.post(`/mcp`,async(e,t)=>{await this.handlePostRequest(e,t)}),this.app.get(`/mcp`,async(e,t)=>{await this.handleGetRequest(e,t)}),this.app.delete(`/mcp`,async(e,t)=>{await this.handleDeleteRequest(e,t)}),this.app.get(`/health`,(e,t)=>{t.json({status:`ok`,transport:`http`})})}async handlePostRequest(e,t){let n=e.headers[`mcp-session-id`],r;if(n&&this.sessionManager.hasSession(n))r=this.sessionManager.getSession(n).transport;else if(!n&&(0,l.isInitializeRequest)(e.body)){let e=this.serverFactory();r=new _.StreamableHTTPServerTransport({sessionIdGenerator:()=>(0,u.randomUUID)(),enableJsonResponse:!0,onsessioninitialized:t=>{this.sessionManager.setSession(t,r,e)}}),r.onclose=()=>{r.sessionId&&this.sessionManager.deleteSession(r.sessionId)},await e.connect(r)}else{t.status(400).json({jsonrpc:`2.0`,error:{code:-32e3,message:`Bad Request: No valid session ID provided`},id:null});return}await r.handleRequest(e,t,e.body)}async handleGetRequest(e,t){let n=e.headers[`mcp-session-id`];if(!n||!this.sessionManager.hasSession(n)){t.status(400).send(`Invalid or missing session ID`);return}await this.sessionManager.getSession(n).transport.handleRequest(e,t)}async handleDeleteRequest(e,t){let n=e.headers[`mcp-session-id`];if(!n||!this.sessionManager.hasSession(n)){t.status(400).send(`Invalid or missing session ID`);return}await this.sessionManager.getSession(n).transport.handleRequest(e,t),this.sessionManager.deleteSession(n)}async start(){return new Promise((e,t)=>{try{this.server=this.app.listen(this.config.port,this.config.host,()=>{process.stderr.write(`imagine-mcp MCP server started on http://${this.config.host}:${this.config.port}/mcp\n`),process.stderr.write(`Health check: http://${this.config.host}:${this.config.port}/health\n`),e()}),this.server.on(`error`,e=>{t(e)})}catch(e){t(e)}})}async stop(){return new Promise((e,t)=>{this.server?(this.sessionManager.clear(),this.server.close(n=>{n?t(n):(this.server=null,e())})):e()})}getPort(){return this.config.port}getHost(){return this.config.host}},P=class{sessions=new Map;getSession(e){return this.sessions.get(e)?.transport}setSession(e,t,n){this.sessions.set(e,{transport:t,server:n})}deleteSession(e){let t=this.sessions.get(e);t&&t.server.close(),this.sessions.delete(e)}hasSession(e){return this.sessions.has(e)}clear(){for(let e of this.sessions.values())e.server.close();this.sessions.clear()}},F=class{serverFactory;app;server=null;sessionManager;config;constructor(e,t){this.serverFactory=typeof e==`function`?e:()=>e,this.app=(0,v.default)(),this.sessionManager=new P,this.config={mode:t.mode,port:t.port??3e3,host:t.host??`localhost`},this.setupMiddleware(),this.setupRoutes()}setupMiddleware(){this.app.use(v.default.json())}setupRoutes(){this.app.get(`/sse`,async(e,t)=>{await this.handleSseConnection(e,t)}),this.app.post(`/messages`,async(e,t)=>{await this.handlePostMessage(e,t)}),this.app.get(`/health`,(e,t)=>{t.json({status:`ok`,transport:`sse`})})}async handleSseConnection(e,t){try{let e=this.serverFactory(),n=new y.SSEServerTransport(`/messages`,t);this.sessionManager.setSession(n.sessionId,n,e),t.on(`close`,()=>{this.sessionManager.deleteSession(n.sessionId)}),await e.connect(n),process.stderr.write(`SSE session established: ${n.sessionId}\n`)}catch(e){process.stderr.write(`Error handling SSE connection: ${e instanceof Error?e.message:String(e)}\n`),t.headersSent||t.status(500).send(`Internal Server Error`)}}async handlePostMessage(e,t){let n=e.query.sessionId;if(!n){t.status(400).send(`Missing sessionId query parameter`);return}let r=this.sessionManager.getSession(n);if(!r){t.status(404).send(`No transport found for sessionId`);return}try{await r.handlePostMessage(e,t,e.body)}catch(e){process.stderr.write(`Error handling post message: ${e instanceof Error?e.message:String(e)}\n`),t.headersSent||t.status(500).send(`Internal Server Error`)}}async start(){return new Promise((e,t)=>{try{this.server=this.app.listen(this.config.port,this.config.host,()=>{process.stderr.write(`imagine-mcp MCP server started with SSE transport on http://${this.config.host}:${this.config.port}\n`),process.stderr.write(`SSE endpoint: http://${this.config.host}:${this.config.port}/sse\n`),process.stderr.write(`Messages endpoint: http://${this.config.host}:${this.config.port}/messages\n`),process.stderr.write(`Health check: http://${this.config.host}:${this.config.port}/health\n`),e()}),this.server.on(`error`,e=>{t(e)})}catch(e){t(e)}})}async stop(){return new Promise((e,t)=>{this.server?(this.sessionManager.clear(),this.server.close(n=>{n?t(n):(this.server=null,e())})):e()})}getPort(){return this.config.port}getHost(){return this.config.host}},I=class{server;transport=null;constructor(e){this.server=e}async start(){this.transport=new b.StdioServerTransport,await this.server.connect(this.transport),process.stderr.write(`imagine-mcp MCP server started on stdio
|
|
25
25
|
`)}async stop(){this.transport&&=(await this.transport.close(),null)}};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return F}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return I}});
|
|
26
|
-
//# sourceMappingURL=stdio-
|
|
26
|
+
//# sourceMappingURL=stdio-DSIKRtTS.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio-DSIKRtTS.cjs","names":["MIME_BY_EXTENSION: Record<string, string>","result: ReadImageResult","McpError","ErrorCode","constants","data: Buffer","McpError","ErrorCode","options: ReadImageOptions","z","z","McpError","ErrorCode","Server","ListToolsRequestSchema","CallToolRequestSchema","config","transport: StreamableHTTPServerTransport","StreamableHTTPServerTransport","sessionId","config","SSEServerTransport","StdioServerTransport"],"sources":["../src/utils.ts","../src/services/ImageReader.ts","../src/tools/ReadImageTool.ts","../src/imagineEnvSchema.ts","../src/config.ts","../src/services/UnsplashService.ts","../src/tools/UnsplashSearchTool.ts","../src/server/index.ts","../src/transports/http.ts","../src/transports/sse.ts","../src/transports/stdio.ts"],"sourcesContent":["import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport fetch from 'node-fetch';\n\nimport { SUPPORTED_INPUT_FORMATS, SUPPORTED_OUTPUT_FORMATS } from './constants';\n\n/**\n * Validates if the provided format is supported for input\n */\nexport function isValidInputFormat(format: string): boolean {\n return SUPPORTED_INPUT_FORMATS.includes(format.toLowerCase());\n}\n\n/**\n * Validates if the provided format is supported for output\n */\nexport function isValidOutputFormat(format: string): boolean {\n return SUPPORTED_OUTPUT_FORMATS.includes(format.toLowerCase());\n}\n\n/**\n * Validates if the provided dimensions are valid\n */\nexport function isValidDimensions(width?: number, height?: number): boolean {\n if (width !== undefined && (width <= 0 || width > 10000)) {\n return false;\n }\n if (height !== undefined && (height <= 0 || height > 10000)) {\n return false;\n }\n return true;\n}\n\n/**\n * Validates if the provided quality is valid\n */\nexport function isValidQuality(quality?: number): boolean {\n if (quality !== undefined && (quality < 1 || quality > 100)) {\n return false;\n }\n return true;\n}\n\n/**\n * Extracts the file extension from a filename\n */\nexport function getFileExtension(filename: string): string {\n const parts = filename.split('.');\n return parts.length > 1 ? parts.pop()!.toLowerCase() : '';\n}\n\n/**\n * Converts a base64 string to a Buffer\n */\nexport function base64ToBuffer(base64: string): Buffer {\n // Remove data URL prefix if present\n const base64Data = base64.replace(/^data:image\\/\\w+;base64,/, '');\n return Buffer.from(base64Data, 'base64');\n}\n\n/**\n * Converts a Buffer to a base64 string\n */\nexport function bufferToBase64(buffer: Buffer, mimeType: string): string {\n return `data:${mimeType};base64,${buffer.toString('base64')}`;\n}\n\n/**\n * Normalizes a file path by handling escaped characters and spaces\n *\n * This function handles cases like 'a\\ name.png' by converting them to 'a name.png'\n */\nexport function normalizeFilePath(filePath: string): string {\n // Replace escaped spaces (\\ ) with actual spaces\n let normalizedPath = filePath.replace(/\\\\+ /g, ' ');\n\n // Replace other common escaped characters\n normalizedPath = normalizedPath\n .replace(/\\\\+'/g, \"'\")\n .replace(/\\\\+\"/g, '\"')\n .replace(/\\\\+`/g, '`')\n .replace(/\\\\+\\(/g, '(')\n .replace(/\\\\+\\)/g, ')')\n .replace(/\\\\+\\[/g, '[')\n .replace(/\\\\+\\]/g, ']')\n .replace(/\\\\+\\{/g, '{')\n .replace(/\\\\+\\}/g, '}');\n\n return normalizedPath;\n}\n\n/**\n * Fetches an image from a URL and returns it as a Buffer\n */\nexport async function fetchImageFromUrl(url: string): Promise<Buffer> {\n try {\n const response = await fetch(url);\n\n // Check if the response is successful\n if (!response.ok) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Failed to fetch image from URL: ${url}, status code: ${response.status}`,\n );\n }\n\n // Check if the content type is an image\n const contentType = response.headers.get('content-type');\n if (!contentType || !contentType.startsWith('image/')) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `URL does not point to an image: ${url}, content-type: ${contentType}`,\n );\n }\n\n // Get the response as an ArrayBuffer and convert to Buffer\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer);\n } catch (error) {\n if (error instanceof McpError) {\n throw error;\n }\n throw new McpError(\n ErrorCode.InternalError,\n `Error fetching image from URL: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n","import { createHash } from 'node:crypto';\nimport { constants } from 'node:fs';\nimport { access, readFile } from 'node:fs/promises';\nimport { basename, extname, resolve } from 'node:path';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport fetch from 'node-fetch';\n\nimport { normalizeFilePath } from '../utils';\n\ninterface ImageSource {\n source: string;\n sourceType: 'data-uri' | 'url' | 'file';\n mimeType: string;\n sizeBytes: number;\n data: Buffer;\n}\n\nexport interface ReadImageResult {\n source: string;\n sourceType: 'data-uri' | 'url' | 'file';\n mimeType: string;\n sizeBytes: number;\n sha256: string;\n fileName?: string;\n base64?: string;\n}\n\nexport interface ReadImageOptions {\n includeBase64?: boolean;\n}\n\nconst DEFAULT_MIME_TYPE = 'application/octet-stream';\nconst MIME_BY_EXTENSION: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.bmp': 'image/bmp',\n '.svg': 'image/svg+xml',\n '.heic': 'image/heic',\n '.heif': 'image/heif',\n};\n\nexport class ImageReader {\n async readImage(source: string, options: ReadImageOptions = {}): Promise<ReadImageResult> {\n const imageSource = await this.resolveImageSource(source);\n const sha256 = createHash('sha256').update(imageSource.data).digest('hex');\n\n const result: ReadImageResult = {\n source: imageSource.source,\n sourceType: imageSource.sourceType,\n mimeType: imageSource.mimeType,\n sizeBytes: imageSource.sizeBytes,\n sha256,\n };\n\n if (imageSource.sourceType !== 'data-uri') {\n result.fileName = basename(imageSource.source);\n }\n\n if (options.includeBase64) {\n result.base64 = imageSource.data.toString('base64');\n }\n\n return result;\n }\n\n private async resolveImageSource(source: string): Promise<ImageSource> {\n const trimmed = source.trim();\n\n if (!trimmed) {\n throw new McpError(ErrorCode.InvalidParams, 'Image source cannot be empty.');\n }\n\n if (trimmed.startsWith('data:')) {\n return this.resolveDataUri(trimmed);\n }\n\n if (/^https?:\\/\\//i.test(trimmed)) {\n return this.resolveRemoteImage(trimmed);\n }\n\n return this.resolveFile(trimmed);\n }\n\n private async resolveFile(filePath: string): Promise<ImageSource> {\n const normalizedPath = resolve(normalizeFilePath(filePath));\n\n try {\n await access(normalizedPath, constants.F_OK);\n } catch {\n throw new McpError(ErrorCode.InvalidParams, `Image file not found at path: ${filePath}`);\n }\n\n let data: Buffer;\n try {\n data = await readFile(normalizedPath);\n } catch (error) {\n throw new McpError(\n ErrorCode.InternalError,\n `Failed to read image file: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n const extension = extname(normalizedPath).toLowerCase();\n const mimeType = MIME_BY_EXTENSION[extension] || DEFAULT_MIME_TYPE;\n\n return {\n source: normalizedPath,\n sourceType: 'file',\n mimeType,\n sizeBytes: data.length,\n data,\n };\n }\n\n private async resolveRemoteImage(url: string): Promise<ImageSource> {\n try {\n const response = await fetch(url, { redirect: 'follow' });\n if (!response.ok) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Failed to fetch image from URL: ${url} (${response.status} ${response.statusText})`,\n );\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const data = Buffer.from(arrayBuffer);\n const contentType = response.headers.get('content-type')?.split(';')[0];\n const fileName = basename(new URL(url).pathname || 'image');\n\n return {\n source: url,\n sourceType: 'url',\n mimeType: contentType ?? MIME_BY_EXTENSION[extname(fileName)] ?? DEFAULT_MIME_TYPE,\n sizeBytes: data.length,\n data,\n };\n } catch (error) {\n if (error instanceof McpError) {\n throw error;\n }\n throw new McpError(\n ErrorCode.InternalError,\n `Failed to download image from URL: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private resolveDataUri(dataUri: string): ImageSource {\n const commaIndex = dataUri.indexOf(',');\n if (commaIndex === -1 || commaIndex === dataUri.length - 1) {\n throw new McpError(\n ErrorCode.InvalidParams,\n 'Invalid data URI format. Expected `data:[<mime>];base64,<payload>`.',\n );\n }\n\n const metadata = dataUri.slice(5, commaIndex).toLowerCase();\n const payload = dataUri.slice(commaIndex + 1);\n const isBase64 = metadata.includes('base64');\n const declaredMimeType = metadata.split(';')[0] || DEFAULT_MIME_TYPE;\n\n try {\n const data = isBase64 ? Buffer.from(payload, 'base64') : Buffer.from(decodeURIComponent(payload));\n return {\n source: dataUri,\n sourceType: 'data-uri',\n mimeType: declaredMimeType || DEFAULT_MIME_TYPE,\n sizeBytes: data.length,\n data,\n };\n } catch (error) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Failed to decode data URI payload: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n}\n","import { CallToolResult, ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { ImageReader, type ReadImageOptions } from '../services/ImageReader';\nimport type { Tool, ToolDefinition } from '../types/index';\n\nexport interface ReadImageToolInput {\n source: string;\n includeBase64?: boolean;\n}\n\nexport class ReadImageTool implements Tool<ReadImageToolInput> {\n static readonly TOOL_NAME = 'read-image';\n\n private service = new ImageReader();\n\n getDefinition(): ToolDefinition {\n return {\n name: ReadImageTool.TOOL_NAME,\n description: 'Read an image from a local path, URL, or data URI and return metadata.',\n inputSchema: {\n type: 'object',\n properties: {\n source: {\n type: 'string',\n minLength: 1,\n description: 'Image source as a local path, HTTP(S) URL, or data URI.',\n },\n includeBase64: {\n type: 'boolean',\n description: 'Whether to include base64-encoded output.',\n default: false,\n },\n },\n required: ['source'],\n additionalProperties: false,\n },\n };\n }\n\n async execute(input: ReadImageToolInput): Promise<CallToolResult> {\n try {\n if (!input || typeof input !== 'object') {\n throw new McpError(ErrorCode.InvalidParams, 'Tool input must be an object.');\n }\n\n const payload = input as ReadImageToolInput & { [key: string]: unknown };\n const source = payload.source;\n const includeBase64 = payload.includeBase64;\n if (!source || typeof source !== 'string' || source.trim().length === 0) {\n throw new McpError(ErrorCode.InvalidParams, 'source must be a non-empty string.');\n }\n if (includeBase64 !== undefined && typeof includeBase64 !== 'boolean') {\n throw new McpError(ErrorCode.InvalidParams, 'includeBase64 must be a boolean.');\n }\n\n const options: ReadImageOptions = {\n includeBase64,\n };\n const result = await this.service.readImage(source, options);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n}\n","import { z } from 'zod';\n\n/**\n * Environment variable schema for imagine-mcp\n *\n * Following Agiflow naming conventions:\n * - Service credentials use provider-specific prefixes\n * - Upload service selection uses UPLOAD_SERVICE\n */\nexport const envSchema = z\n .object({\n // Upload service selection\n UPLOAD_SERVICE: z.enum(['s3', 'cloudflare', 'gcloud']).optional().default('s3'),\n\n // AWS S3 Configuration\n S3_BUCKET: z.string().optional(),\n AWS_ACCESS_KEY_ID: z.string().optional(),\n AWS_SECRET_ACCESS_KEY: z.string().optional(),\n S3_REGION: z.string().optional().default('us-east-1'),\n S3_ENDPOINT: z.string().url('S3_ENDPOINT must be a valid URL').optional(),\n\n // Cloudflare R2 Configuration\n CLOUDFLARE_R2_BUCKET: z.string().optional(),\n CLOUDFLARE_R2_ACCESS_KEY_ID: z.string().optional(),\n CLOUDFLARE_R2_SECRET_ACCESS_KEY: z.string().optional(),\n CLOUDFLARE_R2_REGION: z.string().optional().default('auto'),\n CLOUDFLARE_R2_ENDPOINT: z.string().url('CLOUDFLARE_R2_ENDPOINT must be a valid URL').optional(),\n\n // Google Cloud Storage Configuration\n GCLOUD_BUCKET: z.string().optional(),\n GCLOUD_PROJECT_ID: z.string().optional(),\n GCLOUD_CREDENTIALS_PATH: z.string().optional(),\n\n // Unsplash API Configuration\n UNSPLASH_ACCESS_KEY: z.string().optional(),\n })\n .refine(\n (data) => {\n // If AWS credentials are provided, both must be present\n if (data.AWS_ACCESS_KEY_ID || data.AWS_SECRET_ACCESS_KEY) {\n return data.AWS_ACCESS_KEY_ID && data.AWS_SECRET_ACCESS_KEY;\n }\n return true;\n },\n {\n message: 'Both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be provided together',\n path: ['AWS_ACCESS_KEY_ID'],\n },\n );\n\nexport type EnvConfig = z.infer<typeof envSchema>;\n","import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod';\nimport { type EnvConfig, envSchema } from './imagineEnvSchema';\n\n/**\n * Centralized configuration service for imagine-mcp\n *\n * Validates environment variables at startup and provides\n * type-safe access to configuration throughout the application.\n */\nclass ConfigService {\n private static instance: ConfigService;\n private config: EnvConfig;\n\n private constructor() {\n try {\n this.config = envSchema.parse(process.env);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const errorMessage = error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join('; ');\n throw new McpError(ErrorCode.InternalError, `Environment configuration validation failed: ${errorMessage}`);\n }\n throw error;\n }\n }\n\n /**\n * Get the singleton instance of ConfigService\n */\n public static getInstance(): ConfigService {\n if (!ConfigService.instance) {\n ConfigService.instance = new ConfigService();\n }\n return ConfigService.instance;\n }\n\n /**\n * Get the validated configuration\n */\n public getConfig(): EnvConfig {\n return this.config;\n }\n\n /**\n * Get S3 configuration\n */\n public getS3Config() {\n return {\n bucket: this.config.S3_BUCKET,\n accessKeyId: this.config.AWS_ACCESS_KEY_ID,\n secretAccessKey: this.config.AWS_SECRET_ACCESS_KEY,\n region: this.config.S3_REGION,\n endpoint: this.config.S3_ENDPOINT,\n };\n }\n\n /**\n * Get Cloudflare R2 configuration\n */\n public getCloudflareConfig() {\n return {\n bucket: this.config.CLOUDFLARE_R2_BUCKET,\n accessKeyId: this.config.CLOUDFLARE_R2_ACCESS_KEY_ID,\n secretAccessKey: this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY,\n region: this.config.CLOUDFLARE_R2_REGION,\n endpoint: this.config.CLOUDFLARE_R2_ENDPOINT,\n };\n }\n\n /**\n * Get Google Cloud Storage configuration\n */\n public getGCloudConfig() {\n return {\n bucket: this.config.GCLOUD_BUCKET,\n projectId: this.config.GCLOUD_PROJECT_ID,\n credentialsPath: this.config.GCLOUD_CREDENTIALS_PATH,\n };\n }\n\n /**\n * Get the default upload service\n */\n public getUploadService() {\n return this.config.UPLOAD_SERVICE;\n }\n\n /**\n * Get Unsplash API configuration\n */\n public getUnsplashConfig() {\n return {\n accessKey: this.config.UNSPLASH_ACCESS_KEY,\n };\n }\n\n /**\n * Validate that required credentials exist for a specific service\n */\n public validateServiceConfig(service: 's3' | 'cloudflare' | 'gcloud'): void {\n switch (service) {\n case 's3':\n if (!this.config.S3_BUCKET) {\n throw new McpError(ErrorCode.InvalidParams, 'S3_BUCKET is required for S3 upload service');\n }\n break;\n case 'cloudflare':\n if (!this.config.CLOUDFLARE_R2_BUCKET) {\n throw new McpError(ErrorCode.InvalidParams, 'CLOUDFLARE_R2_BUCKET is required for Cloudflare upload service');\n }\n if (!this.config.CLOUDFLARE_R2_ACCESS_KEY_ID || !this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY) {\n throw new McpError(\n ErrorCode.InvalidParams,\n 'CLOUDFLARE_R2_ACCESS_KEY_ID and CLOUDFLARE_R2_SECRET_ACCESS_KEY are required for Cloudflare upload service',\n );\n }\n if (!this.config.CLOUDFLARE_R2_ENDPOINT) {\n throw new McpError(\n ErrorCode.InvalidParams,\n 'CLOUDFLARE_R2_ENDPOINT is required for Cloudflare upload service',\n );\n }\n break;\n case 'gcloud':\n if (!this.config.GCLOUD_BUCKET) {\n throw new McpError(ErrorCode.InvalidParams, 'GCLOUD_BUCKET is required for Google Cloud upload service');\n }\n if (!this.config.GCLOUD_PROJECT_ID) {\n throw new McpError(ErrorCode.InvalidParams, 'GCLOUD_PROJECT_ID is required for Google Cloud upload service');\n }\n break;\n }\n }\n}\n\n// Export singleton instance\nexport const config = ConfigService.getInstance();\n","/**\n * UnsplashService\n *\n * DESIGN PATTERNS:\n * - Service pattern for business logic encapsulation\n * - Single responsibility principle\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Keep methods focused and well-named\n * - Document complex logic with comments\n *\n * AVOID:\n * - Mixing concerns (keep focused on single domain)\n * - Direct tool implementation (services should be tool-agnostic)\n */\n\nimport { createApi } from 'unsplash-js';\nimport { config } from '../config.js';\n\nexport interface UnsplashImage {\n id: string;\n description: string | null;\n alt_description: string | null;\n urls: {\n raw: string;\n full: string;\n regular: string;\n small: string;\n thumb: string;\n };\n links: {\n html: string;\n download: string;\n };\n user: {\n name: string;\n username: string;\n portfolio_url: string | null;\n };\n width: number;\n height: number;\n color: string;\n likes: number;\n}\n\nexport interface SearchImagesParams {\n query: string;\n perPage?: number;\n page?: number;\n orientation?: 'landscape' | 'portrait' | 'squarish';\n color?:\n | 'black_and_white'\n | 'black'\n | 'white'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'magenta'\n | 'green'\n | 'teal'\n | 'blue';\n}\n\nexport class UnsplashService {\n private api: ReturnType<typeof createApi>;\n\n constructor(accessKey?: string) {\n const unsplashConfig = config.getUnsplashConfig();\n const apiKey = accessKey || unsplashConfig.accessKey;\n\n if (!apiKey) {\n throw new Error('Unsplash API key is required. Set UNSPLASH_ACCESS_KEY environment variable.');\n }\n\n this.api = createApi({\n accessKey: apiKey,\n });\n }\n\n async searchImages(params: SearchImagesParams): Promise<UnsplashImage[]> {\n try {\n const result = await this.api.search.getPhotos({\n query: params.query,\n page: params.page || 1,\n perPage: params.perPage || 10,\n orientation: params.orientation,\n color: params.color,\n });\n\n if (result.errors) {\n throw new Error(`Unsplash API error: ${result.errors.join(', ')}`);\n }\n\n if (!result.response) {\n throw new Error('No response from Unsplash API');\n }\n\n return result.response.results.map((photo) => ({\n id: photo.id,\n description: photo.description,\n alt_description: photo.alt_description,\n urls: {\n raw: photo.urls.raw,\n full: photo.urls.full,\n regular: photo.urls.regular,\n small: photo.urls.small,\n thumb: photo.urls.thumb,\n },\n links: {\n html: photo.links.html,\n download: photo.links.download,\n },\n user: {\n name: photo.user.name,\n username: photo.user.username,\n portfolio_url: photo.user.portfolio_url,\n },\n width: photo.width,\n height: photo.height,\n color: photo.color || '#000000',\n likes: photo.likes,\n }));\n } catch (error) {\n throw new Error(`Failed to search Unsplash images: ${error instanceof Error ? error.message : 'Unknown error'}`, {\n cause: error,\n });\n }\n }\n}\n","/**\n * UnsplashSearchTool\n *\nhttps://github.com/BoomLinkAi/image-worker-mcp * DESIGN PATTERNS:\n * - Tool pattern with getDefinition() and execute() methods\n * - Service delegation for business logic\n * - JSON Schema validation for inputs\n *\n * CODING STANDARDS:\n * - Implement Tool interface from ../types\n * - Use TOOL_NAME constant with snake_case (e.g., 'file_read')\n * - Return CallToolResult with content array\n * - Handle errors with isError flag\n * - Delegate complex logic to services\n *\n * AVOID:\n * - Complex business logic in execute method\n * - Unhandled promise rejections\n * - Missing input validation\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { UnsplashService } from '../services/UnsplashService';\nimport type { Tool, ToolDefinition } from '../types/index';\n\nexport interface UnsplashSearchToolInput {\n query: string;\n perPage?: number;\n page?: number;\n orientation?: 'landscape' | 'portrait' | 'squarish';\n color?:\n | 'black_and_white'\n | 'black'\n | 'white'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'magenta'\n | 'green'\n | 'teal'\n | 'blue';\n}\n\nexport class UnsplashSearchTool implements Tool<UnsplashSearchToolInput> {\n static readonly TOOL_NAME = 'unsplash_search';\n\n private service: UnsplashService | null;\n private readonly accessKey?: string;\n\n constructor(accessKey?: string) {\n this.accessKey = accessKey;\n this.service = null;\n }\n\n private getService(): UnsplashService {\n if (!this.service) {\n this.service = new UnsplashService(this.accessKey);\n }\n return this.service;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: UnsplashSearchTool.TOOL_NAME,\n description: 'Search for stock images from Unsplash by keyword and return image URLs with metadata',\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Search query (e.g., \"sunset\", \"technology\", \"nature\")',\n },\n perPage: {\n type: 'number',\n description: 'Number of results per page (1-30, default: 10)',\n minimum: 1,\n maximum: 30,\n },\n page: {\n type: 'number',\n description: 'Page number for pagination (default: 1)',\n minimum: 1,\n },\n orientation: {\n type: 'string',\n enum: ['landscape', 'portrait', 'squarish'],\n description: 'Filter by photo orientation',\n },\n color: {\n type: 'string',\n enum: [\n 'black_and_white',\n 'black',\n 'white',\n 'yellow',\n 'orange',\n 'red',\n 'purple',\n 'magenta',\n 'green',\n 'teal',\n 'blue',\n ],\n description: 'Filter by photo color',\n },\n },\n required: ['query'],\n additionalProperties: false,\n },\n };\n }\n\n async execute(input: UnsplashSearchToolInput): Promise<CallToolResult> {\n try {\n const images = await this.getService().searchImages({\n query: input.query,\n perPage: input.perPage,\n page: input.page,\n orientation: input.orientation,\n color: input.color,\n });\n\n if (images.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: `No images found for query: \"${input.query}\"`,\n },\n ],\n };\n }\n\n const formattedResults = images\n .map((img, index) => {\n return `\n📷 **Image ${index + 1}**\n- **ID**: ${img.id}\n- **Description**: ${img.alt_description || img.description || 'No description'}\n- **Photographer**: ${img.user.name} (@${img.user.username})\n- **Dimensions**: ${img.width}x${img.height}px\n- **Color**: ${img.color}\n- **Likes**: ${img.likes}\n\n**URLs**:\n- Full: ${img.urls.full}\n- Regular: ${img.urls.regular}\n- Small: ${img.urls.small}\n- Thumbnail: ${img.urls.thumb}\n\n**Links**:\n- View on Unsplash: ${img.links.html}\n- Download: ${img.links.download}\n${img.user.portfolio_url ? `- Photographer Portfolio: ${img.user.portfolio_url}` : ''}\n`.trim();\n })\n .join('\\n\\n' + '-'.repeat(80) + '\\n\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Found ${images.length} image(s) for \"${input.query}\":\\n\\n${formattedResults}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n}\n","/**\n * MCP Server Setup\n *\n * DESIGN PATTERNS:\n * - Factory pattern for server creation\n * - Tool registration pattern\n *\n * CODING STANDARDS:\n * - Register all tools, resources, and prompts here\n * - Keep server setup modular and extensible\n * - Import tools from ../tools/ and register them in the handlers\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { ReadImageTool, type ReadImageToolInput, UnsplashSearchTool, type UnsplashSearchToolInput } from '../tools';\n\nexport function createServer(): Server {\n const server = new Server(\n {\n name: 'imagine-mcp',\n version: '0.1.0',\n },\n {\n capabilities: {\n tools: {},\n },\n },\n );\n\n // Initialize tools\n const unsplashSearchTool = new UnsplashSearchTool();\n const readImageTool = new ReadImageTool();\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [unsplashSearchTool.getDefinition(), readImageTool.getDefinition()],\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n if (name === UnsplashSearchTool.TOOL_NAME) {\n return await unsplashSearchTool.execute(args as unknown as UnsplashSearchToolInput);\n }\n if (name === ReadImageTool.TOOL_NAME) {\n return await readImageTool.execute(args as unknown as ReadImageToolInput);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: `Unknown tool: ${name}`,\n },\n ],\n isError: true,\n };\n });\n\n return server;\n}\n","/**\n * HTTP Transport Handler\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Session management for stateful connections\n * - Streamable HTTP protocol (2025-03-26) with resumability support\n * - Factory pattern for creating MCP server instances per session\n *\n * CODING STANDARDS:\n * - Use async/await for all asynchronous operations\n * - Implement proper session lifecycle management\n * - Handle errors gracefully with appropriate HTTP status codes\n * - Provide health check endpoint for monitoring\n * - Clean up resources on shutdown\n *\n * AVOID:\n * - Sharing MCP server instances across sessions (use factory pattern)\n * - Forgetting to clean up sessions on disconnect\n * - Missing error handling for request processing\n * - Hardcoded configuration (use TransportConfig)\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';\nimport express, { type Request, type Response } from 'express';\nimport type { HttpTransportHandler as IHttpTransportHandler, TransportConfig } from '../types/index.js';\n\n/**\n * Session data for HTTP connections\n */\ninterface HttpSession {\n transport: StreamableHTTPServerTransport;\n server: McpServer;\n}\n\n/**\n * HTTP session manager\n */\nclass HttpFullSessionManager {\n private sessions: Map<string, HttpSession> = new Map();\n\n getSession(sessionId: string): HttpSession | undefined {\n return this.sessions.get(sessionId);\n }\n\n setSession(sessionId: string, transport: StreamableHTTPServerTransport, server: McpServer): void {\n this.sessions.set(sessionId, { transport, server });\n }\n\n deleteSession(sessionId: string): void {\n const session = this.sessions.get(sessionId);\n if (session) {\n session.server.close();\n }\n this.sessions.delete(sessionId);\n }\n\n hasSession(sessionId: string): boolean {\n return this.sessions.has(sessionId);\n }\n\n clear(): void {\n for (const session of this.sessions.values()) {\n session.server.close();\n }\n this.sessions.clear();\n }\n}\n\n/**\n * HTTP transport handler using Streamable HTTP (protocol version 2025-03-26)\n * Provides stateful session management with resumability support\n */\nexport class HttpTransportHandler implements IHttpTransportHandler {\n private serverFactory: () => McpServer;\n private app: express.Application;\n private server: HttpServer | null = null;\n private sessionManager: HttpFullSessionManager;\n private config: Required<TransportConfig>;\n\n constructor(serverFactory: McpServer | (() => McpServer), config: TransportConfig) {\n // Support both a factory function and a direct server instance for backwards compatibility\n this.serverFactory = typeof serverFactory === 'function' ? serverFactory : () => serverFactory;\n this.app = express();\n this.sessionManager = new HttpFullSessionManager();\n this.config = {\n mode: config.mode,\n port: config.port ?? 3000,\n host: config.host ?? 'localhost',\n };\n\n this.setupMiddleware();\n this.setupRoutes();\n }\n\n private setupMiddleware(): void {\n this.app.use(express.json());\n }\n\n private setupRoutes(): void {\n // Handle POST requests for client-to-server communication\n this.app.post('/mcp', async (req: Request, res: Response) => {\n await this.handlePostRequest(req, res);\n });\n\n // Handle GET requests for server-to-client notifications via SSE\n this.app.get('/mcp', async (req: Request, res: Response) => {\n await this.handleGetRequest(req, res);\n });\n\n // Handle DELETE requests for session termination\n this.app.delete('/mcp', async (req: Request, res: Response) => {\n await this.handleDeleteRequest(req, res);\n });\n\n // Health check endpoint\n this.app.get('/health', (_req: Request, res: Response) => {\n res.json({ status: 'ok', transport: 'http' });\n });\n }\n\n private async handlePostRequest(req: Request, res: Response): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n let transport: StreamableHTTPServerTransport;\n\n if (sessionId && this.sessionManager.hasSession(sessionId)) {\n // Reuse existing transport\n const session = this.sessionManager.getSession(sessionId)!;\n transport = session.transport;\n } else if (!sessionId && isInitializeRequest(req.body)) {\n // New initialization request - create new server instance\n const mcpServer = this.serverFactory();\n\n transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n enableJsonResponse: true, // Return JSON instead of SSE for simple request/response\n onsessioninitialized: (sessionId) => {\n this.sessionManager.setSession(sessionId, transport, mcpServer);\n },\n });\n\n // Clean up transport when closed\n transport.onclose = () => {\n if (transport.sessionId) {\n this.sessionManager.deleteSession(transport.sessionId);\n }\n };\n\n // Connect the new MCP server instance to the transport\n await mcpServer.connect(transport);\n } else {\n // Invalid request\n res.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Bad Request: No valid session ID provided',\n },\n id: null,\n });\n return;\n }\n\n // Handle the request\n await transport.handleRequest(req, res, req.body);\n }\n\n private async handleGetRequest(req: Request, res: Response): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n if (!sessionId || !this.sessionManager.hasSession(sessionId)) {\n res.status(400).send('Invalid or missing session ID');\n return;\n }\n\n const session = this.sessionManager.getSession(sessionId)!;\n await session.transport.handleRequest(req, res);\n }\n\n private async handleDeleteRequest(req: Request, res: Response): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n if (!sessionId || !this.sessionManager.hasSession(sessionId)) {\n res.status(400).send('Invalid or missing session ID');\n return;\n }\n\n const session = this.sessionManager.getSession(sessionId)!;\n await session.transport.handleRequest(req, res);\n\n // Clean up session\n this.sessionManager.deleteSession(sessionId);\n }\n\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.server = this.app.listen(this.config.port, this.config.host, () => {\n process.stderr.write(\n `imagine-mcp MCP server started on http://${this.config.host}:${this.config.port}/mcp\\n`,\n );\n process.stderr.write(`Health check: http://${this.config.host}:${this.config.port}/health\\n`);\n resolve();\n });\n\n this.server.on('error', (error: Error) => {\n reject(error);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.server) {\n // Clear all sessions\n this.sessionManager.clear();\n\n this.server.close((err?: Error) => {\n if (err) {\n reject(err);\n } else {\n this.server = null;\n resolve();\n }\n });\n } else {\n resolve();\n }\n });\n }\n\n getPort(): number {\n return this.config.port;\n }\n\n getHost(): string {\n return this.config.host;\n }\n}\n","/**\n * SSE Transport Handler\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Session management for stateful SSE connections\n * - Legacy SSE protocol (2024-11-05) with separate endpoints\n * - Factory pattern for creating MCP server instances per connection\n *\n * CODING STANDARDS:\n * - Use async/await for all asynchronous operations\n * - Implement proper session lifecycle management\n * - Handle connection cleanup on client disconnect\n * - Provide health check endpoint for monitoring\n * - Clean up resources on shutdown\n *\n * AVOID:\n * - Sharing MCP server instances across sessions (use factory pattern)\n * - Forgetting to clean up sessions on disconnect\n * - Missing error handling for connection/message processing\n * - Hardcoded configuration (use TransportConfig)\n */\n\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js';\nimport { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\nimport express, { type Request, type Response } from 'express';\nimport type { HttpTransportHandler as IHttpTransportHandler, TransportConfig } from '../types/index.js';\n\n/**\n * Session data for SSE connections\n */\ninterface SseSession {\n transport: SSEServerTransport;\n server: McpServer;\n}\n\n/**\n * Session manager for SSE transports\n */\nclass SseSessionManager {\n private sessions: Map<string, SseSession> = new Map();\n\n getSession(sessionId: string): SSEServerTransport | undefined {\n return this.sessions.get(sessionId)?.transport;\n }\n\n setSession(sessionId: string, transport: SSEServerTransport, server: McpServer): void {\n this.sessions.set(sessionId, { transport, server });\n }\n\n deleteSession(sessionId: string): void {\n const session = this.sessions.get(sessionId);\n if (session) {\n // Close the server instance\n session.server.close();\n }\n this.sessions.delete(sessionId);\n }\n\n hasSession(sessionId: string): boolean {\n return this.sessions.has(sessionId);\n }\n\n clear(): void {\n // Close all server instances\n for (const session of this.sessions.values()) {\n session.server.close();\n }\n this.sessions.clear();\n }\n}\n\n/**\n * SSE (Server-Sent Events) transport handler\n * Legacy transport for backwards compatibility (protocol version 2024-11-05)\n * Uses separate endpoints: /sse for SSE stream (GET) and /messages for client messages (POST)\n */\nexport class SseTransportHandler implements IHttpTransportHandler {\n private serverFactory: () => McpServer;\n private app: express.Application;\n private server: HttpServer | null = null;\n private sessionManager: SseSessionManager;\n private config: Required<TransportConfig>;\n\n constructor(serverFactory: McpServer | (() => McpServer), config: TransportConfig) {\n // Support both a factory function and a direct server instance for backwards compatibility\n this.serverFactory = typeof serverFactory === 'function' ? serverFactory : () => serverFactory;\n this.app = express();\n this.sessionManager = new SseSessionManager();\n this.config = {\n mode: config.mode,\n port: config.port ?? 3000,\n host: config.host ?? 'localhost',\n };\n\n this.setupMiddleware();\n this.setupRoutes();\n }\n\n private setupMiddleware(): void {\n this.app.use(express.json());\n }\n\n private setupRoutes(): void {\n // SSE endpoint - establishes the SSE stream\n this.app.get('/sse', async (req: Request, res: Response) => {\n await this.handleSseConnection(req, res);\n });\n\n // Messages endpoint - receives client messages\n this.app.post('/messages', async (req: Request, res: Response) => {\n await this.handlePostMessage(req, res);\n });\n\n // Health check endpoint\n this.app.get('/health', (_req: Request, res: Response) => {\n res.json({ status: 'ok', transport: 'sse' });\n });\n }\n\n private async handleSseConnection(_req: Request, res: Response): Promise<void> {\n try {\n // Create a new MCP server instance for this SSE connection\n const mcpServer = this.serverFactory();\n\n // Create SSE transport\n const transport = new SSEServerTransport('/messages', res);\n\n // Store the transport and server\n this.sessionManager.setSession(transport.sessionId, transport, mcpServer);\n\n // Clean up when connection closes\n res.on('close', () => {\n this.sessionManager.deleteSession(transport.sessionId);\n });\n\n // Connect the new server instance to the transport\n await mcpServer.connect(transport);\n\n process.stderr.write(`SSE session established: ${transport.sessionId}\\n`);\n } catch (error) {\n process.stderr.write(\n `Error handling SSE connection: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n if (!res.headersSent) {\n res.status(500).send('Internal Server Error');\n }\n }\n }\n\n private async handlePostMessage(req: Request, res: Response): Promise<void> {\n const sessionId = req.query.sessionId as string;\n\n if (!sessionId) {\n res.status(400).send('Missing sessionId query parameter');\n return;\n }\n\n const transport = this.sessionManager.getSession(sessionId);\n\n if (!transport) {\n res.status(404).send('No transport found for sessionId');\n return;\n }\n\n try {\n await transport.handlePostMessage(req, res, req.body);\n } catch (error) {\n process.stderr.write(`Error handling post message: ${error instanceof Error ? error.message : String(error)}\\n`);\n if (!res.headersSent) {\n res.status(500).send('Internal Server Error');\n }\n }\n }\n\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.server = this.app.listen(this.config.port, this.config.host, () => {\n process.stderr.write(\n `imagine-mcp MCP server started with SSE transport on http://${this.config.host}:${this.config.port}\\n`,\n );\n process.stderr.write(`SSE endpoint: http://${this.config.host}:${this.config.port}/sse\\n`);\n process.stderr.write(`Messages endpoint: http://${this.config.host}:${this.config.port}/messages\\n`);\n process.stderr.write(`Health check: http://${this.config.host}:${this.config.port}/health\\n`);\n resolve();\n });\n\n this.server.on('error', (error: Error) => {\n reject(error);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.server) {\n // Clear all sessions\n this.sessionManager.clear();\n\n this.server.close((err?: Error) => {\n if (err) {\n reject(err);\n } else {\n this.server = null;\n resolve();\n }\n });\n } else {\n resolve();\n }\n });\n }\n\n getPort(): number {\n return this.config.port;\n }\n\n getHost(): string {\n return this.config.host;\n }\n}\n","/**\n * STDIO Transport\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Standard I/O based communication for CLI usage\n *\n * CODING STANDARDS:\n * - Initialize server and transport properly\n * - Handle cleanup on shutdown with stop() method\n * - Use async/await for all operations\n *\n * AVOID:\n * - Forgetting to close transport on shutdown\n * - Missing error handling for connection failures\n */\n\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { TransportHandler } from '../types/index.js';\n\n/**\n * Stdio transport handler for MCP server\n * Used for command-line and direct integrations\n */\nexport class StdioTransportHandler implements TransportHandler {\n private server: Server;\n private transport: StdioServerTransport | null = null;\n\n constructor(server: Server) {\n this.server = server;\n }\n\n async start(): Promise<void> {\n this.transport = new StdioServerTransport();\n await this.server.connect(this.transport);\n process.stderr.write('imagine-mcp MCP server started on stdio\\n');\n }\n\n async stop(): Promise<void> {\n if (this.transport) {\n await this.transport.close();\n this.transport = null;\n }\n }\n}\n"],"mappings":"q8BAuEA,SAAgB,EAAkB,EAA0B,CAE1D,IAAI,EAAiB,EAAS,QAAQ,QAAS,IAAI,CAcnD,MAXA,GAAiB,EACd,QAAQ,QAAS,IAAI,CACrB,QAAQ,QAAS,IAAI,CACrB,QAAQ,QAAS,IAAI,CACrB,QAAQ,SAAU,IAAI,CACtB,QAAQ,SAAU,IAAI,CACtB,QAAQ,SAAU,IAAI,CACtB,QAAQ,SAAU,IAAI,CACtB,QAAQ,SAAU,IAAI,CACtB,QAAQ,SAAU,IAAI,CAElB,ECxDT,MAAM,EAAoB,2BACpBA,EAA4C,CAChD,OAAQ,YACR,OAAQ,aACR,QAAS,aACT,OAAQ,YACR,QAAS,aACT,OAAQ,YACR,OAAQ,gBACR,QAAS,aACT,QAAS,aACV,CAED,IAAa,EAAb,KAAyB,CACvB,MAAM,UAAU,EAAgB,EAA4B,EAAE,CAA4B,CACxF,IAAM,EAAc,MAAM,KAAK,mBAAmB,EAAO,CACnD,GAAA,EAAA,EAAA,YAAoB,SAAS,CAAC,OAAO,EAAY,KAAK,CAAC,OAAO,MAAM,CAEpEC,EAA0B,CAC9B,OAAQ,EAAY,OACpB,WAAY,EAAY,WACxB,SAAU,EAAY,SACtB,UAAW,EAAY,UACvB,SACD,CAUD,OARI,EAAY,aAAe,aAC7B,EAAO,UAAA,EAAA,EAAA,UAAoB,EAAY,OAAO,EAG5C,EAAQ,gBACV,EAAO,OAAS,EAAY,KAAK,SAAS,SAAS,EAG9C,EAGT,MAAc,mBAAmB,EAAsC,CACrE,IAAM,EAAU,EAAO,MAAM,CAE7B,GAAI,CAAC,EACH,MAAM,IAAIC,EAAAA,SAASC,EAAAA,UAAU,cAAe,gCAAgC,CAW9E,OARI,EAAQ,WAAW,QAAQ,CACtB,KAAK,eAAe,EAAQ,CAGjC,gBAAgB,KAAK,EAAQ,CACxB,KAAK,mBAAmB,EAAQ,CAGlC,KAAK,YAAY,EAAQ,CAGlC,MAAc,YAAY,EAAwC,CAChE,IAAM,GAAA,EAAA,EAAA,SAAyB,EAAkB,EAAS,CAAC,CAE3D,GAAI,CACF,MAAA,EAAA,EAAA,QAAa,EAAgBC,EAAAA,UAAU,KAAK,MACtC,CACN,MAAM,IAAIF,EAAAA,SAASC,EAAAA,UAAU,cAAe,iCAAiC,IAAW,CAG1F,IAAIE,EACJ,GAAI,CACF,EAAO,MAAA,EAAA,EAAA,UAAe,EAAe,OAC9B,EAAO,CACd,MAAM,IAAIH,EAAAA,SACRC,EAAAA,UAAU,cACV,8BAA8B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GACrF,CAMH,MAAO,CACL,OAAQ,EACR,WAAY,OACZ,SALe,GAAA,EAAA,EAAA,SADS,EAAe,CAAC,aAAa,GACN,EAM/C,UAAW,EAAK,OAChB,OACD,CAGH,MAAc,mBAAmB,EAAmC,CAClE,GAAI,CACF,IAAM,EAAW,MAAA,EAAA,EAAA,SAAY,EAAK,CAAE,SAAU,SAAU,CAAC,CACzD,GAAI,CAAC,EAAS,GACZ,MAAM,IAAID,EAAAA,SACRC,EAAAA,UAAU,cACV,mCAAmC,EAAI,IAAI,EAAS,OAAO,GAAG,EAAS,WAAW,GACnF,CAGH,IAAM,EAAc,MAAM,EAAS,aAAa,CAC1C,EAAO,OAAO,KAAK,EAAY,CAC/B,EAAc,EAAS,QAAQ,IAAI,eAAe,EAAE,MAAM,IAAI,CAAC,GAC/D,GAAA,EAAA,EAAA,UAAoB,IAAI,IAAI,EAAI,CAAC,UAAY,QAAQ,CAE3D,MAAO,CACL,OAAQ,EACR,WAAY,MACZ,SAAU,GAAe,GAAA,EAAA,EAAA,SAA0B,EAAS,GAAK,EACjE,UAAW,EAAK,OAChB,OACD,OACM,EAAO,CAId,MAHI,aAAiBD,EAAAA,SACb,EAEF,IAAIA,EAAAA,SACRC,EAAAA,UAAU,cACV,sCAAsC,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAC7F,EAIL,eAAuB,EAA8B,CACnD,IAAM,EAAa,EAAQ,QAAQ,IAAI,CACvC,GAAI,IAAe,IAAM,IAAe,EAAQ,OAAS,EACvD,MAAM,IAAID,EAAAA,SACRC,EAAAA,UAAU,cACV,sEACD,CAGH,IAAM,EAAW,EAAQ,MAAM,EAAG,EAAW,CAAC,aAAa,CACrD,EAAU,EAAQ,MAAM,EAAa,EAAE,CACvC,EAAW,EAAS,SAAS,SAAS,CACtC,EAAmB,EAAS,MAAM,IAAI,CAAC,IAAM,EAEnD,GAAI,CACF,IAAM,EAAO,EAAW,OAAO,KAAK,EAAS,SAAS,CAAG,OAAO,KAAK,mBAAmB,EAAQ,CAAC,CACjG,MAAO,CACL,OAAQ,EACR,WAAY,WACZ,SAAU,GAAoB,EAC9B,UAAW,EAAK,OAChB,OACD,OACM,EAAO,CACd,MAAM,IAAID,EAAAA,SACRC,EAAAA,UAAU,cACV,sCAAsC,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAC7F,ICxKM,EAAb,MAAa,CAAkD,CAC7D,OAAgB,UAAY,aAE5B,QAAkB,IAAI,EAEtB,eAAgC,CAC9B,MAAO,CACL,KAAM,EAAc,UACpB,YAAa,yEACb,YAAa,CACX,KAAM,SACN,WAAY,CACV,OAAQ,CACN,KAAM,SACN,UAAW,EACX,YAAa,0DACd,CACD,cAAe,CACb,KAAM,UACN,YAAa,4CACb,QAAS,GACV,CACF,CACD,SAAU,CAAC,SAAS,CACpB,qBAAsB,GACvB,CACF,CAGH,MAAM,QAAQ,EAAoD,CAChE,GAAI,CACF,GAAI,CAAC,GAAS,OAAO,GAAU,SAC7B,MAAM,IAAIG,EAAAA,SAASC,EAAAA,UAAU,cAAe,gCAAgC,CAG9E,IAAM,EAAU,EACV,EAAS,EAAQ,OACjB,EAAgB,EAAQ,cAC9B,GAAI,CAAC,GAAU,OAAO,GAAW,UAAY,EAAO,MAAM,CAAC,SAAW,EACpE,MAAM,IAAID,EAAAA,SAASC,EAAAA,UAAU,cAAe,qCAAqC,CAEnF,GAAI,IAAkB,IAAA,IAAa,OAAO,GAAkB,UAC1D,MAAM,IAAID,EAAAA,SAASC,EAAAA,UAAU,cAAe,mCAAmC,CAGjF,IAAMC,EAA4B,CAChC,gBACD,CACK,EAAS,MAAM,KAAK,QAAQ,UAAU,EAAQ,EAAQ,CAE5D,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,KAAK,UAAU,EAAQ,KAAM,EAAE,CACtC,CACF,CACF,OACM,EAAO,CACd,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,UAAU,aAAiB,MAAQ,EAAM,QAAU,kBAC1D,CACF,CACD,QAAS,GACV,ICnEP,MAAa,EAAYC,EAAAA,EACtB,OAAO,CAEN,eAAgBA,EAAAA,EAAE,KAAK,CAAC,KAAM,aAAc,SAAS,CAAC,CAAC,UAAU,CAAC,QAAQ,KAAK,CAG/E,UAAWA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CAChC,kBAAmBA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CACxC,sBAAuBA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CAC5C,UAAWA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,YAAY,CACrD,YAAaA,EAAAA,EAAE,QAAQ,CAAC,IAAI,kCAAkC,CAAC,UAAU,CAGzE,qBAAsBA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CAC3C,4BAA6BA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CAClD,gCAAiCA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CACtD,qBAAsBA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,OAAO,CAC3D,uBAAwBA,EAAAA,EAAE,QAAQ,CAAC,IAAI,6CAA6C,CAAC,UAAU,CAG/F,cAAeA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CACpC,kBAAmBA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CACxC,wBAAyBA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CAG9C,oBAAqBA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CAC3C,CAAC,CACD,OACE,GAEK,EAAK,mBAAqB,EAAK,sBAC1B,EAAK,mBAAqB,EAAK,sBAEjC,GAET,CACE,QAAS,6EACT,KAAM,CAAC,oBAAoB,CAC5B,CACF,CCtCH,IAAM,EAAN,MAAM,CAAc,CAClB,OAAe,SACf,OAEA,aAAsB,CACpB,GAAI,CACF,KAAK,OAAS,EAAU,MAAM,QAAQ,IAAI,OACnC,EAAO,CACd,GAAI,aAAiBC,EAAAA,EAAE,SAAU,CAC/B,IAAM,EAAe,EAAM,OAAO,IAAK,GAAM,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,KAAK,KAAK,CAC5F,MAAM,IAAIC,EAAAA,SAASC,EAAAA,UAAU,cAAe,gDAAgD,IAAe,CAE7G,MAAM,GAOV,OAAc,aAA6B,CAIzC,MAHA,CACE,EAAc,WAAW,IAAI,EAExB,EAAc,SAMvB,WAA8B,CAC5B,OAAO,KAAK,OAMd,aAAqB,CACnB,MAAO,CACL,OAAQ,KAAK,OAAO,UACpB,YAAa,KAAK,OAAO,kBACzB,gBAAiB,KAAK,OAAO,sBAC7B,OAAQ,KAAK,OAAO,UACpB,SAAU,KAAK,OAAO,YACvB,CAMH,qBAA6B,CAC3B,MAAO,CACL,OAAQ,KAAK,OAAO,qBACpB,YAAa,KAAK,OAAO,4BACzB,gBAAiB,KAAK,OAAO,gCAC7B,OAAQ,KAAK,OAAO,qBACpB,SAAU,KAAK,OAAO,uBACvB,CAMH,iBAAyB,CACvB,MAAO,CACL,OAAQ,KAAK,OAAO,cACpB,UAAW,KAAK,OAAO,kBACvB,gBAAiB,KAAK,OAAO,wBAC9B,CAMH,kBAA0B,CACxB,OAAO,KAAK,OAAO,eAMrB,mBAA2B,CACzB,MAAO,CACL,UAAW,KAAK,OAAO,oBACxB,CAMH,sBAA6B,EAA+C,CAC1E,OAAQ,EAAR,CACE,IAAK,KACH,GAAI,CAAC,KAAK,OAAO,UACf,MAAM,IAAID,EAAAA,SAASC,EAAAA,UAAU,cAAe,8CAA8C,CAE5F,MACF,IAAK,aACH,GAAI,CAAC,KAAK,OAAO,qBACf,MAAM,IAAID,EAAAA,SAASC,EAAAA,UAAU,cAAe,iEAAiE,CAE/G,GAAI,CAAC,KAAK,OAAO,6BAA+B,CAAC,KAAK,OAAO,gCAC3D,MAAM,IAAID,EAAAA,SACRC,EAAAA,UAAU,cACV,6GACD,CAEH,GAAI,CAAC,KAAK,OAAO,uBACf,MAAM,IAAID,EAAAA,SACRC,EAAAA,UAAU,cACV,mEACD,CAEH,MACF,IAAK,SACH,GAAI,CAAC,KAAK,OAAO,cACf,MAAM,IAAID,EAAAA,SAASC,EAAAA,UAAU,cAAe,4DAA4D,CAE1G,GAAI,CAAC,KAAK,OAAO,kBACf,MAAM,IAAID,EAAAA,SAASC,EAAAA,UAAU,cAAe,gEAAgE,CAE9G,SAMR,MAAa,EAAS,EAAc,aAAa,CCtEjD,IAAa,EAAb,KAA6B,CAC3B,IAEA,YAAY,EAAoB,CAC9B,IAAM,EAAiB,EAAO,mBAAmB,CAC3C,EAAS,GAAa,EAAe,UAE3C,GAAI,CAAC,EACH,MAAU,MAAM,8EAA8E,CAGhG,KAAK,KAAA,EAAA,EAAA,WAAgB,CACnB,UAAW,EACZ,CAAC,CAGJ,MAAM,aAAa,EAAsD,CACvE,GAAI,CACF,IAAM,EAAS,MAAM,KAAK,IAAI,OAAO,UAAU,CAC7C,MAAO,EAAO,MACd,KAAM,EAAO,MAAQ,EACrB,QAAS,EAAO,SAAW,GAC3B,YAAa,EAAO,YACpB,MAAO,EAAO,MACf,CAAC,CAEF,GAAI,EAAO,OACT,MAAU,MAAM,uBAAuB,EAAO,OAAO,KAAK,KAAK,GAAG,CAGpE,GAAI,CAAC,EAAO,SACV,MAAU,MAAM,gCAAgC,CAGlD,OAAO,EAAO,SAAS,QAAQ,IAAK,IAAW,CAC7C,GAAI,EAAM,GACV,YAAa,EAAM,YACnB,gBAAiB,EAAM,gBACvB,KAAM,CACJ,IAAK,EAAM,KAAK,IAChB,KAAM,EAAM,KAAK,KACjB,QAAS,EAAM,KAAK,QACpB,MAAO,EAAM,KAAK,MAClB,MAAO,EAAM,KAAK,MACnB,CACD,MAAO,CACL,KAAM,EAAM,MAAM,KAClB,SAAU,EAAM,MAAM,SACvB,CACD,KAAM,CACJ,KAAM,EAAM,KAAK,KACjB,SAAU,EAAM,KAAK,SACrB,cAAe,EAAM,KAAK,cAC3B,CACD,MAAO,EAAM,MACb,OAAQ,EAAM,OACd,MAAO,EAAM,OAAS,UACtB,MAAO,EAAM,MACd,EAAE,OACI,EAAO,CACd,MAAU,MAAM,qCAAqC,aAAiB,MAAQ,EAAM,QAAU,kBAAmB,CAC/G,MAAO,EACR,CAAC,ICpFK,EAAb,MAAa,CAA4D,CACvE,OAAgB,UAAY,kBAE5B,QACA,UAEA,YAAY,EAAoB,CAC9B,KAAK,UAAY,EACjB,KAAK,QAAU,KAGjB,YAAsC,CAIpC,MAHA,CACE,KAAK,UAAU,IAAI,EAAgB,KAAK,UAAU,CAE7C,KAAK,QAGd,eAAgC,CAC9B,MAAO,CACL,KAAM,EAAmB,UACzB,YAAa,uFACb,YAAa,CACX,KAAM,SACN,WAAY,CACV,MAAO,CACL,KAAM,SACN,YAAa,wDACd,CACD,QAAS,CACP,KAAM,SACN,YAAa,iDACb,QAAS,EACT,QAAS,GACV,CACD,KAAM,CACJ,KAAM,SACN,YAAa,0CACb,QAAS,EACV,CACD,YAAa,CACX,KAAM,SACN,KAAM,CAAC,YAAa,WAAY,WAAW,CAC3C,YAAa,8BACd,CACD,MAAO,CACL,KAAM,SACN,KAAM,CACJ,kBACA,QACA,QACA,SACA,SACA,MACA,SACA,UACA,QACA,OACA,OACD,CACD,YAAa,wBACd,CACF,CACD,SAAU,CAAC,QAAQ,CACnB,qBAAsB,GACvB,CACF,CAGH,MAAM,QAAQ,EAAyD,CACrE,GAAI,CACF,IAAM,EAAS,MAAM,KAAK,YAAY,CAAC,aAAa,CAClD,MAAO,EAAM,MACb,QAAS,EAAM,QACf,KAAM,EAAM,KACZ,YAAa,EAAM,YACnB,MAAO,EAAM,MACd,CAAC,CAEF,GAAI,EAAO,SAAW,EACpB,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,+BAA+B,EAAM,MAAM,GAClD,CACF,CACF,CAGH,IAAM,EAAmB,EACtB,KAAK,EAAK,IACF;aACJ,EAAQ,EAAE;YACX,EAAI,GAAG;qBACE,EAAI,iBAAmB,EAAI,aAAe,iBAAiB;sBAC1D,EAAI,KAAK,KAAK,KAAK,EAAI,KAAK,SAAS;oBACvC,EAAI,MAAM,GAAG,EAAI,OAAO;eAC7B,EAAI,MAAM;eACV,EAAI,MAAM;;;UAGf,EAAI,KAAK,KAAK;aACX,EAAI,KAAK,QAAQ;WACnB,EAAI,KAAK,MAAM;eACX,EAAI,KAAK,MAAM;;;sBAGR,EAAI,MAAM,KAAK;cACvB,EAAI,MAAM,SAAS;EAC/B,EAAI,KAAK,cAAgB,6BAA6B,EAAI,KAAK,gBAAkB,GAAG;EACpF,MAAM,CACE,CACD,KAAK;;EAAS,IAAI,OAAO,GAAG,CAAG;;EAAO,CAEzC,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,SAAS,EAAO,OAAO,iBAAiB,EAAM,MAAM,QAAQ,IACnE,CACF,CACF,OACM,EAAO,CACd,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,UAAU,aAAiB,MAAQ,EAAM,QAAU,kBAC1D,CACF,CACD,QAAS,GACV,IC/JP,SAAgB,GAAuB,CACrC,IAAM,EAAS,IAAIC,EAAAA,OACjB,CACE,KAAM,cACN,QAAS,QACV,CACD,CACE,aAAc,CACZ,MAAO,EAAE,CACV,CACF,CACF,CAGK,EAAqB,IAAI,EACzB,EAAgB,IAAI,EA2B1B,OAzBA,EAAO,kBAAkBC,EAAAA,uBAAwB,UAAa,CAC5D,MAAO,CAAC,EAAmB,eAAe,CAAE,EAAc,eAAe,CAAC,CAC3E,EAAE,CAEH,EAAO,kBAAkBC,EAAAA,sBAAuB,KAAO,IAAY,CACjE,GAAM,CAAE,OAAM,UAAW,GAAS,EAAQ,OAS1C,OAPI,IAAS,EAAmB,UACvB,MAAM,EAAmB,QAAQ,EAA2C,CAEjF,IAAS,EAAc,UAClB,MAAM,EAAc,QAAQ,EAAsC,CAGpE,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,iBAAiB,IACxB,CACF,CACD,QAAS,GACV,EACD,CAEK,ECjBT,IAAM,EAAN,KAA6B,CAC3B,SAA6C,IAAI,IAEjD,WAAW,EAA4C,CACrD,OAAO,KAAK,SAAS,IAAI,EAAU,CAGrC,WAAW,EAAmB,EAA0C,EAAyB,CAC/F,KAAK,SAAS,IAAI,EAAW,CAAE,YAAW,SAAQ,CAAC,CAGrD,cAAc,EAAyB,CACrC,IAAM,EAAU,KAAK,SAAS,IAAI,EAAU,CACxC,GACF,EAAQ,OAAO,OAAO,CAExB,KAAK,SAAS,OAAO,EAAU,CAGjC,WAAW,EAA4B,CACrC,OAAO,KAAK,SAAS,IAAI,EAAU,CAGrC,OAAc,CACZ,IAAK,IAAM,KAAW,KAAK,SAAS,QAAQ,CAC1C,EAAQ,OAAO,OAAO,CAExB,KAAK,SAAS,OAAO,GAQZ,EAAb,KAAmE,CACjE,cACA,IACA,OAAoC,KACpC,eACA,OAEA,YAAY,EAA8C,EAAyB,CAEjF,KAAK,cAAgB,OAAO,GAAkB,WAAa,MAAsB,EACjF,KAAK,KAAA,EAAA,EAAA,UAAe,CACpB,KAAK,eAAiB,IAAI,EAC1B,KAAK,OAAS,CACZ,KAAMC,EAAO,KACb,KAAMA,EAAO,MAAQ,IACrB,KAAMA,EAAO,MAAQ,YACtB,CAED,KAAK,iBAAiB,CACtB,KAAK,aAAa,CAGpB,iBAAgC,CAC9B,KAAK,IAAI,IAAI,EAAA,QAAQ,MAAM,CAAC,CAG9B,aAA4B,CAE1B,KAAK,IAAI,KAAK,OAAQ,MAAO,EAAc,IAAkB,CAC3D,MAAM,KAAK,kBAAkB,EAAK,EAAI,EACtC,CAGF,KAAK,IAAI,IAAI,OAAQ,MAAO,EAAc,IAAkB,CAC1D,MAAM,KAAK,iBAAiB,EAAK,EAAI,EACrC,CAGF,KAAK,IAAI,OAAO,OAAQ,MAAO,EAAc,IAAkB,CAC7D,MAAM,KAAK,oBAAoB,EAAK,EAAI,EACxC,CAGF,KAAK,IAAI,IAAI,WAAY,EAAe,IAAkB,CACxD,EAAI,KAAK,CAAE,OAAQ,KAAM,UAAW,OAAQ,CAAC,EAC7C,CAGJ,MAAc,kBAAkB,EAAc,EAA8B,CAC1E,IAAM,EAAY,EAAI,QAAQ,kBAC1BC,EAEJ,GAAI,GAAa,KAAK,eAAe,WAAW,EAAU,CAGxD,EADgB,KAAK,eAAe,WAAW,EAAU,CACrC,kBACX,CAAC,IAAA,EAAA,EAAA,qBAAiC,EAAI,KAAK,CAAE,CAEtD,IAAM,EAAY,KAAK,eAAe,CAEtC,EAAY,IAAIC,EAAAA,8BAA8B,CAC5C,wBAAA,EAAA,EAAA,aAAsC,CACtC,mBAAoB,GACpB,qBAAuB,GAAc,CACnC,KAAK,eAAe,WAAWC,EAAW,EAAW,EAAU,EAElE,CAAC,CAGF,EAAU,YAAgB,CACpB,EAAU,WACZ,KAAK,eAAe,cAAc,EAAU,UAAU,EAK1D,MAAM,EAAU,QAAQ,EAAU,KAC7B,CAEL,EAAI,OAAO,IAAI,CAAC,KAAK,CACnB,QAAS,MACT,MAAO,CACL,KAAM,MACN,QAAS,4CACV,CACD,GAAI,KACL,CAAC,CACF,OAIF,MAAM,EAAU,cAAc,EAAK,EAAK,EAAI,KAAK,CAGnD,MAAc,iBAAiB,EAAc,EAA8B,CACzE,IAAM,EAAY,EAAI,QAAQ,kBAE9B,GAAI,CAAC,GAAa,CAAC,KAAK,eAAe,WAAW,EAAU,CAAE,CAC5D,EAAI,OAAO,IAAI,CAAC,KAAK,gCAAgC,CACrD,OAIF,MADgB,KAAK,eAAe,WAAW,EAAU,CAC3C,UAAU,cAAc,EAAK,EAAI,CAGjD,MAAc,oBAAoB,EAAc,EAA8B,CAC5E,IAAM,EAAY,EAAI,QAAQ,kBAE9B,GAAI,CAAC,GAAa,CAAC,KAAK,eAAe,WAAW,EAAU,CAAE,CAC5D,EAAI,OAAO,IAAI,CAAC,KAAK,gCAAgC,CACrD,OAIF,MADgB,KAAK,eAAe,WAAW,EAAU,CAC3C,UAAU,cAAc,EAAK,EAAI,CAG/C,KAAK,eAAe,cAAc,EAAU,CAG9C,MAAM,OAAuB,CAC3B,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,GAAI,CACF,KAAK,OAAS,KAAK,IAAI,OAAO,KAAK,OAAO,KAAM,KAAK,OAAO,SAAY,CACtE,QAAQ,OAAO,MACb,4CAA4C,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,QAClF,CACD,QAAQ,OAAO,MAAM,wBAAwB,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,WAAW,CAC7F,GAAS,EACT,CAEF,KAAK,OAAO,GAAG,QAAU,GAAiB,CACxC,EAAO,EAAM,EACb,OACK,EAAO,CACd,EAAO,EAAM,GAEf,CAGJ,MAAM,MAAsB,CAC1B,OAAO,IAAI,SAAS,EAAS,IAAW,CAClC,KAAK,QAEP,KAAK,eAAe,OAAO,CAE3B,KAAK,OAAO,MAAO,GAAgB,CAC7B,EACF,EAAO,EAAI,EAEX,KAAK,OAAS,KACd,GAAS,GAEX,EAEF,GAAS,EAEX,CAGJ,SAAkB,CAChB,OAAO,KAAK,OAAO,KAGrB,SAAkB,CAChB,OAAO,KAAK,OAAO,OC3MjB,EAAN,KAAwB,CACtB,SAA4C,IAAI,IAEhD,WAAW,EAAmD,CAC5D,OAAO,KAAK,SAAS,IAAI,EAAU,EAAE,UAGvC,WAAW,EAAmB,EAA+B,EAAyB,CACpF,KAAK,SAAS,IAAI,EAAW,CAAE,YAAW,SAAQ,CAAC,CAGrD,cAAc,EAAyB,CACrC,IAAM,EAAU,KAAK,SAAS,IAAI,EAAU,CACxC,GAEF,EAAQ,OAAO,OAAO,CAExB,KAAK,SAAS,OAAO,EAAU,CAGjC,WAAW,EAA4B,CACrC,OAAO,KAAK,SAAS,IAAI,EAAU,CAGrC,OAAc,CAEZ,IAAK,IAAM,KAAW,KAAK,SAAS,QAAQ,CAC1C,EAAQ,OAAO,OAAO,CAExB,KAAK,SAAS,OAAO,GASZ,EAAb,KAAkE,CAChE,cACA,IACA,OAAoC,KACpC,eACA,OAEA,YAAY,EAA8C,EAAyB,CAEjF,KAAK,cAAgB,OAAO,GAAkB,WAAa,MAAsB,EACjF,KAAK,KAAA,EAAA,EAAA,UAAe,CACpB,KAAK,eAAiB,IAAI,EAC1B,KAAK,OAAS,CACZ,KAAMC,EAAO,KACb,KAAMA,EAAO,MAAQ,IACrB,KAAMA,EAAO,MAAQ,YACtB,CAED,KAAK,iBAAiB,CACtB,KAAK,aAAa,CAGpB,iBAAgC,CAC9B,KAAK,IAAI,IAAI,EAAA,QAAQ,MAAM,CAAC,CAG9B,aAA4B,CAE1B,KAAK,IAAI,IAAI,OAAQ,MAAO,EAAc,IAAkB,CAC1D,MAAM,KAAK,oBAAoB,EAAK,EAAI,EACxC,CAGF,KAAK,IAAI,KAAK,YAAa,MAAO,EAAc,IAAkB,CAChE,MAAM,KAAK,kBAAkB,EAAK,EAAI,EACtC,CAGF,KAAK,IAAI,IAAI,WAAY,EAAe,IAAkB,CACxD,EAAI,KAAK,CAAE,OAAQ,KAAM,UAAW,MAAO,CAAC,EAC5C,CAGJ,MAAc,oBAAoB,EAAe,EAA8B,CAC7E,GAAI,CAEF,IAAM,EAAY,KAAK,eAAe,CAGhC,EAAY,IAAIC,EAAAA,mBAAmB,YAAa,EAAI,CAG1D,KAAK,eAAe,WAAW,EAAU,UAAW,EAAW,EAAU,CAGzE,EAAI,GAAG,YAAe,CACpB,KAAK,eAAe,cAAc,EAAU,UAAU,EACtD,CAGF,MAAM,EAAU,QAAQ,EAAU,CAElC,QAAQ,OAAO,MAAM,4BAA4B,EAAU,UAAU,IAAI,OAClE,EAAO,CACd,QAAQ,OAAO,MACb,kCAAkC,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAAC,IAC1F,CACI,EAAI,aACP,EAAI,OAAO,IAAI,CAAC,KAAK,wBAAwB,EAKnD,MAAc,kBAAkB,EAAc,EAA8B,CAC1E,IAAM,EAAY,EAAI,MAAM,UAE5B,GAAI,CAAC,EAAW,CACd,EAAI,OAAO,IAAI,CAAC,KAAK,oCAAoC,CACzD,OAGF,IAAM,EAAY,KAAK,eAAe,WAAW,EAAU,CAE3D,GAAI,CAAC,EAAW,CACd,EAAI,OAAO,IAAI,CAAC,KAAK,mCAAmC,CACxD,OAGF,GAAI,CACF,MAAM,EAAU,kBAAkB,EAAK,EAAK,EAAI,KAAK,OAC9C,EAAO,CACd,QAAQ,OAAO,MAAM,gCAAgC,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAAC,IAAI,CAC3G,EAAI,aACP,EAAI,OAAO,IAAI,CAAC,KAAK,wBAAwB,EAKnD,MAAM,OAAuB,CAC3B,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,GAAI,CACF,KAAK,OAAS,KAAK,IAAI,OAAO,KAAK,OAAO,KAAM,KAAK,OAAO,SAAY,CACtE,QAAQ,OAAO,MACb,+DAA+D,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,IACrG,CACD,QAAQ,OAAO,MAAM,wBAAwB,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,QAAQ,CAC1F,QAAQ,OAAO,MAAM,6BAA6B,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,aAAa,CACpG,QAAQ,OAAO,MAAM,wBAAwB,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,WAAW,CAC7F,GAAS,EACT,CAEF,KAAK,OAAO,GAAG,QAAU,GAAiB,CACxC,EAAO,EAAM,EACb,OACK,EAAO,CACd,EAAO,EAAM,GAEf,CAGJ,MAAM,MAAsB,CAC1B,OAAO,IAAI,SAAS,EAAS,IAAW,CAClC,KAAK,QAEP,KAAK,eAAe,OAAO,CAE3B,KAAK,OAAO,MAAO,GAAgB,CAC7B,EACF,EAAO,EAAI,EAEX,KAAK,OAAS,KACd,GAAS,GAEX,EAEF,GAAS,EAEX,CAGJ,SAAkB,CAChB,OAAO,KAAK,OAAO,KAGrB,SAAkB,CAChB,OAAO,KAAK,OAAO,OCtMV,EAAb,KAA+D,CAC7D,OACA,UAAiD,KAEjD,YAAY,EAAgB,CAC1B,KAAK,OAAS,EAGhB,MAAM,OAAuB,CAC3B,KAAK,UAAY,IAAIC,EAAAA,qBACrB,MAAM,KAAK,OAAO,QAAQ,KAAK,UAAU,CACzC,QAAQ,OAAO,MAAM;EAA4C,CAGnE,MAAM,MAAsB,CAC1B,AAEE,KAAK,aADL,MAAM,KAAK,UAAU,OAAO,CACX"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"commands-Bda9LPMQ.cjs","names":["Command","StdioTransportHandler","createServer","HttpTransportHandler","SseTransportHandler"],"sources":["../src/types/index.ts","../src/commands/mcp-serve.ts"],"sourcesContent":["/**\n * Shared TypeScript Types\n *\n * DESIGN PATTERNS:\n * - Type-first development\n * - Interface segregation\n *\n * CODING STANDARDS:\n * - Export all shared types from this file\n * - Use descriptive names for types and interfaces\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Tool definition for MCP\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, any>;\n required?: string[];\n additionalProperties?: boolean;\n };\n}\n\n/**\n * Base tool interface following MCP SDK patterns\n */\nexport interface Tool<TInput = any> {\n getDefinition(): ToolDefinition;\n execute(input: TInput): Promise<CallToolResult>;\n}\n\n/**\n * Transport mode types\n */\nexport enum TransportMode {\n STDIO = 'stdio',\n HTTP = 'http',\n SSE = 'sse',\n}\n\n/**\n * Transport configuration options\n */\nexport interface TransportConfig {\n mode: TransportMode;\n port?: number;\n host?: string;\n}\n\n/**\n * Base interface for all transport handlers\n */\nexport interface TransportHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\n/**\n * HTTP transport specific types\n */\nexport interface HttpTransportHandler extends TransportHandler {\n getPort(): number;\n getHost(): string;\n}\n","/**\n * MCP Serve Command\n *\n * DESIGN PATTERNS:\n * - Command pattern with Commander for CLI argument parsing\n * - Transport abstraction pattern for flexible deployment (stdio, HTTP, SSE)\n * - Factory pattern for creating transport handlers\n * - Graceful shutdown pattern with signal handling\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Implement proper error handling with try-catch blocks\n * - Handle process signals for graceful shutdown\n * - Provide clear CLI options and help messages\n *\n * AVOID:\n * - Hardcoded configuration values (use CLI options or environment variables)\n * - Missing error handling for transport startup\n * - Not cleaning up resources on shutdown\n */\n\nimport { Command } from 'commander';\nimport { createServer } from '../server';\nimport { HttpTransportHandler } from '../transports/http';\nimport { SseTransportHandler } from '../transports/sse';\nimport { StdioTransportHandler } from '../transports/stdio';\nimport { type TransportConfig, TransportMode } from '../types';\n\n/**\n * Start MCP server with given transport handler\n */\nasync function startServer(handler: any) {\n await handler.start();\n\n // Handle graceful shutdown\n const shutdown = async (signal: string) => {\n process.stderr.write(`\\nReceived ${signal}, shutting down gracefully...\\n`);\n try {\n await handler.stop();\n process.exit(0);\n } catch (error) {\n process.stderr.write(`Error during shutdown: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n };\n\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n}\n\n/**\n * MCP Serve command\n */\nexport const mcpServeCommand = new Command('mcp-serve')\n .description('Start MCP server with specified transport')\n .option('-t, --type <type>', 'Transport type: stdio, http, or sse', 'stdio')\n .option('-p, --port <port>', 'Port to listen on (http/sse only)', (val) => parseInt(val, 10), 3000)\n .option('--host <host>', 'Host to bind to (http/sse only)', 'localhost')\n .action(async (options) => {\n try {\n const transportType = options.type.toLowerCase();\n\n if (transportType === 'stdio') {\n const server = createServer();\n const handler = new StdioTransportHandler(server);\n await startServer(handler);\n } else if (transportType === 'http') {\n // For HTTP, pass a factory function to create new server instances per session\n const config: TransportConfig = {\n mode: TransportMode.HTTP,\n port: options.port || Number(process.env.MCP_PORT) || 3000,\n host: options.host || process.env.MCP_HOST || 'localhost',\n };\n const handler = new HttpTransportHandler(() => createServer(), config);\n await startServer(handler);\n } else if (transportType === 'sse') {\n // For SSE, pass a factory function to create new server instances per connection\n const config: TransportConfig = {\n mode: TransportMode.SSE,\n port: options.port || Number(process.env.MCP_PORT) || 3000,\n host: options.host || process.env.MCP_HOST || 'localhost',\n };\n const handler = new SseTransportHandler(() => createServer(), config);\n await startServer(handler);\n } else {\n process.stderr.write(`Unknown transport type: ${transportType}. Use: stdio, http, or sse\\n`);\n process.exit(1);\n }\n } catch (error) {\n process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n });\n"],"mappings":"mEAuCY,EAAA,SAAA,EAAL,OACL,GAAA,MAAA,QACA,EAAA,KAAA,OACA,EAAA,IAAA,aCXF,eAAe,EAAY,EAAc,CACvC,MAAM,EAAQ,OAAO,CAGrB,IAAM,EAAW,KAAO,IAAmB,CACzC,QAAQ,OAAO,MAAM,cAAc,EAAO,iCAAiC,CAC3E,GAAI,CACF,MAAM,EAAQ,MAAM,CACpB,QAAQ,KAAK,EAAE,OACR,EAAO,CACd,QAAQ,OAAO,MAAM,0BAA0B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAAC,IAAI,CAC1G,QAAQ,KAAK,EAAE,GAInB,QAAQ,GAAG,aAAgB,EAAS,SAAS,CAAC,CAC9C,QAAQ,GAAG,cAAiB,EAAS,UAAU,CAAC,CAMlD,MAAa,EAAkB,IAAIA,EAAAA,QAAQ,YAAY,CACpD,YAAY,4CAA4C,CACxD,OAAO,oBAAqB,sCAAuC,QAAQ,CAC3E,OAAO,oBAAqB,oCAAsC,GAAQ,SAAS,EAAK,GAAG,CAAE,IAAK,CAClG,OAAO,gBAAiB,kCAAmC,YAAY,CACvE,OAAO,KAAO,IAAY,CACzB,GAAI,CACF,IAAM,EAAgB,EAAQ,KAAK,aAAa,CAE5C,IAAkB,QAGpB,MAAM,EADU,IAAIC,EAAAA,EADLC,EAAAA,GAAc,CACoB,CACvB,CACjB,IAAkB,OAQ3B,MAAM,EADU,IAAIC,EAAAA,MAA2BD,EAAAA,GAAc,CAL7B,CAC9B,KAAM,EAAc,KACpB,KAAM,EAAQ,MAAQ,OAAO,QAAQ,IAAI,SAAS,EAAI,IACtD,KAAM,EAAQ,MAAQ,QAAQ,IAAI,UAAY,YAC/C,CACqE,CAC5C,CACjB,IAAkB,MAQ3B,MAAM,EADU,IAAIE,EAAAA,MAA0BF,EAAAA,GAAc,CAL5B,CAC9B,KAAM,EAAc,IACpB,KAAM,EAAQ,MAAQ,OAAO,QAAQ,IAAI,SAAS,EAAI,IACtD,KAAM,EAAQ,MAAQ,QAAQ,IAAI,UAAY,YAC/C,CACoE,CAC3C,EAE1B,QAAQ,OAAO,MAAM,2BAA2B,EAAc,8BAA8B,CAC5F,QAAQ,KAAK,EAAE,QAEV,EAAO,CACd,QAAQ,OAAO,MAAM,+BAA+B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAAC,IAAI,CAC/G,QAAQ,KAAK,EAAE,GAEjB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"commands-c927CRZc.mjs","names":[],"sources":["../src/types/index.ts","../src/commands/mcp-serve.ts"],"sourcesContent":["/**\n * Shared TypeScript Types\n *\n * DESIGN PATTERNS:\n * - Type-first development\n * - Interface segregation\n *\n * CODING STANDARDS:\n * - Export all shared types from this file\n * - Use descriptive names for types and interfaces\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Tool definition for MCP\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, any>;\n required?: string[];\n additionalProperties?: boolean;\n };\n}\n\n/**\n * Base tool interface following MCP SDK patterns\n */\nexport interface Tool<TInput = any> {\n getDefinition(): ToolDefinition;\n execute(input: TInput): Promise<CallToolResult>;\n}\n\n/**\n * Transport mode types\n */\nexport enum TransportMode {\n STDIO = 'stdio',\n HTTP = 'http',\n SSE = 'sse',\n}\n\n/**\n * Transport configuration options\n */\nexport interface TransportConfig {\n mode: TransportMode;\n port?: number;\n host?: string;\n}\n\n/**\n * Base interface for all transport handlers\n */\nexport interface TransportHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\n/**\n * HTTP transport specific types\n */\nexport interface HttpTransportHandler extends TransportHandler {\n getPort(): number;\n getHost(): string;\n}\n","/**\n * MCP Serve Command\n *\n * DESIGN PATTERNS:\n * - Command pattern with Commander for CLI argument parsing\n * - Transport abstraction pattern for flexible deployment (stdio, HTTP, SSE)\n * - Factory pattern for creating transport handlers\n * - Graceful shutdown pattern with signal handling\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Implement proper error handling with try-catch blocks\n * - Handle process signals for graceful shutdown\n * - Provide clear CLI options and help messages\n *\n * AVOID:\n * - Hardcoded configuration values (use CLI options or environment variables)\n * - Missing error handling for transport startup\n * - Not cleaning up resources on shutdown\n */\n\nimport { Command } from 'commander';\nimport { createServer } from '../server';\nimport { HttpTransportHandler } from '../transports/http';\nimport { SseTransportHandler } from '../transports/sse';\nimport { StdioTransportHandler } from '../transports/stdio';\nimport { type TransportConfig, TransportMode } from '../types';\n\n/**\n * Start MCP server with given transport handler\n */\nasync function startServer(handler: any) {\n await handler.start();\n\n // Handle graceful shutdown\n const shutdown = async (signal: string) => {\n process.stderr.write(`\\nReceived ${signal}, shutting down gracefully...\\n`);\n try {\n await handler.stop();\n process.exit(0);\n } catch (error) {\n process.stderr.write(`Error during shutdown: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n };\n\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n}\n\n/**\n * MCP Serve command\n */\nexport const mcpServeCommand = new Command('mcp-serve')\n .description('Start MCP server with specified transport')\n .option('-t, --type <type>', 'Transport type: stdio, http, or sse', 'stdio')\n .option('-p, --port <port>', 'Port to listen on (http/sse only)', (val) => parseInt(val, 10), 3000)\n .option('--host <host>', 'Host to bind to (http/sse only)', 'localhost')\n .action(async (options) => {\n try {\n const transportType = options.type.toLowerCase();\n\n if (transportType === 'stdio') {\n const server = createServer();\n const handler = new StdioTransportHandler(server);\n await startServer(handler);\n } else if (transportType === 'http') {\n // For HTTP, pass a factory function to create new server instances per session\n const config: TransportConfig = {\n mode: TransportMode.HTTP,\n port: options.port || Number(process.env.MCP_PORT) || 3000,\n host: options.host || process.env.MCP_HOST || 'localhost',\n };\n const handler = new HttpTransportHandler(() => createServer(), config);\n await startServer(handler);\n } else if (transportType === 'sse') {\n // For SSE, pass a factory function to create new server instances per connection\n const config: TransportConfig = {\n mode: TransportMode.SSE,\n port: options.port || Number(process.env.MCP_PORT) || 3000,\n host: options.host || process.env.MCP_HOST || 'localhost',\n };\n const handler = new SseTransportHandler(() => createServer(), config);\n await startServer(handler);\n } else {\n process.stderr.write(`Unknown transport type: ${transportType}. Use: stdio, http, or sse\\n`);\n process.exit(1);\n }\n } catch (error) {\n process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n });\n"],"mappings":"kGAuCA,IAAY,EAAA,SAAA,EAAL,OACL,GAAA,MAAA,QACA,EAAA,KAAA,OACA,EAAA,IAAA,aCXF,eAAe,EAAY,EAAc,CACvC,MAAM,EAAQ,OAAO,CAGrB,IAAM,EAAW,KAAO,IAAmB,CACzC,QAAQ,OAAO,MAAM,cAAc,EAAO,iCAAiC,CAC3E,GAAI,CACF,MAAM,EAAQ,MAAM,CACpB,QAAQ,KAAK,EAAE,OACR,EAAO,CACd,QAAQ,OAAO,MAAM,0BAA0B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAAC,IAAI,CAC1G,QAAQ,KAAK,EAAE,GAInB,QAAQ,GAAG,aAAgB,EAAS,SAAS,CAAC,CAC9C,QAAQ,GAAG,cAAiB,EAAS,UAAU,CAAC,CAMlD,MAAa,EAAkB,IAAI,EAAQ,YAAY,CACpD,YAAY,4CAA4C,CACxD,OAAO,oBAAqB,sCAAuC,QAAQ,CAC3E,OAAO,oBAAqB,oCAAsC,GAAQ,SAAS,EAAK,GAAG,CAAE,IAAK,CAClG,OAAO,gBAAiB,kCAAmC,YAAY,CACvE,OAAO,KAAO,IAAY,CACzB,GAAI,CACF,IAAM,EAAgB,EAAQ,KAAK,aAAa,CAE5C,IAAkB,QAGpB,MAAM,EADU,IAAI,EADL,GAAc,CACoB,CACvB,CACjB,IAAkB,OAQ3B,MAAM,EADU,IAAI,MAA2B,GAAc,CAL7B,CAC9B,KAAM,EAAc,KACpB,KAAM,EAAQ,MAAQ,OAAO,QAAQ,IAAI,SAAS,EAAI,IACtD,KAAM,EAAQ,MAAQ,QAAQ,IAAI,UAAY,YAC/C,CACqE,CAC5C,CACjB,IAAkB,MAQ3B,MAAM,EADU,IAAI,MAA0B,GAAc,CAL5B,CAC9B,KAAM,EAAc,IACpB,KAAM,EAAQ,MAAQ,OAAO,QAAQ,IAAI,SAAS,EAAI,IACtD,KAAM,EAAQ,MAAQ,QAAQ,IAAI,UAAY,YAC/C,CACoE,CAC3C,EAE1B,QAAQ,OAAO,MAAM,2BAA2B,EAAc,8BAA8B,CAC5F,QAAQ,KAAK,EAAE,QAEV,EAAO,CACd,QAAQ,OAAO,MAAM,+BAA+B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAAC,IAAI,CAC/G,QAAQ,KAAK,EAAE,GAEjB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stdio-CuIrQe3m.mjs","names":["MIME_BY_EXTENSION: Record<string, string>","result: ReadImageResult","data: Buffer","options: ReadImageOptions","config","transport: StreamableHTTPServerTransport","sessionId","config"],"sources":["../src/utils.ts","../src/services/ImageReader.ts","../src/tools/ReadImageTool.ts","../src/imagineEnvSchema.ts","../src/config.ts","../src/services/UnsplashService.ts","../src/tools/UnsplashSearchTool.ts","../src/server/index.ts","../src/transports/http.ts","../src/transports/sse.ts","../src/transports/stdio.ts"],"sourcesContent":["import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport fetch from 'node-fetch';\n\nimport { SUPPORTED_INPUT_FORMATS, SUPPORTED_OUTPUT_FORMATS } from './constants';\n\n/**\n * Validates if the provided format is supported for input\n */\nexport function isValidInputFormat(format: string): boolean {\n return SUPPORTED_INPUT_FORMATS.includes(format.toLowerCase());\n}\n\n/**\n * Validates if the provided format is supported for output\n */\nexport function isValidOutputFormat(format: string): boolean {\n return SUPPORTED_OUTPUT_FORMATS.includes(format.toLowerCase());\n}\n\n/**\n * Validates if the provided dimensions are valid\n */\nexport function isValidDimensions(width?: number, height?: number): boolean {\n if (width !== undefined && (width <= 0 || width > 10000)) {\n return false;\n }\n if (height !== undefined && (height <= 0 || height > 10000)) {\n return false;\n }\n return true;\n}\n\n/**\n * Validates if the provided quality is valid\n */\nexport function isValidQuality(quality?: number): boolean {\n if (quality !== undefined && (quality < 1 || quality > 100)) {\n return false;\n }\n return true;\n}\n\n/**\n * Extracts the file extension from a filename\n */\nexport function getFileExtension(filename: string): string {\n const parts = filename.split('.');\n return parts.length > 1 ? parts.pop()!.toLowerCase() : '';\n}\n\n/**\n * Converts a base64 string to a Buffer\n */\nexport function base64ToBuffer(base64: string): Buffer {\n // Remove data URL prefix if present\n const base64Data = base64.replace(/^data:image\\/\\w+;base64,/, '');\n return Buffer.from(base64Data, 'base64');\n}\n\n/**\n * Converts a Buffer to a base64 string\n */\nexport function bufferToBase64(buffer: Buffer, mimeType: string): string {\n return `data:${mimeType};base64,${buffer.toString('base64')}`;\n}\n\n/**\n * Normalizes a file path by handling escaped characters and spaces\n *\n * This function handles cases like 'a\\ name.png' by converting them to 'a name.png'\n */\nexport function normalizeFilePath(filePath: string): string {\n // Replace escaped spaces (\\ ) with actual spaces\n let normalizedPath = filePath.replace(/\\\\+ /g, ' ');\n\n // Replace other common escaped characters\n normalizedPath = normalizedPath\n .replace(/\\\\+'/g, \"'\")\n .replace(/\\\\+\"/g, '\"')\n .replace(/\\\\+`/g, '`')\n .replace(/\\\\+\\(/g, '(')\n .replace(/\\\\+\\)/g, ')')\n .replace(/\\\\+\\[/g, '[')\n .replace(/\\\\+\\]/g, ']')\n .replace(/\\\\+\\{/g, '{')\n .replace(/\\\\+\\}/g, '}');\n\n return normalizedPath;\n}\n\n/**\n * Fetches an image from a URL and returns it as a Buffer\n */\nexport async function fetchImageFromUrl(url: string): Promise<Buffer> {\n try {\n const response = await fetch(url);\n\n // Check if the response is successful\n if (!response.ok) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Failed to fetch image from URL: ${url}, status code: ${response.status}`,\n );\n }\n\n // Check if the content type is an image\n const contentType = response.headers.get('content-type');\n if (!contentType || !contentType.startsWith('image/')) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `URL does not point to an image: ${url}, content-type: ${contentType}`,\n );\n }\n\n // Get the response as an ArrayBuffer and convert to Buffer\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer);\n } catch (error) {\n if (error instanceof McpError) {\n throw error;\n }\n throw new McpError(\n ErrorCode.InternalError,\n `Error fetching image from URL: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n","import { createHash } from 'node:crypto';\nimport { constants } from 'node:fs';\nimport { access, readFile } from 'node:fs/promises';\nimport { basename, extname, resolve } from 'node:path';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport fetch from 'node-fetch';\n\nimport { normalizeFilePath } from '../utils';\n\ninterface ImageSource {\n source: string;\n sourceType: 'data-uri' | 'url' | 'file';\n mimeType: string;\n sizeBytes: number;\n data: Buffer;\n}\n\nexport interface ReadImageResult {\n source: string;\n sourceType: 'data-uri' | 'url' | 'file';\n mimeType: string;\n sizeBytes: number;\n sha256: string;\n fileName?: string;\n base64?: string;\n}\n\nexport interface ReadImageOptions {\n includeBase64?: boolean;\n}\n\nconst DEFAULT_MIME_TYPE = 'application/octet-stream';\nconst MIME_BY_EXTENSION: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.bmp': 'image/bmp',\n '.svg': 'image/svg+xml',\n '.heic': 'image/heic',\n '.heif': 'image/heif',\n};\n\nexport class ImageReader {\n async readImage(source: string, options: ReadImageOptions = {}): Promise<ReadImageResult> {\n const imageSource = await this.resolveImageSource(source);\n const sha256 = createHash('sha256').update(imageSource.data).digest('hex');\n\n const result: ReadImageResult = {\n source: imageSource.source,\n sourceType: imageSource.sourceType,\n mimeType: imageSource.mimeType,\n sizeBytes: imageSource.sizeBytes,\n sha256,\n };\n\n if (imageSource.sourceType !== 'data-uri') {\n result.fileName = basename(imageSource.source);\n }\n\n if (options.includeBase64) {\n result.base64 = imageSource.data.toString('base64');\n }\n\n return result;\n }\n\n private async resolveImageSource(source: string): Promise<ImageSource> {\n const trimmed = source.trim();\n\n if (!trimmed) {\n throw new McpError(ErrorCode.InvalidParams, 'Image source cannot be empty.');\n }\n\n if (trimmed.startsWith('data:')) {\n return this.resolveDataUri(trimmed);\n }\n\n if (/^https?:\\/\\//i.test(trimmed)) {\n return this.resolveRemoteImage(trimmed);\n }\n\n return this.resolveFile(trimmed);\n }\n\n private async resolveFile(filePath: string): Promise<ImageSource> {\n const normalizedPath = resolve(normalizeFilePath(filePath));\n\n try {\n await access(normalizedPath, constants.F_OK);\n } catch (error) {\n throw new McpError(ErrorCode.InvalidParams, `Image file not found at path: ${filePath}`);\n }\n\n let data: Buffer;\n try {\n data = await readFile(normalizedPath);\n } catch (error) {\n throw new McpError(\n ErrorCode.InternalError,\n `Failed to read image file: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n const extension = extname(normalizedPath).toLowerCase();\n const mimeType = MIME_BY_EXTENSION[extension] || DEFAULT_MIME_TYPE;\n\n return {\n source: normalizedPath,\n sourceType: 'file',\n mimeType,\n sizeBytes: data.length,\n data,\n };\n }\n\n private async resolveRemoteImage(url: string): Promise<ImageSource> {\n try {\n const response = await fetch(url, { redirect: 'follow' });\n if (!response.ok) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Failed to fetch image from URL: ${url} (${response.status} ${response.statusText})`,\n );\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const data = Buffer.from(arrayBuffer);\n const contentType = response.headers.get('content-type')?.split(';')[0];\n const fileName = basename(new URL(url).pathname || 'image');\n\n return {\n source: url,\n sourceType: 'url',\n mimeType: contentType ?? MIME_BY_EXTENSION[extname(fileName)] ?? DEFAULT_MIME_TYPE,\n sizeBytes: data.length,\n data,\n };\n } catch (error) {\n if (error instanceof McpError) {\n throw error;\n }\n throw new McpError(\n ErrorCode.InternalError,\n `Failed to download image from URL: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private resolveDataUri(dataUri: string): ImageSource {\n const commaIndex = dataUri.indexOf(',');\n if (commaIndex === -1 || commaIndex === dataUri.length - 1) {\n throw new McpError(\n ErrorCode.InvalidParams,\n 'Invalid data URI format. Expected `data:[<mime>];base64,<payload>`.',\n );\n }\n\n const metadata = dataUri.slice(5, commaIndex).toLowerCase();\n const payload = dataUri.slice(commaIndex + 1);\n const isBase64 = metadata.includes('base64');\n const declaredMimeType = metadata.split(';')[0] || DEFAULT_MIME_TYPE;\n\n try {\n const data = isBase64 ? Buffer.from(payload, 'base64') : Buffer.from(decodeURIComponent(payload));\n return {\n source: dataUri,\n sourceType: 'data-uri',\n mimeType: declaredMimeType || DEFAULT_MIME_TYPE,\n sizeBytes: data.length,\n data,\n };\n } catch (error) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Failed to decode data URI payload: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n}\n","import { CallToolResult, ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { ImageReader, type ReadImageOptions } from '../services/ImageReader';\nimport type { Tool, ToolDefinition } from '../types/index';\n\ninterface ReadImageToolInput {\n source: string;\n includeBase64?: boolean;\n}\n\nexport class ReadImageTool implements Tool<ReadImageToolInput> {\n static readonly TOOL_NAME = 'read-image';\n\n private service = new ImageReader();\n\n getDefinition(): ToolDefinition {\n return {\n name: ReadImageTool.TOOL_NAME,\n description: 'Read an image from a local path, URL, or data URI and return metadata.',\n inputSchema: {\n type: 'object',\n properties: {\n source: {\n type: 'string',\n minLength: 1,\n description: 'Image source as a local path, HTTP(S) URL, or data URI.',\n },\n includeBase64: {\n type: 'boolean',\n description: 'Whether to include base64-encoded output.',\n default: false,\n },\n },\n required: ['source'],\n additionalProperties: false,\n },\n };\n }\n\n async execute(input: ReadImageToolInput): Promise<CallToolResult> {\n try {\n if (!input || typeof input !== 'object') {\n throw new McpError(ErrorCode.InvalidParams, 'Tool input must be an object.');\n }\n\n const payload = input as ReadImageToolInput & { [key: string]: unknown };\n const source = payload.source;\n const includeBase64 = payload.includeBase64;\n if (!source || typeof source !== 'string' || source.trim().length === 0) {\n throw new McpError(ErrorCode.InvalidParams, 'source must be a non-empty string.');\n }\n if (includeBase64 !== undefined && typeof includeBase64 !== 'boolean') {\n throw new McpError(ErrorCode.InvalidParams, 'includeBase64 must be a boolean.');\n }\n\n const options: ReadImageOptions = {\n includeBase64,\n };\n const result = await this.service.readImage(source, options);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n}\n","import { z } from 'zod';\n\n/**\n * Environment variable schema for imagine-mcp\n *\n * Following Agiflow naming conventions:\n * - Service credentials use provider-specific prefixes\n * - Upload service selection uses UPLOAD_SERVICE\n */\nexport const envSchema = z\n .object({\n // Upload service selection\n UPLOAD_SERVICE: z.enum(['s3', 'cloudflare', 'gcloud']).optional().default('s3'),\n\n // AWS S3 Configuration\n S3_BUCKET: z.string().optional(),\n AWS_ACCESS_KEY_ID: z.string().optional(),\n AWS_SECRET_ACCESS_KEY: z.string().optional(),\n S3_REGION: z.string().optional().default('us-east-1'),\n S3_ENDPOINT: z.string().url('S3_ENDPOINT must be a valid URL').optional(),\n\n // Cloudflare R2 Configuration\n CLOUDFLARE_R2_BUCKET: z.string().optional(),\n CLOUDFLARE_R2_ACCESS_KEY_ID: z.string().optional(),\n CLOUDFLARE_R2_SECRET_ACCESS_KEY: z.string().optional(),\n CLOUDFLARE_R2_REGION: z.string().optional().default('auto'),\n CLOUDFLARE_R2_ENDPOINT: z.string().url('CLOUDFLARE_R2_ENDPOINT must be a valid URL').optional(),\n\n // Google Cloud Storage Configuration\n GCLOUD_BUCKET: z.string().optional(),\n GCLOUD_PROJECT_ID: z.string().optional(),\n GCLOUD_CREDENTIALS_PATH: z.string().optional(),\n\n // Unsplash API Configuration\n UNSPLASH_ACCESS_KEY: z.string().optional(),\n })\n .refine(\n (data) => {\n // If AWS credentials are provided, both must be present\n if (data.AWS_ACCESS_KEY_ID || data.AWS_SECRET_ACCESS_KEY) {\n return data.AWS_ACCESS_KEY_ID && data.AWS_SECRET_ACCESS_KEY;\n }\n return true;\n },\n {\n message: 'Both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be provided together',\n path: ['AWS_ACCESS_KEY_ID'],\n },\n );\n\nexport type EnvConfig = z.infer<typeof envSchema>;\n","import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod';\nimport { type EnvConfig, envSchema } from './imagineEnvSchema';\n\n/**\n * Centralized configuration service for imagine-mcp\n *\n * Validates environment variables at startup and provides\n * type-safe access to configuration throughout the application.\n */\nclass ConfigService {\n private static instance: ConfigService;\n private config: EnvConfig;\n\n private constructor() {\n try {\n this.config = envSchema.parse(process.env);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const errorMessage = error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join('; ');\n throw new McpError(ErrorCode.InternalError, `Environment configuration validation failed: ${errorMessage}`);\n }\n throw error;\n }\n }\n\n /**\n * Get the singleton instance of ConfigService\n */\n public static getInstance(): ConfigService {\n if (!ConfigService.instance) {\n ConfigService.instance = new ConfigService();\n }\n return ConfigService.instance;\n }\n\n /**\n * Get the validated configuration\n */\n public getConfig(): EnvConfig {\n return this.config;\n }\n\n /**\n * Get S3 configuration\n */\n public getS3Config() {\n return {\n bucket: this.config.S3_BUCKET,\n accessKeyId: this.config.AWS_ACCESS_KEY_ID,\n secretAccessKey: this.config.AWS_SECRET_ACCESS_KEY,\n region: this.config.S3_REGION,\n endpoint: this.config.S3_ENDPOINT,\n };\n }\n\n /**\n * Get Cloudflare R2 configuration\n */\n public getCloudflareConfig() {\n return {\n bucket: this.config.CLOUDFLARE_R2_BUCKET,\n accessKeyId: this.config.CLOUDFLARE_R2_ACCESS_KEY_ID,\n secretAccessKey: this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY,\n region: this.config.CLOUDFLARE_R2_REGION,\n endpoint: this.config.CLOUDFLARE_R2_ENDPOINT,\n };\n }\n\n /**\n * Get Google Cloud Storage configuration\n */\n public getGCloudConfig() {\n return {\n bucket: this.config.GCLOUD_BUCKET,\n projectId: this.config.GCLOUD_PROJECT_ID,\n credentialsPath: this.config.GCLOUD_CREDENTIALS_PATH,\n };\n }\n\n /**\n * Get the default upload service\n */\n public getUploadService() {\n return this.config.UPLOAD_SERVICE;\n }\n\n /**\n * Get Unsplash API configuration\n */\n public getUnsplashConfig() {\n return {\n accessKey: this.config.UNSPLASH_ACCESS_KEY,\n };\n }\n\n /**\n * Validate that required credentials exist for a specific service\n */\n public validateServiceConfig(service: 's3' | 'cloudflare' | 'gcloud'): void {\n switch (service) {\n case 's3':\n if (!this.config.S3_BUCKET) {\n throw new McpError(ErrorCode.InvalidParams, 'S3_BUCKET is required for S3 upload service');\n }\n break;\n case 'cloudflare':\n if (!this.config.CLOUDFLARE_R2_BUCKET) {\n throw new McpError(ErrorCode.InvalidParams, 'CLOUDFLARE_R2_BUCKET is required for Cloudflare upload service');\n }\n if (!this.config.CLOUDFLARE_R2_ACCESS_KEY_ID || !this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY) {\n throw new McpError(\n ErrorCode.InvalidParams,\n 'CLOUDFLARE_R2_ACCESS_KEY_ID and CLOUDFLARE_R2_SECRET_ACCESS_KEY are required for Cloudflare upload service',\n );\n }\n if (!this.config.CLOUDFLARE_R2_ENDPOINT) {\n throw new McpError(\n ErrorCode.InvalidParams,\n 'CLOUDFLARE_R2_ENDPOINT is required for Cloudflare upload service',\n );\n }\n break;\n case 'gcloud':\n if (!this.config.GCLOUD_BUCKET) {\n throw new McpError(ErrorCode.InvalidParams, 'GCLOUD_BUCKET is required for Google Cloud upload service');\n }\n if (!this.config.GCLOUD_PROJECT_ID) {\n throw new McpError(ErrorCode.InvalidParams, 'GCLOUD_PROJECT_ID is required for Google Cloud upload service');\n }\n break;\n }\n }\n}\n\n// Export singleton instance\nexport const config = ConfigService.getInstance();\n","/**\n * UnsplashService\n *\n * DESIGN PATTERNS:\n * - Service pattern for business logic encapsulation\n * - Single responsibility principle\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Keep methods focused and well-named\n * - Document complex logic with comments\n *\n * AVOID:\n * - Mixing concerns (keep focused on single domain)\n * - Direct tool implementation (services should be tool-agnostic)\n */\n\nimport { createApi } from 'unsplash-js';\nimport { config } from '../config.js';\n\nexport interface UnsplashImage {\n id: string;\n description: string | null;\n alt_description: string | null;\n urls: {\n raw: string;\n full: string;\n regular: string;\n small: string;\n thumb: string;\n };\n links: {\n html: string;\n download: string;\n };\n user: {\n name: string;\n username: string;\n portfolio_url: string | null;\n };\n width: number;\n height: number;\n color: string;\n likes: number;\n}\n\nexport interface SearchImagesParams {\n query: string;\n perPage?: number;\n page?: number;\n orientation?: 'landscape' | 'portrait' | 'squarish';\n color?:\n | 'black_and_white'\n | 'black'\n | 'white'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'magenta'\n | 'green'\n | 'teal'\n | 'blue';\n}\n\nexport class UnsplashService {\n private api: ReturnType<typeof createApi>;\n\n constructor(accessKey?: string) {\n const unsplashConfig = config.getUnsplashConfig();\n const apiKey = accessKey || unsplashConfig.accessKey;\n\n if (!apiKey) {\n throw new Error('Unsplash API key is required. Set UNSPLASH_ACCESS_KEY environment variable.');\n }\n\n this.api = createApi({\n accessKey: apiKey,\n });\n }\n\n async searchImages(params: SearchImagesParams): Promise<UnsplashImage[]> {\n try {\n const result = await this.api.search.getPhotos({\n query: params.query,\n page: params.page || 1,\n perPage: params.perPage || 10,\n orientation: params.orientation,\n color: params.color,\n });\n\n if (result.errors) {\n throw new Error(`Unsplash API error: ${result.errors.join(', ')}`);\n }\n\n if (!result.response) {\n throw new Error('No response from Unsplash API');\n }\n\n return result.response.results.map((photo) => ({\n id: photo.id,\n description: photo.description,\n alt_description: photo.alt_description,\n urls: {\n raw: photo.urls.raw,\n full: photo.urls.full,\n regular: photo.urls.regular,\n small: photo.urls.small,\n thumb: photo.urls.thumb,\n },\n links: {\n html: photo.links.html,\n download: photo.links.download,\n },\n user: {\n name: photo.user.name,\n username: photo.user.username,\n portfolio_url: photo.user.portfolio_url,\n },\n width: photo.width,\n height: photo.height,\n color: photo.color || '#000000',\n likes: photo.likes,\n }));\n } catch (error) {\n throw new Error(`Failed to search Unsplash images: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n}\n","/**\n * UnsplashSearchTool\n *\nhttps://github.com/BoomLinkAi/image-worker-mcp * DESIGN PATTERNS:\n * - Tool pattern with getDefinition() and execute() methods\n * - Service delegation for business logic\n * - JSON Schema validation for inputs\n *\n * CODING STANDARDS:\n * - Implement Tool interface from ../types\n * - Use TOOL_NAME constant with snake_case (e.g., 'file_read')\n * - Return CallToolResult with content array\n * - Handle errors with isError flag\n * - Delegate complex logic to services\n *\n * AVOID:\n * - Complex business logic in execute method\n * - Unhandled promise rejections\n * - Missing input validation\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { UnsplashService } from '../services/UnsplashService';\nimport type { Tool, ToolDefinition } from '../types/index';\n\ninterface UnsplashSearchToolInput {\n query: string;\n perPage?: number;\n page?: number;\n orientation?: 'landscape' | 'portrait' | 'squarish';\n color?:\n | 'black_and_white'\n | 'black'\n | 'white'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'magenta'\n | 'green'\n | 'teal'\n | 'blue';\n}\n\nexport class UnsplashSearchTool implements Tool<UnsplashSearchToolInput> {\n static readonly TOOL_NAME = 'unsplash_search';\n\n private service: UnsplashService | null;\n private readonly accessKey?: string;\n\n constructor(accessKey?: string) {\n this.accessKey = accessKey;\n this.service = null;\n }\n\n private getService(): UnsplashService {\n if (!this.service) {\n this.service = new UnsplashService(this.accessKey);\n }\n return this.service;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: UnsplashSearchTool.TOOL_NAME,\n description: 'Search for stock images from Unsplash by keyword and return image URLs with metadata',\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Search query (e.g., \"sunset\", \"technology\", \"nature\")',\n },\n perPage: {\n type: 'number',\n description: 'Number of results per page (1-30, default: 10)',\n minimum: 1,\n maximum: 30,\n },\n page: {\n type: 'number',\n description: 'Page number for pagination (default: 1)',\n minimum: 1,\n },\n orientation: {\n type: 'string',\n enum: ['landscape', 'portrait', 'squarish'],\n description: 'Filter by photo orientation',\n },\n color: {\n type: 'string',\n enum: [\n 'black_and_white',\n 'black',\n 'white',\n 'yellow',\n 'orange',\n 'red',\n 'purple',\n 'magenta',\n 'green',\n 'teal',\n 'blue',\n ],\n description: 'Filter by photo color',\n },\n },\n required: ['query'],\n additionalProperties: false,\n },\n };\n }\n\n async execute(input: UnsplashSearchToolInput): Promise<CallToolResult> {\n try {\n const images = await this.getService().searchImages({\n query: input.query,\n perPage: input.perPage,\n page: input.page,\n orientation: input.orientation,\n color: input.color,\n });\n\n if (images.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: `No images found for query: \"${input.query}\"`,\n },\n ],\n };\n }\n\n const formattedResults = images\n .map((img, index) => {\n return `\n📷 **Image ${index + 1}**\n- **ID**: ${img.id}\n- **Description**: ${img.alt_description || img.description || 'No description'}\n- **Photographer**: ${img.user.name} (@${img.user.username})\n- **Dimensions**: ${img.width}x${img.height}px\n- **Color**: ${img.color}\n- **Likes**: ${img.likes}\n\n**URLs**:\n- Full: ${img.urls.full}\n- Regular: ${img.urls.regular}\n- Small: ${img.urls.small}\n- Thumbnail: ${img.urls.thumb}\n\n**Links**:\n- View on Unsplash: ${img.links.html}\n- Download: ${img.links.download}\n${img.user.portfolio_url ? `- Photographer Portfolio: ${img.user.portfolio_url}` : ''}\n`.trim();\n })\n .join('\\n\\n' + '-'.repeat(80) + '\\n\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Found ${images.length} image(s) for \"${input.query}\":\\n\\n${formattedResults}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n}\n","/**\n * MCP Server Setup\n *\n * DESIGN PATTERNS:\n * - Factory pattern for server creation\n * - Tool registration pattern\n *\n * CODING STANDARDS:\n * - Register all tools, resources, and prompts here\n * - Keep server setup modular and extensible\n * - Import tools from ../tools/ and register them in the handlers\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { ReadImageTool, UnsplashSearchTool } from '../tools';\n\nexport function createServer(): Server {\n const server = new Server(\n {\n name: 'imagine-mcp',\n version: '0.1.0',\n },\n {\n capabilities: {\n tools: {},\n },\n },\n );\n\n // Initialize tools\n const unsplashSearchTool = new UnsplashSearchTool();\n const readImageTool = new ReadImageTool();\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [unsplashSearchTool.getDefinition(), readImageTool.getDefinition()],\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n if (name === UnsplashSearchTool.TOOL_NAME) {\n return await unsplashSearchTool.execute(args as any);\n }\n if (name === ReadImageTool.TOOL_NAME) {\n return await readImageTool.execute(args as any);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: `Unknown tool: ${name}`,\n },\n ],\n isError: true,\n };\n });\n\n return server;\n}\n","/**\n * HTTP Transport Handler\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Session management for stateful connections\n * - Streamable HTTP protocol (2025-03-26) with resumability support\n * - Factory pattern for creating MCP server instances per session\n *\n * CODING STANDARDS:\n * - Use async/await for all asynchronous operations\n * - Implement proper session lifecycle management\n * - Handle errors gracefully with appropriate HTTP status codes\n * - Provide health check endpoint for monitoring\n * - Clean up resources on shutdown\n *\n * AVOID:\n * - Sharing MCP server instances across sessions (use factory pattern)\n * - Forgetting to clean up sessions on disconnect\n * - Missing error handling for request processing\n * - Hardcoded configuration (use TransportConfig)\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';\nimport express, { type Request, type Response } from 'express';\nimport type { HttpTransportHandler as IHttpTransportHandler, TransportConfig } from '../types/index.js';\n\n/**\n * Session data for HTTP connections\n */\ninterface HttpSession {\n transport: StreamableHTTPServerTransport;\n server: McpServer;\n}\n\n/**\n * HTTP session manager\n */\nclass HttpFullSessionManager {\n private sessions: Map<string, HttpSession> = new Map();\n\n getSession(sessionId: string): HttpSession | undefined {\n return this.sessions.get(sessionId);\n }\n\n setSession(sessionId: string, transport: StreamableHTTPServerTransport, server: McpServer): void {\n this.sessions.set(sessionId, { transport, server });\n }\n\n deleteSession(sessionId: string): void {\n const session = this.sessions.get(sessionId);\n if (session) {\n session.server.close();\n }\n this.sessions.delete(sessionId);\n }\n\n hasSession(sessionId: string): boolean {\n return this.sessions.has(sessionId);\n }\n\n clear(): void {\n for (const session of this.sessions.values()) {\n session.server.close();\n }\n this.sessions.clear();\n }\n}\n\n/**\n * HTTP transport handler using Streamable HTTP (protocol version 2025-03-26)\n * Provides stateful session management with resumability support\n */\nexport class HttpTransportHandler implements IHttpTransportHandler {\n private serverFactory: () => McpServer;\n private app: express.Application;\n private server: HttpServer | null = null;\n private sessionManager: HttpFullSessionManager;\n private config: Required<TransportConfig>;\n\n constructor(serverFactory: McpServer | (() => McpServer), config: TransportConfig) {\n // Support both a factory function and a direct server instance for backwards compatibility\n this.serverFactory = typeof serverFactory === 'function' ? serverFactory : () => serverFactory;\n this.app = express();\n this.sessionManager = new HttpFullSessionManager();\n this.config = {\n mode: config.mode,\n port: config.port ?? 3000,\n host: config.host ?? 'localhost',\n };\n\n this.setupMiddleware();\n this.setupRoutes();\n }\n\n private setupMiddleware(): void {\n this.app.use(express.json());\n }\n\n private setupRoutes(): void {\n // Handle POST requests for client-to-server communication\n this.app.post('/mcp', async (req: Request, res: Response) => {\n await this.handlePostRequest(req, res);\n });\n\n // Handle GET requests for server-to-client notifications via SSE\n this.app.get('/mcp', async (req: Request, res: Response) => {\n await this.handleGetRequest(req, res);\n });\n\n // Handle DELETE requests for session termination\n this.app.delete('/mcp', async (req: Request, res: Response) => {\n await this.handleDeleteRequest(req, res);\n });\n\n // Health check endpoint\n this.app.get('/health', (_req: Request, res: Response) => {\n res.json({ status: 'ok', transport: 'http' });\n });\n }\n\n private async handlePostRequest(req: Request, res: Response): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n let transport: StreamableHTTPServerTransport;\n\n if (sessionId && this.sessionManager.hasSession(sessionId)) {\n // Reuse existing transport\n const session = this.sessionManager.getSession(sessionId)!;\n transport = session.transport;\n } else if (!sessionId && isInitializeRequest(req.body)) {\n // New initialization request - create new server instance\n const mcpServer = this.serverFactory();\n\n transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n enableJsonResponse: true, // Return JSON instead of SSE for simple request/response\n onsessioninitialized: (sessionId) => {\n this.sessionManager.setSession(sessionId, transport, mcpServer);\n },\n });\n\n // Clean up transport when closed\n transport.onclose = () => {\n if (transport.sessionId) {\n this.sessionManager.deleteSession(transport.sessionId);\n }\n };\n\n // Connect the new MCP server instance to the transport\n await mcpServer.connect(transport);\n } else {\n // Invalid request\n res.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Bad Request: No valid session ID provided',\n },\n id: null,\n });\n return;\n }\n\n // Handle the request\n await transport.handleRequest(req, res, req.body);\n }\n\n private async handleGetRequest(req: Request, res: Response): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n if (!sessionId || !this.sessionManager.hasSession(sessionId)) {\n res.status(400).send('Invalid or missing session ID');\n return;\n }\n\n const session = this.sessionManager.getSession(sessionId)!;\n await session.transport.handleRequest(req, res);\n }\n\n private async handleDeleteRequest(req: Request, res: Response): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n if (!sessionId || !this.sessionManager.hasSession(sessionId)) {\n res.status(400).send('Invalid or missing session ID');\n return;\n }\n\n const session = this.sessionManager.getSession(sessionId)!;\n await session.transport.handleRequest(req, res);\n\n // Clean up session\n this.sessionManager.deleteSession(sessionId);\n }\n\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.server = this.app.listen(this.config.port, this.config.host, () => {\n process.stderr.write(\n `imagine-mcp MCP server started on http://${this.config.host}:${this.config.port}/mcp\\n`,\n );\n process.stderr.write(`Health check: http://${this.config.host}:${this.config.port}/health\\n`);\n resolve();\n });\n\n this.server.on('error', (error: Error) => {\n reject(error);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.server) {\n // Clear all sessions\n this.sessionManager.clear();\n\n this.server.close((err?: Error) => {\n if (err) {\n reject(err);\n } else {\n this.server = null;\n resolve();\n }\n });\n } else {\n resolve();\n }\n });\n }\n\n getPort(): number {\n return this.config.port;\n }\n\n getHost(): string {\n return this.config.host;\n }\n}\n","/**\n * SSE Transport Handler\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Session management for stateful SSE connections\n * - Legacy SSE protocol (2024-11-05) with separate endpoints\n * - Factory pattern for creating MCP server instances per connection\n *\n * CODING STANDARDS:\n * - Use async/await for all asynchronous operations\n * - Implement proper session lifecycle management\n * - Handle connection cleanup on client disconnect\n * - Provide health check endpoint for monitoring\n * - Clean up resources on shutdown\n *\n * AVOID:\n * - Sharing MCP server instances across sessions (use factory pattern)\n * - Forgetting to clean up sessions on disconnect\n * - Missing error handling for connection/message processing\n * - Hardcoded configuration (use TransportConfig)\n */\n\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js';\nimport { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\nimport express, { type Request, type Response } from 'express';\nimport type { HttpTransportHandler as IHttpTransportHandler, TransportConfig } from '../types/index.js';\n\n/**\n * Session data for SSE connections\n */\ninterface SseSession {\n transport: SSEServerTransport;\n server: McpServer;\n}\n\n/**\n * Session manager for SSE transports\n */\nclass SseSessionManager {\n private sessions: Map<string, SseSession> = new Map();\n\n getSession(sessionId: string): SSEServerTransport | undefined {\n return this.sessions.get(sessionId)?.transport;\n }\n\n setSession(sessionId: string, transport: SSEServerTransport, server: McpServer): void {\n this.sessions.set(sessionId, { transport, server });\n }\n\n deleteSession(sessionId: string): void {\n const session = this.sessions.get(sessionId);\n if (session) {\n // Close the server instance\n session.server.close();\n }\n this.sessions.delete(sessionId);\n }\n\n hasSession(sessionId: string): boolean {\n return this.sessions.has(sessionId);\n }\n\n clear(): void {\n // Close all server instances\n for (const session of this.sessions.values()) {\n session.server.close();\n }\n this.sessions.clear();\n }\n}\n\n/**\n * SSE (Server-Sent Events) transport handler\n * Legacy transport for backwards compatibility (protocol version 2024-11-05)\n * Uses separate endpoints: /sse for SSE stream (GET) and /messages for client messages (POST)\n */\nexport class SseTransportHandler implements IHttpTransportHandler {\n private serverFactory: () => McpServer;\n private app: express.Application;\n private server: HttpServer | null = null;\n private sessionManager: SseSessionManager;\n private config: Required<TransportConfig>;\n\n constructor(serverFactory: McpServer | (() => McpServer), config: TransportConfig) {\n // Support both a factory function and a direct server instance for backwards compatibility\n this.serverFactory = typeof serverFactory === 'function' ? serverFactory : () => serverFactory;\n this.app = express();\n this.sessionManager = new SseSessionManager();\n this.config = {\n mode: config.mode,\n port: config.port ?? 3000,\n host: config.host ?? 'localhost',\n };\n\n this.setupMiddleware();\n this.setupRoutes();\n }\n\n private setupMiddleware(): void {\n this.app.use(express.json());\n }\n\n private setupRoutes(): void {\n // SSE endpoint - establishes the SSE stream\n this.app.get('/sse', async (req: Request, res: Response) => {\n await this.handleSseConnection(req, res);\n });\n\n // Messages endpoint - receives client messages\n this.app.post('/messages', async (req: Request, res: Response) => {\n await this.handlePostMessage(req, res);\n });\n\n // Health check endpoint\n this.app.get('/health', (_req: Request, res: Response) => {\n res.json({ status: 'ok', transport: 'sse' });\n });\n }\n\n private async handleSseConnection(_req: Request, res: Response): Promise<void> {\n try {\n // Create a new MCP server instance for this SSE connection\n const mcpServer = this.serverFactory();\n\n // Create SSE transport\n const transport = new SSEServerTransport('/messages', res);\n\n // Store the transport and server\n this.sessionManager.setSession(transport.sessionId, transport, mcpServer);\n\n // Clean up when connection closes\n res.on('close', () => {\n this.sessionManager.deleteSession(transport.sessionId);\n });\n\n // Connect the new server instance to the transport\n await mcpServer.connect(transport);\n\n process.stderr.write(`SSE session established: ${transport.sessionId}\\n`);\n } catch (error) {\n process.stderr.write(\n `Error handling SSE connection: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n if (!res.headersSent) {\n res.status(500).send('Internal Server Error');\n }\n }\n }\n\n private async handlePostMessage(req: Request, res: Response): Promise<void> {\n const sessionId = req.query.sessionId as string;\n\n if (!sessionId) {\n res.status(400).send('Missing sessionId query parameter');\n return;\n }\n\n const transport = this.sessionManager.getSession(sessionId);\n\n if (!transport) {\n res.status(404).send('No transport found for sessionId');\n return;\n }\n\n try {\n await transport.handlePostMessage(req, res, req.body);\n } catch (error) {\n process.stderr.write(`Error handling post message: ${error instanceof Error ? error.message : String(error)}\\n`);\n if (!res.headersSent) {\n res.status(500).send('Internal Server Error');\n }\n }\n }\n\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.server = this.app.listen(this.config.port, this.config.host, () => {\n process.stderr.write(\n `imagine-mcp MCP server started with SSE transport on http://${this.config.host}:${this.config.port}\\n`,\n );\n process.stderr.write(`SSE endpoint: http://${this.config.host}:${this.config.port}/sse\\n`);\n process.stderr.write(`Messages endpoint: http://${this.config.host}:${this.config.port}/messages\\n`);\n process.stderr.write(`Health check: http://${this.config.host}:${this.config.port}/health\\n`);\n resolve();\n });\n\n this.server.on('error', (error: Error) => {\n reject(error);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.server) {\n // Clear all sessions\n this.sessionManager.clear();\n\n this.server.close((err?: Error) => {\n if (err) {\n reject(err);\n } else {\n this.server = null;\n resolve();\n }\n });\n } else {\n resolve();\n }\n });\n }\n\n getPort(): number {\n return this.config.port;\n }\n\n getHost(): string {\n return this.config.host;\n }\n}\n","/**\n * STDIO Transport\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Standard I/O based communication for CLI usage\n *\n * CODING STANDARDS:\n * - Initialize server and transport properly\n * - Handle cleanup on shutdown with stop() method\n * - Use async/await for all operations\n *\n * AVOID:\n * - Forgetting to close transport on shutdown\n * - Missing error handling for connection failures\n */\n\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { TransportHandler } from '../types/index.js';\n\n/**\n * Stdio transport handler for MCP server\n * Used for command-line and direct integrations\n */\nexport class StdioTransportHandler implements TransportHandler {\n private server: Server;\n private transport: StdioServerTransport | null = null;\n\n constructor(server: Server) {\n this.server = server;\n }\n\n async start(): Promise<void> {\n this.transport = new StdioServerTransport();\n await this.server.connect(this.transport);\n process.stderr.write('imagine-mcp MCP server started on stdio\\n');\n }\n\n async stop(): Promise<void> {\n if (this.transport) {\n await this.transport.close();\n this.transport = null;\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stdio-qsNJYZZf.cjs","names":["MIME_BY_EXTENSION: Record<string, string>","result: ReadImageResult","McpError","ErrorCode","constants","data: Buffer","McpError","ErrorCode","options: ReadImageOptions","z","z","McpError","ErrorCode","Server","ListToolsRequestSchema","CallToolRequestSchema","config","transport: StreamableHTTPServerTransport","StreamableHTTPServerTransport","sessionId","config","SSEServerTransport","StdioServerTransport"],"sources":["../src/utils.ts","../src/services/ImageReader.ts","../src/tools/ReadImageTool.ts","../src/imagineEnvSchema.ts","../src/config.ts","../src/services/UnsplashService.ts","../src/tools/UnsplashSearchTool.ts","../src/server/index.ts","../src/transports/http.ts","../src/transports/sse.ts","../src/transports/stdio.ts"],"sourcesContent":["import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport fetch from 'node-fetch';\n\nimport { SUPPORTED_INPUT_FORMATS, SUPPORTED_OUTPUT_FORMATS } from './constants';\n\n/**\n * Validates if the provided format is supported for input\n */\nexport function isValidInputFormat(format: string): boolean {\n return SUPPORTED_INPUT_FORMATS.includes(format.toLowerCase());\n}\n\n/**\n * Validates if the provided format is supported for output\n */\nexport function isValidOutputFormat(format: string): boolean {\n return SUPPORTED_OUTPUT_FORMATS.includes(format.toLowerCase());\n}\n\n/**\n * Validates if the provided dimensions are valid\n */\nexport function isValidDimensions(width?: number, height?: number): boolean {\n if (width !== undefined && (width <= 0 || width > 10000)) {\n return false;\n }\n if (height !== undefined && (height <= 0 || height > 10000)) {\n return false;\n }\n return true;\n}\n\n/**\n * Validates if the provided quality is valid\n */\nexport function isValidQuality(quality?: number): boolean {\n if (quality !== undefined && (quality < 1 || quality > 100)) {\n return false;\n }\n return true;\n}\n\n/**\n * Extracts the file extension from a filename\n */\nexport function getFileExtension(filename: string): string {\n const parts = filename.split('.');\n return parts.length > 1 ? parts.pop()!.toLowerCase() : '';\n}\n\n/**\n * Converts a base64 string to a Buffer\n */\nexport function base64ToBuffer(base64: string): Buffer {\n // Remove data URL prefix if present\n const base64Data = base64.replace(/^data:image\\/\\w+;base64,/, '');\n return Buffer.from(base64Data, 'base64');\n}\n\n/**\n * Converts a Buffer to a base64 string\n */\nexport function bufferToBase64(buffer: Buffer, mimeType: string): string {\n return `data:${mimeType};base64,${buffer.toString('base64')}`;\n}\n\n/**\n * Normalizes a file path by handling escaped characters and spaces\n *\n * This function handles cases like 'a\\ name.png' by converting them to 'a name.png'\n */\nexport function normalizeFilePath(filePath: string): string {\n // Replace escaped spaces (\\ ) with actual spaces\n let normalizedPath = filePath.replace(/\\\\+ /g, ' ');\n\n // Replace other common escaped characters\n normalizedPath = normalizedPath\n .replace(/\\\\+'/g, \"'\")\n .replace(/\\\\+\"/g, '\"')\n .replace(/\\\\+`/g, '`')\n .replace(/\\\\+\\(/g, '(')\n .replace(/\\\\+\\)/g, ')')\n .replace(/\\\\+\\[/g, '[')\n .replace(/\\\\+\\]/g, ']')\n .replace(/\\\\+\\{/g, '{')\n .replace(/\\\\+\\}/g, '}');\n\n return normalizedPath;\n}\n\n/**\n * Fetches an image from a URL and returns it as a Buffer\n */\nexport async function fetchImageFromUrl(url: string): Promise<Buffer> {\n try {\n const response = await fetch(url);\n\n // Check if the response is successful\n if (!response.ok) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Failed to fetch image from URL: ${url}, status code: ${response.status}`,\n );\n }\n\n // Check if the content type is an image\n const contentType = response.headers.get('content-type');\n if (!contentType || !contentType.startsWith('image/')) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `URL does not point to an image: ${url}, content-type: ${contentType}`,\n );\n }\n\n // Get the response as an ArrayBuffer and convert to Buffer\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer);\n } catch (error) {\n if (error instanceof McpError) {\n throw error;\n }\n throw new McpError(\n ErrorCode.InternalError,\n `Error fetching image from URL: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n","import { createHash } from 'node:crypto';\nimport { constants } from 'node:fs';\nimport { access, readFile } from 'node:fs/promises';\nimport { basename, extname, resolve } from 'node:path';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport fetch from 'node-fetch';\n\nimport { normalizeFilePath } from '../utils';\n\ninterface ImageSource {\n source: string;\n sourceType: 'data-uri' | 'url' | 'file';\n mimeType: string;\n sizeBytes: number;\n data: Buffer;\n}\n\nexport interface ReadImageResult {\n source: string;\n sourceType: 'data-uri' | 'url' | 'file';\n mimeType: string;\n sizeBytes: number;\n sha256: string;\n fileName?: string;\n base64?: string;\n}\n\nexport interface ReadImageOptions {\n includeBase64?: boolean;\n}\n\nconst DEFAULT_MIME_TYPE = 'application/octet-stream';\nconst MIME_BY_EXTENSION: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.bmp': 'image/bmp',\n '.svg': 'image/svg+xml',\n '.heic': 'image/heic',\n '.heif': 'image/heif',\n};\n\nexport class ImageReader {\n async readImage(source: string, options: ReadImageOptions = {}): Promise<ReadImageResult> {\n const imageSource = await this.resolveImageSource(source);\n const sha256 = createHash('sha256').update(imageSource.data).digest('hex');\n\n const result: ReadImageResult = {\n source: imageSource.source,\n sourceType: imageSource.sourceType,\n mimeType: imageSource.mimeType,\n sizeBytes: imageSource.sizeBytes,\n sha256,\n };\n\n if (imageSource.sourceType !== 'data-uri') {\n result.fileName = basename(imageSource.source);\n }\n\n if (options.includeBase64) {\n result.base64 = imageSource.data.toString('base64');\n }\n\n return result;\n }\n\n private async resolveImageSource(source: string): Promise<ImageSource> {\n const trimmed = source.trim();\n\n if (!trimmed) {\n throw new McpError(ErrorCode.InvalidParams, 'Image source cannot be empty.');\n }\n\n if (trimmed.startsWith('data:')) {\n return this.resolveDataUri(trimmed);\n }\n\n if (/^https?:\\/\\//i.test(trimmed)) {\n return this.resolveRemoteImage(trimmed);\n }\n\n return this.resolveFile(trimmed);\n }\n\n private async resolveFile(filePath: string): Promise<ImageSource> {\n const normalizedPath = resolve(normalizeFilePath(filePath));\n\n try {\n await access(normalizedPath, constants.F_OK);\n } catch (error) {\n throw new McpError(ErrorCode.InvalidParams, `Image file not found at path: ${filePath}`);\n }\n\n let data: Buffer;\n try {\n data = await readFile(normalizedPath);\n } catch (error) {\n throw new McpError(\n ErrorCode.InternalError,\n `Failed to read image file: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n const extension = extname(normalizedPath).toLowerCase();\n const mimeType = MIME_BY_EXTENSION[extension] || DEFAULT_MIME_TYPE;\n\n return {\n source: normalizedPath,\n sourceType: 'file',\n mimeType,\n sizeBytes: data.length,\n data,\n };\n }\n\n private async resolveRemoteImage(url: string): Promise<ImageSource> {\n try {\n const response = await fetch(url, { redirect: 'follow' });\n if (!response.ok) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Failed to fetch image from URL: ${url} (${response.status} ${response.statusText})`,\n );\n }\n\n const arrayBuffer = await response.arrayBuffer();\n const data = Buffer.from(arrayBuffer);\n const contentType = response.headers.get('content-type')?.split(';')[0];\n const fileName = basename(new URL(url).pathname || 'image');\n\n return {\n source: url,\n sourceType: 'url',\n mimeType: contentType ?? MIME_BY_EXTENSION[extname(fileName)] ?? DEFAULT_MIME_TYPE,\n sizeBytes: data.length,\n data,\n };\n } catch (error) {\n if (error instanceof McpError) {\n throw error;\n }\n throw new McpError(\n ErrorCode.InternalError,\n `Failed to download image from URL: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private resolveDataUri(dataUri: string): ImageSource {\n const commaIndex = dataUri.indexOf(',');\n if (commaIndex === -1 || commaIndex === dataUri.length - 1) {\n throw new McpError(\n ErrorCode.InvalidParams,\n 'Invalid data URI format. Expected `data:[<mime>];base64,<payload>`.',\n );\n }\n\n const metadata = dataUri.slice(5, commaIndex).toLowerCase();\n const payload = dataUri.slice(commaIndex + 1);\n const isBase64 = metadata.includes('base64');\n const declaredMimeType = metadata.split(';')[0] || DEFAULT_MIME_TYPE;\n\n try {\n const data = isBase64 ? Buffer.from(payload, 'base64') : Buffer.from(decodeURIComponent(payload));\n return {\n source: dataUri,\n sourceType: 'data-uri',\n mimeType: declaredMimeType || DEFAULT_MIME_TYPE,\n sizeBytes: data.length,\n data,\n };\n } catch (error) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Failed to decode data URI payload: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n}\n","import { CallToolResult, ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { ImageReader, type ReadImageOptions } from '../services/ImageReader';\nimport type { Tool, ToolDefinition } from '../types/index';\n\ninterface ReadImageToolInput {\n source: string;\n includeBase64?: boolean;\n}\n\nexport class ReadImageTool implements Tool<ReadImageToolInput> {\n static readonly TOOL_NAME = 'read-image';\n\n private service = new ImageReader();\n\n getDefinition(): ToolDefinition {\n return {\n name: ReadImageTool.TOOL_NAME,\n description: 'Read an image from a local path, URL, or data URI and return metadata.',\n inputSchema: {\n type: 'object',\n properties: {\n source: {\n type: 'string',\n minLength: 1,\n description: 'Image source as a local path, HTTP(S) URL, or data URI.',\n },\n includeBase64: {\n type: 'boolean',\n description: 'Whether to include base64-encoded output.',\n default: false,\n },\n },\n required: ['source'],\n additionalProperties: false,\n },\n };\n }\n\n async execute(input: ReadImageToolInput): Promise<CallToolResult> {\n try {\n if (!input || typeof input !== 'object') {\n throw new McpError(ErrorCode.InvalidParams, 'Tool input must be an object.');\n }\n\n const payload = input as ReadImageToolInput & { [key: string]: unknown };\n const source = payload.source;\n const includeBase64 = payload.includeBase64;\n if (!source || typeof source !== 'string' || source.trim().length === 0) {\n throw new McpError(ErrorCode.InvalidParams, 'source must be a non-empty string.');\n }\n if (includeBase64 !== undefined && typeof includeBase64 !== 'boolean') {\n throw new McpError(ErrorCode.InvalidParams, 'includeBase64 must be a boolean.');\n }\n\n const options: ReadImageOptions = {\n includeBase64,\n };\n const result = await this.service.readImage(source, options);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n}\n","import { z } from 'zod';\n\n/**\n * Environment variable schema for imagine-mcp\n *\n * Following Agiflow naming conventions:\n * - Service credentials use provider-specific prefixes\n * - Upload service selection uses UPLOAD_SERVICE\n */\nexport const envSchema = z\n .object({\n // Upload service selection\n UPLOAD_SERVICE: z.enum(['s3', 'cloudflare', 'gcloud']).optional().default('s3'),\n\n // AWS S3 Configuration\n S3_BUCKET: z.string().optional(),\n AWS_ACCESS_KEY_ID: z.string().optional(),\n AWS_SECRET_ACCESS_KEY: z.string().optional(),\n S3_REGION: z.string().optional().default('us-east-1'),\n S3_ENDPOINT: z.string().url('S3_ENDPOINT must be a valid URL').optional(),\n\n // Cloudflare R2 Configuration\n CLOUDFLARE_R2_BUCKET: z.string().optional(),\n CLOUDFLARE_R2_ACCESS_KEY_ID: z.string().optional(),\n CLOUDFLARE_R2_SECRET_ACCESS_KEY: z.string().optional(),\n CLOUDFLARE_R2_REGION: z.string().optional().default('auto'),\n CLOUDFLARE_R2_ENDPOINT: z.string().url('CLOUDFLARE_R2_ENDPOINT must be a valid URL').optional(),\n\n // Google Cloud Storage Configuration\n GCLOUD_BUCKET: z.string().optional(),\n GCLOUD_PROJECT_ID: z.string().optional(),\n GCLOUD_CREDENTIALS_PATH: z.string().optional(),\n\n // Unsplash API Configuration\n UNSPLASH_ACCESS_KEY: z.string().optional(),\n })\n .refine(\n (data) => {\n // If AWS credentials are provided, both must be present\n if (data.AWS_ACCESS_KEY_ID || data.AWS_SECRET_ACCESS_KEY) {\n return data.AWS_ACCESS_KEY_ID && data.AWS_SECRET_ACCESS_KEY;\n }\n return true;\n },\n {\n message: 'Both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be provided together',\n path: ['AWS_ACCESS_KEY_ID'],\n },\n );\n\nexport type EnvConfig = z.infer<typeof envSchema>;\n","import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod';\nimport { type EnvConfig, envSchema } from './imagineEnvSchema';\n\n/**\n * Centralized configuration service for imagine-mcp\n *\n * Validates environment variables at startup and provides\n * type-safe access to configuration throughout the application.\n */\nclass ConfigService {\n private static instance: ConfigService;\n private config: EnvConfig;\n\n private constructor() {\n try {\n this.config = envSchema.parse(process.env);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const errorMessage = error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join('; ');\n throw new McpError(ErrorCode.InternalError, `Environment configuration validation failed: ${errorMessage}`);\n }\n throw error;\n }\n }\n\n /**\n * Get the singleton instance of ConfigService\n */\n public static getInstance(): ConfigService {\n if (!ConfigService.instance) {\n ConfigService.instance = new ConfigService();\n }\n return ConfigService.instance;\n }\n\n /**\n * Get the validated configuration\n */\n public getConfig(): EnvConfig {\n return this.config;\n }\n\n /**\n * Get S3 configuration\n */\n public getS3Config() {\n return {\n bucket: this.config.S3_BUCKET,\n accessKeyId: this.config.AWS_ACCESS_KEY_ID,\n secretAccessKey: this.config.AWS_SECRET_ACCESS_KEY,\n region: this.config.S3_REGION,\n endpoint: this.config.S3_ENDPOINT,\n };\n }\n\n /**\n * Get Cloudflare R2 configuration\n */\n public getCloudflareConfig() {\n return {\n bucket: this.config.CLOUDFLARE_R2_BUCKET,\n accessKeyId: this.config.CLOUDFLARE_R2_ACCESS_KEY_ID,\n secretAccessKey: this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY,\n region: this.config.CLOUDFLARE_R2_REGION,\n endpoint: this.config.CLOUDFLARE_R2_ENDPOINT,\n };\n }\n\n /**\n * Get Google Cloud Storage configuration\n */\n public getGCloudConfig() {\n return {\n bucket: this.config.GCLOUD_BUCKET,\n projectId: this.config.GCLOUD_PROJECT_ID,\n credentialsPath: this.config.GCLOUD_CREDENTIALS_PATH,\n };\n }\n\n /**\n * Get the default upload service\n */\n public getUploadService() {\n return this.config.UPLOAD_SERVICE;\n }\n\n /**\n * Get Unsplash API configuration\n */\n public getUnsplashConfig() {\n return {\n accessKey: this.config.UNSPLASH_ACCESS_KEY,\n };\n }\n\n /**\n * Validate that required credentials exist for a specific service\n */\n public validateServiceConfig(service: 's3' | 'cloudflare' | 'gcloud'): void {\n switch (service) {\n case 's3':\n if (!this.config.S3_BUCKET) {\n throw new McpError(ErrorCode.InvalidParams, 'S3_BUCKET is required for S3 upload service');\n }\n break;\n case 'cloudflare':\n if (!this.config.CLOUDFLARE_R2_BUCKET) {\n throw new McpError(ErrorCode.InvalidParams, 'CLOUDFLARE_R2_BUCKET is required for Cloudflare upload service');\n }\n if (!this.config.CLOUDFLARE_R2_ACCESS_KEY_ID || !this.config.CLOUDFLARE_R2_SECRET_ACCESS_KEY) {\n throw new McpError(\n ErrorCode.InvalidParams,\n 'CLOUDFLARE_R2_ACCESS_KEY_ID and CLOUDFLARE_R2_SECRET_ACCESS_KEY are required for Cloudflare upload service',\n );\n }\n if (!this.config.CLOUDFLARE_R2_ENDPOINT) {\n throw new McpError(\n ErrorCode.InvalidParams,\n 'CLOUDFLARE_R2_ENDPOINT is required for Cloudflare upload service',\n );\n }\n break;\n case 'gcloud':\n if (!this.config.GCLOUD_BUCKET) {\n throw new McpError(ErrorCode.InvalidParams, 'GCLOUD_BUCKET is required for Google Cloud upload service');\n }\n if (!this.config.GCLOUD_PROJECT_ID) {\n throw new McpError(ErrorCode.InvalidParams, 'GCLOUD_PROJECT_ID is required for Google Cloud upload service');\n }\n break;\n }\n }\n}\n\n// Export singleton instance\nexport const config = ConfigService.getInstance();\n","/**\n * UnsplashService\n *\n * DESIGN PATTERNS:\n * - Service pattern for business logic encapsulation\n * - Single responsibility principle\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Keep methods focused and well-named\n * - Document complex logic with comments\n *\n * AVOID:\n * - Mixing concerns (keep focused on single domain)\n * - Direct tool implementation (services should be tool-agnostic)\n */\n\nimport { createApi } from 'unsplash-js';\nimport { config } from '../config.js';\n\nexport interface UnsplashImage {\n id: string;\n description: string | null;\n alt_description: string | null;\n urls: {\n raw: string;\n full: string;\n regular: string;\n small: string;\n thumb: string;\n };\n links: {\n html: string;\n download: string;\n };\n user: {\n name: string;\n username: string;\n portfolio_url: string | null;\n };\n width: number;\n height: number;\n color: string;\n likes: number;\n}\n\nexport interface SearchImagesParams {\n query: string;\n perPage?: number;\n page?: number;\n orientation?: 'landscape' | 'portrait' | 'squarish';\n color?:\n | 'black_and_white'\n | 'black'\n | 'white'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'magenta'\n | 'green'\n | 'teal'\n | 'blue';\n}\n\nexport class UnsplashService {\n private api: ReturnType<typeof createApi>;\n\n constructor(accessKey?: string) {\n const unsplashConfig = config.getUnsplashConfig();\n const apiKey = accessKey || unsplashConfig.accessKey;\n\n if (!apiKey) {\n throw new Error('Unsplash API key is required. Set UNSPLASH_ACCESS_KEY environment variable.');\n }\n\n this.api = createApi({\n accessKey: apiKey,\n });\n }\n\n async searchImages(params: SearchImagesParams): Promise<UnsplashImage[]> {\n try {\n const result = await this.api.search.getPhotos({\n query: params.query,\n page: params.page || 1,\n perPage: params.perPage || 10,\n orientation: params.orientation,\n color: params.color,\n });\n\n if (result.errors) {\n throw new Error(`Unsplash API error: ${result.errors.join(', ')}`);\n }\n\n if (!result.response) {\n throw new Error('No response from Unsplash API');\n }\n\n return result.response.results.map((photo) => ({\n id: photo.id,\n description: photo.description,\n alt_description: photo.alt_description,\n urls: {\n raw: photo.urls.raw,\n full: photo.urls.full,\n regular: photo.urls.regular,\n small: photo.urls.small,\n thumb: photo.urls.thumb,\n },\n links: {\n html: photo.links.html,\n download: photo.links.download,\n },\n user: {\n name: photo.user.name,\n username: photo.user.username,\n portfolio_url: photo.user.portfolio_url,\n },\n width: photo.width,\n height: photo.height,\n color: photo.color || '#000000',\n likes: photo.likes,\n }));\n } catch (error) {\n throw new Error(`Failed to search Unsplash images: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n}\n","/**\n * UnsplashSearchTool\n *\nhttps://github.com/BoomLinkAi/image-worker-mcp * DESIGN PATTERNS:\n * - Tool pattern with getDefinition() and execute() methods\n * - Service delegation for business logic\n * - JSON Schema validation for inputs\n *\n * CODING STANDARDS:\n * - Implement Tool interface from ../types\n * - Use TOOL_NAME constant with snake_case (e.g., 'file_read')\n * - Return CallToolResult with content array\n * - Handle errors with isError flag\n * - Delegate complex logic to services\n *\n * AVOID:\n * - Complex business logic in execute method\n * - Unhandled promise rejections\n * - Missing input validation\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { UnsplashService } from '../services/UnsplashService';\nimport type { Tool, ToolDefinition } from '../types/index';\n\ninterface UnsplashSearchToolInput {\n query: string;\n perPage?: number;\n page?: number;\n orientation?: 'landscape' | 'portrait' | 'squarish';\n color?:\n | 'black_and_white'\n | 'black'\n | 'white'\n | 'yellow'\n | 'orange'\n | 'red'\n | 'purple'\n | 'magenta'\n | 'green'\n | 'teal'\n | 'blue';\n}\n\nexport class UnsplashSearchTool implements Tool<UnsplashSearchToolInput> {\n static readonly TOOL_NAME = 'unsplash_search';\n\n private service: UnsplashService | null;\n private readonly accessKey?: string;\n\n constructor(accessKey?: string) {\n this.accessKey = accessKey;\n this.service = null;\n }\n\n private getService(): UnsplashService {\n if (!this.service) {\n this.service = new UnsplashService(this.accessKey);\n }\n return this.service;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: UnsplashSearchTool.TOOL_NAME,\n description: 'Search for stock images from Unsplash by keyword and return image URLs with metadata',\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Search query (e.g., \"sunset\", \"technology\", \"nature\")',\n },\n perPage: {\n type: 'number',\n description: 'Number of results per page (1-30, default: 10)',\n minimum: 1,\n maximum: 30,\n },\n page: {\n type: 'number',\n description: 'Page number for pagination (default: 1)',\n minimum: 1,\n },\n orientation: {\n type: 'string',\n enum: ['landscape', 'portrait', 'squarish'],\n description: 'Filter by photo orientation',\n },\n color: {\n type: 'string',\n enum: [\n 'black_and_white',\n 'black',\n 'white',\n 'yellow',\n 'orange',\n 'red',\n 'purple',\n 'magenta',\n 'green',\n 'teal',\n 'blue',\n ],\n description: 'Filter by photo color',\n },\n },\n required: ['query'],\n additionalProperties: false,\n },\n };\n }\n\n async execute(input: UnsplashSearchToolInput): Promise<CallToolResult> {\n try {\n const images = await this.getService().searchImages({\n query: input.query,\n perPage: input.perPage,\n page: input.page,\n orientation: input.orientation,\n color: input.color,\n });\n\n if (images.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: `No images found for query: \"${input.query}\"`,\n },\n ],\n };\n }\n\n const formattedResults = images\n .map((img, index) => {\n return `\n📷 **Image ${index + 1}**\n- **ID**: ${img.id}\n- **Description**: ${img.alt_description || img.description || 'No description'}\n- **Photographer**: ${img.user.name} (@${img.user.username})\n- **Dimensions**: ${img.width}x${img.height}px\n- **Color**: ${img.color}\n- **Likes**: ${img.likes}\n\n**URLs**:\n- Full: ${img.urls.full}\n- Regular: ${img.urls.regular}\n- Small: ${img.urls.small}\n- Thumbnail: ${img.urls.thumb}\n\n**Links**:\n- View on Unsplash: ${img.links.html}\n- Download: ${img.links.download}\n${img.user.portfolio_url ? `- Photographer Portfolio: ${img.user.portfolio_url}` : ''}\n`.trim();\n })\n .join('\\n\\n' + '-'.repeat(80) + '\\n\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Found ${images.length} image(s) for \"${input.query}\":\\n\\n${formattedResults}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n}\n","/**\n * MCP Server Setup\n *\n * DESIGN PATTERNS:\n * - Factory pattern for server creation\n * - Tool registration pattern\n *\n * CODING STANDARDS:\n * - Register all tools, resources, and prompts here\n * - Keep server setup modular and extensible\n * - Import tools from ../tools/ and register them in the handlers\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { ReadImageTool, UnsplashSearchTool } from '../tools';\n\nexport function createServer(): Server {\n const server = new Server(\n {\n name: 'imagine-mcp',\n version: '0.1.0',\n },\n {\n capabilities: {\n tools: {},\n },\n },\n );\n\n // Initialize tools\n const unsplashSearchTool = new UnsplashSearchTool();\n const readImageTool = new ReadImageTool();\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [unsplashSearchTool.getDefinition(), readImageTool.getDefinition()],\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n if (name === UnsplashSearchTool.TOOL_NAME) {\n return await unsplashSearchTool.execute(args as any);\n }\n if (name === ReadImageTool.TOOL_NAME) {\n return await readImageTool.execute(args as any);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: `Unknown tool: ${name}`,\n },\n ],\n isError: true,\n };\n });\n\n return server;\n}\n","/**\n * HTTP Transport Handler\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Session management for stateful connections\n * - Streamable HTTP protocol (2025-03-26) with resumability support\n * - Factory pattern for creating MCP server instances per session\n *\n * CODING STANDARDS:\n * - Use async/await for all asynchronous operations\n * - Implement proper session lifecycle management\n * - Handle errors gracefully with appropriate HTTP status codes\n * - Provide health check endpoint for monitoring\n * - Clean up resources on shutdown\n *\n * AVOID:\n * - Sharing MCP server instances across sessions (use factory pattern)\n * - Forgetting to clean up sessions on disconnect\n * - Missing error handling for request processing\n * - Hardcoded configuration (use TransportConfig)\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';\nimport express, { type Request, type Response } from 'express';\nimport type { HttpTransportHandler as IHttpTransportHandler, TransportConfig } from '../types/index.js';\n\n/**\n * Session data for HTTP connections\n */\ninterface HttpSession {\n transport: StreamableHTTPServerTransport;\n server: McpServer;\n}\n\n/**\n * HTTP session manager\n */\nclass HttpFullSessionManager {\n private sessions: Map<string, HttpSession> = new Map();\n\n getSession(sessionId: string): HttpSession | undefined {\n return this.sessions.get(sessionId);\n }\n\n setSession(sessionId: string, transport: StreamableHTTPServerTransport, server: McpServer): void {\n this.sessions.set(sessionId, { transport, server });\n }\n\n deleteSession(sessionId: string): void {\n const session = this.sessions.get(sessionId);\n if (session) {\n session.server.close();\n }\n this.sessions.delete(sessionId);\n }\n\n hasSession(sessionId: string): boolean {\n return this.sessions.has(sessionId);\n }\n\n clear(): void {\n for (const session of this.sessions.values()) {\n session.server.close();\n }\n this.sessions.clear();\n }\n}\n\n/**\n * HTTP transport handler using Streamable HTTP (protocol version 2025-03-26)\n * Provides stateful session management with resumability support\n */\nexport class HttpTransportHandler implements IHttpTransportHandler {\n private serverFactory: () => McpServer;\n private app: express.Application;\n private server: HttpServer | null = null;\n private sessionManager: HttpFullSessionManager;\n private config: Required<TransportConfig>;\n\n constructor(serverFactory: McpServer | (() => McpServer), config: TransportConfig) {\n // Support both a factory function and a direct server instance for backwards compatibility\n this.serverFactory = typeof serverFactory === 'function' ? serverFactory : () => serverFactory;\n this.app = express();\n this.sessionManager = new HttpFullSessionManager();\n this.config = {\n mode: config.mode,\n port: config.port ?? 3000,\n host: config.host ?? 'localhost',\n };\n\n this.setupMiddleware();\n this.setupRoutes();\n }\n\n private setupMiddleware(): void {\n this.app.use(express.json());\n }\n\n private setupRoutes(): void {\n // Handle POST requests for client-to-server communication\n this.app.post('/mcp', async (req: Request, res: Response) => {\n await this.handlePostRequest(req, res);\n });\n\n // Handle GET requests for server-to-client notifications via SSE\n this.app.get('/mcp', async (req: Request, res: Response) => {\n await this.handleGetRequest(req, res);\n });\n\n // Handle DELETE requests for session termination\n this.app.delete('/mcp', async (req: Request, res: Response) => {\n await this.handleDeleteRequest(req, res);\n });\n\n // Health check endpoint\n this.app.get('/health', (_req: Request, res: Response) => {\n res.json({ status: 'ok', transport: 'http' });\n });\n }\n\n private async handlePostRequest(req: Request, res: Response): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n let transport: StreamableHTTPServerTransport;\n\n if (sessionId && this.sessionManager.hasSession(sessionId)) {\n // Reuse existing transport\n const session = this.sessionManager.getSession(sessionId)!;\n transport = session.transport;\n } else if (!sessionId && isInitializeRequest(req.body)) {\n // New initialization request - create new server instance\n const mcpServer = this.serverFactory();\n\n transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n enableJsonResponse: true, // Return JSON instead of SSE for simple request/response\n onsessioninitialized: (sessionId) => {\n this.sessionManager.setSession(sessionId, transport, mcpServer);\n },\n });\n\n // Clean up transport when closed\n transport.onclose = () => {\n if (transport.sessionId) {\n this.sessionManager.deleteSession(transport.sessionId);\n }\n };\n\n // Connect the new MCP server instance to the transport\n await mcpServer.connect(transport);\n } else {\n // Invalid request\n res.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Bad Request: No valid session ID provided',\n },\n id: null,\n });\n return;\n }\n\n // Handle the request\n await transport.handleRequest(req, res, req.body);\n }\n\n private async handleGetRequest(req: Request, res: Response): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n if (!sessionId || !this.sessionManager.hasSession(sessionId)) {\n res.status(400).send('Invalid or missing session ID');\n return;\n }\n\n const session = this.sessionManager.getSession(sessionId)!;\n await session.transport.handleRequest(req, res);\n }\n\n private async handleDeleteRequest(req: Request, res: Response): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n if (!sessionId || !this.sessionManager.hasSession(sessionId)) {\n res.status(400).send('Invalid or missing session ID');\n return;\n }\n\n const session = this.sessionManager.getSession(sessionId)!;\n await session.transport.handleRequest(req, res);\n\n // Clean up session\n this.sessionManager.deleteSession(sessionId);\n }\n\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.server = this.app.listen(this.config.port, this.config.host, () => {\n process.stderr.write(\n `imagine-mcp MCP server started on http://${this.config.host}:${this.config.port}/mcp\\n`,\n );\n process.stderr.write(`Health check: http://${this.config.host}:${this.config.port}/health\\n`);\n resolve();\n });\n\n this.server.on('error', (error: Error) => {\n reject(error);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.server) {\n // Clear all sessions\n this.sessionManager.clear();\n\n this.server.close((err?: Error) => {\n if (err) {\n reject(err);\n } else {\n this.server = null;\n resolve();\n }\n });\n } else {\n resolve();\n }\n });\n }\n\n getPort(): number {\n return this.config.port;\n }\n\n getHost(): string {\n return this.config.host;\n }\n}\n","/**\n * SSE Transport Handler\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Session management for stateful SSE connections\n * - Legacy SSE protocol (2024-11-05) with separate endpoints\n * - Factory pattern for creating MCP server instances per connection\n *\n * CODING STANDARDS:\n * - Use async/await for all asynchronous operations\n * - Implement proper session lifecycle management\n * - Handle connection cleanup on client disconnect\n * - Provide health check endpoint for monitoring\n * - Clean up resources on shutdown\n *\n * AVOID:\n * - Sharing MCP server instances across sessions (use factory pattern)\n * - Forgetting to clean up sessions on disconnect\n * - Missing error handling for connection/message processing\n * - Hardcoded configuration (use TransportConfig)\n */\n\nimport type { Server as HttpServer } from 'node:http';\nimport type { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js';\nimport { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\nimport express, { type Request, type Response } from 'express';\nimport type { HttpTransportHandler as IHttpTransportHandler, TransportConfig } from '../types/index.js';\n\n/**\n * Session data for SSE connections\n */\ninterface SseSession {\n transport: SSEServerTransport;\n server: McpServer;\n}\n\n/**\n * Session manager for SSE transports\n */\nclass SseSessionManager {\n private sessions: Map<string, SseSession> = new Map();\n\n getSession(sessionId: string): SSEServerTransport | undefined {\n return this.sessions.get(sessionId)?.transport;\n }\n\n setSession(sessionId: string, transport: SSEServerTransport, server: McpServer): void {\n this.sessions.set(sessionId, { transport, server });\n }\n\n deleteSession(sessionId: string): void {\n const session = this.sessions.get(sessionId);\n if (session) {\n // Close the server instance\n session.server.close();\n }\n this.sessions.delete(sessionId);\n }\n\n hasSession(sessionId: string): boolean {\n return this.sessions.has(sessionId);\n }\n\n clear(): void {\n // Close all server instances\n for (const session of this.sessions.values()) {\n session.server.close();\n }\n this.sessions.clear();\n }\n}\n\n/**\n * SSE (Server-Sent Events) transport handler\n * Legacy transport for backwards compatibility (protocol version 2024-11-05)\n * Uses separate endpoints: /sse for SSE stream (GET) and /messages for client messages (POST)\n */\nexport class SseTransportHandler implements IHttpTransportHandler {\n private serverFactory: () => McpServer;\n private app: express.Application;\n private server: HttpServer | null = null;\n private sessionManager: SseSessionManager;\n private config: Required<TransportConfig>;\n\n constructor(serverFactory: McpServer | (() => McpServer), config: TransportConfig) {\n // Support both a factory function and a direct server instance for backwards compatibility\n this.serverFactory = typeof serverFactory === 'function' ? serverFactory : () => serverFactory;\n this.app = express();\n this.sessionManager = new SseSessionManager();\n this.config = {\n mode: config.mode,\n port: config.port ?? 3000,\n host: config.host ?? 'localhost',\n };\n\n this.setupMiddleware();\n this.setupRoutes();\n }\n\n private setupMiddleware(): void {\n this.app.use(express.json());\n }\n\n private setupRoutes(): void {\n // SSE endpoint - establishes the SSE stream\n this.app.get('/sse', async (req: Request, res: Response) => {\n await this.handleSseConnection(req, res);\n });\n\n // Messages endpoint - receives client messages\n this.app.post('/messages', async (req: Request, res: Response) => {\n await this.handlePostMessage(req, res);\n });\n\n // Health check endpoint\n this.app.get('/health', (_req: Request, res: Response) => {\n res.json({ status: 'ok', transport: 'sse' });\n });\n }\n\n private async handleSseConnection(_req: Request, res: Response): Promise<void> {\n try {\n // Create a new MCP server instance for this SSE connection\n const mcpServer = this.serverFactory();\n\n // Create SSE transport\n const transport = new SSEServerTransport('/messages', res);\n\n // Store the transport and server\n this.sessionManager.setSession(transport.sessionId, transport, mcpServer);\n\n // Clean up when connection closes\n res.on('close', () => {\n this.sessionManager.deleteSession(transport.sessionId);\n });\n\n // Connect the new server instance to the transport\n await mcpServer.connect(transport);\n\n process.stderr.write(`SSE session established: ${transport.sessionId}\\n`);\n } catch (error) {\n process.stderr.write(\n `Error handling SSE connection: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n if (!res.headersSent) {\n res.status(500).send('Internal Server Error');\n }\n }\n }\n\n private async handlePostMessage(req: Request, res: Response): Promise<void> {\n const sessionId = req.query.sessionId as string;\n\n if (!sessionId) {\n res.status(400).send('Missing sessionId query parameter');\n return;\n }\n\n const transport = this.sessionManager.getSession(sessionId);\n\n if (!transport) {\n res.status(404).send('No transport found for sessionId');\n return;\n }\n\n try {\n await transport.handlePostMessage(req, res, req.body);\n } catch (error) {\n process.stderr.write(`Error handling post message: ${error instanceof Error ? error.message : String(error)}\\n`);\n if (!res.headersSent) {\n res.status(500).send('Internal Server Error');\n }\n }\n }\n\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.server = this.app.listen(this.config.port, this.config.host, () => {\n process.stderr.write(\n `imagine-mcp MCP server started with SSE transport on http://${this.config.host}:${this.config.port}\\n`,\n );\n process.stderr.write(`SSE endpoint: http://${this.config.host}:${this.config.port}/sse\\n`);\n process.stderr.write(`Messages endpoint: http://${this.config.host}:${this.config.port}/messages\\n`);\n process.stderr.write(`Health check: http://${this.config.host}:${this.config.port}/health\\n`);\n resolve();\n });\n\n this.server.on('error', (error: Error) => {\n reject(error);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.server) {\n // Clear all sessions\n this.sessionManager.clear();\n\n this.server.close((err?: Error) => {\n if (err) {\n reject(err);\n } else {\n this.server = null;\n resolve();\n }\n });\n } else {\n resolve();\n }\n });\n }\n\n getPort(): number {\n return this.config.port;\n }\n\n getHost(): string {\n return this.config.host;\n }\n}\n","/**\n * STDIO Transport\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Standard I/O based communication for CLI usage\n *\n * CODING STANDARDS:\n * - Initialize server and transport properly\n * - Handle cleanup on shutdown with stop() method\n * - Use async/await for all operations\n *\n * AVOID:\n * - Forgetting to close transport on shutdown\n * - Missing error handling for connection failures\n */\n\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { TransportHandler } from '../types/index.js';\n\n/**\n * Stdio transport handler for MCP server\n * Used for command-line and direct integrations\n */\nexport class StdioTransportHandler implements TransportHandler {\n private server: Server;\n private transport: StdioServerTransport | null = null;\n\n constructor(server: Server) {\n this.server = server;\n }\n\n async start(): Promise<void> {\n this.transport = new StdioServerTransport();\n await this.server.connect(this.transport);\n process.stderr.write('imagine-mcp MCP server started on stdio\\n');\n }\n\n async stop(): Promise<void> {\n if (this.transport) {\n await this.transport.close();\n this.transport = null;\n }\n }\n}\n"],"mappings":"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"}
|