#
tokens: 4294/50000 14/14 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .env.example
├── .gitignore
├── Dockerfile
├── LICENSE
├── package.json
├── README.md
├── smithery.yaml
├── src
│   ├── api
│   │   └── LottieApiClient.ts
│   ├── error
│   │   └── ErrorHandler.ts
│   ├── handlers
│   │   ├── PromptHandler.ts
│   │   ├── ResourceHandler.ts
│   │   └── ToolHandler.ts
│   ├── index.ts
│   └── types.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
LOTTIEFILES_API_URL=https://lottiefiles.com/api
```

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

```
# Dependencies
node_modules/
package-lock.json

# Build output
dist/

# Environment variables
.env

# IDE files
.vscode/
.idea/

# Logs
*.log
npm-debug.log*

# OS files
.DS_Store
Thumbs.db 
```

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

```markdown
# LottieFiles MCP Server
[![smithery badge](https://smithery.ai/badge/mcp-server-lottiefiles)](https://smithery.ai/server/mcp-server-lottiefiles)

A Model Context Protocol (MCP) server for searching and retrieving Lottie animations from LottieFiles.

## Features

- Search Lottie animations
- Get animation details
- Get popular animations list

## Installation

### Installing via Smithery

To install LottieFiles Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/mcp-server-lottiefiles):

```bash
npx -y smithery install mcp-server-lottiefiles --client claude
```

### Manual Installation
```bash
npm install
```

## Usage

1. Start the server:

```bash
npm start
```

2. Connect using an MCP client

## API Tools

### Search Animations

Search for Lottie animations by keywords.

Parameters:
- `query`: Search keywords
- `page`: Page number (optional, default: 1)
- `limit`: Items per page (optional, default: 20)

### Get Animation Details

Get detailed information about a specific Lottie animation.

Parameters:
- `id`: Unique identifier of the animation

### Get Popular Animations

Get a list of currently popular Lottie animations.

Parameters:
- `page`: Page number (optional, default: 1)
- `limit`: Items per page (optional, default: 20)

## Development

```bash
# Build
npm run build
```

## License

MIT

```

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

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
} 
```

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

```json
{
  "name": "mcp-server-lottiefiles",
  "version": "1.0.3",
  "type": "module",
  "main": "dist/index.js",
  "bin": {
    "mcp-server-lottiefiles": "dist/index.js"
  },
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.7.0",
    "axios": "^1.7.9",
    "dotenv": "^16.4.7"
  },
  "devDependencies": {
    "@types/dotenv": "^8.2.3",
    "@types/node": "^18.19.80",
    "typescript": "^5.0.0"
  }
}

```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml

startCommand:
  type: stdio
  configSchema:
    # JSON Schema defining the configuration options for the MCP.
    type: object
    properties: {}
    default: {}
    description: No configuration needed for the LottieFiles MCP server
  commandFunction:
    # A JS function that produces the CLI command based on the given config to start the MCP on stdio.
    |-
    (config) => ({ command: 'node', args: ['dist/index.js'], env: {} })
  exampleConfig: {}

```

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

```dockerfile
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
# Dockerfile for LottieFiles MCP Server
FROM node:lts-alpine AS builder
WORKDIR /app

# Copy package files and typescript config
COPY package.json package-lock.json* tsconfig.json ./

# Copy source
COPY src ./src

# Install dependencies and build
RUN npm install --ignore-scripts && npm run build

# Runtime image
FROM node:lts-alpine AS runtime
WORKDIR /app

# Copy only built files and dependencies
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules

# Copy package.json for completeness
COPY package.json ./

# Default command
CMD ["node", "dist/index.js"]

```

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

```typescript
export interface LottieAnimation {
  id: string;
  name: string;
  description: string;
  previewUrl: string;
  downloadUrl: string;
  tags: string[];
  createdAt: string;
  updatedAt: string;
}

export interface SearchAnimationsParams {
  query: string;
  page?: number;
  limit?: number;
}

export interface GetAnimationParams {
  id: string;
}

export interface GetPopularAnimationsParams {
  page?: number;
  limit?: number;
}

export interface SearchAnimationsResponse {
  animations: LottieAnimation[];
  total: number;
  page: number;
  limit: number;
}

export interface GetPopularAnimationsResponse {
  animations: LottieAnimation[];
  total: number;
  page: number;
  limit: number;
}

export interface Prompt {
  name: string;
  description: string;
  arguments: Array<{
    name: string;
    description: string;
    required: boolean;
  }>;
} 
```

--------------------------------------------------------------------------------
/src/error/ErrorHandler.ts:
--------------------------------------------------------------------------------

```typescript
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
import axios from "axios";

export class ErrorHandler {
  static handleError(error: unknown): never {
    if (axios.isAxiosError(error)) {
      throw new McpError(
        ErrorCode.InternalError,
        `LottieFiles API error: ${error.response?.data?.message || error.message}`
      );
    }

    if (error instanceof McpError) {
      throw error;
    }

    if (error instanceof Error) {
      throw new McpError(
        ErrorCode.InternalError,
        error.message
      );
    }

    throw new McpError(
      ErrorCode.InternalError,
      'An unknown error occurred'
    );
  }

  static validateRequiredParam(param: unknown, paramName: string): void {
    if (param === undefined || param === null) {
      throw new McpError(
        ErrorCode.InvalidRequest,
        `Missing required parameter: ${paramName}`
      );
    }
  }
} 
```

--------------------------------------------------------------------------------
/src/handlers/ResourceHandler.ts:
--------------------------------------------------------------------------------

```typescript
import { LottieApiClient } from '../api/LottieApiClient.js';
import { ListResourcesRequest, ReadResourceRequest } from '@modelcontextprotocol/sdk/types.js';

export class ResourceHandler {
  constructor(private apiClient: LottieApiClient) {
    this.apiClient = apiClient;
  }

  async listResources(request: ListResourcesRequest) {
    return {
      resources: [{
        uri: "lottiefiles://resources/popular",
        name: "popular",
        mimeType: "application/json",
        description: "Popular Lottie animations"
      }]
    };
  }

  async readResource(request: ReadResourceRequest) {
    const { name, uri } = request.params;

    switch (name) {
      case "popular": {
        const popularList = await this.apiClient.getPopularAnimations(
          request.params.page as number,
          request.params.limit as number
        );
        return {
          contents: [{
            uri,
            mimeType: "application/json",
            text: JSON.stringify(popularList, null, 2)
          }]
        };
      }

      default:
        throw new Error(`Unknown resource: ${name}`);
    }
  }
} 
```

--------------------------------------------------------------------------------
/src/handlers/PromptHandler.ts:
--------------------------------------------------------------------------------

```typescript
import { McpError, ErrorCode, ListPromptsRequest, GetPromptRequest } from "@modelcontextprotocol/sdk/types.js";
import { Prompt } from "../types.js";

export class PromptHandler {

  async listPrompts(request: ListPromptsRequest) {
    return {
      prompts: [
        {
          name: "search_animations",
          description: "Search for Lottie animations",
          inputSchema: {
            type: "object",
            properties: {
              query: {
                type: "string",
                description: "Search keywords"
              }
            }
          }
        },
        {
          name: "get_popular_animations",
          description: "Get popular Lottie animations",
          inputSchema: {
            type: "object",
            properties: {}
          }
        }
      ]
    };
  }

  async getPrompt(request: GetPromptRequest) {
    const { name, arguments: args } = request.params;

    switch (name) {
      case "search_animations":
        return {
          prompt: `Please help me search for Lottie animations related to "${args?.query}".`,
          tools: ["search_animations"]
        };

      case "get_popular_animations":
        return {
          prompt: "Please help me get the most popular Lottie animations.",
          tools: ["get_popular_animations"]
        };

      default:
        throw new Error(`Unknown prompt: ${name}`);
    }
  }

} 
```

--------------------------------------------------------------------------------
/src/api/LottieApiClient.ts:
--------------------------------------------------------------------------------

```typescript
import axios from "axios";
export class LottieApiClient {
  private baseUrl: string;
  private axiosInstance: any;

  constructor() {
    this.baseUrl =
      process.env.LOTTIEFILES_API_URL || "https://lottiefiles.com/api";
    this.axiosInstance = axios.create({
      baseURL: this.baseUrl,
      params: {
        format: "json",
      },
    });
  }

  async searchAnimations(query: string, page: number = 1, limit: number = 20) {
    try {
      const response = await this.axiosInstance.get(
        `${this.baseUrl}/search/get-animations`,
        {
          params: {
            query,
            page,
            limit,
          },
        }
      );
      return response.data.data.data;
    } catch (error) {
      if (error instanceof Error) {
        throw new Error(`Failed to search animations: ${error.message}`);
      }
      throw new Error("Failed to search animations: Unknown error");
    }
  }

  async getAnimationById(id: string) {
    try {
      const response = await this.axiosInstance.get(
        `${this.baseUrl}/animations/get-animation-data`,
        {
          params: {
            fileId: id,
          },
        }
      );

      return response.data;
    } catch (error) {
      if (error instanceof Error) {
        throw new Error(`Failed to get animation: ${error.message}`);
      }
      throw new Error("Failed to get animation: Unknown error");
    }
  }

  async getAnimationByUser(user: string, page: number = 1, limit: number = 20) {
    try {
      const response = await this.axiosInstance.get(
        `${this.baseUrl}/search/get-users`,
        {
          params: {
            query: user,
            page,
            limit,
          },
        }
      );
      return response.data;
    } catch (error) {
      if (error instanceof Error) {
        throw new Error(`Failed to get animation: ${error.message}`);
      }
      throw new Error("Failed to get animation: Unknown error");
    }
  }

  async getPopularAnimations(page: number = 1, limit: number = 20) {
    try {
      const response = await this.axiosInstance.get(
        `${this.baseUrl}/iconscout/popular-animations-weekly?api=%26sort%3Dpopular`,
        {
          params: {
            page,
            limit,
          },
        }
      );

      return response.data.popularWeeklyData.data;
    } catch (error) {
      if (error instanceof Error) {
        throw new Error(`Failed to get popular animations: ${error.message}`);
      }
      throw new Error("Failed to get popular animations: Unknown error");
    }
  }
}

```

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

```typescript
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import {
  ListToolsRequestSchema,
  CallToolRequestSchema,
  ListResourcesRequestSchema,
  ReadResourceRequestSchema,
  ListPromptsRequestSchema,
  GetPromptRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import dotenv from "dotenv";

import { LottieApiClient } from "./api/LottieApiClient.js";
import { ToolHandler } from "./handlers/ToolHandler.js";
import { ResourceHandler } from "./handlers/ResourceHandler.js";
import { PromptHandler } from "./handlers/PromptHandler.js";
import { ErrorHandler } from "./error/ErrorHandler.js";
class LottieServer {
  private readonly server: Server;
  private readonly apiClient: LottieApiClient;
  private readonly toolHandler: ToolHandler;
  private readonly resourceHandler: ResourceHandler;
  private readonly promptHandler: PromptHandler;

  constructor() {
    // Load environment variables
    dotenv.config();

    // Initialize API client
    this.apiClient = new LottieApiClient();

    // Initialize handlers
    this.toolHandler = new ToolHandler(this.apiClient);
    this.resourceHandler = new ResourceHandler(this.apiClient);
    this.promptHandler = new PromptHandler();

    // Initialize server with configuration
    this.server = this.initializeServer();

    // Setup handlers and error handling
    this.setupHandlers();
    this.setupErrorHandling();
  }

  private initializeServer(): Server {
    return new Server(
      {
        name: "lottiefiles-server",
        version: "1.0.0",
      },
      {
        capabilities: {
          tools: {},
          resources: {
            list: true,
            read: true,
            subscribe: false,
          },
          prompts: {
            list: true,
            get: true,
          },
        },
      }
    );
  }

  private setupErrorHandling(): void {
    this.server.onerror = (error) => {
      console.error("[MCP Error]", error);
    };
    process.on("SIGINT", async () => {
      await this.server.close();
      process.exit(0);
    });
  }

  private setupHandlers(): void {
    // Tool handlers
    this.server.setRequestHandler(ListToolsRequestSchema, async (request) => {
      return await this.toolHandler.listTools(request);
    });

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      return await this.toolHandler.callTool(request);
    });

    // Resource handlers
    this.server.setRequestHandler(
      ListResourcesRequestSchema,
      async (request) => {
        return await this.resourceHandler.listResources(request);
      }
    );

    this.server.setRequestHandler(
      ReadResourceRequestSchema,
      async (request) => {
        return await this.resourceHandler.readResource(request);
      }
    );

    // Prompt handlers
    this.server.setRequestHandler(ListPromptsRequestSchema, async (request) => {
      return await this.promptHandler.listPrompts(request);
    });

    this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
      return await this.promptHandler.getPrompt(request);
    });
  }

  async run(): Promise<void> {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
  }
}

// Start the server
const server = new LottieServer();
server.run().catch((error) => {
  ErrorHandler.handleError(error);
});

```

--------------------------------------------------------------------------------
/src/handlers/ToolHandler.ts:
--------------------------------------------------------------------------------

```typescript
import {
  CallToolRequest,
  ListToolsRequest,
} from "@modelcontextprotocol/sdk/types.js";
import { LottieApiClient } from "../api/LottieApiClient.js";
export class ToolHandler {
  constructor(private apiClient: LottieApiClient) {
    this.apiClient = apiClient;
  }

  async listTools(request: ListToolsRequest) {
    return {
      tools: [
        {
          name: "search_animations",
          description:
            "Search for Lottie animations by keywords, tags, and other criteria. Supports pagination.",
          inputSchema: {
            type: "object",
            properties: {
              query: {
                type: "string",
                description:
                  "Search keywords that match animation names, descriptions, tags, etc.",
              },
              page: {
                type: "integer",
                description: "Page number, starting from 1",
                minimum: 1,
                default: 1,
              },
              limit: {
                type: "integer",
                description: "Number of items per page",
                minimum: 1,
                maximum: 100,
                default: 20,
              },
            },
          },
        },
        {
          name: "get_animation_details",
          description:
            "Get detailed information about a specific Lottie animation, including animation data, preview images, and tags.",
          inputSchema: {
            type: "object",
            properties: {
              id: {
                type: "string",
                description: "Unique identifier of the animation",
              },
            },
            required: ["id"],
          },
        },
        {
          name: "get_popular_animations",
          description: "Get a list of currently popular Lottie animations.",
          inputSchema: {
            type: "object",
            properties: {
              page: {
                type: "integer",
                description: "Page number, starting from 1",
                minimum: 1,
                default: 1,
              },
              limit: {
                type: "integer",
                description: "Number of items per page",
                minimum: 1,
                maximum: 100,
                default: 20,
              },
            },
          },
        },
      ],
    };
  }

  async callTool(request: CallToolRequest) {
    const { name, arguments: args } = request.params;

    switch (name) {
      case "search_animations":
        const list = await this.apiClient.searchAnimations(
          args?.query as string,
          args?.page as number,
          args?.limit as number
        );

        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(
                {
                  count: list.length,
                  animations: list,
                },
                null,
                2
              ),
            },
          ],
        };

      case "get_animation_details":
        const details = await this.apiClient.getAnimationById(
          args?.id as string
        );

        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(details, null, 2),
            },
          ],
        };

      case "get_popular_animations":
        const popular = await this.apiClient.getPopularAnimations(
          args?.page as number,
          args?.limit as number
        );
        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(
                {
                  count: popular.length,
                  popular: popular,
                },
                null,
                2
              ),
            },
          ],
        };

      default:
        throw new Error(`Unknown tool: ${name}`);
    }
  }
}

```