mcp-hydrocoder-image 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +454 -0
- package/bin/install-skills.js +115 -0
- package/dist/api/geminiClient.d.ts +57 -0
- package/dist/api/geminiClient.d.ts.map +1 -0
- package/dist/api/geminiClient.js +341 -0
- package/dist/api/geminiClient.js.map +1 -0
- package/dist/api/geminiTextClient.d.ts +44 -0
- package/dist/api/geminiTextClient.d.ts.map +1 -0
- package/dist/api/geminiTextClient.js +202 -0
- package/dist/api/geminiTextClient.js.map +1 -0
- package/dist/business/fileManager.d.ts +20 -0
- package/dist/business/fileManager.d.ts.map +1 -0
- package/dist/business/fileManager.js +76 -0
- package/dist/business/fileManager.js.map +1 -0
- package/dist/business/inputValidator.d.ts +44 -0
- package/dist/business/inputValidator.d.ts.map +1 -0
- package/dist/business/inputValidator.js +213 -0
- package/dist/business/inputValidator.js.map +1 -0
- package/dist/business/responseBuilder.d.ts +21 -0
- package/dist/business/responseBuilder.d.ts.map +1 -0
- package/dist/business/responseBuilder.js +166 -0
- package/dist/business/responseBuilder.js.map +1 -0
- package/dist/business/structuredPromptGenerator.d.ts +56 -0
- package/dist/business/structuredPromptGenerator.d.ts.map +1 -0
- package/dist/business/structuredPromptGenerator.js +218 -0
- package/dist/business/structuredPromptGenerator.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/server/errorHandler.d.ts +29 -0
- package/dist/server/errorHandler.d.ts.map +1 -0
- package/dist/server/errorHandler.js +99 -0
- package/dist/server/errorHandler.js.map +1 -0
- package/dist/server/mcpServer.d.ts +159 -0
- package/dist/server/mcpServer.d.ts.map +1 -0
- package/dist/server/mcpServer.js +434 -0
- package/dist/server/mcpServer.js.map +1 -0
- package/dist/server-main.d.ts +5 -0
- package/dist/server-main.d.ts.map +1 -0
- package/dist/server-main.js +37 -0
- package/dist/server-main.js.map +1 -0
- package/dist/types/mcp.d.ts +121 -0
- package/dist/types/mcp.d.ts.map +1 -0
- package/dist/types/mcp.js +22 -0
- package/dist/types/mcp.js.map +1 -0
- package/dist/types/result.d.ts +27 -0
- package/dist/types/result.d.ts.map +1 -0
- package/dist/types/result.js +27 -0
- package/dist/types/result.js.map +1 -0
- package/dist/utils/config.d.ts +29 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +56 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/errors.d.ts +84 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +215 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +80 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +186 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/security.d.ts +50 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +116 -0
- package/dist/utils/security.js.map +1 -0
- package/package.json +89 -0
- package/skills/image-generation/SKILL.md +131 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger utility for structured logging with sensitive data filtering
|
|
3
|
+
* Provides consistent logging format across the application
|
|
4
|
+
*/
|
|
5
|
+
import * as crypto from 'node:crypto';
|
|
6
|
+
/**
|
|
7
|
+
* Logger class for structured logging with sensitive data protection
|
|
8
|
+
*/
|
|
9
|
+
export class Logger {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.sensitivePatterns = [
|
|
12
|
+
/GEMINI_API_KEY=([^\s]+)/gi,
|
|
13
|
+
/api[_-]?key[^\s]*[:=]\s*([^\s]+)/gi,
|
|
14
|
+
/password[^\s]*[:=]\s*([^\s]+)/gi,
|
|
15
|
+
/bearer\s+([a-zA-Z0-9\-._~+/]+=*)/gi,
|
|
16
|
+
/secret[^\s]*[:=]\s*([^\s]+)/gi,
|
|
17
|
+
/token[^\s]*[:=]\s*([^\s]+)/gi,
|
|
18
|
+
];
|
|
19
|
+
this.urlPatterns = [
|
|
20
|
+
/(https?:\/\/[^\s]+)/gi, // URLs - separate to handle differently
|
|
21
|
+
];
|
|
22
|
+
this.filterPatterns = [
|
|
23
|
+
/\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g, // Credit card numbers
|
|
24
|
+
/\b\d{3}-\d{2}-\d{4}\b/g, // SSN
|
|
25
|
+
/\b(?:\+?1[-.]?)?\(?\d{3}\)?[-.]?\d{3}[-.]?\d{4}\b/g, // Phone numbers
|
|
26
|
+
/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/gi, // Emails
|
|
27
|
+
];
|
|
28
|
+
this.keyBasedSensitivePatterns = [
|
|
29
|
+
/api[_-]?key/i,
|
|
30
|
+
/gemini[_-]?api[_-]?key/i,
|
|
31
|
+
/secret/i,
|
|
32
|
+
/password/i,
|
|
33
|
+
/token/i,
|
|
34
|
+
/credential/i,
|
|
35
|
+
/bearer/i,
|
|
36
|
+
];
|
|
37
|
+
// Initialize session ID once per logger instance
|
|
38
|
+
this.currentSessionId = this.generateId();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Log a debug message (only in development mode)
|
|
42
|
+
* @param context Context or module where the log originates
|
|
43
|
+
* @param message Log message
|
|
44
|
+
* @param metadata Optional metadata object
|
|
45
|
+
*/
|
|
46
|
+
debug(context, message, metadata) {
|
|
47
|
+
if (process.env['NODE_ENV'] === 'production')
|
|
48
|
+
return;
|
|
49
|
+
this.writeLog('debug', context, message, metadata);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Log an info message
|
|
53
|
+
* @param context Context or module where the log originates
|
|
54
|
+
* @param message Log message
|
|
55
|
+
* @param metadata Optional metadata object
|
|
56
|
+
*/
|
|
57
|
+
info(context, message, metadata) {
|
|
58
|
+
this.writeLog('info', context, message, metadata);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Log a warning message
|
|
62
|
+
* @param context Context or module where the log originates
|
|
63
|
+
* @param message Log message
|
|
64
|
+
* @param metadata Optional metadata object
|
|
65
|
+
*/
|
|
66
|
+
warn(context, message, metadata) {
|
|
67
|
+
this.writeLog('warn', context, message, metadata);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Log an error message
|
|
71
|
+
* @param context Context or module where the log originates
|
|
72
|
+
* @param message Log message
|
|
73
|
+
* @param error Optional error object
|
|
74
|
+
* @param metadata Optional metadata object
|
|
75
|
+
*/
|
|
76
|
+
error(context, message, error, metadata) {
|
|
77
|
+
const enhancedMetadata = {
|
|
78
|
+
...metadata,
|
|
79
|
+
...(error && {
|
|
80
|
+
errorName: error.name,
|
|
81
|
+
errorMessage: this.sanitizeString(error.message),
|
|
82
|
+
errorStack: process.env['NODE_ENV'] !== 'production' ? error.stack : undefined,
|
|
83
|
+
}),
|
|
84
|
+
};
|
|
85
|
+
this.writeLog('error', context, message, enhancedMetadata);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Core log writing method with structured format
|
|
89
|
+
*/
|
|
90
|
+
writeLog(level, context, message, metadata) {
|
|
91
|
+
const logEntry = {
|
|
92
|
+
timestamp: new Date().toISOString(),
|
|
93
|
+
level,
|
|
94
|
+
context,
|
|
95
|
+
message: this.sanitizeString(message),
|
|
96
|
+
...(metadata && { metadata: this.sanitizeMetadata(metadata) }),
|
|
97
|
+
traceId: this.getCurrentTraceId(),
|
|
98
|
+
sessionId: this.getCurrentSessionId(),
|
|
99
|
+
};
|
|
100
|
+
// JSON format structured log output
|
|
101
|
+
const logOutput = JSON.stringify(logEntry);
|
|
102
|
+
// For MCP servers, ALL logs must go to stderr
|
|
103
|
+
// stdout is reserved for JSON-RPC messages only
|
|
104
|
+
console.error(logOutput);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Sanitize string content by redacting sensitive information
|
|
108
|
+
* @param input String to sanitize
|
|
109
|
+
* @returns Sanitized string
|
|
110
|
+
*/
|
|
111
|
+
sanitizeString(input) {
|
|
112
|
+
let sanitized = input;
|
|
113
|
+
// Redact sensitive data patterns (API keys, passwords, etc.)
|
|
114
|
+
for (const pattern of this.sensitivePatterns) {
|
|
115
|
+
sanitized = sanitized.replace(pattern, (match, group1) => match.replace(group1, '[REDACTED]'));
|
|
116
|
+
}
|
|
117
|
+
// Additional broad filter for API key-like strings in text
|
|
118
|
+
// Remove any reference to API key terms even in plain text
|
|
119
|
+
sanitized = sanitized.replace(/\bapi[_-]?key\b/gi, '[REDACTED]');
|
|
120
|
+
sanitized = sanitized.replace(/\bgemini[_-]?api[_-]?key\b/gi, '[REDACTED]');
|
|
121
|
+
// Remove long alphanumeric strings that might be API keys or secrets
|
|
122
|
+
sanitized = sanitized.replace(/\b[A-Za-z0-9]{20,}\b/g, '[REDACTED]');
|
|
123
|
+
// Redact URLs with specific label
|
|
124
|
+
for (const pattern of this.urlPatterns) {
|
|
125
|
+
sanitized = sanitized.replace(pattern, '[URL_REDACTED]');
|
|
126
|
+
}
|
|
127
|
+
// Filter personal information patterns
|
|
128
|
+
for (const pattern of this.filterPatterns) {
|
|
129
|
+
sanitized = sanitized.replace(pattern, '[FILTERED]');
|
|
130
|
+
}
|
|
131
|
+
return sanitized;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Sanitize metadata by redacting sensitive information
|
|
135
|
+
* @param metadata Metadata object to sanitize
|
|
136
|
+
* @returns Sanitized metadata object
|
|
137
|
+
*/
|
|
138
|
+
sanitizeMetadata(metadata) {
|
|
139
|
+
const sanitized = {};
|
|
140
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
141
|
+
if (this.isSensitiveKey(key)) {
|
|
142
|
+
sanitized[key] = '[REDACTED]';
|
|
143
|
+
}
|
|
144
|
+
else if (typeof value === 'string') {
|
|
145
|
+
sanitized[key] = this.sanitizeString(value);
|
|
146
|
+
}
|
|
147
|
+
else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
148
|
+
sanitized[key] = this.sanitizeMetadata(value);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
sanitized[key] = value;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return sanitized;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Check if a key contains sensitive information
|
|
158
|
+
* @param key Object key to check
|
|
159
|
+
* @returns True if the key contains sensitive information
|
|
160
|
+
*/
|
|
161
|
+
isSensitiveKey(key) {
|
|
162
|
+
return this.keyBasedSensitivePatterns.some((pattern) => pattern.test(key));
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Generate unique ID for trace/session tracking
|
|
166
|
+
*/
|
|
167
|
+
generateId() {
|
|
168
|
+
return crypto.randomUUID().substring(0, 8);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get or generate current trace ID
|
|
172
|
+
*/
|
|
173
|
+
getCurrentTraceId() {
|
|
174
|
+
if (!this.currentTraceId) {
|
|
175
|
+
this.currentTraceId = this.generateId();
|
|
176
|
+
}
|
|
177
|
+
return this.currentTraceId;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get current session ID
|
|
181
|
+
*/
|
|
182
|
+
getCurrentSessionId() {
|
|
183
|
+
return this.currentSessionId;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Manager for file path validation and sanitization
|
|
3
|
+
* Provides protection against path traversal, null byte injection, and other security threats
|
|
4
|
+
*/
|
|
5
|
+
import { type Result } from '../types/result.js';
|
|
6
|
+
import { SecurityError } from './errors.js';
|
|
7
|
+
/**
|
|
8
|
+
* Security manager for handling file path validation and sanitization
|
|
9
|
+
*/
|
|
10
|
+
export declare class SecurityManager {
|
|
11
|
+
private readonly allowedBasePaths;
|
|
12
|
+
/**
|
|
13
|
+
* Sanitize and validate file path for security
|
|
14
|
+
* @param inputPath File path to sanitize
|
|
15
|
+
* @returns Result containing sanitized path or security error
|
|
16
|
+
*/
|
|
17
|
+
sanitizeFilePath(inputPath: string): Result<string, SecurityError>;
|
|
18
|
+
/**
|
|
19
|
+
* Validate image file extension
|
|
20
|
+
* @param filePath File path to validate
|
|
21
|
+
* @returns Result indicating validation success or security error
|
|
22
|
+
*/
|
|
23
|
+
validateImageFile(filePath: string): Result<void, SecurityError>;
|
|
24
|
+
/**
|
|
25
|
+
* Validate directory path for security
|
|
26
|
+
* @param dirPath Directory path to validate
|
|
27
|
+
* @returns Result indicating validation success or security error
|
|
28
|
+
*/
|
|
29
|
+
validateDirectoryPath(dirPath: string): Result<void, SecurityError>;
|
|
30
|
+
/**
|
|
31
|
+
* Generate secure temporary file path
|
|
32
|
+
* @param baseName Base name for the temporary file
|
|
33
|
+
* @param extension File extension (with dot)
|
|
34
|
+
* @returns Secure temporary file path
|
|
35
|
+
*/
|
|
36
|
+
generateSecureTempPath(baseName: string, extension: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Check if a path is within allowed directories
|
|
39
|
+
* @param targetPath Path to check
|
|
40
|
+
* @returns True if path is within allowed directories
|
|
41
|
+
*/
|
|
42
|
+
isPathAllowed(targetPath: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Sanitize filename by removing dangerous characters
|
|
45
|
+
* @param filename Filename to sanitize
|
|
46
|
+
* @returns Sanitized filename
|
|
47
|
+
*/
|
|
48
|
+
sanitizeFilename(filename: string): string;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/utils/security.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAMhC;IAED;;;;OAIG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC;IAwBlE;;;;OAIG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC;IAYhE;;;;OAIG;IACH,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC;IAUnE;;;;;OAKG;IACH,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAQnE;;;;OAIG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAK1C;;;;OAIG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;CAuB3C"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Manager for file path validation and sanitization
|
|
3
|
+
* Provides protection against path traversal, null byte injection, and other security threats
|
|
4
|
+
*/
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import { Err, Ok } from '../types/result.js';
|
|
7
|
+
import { SecurityError } from './errors.js';
|
|
8
|
+
/**
|
|
9
|
+
* Security manager for handling file path validation and sanitization
|
|
10
|
+
*/
|
|
11
|
+
export class SecurityManager {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.allowedBasePaths = [
|
|
14
|
+
process.cwd(),
|
|
15
|
+
path.resolve(process.env['IMAGE_OUTPUT_DIR'] || './output'),
|
|
16
|
+
path.resolve('./temp'),
|
|
17
|
+
path.resolve('./tmp'),
|
|
18
|
+
'/tmp',
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Sanitize and validate file path for security
|
|
23
|
+
* @param inputPath File path to sanitize
|
|
24
|
+
* @returns Result containing sanitized path or security error
|
|
25
|
+
*/
|
|
26
|
+
sanitizeFilePath(inputPath) {
|
|
27
|
+
// Null byte attack prevention
|
|
28
|
+
if (inputPath.includes('\0')) {
|
|
29
|
+
return Err(new SecurityError('Null byte detected in file path'));
|
|
30
|
+
}
|
|
31
|
+
// Path traversal attack prevention
|
|
32
|
+
if (inputPath.includes('..')) {
|
|
33
|
+
return Err(new SecurityError('Path traversal attempt detected'));
|
|
34
|
+
}
|
|
35
|
+
// Resolve and validate absolute path
|
|
36
|
+
const resolvedPath = path.resolve(inputPath);
|
|
37
|
+
const isAllowed = this.allowedBasePaths.some((basePath) => resolvedPath.startsWith(path.resolve(basePath)));
|
|
38
|
+
if (!isAllowed) {
|
|
39
|
+
return Err(new SecurityError('File path outside allowed directories'));
|
|
40
|
+
}
|
|
41
|
+
return Ok(resolvedPath);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Validate image file extension
|
|
45
|
+
* @param filePath File path to validate
|
|
46
|
+
* @returns Result indicating validation success or security error
|
|
47
|
+
*/
|
|
48
|
+
validateImageFile(filePath) {
|
|
49
|
+
// Allowed image file extensions
|
|
50
|
+
const allowedExtensions = ['.png', '.jpg', '.jpeg', '.webp'];
|
|
51
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
52
|
+
if (!allowedExtensions.includes(extension)) {
|
|
53
|
+
return Err(new SecurityError(`Unsupported file extension: ${extension}`));
|
|
54
|
+
}
|
|
55
|
+
return Ok(undefined);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Validate directory path for security
|
|
59
|
+
* @param dirPath Directory path to validate
|
|
60
|
+
* @returns Result indicating validation success or security error
|
|
61
|
+
*/
|
|
62
|
+
validateDirectoryPath(dirPath) {
|
|
63
|
+
// Use same security checks as file path validation
|
|
64
|
+
const pathValidation = this.sanitizeFilePath(dirPath);
|
|
65
|
+
if (!pathValidation.success) {
|
|
66
|
+
return pathValidation;
|
|
67
|
+
}
|
|
68
|
+
return Ok(undefined);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Generate secure temporary file path
|
|
72
|
+
* @param baseName Base name for the temporary file
|
|
73
|
+
* @param extension File extension (with dot)
|
|
74
|
+
* @returns Secure temporary file path
|
|
75
|
+
*/
|
|
76
|
+
generateSecureTempPath(baseName, extension) {
|
|
77
|
+
const timestamp = Date.now();
|
|
78
|
+
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
|
79
|
+
const secureFilename = `${baseName}-${timestamp}-${randomSuffix}${extension}`;
|
|
80
|
+
return path.join('/tmp', secureFilename);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if a path is within allowed directories
|
|
84
|
+
* @param targetPath Path to check
|
|
85
|
+
* @returns True if path is within allowed directories
|
|
86
|
+
*/
|
|
87
|
+
isPathAllowed(targetPath) {
|
|
88
|
+
const resolvedPath = path.resolve(targetPath);
|
|
89
|
+
return this.allowedBasePaths.some((basePath) => resolvedPath.startsWith(path.resolve(basePath)));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Sanitize filename by removing dangerous characters
|
|
93
|
+
* @param filename Filename to sanitize
|
|
94
|
+
* @returns Sanitized filename
|
|
95
|
+
*/
|
|
96
|
+
sanitizeFilename(filename) {
|
|
97
|
+
// Remove null bytes and path separators
|
|
98
|
+
let sanitized = filename.replace(/[\0/\\]/g, '');
|
|
99
|
+
// Remove control characters (ASCII 0-31 and 127) by filtering each character
|
|
100
|
+
sanitized = sanitized
|
|
101
|
+
.split('')
|
|
102
|
+
.filter((char) => {
|
|
103
|
+
const code = char.charCodeAt(0);
|
|
104
|
+
return code > 31 && code !== 127;
|
|
105
|
+
})
|
|
106
|
+
.join('');
|
|
107
|
+
// Trim whitespace and dots (to prevent hidden files and relative paths)
|
|
108
|
+
sanitized = sanitized.replace(/^\.+|\.+$/g, '').trim();
|
|
109
|
+
// Ensure filename is not empty after sanitization
|
|
110
|
+
if (sanitized.length === 0) {
|
|
111
|
+
sanitized = `secure-file-${Date.now()}`;
|
|
112
|
+
}
|
|
113
|
+
return sanitized;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/utils/security.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-hydrocoder-image",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for AI image generation with multi-image support",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mcp-hydrocoder-image": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"mcp",
|
|
12
|
+
"mcp-server",
|
|
13
|
+
"model-context-protocol",
|
|
14
|
+
"image-generation",
|
|
15
|
+
"generative-ai",
|
|
16
|
+
"image-editing",
|
|
17
|
+
"prompt-enhancement",
|
|
18
|
+
"gemini",
|
|
19
|
+
"google-ai",
|
|
20
|
+
"nano-banana",
|
|
21
|
+
"claude-code",
|
|
22
|
+
"cursor",
|
|
23
|
+
"codex",
|
|
24
|
+
"typescript",
|
|
25
|
+
"agent-skills",
|
|
26
|
+
"skills"
|
|
27
|
+
],
|
|
28
|
+
"author": "Shinsuke Kagawa",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/ynzys/mcp-banana.git"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist/**/*",
|
|
36
|
+
"!dist/**/*.test.js",
|
|
37
|
+
"!dist/**/*.test.d.ts",
|
|
38
|
+
"!dist/__tests__",
|
|
39
|
+
"!dist/tests",
|
|
40
|
+
"bin",
|
|
41
|
+
"skills"
|
|
42
|
+
],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc && tsc-alias",
|
|
45
|
+
"link": "npm run build && npm link",
|
|
46
|
+
"test": "vitest run",
|
|
47
|
+
"test:coverage": "vitest run --coverage",
|
|
48
|
+
"test:coverage:summary": "node scripts/show-coverage.js",
|
|
49
|
+
"test:coverage:clean": "rm -rf coverage .vitest-cache",
|
|
50
|
+
"test:coverage:fresh": "npm run test:coverage:clean && npm run test:coverage",
|
|
51
|
+
"test:watch": "vitest",
|
|
52
|
+
"format": "biome format --write src",
|
|
53
|
+
"format:check": "biome format src",
|
|
54
|
+
"lint": "biome lint src",
|
|
55
|
+
"lint:fix": "biome lint --write src",
|
|
56
|
+
"check": "biome check src",
|
|
57
|
+
"check:fix": "biome check --write src",
|
|
58
|
+
"check:deps": "madge --circular --extensions ts src",
|
|
59
|
+
"check:all": "npm run check && npm run lint && npm run format:check && npm run check:deps && npm run build && npm run test",
|
|
60
|
+
"cleanup:processes": "bash ./scripts/cleanup-test-processes.sh",
|
|
61
|
+
"test:safe": "npm test && npm run cleanup:processes"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"@google/genai": "^1.42.0",
|
|
65
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@biomejs/biome": "^2.4.6",
|
|
69
|
+
"@types/node": "^25.3.5",
|
|
70
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
71
|
+
"@vitest/ui": "^4.0.18",
|
|
72
|
+
"husky": "^9.1.7",
|
|
73
|
+
"lint-staged": "^16.3.2",
|
|
74
|
+
"madge": "^8.0.0",
|
|
75
|
+
"tsc-alias": "^1.8.16",
|
|
76
|
+
"tsx": "^4.21.0",
|
|
77
|
+
"typescript": "^5.9.3",
|
|
78
|
+
"vitest": "^4.0.18"
|
|
79
|
+
},
|
|
80
|
+
"engines": {
|
|
81
|
+
"node": ">=20"
|
|
82
|
+
},
|
|
83
|
+
"lint-staged": {
|
|
84
|
+
"src/**/*.{ts,tsx}": [
|
|
85
|
+
"biome check --write --no-errors-on-unmatched",
|
|
86
|
+
"biome format --write --no-errors-on-unmatched"
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: image-generation
|
|
3
|
+
description: Enhances image generation prompts using Subject-Context-Style structure and best practices. Use this skill when generating images, creating illustrations, photos, visual assets, editing images, or crafting prompts for image generation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Image Generation Prompt Best Practices
|
|
7
|
+
|
|
8
|
+
## Prompt Structure
|
|
9
|
+
|
|
10
|
+
Enhance every image generation prompt around three core elements:
|
|
11
|
+
|
|
12
|
+
### 1. SUBJECT (What)
|
|
13
|
+
|
|
14
|
+
The main focus of the image.
|
|
15
|
+
|
|
16
|
+
- Physical characteristics: textures, materials, colors, scale
|
|
17
|
+
- Actions, poses, expressions if applicable
|
|
18
|
+
- Distinctive features that define the subject
|
|
19
|
+
|
|
20
|
+
### 2. CONTEXT (Where/When)
|
|
21
|
+
|
|
22
|
+
The environment and conditions.
|
|
23
|
+
|
|
24
|
+
- Setting, background, spatial relationships (foreground, midground, background)
|
|
25
|
+
- Time of day, weather, atmospheric conditions
|
|
26
|
+
- Mood and emotional tone of the scene
|
|
27
|
+
|
|
28
|
+
### 3. STYLE (How)
|
|
29
|
+
|
|
30
|
+
The visual treatment.
|
|
31
|
+
|
|
32
|
+
- Artistic or photographic approach: reference specific artists, movements, or styles
|
|
33
|
+
- Lighting design: direction, quality, color temperature, shadows
|
|
34
|
+
- Camera/lens choices: specify focal length, aperture, and shooting angle when photographic
|
|
35
|
+
|
|
36
|
+
## Core Principles
|
|
37
|
+
|
|
38
|
+
- **Preserve intent** — Enrich the user's original vision, never override it
|
|
39
|
+
- **Positive descriptions only** — Describe what should be present; rephrase any exclusion as an inclusion
|
|
40
|
+
- **Specific over vague** — "golden hour sunlight at 15° angle" beats "nice lighting"
|
|
41
|
+
- **Natural flow** — Weave elements into a single flowing description, not a bullet list
|
|
42
|
+
|
|
43
|
+
## Enhancement Patterns
|
|
44
|
+
|
|
45
|
+
### Hyper-Specific Details
|
|
46
|
+
|
|
47
|
+
Add concrete visual details where the user left gaps:
|
|
48
|
+
|
|
49
|
+
- Lighting → direction, quality, color temperature, shadow behavior
|
|
50
|
+
- Textures → surface materials, weathering, reflectivity
|
|
51
|
+
- Atmosphere → particulates, humidity, depth haze
|
|
52
|
+
- Scale → relative sizes, distances, proportions
|
|
53
|
+
|
|
54
|
+
### Camera Control Terminology
|
|
55
|
+
|
|
56
|
+
When a photographic look is appropriate:
|
|
57
|
+
|
|
58
|
+
- Lens type: "shot with 85mm portrait lens", "wide-angle 24mm"
|
|
59
|
+
- Aperture: "shallow depth of field at f/1.8", "deep focus at f/11"
|
|
60
|
+
- Angle: "low angle emphasizing height", "bird's eye view"
|
|
61
|
+
- Motion: "motion blur on the paws", "frozen mid-action"
|
|
62
|
+
|
|
63
|
+
### Atmospheric Enhancement
|
|
64
|
+
|
|
65
|
+
Convey mood through environmental details:
|
|
66
|
+
|
|
67
|
+
- Emotional tone: "serene", "ominous", "jubilant"
|
|
68
|
+
- Light quality: "dappled shadows", "harsh midday sun", "soft diffused overcast"
|
|
69
|
+
- Weather/air: "morning mist", "dust particles in a sunbeam"
|
|
70
|
+
|
|
71
|
+
### Text in Images
|
|
72
|
+
|
|
73
|
+
When the image should contain readable text (signs, labels, titles, typography):
|
|
74
|
+
|
|
75
|
+
- Specify the exact text content in quotes: `"OPEN 24 HOURS" in bold sans-serif`
|
|
76
|
+
- Describe visual treatment: font style, weight, size relative to the scene
|
|
77
|
+
- Define placement and integration: "centered on the storefront awning", "hand-lettered on the chalkboard"
|
|
78
|
+
|
|
79
|
+
## Feature Patterns
|
|
80
|
+
|
|
81
|
+
### Character Consistency
|
|
82
|
+
|
|
83
|
+
When the same character must be recognizable across multiple images:
|
|
84
|
+
|
|
85
|
+
- Include **at least 3 recognizable visual markers** (distinctive scar, signature clothing, unique hairstyle, characteristic accessory)
|
|
86
|
+
- Use anchoring words: "distinctive", "signature", "always wears", "always has"
|
|
87
|
+
- Be specific: "round tortoiseshell glasses" not just "glasses"
|
|
88
|
+
|
|
89
|
+
### Compositional Integration (Multi-Element Blending)
|
|
90
|
+
|
|
91
|
+
When combining multiple visual elements in one scene:
|
|
92
|
+
|
|
93
|
+
- Define spatial relationships with proportions: "foreground (40% of frame)", "midground", "background"
|
|
94
|
+
- Use integration language: "seamlessly blending", "harmoniously composed", "naturally integrated"
|
|
95
|
+
- Specify relative scale and interaction between elements
|
|
96
|
+
|
|
97
|
+
### Real-World Accuracy
|
|
98
|
+
|
|
99
|
+
When depicting real places, cultures, or historical elements:
|
|
100
|
+
|
|
101
|
+
- Use specific terminology: "traditional Edo-period architecture", "authentic Moroccan zellige tilework"
|
|
102
|
+
- Include culturally accurate details
|
|
103
|
+
- Reference geographical or historical specifics
|
|
104
|
+
|
|
105
|
+
### Purpose-Driven Enhancement
|
|
106
|
+
|
|
107
|
+
Tailor the prompt to the intended use:
|
|
108
|
+
|
|
109
|
+
| Purpose | Emphasis |
|
|
110
|
+
|---------|----------|
|
|
111
|
+
| Product photo | Clean background, studio lighting, commercial appeal |
|
|
112
|
+
| UI mockup | Flat design elements, consistent spacing, screen-appropriate |
|
|
113
|
+
| Presentation slide | Bold composition, clear focal point, text-friendly layout |
|
|
114
|
+
| Social media | Eye-catching, vibrant, crop-friendly aspect ratio |
|
|
115
|
+
| Book/album cover | Typography space, dramatic mood, symbolic elements |
|
|
116
|
+
|
|
117
|
+
## Image Editing
|
|
118
|
+
|
|
119
|
+
When modifying an existing image:
|
|
120
|
+
|
|
121
|
+
- **Preserve** the original's core characteristics: color palette, lighting style, composition
|
|
122
|
+
- Use anchoring phrases: "maintain the existing...", "preserve the original...", "keep the same..."
|
|
123
|
+
- Be specific about what to change vs what to keep unchanged
|
|
124
|
+
- Describe modifications relative to the existing image, not from scratch
|
|
125
|
+
|
|
126
|
+
## Example
|
|
127
|
+
|
|
128
|
+
**Input:** "A happy dog in a park"
|
|
129
|
+
|
|
130
|
+
**Enhanced:** "Golden retriever mid-leap catching a red frisbee, ears flying, tongue out in joy, in a sunlit urban park. Soft morning light filtering through oak trees creates dappled shadows on emerald grass. Background shows families on picnic blankets, slightly out of focus. Shot from low angle emphasizing the dog's athletic movement, with motion blur on the paws suggesting speed."
|
|
131
|
+
|