nos-upload-mcp 0.2.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/LICENSE +21 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +399 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 jeekdong
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import path2 from "path";
|
|
8
|
+
|
|
9
|
+
// ../core/dist/index.js
|
|
10
|
+
import nconf from "nconf";
|
|
11
|
+
import path from "path";
|
|
12
|
+
import fs from "fs";
|
|
13
|
+
import os from "os";
|
|
14
|
+
import url from "url";
|
|
15
|
+
import fs2 from "fs";
|
|
16
|
+
import { NosClient } from "@nos-sdk/nos-node-sdk";
|
|
17
|
+
var log = {
|
|
18
|
+
log: (message) => {
|
|
19
|
+
console.log(message);
|
|
20
|
+
},
|
|
21
|
+
error: (message) => {
|
|
22
|
+
console.error(message);
|
|
23
|
+
},
|
|
24
|
+
warn: (message) => {
|
|
25
|
+
console.warn(message);
|
|
26
|
+
},
|
|
27
|
+
info: (message) => {
|
|
28
|
+
console.info(message);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var log_default = log;
|
|
32
|
+
var NOS_CONF_PATH = path.resolve(os.homedir(), ".nos-conf", "config.json");
|
|
33
|
+
var ENV_KEY_MAP = {
|
|
34
|
+
"NOS_ACCESS_KEY": "accessKey",
|
|
35
|
+
"NOS_ACCESS_SECRET": "accessSecret",
|
|
36
|
+
"NOS_ENDPOINT": "endpoint",
|
|
37
|
+
"NOS_DEFAULT_BUCKET": "defaultBucket",
|
|
38
|
+
"NOS_HOST": "host",
|
|
39
|
+
"NOS_PROTOCOL": "protocol"
|
|
40
|
+
};
|
|
41
|
+
var REQUIRED_KEYS = ["accessKey", "accessSecret", "endpoint", "defaultBucket", "host"];
|
|
42
|
+
var SENSITIVE_KEYS = ["accessKey", "accessSecret"];
|
|
43
|
+
function maskValue(value, key) {
|
|
44
|
+
if (!value) return "";
|
|
45
|
+
if (SENSITIVE_KEYS.includes(key)) {
|
|
46
|
+
if (value.length <= 4) return "****";
|
|
47
|
+
return value.slice(0, 2) + "***" + value.slice(-2);
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
var NOSConf = class _NOSConf {
|
|
52
|
+
constructor() {
|
|
53
|
+
this.nconfInstance = nconf;
|
|
54
|
+
this.nconfInstance.env({
|
|
55
|
+
separator: "__",
|
|
56
|
+
match: /^NOS_/,
|
|
57
|
+
transform: (obj) => {
|
|
58
|
+
const newKey = ENV_KEY_MAP[obj.key];
|
|
59
|
+
if (newKey) {
|
|
60
|
+
return { key: newKey, value: obj.value };
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}).file({ file: NOS_CONF_PATH });
|
|
65
|
+
_NOSConf.ensureConfigDir();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 确保配置目录存在(同步方式,避免竞态)
|
|
69
|
+
*/
|
|
70
|
+
static ensureConfigDir() {
|
|
71
|
+
const dirPath = path.dirname(NOS_CONF_PATH);
|
|
72
|
+
try {
|
|
73
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
74
|
+
} catch (err) {
|
|
75
|
+
log_default.error("\u521D\u59CB\u5316\u914D\u7F6E\u76EE\u5F55\u5931\u8D25");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 获取配置
|
|
80
|
+
*/
|
|
81
|
+
getConfig() {
|
|
82
|
+
return {
|
|
83
|
+
accessKey: this.nconfInstance.get("accessKey"),
|
|
84
|
+
accessSecret: this.nconfInstance.get("accessSecret"),
|
|
85
|
+
endpoint: this.nconfInstance.get("endpoint"),
|
|
86
|
+
defaultBucket: this.nconfInstance.get("defaultBucket"),
|
|
87
|
+
host: this.nconfInstance.get("host"),
|
|
88
|
+
protocol: this.nconfInstance.get("protocol")
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 设置配置(简化版,自动处理 protocol)
|
|
93
|
+
*/
|
|
94
|
+
setConfig(config) {
|
|
95
|
+
const validKeys = ["accessKey", "accessSecret", "endpoint", "defaultBucket", "host", "protocol"];
|
|
96
|
+
for (const [key, value] of Object.entries(config)) {
|
|
97
|
+
if (validKeys.includes(key) && value) {
|
|
98
|
+
this.nconfInstance.set(key, value);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (config.endpoint && !config.protocol) {
|
|
102
|
+
const parsedUrl = url.parse(config.endpoint);
|
|
103
|
+
const protocol = parsedUrl.protocol?.slice(0, -1) || "http";
|
|
104
|
+
this.nconfInstance.set("protocol", protocol);
|
|
105
|
+
}
|
|
106
|
+
this.nconfInstance.save(void 0);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 重置配置
|
|
110
|
+
*/
|
|
111
|
+
reset() {
|
|
112
|
+
this.nconfInstance.reset();
|
|
113
|
+
this.nconfInstance.save(void 0);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 获取配置状态(脱敏输出)
|
|
117
|
+
*/
|
|
118
|
+
getConfigStatus() {
|
|
119
|
+
const config = this.getConfig();
|
|
120
|
+
const items = [];
|
|
121
|
+
const missingKeys = [];
|
|
122
|
+
for (const key of REQUIRED_KEYS) {
|
|
123
|
+
const value = config[key];
|
|
124
|
+
const isSet = !!value;
|
|
125
|
+
if (!isSet) {
|
|
126
|
+
missingKeys.push(key);
|
|
127
|
+
}
|
|
128
|
+
let source;
|
|
129
|
+
const envKey = Object.entries(ENV_KEY_MAP).find(([, v]) => v === key)?.[0];
|
|
130
|
+
if (envKey && process.env[envKey]) {
|
|
131
|
+
source = "env";
|
|
132
|
+
} else if (isSet) {
|
|
133
|
+
source = "file";
|
|
134
|
+
}
|
|
135
|
+
items.push({
|
|
136
|
+
key,
|
|
137
|
+
isSet,
|
|
138
|
+
source,
|
|
139
|
+
preview: isSet ? maskValue(value, key) : void 0
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
isComplete: missingKeys.length === 0,
|
|
144
|
+
missingKeys,
|
|
145
|
+
items
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* 验证配置完整性
|
|
150
|
+
*/
|
|
151
|
+
validateConfig() {
|
|
152
|
+
const config = this.getConfig();
|
|
153
|
+
const missingKeys = [];
|
|
154
|
+
for (const key of REQUIRED_KEYS) {
|
|
155
|
+
if (!config[key]) {
|
|
156
|
+
missingKeys.push(key);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (missingKeys.length === 0) {
|
|
160
|
+
return {
|
|
161
|
+
valid: true,
|
|
162
|
+
missingKeys: [],
|
|
163
|
+
message: "Configuration is complete"
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
valid: false,
|
|
168
|
+
missingKeys,
|
|
169
|
+
message: `Missing required config: ${missingKeys.join(", ")}`
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
var nosConf = new NOSConf();
|
|
174
|
+
var client = null;
|
|
175
|
+
var lastConfigHash = "";
|
|
176
|
+
function getConfigHash(config) {
|
|
177
|
+
return JSON.stringify([
|
|
178
|
+
config.accessKey,
|
|
179
|
+
config.accessSecret,
|
|
180
|
+
config.endpoint,
|
|
181
|
+
config.defaultBucket,
|
|
182
|
+
config.host,
|
|
183
|
+
config.protocol
|
|
184
|
+
]);
|
|
185
|
+
}
|
|
186
|
+
function validateConfig(config) {
|
|
187
|
+
return Boolean(
|
|
188
|
+
config.accessKey && config.accessSecret && config.endpoint && config.defaultBucket && config.host
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
function getClient() {
|
|
192
|
+
const config = nosConf.getConfig();
|
|
193
|
+
if (!validateConfig(config)) {
|
|
194
|
+
throw new Error("NOS \u914D\u7F6E\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u5148\u914D\u7F6E accessKey, accessSecret, endpoint, defaultBucket, host");
|
|
195
|
+
}
|
|
196
|
+
const currentHash = getConfigHash(config);
|
|
197
|
+
if (!client || currentHash !== lastConfigHash) {
|
|
198
|
+
client = new NosClient(config);
|
|
199
|
+
lastConfigHash = currentHash;
|
|
200
|
+
}
|
|
201
|
+
return client;
|
|
202
|
+
}
|
|
203
|
+
var upload = async (file) => {
|
|
204
|
+
const { pathName, name } = file;
|
|
205
|
+
try {
|
|
206
|
+
const nosClient = getClient();
|
|
207
|
+
await nosClient.putObject({
|
|
208
|
+
objectKey: name,
|
|
209
|
+
body: fs2.createReadStream(decodeURIComponent(pathName))
|
|
210
|
+
});
|
|
211
|
+
const config = nosConf.getConfig();
|
|
212
|
+
const fileUrl = `${config.endpoint}/${name}`;
|
|
213
|
+
return {
|
|
214
|
+
success: true,
|
|
215
|
+
url: fileUrl,
|
|
216
|
+
filePath: pathName
|
|
217
|
+
};
|
|
218
|
+
} catch (error) {
|
|
219
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown upload error";
|
|
220
|
+
log_default.error(`Upload failed for ${pathName}: ${errorMessage}`);
|
|
221
|
+
return {
|
|
222
|
+
success: false,
|
|
223
|
+
error: errorMessage,
|
|
224
|
+
filePath: pathName
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// src/index.ts
|
|
230
|
+
var server = new McpServer({
|
|
231
|
+
name: "nos-upload",
|
|
232
|
+
version: "0.2.0"
|
|
233
|
+
});
|
|
234
|
+
server.tool(
|
|
235
|
+
"nos_upload",
|
|
236
|
+
'\u4E0A\u4F20\u6587\u4EF6\u5230 NOS (\u7F51\u6613\u5BF9\u8C61\u5B58\u50A8/Netease Object Storage)\u3002\u5F53\u7528\u6237\u8BF4"\u4E0A\u4F20\u5230nos"\u3001"\u4E0A\u4F20\u5230\u5BF9\u8C61\u5B58\u50A8"\u3001"\u4E0A\u4F20\u56FE\u7247/\u6587\u4EF6\u5230\u4E91\u7AEF"\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177\u3002\u8FD4\u56DE\u4E0A\u4F20\u540E\u7684\u516C\u5F00\u8BBF\u95EE URL\u3002',
|
|
237
|
+
{
|
|
238
|
+
filePaths: z.array(z.string()).min(1).describe("\u8981\u4E0A\u4F20\u7684\u6587\u4EF6\u8DEF\u5F84\u5217\u8868\uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u8DEF\u5F84\uFF09"),
|
|
239
|
+
prefix: z.string().optional().describe("\u53EF\u9009\u7684\u5BF9\u8C61\u5B58\u50A8\u8DEF\u5F84\u524D\u7F00\uFF0C\u9ED8\u8BA4\u4F7F\u7528 nos-upload-cli/{timestamp}")
|
|
240
|
+
},
|
|
241
|
+
async ({ filePaths, prefix }) => {
|
|
242
|
+
const timestamp = Date.now();
|
|
243
|
+
const objectPrefix = prefix || `nos-upload-cli/${timestamp}`;
|
|
244
|
+
const results = [];
|
|
245
|
+
for (const filePath of filePaths) {
|
|
246
|
+
const result = await upload({
|
|
247
|
+
pathName: filePath,
|
|
248
|
+
name: `${objectPrefix}/${path2.basename(filePath)}`
|
|
249
|
+
});
|
|
250
|
+
results.push(result);
|
|
251
|
+
}
|
|
252
|
+
const successful = results.filter((r) => r.success);
|
|
253
|
+
const failed = results.filter((r) => !r.success);
|
|
254
|
+
let content = "";
|
|
255
|
+
if (successful.length > 0) {
|
|
256
|
+
content += `\u2705 \u6210\u529F\u4E0A\u4F20 ${successful.length} \u4E2A\u6587\u4EF6:
|
|
257
|
+
`;
|
|
258
|
+
content += successful.map((r) => ` - ${r.filePath} \u2192 ${r.url}`).join("\n");
|
|
259
|
+
}
|
|
260
|
+
if (failed.length > 0) {
|
|
261
|
+
if (content) content += "\n\n";
|
|
262
|
+
content += `\u274C \u4E0A\u4F20\u5931\u8D25 ${failed.length} \u4E2A\u6587\u4EF6:
|
|
263
|
+
`;
|
|
264
|
+
content += failed.map((r) => ` - ${r.filePath}: ${r.error}`).join("\n");
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
content: [{ type: "text", text: content }],
|
|
268
|
+
isError: failed.length > 0 && successful.length === 0
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
server.tool(
|
|
273
|
+
"get_config_status",
|
|
274
|
+
"\u83B7\u53D6\u5F53\u524D NOS \u914D\u7F6E\u72B6\u6001\uFF0C\u663E\u793A\u54EA\u4E9B\u914D\u7F6E\u9879\u5DF2\u8BBE\u7F6E\uFF08\u4E0D\u663E\u793A\u654F\u611F\u503C\uFF09",
|
|
275
|
+
{},
|
|
276
|
+
async () => {
|
|
277
|
+
const config = nosConf.getConfig();
|
|
278
|
+
const requiredFields = [
|
|
279
|
+
"accessKey",
|
|
280
|
+
"accessSecret",
|
|
281
|
+
"endpoint",
|
|
282
|
+
"defaultBucket",
|
|
283
|
+
"host"
|
|
284
|
+
];
|
|
285
|
+
const status = requiredFields.map((field) => {
|
|
286
|
+
const value = config[field];
|
|
287
|
+
const isSet = Boolean(value);
|
|
288
|
+
let displayValue = "(\u672A\u8BBE\u7F6E)";
|
|
289
|
+
if (isSet) {
|
|
290
|
+
if (field === "accessSecret") {
|
|
291
|
+
displayValue = "******";
|
|
292
|
+
} else if (typeof value === "string" && value.length > 8) {
|
|
293
|
+
displayValue = value.substring(0, 4) + "..." + value.substring(value.length - 4);
|
|
294
|
+
} else {
|
|
295
|
+
displayValue = String(value);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return ` ${field}: ${displayValue} ${isSet ? "\u2705" : "\u274C"}`;
|
|
299
|
+
});
|
|
300
|
+
const allSet = requiredFields.every((field) => Boolean(config[field]));
|
|
301
|
+
let content = allSet ? "\u2705 \u6240\u6709\u5FC5\u9700\u914D\u7F6E\u5DF2\u5C31\u7EEA\n\n" : "\u26A0\uFE0F \u90E8\u5206\u914D\u7F6E\u7F3A\u5931\uFF0C\u8BF7\u4F7F\u7528 set_config \u5DE5\u5177\u8BBE\u7F6E\n\n";
|
|
302
|
+
content += "\u914D\u7F6E\u72B6\u6001:\n" + status.join("\n");
|
|
303
|
+
if (config.protocol) {
|
|
304
|
+
content += `
|
|
305
|
+
|
|
306
|
+
protocol: ${config.protocol}`;
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
content: [{ type: "text", text: content }]
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
);
|
|
313
|
+
server.tool(
|
|
314
|
+
"set_config",
|
|
315
|
+
"\u8BBE\u7F6E NOS \u914D\u7F6E\u9879\u5E76\u6301\u4E45\u5316\u5230\u672C\u5730\u914D\u7F6E\u6587\u4EF6 (~/.nos-conf/config.json)",
|
|
316
|
+
{
|
|
317
|
+
accessKey: z.string().optional().describe("NOS Access Key ID"),
|
|
318
|
+
accessSecret: z.string().optional().describe("NOS Access Key Secret"),
|
|
319
|
+
endpoint: z.string().optional().describe("NOS \u8BBF\u95EE\u57DF\u540D (\u7528\u4E8E\u751F\u6210 URL)"),
|
|
320
|
+
defaultBucket: z.string().optional().describe("\u9ED8\u8BA4 Bucket \u540D\u79F0"),
|
|
321
|
+
host: z.string().optional().describe("NOS API Endpoint")
|
|
322
|
+
},
|
|
323
|
+
async (config) => {
|
|
324
|
+
const updates = {};
|
|
325
|
+
if (config.accessKey) updates.accessKey = config.accessKey;
|
|
326
|
+
if (config.accessSecret) updates.accessSecret = config.accessSecret;
|
|
327
|
+
if (config.endpoint) updates.endpoint = config.endpoint;
|
|
328
|
+
if (config.defaultBucket) updates.defaultBucket = config.defaultBucket;
|
|
329
|
+
if (config.host) updates.host = config.host;
|
|
330
|
+
if (Object.keys(updates).length === 0) {
|
|
331
|
+
return {
|
|
332
|
+
content: [{ type: "text", text: "\u26A0\uFE0F \u672A\u63D0\u4F9B\u4EFB\u4F55\u914D\u7F6E\u9879" }]
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
nosConf.setConfig(updates);
|
|
336
|
+
const updatedFields = Object.keys(updates).join(", ");
|
|
337
|
+
return {
|
|
338
|
+
content: [
|
|
339
|
+
{
|
|
340
|
+
type: "text",
|
|
341
|
+
text: `\u2705 \u914D\u7F6E\u5DF2\u66F4\u65B0: ${updatedFields}
|
|
342
|
+
|
|
343
|
+
\u914D\u7F6E\u5DF2\u4FDD\u5B58\u5230 ~/.nos-conf/config.json`
|
|
344
|
+
}
|
|
345
|
+
]
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
);
|
|
349
|
+
server.tool(
|
|
350
|
+
"validate_config",
|
|
351
|
+
"\u9A8C\u8BC1\u5F53\u524D NOS \u914D\u7F6E\u662F\u5426\u5B8C\u6574\uFF0C\u8FD4\u56DE\u7F3A\u5931\u7684\u914D\u7F6E\u9879",
|
|
352
|
+
{},
|
|
353
|
+
async () => {
|
|
354
|
+
const config = nosConf.getConfig();
|
|
355
|
+
const requiredFields = [
|
|
356
|
+
"accessKey",
|
|
357
|
+
"accessSecret",
|
|
358
|
+
"endpoint",
|
|
359
|
+
"defaultBucket",
|
|
360
|
+
"host"
|
|
361
|
+
];
|
|
362
|
+
const missingFields = requiredFields.filter((field) => !config[field]);
|
|
363
|
+
if (missingFields.length === 0) {
|
|
364
|
+
return {
|
|
365
|
+
content: [
|
|
366
|
+
{
|
|
367
|
+
type: "text",
|
|
368
|
+
text: "\u2705 \u914D\u7F6E\u9A8C\u8BC1\u901A\u8FC7\uFF01\u6240\u6709\u5FC5\u9700\u914D\u7F6E\u9879\u5DF2\u8BBE\u7F6E\uFF0C\u53EF\u4EE5\u8FDB\u884C\u6587\u4EF6\u4E0A\u4F20\u3002"
|
|
369
|
+
}
|
|
370
|
+
]
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
content: [
|
|
375
|
+
{
|
|
376
|
+
type: "text",
|
|
377
|
+
text: `\u274C \u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25
|
|
378
|
+
|
|
379
|
+
\u7F3A\u5931\u7684\u914D\u7F6E\u9879:
|
|
380
|
+
${missingFields.map((f) => ` - ${f}`).join("\n")}
|
|
381
|
+
|
|
382
|
+
\u8BF7\u4F7F\u7528 set_config \u5DE5\u5177\u8BBE\u7F6E\u8FD9\u4E9B\u914D\u7F6E\u9879\uFF0C\u6216\u901A\u8FC7\u73AF\u5883\u53D8\u91CF\u63D0\u4F9B\uFF1A
|
|
383
|
+
${missingFields.map((f) => ` - NOS_${f.toUpperCase().replace(/([A-Z])/g, "_$1").replace(/^_/, "")}`).join("\n")}`
|
|
384
|
+
}
|
|
385
|
+
],
|
|
386
|
+
isError: true
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
);
|
|
390
|
+
async function main() {
|
|
391
|
+
const transport = new StdioServerTransport();
|
|
392
|
+
await server.connect(transport);
|
|
393
|
+
log.info("NOS Upload MCP Server started");
|
|
394
|
+
}
|
|
395
|
+
main().catch((error) => {
|
|
396
|
+
log.error(`Failed to start MCP server: ${error}`);
|
|
397
|
+
process.exit(1);
|
|
398
|
+
});
|
|
399
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../../core/dist/index.js"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport path from 'path'\n\nimport { nosConf, upload, log } from '@nos-upload/core'\nimport type { NOSConfig, UploadResult } from '@nos-upload/core'\n\n// 创建 MCP 服务器实例\nconst server = new McpServer({\n name: 'nos-upload',\n version: '0.2.0',\n})\n\n/**\n * Tool: nos_upload\n * 上传一个或多个文件到 NOS\n */\nserver.tool(\n 'nos_upload',\n '上传文件到 NOS (网易对象存储/Netease Object Storage)。当用户说\"上传到nos\"、\"上传到对象存储\"、\"上传图片/文件到云端\"时使用此工具。返回上传后的公开访问 URL。',\n {\n filePaths: z\n .array(z.string())\n .min(1)\n .describe('要上传的文件路径列表(绝对路径或相对路径)'),\n prefix: z\n .string()\n .optional()\n .describe('可选的对象存储路径前缀,默认使用 nos-upload-cli/{timestamp}'),\n },\n async ({ filePaths, prefix }) => {\n const timestamp = Date.now()\n const objectPrefix = prefix || `nos-upload-cli/${timestamp}`\n\n const results: UploadResult[] = []\n\n for (const filePath of filePaths) {\n const result = await upload({\n pathName: filePath,\n name: `${objectPrefix}/${path.basename(filePath)}`,\n })\n results.push(result)\n }\n\n const successful = results.filter(r => r.success)\n const failed = results.filter(r => !r.success)\n\n // 构建响应内容\n let content = ''\n\n if (successful.length > 0) {\n content += `✅ 成功上传 ${successful.length} 个文件:\\n`\n content += successful.map(r => ` - ${r.filePath} → ${r.url}`).join('\\n')\n }\n\n if (failed.length > 0) {\n if (content) content += '\\n\\n'\n content += `❌ 上传失败 ${failed.length} 个文件:\\n`\n content += failed.map(r => ` - ${r.filePath}: ${r.error}`).join('\\n')\n }\n\n return {\n content: [{ type: 'text', text: content }],\n isError: failed.length > 0 && successful.length === 0,\n }\n }\n)\n\n/**\n * Tool: get_config_status\n * 获取当前 NOS 配置状态\n */\nserver.tool(\n 'get_config_status',\n '获取当前 NOS 配置状态,显示哪些配置项已设置(不显示敏感值)',\n {},\n async () => {\n const config = nosConf.getConfig()\n const requiredFields: (keyof NOSConfig)[] = [\n 'accessKey',\n 'accessSecret',\n 'endpoint',\n 'defaultBucket',\n 'host',\n ]\n\n const status = requiredFields.map(field => {\n const value = config[field]\n const isSet = Boolean(value)\n // 对敏感字段进行脱敏处理\n let displayValue = '(未设置)'\n if (isSet) {\n if (field === 'accessSecret') {\n displayValue = '******' // 永远不显示密钥\n } else if (typeof value === 'string' && value.length > 8) {\n displayValue = value.substring(0, 4) + '...' + value.substring(value.length - 4)\n } else {\n displayValue = String(value)\n }\n }\n return ` ${field}: ${displayValue} ${isSet ? '✅' : '❌'}`\n })\n\n const allSet = requiredFields.every(field => Boolean(config[field]))\n\n let content = allSet\n ? '✅ 所有必需配置已就绪\\n\\n'\n : '⚠️ 部分配置缺失,请使用 set_config 工具设置\\n\\n'\n\n content += '配置状态:\\n' + status.join('\\n')\n\n if (config.protocol) {\n content += `\\n\\nprotocol: ${config.protocol}`\n }\n\n return {\n content: [{ type: 'text', text: content }],\n }\n }\n)\n\n/**\n * Tool: set_config\n * 设置 NOS 配置项\n */\nserver.tool(\n 'set_config',\n '设置 NOS 配置项并持久化到本地配置文件 (~/.nos-conf/config.json)',\n {\n accessKey: z.string().optional().describe('NOS Access Key ID'),\n accessSecret: z.string().optional().describe('NOS Access Key Secret'),\n endpoint: z.string().optional().describe('NOS 访问域名 (用于生成 URL)'),\n defaultBucket: z.string().optional().describe('默认 Bucket 名称'),\n host: z.string().optional().describe('NOS API Endpoint'),\n },\n async (config) => {\n // 过滤掉未提供的字段\n const updates: Partial<NOSConfig> = {}\n if (config.accessKey) updates.accessKey = config.accessKey\n if (config.accessSecret) updates.accessSecret = config.accessSecret\n if (config.endpoint) updates.endpoint = config.endpoint\n if (config.defaultBucket) updates.defaultBucket = config.defaultBucket\n if (config.host) updates.host = config.host\n\n if (Object.keys(updates).length === 0) {\n return {\n content: [{ type: 'text', text: '⚠️ 未提供任何配置项' }],\n }\n }\n\n nosConf.setConfig(updates)\n\n const updatedFields = Object.keys(updates).join(', ')\n return {\n content: [\n {\n type: 'text',\n text: `✅ 配置已更新: ${updatedFields}\\n\\n配置已保存到 ~/.nos-conf/config.json`,\n },\n ],\n }\n }\n)\n\n/**\n * Tool: validate_config\n * 验证当前配置是否完整有效\n */\nserver.tool(\n 'validate_config',\n '验证当前 NOS 配置是否完整,返回缺失的配置项',\n {},\n async () => {\n const config = nosConf.getConfig()\n const requiredFields: (keyof NOSConfig)[] = [\n 'accessKey',\n 'accessSecret',\n 'endpoint',\n 'defaultBucket',\n 'host',\n ]\n\n const missingFields = requiredFields.filter(field => !config[field])\n\n if (missingFields.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: '✅ 配置验证通过!所有必需配置项已设置,可以进行文件上传。',\n },\n ],\n }\n }\n\n return {\n content: [\n {\n type: 'text',\n text: `❌ 配置验证失败\\n\\n缺失的配置项:\\n${missingFields.map(f => ` - ${f}`).join('\\n')}\\n\\n请使用 set_config 工具设置这些配置项,或通过环境变量提供:\\n${missingFields.map(f => ` - NOS_${f.toUpperCase().replace(/([A-Z])/g, '_$1').replace(/^_/, '')}`).join('\\n')}`,\n },\n ],\n isError: true,\n }\n }\n)\n\n// 启动服务器\nasync function main() {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n log.info('NOS Upload MCP Server started')\n}\n\nmain().catch((error) => {\n log.error(`Failed to start MCP server: ${error}`)\n process.exit(1)\n})\n","// src/config.ts\nimport nconf from \"nconf\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport os from \"os\";\nimport url from \"url\";\n\n// src/log.ts\nvar log = {\n log: (message) => {\n console.log(message);\n },\n error: (message) => {\n console.error(message);\n },\n warn: (message) => {\n console.warn(message);\n },\n info: (message) => {\n console.info(message);\n }\n};\nvar log_default = log;\n\n// src/config.ts\nvar NOS_CONF_PATH = path.resolve(os.homedir(), \".nos-conf\", \"config.json\");\nvar ENV_KEY_MAP = {\n \"NOS_ACCESS_KEY\": \"accessKey\",\n \"NOS_ACCESS_SECRET\": \"accessSecret\",\n \"NOS_ENDPOINT\": \"endpoint\",\n \"NOS_DEFAULT_BUCKET\": \"defaultBucket\",\n \"NOS_HOST\": \"host\",\n \"NOS_PROTOCOL\": \"protocol\"\n};\nvar REQUIRED_KEYS = [\"accessKey\", \"accessSecret\", \"endpoint\", \"defaultBucket\", \"host\"];\nvar SENSITIVE_KEYS = [\"accessKey\", \"accessSecret\"];\nfunction maskValue(value, key) {\n if (!value) return \"\";\n if (SENSITIVE_KEYS.includes(key)) {\n if (value.length <= 4) return \"****\";\n return value.slice(0, 2) + \"***\" + value.slice(-2);\n }\n return value;\n}\nvar NOSConf = class _NOSConf {\n constructor() {\n this.nconfInstance = nconf;\n this.nconfInstance.env({\n separator: \"__\",\n match: /^NOS_/,\n transform: (obj) => {\n const newKey = ENV_KEY_MAP[obj.key];\n if (newKey) {\n return { key: newKey, value: obj.value };\n }\n return false;\n }\n }).file({ file: NOS_CONF_PATH });\n _NOSConf.ensureConfigDir();\n }\n /**\n * 确保配置目录存在(同步方式,避免竞态)\n */\n static ensureConfigDir() {\n const dirPath = path.dirname(NOS_CONF_PATH);\n try {\n fs.mkdirSync(dirPath, { recursive: true });\n } catch (err) {\n log_default.error(\"\\u521D\\u59CB\\u5316\\u914D\\u7F6E\\u76EE\\u5F55\\u5931\\u8D25\");\n }\n }\n /**\n * 获取配置\n */\n getConfig() {\n return {\n accessKey: this.nconfInstance.get(\"accessKey\"),\n accessSecret: this.nconfInstance.get(\"accessSecret\"),\n endpoint: this.nconfInstance.get(\"endpoint\"),\n defaultBucket: this.nconfInstance.get(\"defaultBucket\"),\n host: this.nconfInstance.get(\"host\"),\n protocol: this.nconfInstance.get(\"protocol\")\n };\n }\n /**\n * 设置配置(简化版,自动处理 protocol)\n */\n setConfig(config) {\n const validKeys = [\"accessKey\", \"accessSecret\", \"endpoint\", \"defaultBucket\", \"host\", \"protocol\"];\n for (const [key, value] of Object.entries(config)) {\n if (validKeys.includes(key) && value) {\n this.nconfInstance.set(key, value);\n }\n }\n if (config.endpoint && !config.protocol) {\n const parsedUrl = url.parse(config.endpoint);\n const protocol = parsedUrl.protocol?.slice(0, -1) || \"http\";\n this.nconfInstance.set(\"protocol\", protocol);\n }\n this.nconfInstance.save(void 0);\n }\n /**\n * 重置配置\n */\n reset() {\n this.nconfInstance.reset();\n this.nconfInstance.save(void 0);\n }\n /**\n * 获取配置状态(脱敏输出)\n */\n getConfigStatus() {\n const config = this.getConfig();\n const items = [];\n const missingKeys = [];\n for (const key of REQUIRED_KEYS) {\n const value = config[key];\n const isSet = !!value;\n if (!isSet) {\n missingKeys.push(key);\n }\n let source;\n const envKey = Object.entries(ENV_KEY_MAP).find(([, v]) => v === key)?.[0];\n if (envKey && process.env[envKey]) {\n source = \"env\";\n } else if (isSet) {\n source = \"file\";\n }\n items.push({\n key,\n isSet,\n source,\n preview: isSet ? maskValue(value, key) : void 0\n });\n }\n return {\n isComplete: missingKeys.length === 0,\n missingKeys,\n items\n };\n }\n /**\n * 验证配置完整性\n */\n validateConfig() {\n const config = this.getConfig();\n const missingKeys = [];\n for (const key of REQUIRED_KEYS) {\n if (!config[key]) {\n missingKeys.push(key);\n }\n }\n if (missingKeys.length === 0) {\n return {\n valid: true,\n missingKeys: [],\n message: \"Configuration is complete\"\n };\n }\n return {\n valid: false,\n missingKeys,\n message: `Missing required config: ${missingKeys.join(\", \")}`\n };\n }\n};\nvar nosConf = new NOSConf();\n\n// src/upload.ts\nimport fs2 from \"fs\";\nimport { NosClient } from \"@nos-sdk/nos-node-sdk\";\nvar client = null;\nvar lastConfigHash = \"\";\nfunction getConfigHash(config) {\n return JSON.stringify([\n config.accessKey,\n config.accessSecret,\n config.endpoint,\n config.defaultBucket,\n config.host,\n config.protocol\n ]);\n}\nfunction validateConfig(config) {\n return Boolean(\n config.accessKey && config.accessSecret && config.endpoint && config.defaultBucket && config.host\n );\n}\nfunction getClient() {\n const config = nosConf.getConfig();\n if (!validateConfig(config)) {\n throw new Error(\"NOS \\u914D\\u7F6E\\u4E0D\\u5B8C\\u6574\\uFF0C\\u8BF7\\u5148\\u914D\\u7F6E accessKey, accessSecret, endpoint, defaultBucket, host\");\n }\n const currentHash = getConfigHash(config);\n if (!client || currentHash !== lastConfigHash) {\n client = new NosClient(config);\n lastConfigHash = currentHash;\n }\n return client;\n}\nvar upload = async (file) => {\n const { pathName, name } = file;\n try {\n const nosClient = getClient();\n await nosClient.putObject({\n objectKey: name,\n body: fs2.createReadStream(decodeURIComponent(pathName))\n });\n const config = nosConf.getConfig();\n const fileUrl = `${config.endpoint}/${name}`;\n return {\n success: true,\n url: fileUrl,\n filePath: pathName\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown upload error\";\n log_default.error(`Upload failed for ${pathName}: ${errorMessage}`);\n return {\n success: false,\n error: errorMessage,\n filePath: pathName\n };\n }\n};\nvar uploadBatch = async (files) => {\n const results = await Promise.all(files.map((file) => upload(file)));\n return results;\n};\nexport {\n NOSConf,\n log,\n log_default as logDefault,\n nosConf,\n upload,\n uploadBatch\n};\n"],"mappings":"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","names":["path","path"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nos-upload-mcp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "NOS Upload MCP Server - AI-friendly file upload service for Netease Object Storage",
|
|
6
|
+
"author": "liushichuan <hzliushichuan@corp.netease.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/nicepkg/nos-upload-cli.git",
|
|
11
|
+
"directory": "packages/mcp"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"mcp",
|
|
15
|
+
"model-context-protocol",
|
|
16
|
+
"nos",
|
|
17
|
+
"netease",
|
|
18
|
+
"object-storage",
|
|
19
|
+
"upload",
|
|
20
|
+
"ai",
|
|
21
|
+
"llm",
|
|
22
|
+
"cursor",
|
|
23
|
+
"windsurf"
|
|
24
|
+
],
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"main": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"bin": {
|
|
34
|
+
"nos-upload-mcp": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
44
|
+
"@nos-sdk/nos-node-sdk": "^0.2.6",
|
|
45
|
+
"nconf": "^0.12.0",
|
|
46
|
+
"zod": "^3.24.4"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/nconf": "^0.10.3",
|
|
50
|
+
"@types/node": "^22.15.21",
|
|
51
|
+
"tsup": "^8.5.0",
|
|
52
|
+
"typescript": "^5.8.3",
|
|
53
|
+
"@nos-upload/core": "0.2.0"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "tsup",
|
|
57
|
+
"dev": "tsup --watch",
|
|
58
|
+
"start": "node dist/index.js",
|
|
59
|
+
"clean": "rm -rf dist"
|
|
60
|
+
}
|
|
61
|
+
}
|