mcp-file-uploader-cli 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/index.ts ADDED
@@ -0,0 +1,131 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
4
+ import express from "express";
5
+ import { z } from "zod";
6
+ import fs from "fs";
7
+ import path from "path";
8
+
9
+ // 定義上傳回應的介面
10
+ interface UploadResponse {
11
+ message: string;
12
+ download_url: string;
13
+ }
14
+
15
+ const server = new McpServer({
16
+ name: "file-uploader-mcp",
17
+ version: "1.0.0",
18
+ });
19
+
20
+
21
+ // 註冊檔案上傳工具
22
+ server.registerTool(
23
+ 'upload_file',
24
+ {
25
+ title: 'File Upload Tool',
26
+ description: 'Upload a file from the current working directory to the web file uploader service',
27
+ inputSchema: z.object({
28
+ filename: z.string(),
29
+ }),
30
+ outputSchema: z.object({
31
+ success: z.boolean(),
32
+ message: z.string(),
33
+ downloadUrl: z.string().optional(),
34
+ }),
35
+ },
36
+ async ({ filename }) => {
37
+ const uploaderUrl = process.env.WEB_FILE_UPLOADER_URL;
38
+ if (!uploaderUrl) {
39
+ return {
40
+ content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'WEB_FILE_UPLOADER_URL environment variable not set' }) }],
41
+ structuredContent: { success: false, message: 'WEB_FILE_UPLOADER_URL environment variable not set' }
42
+ };
43
+ }
44
+
45
+ const filePath = path.resolve(process.cwd(), filename);
46
+ if (!fs.existsSync(filePath)) {
47
+ return {
48
+ content: [{ type: 'text', text: JSON.stringify({ success: false, message: `File ${filename} not found in current directory` }) }],
49
+ structuredContent: { success: false, message: `File ${filename} not found in current directory` }
50
+ };
51
+ }
52
+
53
+ try {
54
+ const fileBuffer = fs.readFileSync(filePath);
55
+ const blob = new Blob([fileBuffer]);
56
+ const formData = new FormData();
57
+ formData.append('file', blob, path.basename(filename));
58
+
59
+ const response = await fetch(`${uploaderUrl}/upload`, {
60
+ method: 'POST',
61
+ body: formData,
62
+ });
63
+
64
+ if (response.ok) {
65
+ const result = await response.json() as UploadResponse;
66
+ const fullDownloadUrl = `${uploaderUrl}${result.download_url}`;
67
+ return {
68
+ content: [{ type: 'text', text: JSON.stringify({ success: true, message: result.message, downloadUrl: fullDownloadUrl }) }],
69
+ structuredContent: { success: true, message: result.message, downloadUrl: fullDownloadUrl }
70
+ };
71
+ } else {
72
+ const errorText = await response.text();
73
+ return {
74
+ content: [{ type: 'text', text: JSON.stringify({ success: false, message: `Upload failed: ${errorText}` }) }],
75
+ structuredContent: { success: false, message: `Upload failed: ${errorText}` }
76
+ };
77
+ }
78
+ } catch (error) {
79
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
80
+ return {
81
+ content: [{ type: 'text', text: JSON.stringify({ success: false, message: `Upload error: ${errorMessage}` }) }],
82
+ structuredContent: { success: false, message: `Upload error: ${errorMessage}` }
83
+ };
84
+ }
85
+ }
86
+ );
87
+
88
+ // 檢查是否通過命令行參數指定 HTTP 模式
89
+ const useHttp = process.argv.includes('--http');
90
+
91
+ // 如果是 HTTP 模式,啟動 HTTP Server
92
+ if (useHttp) {
93
+ const app = express();
94
+ app.use(express.json());
95
+
96
+ app.post('/mcp', async (req, res) => {
97
+
98
+ try {
99
+ const transport = new StreamableHTTPServerTransport({
100
+ sessionIdGenerator: undefined,
101
+ enableJsonResponse: true
102
+ });
103
+
104
+ res.on('close', () => {
105
+ transport.close();
106
+ });
107
+
108
+ await server.connect(transport);
109
+ await transport.handleRequest(req, res, req.body);
110
+ } catch (error) {
111
+ console.error('HTTP Transport error:', error);
112
+ res.status(500).json({ error: 'Internal server error' });
113
+ }
114
+ });
115
+
116
+
117
+ const port = parseInt(process.env.PORT || '3000');
118
+ app.listen(port, () => {
119
+ console.log(`MCP HTTP Server running on http://localhost:${port}/mcp`);
120
+
121
+ console.log(`Test with: npx @modelcontextprotocol/inspector http://localhost:${port}/mcp`);
122
+ }).on('error', error => {
123
+ console.error('Server error:', error);
124
+ process.exit(1);
125
+ });
126
+ } else {
127
+ // 否則使用 Stdio 模式
128
+ const transport = new StdioServerTransport();
129
+ await server.connect(transport);
130
+ console.error("MCP Server started and listening on stdio");
131
+ }
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "mcp-file-uploader-cli",
3
+ "version": "1.0.0",
4
+ "description": "MCP Server for file uploading functionality",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "mcp-file-uploader-cli": "dist/index.js"
8
+ },
9
+ "module": "index.ts",
10
+ "type": "module",
11
+ "scripts": {
12
+ "build": "bun build ./index.ts --outdir ./dist --target node",
13
+ "start": "bun run dist/index.js",
14
+ "dev": "bun run index.ts"
15
+ },
16
+ "devDependencies": {
17
+ "@types/bun": "latest",
18
+ "@types/express": "^5.0.5"
19
+ },
20
+ "peerDependencies": {
21
+ "typescript": "^5"
22
+ },
23
+ "dependencies": {
24
+ "@modelcontextprotocol/sdk": "^1.22.0",
25
+ "express": "^5.1.0",
26
+ "zod": "^3.23.8"
27
+ }
28
+ }
@@ -0,0 +1,68 @@
1
+ [
2
+ {
3
+ "text": "在哪里?李宜润讲师分享 LLM LINE 电商平台的落地应用商品属性自动化解决方案,请掌声欢迎!",
4
+ "start": 0.04,
5
+ "end": 10.28,
6
+ "speaker": "0"
7
+ },
8
+ {
9
+ "text": "OK 吗?",
10
+ "start": 10.28,
11
+ "end": 10.4,
12
+ "speaker": "1"
13
+ },
14
+ {
15
+ "text": "有。",
16
+ "start": 21.92,
17
+ "end": 22.08,
18
+ "speaker": "2"
19
+ },
20
+ {
21
+ "text": "好好 OK 好那就是很高兴大家来聆听这一场就是我们呃 LLM 的一个实际的落地的应用,",
22
+ "start": 22.08,
23
+ "end": 32.68,
24
+ "speaker": "1"
25
+ },
26
+ {
27
+ "text": "那就是在刚才有听到那个 cursor 啊或者是早上有蛮多场是跟 AI agent 相关的一些题目嘛,那我们这边呢就是进呃算最近啊,最近我们其实也在开发是用 LLM 来处理一些比较实务上大规模的一些问题,",
28
+ "start": 34.96,
29
+ "end": 53.04,
30
+ "speaker": "1"
31
+ },
32
+ {
33
+ "text": "那这比较属于是呃产品产品规格这一块的一个应用,那我这边呢标题就是呃我这边取题是商品规格萃取的一个大冒险,就是我觉得它很像在呃玩我们的 RPG 游戏,就是在打关,然后不断地升级,然后还挑战一些魔王关卡,对,",
34
+ "start": 53.04,
35
+ "end": 72.56,
36
+ "speaker": "1"
37
+ },
38
+ {
39
+ "text": "那好那我这边就简短介绍一下,我就没有写太多,那我叫 Villa,那我是 LINE 台湾的 Easy Data 的的 lead ,那我会负责就是我们团队的呃 developer architecture 或者是 project 相关的一些时程规划,",
40
+ "start": 72.56,
41
+ "end": 90.04,
42
+ "speaker": "1"
43
+ },
44
+ {
45
+ "text": "那我的兴趣呢就是将我们现在最前沿的一些 AI 技术转成一个赚钱的产品,因为我们是电商团队嘛,那 revenue 其实是我们的主要核心,那另外一个呢就是我很热衷于就是打造一个比较就是又可以稳定,然后又可以快速去进行的一个产品,让我们的开发团队可以呃做事情更顺畅,更快乐,对,就是希望我们的 developer 在我们的环境公司中可以发展很愉快。",
46
+ "start": 90.04,
47
+ "end": 125.32,
48
+ "speaker": "1"
49
+ },
50
+ {
51
+ "text": "好,那这边的话就是我接下来会有四个四个面向嘛,那你就把它当成在讲一个 RPG 的游戏,就是我有序章啊,然后我们要打关要学一些技能,因为有很多人都会说 LLM 就像呃魔法一样,",
52
+ "start": 127.44,
53
+ "end": 140.16,
54
+ "speaker": "1"
55
+ },
56
+ {
57
+ "text": "那后面会有一些实战的呃经验,还有最后会给大家一些 takeaway 。",
58
+ "start": 140.16,
59
+ "end": 150.04,
60
+ "speaker": "1"
61
+ },
62
+ {
63
+ "text": "那一开始呢,其实我们就是收到了需求端这边的一个呃提醒,算是他们需要有人去呃突破这个关卡,这个瓶颈,那就是因为我们呃电商团队其实有非常多的产品线,那这一个商品规格萃取主要是在 LINE 购物,呃 LINE 购物本身的商品其实有2000多万件,商品非常非常多,那加上说其实 LINE 购物本身是一个导购的平台,呃串接了非常多不同的厂商。",
64
+ "start": 150.04,
65
+ "end": 180.04,
66
+ "speaker": "1"
67
+ }
68
+ ]
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedIndexedAccess": true,
22
+ "noImplicitOverride": true,
23
+
24
+ // Some stricter flags (disabled by default)
25
+ "noUnusedLocals": false,
26
+ "noUnusedParameters": false,
27
+ "noPropertyAccessFromIndexSignature": false
28
+ }
29
+ }
package/vercel.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "version": 2,
3
+ "builds": [
4
+ {
5
+ "src": "dist/index.js",
6
+ "use": "@vercel/node"
7
+ }
8
+ ],
9
+ "routes": [
10
+ {
11
+ "src": "/(.*)",
12
+ "dest": "dist/index.js"
13
+ }
14
+ ],
15
+ "env": {
16
+ "WEB_FILE_UPLOADER_URL": "@web_file_uploader_url"
17
+ }
18
+ }