INTRODUCE.md•17.3 kB
# 保姆级教程!使用 Cursor 开发 MCP 服务器完整指南
## 📖 项目背景
作为前端开发者,我们经常需要处理大量图片资源。无论是设计师提供的素材还是从 Figma 下载的图片,通常尺寸都较大,严重影响网页加载性能。因此,在开发过程中压缩图片是必要的优化步骤。
### 现有方案的痛点
- **手动操作繁琐**:压缩网站需要逐个上传,限制单次处理数量
- **协作不便**:团队成员压缩后的图片缺乏同步,容易重复处理影响质量
- **技术门槛**:虽然可以通过脚本批量处理,但对有同样需求的非技术同事操作困难
本 MCP 服务器正是为了解决上述问题而开发,提供便捷的图片批量压缩解决方案。
---
## 🛠️ 开发环境要求
| 工具 | 版本要求 | 说明 |
|------|----------|------|
| **Node.js** | 22.14.0+ | 推荐使用指定版本 |
| **Cursor** | 1.0.0+ | 使用 Claude-4-Sonnet MAX 模式 |
| **Agent Mode** | Claude-4-Sonnet | 最佳代码生成体验 |
---
## 📚 参考文档
- [MCP Node.js SDK](https://www.npmjs.com/package/@modelcontextprotocol/sdk)
- [TinyPNG API SDK](https://www.npmjs.com/package/tinify)
---
## 🚀 项目搭建步骤
### Step 1: 创建项目结构
```bash
mkdir joyme-mcp-tinify-image
cd joyme-mcp-tinify-image
```
### Step 2: 初始化 Node.js 项目
```bash
npm init
```
按提示填写项目基础信息:
- 项目名称、描述、版本号
- 作者信息、开源协议等
---
## 🎯 开始使用 Cursor 开发
> 🎉 **从这里开始,让 Cursor 成为你的编程助手!**
### Step 3: 配置 Cursor Rules
优秀的工具配置是高效开发的基础。Rules 和 Prompts 是 Cursor 的两大核心功能。
#### 获取 Rules 的方式:
- 🔍 浏览 [cursor.directory](https://cursor.directory/) 寻找适合的技术栈规则
- 🤖 直接让 Cursor 生成定制化规则
#### 这里我们使用 Prompt 让 Cursor 给我们生成 Rules:
```
写一段rules,告诉agent精通nodejs和typescript,需要适配当前流行的javascript代码风格,
包括但不局限于文件/命名规则/eslint规则。rules需要添加参考资料
https://www.npmjs.com/package/@modelcontextprotocol/sdk 和
https://tinypng.com/developers/reference/nodejs
```
<details>
<summary>💡 点击查看生成的 Rules 配置</summary>
```markdown
# MCP Agent Rules - Node.js & TypeScript Expert
## Core Competencies
This agent is an expert in **Node.js** and **TypeScript** development, with deep understanding of:
- Modern JavaScript (ES2020+) and TypeScript features
- Node.js runtime and ecosystem
- Asynchronous programming patterns (async/await, Promises)
- Package management with npm/yarn/pnpm
- Build tools and bundlers
- Testing frameworks and methodologies
## Code Style & Standards
### File Structure & Naming Conventions
**File Naming:**
- Use `kebab-case` for file and directory names: `user-service.ts`, `image-processor.js`
- TypeScript files: `.ts` for modules, `.d.ts` for type definitions
- Test files: `*.test.ts`, `*.spec.ts`
- Configuration files: `*.config.js/ts`
**Export/Import Conventions:**
- Prefer named exports over default exports for better tree-shaking
- Use barrel exports (`index.ts`) for clean module interfaces
- Import ordering: external libraries → internal modules → types
### TypeScript Best Practices
**Type Definitions:**
- Use `interface` for object shapes, `type` for unions/intersections
- Prefer `const assertions` for immutable data
- Use generic constraints appropriately: `<T extends BaseType>`
- Avoid `any`, prefer `unknown` or proper typing
**Code Organization:**
- Separate types into dedicated `types/` directory
- Use module declaration merging sparingly
- Implement proper error types extending `Error`
**Formatting Standards:**
- 2-space indentation
- Single quotes for strings
- Trailing commas in multiline structures
- No semicolons
- Line length: 100 characters
**Error Handling:**
- Use custom error classes
- Implement proper error boundaries
- Leverage `cause` property for error chaining
**Functional Programming:**
- Prefer immutable operations
- Use array methods (`map`, `filter`, `reduce`) over loops
- Implement pure functions where possible
## Package & Dependency Management
**package.json Structure:**
- Use semantic versioning
- Separate `dependencies` from `devDependencies`
- Include proper scripts for common tasks
- Use `engines` field to specify Node.js version requirements
**Dependencies:**
- Prefer well-maintained, popular packages
- Keep dependencies minimal and updated
- Use exact versions for production dependencies
## Reference Resources
### Model Context Protocol (MCP)
**Documentation:** https://www.npmjs.com/package/@modelcontextprotocol/sdk
Key concepts to understand:
- MCP server/client architecture
- Protocol message handling
- Resource and tool definitions
- Type-safe implementations
### TinyPNG API Integration
**Documentation:** https://tinypng.com/developers/reference/nodejs
Essential patterns:
- API key management and security
- Error handling for API calls
- File upload/download operations
- Rate limiting and quota management
## Development Workflow
**Project Setup:**
1. Initialize with `npm init` or appropriate template
2. Configure TypeScript with strict settings
3. Set up ESLint + Prettier
4. Configure testing framework (Jest/Vitest)
5. Implement CI/CD pipeline
**Code Quality:**
- Implement integration tests for external APIs
- Use type-safe configurations
- Document public APIs with JSDoc
**Performance Considerations:**
- Use streaming for large file operations
- Implement proper caching strategies
- Monitor memory usage and optimize accordingly
- Use compression and bundling appropriately
## Security Best Practices
- Never commit API keys or secrets
- Use environment variables for configuration
- Validate all inputs and sanitize outputs
- Implement proper authentication/authorization
- Keep dependencies updated and audit regularly
This agent should consistently apply these rules while maintaining flexibility to adapt to project-specific requirements and team preferences.
```
</details>
---
### Step 4: 配置 ESLint 代码规范
良好的代码规范确保团队协作的一致性。
#### Prompt:
```
为项目增加eslint规则/文件,符合当前代码流行趋势,例如tab=2,不使用分号,
优先单引号,只允许JSDoc风格的注释等。只需要eslint,不要prettier以及其他规则。
```
**✅ 执行结果:** Cursor 成功创建了 `.eslintrc.json` 配置文件
---
### Step 5: 配置编辑器自动格式化
添加 ESLint 还不够,还需要配置编辑器实现自动格式化,避免手动修改代码格式。
#### Prompt:
```
在编辑器中保存代码时请执行eslint fix操作,禁用此项目prettier,
禁用项目formatOnSave操作。并将这些配置添加到.vscode/setting.json。
```
**✅ 执行结果:** 现在使用 `Ctrl + S` 保存时,编辑器会自动格式化代码
---
### Step 6: 实现核心业务逻辑
完成基础框架搭建后,开始开发具体功能。
#### 创建 MCP 服务器
**Prompt:**
```
@https://www.npmjs.com/package/@modelcontextprotocol/sdk 根据官方文档帮我实现以下需求。
in src folder, create index.ts, in this file, you will help me create an mcp server,
name: joyme-mcp-tinify-image; version: 1.0.0;
then start receiving messages on stdin and sending message on stdout.
```
<details>
<summary>💡 点击查看生成的核心代码</summary>
```typescript
/**
* MCP Server for TinyPNG image optimization.
* This server provides tools and resources for image compression using TinyPNG API.
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
/**
* Main function to initialize and start the MCP server.
* Sets up the server with stdio transport for communication.
*/
async function main(): Promise<void> {
/**
* Create an MCP server instance with project-specific configuration.
*/
const server = new McpServer({
name: 'joyme-mcp-tinify-image',
version: '1.0.0',
})
/**
* Add a basic ping tool for testing server connectivity.
* This tool simply returns a pong response to verify the server is working.
*/
server.tool(
'ping',
{},
async () => ({
content: [{ type: 'text', text: 'pong' }],
})
)
/**
* Create stdio transport for communication via stdin/stdout.
* This allows the server to receive messages from stdin and send responses to stdout.
*/
const transport = new StdioServerTransport()
/**
* Connect the server to the transport and start listening for messages.
* This establishes the MCP protocol communication channel.
*/
await server.connect(transport)
console.error('joyme-mcp-tinify-image server started and listening on stdio')
}
/**
* Start the server and handle any initialization errors.
* Ensures proper error handling and logging for debugging.
*/
main().catch((error) => {
console.error('Failed to start MCP server:', error)
process.exit(1)
})
```
</details>
> ⚠️ **注意:** 这里Cursor老毛病又犯了,首次生成的代码包含了无需的额外功能(如 `server.tool` 和 `server.resource`),最终根据实际需求进行调整。
---
### Step 7: 实现图片压缩工具
实现项目的核心功能 - 图片批量压缩。
#### Prompt:
```
first, we require user who should provide their tinify_api_key;
then create a tool named 'minify_image', the tool will accept a parameter named directoryPath which is string;
then loop the every image in directory, use node fs module read the file,
then use tinify@https://tinypng.com/developers/reference/nodejs to compress the current image,
in the last, use fs module to cover the origin image with same name.
```
<details>
<summary>💡 点击查看核心实现代码</summary>
```typescript
/**
* Add image minification tool.
* This tool compresses all images in a specified directory using TinyPNG API.
*/
server.tool(
'minify_image',
{
tinify_api_key: z.string().describe('Your TinyPNG API key'),
directoryPath: z.string().describe('Path to directory containing images to compress'),
},
async ({ tinify_api_key, directoryPath }) => {
try {
// Validate the API key first
await validateApiKey(tinify_api_key)
// Check if directory exists
if (!fs.existsSync(directoryPath)) {
throw new Error(`Directory does not exist: ${directoryPath}`)
}
// Get all image files in the directory
const imageFiles = await getImageFiles(directoryPath)
if (imageFiles.length === 0) {
return {
content: [{
type: 'text',
text: `No supported image files found in directory: ${directoryPath}`,
}],
}
}
// Compress each image
const results: string[] = []
let successCount = 0
let errorCount = 0
for (const imagePath of imageFiles) {
try {
const originalSize = (await fs.promises.stat(imagePath)).size
await compressImage(imagePath)
const compressedSize = (await fs.promises.stat(imagePath)).size
const savings = ((originalSize - compressedSize) / originalSize * 100).toFixed(1)
results.push(`✅ ${path.basename(imagePath)}: ${originalSize} → ${compressedSize} bytes (${savings}% reduction)`)
successCount++
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
results.push(`❌ ${path.basename(imagePath)}: ${errorMessage}`)
errorCount++
}
}
// Add compression statistics
results.push(`\n📊 Total compressions used this month: ${tinify.compressionCount}`)
results.push(`✅ Successfully compressed: ${successCount} images`)
if (errorCount > 0) {
results.push(`❌ Failed to compress: ${errorCount} images`)
}
return {
content: [{
type: 'text',
text: results.join('\n'),
}],
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
return {
content: [{
type: 'text',
text: `❌ Error: ${errorMessage}`,
}],
}
}
}
)
```
</details>
#### 🔧 优化 API Key 管理
初始实现将 `tinify_api_key` 作为工具参数,这会导致用户每次调用都需要输入密钥,体验不佳且不安全。
**优化方案:**
```
tinify_api_key 应该是放在mcp.json env的参数,而不是tool中的参数。
```
**✅ 优化结果:** API 密钥通过环境变量管理,在服务器启动时验证,提升安全性和用户体验。
---
## 🧪 本地测试
### Step 8: 构建项目
```bash
npm run build
```
编译后的文件位于 `build/index.js`
### Step 9: 配置 Cursor MCP
在 Cursor 中添加自定义 MCP 服务器:
**路径:** `Cursor Settings` → `MCP Tools` → `Add Custom MCP Server`
**配置示例:**
```json
{
"mcpServers": {
"joyme-mcp-tinify-image": {
"command": "node",
"args": [
"/Users/dengjingwen/projects/joyme-mcp-tinify-image/build/index.js"
],
"env": {
"TINIFY_API_KEY": "你的_TinyPNG_API_密钥"
}
}
}
}
```
> ⚠️ **重要:** `args` 中的路径必须是你本地的绝对路径
### Step 10: 测试功能
在 Cursor 对话框中使用 Agent 模式:
```
@/images/login minify images
```
**执行结果示例:**
| 图片文件 | 原始大小 | 压缩后大小 | 压缩率 |
|----------|----------|------------|--------|
| icon-act.png | 12.6KB | 3.5KB | 72.1% |
| icon-back.png | 1.1KB | 546B | 51.3% |
| icon-clear.png | 456B | 269B | 41.0% |
| icon-eye-close.png | 690B | 341B | 50.6% |
| icon-eye-open.png | 1.2KB | 494B | 58.5% |
**📊 压缩总结:**
- 处理图片总数:8 个 PNG 文件
- 最佳压缩效果:icon-act.png 减少 72.1%
- 总计节省空间:10KB+
- 图片质量保持不变,显著提升加载性能
---
## 📦 发布到 NPM
### Step 11: 完善项目文档
发布前需要补充 README.md,详细介绍项目技术栈和使用方法。
### Step 12: 发布到NPM仓库
我们可以把包发布到公开的官网NPM,但我们选择发布到 JoyMe 私有 NPM 仓库:
```bash
# 登录私有仓库
npm login --registry=http://npm.joyme.sg
# 输入你的 JoyMe 账号密码(用户名不需要 @joyme.sg 后缀,例如dengjingwen)
# 发布包
npm publish
```
**✅ 发布成功!** 访问 [http://npm.joyme.sg](http://npm.joyme.sg) 查看已发布的包。
[@joyme/mcp-tinify-image地址](http://npm.joyme.sg/-/web/detail/@joyme/mcp-tinify-image)
---
## 🎯 总结
通过本教程,我们成功使用 Cursor 开发了一个完整的 MCP 图片压缩服务器,主要收获:
### 技术亮点
- ✅ **MCP 协议集成** - 实现标准化的工具通信
- ✅ **TinyPNG API 集成** - 高质量图片压缩
- ✅ **TypeScript 最佳实践** - 类型安全的代码开发
- ✅ **环境变量管理** - 安全的 API 密钥处理
### 开发体验
- 🚀 **AI 辅助开发** - Cursor 大幅提升开发效率
- 📝 **代码规范自动化** - ESLint 确保代码质量
- 🔧 **一键格式化** - 保存时自动修复代码格式
- 📚 **完整的文档** - JSDoc 注释确保代码可维护性
### 实际价值
- 🎯 **解决实际痛点** - 批量图片压缩自动化
- 👥 **团队协作友好** - 技术和非技术人员都能使用
- ⚡ **性能优化** - 显著减少图片文件大小
- 🔒 **安全可靠** - 私有 NPM 仓库部署
### 待开发内容
- TODO 本项目还未实现重复压缩的问题,大致解决方案是在文件内新增json文件,记录已压缩的图片。
- TODO 目前只实现了tinify的compress功能,其他resize/converting images待开发。
## Cursor开发总结
这个项目展示了 AI 辅助开发的强大能力,让我们能够专注于业务逻辑和用户体验,而不是繁琐的代码实现细节。但是仍有许多问题
- 本文上述步骤展示的只是Cursor最理想的情况,其实很多代码细节都是经过手动调整。
- 有很多步骤其实是为了Cursor而Cursor,很多配置/初始/业务强相关代码自己去写还会更快,让Cursor去弄反而还需要多次调整。所以还是得多用,找到Cursor的甜点区,找到适合他的场景,即效率/经济性达到完美平衡。
- 大型任务应拆分成小任务让Cursor分部完成。
- “如果不知道答案,老实回答,别乱猜”,“不要实现提示词以外的功能”。Cursor真的很喜欢乱改文件!!!
- 提示词精准性,一般来说, English >= 中文,当然用最适合自己的,只要能够精准描述需求。因为很多开发文档只有英文的,直接copy下来问,不要先翻译。
[Joyme MCP Tinify Image Gitlab地址](https://git.joyme.sg/liveme_fe/joyme-MCP-tinify-image)
[@joyme/mcp-tinify-image NPM地址](http://npm.joyme.sg/-/web/detail/@joyme/mcp-tinify-image)
完结撒花,感谢各位阅读!