DEV Community

Nam Tran
Nam Tran

Posted on

Building an MCP Server: How We Connected DiskCleanKit to AI Assistants

Introduction

In my previous article, I explained what Model Context Protocol (MCP) is — the "USB-C for AI" that provides a universal way to connect AI assistants to external tools and applications.

Today, I'm excited to share how we implemented MCP in DiskCleanKit 5.0, allowing AI assistants like Claude, Cursor, and VS Code Copilot to scan and clean your Mac automatically.

What you'll learn:

  • How MCP works under the hood
  • Step-by-step guide to building your own MCP server
  • Real-world implementation patterns
  • Publishing to npm and MCP Registry

Resources:


What is MCP? (Quick Recap)

MCP (Model Context Protocol) is an open-source standard created by Anthropic that allows AI assistants to interact with external tools, data sources, and applications.

┌─────────────────────────────────────────────────────────────────┐
│                    Before MCP                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Claude ──── Custom Code ──── Your App                         │
│   ChatGPT ─── Different Code ── Your App                        │
│   Copilot ─── Another Code ──── Your App                        │
│                                                                 │
│   😫 Every AI needs custom integration                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                    After MCP                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Claude ────┐                                                  │
│   Cursor ────┼──── MCP Server ──── Your App                     │
│   Copilot ───┘                                                  │
│                                                                 │
│   😊 One integration works with all AI assistants               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

DiskCleanKit MCP Architecture

Here's how our MCP server connects AI assistants to DiskCleanKit:

┌─────────────────────────────────────────────────────────────────┐
│                    DiskCleanKit MCP Flow                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   User: "Clean up my Mac"                                       │
│            │                                                    │
│            ▼                                                    │
│   ┌─────────────────┐                                           │
│   │  AI Assistant   │  (Claude, Cursor, Copilot)                │
│   │  Understands    │                                           │
│   │  user intent    │                                           │
│   └────────┬────────┘                                           │
│            │ Calls MCP tool: one_touch_clean                    │
│            ▼                                                    │
│   ┌─────────────────┐                                           │
│   │   MCP Server    │  (@vannamtran/diskcleankit-mcp)           │
│   │   Node.js       │                                           │
│   └────────┬────────┘                                           │
│            │ URL Scheme: diskcleankit://one-touch-clean         │
│            ▼                                                    │
│   ┌─────────────────┐                                           │
│   │  DiskCleanKit   │  (macOS App)                              │
│   │  Native App     │                                           │
│   └────────┬────────┘                                           │
│            │ Writes result to temp file                         │
│            ▼                                                    │
│   ┌─────────────────┐                                           │
│   │  Response JSON  │  /tmp/diskcleankit_mcp_response.json      │
│   └────────┬────────┘                                           │
│            │                                                    │
│            ▼                                                    │
│   AI: "I cleaned 2.5GB of cache files from your Mac! 🎉"        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Step-by-Step: Building an MCP Server

Step 1: Project Setup

mkdir my-mcp-server
cd my-mcp-server
npm init -y
Enter fullscreen mode Exit fullscreen mode

Install dependencies:

npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
Enter fullscreen mode Exit fullscreen mode

package.json:

{
  "name": "@yourname/my-mcp-server",
  "version": "1.0.0",
  "type": "module",
  "bin": {
    "my-mcp-server": "./dist/index.js"
  },
  "files": ["dist"],
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0",
    "zod": "^3.22.0"
  },
  "devDependencies": {
    "typescript": "^5.3.0",
    "@types/node": "^20.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "declaration": true
  },
  "include": ["src/**/*"]
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Create the MCP Server

src/index.ts:

#!/usr/bin/env node

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Create server instance
const server = new McpServer({
  name: "my-mcp-server",
  version: "1.0.0",
});

// Define your tools
server.tool(
  "hello_world",
  "Says hello to the user",
  {
    name: z.string().describe("Name to greet"),
  },
  async ({ name }) => {
    return {
      content: [
        {
          type: "text",
          text: `Hello, ${name}! 👋`,
        },
      ],
    };
  }
);

// Start the server
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("MCP Server running on stdio");
}

main().catch(console.error);
Enter fullscreen mode Exit fullscreen mode

Step 3: How DiskCleanKit Implements Tools

Here's how we implemented the actual tools in DiskCleanKit MCP:

// Tool: one_touch_scan
server.tool(
  "one_touch_scan",
  "Scan Mac for junk files without deleting anything",
  {},
  async () => {
    // 1. Trigger the app via URL scheme
    const { exec } = await import("child_process");
    exec("open 'diskcleankit://one-touch-scan'");

    // 2. Wait for response file
    const response = await waitForResponse();

    // 3. Return result to AI
    return {
      content: [
        {
          type: "text",
          text: JSON.stringify(response, null, 2),
        },
      ],
    };
  }
);

// Tool: one_touch_clean
server.tool(
  "one_touch_clean",
  "Scan AND clean junk files automatically (safe items only)",
  {},
  async () => {
    const { exec } = await import("child_process");
    exec("open 'diskcleankit://one-touch-clean'");

    const response = await waitForResponse();

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

// Tool: get_disk_status
server.tool(
  "get_disk_status",
  "Check disk space and health status",
  {},
  async () => {
    const { exec } = await import("child_process");
    exec("open 'diskcleankit://disk-status'");

    const response = await waitForResponse();

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

// Helper: Wait for app response
async function waitForResponse(timeout = 30000): Promise<any> {
  const responseFile = "/tmp/diskcleankit_mcp_response.json";
  const startTime = Date.now();

  while (Date.now() - startTime < timeout) {
    try {
      const fs = await import("fs/promises");
      const data = await fs.readFile(responseFile, "utf-8");
      await fs.unlink(responseFile); // Clean up
      return JSON.parse(data);
    } catch {
      // File not ready yet, wait and retry
      await new Promise((resolve) => setTimeout(resolve, 500));
    }
  }

  throw new Error("Timeout waiting for DiskCleanKit response");
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Communication via URL Schemes

The key to connecting Node.js MCP server to a native macOS app is URL schemes.

In your macOS app (Swift):

// Register URL scheme in Info.plist
// <key>CFBundleURLSchemes</key>
// <array><string>diskcleankit</string></array>

// Handle URL in AppDelegate
func application(_ application: NSApplication, 
                 open urls: [URL]) {
    guard let url = urls.first else { return }

    switch url.host {
    case "one-touch-scan":
        performScan()
    case "one-touch-clean":
        performClean()
    case "disk-status":
        getDiskStatus()
    default:
        break
    }
}

// Write response for MCP server
func writeResponse(_ result: [String: Any]) {
    let path = "/tmp/diskcleankit_mcp_response.json"
    let data = try? JSONSerialization.data(withJSONObject: result)
    try? data?.write(to: URL(fileURLWithPath: path))
}
Enter fullscreen mode Exit fullscreen mode

Publishing Your MCP Server

Step 1: Build the Project

npm run build
Enter fullscreen mode Exit fullscreen mode

Step 2: Add Shebang to Entry File

Make sure dist/index.js starts with:

#!/usr/bin/env node
Enter fullscreen mode Exit fullscreen mode

Step 3: Publish to npm

npm login
npm publish --access public
Enter fullscreen mode Exit fullscreen mode

Step 4: Register with MCP Registry

Create a server.json file:

{
  "$schema": "https://registry.modelcontextprotocol.io/schema/server.json",
  "name": "@vannamtran/diskcleankit-mcp",
  "description": "MCP server for DiskCleanKit - One Touch Scan and Clean for Mac",
  "repository": {
    "type": "git",
    "url": "https://github.com/namtran/diskcleankit-mcp"
  },
  "version_detail": {
    "version": "1.0.0",
    "release_date": "2025-01-01",
    "is_latest": true
  },
  "packages": [
    {
      "registry_name": "npm",
      "name": "@vannamtran/diskcleankit-mcp",
      "version": "1.0.0"
    }
  ],
  "tools": [
    {
      "name": "one_touch_scan",
      "description": "Scan Mac for junk files (safe, no deletion)"
    },
    {
      "name": "one_touch_clean",
      "description": "Scan AND clean junk files automatically"
    },
    {
      "name": "get_disk_status",
      "description": "Check disk space and health"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Submit to the registry by creating a PR to github.com/modelcontextprotocol/registry.


Configuring AI Clients

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "diskcleankit": {
      "command": "npx",
      "args": ["@vannamtran/diskcleankit-mcp"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Claude Code (CLI)

claude mcp add diskcleankit -- npx @vannamtran/diskcleankit-mcp
Enter fullscreen mode Exit fullscreen mode

Cursor

Add to ~/.cursor/mcp.json:

{
  "mcpServers": {
    "diskcleankit": {
      "command": "npx",
      "args": ["@vannamtran/diskcleankit-mcp"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

VS Code (GitHub Copilot)

Add to settings.json:

{
  "mcp.servers": {
    "diskcleankit": {
      "command": "npx",
      "args": ["@vannamtran/diskcleankit-mcp"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Usage Examples

Once configured, you can ask your AI assistant:

User Says AI Calls Result
"Scan my Mac for junk" one_touch_scan Shows cleanable items
"Clean up my Mac" one_touch_clean Removes cache/logs
"How much disk space do I have?" get_disk_status Shows storage info
"What can be cleaned?" get_cleanable_estimate Estimates by category
"Show cleaning history" get_cleaning_history Past sessions

Example conversation:

User: My Mac is running low on storage. Can you help?

Claude: I'll check your disk status and scan for cleanable files.

[Calls get_disk_status]
[Calls one_touch_scan]

Your Mac has 45GB free out of 500GB. I found 8.2GB of cleanable 
files:
- System Caches: 3.1GB
- Application Caches: 2.8GB
- Browser Caches: 1.5GB
- Log Files: 0.8GB

Would you like me to clean these? They're all safe to remove and 
will be regenerated as needed.

User: Yes, please clean them.

Claude: [Calls one_touch_clean]

Done! I've cleaned 8.2GB of cache and log files. Your Mac now 
has 53.2GB free. 🎉
Enter fullscreen mode Exit fullscreen mode

Security Considerations

When building MCP servers, security is crucial:

Practice Description
Principle of Least Privilege Only expose necessary tools
Safe by Default one_touch_scan doesn't delete anything
Clear Tool Names Users understand what each tool does
No External Data All processing happens locally
Safe Items Only Only clean regenerable files (caches, logs)

What DiskCleanKit MCP cleans:

Category Safe? Regenerable?
System Caches ✅ Yes ✅ Yes
Application Caches ✅ Yes ✅ Yes
Browser Caches ✅ Yes ✅ Yes
Log Files ✅ Yes ✅ Yes
Personal Documents ❌ Never touched -
Photos/Videos ❌ Never touched -

Common Patterns for MCP Servers

Pattern 1: URL Scheme (Desktop Apps)

MCP Server → URL Scheme → Native App → Temp File → MCP Server
Enter fullscreen mode Exit fullscreen mode

Best for: macOS/Windows desktop applications

Pattern 2: Local HTTP API

MCP Server → HTTP Request → Local Server → HTTP Response
Enter fullscreen mode Exit fullscreen mode

Best for: Apps with REST APIs

Pattern 3: IPC (Inter-Process Communication)

MCP Server → Named Pipe/Socket → Native Process
Enter fullscreen mode Exit fullscreen mode

Best for: Background services

Pattern 4: Direct File System

MCP Server → Read/Write Files Directly
Enter fullscreen mode Exit fullscreen mode

Best for: File management tools


Troubleshooting

"Timeout waiting for response"

# Check if DiskCleanKit is installed
ls /Applications/DiskCleanKit.app

# Try opening manually
open "diskcleankit://disk-status"
Enter fullscreen mode Exit fullscreen mode

"Server not appearing in Claude"

  1. Verify config path is correct
  2. Restart Claude Desktop
  3. Check logs: ~/Library/Logs/Claude/mcp*.log

"Permission denied"

# Make the script executable
chmod +x ./dist/index.js
Enter fullscreen mode Exit fullscreen mode

What's Next?

MCP is still evolving. Here's what we're excited about:

  • More AI Clients: Expecting ChatGPT support soon
  • Remote MCP Servers: HTTP-based servers (not just stdio)
  • Richer Tool Types: Beyond just function calls
  • Better Discovery: Improved MCP Registry features

Conclusion

Building an MCP server is straightforward once you understand the pattern:

  1. Create a Node.js server using @modelcontextprotocol/sdk
  2. Define tools that AI assistants can call
  3. Connect to your app via URL schemes, HTTP, or IPC
  4. Publish to npm and register with MCP Registry
  5. Configure AI clients with a simple JSON config

MCP opens up exciting possibilities for AI-powered applications. Your desktop app can now be controlled by natural language through any MCP-compatible AI assistant.

Try it yourself:

npm install -g @vannamtran/diskcleankit-mcp
Enter fullscreen mode Exit fullscreen mode

Resources


Have you built an MCP server? Share your experience in the comments!


Tags: #mcp #ai #typescript #macos #tutorial

Top comments (0)