FI

file-organizer-dev

A development guide for the File Organizer MCP server, covering architecture, Zod schemas, and security testing.

Install

mkdir -p .claude/skills/file-organizer-dev && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/15414" && unzip -o skill.zip -d .claude/skills/file-organizer-dev && rm skill.zip

Installs to .claude/skills/file-organizer-dev

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Development guide for the File Organizer MCP server codebase. Use when (1) adding new MCP tools, (2) adding new services, (3) modifying existing tools or services, (4) writing tests, (5) fixing security issues, (6) refactoring code, or (7) understanding the architecture. Provides patterns for Zod schemas, path validation, error handling, and security-hardened file operations.
378 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Advanced

Key capabilities

  • Compile TypeScript to JavaScript for the server
  • Run unit tests for services, tools, and utilities
  • Generate test coverage reports
  • Perform security test suites
  • Lint and format code using ESLint and Prettier
  • Start the MCP server in development mode

How it works

This skill provides a development guide for the File Organizer MCP server, outlining commands for building, testing, linting, and running the server, along with project structure and code patterns.

Inputs & outputs

You give it
TypeScript source files in src/
You get back
compiled JavaScript files in dist/

When to use file-organizer-dev

  • Add new MCP tools
  • Refactor file organization services
  • Run security tests
  • Maintain codebase

About this skill

File Organizer MCP - Development Guide

This skill helps developers work on the File Organizer MCP server codebase.

Quick Reference

Essential Commands

# Build
npm run build              # Compile TypeScript to dist/
npm run build:watch        # Watch mode
npm run clean              # Remove dist/

# Test
npm test                   # Run all tests
npm test -- tests/unit/services/organizer.test.ts  # Single test
npm run test:coverage      # With coverage
npm run test:security      # Security test suite

# Lint/Format
npm run lint               # ESLint
npm run lint:fix           # Auto-fix
npm run format             # Prettier

# Dev
npm run dev                # Build + start
npm run setup              # TUI setup wizard

Project Structure

src/
├── server.ts              # MCP server entry
├── index.ts               # Main entry
├── types.ts               # TypeScript types
├── constants.ts           # App constants
├── config.ts              # Config loader
├── errors.ts              # Error classes
├── services/              # Business logic
│   ├── path-validator.service.ts    # Security-critical
│   ├── organizer.service.ts         # File organization
│   ├── file-scanner.service.ts      # Directory scanning
│   ├── categorizer.service.ts       # File categorization
│   ├── hash-calculator.service.ts   # Duplicate detection
│   ├── rollback.service.ts          # Undo operations
│   └── metadata.service.ts          # EXIF/ID3 extraction
├── tools/                 # MCP tool implementations
│   ├── index.ts           # Tool registry
│   ├── file-organization.ts
│   ├── file-scanning.ts
│   ├── file-duplicates.ts
│   └── ...
├── schemas/               # Zod validation schemas
│   ├── common.schemas.ts
│   ├── security.schemas.ts
│   └── ...
└── utils/                 # Utilities
    ├── logger.ts
    ├── error-handler.ts
    ├── file-utils.ts
    └── formatters.ts

tests/
├── unit/services/         # Service unit tests
├── unit/tools/            # Tool unit tests
└── unit/utils/            # Utility tests

Adding a New MCP Tool

Step 1: Create Tool File

Create src/tools/my-feature.ts:

/**
 * File Organizer MCP Server
 * my_feature Tool
 */

import { z } from 'zod';
import type { ToolDefinition, ToolResponse } from '../types.js';
import { validateStrictPath } from '../services/path-validator.service.js';
import { createErrorResponse } from '../utils/error-handler.js';
import { CommonParamsSchema } from '../schemas/common.schemas.js';

// ==================== Schema ====================

export const MyFeatureInputSchema = z
  .object({
    directory: z
      .string()
      .min(1, 'Directory path cannot be empty')
      .describe('Full path to the directory'),
    some_param: z
      .boolean()
      .optional()
      .default(false)
      .describe('Description of param'),
  })
  .merge(CommonParamsSchema);

export type MyFeatureInput = z.infer<typeof MyFeatureInputSchema>;

// ==================== Tool Definition ====================

export const myFeatureToolDefinition: ToolDefinition = {
  name: 'file_organizer_my_feature',
  title: 'My Feature',
  description: 'What this tool does. Be descriptive for LLM understanding.',
  inputSchema: {
    type: 'object',
    properties: {
      directory: { type: 'string', description: 'Full path to the directory' },
      some_param: { type: 'boolean', description: 'What it does', default: false },
      response_format: { type: 'string', enum: ['json', 'markdown'], default: 'markdown' },
    },
    required: ['directory'],
  },
  annotations: {
    readOnlyHint: true,      // true if doesn't modify files
    destructiveHint: false,  // true if deletes/modifies files
    idempotentHint: true,    // true if running twice = same result
    openWorldHint: true,     // true if accesses filesystem
  },
};

// ==================== Handler ====================

export async function handleMyFeature(args: Record<string, unknown>): Promise<ToolResponse> {
  try {
    // 1. Validate input
    const parsed = MyFeatureInputSchema.safeParse(args);
    if (!parsed.success) {
      return {
        content: [
          { type: 'text', text: `Error: ${parsed.error.issues.map((i) => i.message).join(', ')}` },
        ],
      };
    }

    const { directory, some_param, response_format } = parsed.data;

    // 2. Validate path (SECURITY CRITICAL)
    const validatedPath = await validateStrictPath(directory);

    // 3. Call service layer
    // const result = await myService.doSomething(validatedPath, some_param);

    // 4. Format response
    if (response_format === 'json') {
      return {
        content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
        structuredContent: result as unknown as Record<string, unknown>,
      };
    }

    const markdown = `### My Feature Result

**Directory:** ${validatedPath}
**Param:** ${some_param}

Results here...`;

    return {
      content: [{ type: 'text', text: markdown }],
    };
  } catch (error) {
    // 5. Centralized error handling
    return createErrorResponse(error);
  }
}

Step 2: Register in Tool Index

Edit src/tools/index.ts:

// Add export
export {
  myFeatureToolDefinition,
  handleMyFeature,
  MyFeatureInputSchema,
} from './my-feature.js';
export type { MyFeatureInput } from './my-feature.js';

// Add to TOOLS array
export const TOOLS: ToolDefinition[] = [
  // ...existing tools
  myFeatureToolDefinition,
];

Step 3: Add to Server Router

Edit src/server.ts - add import and route:

import { handleMyFeature } from './tools/index.js';

async function handleToolCall(name: string, args: Record<string, unknown>) {
  switch (name) {
    // ...existing cases
    case 'file_organizer_my_feature':
      return handleMyFeature(args);
  }
}

Step 4: Write Tests

Create tests/unit/tools/my-feature.test.ts:

import fs from 'fs/promises';
import path from 'path';
import os from 'os';
import { handleMyFeature } from '../../../src/tools/my-feature.js';

describe('handleMyFeature', () => {
  let testDir: string;

  beforeEach(async () => {
    testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'test-myfeature-'));
  });

  afterEach(async () => {
    await fs.rm(testDir, { recursive: true, force: true });
  });

  it('should process directory successfully', async () => {
    const result = await handleMyFeature({
      directory: testDir,
      some_param: true,
      response_format: 'json',
    });

    expect(result.content[0].text).toContain('expected content');
  });

  it('should reject invalid paths', async () => {
    const result = await handleMyFeature({
      directory: '/invalid/path',
      response_format: 'json',
    });

    expect(result.content[0].text).toContain('Error');
  });
});

Adding a New Service

Create src/services/my-service.service.ts:

/**
 * My Service - Business logic for X
 */

import type { SomeType } from '../types.js';
import { logger } from '../utils/logger.js';

export interface MyServiceOptions {
  option1?: boolean;
  option2?: number;
}

export class MyService {
  constructor(private options: MyServiceOptions = {}) {}

  async doSomething(input: string): Promise<SomeType> {
    logger.debug('Doing something', { input });
    
    // Implementation
    
    return result;
  }
}

Security Guidelines (CRITICAL)

8-Layer Path Validation

Every file path MUST go through validation:

import { validateStrictPath } from '../services/path-validator.service.js';

// Basic validation
const validatedPath = await validateStrictPath(userInput);

// With options
import { validatePathBase } from '../services/path-validator.service.js';
const path = await validatePathBase(input, {
  basePath: '/base',
  allowedPaths: ['/allowed'],
  requireExists: true,
  checkWrite: true,
  allowSymlinks: false,
});

Security Rules

  1. Never trust user paths - Always validate
  2. Use O_NOFOLLOW - Prevent symlink attacks
  3. Atomic operations - Use COPYFILE_EXCL for race condition safety
  4. No path traversal - Block ../ sequences
  5. Windows reserved names - Block CON, PRN, AUX, NUL, COM1-9, LPT1-9
  6. Sanitize errors - Never expose internal paths in error messages

Safe File Operations

import { constants } from 'fs';

// Validate via file descriptor (TOCTOU protection)
const validator = new PathValidatorService();
const handle = await validator.openAndValidateFile(path);
// ... use handle ...
await handle.close();

// Atomic copy (prevents race conditions)
await fs.copyFile(source, dest, constants.COPYFILE_EXCL);

// Safe overwrite (backup first)
if (await fileExists(targetPath)) {
  const backupPath = path.join('.file-organizer-backups', `${Date.now()}_${basename}`);
  await fs.rename(targetPath, backupPath);
}

Testing Patterns

Unit Test Template

import { jest } from '@jest/globals';
import fs from 'fs/promises';
import path from 'path';
import os from 'os';
import { MyService } from '../../../src/services/my-service.service.js';

describe('MyService', () => {
  let service: MyService;
  let testDir: string;

  beforeEach(async () => {
    testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'test-'));
    service = new MyService();
  });

  afterEach(async () => {
    await new Promise((resolve) => setTimeout(resolve, 100)); // Windows cleanup
    await fs.rm(testDir, { recursive: true, force: true });
  });

  it('should do something correctly', async () => {
    // Arrange
    const input = 'test';
    
    // Act
    const result = await service.doSomething(input);
    
    // Assert
    expect(result).toBe(expected);
  });

  it('should handle errors gracefully', async () => {
    await expect(service.doSomething(invalidInput))
      .rejects.toThrow(ExpectedError);
  });
});

Security Test Pattern

describe('Securi

---

*Content truncated.*

When not to use it

  • When the task is not related to developing the File Organizer MCP server codebase
  • When the user needs to perform file operations outside the development context
  • When the task is not about adding, modifying, or testing code

Limitations

  • Path validation requires checking 'config.json' allowed directories
  • ESM import errors require using '.js' extension in imports
  • Windows file locks may require adding a delay before cleanup in tests

How it compares

This skill focuses on the development workflow and code structure of the File Organizer MCP server, providing specific commands and patterns for contributors rather than end-user functionality.

Compared to similar skills

file-organizer-dev side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
file-organizer-dev (this skill)06moReviewAdvanced
telegram-bot-builder1066moReviewIntermediate
workflow-orchestration-patterns102moNo flagsAdvanced
bullmq-specialist256moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

telegram-bot-builder

davila7

Expert in building Telegram bots that solve real problems - from simple automation to complex AI-powered bots. Covers bot architecture, the Telegram Bot API, user experience, monetization strategies, and scaling bots to thousands of users. Use when: telegram bot, bot api, telegram automation, chat bot telegram, tg bot.

106130

workflow-orchestration-patterns

wshobson

Design durable workflows with Temporal for distributed systems. Covers workflow vs activity separation, saga patterns, state management, and determinism constraints. Use when building long-running processes, distributed transactions, or microservice orchestration.

10117

bullmq-specialist

davila7

BullMQ expert for Redis-backed job queues, background processing, and reliable async execution in Node.js/TypeScript applications. Use when: bullmq, bull queue, redis queue, background job, job queue.

2595

workflow

vercel

Creates durable, resumable workflows using Vercel's Workflow DevKit. Use when building workflows that need to survive restarts, pause for external events, retry on failure, or coordinate multi-step operations over time. Triggers on mentions of "workflow", "durable functions", "resumable", "workflow devkit", or step-based orchestration.

431

trigger-dev

davila7

Trigger.dev expert for background jobs, AI workflows, and reliable async execution with excellent developer experience and TypeScript-first design. Use when: trigger.dev, trigger dev, background task, ai background job, long running task.

214

fullstack-guardian

Jeffallan

Use when implementing features across frontend and backend, building APIs with UI, or creating end-to-end data flows. Invoke for feature implementation, API development, UI building, cross-stack work.

15

Search skills

Search the agent skills registry