mimo-vision-claude 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -0
- package/dist/client.d.ts +43 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +114 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +173 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/analyze-screenshot.d.ts +26 -0
- package/dist/tools/analyze-screenshot.d.ts.map +1 -0
- package/dist/tools/analyze-screenshot.js +35 -0
- package/dist/tools/analyze-screenshot.js.map +1 -0
- package/dist/tools/compare-images.d.ts +30 -0
- package/dist/tools/compare-images.d.ts.map +1 -0
- package/dist/tools/compare-images.js +39 -0
- package/dist/tools/compare-images.js.map +1 -0
- package/dist/tools/describe-image.d.ts +23 -0
- package/dist/tools/describe-image.d.ts.map +1 -0
- package/dist/tools/describe-image.js +20 -0
- package/dist/tools/describe-image.js.map +1 -0
- package/dist/tools/ocr-image.d.ts +27 -0
- package/dist/tools/ocr-image.d.ts.map +1 -0
- package/dist/tools/ocr-image.js +37 -0
- package/dist/tools/ocr-image.js.map +1 -0
- package/dist/tools/tool-types.d.ts +14 -0
- package/dist/tools/tool-types.d.ts.map +1 -0
- package/dist/tools/tool-types.js +5 -0
- package/dist/tools/tool-types.js.map +1 -0
- package/dist/utils/config.d.ts +22 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +28 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/image.d.ts +44 -0
- package/dist/utils/image.d.ts.map +1 -0
- package/dist/utils/image.js +80 -0
- package/dist/utils/image.js.map +1 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# MiMo Vision MCP Server
|
|
2
|
+
|
|
3
|
+
> 封装小米 MiMo 全模态大模型视觉 API,为 Claude Code 等 MCP 客户端提供图片识别(识图)能力。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- **通用图片描述** (`describe_image`) — 对任意图片进行内容描述,支持自定义提示词
|
|
8
|
+
- **OCR 文字提取** (`ocr_image`) — 从图片中提取文字,支持中/英/日/韩/自动检测
|
|
9
|
+
- **双图对比分析** (`compare_images`) — 对比两张图片的异同,多维度分析
|
|
10
|
+
- **截图/UI 分析** (`analyze_screenshot`) — 分析 UI 截图,评估布局、可用性、可访问性
|
|
11
|
+
|
|
12
|
+
## 快速开始
|
|
13
|
+
|
|
14
|
+
### 前置条件
|
|
15
|
+
|
|
16
|
+
- Node.js >= 18
|
|
17
|
+
- MiMo 平台 API Key
|
|
18
|
+
|
|
19
|
+
### 安装运行
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# 设置 API Key
|
|
23
|
+
export MIMO_API_KEY=your-api-key-here
|
|
24
|
+
|
|
25
|
+
# 通过 npx 一键运行
|
|
26
|
+
npx -y mimo-vision-claude
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Claude Code 集成
|
|
30
|
+
|
|
31
|
+
在 `settings.json` 的 `mcpServers` 中添加:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"mcpServers": {
|
|
36
|
+
"mimo-vision": {
|
|
37
|
+
"command": "npx",
|
|
38
|
+
"args": ["-y", "mimo-vision-mcp"],
|
|
39
|
+
"env": {
|
|
40
|
+
"MIMO_API_KEY": "${MIMO_API_KEY}"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 配置
|
|
48
|
+
|
|
49
|
+
| 环境变量 | 必填 | 默认值 | 说明 |
|
|
50
|
+
|----------|------|--------|------|
|
|
51
|
+
| `MIMO_API_KEY` | ✅ | — | MiMo 平台 API Key |
|
|
52
|
+
| `MIMO_BASE_URL` | ❌ | `https://api.xiaomimimo.com/v1` | 自定义 API 端点 |
|
|
53
|
+
| `MIMO_MODEL` | ❌ | `mimo-v2-omni` | 默认模型 |
|
|
54
|
+
| `MIMO_MAX_TOKENS` | ❌ | `4096` | 单次最大输出 token |
|
|
55
|
+
|
|
56
|
+
## 工具详情
|
|
57
|
+
|
|
58
|
+
### describe_image
|
|
59
|
+
|
|
60
|
+
对任意图片进行内容描述。
|
|
61
|
+
|
|
62
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
63
|
+
|------|------|------|--------|------|
|
|
64
|
+
| `image_path` | string | ✅ | — | 本地文件绝对路径或 HTTP/HTTPS URL |
|
|
65
|
+
| `prompt` | string | ❌ | `"请详细描述这张图片的内容"` | 自定义分析指令 |
|
|
66
|
+
|
|
67
|
+
### ocr_image
|
|
68
|
+
|
|
69
|
+
从图片中提取文字。
|
|
70
|
+
|
|
71
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
72
|
+
|------|------|------|--------|------|
|
|
73
|
+
| `image_path` | string | ✅ | — | 图片路径或 URL |
|
|
74
|
+
| `language` | string | ❌ | `"zh"` | 偏好语言:`zh`/`en`/`ja`/`ko`/`auto` |
|
|
75
|
+
|
|
76
|
+
### compare_images
|
|
77
|
+
|
|
78
|
+
对比两张图片的异同。
|
|
79
|
+
|
|
80
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
81
|
+
|------|------|------|--------|------|
|
|
82
|
+
| `image1` | string | ✅ | — | 第一张图片路径/URL |
|
|
83
|
+
| `image2` | string | ✅ | — | 第二张图片路径/URL |
|
|
84
|
+
| `aspect` | string | ❌ | `"general"` | 对比维度:`general`/`differences`/`similarity`/`which_is_better` |
|
|
85
|
+
|
|
86
|
+
### analyze_screenshot
|
|
87
|
+
|
|
88
|
+
分析 UI 截图。
|
|
89
|
+
|
|
90
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
91
|
+
|------|------|------|--------|------|
|
|
92
|
+
| `image_path` | string | ✅ | — | 截图路径/URL |
|
|
93
|
+
| `focus` | string | ❌ | `"all"` | 分析焦点:`layout`/`usability`/`accessibility`/`all` |
|
|
94
|
+
|
|
95
|
+
## 开发
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# 安装依赖
|
|
99
|
+
npm install
|
|
100
|
+
|
|
101
|
+
# 编译
|
|
102
|
+
npm run build
|
|
103
|
+
|
|
104
|
+
# 测试
|
|
105
|
+
npm test
|
|
106
|
+
|
|
107
|
+
# 开发模式(监听编译)
|
|
108
|
+
npm run dev
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## 许可证
|
|
112
|
+
|
|
113
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MiMo API 客户端 — 封装 OpenAI SDK 实例,提供统一的视觉 API 调用接口
|
|
3
|
+
*
|
|
4
|
+
* MiMo API 完全兼容 OpenAI chat/completions 格式,
|
|
5
|
+
* 因此可以直接使用 OpenAI SDK 进行调用。
|
|
6
|
+
*/
|
|
7
|
+
/** 视觉 API 调用参数 */
|
|
8
|
+
export interface VisionRequest {
|
|
9
|
+
/** 用户提示词 */
|
|
10
|
+
prompt: string;
|
|
11
|
+
/** 图片 URL(支持 https:// URL 或 data: URL) */
|
|
12
|
+
imageUrl: string;
|
|
13
|
+
/** 可选:第二张图片 URL(用于对比) */
|
|
14
|
+
imageUrl2?: string;
|
|
15
|
+
/** 可选:覆盖默认模型 */
|
|
16
|
+
model?: string;
|
|
17
|
+
/** 可选:覆盖默认 max_tokens */
|
|
18
|
+
maxTokens?: number;
|
|
19
|
+
}
|
|
20
|
+
/** 视觉 API 返回结果 */
|
|
21
|
+
export interface VisionResponse {
|
|
22
|
+
/** AI 生成的文本内容 */
|
|
23
|
+
content: string;
|
|
24
|
+
/** 实际使用的模型名 */
|
|
25
|
+
model: string;
|
|
26
|
+
/** 消耗的 token 数 */
|
|
27
|
+
tokens: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 调用 MiMo Vision API
|
|
31
|
+
*
|
|
32
|
+
* 错误处理:
|
|
33
|
+
* - 401 → 认证失败
|
|
34
|
+
* - 429 → 配额耗尽
|
|
35
|
+
* - 5xx → 服务异常
|
|
36
|
+
* - 超时 → 超时错误
|
|
37
|
+
* - 网络错误 → 连接失败
|
|
38
|
+
*
|
|
39
|
+
* @param request - 视觉 API 调用参数
|
|
40
|
+
* @returns AI 生成的响应
|
|
41
|
+
*/
|
|
42
|
+
export declare function callVision(request: VisionRequest): Promise<VisionResponse>;
|
|
43
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,kBAAkB;AAClB,MAAM,WAAW,aAAa;IAC5B,YAAY;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,kBAAkB;AAClB,MAAM,WAAW,cAAc;IAC7B,iBAAiB;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe;IACf,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAsBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,cAAc,CAAC,CAmGzB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MiMo API 客户端 — 封装 OpenAI SDK 实例,提供统一的视觉 API 调用接口
|
|
3
|
+
*
|
|
4
|
+
* MiMo API 完全兼容 OpenAI chat/completions 格式,
|
|
5
|
+
* 因此可以直接使用 OpenAI SDK 进行调用。
|
|
6
|
+
*/
|
|
7
|
+
import OpenAI from "openai";
|
|
8
|
+
import { getConfig } from "./utils/config.js";
|
|
9
|
+
/** 请求超时时间(毫秒) */
|
|
10
|
+
const TIMEOUT_MS = 30_000;
|
|
11
|
+
/**
|
|
12
|
+
* 创建 OpenAI 客户端实例(懒加载单例)
|
|
13
|
+
*/
|
|
14
|
+
let _client = null;
|
|
15
|
+
let _config = null;
|
|
16
|
+
function getClient() {
|
|
17
|
+
const config = getConfig();
|
|
18
|
+
// 配置变更时重新创建客户端
|
|
19
|
+
if (!_client || _config?.apiKey !== config.apiKey || _config?.baseUrl !== config.baseUrl) {
|
|
20
|
+
_config = config;
|
|
21
|
+
_client = new OpenAI({
|
|
22
|
+
apiKey: config.apiKey,
|
|
23
|
+
baseURL: config.baseUrl,
|
|
24
|
+
timeout: TIMEOUT_MS,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return _client;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 调用 MiMo Vision API
|
|
31
|
+
*
|
|
32
|
+
* 错误处理:
|
|
33
|
+
* - 401 → 认证失败
|
|
34
|
+
* - 429 → 配额耗尽
|
|
35
|
+
* - 5xx → 服务异常
|
|
36
|
+
* - 超时 → 超时错误
|
|
37
|
+
* - 网络错误 → 连接失败
|
|
38
|
+
*
|
|
39
|
+
* @param request - 视觉 API 调用参数
|
|
40
|
+
* @returns AI 生成的响应
|
|
41
|
+
*/
|
|
42
|
+
export async function callVision(request) {
|
|
43
|
+
const config = getConfig();
|
|
44
|
+
const client = getClient();
|
|
45
|
+
const model = request.model || config.model;
|
|
46
|
+
const maxTokens = request.maxTokens || config.maxTokens;
|
|
47
|
+
// 构造消息内容(OpenAI Vision 格式)
|
|
48
|
+
const content = [
|
|
49
|
+
{ type: "text", text: request.prompt },
|
|
50
|
+
{
|
|
51
|
+
type: "image_url",
|
|
52
|
+
image_url: { url: request.imageUrl },
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
// 第二张图片(用于对比场景)
|
|
56
|
+
if (request.imageUrl2) {
|
|
57
|
+
content.push({
|
|
58
|
+
type: "image_url",
|
|
59
|
+
image_url: { url: request.imageUrl2 },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const response = await client.chat.completions.create({
|
|
64
|
+
model,
|
|
65
|
+
messages: [
|
|
66
|
+
{
|
|
67
|
+
role: "user",
|
|
68
|
+
content,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
max_tokens: maxTokens,
|
|
72
|
+
});
|
|
73
|
+
const choice = response.choices[0];
|
|
74
|
+
if (!choice?.message?.content) {
|
|
75
|
+
throw new Error("MiMo API 返回了空响应");
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
content: choice.message.content,
|
|
79
|
+
model: response.model || model,
|
|
80
|
+
tokens: response.usage?.total_tokens || 0,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
// 处理 OpenAI SDK 抛出的错误
|
|
85
|
+
if (error && typeof error === "object") {
|
|
86
|
+
const apiError = error;
|
|
87
|
+
// HTTP 状态错误
|
|
88
|
+
if (apiError.status === 401) {
|
|
89
|
+
throw new Error("认证失败,请检查 MIMO_API_KEY 环境变量是否正确");
|
|
90
|
+
}
|
|
91
|
+
if (apiError.status === 429) {
|
|
92
|
+
throw new Error("API 配额已耗尽,请稍后重试或检查账户余额");
|
|
93
|
+
}
|
|
94
|
+
if (apiError.status && apiError.status >= 500) {
|
|
95
|
+
throw new Error(`MiMo API 服务异常 (状态码: ${apiError.status}),请稍后重试`);
|
|
96
|
+
}
|
|
97
|
+
// 网络/超时错误
|
|
98
|
+
if (apiError.code === "ETIMEDOUT" ||
|
|
99
|
+
apiError.code === "ECONNABORTED" ||
|
|
100
|
+
apiError.name === "APIConnectionTimeoutError") {
|
|
101
|
+
throw new Error("请求超时(30秒),请重试或尝试更小的图片");
|
|
102
|
+
}
|
|
103
|
+
if (apiError.code === "ENOTFOUND" ||
|
|
104
|
+
apiError.code === "ECONNREFUSED" ||
|
|
105
|
+
apiError.name === "APIConnectionError") {
|
|
106
|
+
throw new Error(`无法连接到 MiMo API: ${apiError.message || "网络连接失败"}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// 通用未知错误
|
|
110
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
111
|
+
throw new Error(`未知错误: ${message}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MiMo Vision MCP Server — 入口文件
|
|
4
|
+
*
|
|
5
|
+
* 启动 MCP Server,注册 4 个视觉工具:
|
|
6
|
+
* - describe_image: 通用图片描述
|
|
7
|
+
* - ocr_image: OCR 文字提取
|
|
8
|
+
* - compare_images: 双图对比分析
|
|
9
|
+
* - analyze_screenshot: 截图/UI 分析
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;GAQG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MiMo Vision MCP Server — 入口文件
|
|
4
|
+
*
|
|
5
|
+
* 启动 MCP Server,注册 4 个视觉工具:
|
|
6
|
+
* - describe_image: 通用图片描述
|
|
7
|
+
* - ocr_image: OCR 文字提取
|
|
8
|
+
* - compare_images: 双图对比分析
|
|
9
|
+
* - analyze_screenshot: 截图/UI 分析
|
|
10
|
+
*/
|
|
11
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
|
+
import { getConfig } from "./utils/config.js";
|
|
14
|
+
import { isUrl, imageToBase64 } from "./utils/image.js";
|
|
15
|
+
import { callVision } from "./client.js";
|
|
16
|
+
import { describeImageParams } from "./tools/describe-image.js";
|
|
17
|
+
import { ocrImageParams, buildOcrPrompt, } from "./tools/ocr-image.js";
|
|
18
|
+
import { compareImagesParams, buildComparePrompt, } from "./tools/compare-images.js";
|
|
19
|
+
import { analyzeScreenshotParams, buildScreenshotPrompt, } from "./tools/analyze-screenshot.js";
|
|
20
|
+
// 启动时验证配置
|
|
21
|
+
try {
|
|
22
|
+
getConfig();
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
26
|
+
console.error(`[MiMo MCP] 配置错误: ${message}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
const server = new McpServer({
|
|
30
|
+
name: "mimo-vision-mcp",
|
|
31
|
+
version: "1.0.0",
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* 解析图片输入为 vision API 可用的 URL
|
|
35
|
+
* - URL → 直接返回
|
|
36
|
+
* - 本地路径 → 转为 base64 data URL
|
|
37
|
+
*/
|
|
38
|
+
async function resolveImageUrl(imagePath) {
|
|
39
|
+
if (isUrl(imagePath)) {
|
|
40
|
+
return imagePath;
|
|
41
|
+
}
|
|
42
|
+
const { dataUrl } = await imageToBase64(imagePath);
|
|
43
|
+
return dataUrl;
|
|
44
|
+
}
|
|
45
|
+
// ========== Tool 1: describe_image ==========
|
|
46
|
+
server.tool("describe_image", "通用图片描述 — 对任意图片进行内容描述,支持自定义提示词。输入可以是本地文件路径或 HTTP/HTTPS URL。", describeImageParams.shape, async ({ image_path, prompt }) => {
|
|
47
|
+
try {
|
|
48
|
+
const imageUrl = await resolveImageUrl(image_path);
|
|
49
|
+
const result = await callVision({
|
|
50
|
+
prompt: `你是一个专业的图片分析助手。${prompt}`,
|
|
51
|
+
imageUrl,
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
content: [
|
|
55
|
+
{
|
|
56
|
+
type: "text",
|
|
57
|
+
text: JSON.stringify({
|
|
58
|
+
description: result.content,
|
|
59
|
+
model: result.model,
|
|
60
|
+
tokens: result.tokens,
|
|
61
|
+
}, null, 2),
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
68
|
+
return {
|
|
69
|
+
content: [{ type: "text", text: `错误: ${message}` }],
|
|
70
|
+
isError: true,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
// ========== Tool 2: ocr_image ==========
|
|
75
|
+
server.tool("ocr_image", "OCR 文字提取 — 从图片中提取文字,支持指定语言偏好 (zh/en/ja/ko/auto)。", ocrImageParams.shape, async ({ image_path, language }) => {
|
|
76
|
+
try {
|
|
77
|
+
const imageUrl = await resolveImageUrl(image_path);
|
|
78
|
+
const result = await callVision({
|
|
79
|
+
prompt: buildOcrPrompt(language),
|
|
80
|
+
imageUrl,
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
content: [
|
|
84
|
+
{
|
|
85
|
+
type: "text",
|
|
86
|
+
text: JSON.stringify({
|
|
87
|
+
text: result.content,
|
|
88
|
+
language,
|
|
89
|
+
model: result.model,
|
|
90
|
+
}, null, 2),
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
97
|
+
return {
|
|
98
|
+
content: [{ type: "text", text: `错误: ${message}` }],
|
|
99
|
+
isError: true,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
// ========== Tool 3: compare_images ==========
|
|
104
|
+
server.tool("compare_images", "双图对比分析 — 对比两张图片的异同,支持多种对比维度 (general/differences/similarity/which_is_better)。", compareImagesParams.shape, async ({ image1, image2, aspect }) => {
|
|
105
|
+
try {
|
|
106
|
+
const [imageUrl1, imageUrl2] = await Promise.all([
|
|
107
|
+
resolveImageUrl(image1),
|
|
108
|
+
resolveImageUrl(image2),
|
|
109
|
+
]);
|
|
110
|
+
const result = await callVision({
|
|
111
|
+
prompt: buildComparePrompt(aspect),
|
|
112
|
+
imageUrl: imageUrl1,
|
|
113
|
+
imageUrl2: imageUrl2,
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
content: [
|
|
117
|
+
{
|
|
118
|
+
type: "text",
|
|
119
|
+
text: JSON.stringify({
|
|
120
|
+
comparison: result.content,
|
|
121
|
+
model: result.model,
|
|
122
|
+
}, null, 2),
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
129
|
+
return {
|
|
130
|
+
content: [{ type: "text", text: `错误: ${message}` }],
|
|
131
|
+
isError: true,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
// ========== Tool 4: analyze_screenshot ==========
|
|
136
|
+
server.tool("analyze_screenshot", "截图/UI 分析 — 分析 UI 截图的布局、可用性、可访问性,给出改进建议。支持指定分析焦点 (layout/usability/accessibility/all)。", analyzeScreenshotParams.shape, async ({ image_path, focus }) => {
|
|
137
|
+
try {
|
|
138
|
+
const imageUrl = await resolveImageUrl(image_path);
|
|
139
|
+
const result = await callVision({
|
|
140
|
+
prompt: buildScreenshotPrompt(focus),
|
|
141
|
+
imageUrl,
|
|
142
|
+
});
|
|
143
|
+
return {
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: "text",
|
|
147
|
+
text: JSON.stringify({
|
|
148
|
+
analysis: result.content,
|
|
149
|
+
model: result.model,
|
|
150
|
+
}, null, 2),
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
157
|
+
return {
|
|
158
|
+
content: [{ type: "text", text: `错误: ${message}` }],
|
|
159
|
+
isError: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
// 启动服务器
|
|
164
|
+
async function main() {
|
|
165
|
+
const transport = new StdioServerTransport();
|
|
166
|
+
await server.connect(transport);
|
|
167
|
+
console.error("[MiMo MCP] 服务器已启动");
|
|
168
|
+
}
|
|
169
|
+
main().catch((error) => {
|
|
170
|
+
console.error("[MiMo MCP] 启动失败:", error);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
});
|
|
173
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* analyze_screenshot 工具 — 截图/UI 结构分析
|
|
3
|
+
*
|
|
4
|
+
* 分析 UI 截图,评估布局/可用性/可访问性等维度。
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
/** analyze_screenshot 工具的参数 schema */
|
|
8
|
+
export declare const analyzeScreenshotParams: z.ZodObject<{
|
|
9
|
+
/** 截图路径或 URL */
|
|
10
|
+
image_path: z.ZodString;
|
|
11
|
+
/** 分析重点 */
|
|
12
|
+
focus: z.ZodDefault<z.ZodOptional<z.ZodEnum<["layout", "usability", "accessibility", "all"]>>>;
|
|
13
|
+
}, "strip", z.ZodTypeAny, {
|
|
14
|
+
image_path: string;
|
|
15
|
+
focus: "layout" | "usability" | "accessibility" | "all";
|
|
16
|
+
}, {
|
|
17
|
+
image_path: string;
|
|
18
|
+
focus?: "layout" | "usability" | "accessibility" | "all" | undefined;
|
|
19
|
+
}>;
|
|
20
|
+
/** 参数类型 */
|
|
21
|
+
export type AnalyzeScreenshotParams = z.infer<typeof analyzeScreenshotParams>;
|
|
22
|
+
/**
|
|
23
|
+
* 构造 UI 分析提示词
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildScreenshotPrompt(focus: string): string;
|
|
26
|
+
//# sourceMappingURL=analyze-screenshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-screenshot.d.ts","sourceRoot":"","sources":["../../src/tools/analyze-screenshot.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,sCAAsC;AACtC,eAAO,MAAM,uBAAuB;IAClC,gBAAgB;;IAIhB,WAAW;;;;;;;;EAKX,CAAC;AAEH,WAAW;AACX,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAU9E;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG3D"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* analyze_screenshot 工具 — 截图/UI 结构分析
|
|
3
|
+
*
|
|
4
|
+
* 分析 UI 截图,评估布局/可用性/可访问性等维度。
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
/** 分析焦点 */
|
|
8
|
+
const FOCUS_AREAS = ["layout", "usability", "accessibility", "all"];
|
|
9
|
+
/** analyze_screenshot 工具的参数 schema */
|
|
10
|
+
export const analyzeScreenshotParams = z.object({
|
|
11
|
+
/** 截图路径或 URL */
|
|
12
|
+
image_path: z
|
|
13
|
+
.string()
|
|
14
|
+
.min(1, "请提供截图路径或 URL"),
|
|
15
|
+
/** 分析重点 */
|
|
16
|
+
focus: z
|
|
17
|
+
.enum(FOCUS_AREAS)
|
|
18
|
+
.optional()
|
|
19
|
+
.default("all"),
|
|
20
|
+
});
|
|
21
|
+
/** 分析焦点中文映射 */
|
|
22
|
+
const FOCUS_LABELS = {
|
|
23
|
+
layout: "布局结构",
|
|
24
|
+
usability: "可用性",
|
|
25
|
+
accessibility: "可访问性",
|
|
26
|
+
all: "布局、可用性、可访问性",
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* 构造 UI 分析提示词
|
|
30
|
+
*/
|
|
31
|
+
export function buildScreenshotPrompt(focus) {
|
|
32
|
+
const focusLabel = FOCUS_LABELS[focus] || focus;
|
|
33
|
+
return `你是一个专业的 UI/UX 分析专家。请对这张截图进行${focusLabel}方面的分析,给出具体、可操作的改进建议。`;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=analyze-screenshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-screenshot.js","sourceRoot":"","sources":["../../src/tools/analyze-screenshot.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,WAAW;AACX,MAAM,WAAW,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,KAAK,CAAU,CAAC;AAE7E,sCAAsC;AACtC,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,gBAAgB;IAChB,UAAU,EAAE,CAAC;SACV,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC;IACzB,WAAW;IACX,KAAK,EAAE,CAAC;SACL,IAAI,CAAC,WAAW,CAAC;SACjB,QAAQ,EAAE;SACV,OAAO,CAAC,KAAK,CAAC;CAClB,CAAC,CAAC;AAKH,eAAe;AACf,MAAM,YAAY,GAA2B;IAC3C,MAAM,EAAE,MAAM;IACd,SAAS,EAAE,KAAK;IAChB,aAAa,EAAE,MAAM;IACrB,GAAG,EAAE,aAAa;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;IAChD,OAAO,8BAA8B,UAAU,sBAAsB,CAAC;AACxE,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* compare_images 工具 — 双图对比分析
|
|
3
|
+
*
|
|
4
|
+
* 对比两张图片的异同,支持从不同维度进行分析。
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
/** compare_images 工具的参数 schema */
|
|
8
|
+
export declare const compareImagesParams: z.ZodObject<{
|
|
9
|
+
/** 第一张图片路径或 URL */
|
|
10
|
+
image1: z.ZodString;
|
|
11
|
+
/** 第二张图片路径或 URL */
|
|
12
|
+
image2: z.ZodString;
|
|
13
|
+
/** 对比维度 */
|
|
14
|
+
aspect: z.ZodDefault<z.ZodOptional<z.ZodEnum<["general", "differences", "similarity", "which_is_better"]>>>;
|
|
15
|
+
}, "strip", z.ZodTypeAny, {
|
|
16
|
+
image1: string;
|
|
17
|
+
image2: string;
|
|
18
|
+
aspect: "general" | "differences" | "similarity" | "which_is_better";
|
|
19
|
+
}, {
|
|
20
|
+
image1: string;
|
|
21
|
+
image2: string;
|
|
22
|
+
aspect?: "general" | "differences" | "similarity" | "which_is_better" | undefined;
|
|
23
|
+
}>;
|
|
24
|
+
/** 参数类型 */
|
|
25
|
+
export type CompareImagesParams = z.infer<typeof compareImagesParams>;
|
|
26
|
+
/**
|
|
27
|
+
* 构造对比分析提示词
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildComparePrompt(aspect: string): string;
|
|
30
|
+
//# sourceMappingURL=compare-images.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare-images.d.ts","sourceRoot":"","sources":["../../src/tools/compare-images.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,kCAAkC;AAClC,eAAO,MAAM,mBAAmB;IAC9B,mBAAmB;;IAInB,mBAAmB;;IAInB,WAAW;;;;;;;;;;EAKX,CAAC;AAEH,WAAW;AACX,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAUtE;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGzD"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* compare_images 工具 — 双图对比分析
|
|
3
|
+
*
|
|
4
|
+
* 对比两张图片的异同,支持从不同维度进行分析。
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
/** 对比维度 */
|
|
8
|
+
const ASPECTS = ["general", "differences", "similarity", "which_is_better"];
|
|
9
|
+
/** compare_images 工具的参数 schema */
|
|
10
|
+
export const compareImagesParams = z.object({
|
|
11
|
+
/** 第一张图片路径或 URL */
|
|
12
|
+
image1: z
|
|
13
|
+
.string()
|
|
14
|
+
.min(1, "请提供第一张图片的路径或 URL"),
|
|
15
|
+
/** 第二张图片路径或 URL */
|
|
16
|
+
image2: z
|
|
17
|
+
.string()
|
|
18
|
+
.min(1, "请提供第二张图片的路径或 URL"),
|
|
19
|
+
/** 对比维度 */
|
|
20
|
+
aspect: z
|
|
21
|
+
.enum(ASPECTS)
|
|
22
|
+
.optional()
|
|
23
|
+
.default("general"),
|
|
24
|
+
});
|
|
25
|
+
/** 对比维度中文映射 */
|
|
26
|
+
const ASPECT_LABELS = {
|
|
27
|
+
general: "综合",
|
|
28
|
+
differences: "差异点",
|
|
29
|
+
similarity: "相似点",
|
|
30
|
+
which_is_better: "优劣对比",
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* 构造对比分析提示词
|
|
34
|
+
*/
|
|
35
|
+
export function buildComparePrompt(aspect) {
|
|
36
|
+
const aspectLabel = ASPECT_LABELS[aspect] || aspect;
|
|
37
|
+
return `你是一个专业的图片对比分析助手。请对比以下两张图片,从「${aspectLabel}」角度进行详细分析。`;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=compare-images.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare-images.js","sourceRoot":"","sources":["../../src/tools/compare-images.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,WAAW;AACX,MAAM,OAAO,GAAG,CAAC,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,iBAAiB,CAAU,CAAC;AAErF,kCAAkC;AAClC,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,mBAAmB;IACnB,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,kBAAkB,CAAC;IAC7B,mBAAmB;IACnB,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,kBAAkB,CAAC;IAC7B,WAAW;IACX,MAAM,EAAE,CAAC;SACN,IAAI,CAAC,OAAO,CAAC;SACb,QAAQ,EAAE;SACV,OAAO,CAAC,SAAS,CAAC;CACtB,CAAC,CAAC;AAKH,eAAe;AACf,MAAM,aAAa,GAA2B;IAC5C,OAAO,EAAE,IAAI;IACb,WAAW,EAAE,KAAK;IAClB,UAAU,EAAE,KAAK;IACjB,eAAe,EAAE,MAAM;CACxB,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC;IACpD,OAAO,+BAA+B,WAAW,YAAY,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* describe_image 工具 — 通用图片描述
|
|
3
|
+
*
|
|
4
|
+
* 对任意图片进行内容描述,支持自定义提示词。
|
|
5
|
+
* 输入支持本地文件路径或远程 URL。
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
/** describe_image 工具的参数 schema */
|
|
9
|
+
export declare const describeImageParams: z.ZodObject<{
|
|
10
|
+
/** 本地文件绝对路径或 HTTP/HTTPS URL */
|
|
11
|
+
image_path: z.ZodString;
|
|
12
|
+
/** 自定义分析指令 */
|
|
13
|
+
prompt: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
14
|
+
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
image_path: string;
|
|
16
|
+
prompt: string;
|
|
17
|
+
}, {
|
|
18
|
+
image_path: string;
|
|
19
|
+
prompt?: string | undefined;
|
|
20
|
+
}>;
|
|
21
|
+
/** 参数类型 */
|
|
22
|
+
export type DescribeImageParams = z.infer<typeof describeImageParams>;
|
|
23
|
+
//# sourceMappingURL=describe-image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describe-image.d.ts","sourceRoot":"","sources":["../../src/tools/describe-image.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,kCAAkC;AAClC,eAAO,MAAM,mBAAmB;IAC9B,+BAA+B;;IAI/B,cAAc;;;;;;;;EAKd,CAAC;AAEH,WAAW;AACX,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* describe_image 工具 — 通用图片描述
|
|
3
|
+
*
|
|
4
|
+
* 对任意图片进行内容描述,支持自定义提示词。
|
|
5
|
+
* 输入支持本地文件路径或远程 URL。
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
/** describe_image 工具的参数 schema */
|
|
9
|
+
export const describeImageParams = z.object({
|
|
10
|
+
/** 本地文件绝对路径或 HTTP/HTTPS URL */
|
|
11
|
+
image_path: z
|
|
12
|
+
.string()
|
|
13
|
+
.min(1, "请提供图片路径或 URL"),
|
|
14
|
+
/** 自定义分析指令 */
|
|
15
|
+
prompt: z
|
|
16
|
+
.string()
|
|
17
|
+
.optional()
|
|
18
|
+
.default("请详细描述这张图片的内容"),
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=describe-image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describe-image.js","sourceRoot":"","sources":["../../src/tools/describe-image.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,kCAAkC;AAClC,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,+BAA+B;IAC/B,UAAU,EAAE,CAAC;SACV,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC;IACzB,cAAc;IACd,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,OAAO,CAAC,cAAc,CAAC;CAC3B,CAAC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ocr_image 工具 — 文字提取
|
|
3
|
+
*
|
|
4
|
+
* 从图片中提取文字,支持指定语言偏好。
|
|
5
|
+
* 模型会被引导只输出提取到的文字,不添加额外说明。
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
/** ocr_image 工具的参数 schema */
|
|
9
|
+
export declare const ocrImageParams: z.ZodObject<{
|
|
10
|
+
/** 本地文件绝对路径或 HTTP/HTTPS URL */
|
|
11
|
+
image_path: z.ZodString;
|
|
12
|
+
/** 偏好语言 */
|
|
13
|
+
language: z.ZodDefault<z.ZodOptional<z.ZodEnum<["zh", "en", "ja", "ko", "auto"]>>>;
|
|
14
|
+
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
image_path: string;
|
|
16
|
+
language: "zh" | "en" | "ja" | "ko" | "auto";
|
|
17
|
+
}, {
|
|
18
|
+
image_path: string;
|
|
19
|
+
language?: "zh" | "en" | "ja" | "ko" | "auto" | undefined;
|
|
20
|
+
}>;
|
|
21
|
+
/** 参数类型 */
|
|
22
|
+
export type OcrImageParams = z.infer<typeof ocrImageParams>;
|
|
23
|
+
/**
|
|
24
|
+
* 构造 OCR 专用的系统提示词
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildOcrPrompt(language: string): string;
|
|
27
|
+
//# sourceMappingURL=ocr-image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ocr-image.d.ts","sourceRoot":"","sources":["../../src/tools/ocr-image.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,6BAA6B;AAC7B,eAAO,MAAM,cAAc;IACzB,+BAA+B;;IAI/B,WAAW;;;;;;;;EAKX,CAAC;AAEH,WAAW;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAW5D;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGvD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ocr_image 工具 — 文字提取
|
|
3
|
+
*
|
|
4
|
+
* 从图片中提取文字,支持指定语言偏好。
|
|
5
|
+
* 模型会被引导只输出提取到的文字,不添加额外说明。
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
/** 支持的语言 */
|
|
9
|
+
const SUPPORTED_LANGUAGES = ["zh", "en", "ja", "ko", "auto"];
|
|
10
|
+
/** ocr_image 工具的参数 schema */
|
|
11
|
+
export const ocrImageParams = z.object({
|
|
12
|
+
/** 本地文件绝对路径或 HTTP/HTTPS URL */
|
|
13
|
+
image_path: z
|
|
14
|
+
.string()
|
|
15
|
+
.min(1, "请提供图片路径或 URL"),
|
|
16
|
+
/** 偏好语言 */
|
|
17
|
+
language: z
|
|
18
|
+
.enum(SUPPORTED_LANGUAGES)
|
|
19
|
+
.optional()
|
|
20
|
+
.default("zh"),
|
|
21
|
+
});
|
|
22
|
+
/** 语言中文名映射 */
|
|
23
|
+
const LANGUAGE_LABELS = {
|
|
24
|
+
zh: "中文",
|
|
25
|
+
en: "英文",
|
|
26
|
+
ja: "日文",
|
|
27
|
+
ko: "韩文",
|
|
28
|
+
auto: "所有",
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* 构造 OCR 专用的系统提示词
|
|
32
|
+
*/
|
|
33
|
+
export function buildOcrPrompt(language) {
|
|
34
|
+
const langLabel = LANGUAGE_LABELS[language] || language;
|
|
35
|
+
return `请提取这张图片中的所有文字,优先识别${langLabel}文字。只输出提取到的文字,不要添加额外说明。`;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=ocr-image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ocr-image.js","sourceRoot":"","sources":["../../src/tools/ocr-image.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,YAAY;AACZ,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAU,CAAC;AAEtE,6BAA6B;AAC7B,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,+BAA+B;IAC/B,UAAU,EAAE,CAAC;SACV,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC;IACzB,WAAW;IACX,QAAQ,EAAE,CAAC;SACR,IAAI,CAAC,mBAAmB,CAAC;SACzB,QAAQ,EAAE;SACV,OAAO,CAAC,IAAI,CAAC;CACjB,CAAC,CAAC;AAKH,cAAc;AACd,MAAM,eAAe,GAA2B;IAC9C,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;IACR,IAAI,EAAE,IAAI;CACX,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,MAAM,SAAS,GAAG,eAAe,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC;IACxD,OAAO,qBAAqB,SAAS,wBAAwB,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具类型定义 — 所有工具共享的类型
|
|
3
|
+
*/
|
|
4
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
/** 工具处理函数结果 */
|
|
6
|
+
export interface ToolHandlerResult {
|
|
7
|
+
content: Array<{
|
|
8
|
+
type: "text";
|
|
9
|
+
text: string;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
/** 在 MCP Server 上注册工具的函数类型 */
|
|
13
|
+
export type ToolRegistrar = (server: McpServer) => void;
|
|
14
|
+
//# sourceMappingURL=tool-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-types.d.ts","sourceRoot":"","sources":["../../src/tools/tool-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,eAAe;AACf,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,8BAA8B;AAC9B,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-types.js","sourceRoot":"","sources":["../../src/tools/tool-types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置模块 — 从环境变量读取结构化配置
|
|
3
|
+
*
|
|
4
|
+
* 优先级:环境变量 > 默认值
|
|
5
|
+
* 仅 MIMO_API_KEY 为必填项
|
|
6
|
+
*/
|
|
7
|
+
export interface Config {
|
|
8
|
+
/** MiMo 平台 API Key */
|
|
9
|
+
apiKey: string;
|
|
10
|
+
/** MiMo API 端点 */
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
/** 默认使用的视觉模型 */
|
|
13
|
+
model: string;
|
|
14
|
+
/** 单次请求最大输出 token 数 */
|
|
15
|
+
maxTokens: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 从环境变量读取配置
|
|
19
|
+
* @throws {Error} 当 MIMO_API_KEY 未设置时抛出
|
|
20
|
+
*/
|
|
21
|
+
export declare function getConfig(): Config;
|
|
22
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,MAAM;IACrB,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAuBlC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置模块 — 从环境变量读取结构化配置
|
|
3
|
+
*
|
|
4
|
+
* 优先级:环境变量 > 默认值
|
|
5
|
+
* 仅 MIMO_API_KEY 为必填项
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* 从环境变量读取配置
|
|
9
|
+
* @throws {Error} 当 MIMO_API_KEY 未设置时抛出
|
|
10
|
+
*/
|
|
11
|
+
export function getConfig() {
|
|
12
|
+
const apiKey = process.env.MIMO_API_KEY;
|
|
13
|
+
if (!apiKey) {
|
|
14
|
+
throw new Error("请设置 MIMO_API_KEY 环境变量。示例: export MIMO_API_KEY=your-key");
|
|
15
|
+
}
|
|
16
|
+
const baseUrl = process.env.MIMO_BASE_URL || "https://api.xiaomimimo.com/v1";
|
|
17
|
+
const model = process.env.MIMO_MODEL || "mimo-v2-omni";
|
|
18
|
+
const maxTokensRaw = process.env.MIMO_MAX_TOKENS;
|
|
19
|
+
let maxTokens = 4096;
|
|
20
|
+
if (maxTokensRaw) {
|
|
21
|
+
const parsed = parseInt(maxTokensRaw, 10);
|
|
22
|
+
if (!isNaN(parsed) && parsed > 0) {
|
|
23
|
+
maxTokens = parsed;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { apiKey, baseUrl, model, maxTokens };
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH;;;GAGG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,wDAAwD,CACzD,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GACX,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,+BAA+B,CAAC;IAE/D,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,cAAc,CAAC;IAEvD,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACjD,IAAI,SAAS,GAAG,IAAI,CAAC;IACrB,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,SAAS,GAAG,MAAM,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 图片工具模块 — 本地图片路径判断、格式检测、base64 编码
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* - 判断输入是本地路径还是远程 URL
|
|
6
|
+
* - 通过文件扩展名检测 MIME 类型
|
|
7
|
+
* - 将本地图片转为 base64 data URL(检查大小限制)
|
|
8
|
+
*/
|
|
9
|
+
/** 最大文件大小:10MB */
|
|
10
|
+
export declare const MAX_FILE_SIZE: number;
|
|
11
|
+
/** 支持的格式列表 */
|
|
12
|
+
export declare const SUPPORTED_FORMATS: string[];
|
|
13
|
+
/**
|
|
14
|
+
* 判断字符串是否为 HTTP/HTTPS URL
|
|
15
|
+
*/
|
|
16
|
+
export declare function isUrl(str: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* 判断字符串是否为本地文件路径(非 URL)
|
|
19
|
+
*/
|
|
20
|
+
export declare function isLocalFile(str: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* 根据文件扩展名获取 MIME 类型
|
|
23
|
+
* @param ext - 扩展名(不含点号,如 "png")
|
|
24
|
+
* @returns MIME 类型字符串,不支持的格式返回 null
|
|
25
|
+
*/
|
|
26
|
+
export declare function getMimeType(ext: string): string | null;
|
|
27
|
+
/**
|
|
28
|
+
* 将本地图片文件转为 base64 data URL
|
|
29
|
+
*
|
|
30
|
+
* 处理流程:
|
|
31
|
+
* 1. 检查文件是否存在
|
|
32
|
+
* 2. 检查格式是否支持
|
|
33
|
+
* 3. 检查文件大小是否超过 10MB
|
|
34
|
+
* 4. 读取文件并编码为 base64
|
|
35
|
+
*
|
|
36
|
+
* @param filePath - 图片文件的绝对路径
|
|
37
|
+
* @returns base64 data URL 和 MIME 类型
|
|
38
|
+
* @throws {Error} 文件不存在 / 格式不支持 / 超出大小限制
|
|
39
|
+
*/
|
|
40
|
+
export declare function imageToBase64(filePath: string): Promise<{
|
|
41
|
+
dataUrl: string;
|
|
42
|
+
mimeType: string;
|
|
43
|
+
}>;
|
|
44
|
+
//# sourceMappingURL=image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/utils/image.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH,kBAAkB;AAClB,eAAO,MAAM,aAAa,QAAmB,CAAC;AAE9C,cAAc;AACd,eAAO,MAAM,iBAAiB,UAAwB,CAAC;AAEvD;;GAEG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE1C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgChD"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 图片工具模块 — 本地图片路径判断、格式检测、base64 编码
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* - 判断输入是本地路径还是远程 URL
|
|
6
|
+
* - 通过文件扩展名检测 MIME 类型
|
|
7
|
+
* - 将本地图片转为 base64 data URL(检查大小限制)
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from "node:fs";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
/** 支持的图片格式及其 MIME 类型 */
|
|
12
|
+
const MIME_MAP = {
|
|
13
|
+
png: "image/png",
|
|
14
|
+
jpg: "image/jpeg",
|
|
15
|
+
jpeg: "image/jpeg",
|
|
16
|
+
gif: "image/gif",
|
|
17
|
+
webp: "image/webp",
|
|
18
|
+
bmp: "image/bmp",
|
|
19
|
+
};
|
|
20
|
+
/** 最大文件大小:10MB */
|
|
21
|
+
export const MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
22
|
+
/** 支持的格式列表 */
|
|
23
|
+
export const SUPPORTED_FORMATS = Object.keys(MIME_MAP);
|
|
24
|
+
/**
|
|
25
|
+
* 判断字符串是否为 HTTP/HTTPS URL
|
|
26
|
+
*/
|
|
27
|
+
export function isUrl(str) {
|
|
28
|
+
return /^https?:\/\//i.test(str);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 判断字符串是否为本地文件路径(非 URL)
|
|
32
|
+
*/
|
|
33
|
+
export function isLocalFile(str) {
|
|
34
|
+
return !isUrl(str);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 根据文件扩展名获取 MIME 类型
|
|
38
|
+
* @param ext - 扩展名(不含点号,如 "png")
|
|
39
|
+
* @returns MIME 类型字符串,不支持的格式返回 null
|
|
40
|
+
*/
|
|
41
|
+
export function getMimeType(ext) {
|
|
42
|
+
return MIME_MAP[ext.toLowerCase()] ?? null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 将本地图片文件转为 base64 data URL
|
|
46
|
+
*
|
|
47
|
+
* 处理流程:
|
|
48
|
+
* 1. 检查文件是否存在
|
|
49
|
+
* 2. 检查格式是否支持
|
|
50
|
+
* 3. 检查文件大小是否超过 10MB
|
|
51
|
+
* 4. 读取文件并编码为 base64
|
|
52
|
+
*
|
|
53
|
+
* @param filePath - 图片文件的绝对路径
|
|
54
|
+
* @returns base64 data URL 和 MIME 类型
|
|
55
|
+
* @throws {Error} 文件不存在 / 格式不支持 / 超出大小限制
|
|
56
|
+
*/
|
|
57
|
+
export async function imageToBase64(filePath) {
|
|
58
|
+
// 1. 检查文件是否存在
|
|
59
|
+
if (!fs.existsSync(filePath)) {
|
|
60
|
+
throw new Error(`文件未找到: ${filePath},请检查路径是否正确`);
|
|
61
|
+
}
|
|
62
|
+
// 2. 检查格式
|
|
63
|
+
const ext = path.extname(filePath).replace(/^\./, "");
|
|
64
|
+
const mimeType = getMimeType(ext);
|
|
65
|
+
if (!mimeType) {
|
|
66
|
+
throw new Error(`不支持的图片格式: .${ext || "(无扩展名)"},支持: ${SUPPORTED_FORMATS.map((f) => f.toUpperCase()).join(", ")}`);
|
|
67
|
+
}
|
|
68
|
+
// 3. 检查大小
|
|
69
|
+
const stat = fs.statSync(filePath);
|
|
70
|
+
if (stat.size > MAX_FILE_SIZE) {
|
|
71
|
+
const sizeMB = (stat.size / (1024 * 1024)).toFixed(1);
|
|
72
|
+
throw new Error(`图片大小 ${sizeMB}MB 超过 ${MAX_FILE_SIZE / (1024 * 1024)}MB 限制,请压缩后重试`);
|
|
73
|
+
}
|
|
74
|
+
// 4. 读取并编码
|
|
75
|
+
const buffer = fs.readFileSync(filePath);
|
|
76
|
+
const base64 = buffer.toString("base64");
|
|
77
|
+
const dataUrl = `data:${mimeType};base64,${base64}`;
|
|
78
|
+
return { dataUrl, mimeType };
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/utils/image.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,wBAAwB;AACxB,MAAM,QAAQ,GAA2B;IACvC,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,YAAY;IACjB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,WAAW;IAChB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,WAAW;CACjB,CAAC;AAEF,kBAAkB;AAClB,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C,cAAc;AACd,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAEvD;;GAEG;AACH,MAAM,UAAU,KAAK,CAAC,GAAW;IAC/B,OAAO,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,IAAI,IAAI,CAAC;AAC7C,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,QAAgB;IAEhB,cAAc;IACd,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,UAAU,QAAQ,YAAY,CAC/B,CAAC;IACJ,CAAC;IAED,UAAU;IACV,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,cAAc,GAAG,IAAI,QAAQ,QAAQ,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAChG,CAAC;IACJ,CAAC;IAED,UAAU;IACV,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,IAAI,GAAG,aAAa,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACtD,MAAM,IAAI,KAAK,CACb,QAAQ,MAAM,SAAS,aAAa,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,cAAc,CACnE,CAAC;IACJ,CAAC;IAED,WAAW;IACX,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,QAAQ,QAAQ,WAAW,MAAM,EAAE,CAAC;IAEpD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AAC/B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mimo-vision-claude",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MiMo Vision MCP Server — 封装小米 MiMo 全模态大模型视觉 API,为 Claude Code 提供识图能力",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mimo-vision-claude": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsc --watch",
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:watch": "vitest",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"mimo",
|
|
24
|
+
"vision",
|
|
25
|
+
"ai",
|
|
26
|
+
"claude"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
31
|
+
"openai": "^4.73.0",
|
|
32
|
+
"zod": "^3.23.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^22.0.0",
|
|
36
|
+
"typescript": "^5.6.0",
|
|
37
|
+
"vitest": "^2.1.0"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|