# 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);
```