diff --git a/Dockerfile b/Dockerfile index 8b8b2c7..9e460d3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,8 @@ RUN apk add --no-cache \ make \ python3 \ curl \ - ca-certificates + ca-certificates \ + vips-dev # Configure npm for better reliability RUN npm config set strict-ssl false @@ -16,18 +17,24 @@ RUN npm config set registry https://registry.npmjs.org/ RUN npm config set fetch-retry-mintimeout 20000 RUN npm config set fetch-retry-maxtimeout 120000 -# Copy package files -COPY package*.json ./ +# Install global packages needed for build +RUN npm install -g typescript shx -# Clean npm cache and install dependencies +# Copy package files and modify to prevent prepare script from running +COPY package*.json ./ +RUN node -e "const pkg = require('./package.json'); delete pkg.scripts.prepare; require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2));" + +# Install dependencies without running the prepare script, including sharp with the correct platform RUN npm cache clean --force && \ - npm install --legacy-peer-deps --no-optional + npm install --legacy-peer-deps --no-optional && \ + npm install --platform=linuxmusl --arch=x64 sharp # Copy source code COPY . . -# Build TypeScript code -RUN npm run build +# Build TypeScript code manually +RUN npx tsc && \ + npx shx chmod +x dist/*.js # Switch to production for runtime ENV NODE_ENV=production diff --git a/src/openrouter-api.ts b/src/openrouter-api.ts index 0a0803e..84ef5ab 100644 --- a/src/openrouter-api.ts +++ b/src/openrouter-api.ts @@ -1,5 +1,5 @@ import axios, { AxiosError, AxiosInstance } from 'axios'; -import { McpError } from '@modelcontextprotocol/sdk/types.js'; +import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; /** * Client for interacting with the OpenRouter API @@ -90,7 +90,7 @@ export class OpenRouterAPIClient { if (axiosError.response) { const responseData = axiosError.response.data as any; const message = responseData?.error?.message || axiosError.message; - throw new McpError('RequestFailed', `OpenRouter API error: ${message}`); + throw new McpError(ErrorCode.InternalError, `OpenRouter API error: ${message}`); } } @@ -114,17 +114,17 @@ export class OpenRouterAPIClient { const message = responseData?.error?.message || axiosError.message; if (status === 401 || status === 403) { - throw new McpError('Unauthorized', `Authentication error: ${message}`); + throw new McpError(ErrorCode.InvalidRequest, `Authentication error: ${message}`); } else if (status === 429) { - throw new McpError('RateLimitExceeded', `Rate limit exceeded: ${message}`); + throw new McpError(ErrorCode.InternalError, `Rate limit exceeded: ${message}`); } else { - throw new McpError('RequestFailed', `OpenRouter API error (${status}): ${message}`); + throw new McpError(ErrorCode.InternalError, `OpenRouter API error (${status}): ${message}`); } } else if (axiosError.request) { - throw new McpError('NetworkError', `Network error: ${axiosError.message}`); + throw new McpError(ErrorCode.ConnectionClosed, `Network error: ${axiosError.message}`); } } - throw new McpError('UnknownError', `Unknown error: ${error.message || 'No error message'}`); + throw new McpError(ErrorCode.InternalError, `Unknown error: ${error.message || 'No error message'}`); } } diff --git a/src/tool-handlers/analyze-image.ts b/src/tool-handlers/analyze-image.ts index 3355e78..c594179 100644 --- a/src/tool-handlers/analyze-image.ts +++ b/src/tool-handlers/analyze-image.ts @@ -1,7 +1,7 @@ import path from 'path'; import { promises as fs } from 'fs'; import sharp from 'sharp'; -import { McpError } from '@modelcontextprotocol/sdk/types.js'; +import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import OpenAI from 'openai'; export interface AnalyzeImageToolRequest { @@ -21,7 +21,7 @@ export async function handleAnalyzeImage( // Validate image path const imagePath = args.image_path; if (!path.isAbsolute(imagePath)) { - throw new McpError('InvalidParams', 'Image path must be absolute'); + throw new McpError(ErrorCode.InvalidParams, 'Image path must be absolute'); } // Read image file @@ -85,7 +85,7 @@ export async function handleAnalyzeImage( // Call OpenRouter API const completion = await openai.chat.completions.create({ model, - messages, + messages: messages as any, }); return { diff --git a/src/tool-handlers/get-model-info.ts b/src/tool-handlers/get-model-info.ts index ab7f804..47bef5e 100644 --- a/src/tool-handlers/get-model-info.ts +++ b/src/tool-handlers/get-model-info.ts @@ -1,4 +1,4 @@ -import { McpError } from '@modelcontextprotocol/sdk/types.js'; +import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import { ModelCache } from '../model-cache.js'; export interface GetModelInfoToolRequest { @@ -26,7 +26,7 @@ export async function handleGetModelInfo( const model = modelCache.getModel(args.model); if (!model) { - throw new McpError('NotFound', `Model '${args.model}' not found`); + throw new McpError(ErrorCode.InvalidParams, `Model '${args.model}' not found`); } return { diff --git a/src/tool-handlers/multi-image-analysis.ts b/src/tool-handlers/multi-image-analysis.ts index 3e81c59..e9dfa63 100644 --- a/src/tool-handlers/multi-image-analysis.ts +++ b/src/tool-handlers/multi-image-analysis.ts @@ -1,6 +1,6 @@ import fetch from 'node-fetch'; import sharp from 'sharp'; -import { McpError } from '@modelcontextprotocol/sdk/types.js'; +import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import OpenAI from 'openai'; export interface MultiImageAnalysisToolRequest { @@ -90,11 +90,11 @@ export async function handleMultiImageAnalysis( try { // Validate inputs if (!args.images || args.images.length === 0) { - throw new McpError('InvalidParams', 'At least one image is required'); + throw new McpError(ErrorCode.InvalidParams, 'At least one image is required'); } if (!args.prompt) { - throw new McpError('InvalidParams', 'A prompt is required'); + throw new McpError(ErrorCode.InvalidParams, 'A prompt is required'); } // Prepare content array for the message @@ -137,7 +137,7 @@ export async function handleMultiImageAnalysis( messages: [{ role: 'user', content - }] + }] as any }); return {