feishu-mcp 0.1.0 → 0.1.2
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 +264 -257
- package/dist/mcp/feishuMcp.js +1 -1
- package/dist/mcp/tools/feishuBlockTools.js +76 -2
- package/dist/mcp/tools/feishuTools.js +43 -5
- package/dist/server.js +117 -1
- package/dist/services/baseService.js +5 -1
- package/dist/services/blockFactory.js +109 -0
- package/dist/services/feishuApiService.js +151 -9
- package/dist/types/feishuSchema.js +37 -1
- package/dist/utils/cache.js +3 -0
- package/dist/utils/document.js +114 -114
- package/dist/utils/paramUtils.js +38 -0
- package/package.json +75 -75
|
@@ -103,7 +103,7 @@ export const ListBlockSchema = z.object({
|
|
|
103
103
|
align: AlignSchemaWithValidation,
|
|
104
104
|
});
|
|
105
105
|
// 块类型枚举 - 用于批量创建块工具
|
|
106
|
-
export const BlockTypeEnum = z.string().describe("Block type (required). Supports: 'text', 'code', 'heading', 'list', 'image',
|
|
106
|
+
export const BlockTypeEnum = z.string().describe("Block type (required). Supports: 'text', 'code', 'heading', 'list', 'image','mermaid',as well as 'heading1' through 'heading9'. " +
|
|
107
107
|
"For headings, we recommend using 'heading' with level property, but 'heading1'-'heading9' are also supported. " +
|
|
108
108
|
"For images, use 'image' to create empty image blocks that can be filled later. " +
|
|
109
109
|
"For text blocks, you can include both regular text and equation elements in the same block.");
|
|
@@ -116,6 +116,14 @@ export const ImageBlockSchema = z.object({
|
|
|
116
116
|
width: ImageWidthSchema,
|
|
117
117
|
height: ImageHeightSchema
|
|
118
118
|
});
|
|
119
|
+
// Mermaid代码参数定义
|
|
120
|
+
export const MermaidCodeSchema = z.string().describe('Mermaid code (required). The complete Mermaid chart code, e.g. \'graph TD; A-->B;\'. ' +
|
|
121
|
+
'IMPORTANT: When node text contains special characters like parentheses (), brackets [], or arrows -->, ' +
|
|
122
|
+
'wrap the entire text in double quotes to prevent parsing errors. ' +
|
|
123
|
+
'Example: A["finish()/返回键"] instead of A[finish()/返回键].');
|
|
124
|
+
export const MermaidBlockSchema = z.object({
|
|
125
|
+
code: MermaidCodeSchema,
|
|
126
|
+
});
|
|
119
127
|
// 块配置定义 - 用于批量创建块工具
|
|
120
128
|
export const BlockConfigSchema = z.object({
|
|
121
129
|
blockType: BlockTypeEnum,
|
|
@@ -125,9 +133,37 @@ export const BlockConfigSchema = z.object({
|
|
|
125
133
|
z.object({ heading: HeadingBlockSchema }).describe("Heading block options. Used with both 'heading' and 'headingN' formats."),
|
|
126
134
|
z.object({ list: ListBlockSchema }).describe("List block options. Used when blockType is 'list'."),
|
|
127
135
|
z.object({ image: ImageBlockSchema }).describe("Image block options. Used when blockType is 'image'. Creates empty image blocks."),
|
|
136
|
+
z.object({ mermaid: MermaidBlockSchema }).describe("Mermaid block options. Used when blockType is 'mermaid'."),
|
|
128
137
|
z.record(z.any()).describe("Fallback for any other block options")
|
|
129
138
|
]).describe('Options for the specific block type. Provide the corresponding options object based on blockType.'),
|
|
130
139
|
});
|
|
140
|
+
// 表格列数参数定义
|
|
141
|
+
export const TableColumnSizeSchema = z.number().min(1).describe('Table column size (required). The number of columns in the table. Must be at least 1.');
|
|
142
|
+
// 表格行数参数定义
|
|
143
|
+
export const TableRowSizeSchema = z.number().min(1).describe('Table row size (required). The number of rows in the table. Must be at least 1.');
|
|
144
|
+
// 表格单元格坐标参数定义
|
|
145
|
+
export const TableCellCoordinateSchema = z.object({
|
|
146
|
+
row: z.number().min(0).describe('Row coordinate (0-based). The row position of the cell in the table.'),
|
|
147
|
+
column: z.number().min(0).describe('Column coordinate (0-based). The column position of the cell in the table.')
|
|
148
|
+
});
|
|
149
|
+
// 表格单元格内容配置定义
|
|
150
|
+
export const TableCellContentSchema = z.object({
|
|
151
|
+
coordinate: TableCellCoordinateSchema,
|
|
152
|
+
content: BlockConfigSchema
|
|
153
|
+
});
|
|
154
|
+
// 表格创建参数定义 - 专门用于创建表格块工具
|
|
155
|
+
export const TableCreateSchema = z.object({
|
|
156
|
+
columnSize: TableColumnSizeSchema,
|
|
157
|
+
rowSize: TableRowSizeSchema,
|
|
158
|
+
cells: z.array(TableCellContentSchema).optional().describe('Array of cell configurations (optional). Each cell specifies its position (row, column) and content block configuration. ' +
|
|
159
|
+
'If not provided, empty text blocks will be created for all cells. ' +
|
|
160
|
+
'IMPORTANT: Multiple cells can have the same coordinates (row, column) - when this happens, ' +
|
|
161
|
+
'the content blocks will be added sequentially to the same cell, allowing you to create rich content ' +
|
|
162
|
+
'with multiple blocks (text, code, images, etc.) within a single cell. ' +
|
|
163
|
+
'Example: [{coordinate:{row:0,column:0}, content:{blockType:"text", options:{text:{textStyles:[{text:"Header"}]}}}, ' +
|
|
164
|
+
'{coordinate:{row:0,column:0}, content:{blockType:"code", options:{code:{code:"console.log(\'hello\')", language:30}}}}] ' +
|
|
165
|
+
'will add both a text block and a code block to cell (0,0).')
|
|
166
|
+
});
|
|
131
167
|
// 媒体ID参数定义
|
|
132
168
|
export const MediaIdSchema = z.string().describe('Media ID (required). The unique identifier for a media resource (image, file, etc.) in Feishu. ' +
|
|
133
169
|
'Usually obtained from image blocks or file references in documents. ' +
|
package/dist/utils/cache.js
CHANGED
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/paramUtils.js
CHANGED
|
@@ -159,6 +159,44 @@ export class ParamUtils {
|
|
|
159
159
|
// 限制在1-9范围内
|
|
160
160
|
return Math.max(1, Math.min(9, level));
|
|
161
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* 处理画板ID参数
|
|
164
|
+
* 验证并规范化画板ID,支持从URL中提取
|
|
165
|
+
*
|
|
166
|
+
* @param whiteboardId 画板ID或URL
|
|
167
|
+
* @returns 规范化的画板ID
|
|
168
|
+
* @throws 如果画板ID无效则抛出错误
|
|
169
|
+
*/
|
|
170
|
+
static processWhiteboardId(whiteboardId) {
|
|
171
|
+
if (!whiteboardId) {
|
|
172
|
+
throw new ParamValidationError('whiteboardId', '画板ID不能为空');
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
// 从URL中提取画板ID
|
|
176
|
+
let normalizedWhiteboardId = whiteboardId;
|
|
177
|
+
if (whiteboardId.includes('feishu.cn/board/')) {
|
|
178
|
+
// 从URL中提取画板ID
|
|
179
|
+
const matches = whiteboardId.match(/board\/([^\/\?]+)/);
|
|
180
|
+
if (matches) {
|
|
181
|
+
normalizedWhiteboardId = matches[1];
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
throw new ParamValidationError('whiteboardId', '无法从URL中提取画板ID');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// 验证画板ID格式(基本格式检查)
|
|
188
|
+
if (!/^[a-zA-Z0-9_-]{5,}$/.test(normalizedWhiteboardId)) {
|
|
189
|
+
throw new ParamValidationError('whiteboardId', '画板ID格式无效');
|
|
190
|
+
}
|
|
191
|
+
return normalizedWhiteboardId;
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
if (error instanceof ParamValidationError) {
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
throw new ParamValidationError('whiteboardId', formatErrorMessage(error));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
162
200
|
/**
|
|
163
201
|
* 批量处理通用参数
|
|
164
202
|
* 验证并规范化常用参数集
|
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.
|
|
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.2",
|
|
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
|
+
}
|