feishu-mcp 0.0.15 → 0.0.17

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.
@@ -110,3 +110,157 @@ export function detectMimeType(buffer) {
110
110
  return 'application/octet-stream';
111
111
  }
112
112
  }
113
+ function formatExpire(seconds) {
114
+ if (!seconds || isNaN(seconds))
115
+ return '';
116
+ if (seconds < 0)
117
+ return `<span style='color:#e53935'>已过期</span> (${seconds}s)`;
118
+ const h = Math.floor(seconds / 3600);
119
+ const m = Math.floor((seconds % 3600) / 60);
120
+ const s = seconds % 60;
121
+ let str = '';
122
+ if (h)
123
+ str += h + '小时';
124
+ if (m)
125
+ str += m + '分';
126
+ if (s || (!h && !m))
127
+ str += s + '秒';
128
+ return `${str} (${seconds}s)`;
129
+ }
130
+ export function renderFeishuAuthResultHtml(data) {
131
+ const isError = data && data.error;
132
+ const now = Math.floor(Date.now() / 1000);
133
+ let expiresIn = data && data.expires_in;
134
+ let refreshExpiresIn = data && (data.refresh_token_expires_in || data.refresh_expires_in);
135
+ if (expiresIn && expiresIn > 1000000000)
136
+ expiresIn = expiresIn - now;
137
+ if (refreshExpiresIn && refreshExpiresIn > 1000000000)
138
+ refreshExpiresIn = refreshExpiresIn - now;
139
+ const tokenBlock = data && !isError ? `
140
+ <div class="card">
141
+ <h3>Token 信息</h3>
142
+ <ul class="kv-list">
143
+ <li><b>token_type:</b> <span>${data.token_type || ''}</span></li>
144
+ <li><b>access_token:</b> <span class="foldable" onclick="toggleFold(this)">点击展开/收起</span><pre class="fold scrollable">${data.access_token || ''}</pre></li>
145
+ <li><b>expires_in:</b> <span>${formatExpire(expiresIn)}</span></li>
146
+ <li><b>refresh_token:</b> <span class="foldable" onclick="toggleFold(this)">点击展开/收起</span><pre class="fold scrollable">${data.refresh_token || ''}</pre></li>
147
+ <li><b>refresh_token_expires_in:</b> <span>${formatExpire(refreshExpiresIn)}</span></li>
148
+ <li><b>scope:</b> <pre class="scope">${(data.scope || '').replace(/ /g, '\n')}</pre></li>
149
+ </ul>
150
+ <div class="success-action">
151
+ <span class="success-msg">授权成功,继续完成任务</span>
152
+ <button class="copy-btn" onclick="copySuccessMsg(this)">点击复制到粘贴板</button>
153
+ </div>
154
+ </div>
155
+ ` : '';
156
+ let userBlock = '';
157
+ const userInfo = data && data.userInfo && data.userInfo.data;
158
+ if (userInfo) {
159
+ userBlock = `
160
+ <div class="card user-card">
161
+ <div class="avatar-wrap">
162
+ <img src="${userInfo.avatar_big || userInfo.avatar_thumb || userInfo.avatar_url || ''}" class="avatar" />
163
+ </div>
164
+ <div class="user-info">
165
+ <div class="user-name">${userInfo.name || ''}</div>
166
+ <div class="user-en">${userInfo.en_name || ''}</div>
167
+ </div>
168
+ </div>
169
+ `;
170
+ }
171
+ const errorBlock = isError ? `
172
+ <div class="card error-card">
173
+ <h3>授权失败</h3>
174
+ <div class="error-msg">${escapeHtml(data.error || '')}</div>
175
+ <div class="error-code">错误码: ${data.code || ''}</div>
176
+ </div>
177
+ ` : '';
178
+ return `
179
+ <html>
180
+ <head>
181
+ <title>飞书授权结果</title>
182
+ <meta charset="utf-8"/>
183
+ <meta name="viewport" content="width=device-width,initial-scale=1"/>
184
+ <style>
185
+ body { background: #f7f8fa; font-family: 'Segoe UI', Arial, sans-serif; margin:0; padding:0; }
186
+ .container { max-width: 600px; margin: 40px auto; padding: 16px; }
187
+ .card { background: #fff; border-radius: 12px; box-shadow: 0 2px 12px #0001; margin-bottom: 24px; padding: 24px 20px; }
188
+ .user-card { display: flex; align-items: center; gap: 24px; }
189
+ .avatar-wrap { flex-shrink: 0; }
190
+ .avatar { width: 96px; height: 96px; border-radius: 50%; box-shadow: 0 2px 8px #0002; display: block; margin: 0 auto; }
191
+ .user-info { flex: 1; }
192
+ .user-name { font-size: 1.5em; font-weight: bold; margin-bottom: 4px; }
193
+ .user-en { color: #888; margin-bottom: 10px; }
194
+ .kv-list { list-style: none; padding: 0; margin: 0; }
195
+ .kv-list li { margin-bottom: 6px; word-break: break-all; }
196
+ .kv-list b { color: #1976d2; }
197
+ .scope { background: #f0f4f8; border-radius: 4px; padding: 6px; font-size: 0.95em; white-space: pre-line; }
198
+ .foldable { color: #1976d2; cursor: pointer; text-decoration: underline; margin-left: 8px; }
199
+ .fold { display: none; background: #f6f6f6; border-radius: 4px; padding: 6px; margin: 4px 0; font-size: 0.92em; max-width: 100%; overflow-x: auto; word-break: break-all; }
200
+ .scrollable { max-width: 100%; overflow-x: auto; font-family: 'Fira Mono', 'Consolas', 'Menlo', monospace; font-size: 0.93em; }
201
+ .success-action { margin-top: 18px; display: flex; align-items: center; gap: 16px; }
202
+ .success-msg { color: #388e3c; font-weight: bold; }
203
+ .copy-btn { background: #1976d2; color: #fff; border: none; border-radius: 4px; padding: 6px 16px; font-size: 1em; cursor: pointer; transition: background 0.2s; }
204
+ .copy-btn:hover { background: #125ea2; }
205
+ .error-card { border-left: 6px solid #e53935; background: #fff0f0; color: #b71c1c; }
206
+ .error-msg { font-size: 1.1em; margin-bottom: 8px; }
207
+ .error-code { color: #b71c1c; font-size: 0.95em; }
208
+ .raw-block { margin-top: 24px; }
209
+ .raw-toggle { color: #1976d2; cursor: pointer; text-decoration: underline; margin-bottom: 8px; display: inline-block; }
210
+ .raw-pre { display: none; background: #23272e; color: #fff; border-radius: 6px; padding: 12px; font-size: 0.95em; overflow-x: auto; max-width: 100%; }
211
+ @media (max-width: 700px) {
212
+ .container { max-width: 98vw; padding: 4vw; }
213
+ .card { padding: 4vw 3vw; }
214
+ .avatar { width: 64px; height: 64px; }
215
+ }
216
+ </style>
217
+ <script>
218
+ function toggleFold(el) {
219
+ var pre = el.nextElementSibling;
220
+ if (pre.style.display === 'block') {
221
+ pre.style.display = 'none';
222
+ } else {
223
+ pre.style.display = 'block';
224
+ }
225
+ }
226
+ function toggleRaw() {
227
+ var pre = document.getElementById('raw-pre');
228
+ if (pre.style.display === 'block') {
229
+ pre.style.display = 'none';
230
+ } else {
231
+ pre.style.display = 'block';
232
+ }
233
+ }
234
+ function copySuccessMsg(btn) {
235
+ var text = '授权成功,继续完成任务';
236
+ navigator.clipboard.writeText(text).then(function() {
237
+ btn.innerText = '已复制';
238
+ btn.disabled = true;
239
+ setTimeout(function() {
240
+ btn.innerText = '点击复制到粘贴板';
241
+ btn.disabled = false;
242
+ }, 2000);
243
+ });
244
+ }
245
+ </script>
246
+ </head>
247
+ <body>
248
+ <div class="container">
249
+ <h2 style="margin-bottom:24px;">飞书授权结果</h2>
250
+ ${errorBlock}
251
+ ${tokenBlock}
252
+ ${userBlock}
253
+ <div class="card raw-block">
254
+ <span class="raw-toggle" onclick="toggleRaw()">点击展开/收起原始数据</span>
255
+ <pre id="raw-pre" class="raw-pre">${escapeHtml(JSON.stringify(data, null, 2))}</pre>
256
+ </div>
257
+ </div>
258
+ </body>
259
+ </html>
260
+ `;
261
+ }
262
+ function escapeHtml(str) {
263
+ return str.replace(/[&<>"]|'/g, function (c) {
264
+ return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c] || c;
265
+ });
266
+ }
package/package.json CHANGED
@@ -1,75 +1,75 @@
1
- {
2
- "name": "feishu-mcp",
3
- "version": "0.0.15",
4
- "description": "Model Context Protocol server for Feishu integration",
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "bin": {
8
- "feishu-mcp": "./dist/cli.js"
9
- },
10
- "files": [
11
- "dist",
12
- "README.md"
13
- ],
14
- "scripts": {
15
- "build": "tsc && tsc-alias",
16
- "type-check": "tsc --noEmit",
17
- "start": "node dist/index.js",
18
- "start:cli": "cross-env NODE_ENV=cli node dist/index.js",
19
- "start:http": "node dist/index.js",
20
- "dev": "cross-env NODE_ENV=development tsx watch src/index.ts",
21
- "dev:cli": "cross-env NODE_ENV=development tsx watch src/index.ts --stdio",
22
- "lint": "eslint . --ext .ts",
23
- "format": "prettier --write \"src/**/*.ts\"",
24
- "inspect": "pnpx @modelcontextprotocol/inspector",
25
- "prepare": "pnpm run build",
26
- "pub:release": "pnpm build && npm publish"
27
- },
28
- "engines": {
29
- "node": "^20.17.0"
30
- },
31
- "repository": {
32
- "type": "git",
33
- "url": "https://github.com/cso1z/Feishu-MCP.git"
34
- },
35
- "keywords": [
36
- "feishu",
37
- "lark",
38
- "mcp",
39
- "typescript"
40
- ],
41
- "author": "cso1z",
42
- "license": "MIT",
43
- "dependencies": {
44
- "@modelcontextprotocol/sdk": "^1.13.1",
45
- "@types/yargs": "^17.0.33",
46
- "axios": "^1.7.9",
47
- "cross-env": "^7.0.3",
48
- "dotenv": "^16.4.7",
49
- "express": "^4.21.2",
50
- "form-data": "^4.0.3",
51
- "remeda": "^2.20.1",
52
- "yargs": "^17.7.2",
53
- "zod": "^3.24.2"
54
- },
55
- "devDependencies": {
56
- "@types/express": "^5.0.0",
57
- "@types/jest": "^29.5.11",
58
- "@types/node": "^20.17.0",
59
- "@typescript-eslint/eslint-plugin": "^8.24.0",
60
- "@typescript-eslint/parser": "^8.24.0",
61
- "eslint": "^9.20.1",
62
- "eslint-config-prettier": "^10.0.1",
63
- "jest": "^29.7.0",
64
- "prettier": "^3.5.0",
65
- "ts-jest": "^29.2.5",
66
- "tsc-alias": "^1.8.10",
67
- "tsx": "^4.19.2",
68
- "typescript": "^5.7.3"
69
- },
70
- "pnpm": {
71
- "overrides": {
72
- "feishu-mcp": "link:"
73
- }
74
- }
75
- }
1
+ {
2
+ "name": "feishu-mcp",
3
+ "version": "0.0.17",
4
+ "description": "Model Context Protocol server for Feishu integration",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "feishu-mcp": "./dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc && tsc-alias",
16
+ "type-check": "tsc --noEmit",
17
+ "start": "node dist/index.js",
18
+ "start:cli": "cross-env NODE_ENV=cli node dist/index.js",
19
+ "start:http": "node dist/index.js",
20
+ "dev": "cross-env NODE_ENV=development tsx watch src/index.ts",
21
+ "dev:cli": "cross-env NODE_ENV=development tsx watch src/index.ts --stdio",
22
+ "lint": "eslint . --ext .ts",
23
+ "format": "prettier --write \"src/**/*.ts\"",
24
+ "inspect": "pnpx @modelcontextprotocol/inspector",
25
+ "prepare": "pnpm run build",
26
+ "pub:release": "pnpm build && npm publish"
27
+ },
28
+ "engines": {
29
+ "node": "^20.17.0"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/cso1z/Feishu-MCP.git"
34
+ },
35
+ "keywords": [
36
+ "feishu",
37
+ "lark",
38
+ "mcp",
39
+ "typescript"
40
+ ],
41
+ "author": "cso1z",
42
+ "license": "MIT",
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^1.13.1",
45
+ "@types/yargs": "^17.0.33",
46
+ "axios": "^1.7.9",
47
+ "cross-env": "^7.0.3",
48
+ "dotenv": "^16.4.7",
49
+ "express": "^4.21.2",
50
+ "form-data": "^4.0.3",
51
+ "remeda": "^2.20.1",
52
+ "yargs": "^17.7.2",
53
+ "zod": "^3.24.2"
54
+ },
55
+ "devDependencies": {
56
+ "@types/express": "^5.0.0",
57
+ "@types/jest": "^29.5.11",
58
+ "@types/node": "^20.17.0",
59
+ "@typescript-eslint/eslint-plugin": "^8.24.0",
60
+ "@typescript-eslint/parser": "^8.24.0",
61
+ "eslint": "^9.20.1",
62
+ "eslint-config-prettier": "^10.0.1",
63
+ "jest": "^29.7.0",
64
+ "prettier": "^3.5.0",
65
+ "ts-jest": "^29.2.5",
66
+ "tsc-alias": "^1.8.10",
67
+ "tsx": "^4.19.2",
68
+ "typescript": "^5.7.3"
69
+ },
70
+ "pnpm": {
71
+ "overrides": {
72
+ "feishu-mcp": "link:"
73
+ }
74
+ }
75
+ }