mcp-image 0.1.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 +163 -0
- package/dist/api/geminiClient.d.ts +55 -0
- package/dist/api/geminiClient.d.ts.map +1 -0
- package/dist/api/geminiClient.js +194 -0
- package/dist/api/geminiClient.js.map +1 -0
- package/dist/api/urlContextClient.d.ts +149 -0
- package/dist/api/urlContextClient.d.ts.map +1 -0
- package/dist/api/urlContextClient.js +320 -0
- package/dist/api/urlContextClient.js.map +1 -0
- package/dist/business/errorHandler.d.ts +97 -0
- package/dist/business/errorHandler.d.ts.map +1 -0
- package/dist/business/errorHandler.js +511 -0
- package/dist/business/errorHandler.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 +112 -0
- package/dist/business/fileManager.js.map +1 -0
- package/dist/business/imageGenerator.d.ts +64 -0
- package/dist/business/imageGenerator.d.ts.map +1 -0
- package/dist/business/imageGenerator.js +147 -0
- package/dist/business/imageGenerator.js.map +1 -0
- package/dist/business/imageGeneratorRobust.d.ts +60 -0
- package/dist/business/imageGeneratorRobust.d.ts.map +1 -0
- package/dist/business/imageGeneratorRobust.js +242 -0
- package/dist/business/imageGeneratorRobust.js.map +1 -0
- package/dist/business/inputValidator.d.ts +29 -0
- package/dist/business/inputValidator.d.ts.map +1 -0
- package/dist/business/inputValidator.js +132 -0
- package/dist/business/inputValidator.js.map +1 -0
- package/dist/business/performanceManager.d.ts +88 -0
- package/dist/business/performanceManager.d.ts.map +1 -0
- package/dist/business/performanceManager.js +142 -0
- package/dist/business/performanceManager.js.map +1 -0
- package/dist/business/responseBuilder.d.ts +20 -0
- package/dist/business/responseBuilder.d.ts.map +1 -0
- package/dist/business/responseBuilder.js +162 -0
- package/dist/business/responseBuilder.js.map +1 -0
- package/dist/business/secureFileManager.d.ts +56 -0
- package/dist/business/secureFileManager.d.ts.map +1 -0
- package/dist/business/secureFileManager.js +185 -0
- package/dist/business/secureFileManager.js.map +1 -0
- package/dist/business/urlExtractor.d.ts +60 -0
- package/dist/business/urlExtractor.d.ts.map +1 -0
- package/dist/business/urlExtractor.js +144 -0
- package/dist/business/urlExtractor.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/server/concurrencyManager.d.ts +56 -0
- package/dist/server/concurrencyManager.d.ts.map +1 -0
- package/dist/server/concurrencyManager.js +133 -0
- package/dist/server/concurrencyManager.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 +93 -0
- package/dist/server/errorHandler.js.map +1 -0
- package/dist/server/mcpServer.d.ts +111 -0
- package/dist/server/mcpServer.d.ts.map +1 -0
- package/dist/server/mcpServer.js +353 -0
- package/dist/server/mcpServer.js.map +1 -0
- package/dist/types/mcp.d.ts +72 -0
- package/dist/types/mcp.d.ts.map +1 -0
- package/dist/types/mcp.js +7 -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 +31 -0
- package/dist/types/result.js.map +1 -0
- package/dist/utils/config.d.ts +26 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +53 -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 +218 -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 +223 -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 +153 -0
- package/dist/utils/security.js.map +1 -0
- package/package.json +73 -0
- package/vitest.config.mjs +47 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure File Manager for handling image file operations with enhanced security
|
|
3
|
+
* Extends FileManager with security features, temporary file management, and cleanup
|
|
4
|
+
*/
|
|
5
|
+
import { promises as fs } from 'node:fs';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import { Err, Ok } from '../types/result';
|
|
8
|
+
import { FileOperationError } from '../utils/errors';
|
|
9
|
+
import { Logger } from '../utils/logger';
|
|
10
|
+
import { SecurityManager } from '../utils/security';
|
|
11
|
+
import { FileManager } from './fileManager';
|
|
12
|
+
/**
|
|
13
|
+
* Secure file manager with enhanced security features and temporary file management
|
|
14
|
+
*/
|
|
15
|
+
export class SecureFileManager extends FileManager {
|
|
16
|
+
constructor() {
|
|
17
|
+
super(...arguments);
|
|
18
|
+
this.tempFiles = new Set();
|
|
19
|
+
this.securityManager = new SecurityManager();
|
|
20
|
+
this.logger = new Logger();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Securely save image data with comprehensive security checks
|
|
24
|
+
* @param imageData Buffer containing the image data
|
|
25
|
+
* @param outputPath Path where the image should be saved
|
|
26
|
+
* @param format Image format for validation (optional)
|
|
27
|
+
* @returns Result containing the saved file path or an error
|
|
28
|
+
*/
|
|
29
|
+
async saveImageSecure(imageData, outputPath, format) {
|
|
30
|
+
try {
|
|
31
|
+
// Security validation
|
|
32
|
+
const sanitizedPath = this.securityManager.sanitizeFilePath(outputPath);
|
|
33
|
+
if (!sanitizedPath.success) {
|
|
34
|
+
return sanitizedPath;
|
|
35
|
+
}
|
|
36
|
+
const validationResult = this.securityManager.validateImageFile(sanitizedPath.data);
|
|
37
|
+
if (!validationResult.success) {
|
|
38
|
+
return validationResult;
|
|
39
|
+
}
|
|
40
|
+
// Ensure secure directory creation
|
|
41
|
+
const dirPath = path.dirname(sanitizedPath.data);
|
|
42
|
+
const dirResult = await this.ensureSecureDirectory(dirPath);
|
|
43
|
+
if (!dirResult.success) {
|
|
44
|
+
return dirResult;
|
|
45
|
+
}
|
|
46
|
+
// Atomic file operation using temporary file
|
|
47
|
+
const tempPath = `${sanitizedPath.data}.tmp`;
|
|
48
|
+
this.tempFiles.add(tempPath);
|
|
49
|
+
this.logger.debug('secure-file-manager', 'Starting atomic file save', {
|
|
50
|
+
outputPath: sanitizedPath.data,
|
|
51
|
+
tempPath,
|
|
52
|
+
fileSize: imageData.length,
|
|
53
|
+
format,
|
|
54
|
+
});
|
|
55
|
+
// Write to temporary file first
|
|
56
|
+
await fs.writeFile(tempPath, imageData);
|
|
57
|
+
// Atomic move to final destination
|
|
58
|
+
await fs.rename(tempPath, sanitizedPath.data);
|
|
59
|
+
this.tempFiles.delete(tempPath);
|
|
60
|
+
this.logger.info('secure-file-manager', 'Image saved successfully', {
|
|
61
|
+
outputPath: sanitizedPath.data,
|
|
62
|
+
fileSize: imageData.length,
|
|
63
|
+
format,
|
|
64
|
+
securityChecks: 'passed',
|
|
65
|
+
});
|
|
66
|
+
return Ok(sanitizedPath.data);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
70
|
+
this.logger.error('secure-file-manager', 'Failed to save image securely', error, {
|
|
71
|
+
outputPath,
|
|
72
|
+
format,
|
|
73
|
+
errorType: 'file-operation',
|
|
74
|
+
});
|
|
75
|
+
return Err(new FileOperationError(`Failed to save image: ${errorMessage}`));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Ensure secure directory creation with security validation
|
|
80
|
+
* @param dirPath Directory path to create
|
|
81
|
+
* @returns Result indicating success or failure
|
|
82
|
+
*/
|
|
83
|
+
async ensureSecureDirectory(dirPath) {
|
|
84
|
+
try {
|
|
85
|
+
// Validate directory path security
|
|
86
|
+
const validationResult = this.securityManager.validateDirectoryPath(dirPath);
|
|
87
|
+
if (!validationResult.success) {
|
|
88
|
+
return validationResult;
|
|
89
|
+
}
|
|
90
|
+
// Use parent class method for actual directory creation
|
|
91
|
+
const createResult = this.ensureDirectoryExists(dirPath);
|
|
92
|
+
if (!createResult.success) {
|
|
93
|
+
return createResult;
|
|
94
|
+
}
|
|
95
|
+
this.logger.debug('secure-file-manager', 'Secure directory created', {
|
|
96
|
+
directoryPath: dirPath,
|
|
97
|
+
});
|
|
98
|
+
return Ok(undefined);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
102
|
+
this.logger.error('secure-file-manager', 'Failed to create secure directory', error, {
|
|
103
|
+
directoryPath: dirPath,
|
|
104
|
+
});
|
|
105
|
+
return Err(new FileOperationError(`Failed to create secure directory: ${errorMessage}`));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Cleanup all tracked temporary files
|
|
110
|
+
*/
|
|
111
|
+
async cleanup() {
|
|
112
|
+
const cleanupPromises = Array.from(this.tempFiles).map(async (tempFile) => {
|
|
113
|
+
try {
|
|
114
|
+
await fs.unlink(tempFile);
|
|
115
|
+
this.tempFiles.delete(tempFile);
|
|
116
|
+
this.logger.debug('secure-file-manager', 'Temporary file cleaned up', {
|
|
117
|
+
file: tempFile,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
this.logger.warn('secure-file-manager', 'Failed to cleanup temporary file', {
|
|
122
|
+
file: tempFile,
|
|
123
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
await Promise.all(cleanupPromises);
|
|
128
|
+
if (this.tempFiles.size > 0) {
|
|
129
|
+
this.logger.warn('secure-file-manager', 'Some temporary files could not be cleaned', {
|
|
130
|
+
remainingCount: this.tempFiles.size,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Setup process cleanup handlers for graceful shutdown
|
|
136
|
+
*/
|
|
137
|
+
setupProcessCleanup() {
|
|
138
|
+
const cleanup = () => {
|
|
139
|
+
this.logger.info('secure-file-manager', 'Process cleanup initiated');
|
|
140
|
+
this.cleanup().catch((error) => {
|
|
141
|
+
console.error('Failed to cleanup temporary files:', error);
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
// Setup cleanup on various process exit scenarios
|
|
145
|
+
process.on('exit', cleanup);
|
|
146
|
+
process.on('SIGINT', cleanup);
|
|
147
|
+
process.on('SIGTERM', cleanup);
|
|
148
|
+
process.on('uncaughtException', (error) => {
|
|
149
|
+
this.logger.error('secure-file-manager', 'Uncaught exception during cleanup', error);
|
|
150
|
+
cleanup();
|
|
151
|
+
});
|
|
152
|
+
this.logger.info('secure-file-manager', 'Process cleanup handlers registered');
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Generate secure filename with validation
|
|
156
|
+
* @param baseName Base name for the file
|
|
157
|
+
* @param extension File extension (with dot)
|
|
158
|
+
* @returns Secure filename
|
|
159
|
+
*/
|
|
160
|
+
generateSecureFileName(baseName, extension) {
|
|
161
|
+
const sanitizedBase = baseName
|
|
162
|
+
? this.securityManager.sanitizeFilename(baseName)
|
|
163
|
+
: 'gemini-image';
|
|
164
|
+
const safeExtension = extension || '.png';
|
|
165
|
+
const timestamp = Date.now();
|
|
166
|
+
const random = Math.floor(Math.random() * 1000);
|
|
167
|
+
return `${sanitizedBase}-${timestamp}-${random}${safeExtension}`;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get count of tracked temporary files
|
|
171
|
+
* @returns Number of temporary files being tracked
|
|
172
|
+
*/
|
|
173
|
+
getTempFileCount() {
|
|
174
|
+
return this.tempFiles.size;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Check if a specific temporary file is being tracked
|
|
178
|
+
* @param filePath Path to check
|
|
179
|
+
* @returns True if file is being tracked as temporary
|
|
180
|
+
*/
|
|
181
|
+
isTempFileTracked(filePath) {
|
|
182
|
+
return this.tempFiles.has(filePath);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=secureFileManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secureFileManager.js","sourceRoot":"","sources":["../../src/business/secureFileManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL extraction logic for processing URLs in prompts
|
|
3
|
+
* Provides functionality to extract, validate, and manipulate URLs in text
|
|
4
|
+
*/
|
|
5
|
+
import { type Result } from '../types/result';
|
|
6
|
+
import { InvalidUrlError } from '../utils/errors';
|
|
7
|
+
/**
|
|
8
|
+
* Extract URLs from a given prompt text
|
|
9
|
+
* @param prompt The text to extract URLs from
|
|
10
|
+
* @returns Array of unique URLs found in the prompt (max 10)
|
|
11
|
+
*/
|
|
12
|
+
export declare function extractUrls(prompt: string): string[];
|
|
13
|
+
/**
|
|
14
|
+
* Check if the prompt contains any URLs
|
|
15
|
+
* @param prompt The text to check for URLs
|
|
16
|
+
* @returns True if URLs are found, false otherwise
|
|
17
|
+
*/
|
|
18
|
+
export declare function hasUrls(prompt: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Remove all URLs from the prompt text
|
|
21
|
+
* @param prompt The text to remove URLs from
|
|
22
|
+
* @returns The prompt with URLs removed and extra spaces trimmed
|
|
23
|
+
*/
|
|
24
|
+
export declare function removeUrls(prompt: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Validate a single URL using built-in URL constructor
|
|
27
|
+
* @param url The URL string to validate
|
|
28
|
+
* @returns Result containing validation success or error
|
|
29
|
+
*/
|
|
30
|
+
export declare function validateUrl(url: string): Result<string, InvalidUrlError>;
|
|
31
|
+
/**
|
|
32
|
+
* Extract and validate URLs from prompt with detailed error reporting
|
|
33
|
+
* @param prompt The text to extract URLs from
|
|
34
|
+
* @returns Result containing valid URLs or validation error
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractValidUrls(prompt: string): Result<string[], InvalidUrlError>;
|
|
37
|
+
/**
|
|
38
|
+
* Get URL statistics for analysis
|
|
39
|
+
* @param prompt The text to analyze
|
|
40
|
+
* @returns Statistics about URLs in the prompt
|
|
41
|
+
*/
|
|
42
|
+
export declare function getUrlStats(prompt: string): {
|
|
43
|
+
totalFound: number;
|
|
44
|
+
validUrls: number;
|
|
45
|
+
httpUrls: number;
|
|
46
|
+
httpsUrls: number;
|
|
47
|
+
uniqueDomains: string[];
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* URL extractor utilities - backward compatibility object
|
|
51
|
+
*/
|
|
52
|
+
export declare const URLExtractor: {
|
|
53
|
+
extractUrls: typeof extractUrls;
|
|
54
|
+
hasUrls: typeof hasUrls;
|
|
55
|
+
removeUrls: typeof removeUrls;
|
|
56
|
+
validateUrl: typeof validateUrl;
|
|
57
|
+
extractValidUrls: typeof extractValidUrls;
|
|
58
|
+
getUrlStats: typeof getUrlStats;
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=urlExtractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"urlExtractor.d.ts","sourceRoot":"","sources":["../../src/business/urlExtractor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAcjD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CASpD;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CA4BxE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAuBlF;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG;IAC3C,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,EAAE,CAAA;CACxB,CAwBA;AAED;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;CAOxB,CAAA"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* URL extraction logic for processing URLs in prompts
|
|
4
|
+
* Provides functionality to extract, validate, and manipulate URLs in text
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.URLExtractor = void 0;
|
|
8
|
+
exports.extractUrls = extractUrls;
|
|
9
|
+
exports.hasUrls = hasUrls;
|
|
10
|
+
exports.removeUrls = removeUrls;
|
|
11
|
+
exports.validateUrl = validateUrl;
|
|
12
|
+
exports.extractValidUrls = extractValidUrls;
|
|
13
|
+
exports.getUrlStats = getUrlStats;
|
|
14
|
+
const result_1 = require("../types/result");
|
|
15
|
+
const errors_1 = require("../utils/errors");
|
|
16
|
+
/**
|
|
17
|
+
* Enhanced regular expression pattern for matching HTTP and HTTPS URLs
|
|
18
|
+
* Supports complex URLs with query parameters, fragments, and various domains
|
|
19
|
+
* More precise pattern to avoid false positives
|
|
20
|
+
*/
|
|
21
|
+
const URL_PATTERN = /https?:\/\/(?:[-\w.])+(?:\.[a-zA-Z]{2,})+(?:\/[-\w._~:/?#[\]@!$&'()*+,;=]*)?/g;
|
|
22
|
+
/**
|
|
23
|
+
* Maximum number of URLs to extract (performance consideration)
|
|
24
|
+
*/
|
|
25
|
+
const MAX_URLS = 10;
|
|
26
|
+
/**
|
|
27
|
+
* Extract URLs from a given prompt text
|
|
28
|
+
* @param prompt The text to extract URLs from
|
|
29
|
+
* @returns Array of unique URLs found in the prompt (max 10)
|
|
30
|
+
*/
|
|
31
|
+
function extractUrls(prompt) {
|
|
32
|
+
const matches = prompt.match(URL_PATTERN);
|
|
33
|
+
if (!matches) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
// Remove duplicates using Set and limit to MAX_URLS
|
|
37
|
+
const uniqueUrls = [...new Set(matches)];
|
|
38
|
+
return uniqueUrls.slice(0, MAX_URLS);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check if the prompt contains any URLs
|
|
42
|
+
* @param prompt The text to check for URLs
|
|
43
|
+
* @returns True if URLs are found, false otherwise
|
|
44
|
+
*/
|
|
45
|
+
function hasUrls(prompt) {
|
|
46
|
+
return extractUrls(prompt).length > 0;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Remove all URLs from the prompt text
|
|
50
|
+
* @param prompt The text to remove URLs from
|
|
51
|
+
* @returns The prompt with URLs removed and extra spaces trimmed
|
|
52
|
+
*/
|
|
53
|
+
function removeUrls(prompt) {
|
|
54
|
+
return prompt.replace(URL_PATTERN, '').replace(/\s+/g, ' ').trim();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Validate a single URL using built-in URL constructor
|
|
58
|
+
* @param url The URL string to validate
|
|
59
|
+
* @returns Result containing validation success or error
|
|
60
|
+
*/
|
|
61
|
+
function validateUrl(url) {
|
|
62
|
+
try {
|
|
63
|
+
const urlObj = new URL(url);
|
|
64
|
+
// Only allow HTTP and HTTPS protocols
|
|
65
|
+
if (!['http:', 'https:'].includes(urlObj.protocol)) {
|
|
66
|
+
return (0, result_1.Err)(new errors_1.InvalidUrlError(`Invalid protocol: ${urlObj.protocol}`, 'Only HTTP and HTTPS URLs are supported', url));
|
|
67
|
+
}
|
|
68
|
+
// Basic hostname validation
|
|
69
|
+
if (!urlObj.hostname || urlObj.hostname.length === 0) {
|
|
70
|
+
return (0, result_1.Err)(new errors_1.InvalidUrlError('Invalid hostname in URL', 'URL must have a valid hostname', url));
|
|
71
|
+
}
|
|
72
|
+
return (0, result_1.Ok)(url);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
return (0, result_1.Err)(new errors_1.InvalidUrlError(`Malformed URL: ${url}`, 'Please check the URL format and try again', url));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Extract and validate URLs from prompt with detailed error reporting
|
|
80
|
+
* @param prompt The text to extract URLs from
|
|
81
|
+
* @returns Result containing valid URLs or validation error
|
|
82
|
+
*/
|
|
83
|
+
function extractValidUrls(prompt) {
|
|
84
|
+
const extractedUrls = extractUrls(prompt);
|
|
85
|
+
const validUrls = [];
|
|
86
|
+
const errors = [];
|
|
87
|
+
for (const url of extractedUrls) {
|
|
88
|
+
const validation = validateUrl(url);
|
|
89
|
+
if (validation.success) {
|
|
90
|
+
validUrls.push(validation.data);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
errors.push(validation.error);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// If there are validation errors and no valid URLs, return first error
|
|
97
|
+
if (errors.length > 0 && validUrls.length === 0) {
|
|
98
|
+
const firstError = errors[0];
|
|
99
|
+
if (firstError) {
|
|
100
|
+
return (0, result_1.Err)(firstError);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return (0, result_1.Ok)(validUrls);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get URL statistics for analysis
|
|
107
|
+
* @param prompt The text to analyze
|
|
108
|
+
* @returns Statistics about URLs in the prompt
|
|
109
|
+
*/
|
|
110
|
+
function getUrlStats(prompt) {
|
|
111
|
+
const urls = extractUrls(prompt);
|
|
112
|
+
const httpUrls = urls.filter((url) => url.startsWith('http:')).length;
|
|
113
|
+
const httpsUrls = urls.filter((url) => url.startsWith('https:')).length;
|
|
114
|
+
const domains = urls
|
|
115
|
+
.map((url) => {
|
|
116
|
+
try {
|
|
117
|
+
return new URL(url).hostname;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
.filter((domain) => domain !== null);
|
|
124
|
+
const uniqueDomains = [...new Set(domains)];
|
|
125
|
+
return {
|
|
126
|
+
totalFound: urls.length,
|
|
127
|
+
validUrls: domains.length,
|
|
128
|
+
httpUrls,
|
|
129
|
+
httpsUrls,
|
|
130
|
+
uniqueDomains,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* URL extractor utilities - backward compatibility object
|
|
135
|
+
*/
|
|
136
|
+
exports.URLExtractor = {
|
|
137
|
+
extractUrls,
|
|
138
|
+
hasUrls,
|
|
139
|
+
removeUrls,
|
|
140
|
+
validateUrl,
|
|
141
|
+
extractValidUrls,
|
|
142
|
+
getUrlStats,
|
|
143
|
+
};
|
|
144
|
+
//# sourceMappingURL=urlExtractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"urlExtractor.js","sourceRoot":"","sources":["../../src/business/urlExtractor.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AA+CA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACnE,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AACvE,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.MCPServerImpl = exports.createMCPServer = void 0;
|
|
5
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6
|
+
/**
|
|
7
|
+
* MCP Image Generator entry point
|
|
8
|
+
* MCP server startup process
|
|
9
|
+
*/
|
|
10
|
+
const mcpServer_1 = require("./server/mcpServer");
|
|
11
|
+
const logger_1 = require("./utils/logger");
|
|
12
|
+
const logger = new logger_1.Logger();
|
|
13
|
+
/**
|
|
14
|
+
* Application startup
|
|
15
|
+
*/
|
|
16
|
+
async function main() {
|
|
17
|
+
try {
|
|
18
|
+
logger.info('mcp-startup', 'Starting MCP Image Generator initialization', {
|
|
19
|
+
nodeVersion: process.version,
|
|
20
|
+
platform: process.platform,
|
|
21
|
+
env: process.env['NODE_ENV'] || 'development',
|
|
22
|
+
});
|
|
23
|
+
const mcpServerImpl = new mcpServer_1.MCPServerImpl();
|
|
24
|
+
const server = mcpServerImpl.initialize();
|
|
25
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
26
|
+
await server.connect(transport);
|
|
27
|
+
logger.info('mcp-startup', 'Image Generator MCP Server started successfully');
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
logger.error('mcp-startup', 'Failed to start MCP server', error, {
|
|
31
|
+
errorType: error?.constructor?.name,
|
|
32
|
+
stack: error?.stack,
|
|
33
|
+
});
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Run main function
|
|
38
|
+
main().catch((error) => {
|
|
39
|
+
logger.error('mcp-startup', 'Fatal error during startup', error);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
});
|
|
42
|
+
var mcpServer_2 = require("./server/mcpServer");
|
|
43
|
+
Object.defineProperty(exports, "createMCPServer", { enumerable: true, get: function () { return mcpServer_2.createMCPServer; } });
|
|
44
|
+
Object.defineProperty(exports, "MCPServerImpl", { enumerable: true, get: function () { return mcpServer_2.MCPServerImpl; } });
|
|
45
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;AAEA,wEAAgF;AAChF;;;GAGG;AACH,kDAAkD;AAClD,2CAAuC;AAEvC,MAAM,MAAM,GAAG,IAAI,eAAM,EAAE,CAAA;AAE3B;;GAEG;AACH,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,6CAA6C,EAAE;YACxE,WAAW,EAAE,OAAO,CAAC,OAAO;YAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,aAAa;SAC9C,CAAC,CAAA;QAEF,MAAM,aAAa,GAAG,IAAI,yBAAa,EAAE,CAAA;QAEzC,MAAM,MAAM,GAAG,aAAa,CAAC,UAAU,EAAE,CAAA;QAEzC,MAAM,SAAS,GAAG,IAAI,+BAAoB,EAAE,CAAA;QAE5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QAE/B,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,iDAAiD,CAAC,CAAA;IAC/E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,4BAA4B,EAAE,KAAc,EAAE;YACxE,SAAS,EAAG,KAAe,EAAE,WAAW,EAAE,IAAI;YAC9C,KAAK,EAAG,KAAe,EAAE,KAAK;SAC/B,CAAC,CAAA;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED,oBAAoB;AACpB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,4BAA4B,EAAE,KAAc,CAAC,CAAA;IACzE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA;AAEF,gDAAmE;AAA1D,4GAAA,eAAe,OAAA;AAAE,0GAAA,aAAa,OAAA"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concurrency Manager - Controls concurrent request execution
|
|
3
|
+
* Implements singleton pattern with queue management
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Concurrency manager for limiting concurrent requests
|
|
7
|
+
*/
|
|
8
|
+
export declare class ConcurrencyManager {
|
|
9
|
+
private static instance;
|
|
10
|
+
private activeRequests;
|
|
11
|
+
private readonly maxConcurrent;
|
|
12
|
+
private requestQueue;
|
|
13
|
+
private readonly queueTimeout;
|
|
14
|
+
/**
|
|
15
|
+
* Get singleton instance of ConcurrencyManager
|
|
16
|
+
* @returns ConcurrencyManager instance
|
|
17
|
+
*/
|
|
18
|
+
static getInstance(): ConcurrencyManager;
|
|
19
|
+
/**
|
|
20
|
+
* Acquire a concurrency lock
|
|
21
|
+
* @returns Promise that resolves when lock is acquired
|
|
22
|
+
*/
|
|
23
|
+
acquireLock(): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Release a concurrency lock
|
|
26
|
+
*/
|
|
27
|
+
releaseLock(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Process the next request in the queue
|
|
30
|
+
*/
|
|
31
|
+
private processNextRequest;
|
|
32
|
+
/**
|
|
33
|
+
* Clean up expired requests from the queue
|
|
34
|
+
*/
|
|
35
|
+
private cleanupExpiredRequests;
|
|
36
|
+
/**
|
|
37
|
+
* Check if concurrency limit is reached
|
|
38
|
+
* @returns True if at limit
|
|
39
|
+
*/
|
|
40
|
+
isAtLimit(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Get current queue length
|
|
43
|
+
* @returns Number of queued requests
|
|
44
|
+
*/
|
|
45
|
+
getQueueLength(): number;
|
|
46
|
+
/**
|
|
47
|
+
* Get current active request count
|
|
48
|
+
* @returns Number of active requests
|
|
49
|
+
*/
|
|
50
|
+
getActiveRequestCount(): number;
|
|
51
|
+
/**
|
|
52
|
+
* Reset the concurrency manager (for testing purposes)
|
|
53
|
+
*/
|
|
54
|
+
reset(): void;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=concurrencyManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrencyManager.d.ts","sourceRoot":"","sources":["../../src/server/concurrencyManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAoB;IAC3C,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAI;IAClC,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IAErC;;;OAGG;IACH,MAAM,CAAC,WAAW,IAAI,kBAAkB;IAOxC;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAuClC;;OAEG;IACH,WAAW,IAAI,IAAI;IAOnB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAoB9B;;;OAGG;IACH,SAAS,IAAI,OAAO;IAIpB;;;OAGG;IACH,cAAc,IAAI,MAAM;IAIxB;;;OAGG;IACH,qBAAqB,IAAI,MAAM;IAI/B;;OAEG;IACH,KAAK,IAAI,IAAI;CAId"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concurrency Manager - Controls concurrent request execution
|
|
3
|
+
* Implements singleton pattern with queue management
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Concurrency manager for limiting concurrent requests
|
|
7
|
+
*/
|
|
8
|
+
export class ConcurrencyManager {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.activeRequests = 0;
|
|
11
|
+
this.maxConcurrent = 1;
|
|
12
|
+
this.requestQueue = [];
|
|
13
|
+
this.queueTimeout = 30000; // 30 seconds
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get singleton instance of ConcurrencyManager
|
|
17
|
+
* @returns ConcurrencyManager instance
|
|
18
|
+
*/
|
|
19
|
+
static getInstance() {
|
|
20
|
+
if (!ConcurrencyManager.instance) {
|
|
21
|
+
ConcurrencyManager.instance = new ConcurrencyManager();
|
|
22
|
+
}
|
|
23
|
+
return ConcurrencyManager.instance;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Acquire a concurrency lock
|
|
27
|
+
* @returns Promise that resolves when lock is acquired
|
|
28
|
+
*/
|
|
29
|
+
async acquireLock() {
|
|
30
|
+
if (this.activeRequests < this.maxConcurrent) {
|
|
31
|
+
this.activeRequests++;
|
|
32
|
+
return Promise.resolve();
|
|
33
|
+
}
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const request = {
|
|
36
|
+
resolve,
|
|
37
|
+
reject,
|
|
38
|
+
timestamp: Date.now(),
|
|
39
|
+
};
|
|
40
|
+
this.requestQueue.push(request);
|
|
41
|
+
// Set timeout for this request
|
|
42
|
+
const timeoutId = setTimeout(() => {
|
|
43
|
+
const index = this.requestQueue.findIndex((req) => req === request);
|
|
44
|
+
if (index >= 0) {
|
|
45
|
+
this.requestQueue.splice(index, 1);
|
|
46
|
+
reject(new Error('Concurrency limit timeout'));
|
|
47
|
+
}
|
|
48
|
+
}, this.queueTimeout);
|
|
49
|
+
// Clear timeout when request resolves
|
|
50
|
+
const originalResolve = request.resolve;
|
|
51
|
+
request.resolve = () => {
|
|
52
|
+
clearTimeout(timeoutId);
|
|
53
|
+
originalResolve(undefined);
|
|
54
|
+
};
|
|
55
|
+
const originalReject = request.reject;
|
|
56
|
+
request.reject = (error) => {
|
|
57
|
+
clearTimeout(timeoutId);
|
|
58
|
+
originalReject(error);
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Release a concurrency lock
|
|
64
|
+
*/
|
|
65
|
+
releaseLock() {
|
|
66
|
+
this.activeRequests = Math.max(0, this.activeRequests - 1);
|
|
67
|
+
// Process next request in queue
|
|
68
|
+
this.processNextRequest();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Process the next request in the queue
|
|
72
|
+
*/
|
|
73
|
+
processNextRequest() {
|
|
74
|
+
// Clean up expired requests first
|
|
75
|
+
this.cleanupExpiredRequests();
|
|
76
|
+
if (this.requestQueue.length > 0 && this.activeRequests < this.maxConcurrent) {
|
|
77
|
+
const next = this.requestQueue.shift();
|
|
78
|
+
if (next) {
|
|
79
|
+
this.activeRequests++;
|
|
80
|
+
next.resolve(undefined);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Clean up expired requests from the queue
|
|
86
|
+
*/
|
|
87
|
+
cleanupExpiredRequests() {
|
|
88
|
+
const now = Date.now();
|
|
89
|
+
const initialLength = this.requestQueue.length;
|
|
90
|
+
this.requestQueue = this.requestQueue.filter((request) => {
|
|
91
|
+
const isExpired = now - request.timestamp > this.queueTimeout;
|
|
92
|
+
if (isExpired) {
|
|
93
|
+
request.reject(new Error('Request expired in queue'));
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
});
|
|
98
|
+
// Log cleanup if any requests were removed
|
|
99
|
+
const removedCount = initialLength - this.requestQueue.length;
|
|
100
|
+
if (removedCount > 0) {
|
|
101
|
+
console.warn(`[ConcurrencyManager] Cleaned up ${removedCount} expired requests from queue`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Check if concurrency limit is reached
|
|
106
|
+
* @returns True if at limit
|
|
107
|
+
*/
|
|
108
|
+
isAtLimit() {
|
|
109
|
+
return this.activeRequests >= this.maxConcurrent;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get current queue length
|
|
113
|
+
* @returns Number of queued requests
|
|
114
|
+
*/
|
|
115
|
+
getQueueLength() {
|
|
116
|
+
return this.requestQueue.length;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get current active request count
|
|
120
|
+
* @returns Number of active requests
|
|
121
|
+
*/
|
|
122
|
+
getActiveRequestCount() {
|
|
123
|
+
return this.activeRequests;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Reset the concurrency manager (for testing purposes)
|
|
127
|
+
*/
|
|
128
|
+
reset() {
|
|
129
|
+
this.activeRequests = 0;
|
|
130
|
+
this.requestQueue = [];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=concurrencyManager.js.map
|