feishu-mcp 0.1.6 → 0.1.7
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 -21
- package/README.md +251 -221
- package/dist/mcp/feishuMcp.js +1 -1
- package/dist/mcp/tools/feishuBlockTools.js +127 -6
- package/dist/services/baseService.js +13 -2
- package/dist/services/blockFactory.js +17 -0
- package/dist/services/feishuApiService.js +272 -2
- package/dist/types/feishuSchema.js +30 -1
- package/dist/utils/auth/tokenCacheManager.js +112 -0
- package/dist/utils/document.js +114 -114
- package/dist/utils/error.js +24 -0
- package/package.json +75 -75
|
@@ -28,9 +28,16 @@ export class TokenCacheManager {
|
|
|
28
28
|
writable: true,
|
|
29
29
|
value: void 0
|
|
30
30
|
});
|
|
31
|
+
Object.defineProperty(this, "scopeVersionCacheFile", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
configurable: true,
|
|
34
|
+
writable: true,
|
|
35
|
+
value: void 0
|
|
36
|
+
});
|
|
31
37
|
this.cache = new Map();
|
|
32
38
|
this.userTokenCacheFile = path.resolve(process.cwd(), 'user_token_cache.json');
|
|
33
39
|
this.tenantTokenCacheFile = path.resolve(process.cwd(), 'tenant_token_cache.json');
|
|
40
|
+
this.scopeVersionCacheFile = path.resolve(process.cwd(), 'scope_version_cache.json');
|
|
34
41
|
this.loadTokenCaches();
|
|
35
42
|
this.startCacheCleanupTimer();
|
|
36
43
|
}
|
|
@@ -49,6 +56,7 @@ export class TokenCacheManager {
|
|
|
49
56
|
loadTokenCaches() {
|
|
50
57
|
this.loadUserTokenCache();
|
|
51
58
|
this.loadTenantTokenCache();
|
|
59
|
+
this.loadScopeVersionCache();
|
|
52
60
|
}
|
|
53
61
|
/**
|
|
54
62
|
* 加载用户token缓存
|
|
@@ -433,4 +441,108 @@ export class TokenCacheManager {
|
|
|
433
441
|
Logger.debug(`获取到 ${keys.length} 个用户token keys`);
|
|
434
442
|
return keys;
|
|
435
443
|
}
|
|
444
|
+
/**
|
|
445
|
+
* 获取scope版本信息
|
|
446
|
+
* @param clientKey 客户端缓存键
|
|
447
|
+
* @returns scope版本信息,如果未找到则返回null
|
|
448
|
+
*/
|
|
449
|
+
getScopeVersionInfo(clientKey) {
|
|
450
|
+
const cacheKey = `scope_version:${clientKey}`;
|
|
451
|
+
const cacheItem = this.cache.get(cacheKey);
|
|
452
|
+
if (!cacheItem) {
|
|
453
|
+
Logger.debug(`Scope版本信息未找到: ${clientKey}`);
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
Logger.debug(`获取Scope版本信息成功: ${clientKey}`);
|
|
457
|
+
return cacheItem.data;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* 保存scope版本信息
|
|
461
|
+
* @param clientKey 客户端缓存键
|
|
462
|
+
* @param scopeVersionInfo scope版本信息
|
|
463
|
+
* @returns 是否成功保存
|
|
464
|
+
*/
|
|
465
|
+
saveScopeVersionInfo(clientKey, scopeVersionInfo) {
|
|
466
|
+
try {
|
|
467
|
+
const cacheKey = `scope_version:${clientKey}`;
|
|
468
|
+
const now = Date.now();
|
|
469
|
+
// scope版本信息永久有效,不设置过期时间
|
|
470
|
+
const cacheItem = {
|
|
471
|
+
data: scopeVersionInfo,
|
|
472
|
+
timestamp: now,
|
|
473
|
+
expiresAt: Number.MAX_SAFE_INTEGER // 永久有效
|
|
474
|
+
};
|
|
475
|
+
this.cache.set(cacheKey, cacheItem);
|
|
476
|
+
this.saveScopeVersionCache();
|
|
477
|
+
Logger.debug(`Scope版本信息保存成功: ${clientKey}, 版本: ${scopeVersionInfo.scopeVersion}`);
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
catch (error) {
|
|
481
|
+
Logger.error(`保存Scope版本信息失败: ${clientKey}`, error);
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* 检查scope版本是否需要校验
|
|
487
|
+
* @param clientKey 客户端缓存键
|
|
488
|
+
* @param currentScopeVersion 当前scope版本号
|
|
489
|
+
* @returns 是否需要校验
|
|
490
|
+
*/
|
|
491
|
+
shouldValidateScope(clientKey, currentScopeVersion) {
|
|
492
|
+
const scopeVersionInfo = this.getScopeVersionInfo(clientKey);
|
|
493
|
+
if (!scopeVersionInfo) {
|
|
494
|
+
Logger.debug(`Scope版本信息不存在,需要校验: ${clientKey}`);
|
|
495
|
+
return true;
|
|
496
|
+
}
|
|
497
|
+
// 如果版本号不同,需要重新校验
|
|
498
|
+
if (scopeVersionInfo.validatedVersion !== currentScopeVersion) {
|
|
499
|
+
Logger.debug(`Scope版本号已更新,需要重新校验: ${clientKey}, 旧版本: ${scopeVersionInfo.validatedVersion}, 新版本: ${currentScopeVersion}`);
|
|
500
|
+
return true;
|
|
501
|
+
}
|
|
502
|
+
Logger.debug(`Scope版本已校验过,无需重复校验: ${clientKey}, 版本: ${currentScopeVersion}`);
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* 加载scope版本缓存
|
|
507
|
+
*/
|
|
508
|
+
loadScopeVersionCache() {
|
|
509
|
+
if (fs.existsSync(this.scopeVersionCacheFile)) {
|
|
510
|
+
try {
|
|
511
|
+
const raw = fs.readFileSync(this.scopeVersionCacheFile, 'utf-8');
|
|
512
|
+
const cacheData = JSON.parse(raw);
|
|
513
|
+
let loadedCount = 0;
|
|
514
|
+
for (const key in cacheData) {
|
|
515
|
+
if (key.startsWith('scope_version:')) {
|
|
516
|
+
this.cache.set(key, cacheData[key]);
|
|
517
|
+
loadedCount++;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
Logger.info(`已加载Scope版本缓存,共 ${loadedCount} 条记录`);
|
|
521
|
+
}
|
|
522
|
+
catch (error) {
|
|
523
|
+
Logger.warn('加载Scope版本缓存失败:', error);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
Logger.info('Scope版本缓存文件不存在,将创建新的缓存');
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* 保存scope版本缓存到文件
|
|
532
|
+
*/
|
|
533
|
+
saveScopeVersionCache() {
|
|
534
|
+
const cacheData = {};
|
|
535
|
+
for (const [key, value] of this.cache.entries()) {
|
|
536
|
+
if (key.startsWith('scope_version:')) {
|
|
537
|
+
cacheData[key] = value;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
try {
|
|
541
|
+
fs.writeFileSync(this.scopeVersionCacheFile, JSON.stringify(cacheData, null, 2), 'utf-8');
|
|
542
|
+
Logger.debug('Scope版本缓存已保存到文件');
|
|
543
|
+
}
|
|
544
|
+
catch (error) {
|
|
545
|
+
Logger.warn('保存Scope版本缓存失败:', error);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
436
548
|
}
|
package/dist/utils/document.js
CHANGED
|
@@ -136,127 +136,127 @@ export function renderFeishuAuthResultHtml(data) {
|
|
|
136
136
|
expiresIn = expiresIn - now;
|
|
137
137
|
if (refreshExpiresIn && refreshExpiresIn > 1000000000)
|
|
138
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>
|
|
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
155
|
` : '';
|
|
156
156
|
let userBlock = '';
|
|
157
157
|
const userInfo = data && data.userInfo && data.userInfo.data;
|
|
158
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>
|
|
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
169
|
`;
|
|
170
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>
|
|
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
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>
|
|
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
260
|
`;
|
|
261
261
|
}
|
|
262
262
|
function escapeHtml(str) {
|
package/dist/utils/error.js
CHANGED
|
@@ -206,3 +206,27 @@ export class AuthRequiredError extends Error {
|
|
|
206
206
|
this.message = message;
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* 权限不足异常类
|
|
211
|
+
* 用于处理应用权限范围不足的情况
|
|
212
|
+
*/
|
|
213
|
+
export class ScopeInsufficientError extends Error {
|
|
214
|
+
constructor(missingScopes, message) {
|
|
215
|
+
super(message);
|
|
216
|
+
Object.defineProperty(this, "missingScopes", {
|
|
217
|
+
enumerable: true,
|
|
218
|
+
configurable: true,
|
|
219
|
+
writable: true,
|
|
220
|
+
value: void 0
|
|
221
|
+
});
|
|
222
|
+
Object.defineProperty(this, "message", {
|
|
223
|
+
enumerable: true,
|
|
224
|
+
configurable: true,
|
|
225
|
+
writable: true,
|
|
226
|
+
value: void 0
|
|
227
|
+
});
|
|
228
|
+
this.name = 'ScopeInsufficientError';
|
|
229
|
+
this.missingScopes = missingScopes;
|
|
230
|
+
this.message = message;
|
|
231
|
+
}
|
|
232
|
+
}
|
package/package.json
CHANGED
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "feishu-mcp",
|
|
3
|
-
"version": "0.1.
|
|
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.17.5",
|
|
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.1.7",
|
|
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.17.5",
|
|
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
|
+
}
|