#
tokens: 8563/50000 10/10 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .dockerignore
├── .gitignore
├── Dockerfile
├── docs
│   └── API_SETUP.md
├── LICENSE
├── package.json
├── README.md
├── src
│   ├── api
│   │   └── client.ts
│   ├── index.ts
│   └── types.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
1 | node_modules/
2 | build/
3 | dist/
4 | .env
5 | .env.local
6 | .vscode/
7 | .idea/
8 | *.log
9 | .DS_Store
```

--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------

```
 1 | # Node modules and package-lock.json (will be installed in container)
 2 | node_modules
 3 | npm-debug.log*
 4 | yarn-debug.log*
 5 | yarn-error.log*
 6 | 
 7 | # Build output (will be built in container)
 8 | build
 9 | dist
10 | 
11 | # Development and IDE files
12 | .git
13 | .gitignore
14 | README.md
15 | .env
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 | 
21 | # OS generated files
22 | .DS_Store
23 | .DS_Store?
24 | ._*
25 | .Spotlight-V100
26 | .Trashes
27 | ehthumbs.db
28 | Thumbs.db
29 | 
30 | # Editor directories and files
31 | .vscode/*
32 | !.vscode/extensions.json
33 | .idea
34 | *.suo
35 | *.ntvs*
36 | *.njsproj
37 | *.sln
38 | *.sw?
39 | 
40 | # Logs
41 | logs
42 | *.log
43 | 
44 | # Coverage directory used by tools like istanbul
45 | coverage
46 | *.lcov
47 | 
48 | # nyc test coverage
49 | .nyc_output
50 | 
51 | # ESLint cache
52 | .eslintcache
53 | 
54 | # Optional npm cache directory
55 | .npm
56 | 
57 | # Optional REPL history
58 | .node_repl_history
59 | 
60 | # Output of 'npm pack'
61 | *.tgz
62 | 
63 | # Yarn Integrity file
64 | .yarn-integrity
65 | 
66 | # parcel-bundler cache (https://parceljs.org/)
67 | .cache
68 | .parcel-cache
69 | 
70 | # Next.js build output
71 | .next
72 | 
73 | # Nuxt.js build / generate output
74 | .nuxt
75 | 
76 | # Storybook build outputs
77 | .out
78 | .storybook-out
79 | 
80 | # Temporary folders
81 | tmp/
82 | temp/
83 | 
84 | # Test files
85 | test.txt
86 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Freepik MCP Server
  2 | 
  3 | An MCP server implementation for interacting with Freepik's API, providing access to stock photos and Mystic AI image generation capabilities.
  4 | 
  5 | ## Features
  6 | 
  7 | - Search Freepik resources (photos, vectors, PSDs)
  8 | - Get detailed resource information
  9 | - Download resources
 10 | - Generate images using Mystic AI
 11 | - Check image generation status
 12 | 
 13 | ## Prerequisites
 14 | 
 15 | - Node.js 18 or higher
 16 | - A Freepik API key (see [API Setup Guide](docs/API_SETUP.md))
 17 | 
 18 | ## Installation
 19 | 
 20 | ```bash
 21 | # Create a new directory for your MCP servers
 22 | mkdir mcp-servers
 23 | cd mcp-servers
 24 | 
 25 | # Clone the repository
 26 | git clone https://github.com/MCERQUA/freepik-mcp.git
 27 | cd freepik-mcp
 28 | 
 29 | # Install dependencies
 30 | npm install
 31 | 
 32 | # Build the server
 33 | npm run build
 34 | ```
 35 | 
 36 | ## Configuration
 37 | 
 38 | 1. First, obtain your Freepik API key by following the instructions in [API_SETUP.md](docs/API_SETUP.md)
 39 | 
 40 | 2. Add the server to your MCP settings file:
 41 | 
 42 | ```json
 43 | {
 44 |   "mcpServers": {
 45 |     "freepik": {
 46 |       "command": "node",
 47 |       "args": ["path/to/freepik-mcp/build/index.js"],
 48 |       "env": {
 49 |         "FREEPIK_API_KEY": "your-api-key-here"
 50 |       },
 51 |       "disabled": false,
 52 |       "autoApprove": []
 53 |     }
 54 |   }
 55 | }
 56 | ```
 57 | 
 58 | ## Available Tools
 59 | 
 60 | ### search_resources
 61 | Search for Freepik resources with various filters:
 62 | ```typescript
 63 | {
 64 |   term?: string;          // Search term
 65 |   limit?: number;         // Results per page
 66 |   order?: 'relevance' | 'recent';
 67 |   filters?: {
 68 |     orientation?: {
 69 |       landscape?: boolean;
 70 |       portrait?: boolean;
 71 |       square?: boolean;
 72 |       panoramic?: boolean;
 73 |     };
 74 |     content_type?: {
 75 |       photo?: boolean;
 76 |       psd?: boolean;
 77 |       vector?: boolean;
 78 |     };
 79 |     license?: {
 80 |       freemium?: boolean;
 81 |       premium?: boolean;
 82 |     };
 83 |   };
 84 | }
 85 | ```
 86 | 
 87 | ### get_resource
 88 | Get detailed information about a specific resource:
 89 | ```typescript
 90 | {
 91 |   id: number;  // Resource ID to get details for
 92 | }
 93 | ```
 94 | 
 95 | ### download_resource
 96 | Get download URL for a specific resource:
 97 | ```typescript
 98 | {
 99 |   id: number;  // Resource ID to download
100 | }
101 | ```
102 | 
103 | ### generate_image
104 | Generate an image using Freepik Mystic AI:
105 | ```typescript
106 | {
107 |   prompt: string;         // Text description of the image to generate
108 |   resolution?: '2k' | '4k';
109 |   aspect_ratio?: 'square_1_1' | 'classic_4_3' | 'traditional_3_4' | 
110 |                  'widescreen_16_9' | 'social_story_9_16';
111 |   realism?: boolean;      // Enable realistic style
112 |   engine?: 'automatic' | 'magnific_illusio' | 'magnific_sharpy' | 'magnific_sparkle';
113 |   creative_detailing?: number;  // 0-100
114 | }
115 | ```
116 | 
117 | ### check_status
118 | Check the status of a Mystic image generation task:
119 | ```typescript
120 | {
121 |   task_id: string;  // ID of the generation task to check
122 | }
123 | ```
124 | 
125 | ## Development
126 | 
127 | ```bash
128 | # Install dependencies
129 | npm install
130 | 
131 | # Build the server
132 | npm run build
133 | 
134 | # Run in development mode
135 | npm run dev
136 | ```
137 | 
138 | ## Error Handling
139 | 
140 | The server implements comprehensive error handling:
141 | 
142 | - API errors are logged with detailed information
143 | - Input validation using Zod schemas
144 | - Proper error responses with context
145 | - Rate limiting awareness
146 | 
147 | ## Contributing
148 | 
149 | 1. Fork the repository
150 | 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
151 | 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
152 | 4. Push to the branch (`git push origin feature/amazing-feature`)
153 | 5. Open a Pull Request
154 | 
155 | ## License
156 | 
157 | MIT
158 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "ES2020",
 5 |     "moduleResolution": "node",
 6 |     "outDir": "./build",
 7 |     "rootDir": "./src",
 8 |     "strict": true,
 9 |     "esModuleInterop": true,
10 |     "skipLibCheck": true,
11 |     "forceConsistentCasingInFileNames": true,
12 |     "declaration": true
13 |   },
14 |   "include": ["src/**/*"],
15 |   "exclude": ["node_modules", "build"]
16 | }
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "freepik-mcp",
 3 |   "version": "0.1.0",
 4 |   "description": "MCP server for interacting with Freepik's API",
 5 |   "type": "module",
 6 |   "main": "build/index.js",
 7 |   "scripts": {
 8 |     "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
 9 |     "dev": "tsc -w",
10 |     "start": "node build/index.js",
11 |     "lint": "eslint . --ext .ts",
12 |     "format": "prettier --write 'src/**/*.ts'"
13 |   },
14 |   "keywords": [
15 |     "mcp",
16 |     "freepik",
17 |     "api",
18 |     "stock-photos",
19 |     "ai-generation"
20 |   ],
21 |   "author": "",
22 |   "license": "MIT",
23 |   "dependencies": {
24 |     "@modelcontextprotocol/sdk": "^1.15.0",
25 |     "axios": "^1.6.0",
26 |     "zod": "^3.22.0"
27 |   },
28 |   "devDependencies": {
29 |     "@types/node": "^20.0.0",
30 |     "@typescript-eslint/eslint-plugin": "^6.0.0",
31 |     "@typescript-eslint/parser": "^6.0.0",
32 |     "eslint": "^8.0.0",
33 |     "prettier": "^3.0.0",
34 |     "typescript": "^5.0.0"
35 |   }
36 | }
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | # Multi-stage build for optimal image size
 2 | FROM node:20-alpine AS builder
 3 | 
 4 | # Set working directory
 5 | WORKDIR /app
 6 | 
 7 | # Copy package files
 8 | COPY package*.json ./
 9 | COPY tsconfig.json ./
10 | 
11 | # Install dependencies (including dev dependencies for build)
12 | RUN npm ci
13 | 
14 | # Copy source code
15 | COPY src/ ./src/
16 | 
17 | # Build the application
18 | RUN npm run build
19 | 
20 | # Production stage
21 | FROM node:20-alpine AS production
22 | 
23 | # Create app directory
24 | WORKDIR /app
25 | 
26 | # Copy package files
27 | COPY package*.json ./
28 | 
29 | # Install only production dependencies
30 | RUN npm ci --only=production && npm cache clean --force
31 | 
32 | # Copy built application from builder stage
33 | COPY --from=builder /app/build ./build
34 | 
35 | # Create non-root user for security
36 | RUN addgroup -g 1001 -S nodejs && \
37 |     adduser -S mcp -u 1001
38 | 
39 | # Change ownership of the app directory
40 | RUN chown -R mcp:nodejs /app
41 | USER mcp
42 | 
43 | # Expose the port the app runs on (if applicable)
44 | # Note: MCP servers typically use stdio, but this is here for potential HTTP usage
45 | EXPOSE 3000
46 | 
47 | # Health check (optional, can be customized based on your server's health endpoint)
48 | HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
49 |   CMD node -e "process.exit(0)" || exit 1
50 | 
51 | # Start the application
52 | CMD ["node", "build/index.js"]
53 | 
```

--------------------------------------------------------------------------------
/docs/API_SETUP.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Freepik API Setup Guide
 2 | 
 3 | ## Getting Your API Key
 4 | 
 5 | 1. Create a Freepik Account
 6 |    - Visit [Freepik.com](https://www.freepik.com)
 7 |    - Click "Sign up" and create an account
 8 |    - Verify your email address
 9 | 
10 | 2. Access the Developer Portal
11 |    - Log in to your Freepik account
12 |    - Visit the [API Documentation](https://developer.freepik.com/)
13 |    - Click "Get API Key"
14 | 
15 | 3. Create an API Key
16 |    - Fill out the required information about your intended API usage
17 |    - Accept the terms of service
18 |    - Your API key will be generated and displayed
19 | 
20 | ## Configuring the MCP Server
21 | 
22 | 1. Locate your MCP settings file:
23 |    - VSCode extension: `~/.vscode/extensions/your-extension/settings/mcp_settings.json`
24 |    - Claude desktop app: `~/Library/Application Support/Claude/claude_desktop_config.json`
25 | 
26 | 2. Add the Freepik MCP server configuration:
27 |    ```json
28 |    {
29 |      "mcpServers": {
30 |        "freepik": {
31 |          "command": "node",
32 |          "args": ["path/to/freepik-mcp/build/index.js"],
33 |          "env": {
34 |            "FREEPIK_API_KEY": "your-api-key-here"
35 |          },
36 |          "disabled": false,
37 |          "autoApprove": []
38 |        }
39 |      }
40 |    }
41 |    ```
42 | 
43 | 3. Replace `your-api-key-here` with your actual Freepik API key
44 | 
45 | ## Security Best Practices
46 | 
47 | 1. Keep your API key secure:
48 |    - Never commit your API key to version control
49 |    - Use environment variables or secure configuration management
50 |    - Rotate your API key periodically
51 | 
52 | 2. Monitor API usage:
53 |    - Keep track of your API consumption
54 |    - Set up alerts for unusual activity
55 |    - Review API logs regularly
56 | 
57 | 3. Rate Limiting:
58 |    - Be aware of Freepik's API rate limits
59 |    - Implement appropriate error handling
60 |    - Cache responses when possible
61 | 
62 | ## Troubleshooting
63 | 
64 | If you encounter issues:
65 | 
66 | 1. Verify your API key is correct
67 | 2. Check your network connection
68 | 3. Ensure your API key has the necessary permissions
69 | 4. Review Freepik's API documentation for any changes
70 | 5. Check the server logs for detailed error messages
71 | 
72 | For additional help, visit the [Freepik Developer Support](https://developer.freepik.com/support)
```

--------------------------------------------------------------------------------
/src/api/client.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import axios, { AxiosInstance } from 'axios';
 2 | import { 
 3 |   FreepikConfig, 
 4 |   GenerateImageParams, 
 5 |   GenerateImageResponse, 
 6 |   CheckStatusResponse,
 7 |   SearchResourcesParams,
 8 |   SearchResourcesResponse,
 9 |   ResourceResponse
10 | } from '../types.js';
11 | 
12 | export class FreepikClient {
13 |   private client: AxiosInstance;
14 |   private baseUrl = 'https://api.freepik.com';
15 | 
16 |   constructor(config: FreepikConfig) {
17 |     this.client = axios.create({
18 |       baseURL: this.baseUrl,
19 |       headers: {
20 |         'x-freepik-api-key': config.apiKey,
21 |         'Content-Type': 'application/json'
22 |       }
23 |     });
24 | 
25 |     // Add response interceptor for error handling
26 |     this.client.interceptors.response.use(
27 |       response => response,
28 |       error => {
29 |         if (error.response) {
30 |           const { status, data } = error.response;
31 |           console.error(`[Freepik API Error] Status: ${status}, Message:`, data);
32 |         }
33 |         throw error;
34 |       }
35 |     );
36 |   }
37 | 
38 |   // Stock Photo API methods
39 |   async searchResources(params: SearchResourcesParams): Promise<SearchResourcesResponse> {
40 |     console.error('[Freepik] Searching resources with params:', params);
41 |     const response = await this.client.get<SearchResourcesResponse>('/v1/resources', { params });
42 |     return response.data;
43 |   }
44 | 
45 |   async getResourceDetails(id: number): Promise<ResourceResponse> {
46 |     console.error(`[Freepik] Getting resource details for id: ${id}`);
47 |     const response = await this.client.get<ResourceResponse>(`/v1/resources/${id}`);
48 |     return response.data;
49 |   }
50 | 
51 |   async downloadResource(id: number): Promise<{ url: string }> {
52 |     console.error(`[Freepik] Downloading resource id: ${id}`);
53 |     const response = await this.client.get<{ url: string }>(`/v1/resources/${id}/download`);
54 |     return response.data;
55 |   }
56 | 
57 |   // Mystic API methods
58 |   async generateImage(params: GenerateImageParams): Promise<GenerateImageResponse> {
59 |     console.error('[Freepik] Generating image with params:', params);
60 |     const response = await this.client.post<GenerateImageResponse>('/v1/ai/mystic', params);
61 |     return response.data;
62 |   }
63 | 
64 |   async checkStatus(taskId: string): Promise<CheckStatusResponse> {
65 |     console.error(`[Freepik] Checking status for task: ${taskId}`);
66 |     const response = await this.client.get<CheckStatusResponse>(`/v1/ai/mystic/${taskId}`);
67 |     return response.data;
68 |   }
69 | }
```

--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { z } from 'zod';
  2 | 
  3 | // Common types
  4 | export interface FreepikConfig {
  5 |   apiKey: string;
  6 | }
  7 | 
  8 | // Stock Photo types
  9 | export interface SearchResourcesParams {
 10 |   term?: string;
 11 |   page?: number;
 12 |   limit?: number;
 13 |   order?: 'relevance' | 'recent';
 14 |   filters?: {
 15 |     orientation?: {
 16 |       landscape?: boolean;
 17 |       portrait?: boolean;
 18 |       square?: boolean;
 19 |       panoramic?: boolean;
 20 |     };
 21 |     content_type?: {
 22 |       photo?: boolean;
 23 |       psd?: boolean;
 24 |       vector?: boolean;
 25 |     };
 26 |     license?: {
 27 |       freemium?: boolean;
 28 |       premium?: boolean;
 29 |     };
 30 |     people?: {
 31 |       include?: boolean;
 32 |       exclude?: boolean;
 33 |       number?: '1' | '2' | '3' | 'more_than_three';
 34 |       age?: 'infant' | 'child' | 'teen' | 'young-adult' | 'adult' | 'senior' | 'elder';
 35 |       gender?: 'male' | 'female';
 36 |       ethnicity?: 'south-asian' | 'middle-eastern' | 'east-asian' | 'black' | 'hispanic' | 'indian' | 'white' | 'multiracial';
 37 |     };
 38 |     color?: 'black' | 'blue' | 'gray' | 'green' | 'orange' | 'red' | 'white' | 'yellow' | 'purple' | 'cyan' | 'pink';
 39 |   };
 40 | }
 41 | 
 42 | export interface ResourceResponse {
 43 |   id: number;
 44 |   title: string;
 45 |   url: string;
 46 |   filename: string;
 47 |   licenses: Array<{
 48 |     type: 'freemium' | 'premium';
 49 |     url: string;
 50 |   }>;
 51 |   image: {
 52 |     type: 'photo' | 'vector' | 'psd';
 53 |     orientation: 'horizontal' | 'vertical' | 'square' | 'panoramic' | 'unknown';
 54 |     source: {
 55 |       url: string;
 56 |       key: string;
 57 |       size: string;
 58 |     };
 59 |   };
 60 |   author: {
 61 |     id: number;
 62 |     name: string;
 63 |     avatar: string;
 64 |     assets: number;
 65 |     slug: string;
 66 |   };
 67 |   stats: {
 68 |     downloads: number;
 69 |     likes: number;
 70 |   };
 71 | }
 72 | 
 73 | export interface SearchResourcesResponse {
 74 |   data: ResourceResponse[];
 75 |   meta: {
 76 |     current_page: number;
 77 |     last_page: number;
 78 |     per_page: number;
 79 |     total: number;
 80 |   };
 81 | }
 82 | 
 83 | // Zod schemas for validation
 84 | export const SearchResourcesSchema = z.object({
 85 |   term: z.string().optional(),
 86 |   page: z.number().min(1).optional(),
 87 |   limit: z.number().min(1).optional(),
 88 |   order: z.enum(['relevance', 'recent']).optional(),
 89 |   filters: z.object({
 90 |     orientation: z.object({
 91 |       landscape: z.boolean().optional(),
 92 |       portrait: z.boolean().optional(),
 93 |       square: z.boolean().optional(),
 94 |       panoramic: z.boolean().optional()
 95 |     }).optional(),
 96 |     content_type: z.object({
 97 |       photo: z.boolean().optional(),
 98 |       psd: z.boolean().optional(),
 99 |       vector: z.boolean().optional()
100 |     }).optional(),
101 |     license: z.object({
102 |       freemium: z.boolean().optional(),
103 |       premium: z.boolean().optional()
104 |     }).optional()
105 |   }).optional()
106 | });
107 | 
108 | export const GetResourceSchema = z.object({
109 |   id: z.number().min(1)
110 | });
111 | 
112 | export const DownloadResourceSchema = z.object({
113 |   id: z.number().min(1)
114 | });
115 | 
116 | // Mystic types
117 | export interface GenerateImageParams {
118 |   prompt: string;
119 |   resolution?: '2k' | '4k';
120 |   aspect_ratio?: 'square_1_1' | 'classic_4_3' | 'traditional_3_4' | 'widescreen_16_9' | 'social_story_9_16';
121 |   structure_reference?: string;
122 |   style_reference?: string;
123 |   realism?: boolean;
124 |   engine?: 'automatic' | 'magnific_illusio' | 'magnific_sharpy' | 'magnific_sparkle';
125 |   creative_detailing?: number;
126 |   filter_nsfw?: boolean;
127 | }
128 | 
129 | export interface GenerateImageResponse {
130 |   task_id: string;
131 |   status: string;
132 | }
133 | 
134 | export interface CheckStatusResponse {
135 |   status: string;
136 |   generated?: string[];
137 | }
138 | 
139 | // Mystic Zod schemas
140 | export const GenerateImageSchema = z.object({
141 |   prompt: z.string().min(1),
142 |   resolution: z.enum(['2k', '4k']).optional(),
143 |   aspect_ratio: z.enum([
144 |     'square_1_1',
145 |     'classic_4_3',
146 |     'traditional_3_4',
147 |     'widescreen_16_9',
148 |     'social_story_9_16'
149 |   ]).optional(),
150 |   structure_reference: z.string().optional(),
151 |   style_reference: z.string().optional(),
152 |   realism: z.boolean().optional(),
153 |   engine: z.enum(['automatic', 'magnific_illusio', 'magnific_sharpy', 'magnific_sparkle']).optional(),
154 |   creative_detailing: z.number().min(0).max(100).optional(),
155 |   filter_nsfw: z.boolean().optional()
156 | });
157 | 
158 | export const CheckStatusSchema = z.object({
159 |   task_id: z.string()
160 | });
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
  3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
  4 | import {
  5 |   CallToolRequestSchema,
  6 |   ErrorCode,
  7 |   ListToolsRequestSchema,
  8 |   McpError,
  9 | } from '@modelcontextprotocol/sdk/types.js';
 10 | 
 11 | import { FreepikClient } from './api/client.js';
 12 | import { 
 13 |   GenerateImageSchema, 
 14 |   CheckStatusSchema,
 15 |   SearchResourcesSchema,
 16 |   GetResourceSchema,
 17 |   DownloadResourceSchema
 18 | } from './types.js';
 19 | 
 20 | const API_KEY = process.env.FREEPIK_API_KEY;
 21 | if (!API_KEY) {
 22 |   throw new Error('FREEPIK_API_KEY environment variable is required');
 23 | }
 24 | 
 25 | class FreepikServer {
 26 |   private server: Server;
 27 |   private client: FreepikClient;
 28 | 
 29 |   constructor() {
 30 |     this.server = new Server(
 31 |       {
 32 |         name: 'freepik-mcp',
 33 |         version: '0.1.0',
 34 |       },
 35 |       {
 36 |         capabilities: {
 37 |           tools: {},
 38 |           resources: {},
 39 |         },
 40 |       }
 41 |     );
 42 | 
 43 |     this.client = new FreepikClient({ apiKey: API_KEY });
 44 | 
 45 |     this.setupToolHandlers();
 46 |     
 47 |     // Error handling
 48 |     this.server.onerror = (error) => console.error('[MCP Error]', error);
 49 |     process.on('SIGINT', async () => {
 50 |       await this.server.close();
 51 |       process.exit(0);
 52 |     });
 53 |   }
 54 | 
 55 |   private setupToolHandlers() {
 56 |     // List available tools - This is the key method for inspection
 57 |     this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
 58 |       tools: [
 59 |         {
 60 |           name: 'search_resources',
 61 |           description: 'Search for Freepik resources (photos, vectors, PSDs) with filters',
 62 |           inputSchema: {
 63 |             type: 'object',
 64 |             properties: {
 65 |               term: {
 66 |                 type: 'string',
 67 |                 description: 'Search term'
 68 |               },
 69 |               limit: {
 70 |                 type: 'number',
 71 |                 description: 'Limit results per page (default: 20, max: 200)',
 72 |                 default: 20,
 73 |                 maximum: 200
 74 |               },
 75 |               order: {
 76 |                 type: 'string',
 77 |                 enum: ['relevance', 'recent'],
 78 |                 description: 'Sort order',
 79 |                 default: 'relevance'
 80 |               },
 81 |               filters: {
 82 |                 type: 'object',
 83 |                 description: 'Optional filters to apply to search results',
 84 |                 properties: {
 85 |                   orientation: {
 86 |                     type: 'object',
 87 |                     description: 'Filter by image orientation',
 88 |                     properties: {
 89 |                       landscape: { type: 'boolean', description: 'Include landscape orientation' },
 90 |                       portrait: { type: 'boolean', description: 'Include portrait orientation' },
 91 |                       square: { type: 'boolean', description: 'Include square orientation' },
 92 |                       panoramic: { type: 'boolean', description: 'Include panoramic orientation' }
 93 |                     }
 94 |                   },
 95 |                   content_type: {
 96 |                     type: 'object',
 97 |                     description: 'Filter by content type',
 98 |                     properties: {
 99 |                       photo: { type: 'boolean', description: 'Include photos' },
100 |                       psd: { type: 'boolean', description: 'Include PSDs' },
101 |                       vector: { type: 'boolean', description: 'Include vectors' }
102 |                     }
103 |                   },
104 |                   license: {
105 |                     type: 'object',
106 |                     description: 'Filter by license type',
107 |                     properties: {
108 |                       freemium: { type: 'boolean', description: 'Include freemium resources' },
109 |                       premium: { type: 'boolean', description: 'Include premium resources' }
110 |                     }
111 |                   }
112 |                 }
113 |               }
114 |             },
115 |             required: ['term']
116 |           }
117 |         },
118 |         {
119 |           name: 'get_resource',
120 |           description: 'Get detailed information about a specific Freepik resource',
121 |           inputSchema: {
122 |             type: 'object',
123 |             properties: {
124 |               id: {
125 |                 type: 'number',
126 |                 description: 'Resource ID to get details for'
127 |               }
128 |             },
129 |             required: ['id']
130 |           }
131 |         },
132 |         {
133 |           name: 'download_resource',
134 |           description: 'Get download URL for a specific Freepik resource',
135 |           inputSchema: {
136 |             type: 'object',
137 |             properties: {
138 |               id: {
139 |                 type: 'number',
140 |                 description: 'Resource ID to download'
141 |               }
142 |             },
143 |             required: ['id']
144 |           }
145 |         },
146 |         {
147 |           name: 'generate_image',
148 |           description: 'Generate an AI image using Freepik Mystic (requires Freepik Premium)',
149 |           inputSchema: {
150 |             type: 'object',
151 |             properties: {
152 |               prompt: {
153 |                 type: 'string',
154 |                 description: 'Text description of the image to generate'
155 |               },
156 |               resolution: {
157 |                 type: 'string',
158 |                 enum: ['2k', '4k'],
159 |                 description: 'Image resolution',
160 |                 default: '2k'
161 |               },
162 |               aspect_ratio: {
163 |                 type: 'string',
164 |                 enum: ['square_1_1', 'classic_4_3', 'traditional_3_4', 'widescreen_16_9', 'social_story_9_16'],
165 |                 description: 'Image aspect ratio',
166 |                 default: 'square_1_1'
167 |               },
168 |               realism: {
169 |                 type: 'boolean',
170 |                 description: 'Enable realistic style',
171 |                 default: false
172 |               },
173 |               engine: {
174 |                 type: 'string',
175 |                 enum: ['automatic', 'magnific_illusio', 'magnific_sharpy', 'magnific_sparkle'],
176 |                 description: 'AI engine to use',
177 |                 default: 'automatic'
178 |               },
179 |               creative_detailing: {
180 |                 type: 'number',
181 |                 minimum: 0,
182 |                 maximum: 100,
183 |                 description: 'Level of creative detail (0-100)',
184 |                 default: 50
185 |               }
186 |             },
187 |             required: ['prompt']
188 |           }
189 |         },
190 |         {
191 |           name: 'check_status',
192 |           description: 'Check the status of a Freepik Mystic image generation task',
193 |           inputSchema: {
194 |             type: 'object',
195 |             properties: {
196 |               task_id: {
197 |                 type: 'string',
198 |                 description: 'ID of the generation task to check'
199 |               }
200 |             },
201 |             required: ['task_id']
202 |           }
203 |         },
204 |       ],
205 |     }));
206 | 
207 |     // Handle tool calls
208 |     this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
209 |       try {
210 |         switch (request.params.name) {
211 |           case 'search_resources': {
212 |             const validatedParams = SearchResourcesSchema.parse(request.params.arguments);
213 |             const result = await this.client.searchResources(validatedParams);
214 |             return {
215 |               content: [
216 |                 {
217 |                   type: 'text',
218 |                   text: JSON.stringify(result, null, 2),
219 |                 },
220 |               ],
221 |             };
222 |           }
223 | 
224 |           case 'get_resource': {
225 |             const { id } = GetResourceSchema.parse(request.params.arguments);
226 |             const result = await this.client.getResourceDetails(id);
227 |             return {
228 |               content: [
229 |                 {
230 |                   type: 'text',
231 |                   text: JSON.stringify(result, null, 2),
232 |                 },
233 |               ],
234 |             };
235 |           }
236 | 
237 |           case 'download_resource': {
238 |             const { id } = DownloadResourceSchema.parse(request.params.arguments);
239 |             const result = await this.client.downloadResource(id);
240 |             return {
241 |               content: [
242 |                 {
243 |                   type: 'text',
244 |                   text: JSON.stringify(result, null, 2),
245 |                 },
246 |               ],
247 |             };
248 |           }
249 | 
250 |           case 'generate_image': {
251 |             const validatedParams = GenerateImageSchema.parse(request.params.arguments);
252 |             const result = await this.client.generateImage(validatedParams);
253 |             return {
254 |               content: [
255 |                 {
256 |                   type: 'text',
257 |                   text: JSON.stringify(result, null, 2),
258 |                 },
259 |               ],
260 |             };
261 |           }
262 | 
263 |           case 'check_status': {
264 |             const { task_id } = CheckStatusSchema.parse(request.params.arguments);
265 |             const result = await this.client.checkStatus(task_id);
266 |             return {
267 |               content: [
268 |                 {
269 |                   type: 'text',
270 |                   text: JSON.stringify(result, null, 2),
271 |                 },
272 |               ],
273 |             };
274 |           }
275 | 
276 |           default:
277 |             throw new McpError(
278 |               ErrorCode.MethodNotFound,
279 |               `Unknown tool: ${request.params.name}`
280 |             );
281 |         }
282 |       } catch (error) {
283 |         console.error('[Tool Error]', error);
284 |         return {
285 |           content: [
286 |             {
287 |               type: 'text',
288 |               text: error instanceof Error ? error.message : String(error),
289 |             },
290 |           ],
291 |           isError: true,
292 |         };
293 |       }
294 |     });
295 |   }
296 | 
297 |   async run() {
298 |     const transport = new StdioServerTransport();
299 |     await this.server.connect(transport);
300 |     console.error('Freepik MCP server running on stdio');
301 |   }
302 | }
303 | 
304 | const server = new FreepikServer();
305 | server.run().catch(console.error);
```