@stabgan/openrouter-mcp-multimodal 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.
Files changed (32) hide show
  1. package/README.md +331 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +52 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/model-cache.d.ts +56 -0
  6. package/dist/model-cache.js +143 -0
  7. package/dist/model-cache.js.map +1 -0
  8. package/dist/openrouter-api.d.ts +26 -0
  9. package/dist/openrouter-api.js +119 -0
  10. package/dist/openrouter-api.js.map +1 -0
  11. package/dist/tool-handlers/analyze-image.d.ts +23 -0
  12. package/dist/tool-handlers/analyze-image.js +93 -0
  13. package/dist/tool-handlers/analyze-image.js.map +1 -0
  14. package/dist/tool-handlers/chat-completion.d.ts +24 -0
  15. package/dist/tool-handlers/chat-completion.js +111 -0
  16. package/dist/tool-handlers/chat-completion.js.map +1 -0
  17. package/dist/tool-handlers/get-model-info.d.ts +21 -0
  18. package/dist/tool-handlers/get-model-info.js +44 -0
  19. package/dist/tool-handlers/get-model-info.js.map +1 -0
  20. package/dist/tool-handlers/multi-image-analysis.d.ts +27 -0
  21. package/dist/tool-handlers/multi-image-analysis.js +136 -0
  22. package/dist/tool-handlers/multi-image-analysis.js.map +1 -0
  23. package/dist/tool-handlers/search-models.d.ts +34 -0
  24. package/dist/tool-handlers/search-models.js +44 -0
  25. package/dist/tool-handlers/search-models.js.map +1 -0
  26. package/dist/tool-handlers/validate-model.d.ts +21 -0
  27. package/dist/tool-handlers/validate-model.js +40 -0
  28. package/dist/tool-handlers/validate-model.js.map +1 -0
  29. package/dist/tool-handlers.d.ts +10 -0
  30. package/dist/tool-handlers.js +316 -0
  31. package/dist/tool-handlers.js.map +1 -0
  32. package/package.json +60 -0
@@ -0,0 +1,119 @@
1
+ import axios from 'axios';
2
+ import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
3
+ /**
4
+ * Client for interacting with the OpenRouter API
5
+ */
6
+ export class OpenRouterAPIClient {
7
+ apiKey;
8
+ axiosInstance;
9
+ retryCount = 3;
10
+ retryDelay = 1000; // Initial delay in ms
11
+ constructor(apiKey) {
12
+ this.apiKey = apiKey;
13
+ this.axiosInstance = axios.create({
14
+ baseURL: 'https://openrouter.ai/api/v1',
15
+ headers: {
16
+ 'Authorization': `Bearer ${this.apiKey}`,
17
+ 'Content-Type': 'application/json',
18
+ 'HTTP-Referer': 'https://github.com/stabgan/openrouter-mcp-multimodal',
19
+ 'X-Title': 'OpenRouter MCP Multimodal Server'
20
+ },
21
+ timeout: 60000 // 60 seconds timeout
22
+ });
23
+ }
24
+ /**
25
+ * Get all available models from OpenRouter
26
+ */
27
+ async getModels() {
28
+ try {
29
+ const response = await this.axiosInstance.get('/models');
30
+ return response.data.data;
31
+ }
32
+ catch (error) {
33
+ this.handleRequestError(error);
34
+ return [];
35
+ }
36
+ }
37
+ /**
38
+ * Send a request to the OpenRouter API with retry functionality
39
+ */
40
+ async request(endpoint, method, data) {
41
+ let lastError = null;
42
+ let retries = 0;
43
+ while (retries <= this.retryCount) {
44
+ try {
45
+ const response = await this.axiosInstance.request({
46
+ url: endpoint,
47
+ method,
48
+ data
49
+ });
50
+ return response.data;
51
+ }
52
+ catch (error) {
53
+ lastError = this.handleRetryableError(error, retries);
54
+ retries++;
55
+ if (retries <= this.retryCount) {
56
+ // Exponential backoff with jitter
57
+ const delay = this.retryDelay * Math.pow(2, retries - 1) * (0.5 + Math.random() * 0.5);
58
+ console.error(`Retrying in ${Math.round(delay)}ms (${retries}/${this.retryCount})`);
59
+ await new Promise(resolve => setTimeout(resolve, delay));
60
+ }
61
+ }
62
+ }
63
+ // If we get here, all retries failed
64
+ throw lastError || new Error('Request failed after multiple retries');
65
+ }
66
+ /**
67
+ * Handle retryable errors
68
+ */
69
+ handleRetryableError(error, retryCount) {
70
+ if (axios.isAxiosError(error)) {
71
+ const axiosError = error;
72
+ // Rate limiting (429) or server errors (5xx)
73
+ if (axiosError.response?.status === 429 || (axiosError.response?.status && axiosError.response.status >= 500)) {
74
+ console.error(`Request error (retry ${retryCount}): ${axiosError.message}`);
75
+ if (axiosError.response?.status === 429) {
76
+ console.error('Rate limit exceeded. Retrying with backoff...');
77
+ }
78
+ return new Error(`OpenRouter API error: ${axiosError.response?.status} ${axiosError.message}`);
79
+ }
80
+ // For other status codes, don't retry
81
+ if (axiosError.response) {
82
+ const responseData = axiosError.response.data;
83
+ const message = responseData?.error?.message || axiosError.message;
84
+ throw new McpError(ErrorCode.InternalError, `OpenRouter API error: ${message}`);
85
+ }
86
+ }
87
+ // Network errors should be retried
88
+ console.error(`Network error (retry ${retryCount}): ${error.message}`);
89
+ return new Error(`Network error: ${error.message}`);
90
+ }
91
+ /**
92
+ * Handle request errors
93
+ */
94
+ handleRequestError(error) {
95
+ console.error('Error in OpenRouter API request:', error);
96
+ if (axios.isAxiosError(error)) {
97
+ const axiosError = error;
98
+ if (axiosError.response) {
99
+ const status = axiosError.response.status;
100
+ const responseData = axiosError.response.data;
101
+ const message = responseData?.error?.message || axiosError.message;
102
+ if (status === 401 || status === 403) {
103
+ throw new McpError(ErrorCode.InvalidRequest, `Authentication error: ${message}`);
104
+ }
105
+ else if (status === 429) {
106
+ throw new McpError(ErrorCode.InternalError, `Rate limit exceeded: ${message}`);
107
+ }
108
+ else {
109
+ throw new McpError(ErrorCode.InternalError, `OpenRouter API error (${status}): ${message}`);
110
+ }
111
+ }
112
+ else if (axiosError.request) {
113
+ throw new McpError(ErrorCode.ConnectionClosed, `Network error: ${axiosError.message}`);
114
+ }
115
+ }
116
+ throw new McpError(ErrorCode.InternalError, `Unknown error: ${error.message || 'No error message'}`);
117
+ }
118
+ }
119
+ //# sourceMappingURL=openrouter-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openrouter-api.js","sourceRoot":"","sources":["../src/openrouter-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import OpenAI from 'openai';
2
+ export interface AnalyzeImageToolRequest {
3
+ image_path: string;
4
+ question?: string;
5
+ model?: string;
6
+ }
7
+ export declare function handleAnalyzeImage(request: {
8
+ params: {
9
+ arguments: AnalyzeImageToolRequest;
10
+ };
11
+ }, openai: OpenAI, defaultModel?: string): Promise<{
12
+ content: {
13
+ type: string;
14
+ text: string;
15
+ }[];
16
+ isError?: undefined;
17
+ } | {
18
+ content: {
19
+ type: string;
20
+ text: string;
21
+ }[];
22
+ isError: boolean;
23
+ }>;
@@ -0,0 +1,93 @@
1
+ import path from 'path';
2
+ import { promises as fs } from 'fs';
3
+ import sharp from 'sharp';
4
+ import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
5
+ export async function handleAnalyzeImage(request, openai, defaultModel) {
6
+ const args = request.params.arguments;
7
+ try {
8
+ // Validate image path
9
+ const imagePath = args.image_path;
10
+ if (!path.isAbsolute(imagePath)) {
11
+ throw new McpError(ErrorCode.InvalidParams, 'Image path must be absolute');
12
+ }
13
+ // Read image file
14
+ const imageBuffer = await fs.readFile(imagePath);
15
+ console.error(`Successfully read image buffer of size: ${imageBuffer.length}`);
16
+ // Get image metadata
17
+ const metadata = await sharp(imageBuffer).metadata();
18
+ console.error('Image metadata:', metadata);
19
+ // Calculate dimensions to keep base64 size reasonable
20
+ const MAX_DIMENSION = 800; // Larger than original example for better quality
21
+ const JPEG_QUALITY = 80; // Higher quality
22
+ let resizedBuffer = imageBuffer;
23
+ if (metadata.width && metadata.height) {
24
+ const largerDimension = Math.max(metadata.width, metadata.height);
25
+ if (largerDimension > MAX_DIMENSION) {
26
+ const resizeOptions = metadata.width > metadata.height
27
+ ? { width: MAX_DIMENSION }
28
+ : { height: MAX_DIMENSION };
29
+ resizedBuffer = await sharp(imageBuffer)
30
+ .resize(resizeOptions)
31
+ .jpeg({ quality: JPEG_QUALITY })
32
+ .toBuffer();
33
+ }
34
+ else {
35
+ resizedBuffer = await sharp(imageBuffer)
36
+ .jpeg({ quality: JPEG_QUALITY })
37
+ .toBuffer();
38
+ }
39
+ }
40
+ // Convert to base64
41
+ const base64Image = resizedBuffer.toString('base64');
42
+ // Select model
43
+ const model = args.model || defaultModel || 'anthropic/claude-3.5-sonnet';
44
+ // Prepare message with image
45
+ const messages = [
46
+ {
47
+ role: 'user',
48
+ content: [
49
+ {
50
+ type: 'text',
51
+ text: args.question || "What's in this image?"
52
+ },
53
+ {
54
+ type: 'image_url',
55
+ image_url: {
56
+ url: `data:image/jpeg;base64,${base64Image}`
57
+ }
58
+ }
59
+ ]
60
+ }
61
+ ];
62
+ console.error('Sending request to OpenRouter...');
63
+ // Call OpenRouter API
64
+ const completion = await openai.chat.completions.create({
65
+ model,
66
+ messages: messages,
67
+ });
68
+ return {
69
+ content: [
70
+ {
71
+ type: 'text',
72
+ text: completion.choices[0].message.content || '',
73
+ },
74
+ ],
75
+ };
76
+ }
77
+ catch (error) {
78
+ console.error('Error analyzing image:', error);
79
+ if (error instanceof McpError) {
80
+ throw error;
81
+ }
82
+ return {
83
+ content: [
84
+ {
85
+ type: 'text',
86
+ text: `Error analyzing image: ${error instanceof Error ? error.message : String(error)}`,
87
+ },
88
+ ],
89
+ isError: true,
90
+ };
91
+ }
92
+ }
93
+ //# sourceMappingURL=analyze-image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze-image.js","sourceRoot":"","sources":["../../src/tool-handlers/analyze-image.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import OpenAI from 'openai';
2
+ import { ChatCompletionMessageParam } from 'openai/resources/chat/completions.js';
3
+ export interface ChatCompletionToolRequest {
4
+ model?: string;
5
+ messages: ChatCompletionMessageParam[];
6
+ temperature?: number;
7
+ }
8
+ export declare function handleChatCompletion(request: {
9
+ params: {
10
+ arguments: ChatCompletionToolRequest;
11
+ };
12
+ }, openai: OpenAI, defaultModel?: string): Promise<{
13
+ content: {
14
+ type: string;
15
+ text: string;
16
+ }[];
17
+ isError: boolean;
18
+ } | {
19
+ content: {
20
+ type: string;
21
+ text: string;
22
+ }[];
23
+ isError?: undefined;
24
+ }>;
@@ -0,0 +1,111 @@
1
+ // Maximum context tokens
2
+ const MAX_CONTEXT_TOKENS = 200000;
3
+ // Utility function to estimate token count (simplified)
4
+ function estimateTokenCount(text) {
5
+ // Rough approximation: 4 characters per token
6
+ return Math.ceil(text.length / 4);
7
+ }
8
+ // Truncate messages to fit within the context window
9
+ function truncateMessagesToFit(messages, maxTokens) {
10
+ const truncated = [];
11
+ let currentTokenCount = 0;
12
+ // Always include system message first if present
13
+ if (messages[0]?.role === 'system') {
14
+ truncated.push(messages[0]);
15
+ currentTokenCount += estimateTokenCount(messages[0].content);
16
+ }
17
+ // Add messages from the end, respecting the token limit
18
+ for (let i = messages.length - 1; i >= 0; i--) {
19
+ const message = messages[i];
20
+ // Skip if it's the system message we've already added
21
+ if (i === 0 && message.role === 'system')
22
+ continue;
23
+ // For string content, estimate tokens directly
24
+ if (typeof message.content === 'string') {
25
+ const messageTokens = estimateTokenCount(message.content);
26
+ if (currentTokenCount + messageTokens > maxTokens)
27
+ break;
28
+ truncated.unshift(message);
29
+ currentTokenCount += messageTokens;
30
+ }
31
+ // For multimodal content (array), estimate tokens for text content
32
+ else if (Array.isArray(message.content)) {
33
+ let messageTokens = 0;
34
+ for (const part of message.content) {
35
+ if (part.type === 'text' && part.text) {
36
+ messageTokens += estimateTokenCount(part.text);
37
+ }
38
+ else if (part.type === 'image_url') {
39
+ // Add a token cost estimate for images - this is a simplification
40
+ // Actual image token costs depend on resolution and model
41
+ messageTokens += 1000;
42
+ }
43
+ }
44
+ if (currentTokenCount + messageTokens > maxTokens)
45
+ break;
46
+ truncated.unshift(message);
47
+ currentTokenCount += messageTokens;
48
+ }
49
+ }
50
+ return truncated;
51
+ }
52
+ export async function handleChatCompletion(request, openai, defaultModel) {
53
+ const args = request.params.arguments;
54
+ // Validate model selection
55
+ const model = args.model || defaultModel;
56
+ if (!model) {
57
+ return {
58
+ content: [
59
+ {
60
+ type: 'text',
61
+ text: 'No model specified and no default model configured in MCP settings. Please specify a model or set OPENROUTER_DEFAULT_MODEL in the MCP configuration.',
62
+ },
63
+ ],
64
+ isError: true,
65
+ };
66
+ }
67
+ // Validate message array
68
+ if (args.messages.length === 0) {
69
+ return {
70
+ content: [
71
+ {
72
+ type: 'text',
73
+ text: 'Messages array cannot be empty. At least one message is required.',
74
+ },
75
+ ],
76
+ isError: true,
77
+ };
78
+ }
79
+ try {
80
+ // Truncate messages to fit within context window
81
+ const truncatedMessages = truncateMessagesToFit(args.messages, MAX_CONTEXT_TOKENS);
82
+ const completion = await openai.chat.completions.create({
83
+ model,
84
+ messages: truncatedMessages,
85
+ temperature: args.temperature ?? 1,
86
+ });
87
+ return {
88
+ content: [
89
+ {
90
+ type: 'text',
91
+ text: completion.choices[0].message.content || '',
92
+ },
93
+ ],
94
+ };
95
+ }
96
+ catch (error) {
97
+ if (error instanceof Error) {
98
+ return {
99
+ content: [
100
+ {
101
+ type: 'text',
102
+ text: `OpenRouter API error: ${error.message}`,
103
+ },
104
+ ],
105
+ isError: true,
106
+ };
107
+ }
108
+ throw error;
109
+ }
110
+ }
111
+ //# sourceMappingURL=chat-completion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-completion.js","sourceRoot":"","sources":["../../src/tool-handlers/chat-completion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { ModelCache } from '../model-cache.js';
2
+ export interface GetModelInfoToolRequest {
3
+ model: string;
4
+ }
5
+ export declare function handleGetModelInfo(request: {
6
+ params: {
7
+ arguments: GetModelInfoToolRequest;
8
+ };
9
+ }, modelCache: ModelCache): Promise<{
10
+ content: {
11
+ type: string;
12
+ text: string;
13
+ }[];
14
+ isError: boolean;
15
+ } | {
16
+ content: {
17
+ type: string;
18
+ text: string;
19
+ }[];
20
+ isError?: undefined;
21
+ }>;
@@ -0,0 +1,44 @@
1
+ import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
2
+ export async function handleGetModelInfo(request, modelCache) {
3
+ const args = request.params.arguments;
4
+ try {
5
+ if (!modelCache.isCacheValid()) {
6
+ return {
7
+ content: [
8
+ {
9
+ type: 'text',
10
+ text: 'Model cache is empty or expired. Please call search_models first to populate the cache.',
11
+ },
12
+ ],
13
+ isError: true,
14
+ };
15
+ }
16
+ const model = modelCache.getModel(args.model);
17
+ if (!model) {
18
+ throw new McpError(ErrorCode.InvalidParams, `Model '${args.model}' not found`);
19
+ }
20
+ return {
21
+ content: [
22
+ {
23
+ type: 'text',
24
+ text: JSON.stringify(model, null, 2),
25
+ },
26
+ ],
27
+ };
28
+ }
29
+ catch (error) {
30
+ if (error instanceof Error) {
31
+ return {
32
+ content: [
33
+ {
34
+ type: 'text',
35
+ text: `Error retrieving model info: ${error.message}`,
36
+ },
37
+ ],
38
+ isError: true,
39
+ };
40
+ }
41
+ throw error;
42
+ }
43
+ }
44
+ //# sourceMappingURL=get-model-info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-model-info.js","sourceRoot":"","sources":["../../src/tool-handlers/get-model-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAOzE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAA2D,EAC3D,UAAsB;IAEtB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;IAEtC,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,EAAE,CAAC;YAC/B,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,yFAAyF;qBAChG;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,UAAU,IAAI,CAAC,KAAK,aAAa,CAAC,CAAC;QACjF,CAAC;QAED,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;iBACrC;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,gCAAgC,KAAK,CAAC,OAAO,EAAE;qBACtD;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,27 @@
1
+ import OpenAI from 'openai';
2
+ export interface MultiImageAnalysisToolRequest {
3
+ images: Array<{
4
+ url: string;
5
+ alt?: string;
6
+ }>;
7
+ prompt: string;
8
+ markdown_response?: boolean;
9
+ model?: string;
10
+ }
11
+ export declare function handleMultiImageAnalysis(request: {
12
+ params: {
13
+ arguments: MultiImageAnalysisToolRequest;
14
+ };
15
+ }, openai: OpenAI, defaultModel?: string): Promise<{
16
+ content: {
17
+ type: string;
18
+ text: string;
19
+ }[];
20
+ isError?: undefined;
21
+ } | {
22
+ content: {
23
+ type: string;
24
+ text: string;
25
+ }[];
26
+ isError: boolean;
27
+ }>;
@@ -0,0 +1,136 @@
1
+ import fetch from 'node-fetch';
2
+ import sharp from 'sharp';
3
+ import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
4
+ async function fetchImageAsBuffer(url) {
5
+ try {
6
+ // Handle data URLs
7
+ if (url.startsWith('data:')) {
8
+ const matches = url.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
9
+ if (!matches || matches.length !== 3) {
10
+ throw new Error('Invalid data URL');
11
+ }
12
+ return Buffer.from(matches[2], 'base64');
13
+ }
14
+ // Handle file URLs
15
+ if (url.startsWith('file://')) {
16
+ const filePath = url.replace('file://', '');
17
+ const fs = await import('fs/promises');
18
+ return await fs.readFile(filePath);
19
+ }
20
+ // Handle http/https URLs
21
+ const response = await fetch(url);
22
+ if (!response.ok) {
23
+ throw new Error(`HTTP error! status: ${response.status}`);
24
+ }
25
+ return Buffer.from(await response.arrayBuffer());
26
+ }
27
+ catch (error) {
28
+ console.error(`Error fetching image from ${url}:`, error);
29
+ throw error;
30
+ }
31
+ }
32
+ async function processImage(buffer) {
33
+ try {
34
+ // Get image metadata
35
+ const metadata = await sharp(buffer).metadata();
36
+ // Calculate dimensions to keep base64 size reasonable
37
+ const MAX_DIMENSION = 800;
38
+ const JPEG_QUALITY = 80;
39
+ if (metadata.width && metadata.height) {
40
+ const largerDimension = Math.max(metadata.width, metadata.height);
41
+ if (largerDimension > MAX_DIMENSION) {
42
+ const resizeOptions = metadata.width > metadata.height
43
+ ? { width: MAX_DIMENSION }
44
+ : { height: MAX_DIMENSION };
45
+ const resizedBuffer = await sharp(buffer)
46
+ .resize(resizeOptions)
47
+ .jpeg({ quality: JPEG_QUALITY })
48
+ .toBuffer();
49
+ return resizedBuffer.toString('base64');
50
+ }
51
+ }
52
+ // If no resizing needed, just convert to JPEG
53
+ const jpegBuffer = await sharp(buffer)
54
+ .jpeg({ quality: JPEG_QUALITY })
55
+ .toBuffer();
56
+ return jpegBuffer.toString('base64');
57
+ }
58
+ catch (error) {
59
+ console.error('Error processing image:', error);
60
+ throw error;
61
+ }
62
+ }
63
+ export async function handleMultiImageAnalysis(request, openai, defaultModel) {
64
+ const args = request.params.arguments;
65
+ try {
66
+ // Validate inputs
67
+ if (!args.images || args.images.length === 0) {
68
+ throw new McpError(ErrorCode.InvalidParams, 'At least one image is required');
69
+ }
70
+ if (!args.prompt) {
71
+ throw new McpError(ErrorCode.InvalidParams, 'A prompt is required');
72
+ }
73
+ // Prepare content array for the message
74
+ const content = [{
75
+ type: 'text',
76
+ text: args.prompt
77
+ }];
78
+ // Process each image
79
+ for (const image of args.images) {
80
+ try {
81
+ // Fetch and process the image
82
+ const imageBuffer = await fetchImageAsBuffer(image.url);
83
+ const base64Image = await processImage(imageBuffer);
84
+ // Add to content
85
+ content.push({
86
+ type: 'image_url',
87
+ image_url: {
88
+ url: `data:image/jpeg;base64,${base64Image}`
89
+ }
90
+ });
91
+ }
92
+ catch (error) {
93
+ console.error(`Error processing image ${image.url}:`, error);
94
+ // Continue with other images if one fails
95
+ }
96
+ }
97
+ // If no images were successfully processed
98
+ if (content.length === 1) {
99
+ throw new Error('Failed to process any of the provided images');
100
+ }
101
+ // Select model
102
+ const model = args.model || defaultModel || 'anthropic/claude-3.5-sonnet';
103
+ // Make the API call
104
+ const completion = await openai.chat.completions.create({
105
+ model,
106
+ messages: [{
107
+ role: 'user',
108
+ content
109
+ }]
110
+ });
111
+ return {
112
+ content: [
113
+ {
114
+ type: 'text',
115
+ text: completion.choices[0].message.content || '',
116
+ },
117
+ ],
118
+ };
119
+ }
120
+ catch (error) {
121
+ console.error('Error in multi-image analysis:', error);
122
+ if (error instanceof McpError) {
123
+ throw error;
124
+ }
125
+ return {
126
+ content: [
127
+ {
128
+ type: 'text',
129
+ text: `Error analyzing images: ${error instanceof Error ? error.message : String(error)}`,
130
+ },
131
+ ],
132
+ isError: true,
133
+ };
134
+ }
135
+ }
136
+ //# sourceMappingURL=multi-image-analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-image-analysis.js","sourceRoot":"","sources":["../../src/tool-handlers/multi-image-analysis.ts"],"names":[],"mappings":"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"}