mcp-cos-upload 1.1.2 → 1.1.3
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/.env.example +10 -0
- package/README.md +8 -32
- package/package.json +3 -2
- package/src/cos.js +51 -29
- package/src/index.js +11 -3
package/.env.example
ADDED
package/README.md
CHANGED
|
@@ -71,38 +71,14 @@ COS_CDN_DOMAIN=cdn.example.com
|
|
|
71
71
|
|
|
72
72
|
> **Note:** The `.env` file contains secrets — never commit it. Use `.env.example` as a reference template.
|
|
73
73
|
|
|
74
|
-
The server
|
|
75
|
-
1.
|
|
76
|
-
2. Project root (`process.cwd()/.env`, set by Cursor to workspace root)
|
|
77
|
-
3. `~/.mcp-cos-upload.env`
|
|
78
|
-
4. `~/.config/mcp-cos-upload/.env`
|
|
74
|
+
The server only reads one file:
|
|
75
|
+
1. Project root `.env` (`/path/to/mcp-cos-upload/.env`)
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
{
|
|
86
|
-
"mcpServers": {
|
|
87
|
-
"mcp-cos-upload": {
|
|
88
|
-
"command": "sh",
|
|
89
|
-
"args": ["-c", "command -v mcp-cos-upload >/dev/null 2>&1 || npm i -g mcp-cos-upload >/dev/null 2>&1; exec mcp-cos-upload"],
|
|
90
|
-
"env": {
|
|
91
|
-
"COS_SECRET_ID": "your_secret_id",
|
|
92
|
-
"COS_SECRET_KEY": "your_secret_key",
|
|
93
|
-
"COS_DEFAULT_BUCKET": "your_bucket_name",
|
|
94
|
-
"COS_DEFAULT_REGION": "ap-guangzhou",
|
|
95
|
-
"COS_KEY_PREFIX": "figma-assets",
|
|
96
|
-
"COS_CDN_DOMAIN": "cdn.example.com"
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
#### Priority
|
|
104
|
-
|
|
105
|
-
If both are configured, project `.env` file takes **higher priority** over MCP `env` configuration.
|
|
77
|
+
It does **not** read:
|
|
78
|
+
1. MCP `env` configuration
|
|
79
|
+
2. Home directory `.env`
|
|
80
|
+
3. Parent directory `.env`
|
|
81
|
+
4. `COS_ENV_PATH`
|
|
106
82
|
|
|
107
83
|
### Environment Variables
|
|
108
84
|
|
|
@@ -271,7 +247,7 @@ This happens when using `npx -y mcp-cos-upload` directly — npm outputs package
|
|
|
271
247
|
|
|
272
248
|
### Missing COS_SECRET_ID or COS_SECRET_KEY
|
|
273
249
|
|
|
274
|
-
The server
|
|
250
|
+
The server only reads the project root `.env`. If that file does not exist, or if it is missing `COS_SECRET_ID` / `COS_SECRET_KEY`, tool calls will return a clear error telling you to fix that single file.
|
|
275
251
|
|
|
276
252
|
### MCP not working after code changes
|
|
277
253
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-cos-upload",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "MCP server for uploading files to Tencent Cloud COS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"mcp-cos-upload": "src/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"src/**/*"
|
|
11
|
+
"src/**/*",
|
|
12
|
+
".env.example"
|
|
12
13
|
],
|
|
13
14
|
"keywords": [
|
|
14
15
|
"mcp",
|
package/src/cos.js
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
import COS from "cos-nodejs-sdk-v5";
|
|
3
3
|
import { readFileSync, existsSync } from "fs";
|
|
4
4
|
import { join, dirname } from "path";
|
|
5
|
-
import { homedir } from "os";
|
|
6
5
|
import { fileURLToPath } from "url";
|
|
7
6
|
|
|
8
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const PROJECT_ENV_PATH = join(__dirname, "..", ".env");
|
|
9
|
+
|
|
10
|
+
function parseEnvFile(filePath) {
|
|
11
|
+
const env = {};
|
|
9
12
|
|
|
10
|
-
function loadEnvFile(filePath) {
|
|
11
13
|
try {
|
|
12
14
|
const content = readFileSync(filePath, "utf-8");
|
|
13
15
|
for (const line of content.split("\n")) {
|
|
@@ -17,43 +19,63 @@ function loadEnvFile(filePath) {
|
|
|
17
19
|
if (eqIdx === -1) continue;
|
|
18
20
|
const key = trimmed.slice(0, eqIdx).trim();
|
|
19
21
|
const value = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
|
|
20
|
-
if (key
|
|
22
|
+
if (key) env[key] = value;
|
|
21
23
|
}
|
|
22
|
-
return true;
|
|
23
24
|
} catch {
|
|
24
|
-
return
|
|
25
|
+
return null;
|
|
25
26
|
}
|
|
27
|
+
|
|
28
|
+
return env;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
const fileEnv = existsSync(PROJECT_ENV_PATH) ? parseEnvFile(PROJECT_ENV_PATH) : null;
|
|
32
|
+
|
|
33
|
+
function getMissingCredentialMessage() {
|
|
34
|
+
if (!fileEnv) {
|
|
35
|
+
return [
|
|
36
|
+
"[mcp-cos-upload] ❌ 未找到配置文件",
|
|
37
|
+
`只会读取项目根目录 .env: ${PROJECT_ENV_PATH}`,
|
|
38
|
+
"请在该文件中设置:",
|
|
39
|
+
"COS_SECRET_ID=your_secret_id",
|
|
40
|
+
"COS_SECRET_KEY=your_secret_key",
|
|
41
|
+
"COS_DEFAULT_BUCKET=your_bucket_name",
|
|
42
|
+
"COS_DEFAULT_REGION=ap-guangzhou",
|
|
43
|
+
].join("\n");
|
|
41
44
|
}
|
|
45
|
+
|
|
46
|
+
return [
|
|
47
|
+
"[mcp-cos-upload] ❌ .env 缺少 COS_SECRET_ID 或 COS_SECRET_KEY",
|
|
48
|
+
`只读取此文件: ${PROJECT_ENV_PATH}`,
|
|
49
|
+
"请补全以下字段:",
|
|
50
|
+
"COS_SECRET_ID=your_secret_id",
|
|
51
|
+
"COS_SECRET_KEY=your_secret_key",
|
|
52
|
+
].join("\n");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getRequiredConfigValue(key) {
|
|
56
|
+
return fileEnv?.[key] || null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getCosClientConfigError() {
|
|
60
|
+
const secretId = getRequiredConfigValue("COS_SECRET_ID");
|
|
61
|
+
const secretKey = getRequiredConfigValue("COS_SECRET_KEY");
|
|
62
|
+
|
|
63
|
+
if (secretId && secretKey) return null;
|
|
64
|
+
return getMissingCredentialMessage();
|
|
42
65
|
}
|
|
43
66
|
|
|
44
67
|
export function createCosClient() {
|
|
45
|
-
const
|
|
46
|
-
const secretKey = process.env.COS_SECRET_KEY;
|
|
68
|
+
const configError = getCosClientConfigError();
|
|
47
69
|
|
|
48
|
-
if (
|
|
49
|
-
console.error("[mcp-cos-upload] ❌ 缺少 COS_SECRET_ID 或 COS_SECRET_KEY");
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
70
|
+
if (configError) throw new Error(configError);
|
|
52
71
|
|
|
53
|
-
return new COS({
|
|
72
|
+
return new COS({
|
|
73
|
+
SecretId: getRequiredConfigValue("COS_SECRET_ID"),
|
|
74
|
+
SecretKey: getRequiredConfigValue("COS_SECRET_KEY"),
|
|
75
|
+
});
|
|
54
76
|
}
|
|
55
77
|
|
|
56
|
-
export const DEFAULT_BUCKET =
|
|
57
|
-
export const DEFAULT_REGION =
|
|
58
|
-
export const KEY_PREFIX =
|
|
59
|
-
export const CDN_DOMAIN =
|
|
78
|
+
export const DEFAULT_BUCKET = getRequiredConfigValue("COS_DEFAULT_BUCKET");
|
|
79
|
+
export const DEFAULT_REGION = getRequiredConfigValue("COS_DEFAULT_REGION");
|
|
80
|
+
export const KEY_PREFIX = getRequiredConfigValue("COS_KEY_PREFIX") || "figma-assets";
|
|
81
|
+
export const CDN_DOMAIN = getRequiredConfigValue("COS_CDN_DOMAIN");
|
package/src/index.js
CHANGED
|
@@ -20,8 +20,6 @@ import {
|
|
|
20
20
|
CDN_DOMAIN,
|
|
21
21
|
} from "./cos.js";
|
|
22
22
|
|
|
23
|
-
const cos = createCosClient();
|
|
24
|
-
|
|
25
23
|
// 工具参数校验(zod)
|
|
26
24
|
const CosUploadArgsSchema = z.object({
|
|
27
25
|
bucket: z.string().optional().describe("COS Bucket 名,不传则用默认 COS_DEFAULT_BUCKET"),
|
|
@@ -58,7 +56,7 @@ const cosUploadInputSchema = {
|
|
|
58
56
|
|
|
59
57
|
async function main() {
|
|
60
58
|
const server = new Server(
|
|
61
|
-
{ name: "mcp-cos-upload", version: "1.
|
|
59
|
+
{ name: "mcp-cos-upload", version: "1.1.2" },
|
|
62
60
|
{ capabilities: { tools: {} } }
|
|
63
61
|
);
|
|
64
62
|
|
|
@@ -103,6 +101,16 @@ async function main() {
|
|
|
103
101
|
return { isError: true, content: [{ type: "text", text: "必须提供 content、url 或 filepath 其中一个参数。" }] };
|
|
104
102
|
}
|
|
105
103
|
|
|
104
|
+
let cos;
|
|
105
|
+
try {
|
|
106
|
+
cos = createCosClient();
|
|
107
|
+
} catch (err) {
|
|
108
|
+
return {
|
|
109
|
+
isError: true,
|
|
110
|
+
content: [{ type: "text", text: err?.message || String(err) }],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
106
114
|
// 扩展名到 MIME 类型映射
|
|
107
115
|
const extToMimeType = {
|
|
108
116
|
png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg",
|