@jurislm/judicial-mcp 1.3.0 → 1.6.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/src/tools.js DELETED
@@ -1,360 +0,0 @@
1
- /**
2
- * @file src/tools.js
3
- * @description MCP 工具定義和配置
4
- */
5
-
6
- const axios = require('axios');
7
-
8
- /**
9
- * 輸入驗證工具函數
10
- */
11
- const validateInput = {
12
- /**
13
- * 驗證必要參數是否存在
14
- * @param {Object} params - 參數物件
15
- * @param {Array<string>} required - 必要參數名稱陣列
16
- */
17
- required(params, required) {
18
- const missing = required.filter(key => !params[key]);
19
- if (missing.length > 0) {
20
- throw new Error(`缺少必要參數: ${missing.join(', ')}`);
21
- }
22
- },
23
-
24
- /**
25
- * 驗證 Token 格式
26
- * @param {string} token - 授權 Token
27
- */
28
- token(token) {
29
- if (!token || typeof token !== 'string' || token.trim().length === 0) {
30
- throw new Error('無效的授權 Token');
31
- }
32
- },
33
-
34
- /**
35
- * 驗證數字字串格式
36
- * @param {string} value - 要驗證的值
37
- * @param {string} fieldName - 欄位名稱
38
- */
39
- numericString(value, fieldName) {
40
- if (value && !/^\d+$/.test(value)) {
41
- throw new Error(`${fieldName} 必須是數字字串`);
42
- }
43
- }
44
- };
45
-
46
- /**
47
- * 司法院 API 基礎配置
48
- */
49
- const JUDICIAL_API_BASE = 'https://data.judicial.gov.tw/jdg/api';
50
- const OPENDATA_API_BASE = 'https://opendata.judicial.gov.tw';
51
-
52
- /**
53
- * 工具定義配置
54
- */
55
- const TOOLS_CONFIG = {
56
- auth_token: {
57
- name: 'auth_token',
58
- description: '取得司法院裁判書查詢系統的授權 Token。支援使用環境變數中的預設帳密。',
59
- inputSchema: {
60
- type: 'object',
61
- properties: {
62
- user: {
63
- type: 'string',
64
- description: '使用者帳號(可選,未提供時使用環境變數 JUDICIAL_USER)'
65
- },
66
- password: {
67
- type: 'string',
68
- description: '使用者密碼(可選,未提供時使用環境變數 JUDICIAL_PASSWORD)'
69
- }
70
- },
71
- additionalProperties: false
72
- }
73
- },
74
-
75
- list_judgments: {
76
- name: 'list_judgments',
77
- description: '取得裁判書異動清單,包含新增、修改或刪除的裁判書資訊。',
78
- inputSchema: {
79
- type: 'object',
80
- properties: {
81
- token: {
82
- type: 'string',
83
- description: '從 auth_token 工具取得的授權 Token'
84
- }
85
- },
86
- required: ['token'],
87
- additionalProperties: false
88
- }
89
- },
90
-
91
- get_judgment: {
92
- name: 'get_judgment',
93
- description: '取得特定裁判書的完整內容,包含裁判全文。',
94
- inputSchema: {
95
- type: 'object',
96
- properties: {
97
- token: {
98
- type: 'string',
99
- description: '從 auth_token 工具取得的授權 Token'
100
- },
101
- jid: {
102
- type: 'string',
103
- description: '從 list_judgments 工具取得的裁判書 ID'
104
- }
105
- },
106
- required: ['token', 'jid'],
107
- additionalProperties: false
108
- }
109
- },
110
-
111
- list_categories: {
112
- name: 'list_categories',
113
- description: '取得司法院開放資料平台的所有主題分類清單。',
114
- inputSchema: {
115
- type: 'object',
116
- properties: {
117
- token: {
118
- type: 'string',
119
- description: '從 member_token 工具取得的會員授權 Token'
120
- }
121
- },
122
- required: ['token'],
123
- additionalProperties: false
124
- }
125
- },
126
-
127
- list_resources: {
128
- name: 'list_resources',
129
- description: '取得指定分類下的所有資料源清單。',
130
- inputSchema: {
131
- type: 'object',
132
- properties: {
133
- categoryNo: {
134
- type: 'string',
135
- description: '從 list_categories 工具取得的分類編號'
136
- },
137
- token: {
138
- type: 'string',
139
- description: '從 member_token 工具取得的會員授權 Token'
140
- }
141
- },
142
- required: ['categoryNo', 'token'],
143
- additionalProperties: false
144
- }
145
- },
146
-
147
- download_file: {
148
- name: 'download_file',
149
- description: '下載指定檔案集的資料檔案,支援分頁參數。',
150
- inputSchema: {
151
- type: 'object',
152
- properties: {
153
- fileSetId: {
154
- type: 'string',
155
- description: '從 list_resources 工具取得的檔案集 ID'
156
- },
157
- top: {
158
- type: 'string',
159
- description: '限制回傳筆數(可選)',
160
- pattern: '^[0-9]+$'
161
- },
162
- skip: {
163
- type: 'string',
164
- description: '跳過筆數,用於分頁(可選)',
165
- pattern: '^[0-9]+$'
166
- },
167
- token: {
168
- type: 'string',
169
- description: '從 member_token 工具取得的會員授權 Token'
170
- }
171
- },
172
- required: ['fileSetId', 'token'],
173
- additionalProperties: false
174
- }
175
- },
176
-
177
- member_token: {
178
- name: 'member_token',
179
- description: '取得司法院開放平台的會員授權 Token,用於存取會員專屬資源。',
180
- inputSchema: {
181
- type: 'object',
182
- properties: {
183
- user: {
184
- type: 'string',
185
- description: '使用者帳號(可選,未提供時使用環境變數 JUDICIAL_USER)'
186
- },
187
- password: {
188
- type: 'string',
189
- description: '使用者密碼(可選,未提供時使用環境變數 JUDICIAL_PASSWORD)'
190
- }
191
- },
192
- additionalProperties: false
193
- }
194
- }
195
- };
196
-
197
- /**
198
- * 工具執行器映射
199
- */
200
- const TOOL_HANDLERS = {
201
- async auth_token(args) {
202
- const user = args.user || process.env.JUDICIAL_USER;
203
- const password = args.password || process.env.JUDICIAL_PASSWORD;
204
-
205
- if (!user || !password) {
206
- throw new Error('未提供使用者帳號或密碼,且環境變數 JUDICIAL_USER 或 JUDICIAL_PASSWORD 未設定');
207
- }
208
-
209
- try {
210
- const result = await axios.post(`${JUDICIAL_API_BASE}/Auth`, { user, password });
211
- return {
212
- success: true,
213
- message: '授權成功',
214
- data: result.data
215
- };
216
- } catch (error) {
217
- throw new Error(`授權失敗: ${error.response?.data?.message || error.message}`);
218
- }
219
- },
220
-
221
- async list_judgments(args) {
222
- validateInput.required(args, ['token']);
223
- validateInput.token(args.token);
224
-
225
- try {
226
- const result = await axios.post(`${JUDICIAL_API_BASE}/JList`, { token: args.token });
227
- return {
228
- success: true,
229
- message: '取得裁判書異動清單成功',
230
- data: result.data
231
- };
232
- } catch (error) {
233
- throw new Error(`取得裁判書清單失敗: ${error.response?.data?.message || error.message}`);
234
- }
235
- },
236
-
237
- async get_judgment(args) {
238
- validateInput.required(args, ['token', 'jid']);
239
- validateInput.token(args.token);
240
-
241
- try {
242
- const result = await axios.post(`${JUDICIAL_API_BASE}/JDoc`, {
243
- token: args.token,
244
- j: args.jid
245
- });
246
- return {
247
- success: true,
248
- message: '取得裁判書內容成功',
249
- data: result.data
250
- };
251
- } catch (error) {
252
- throw new Error(`取得裁判書內容失敗: ${error.response?.data?.message || error.message}`);
253
- }
254
- },
255
-
256
- async list_categories(args) {
257
- validateInput.required(args, ['token']);
258
- validateInput.token(args.token);
259
-
260
- try {
261
- const result = await axios.get(`${OPENDATA_API_BASE}/data/api/rest/categories`, {
262
- headers: {
263
- 'Authorization': `Bearer ${args.token}`
264
- }
265
- });
266
- return {
267
- success: true,
268
- message: '取得主題分類清單成功',
269
- data: result.data
270
- };
271
- } catch (error) {
272
- throw new Error(`取得主題分類清單失敗: ${error.response?.data?.message || error.message}`);
273
- }
274
- },
275
-
276
- async list_resources(args) {
277
- validateInput.required(args, ['categoryNo', 'token']);
278
- validateInput.token(args.token);
279
-
280
- try {
281
- const url = `${OPENDATA_API_BASE}/data/api/rest/categories/${args.categoryNo}/resources`;
282
- const result = await axios.get(url, {
283
- headers: {
284
- 'Authorization': `Bearer ${args.token}`
285
- }
286
- });
287
- return {
288
- success: true,
289
- message: `取得分類 ${args.categoryNo} 的資料源清單成功`,
290
- data: result.data
291
- };
292
- } catch (error) {
293
- throw new Error(`取得資料源清單失敗: ${error.response?.data?.message || error.message}`);
294
- }
295
- },
296
-
297
- async download_file(args) {
298
- validateInput.required(args, ['fileSetId', 'token']);
299
- validateInput.numericString(args.top, 'top');
300
- validateInput.numericString(args.skip, 'skip');
301
- validateInput.token(args.token);
302
-
303
- try {
304
- const params = new URLSearchParams();
305
- if (args.top) params.append('top', args.top);
306
- if (args.skip) params.append('skip', args.skip);
307
-
308
- let url = `${OPENDATA_API_BASE}/api/FilesetLists/${args.fileSetId}/file`;
309
- if (params.toString()) {
310
- url += `?${params.toString()}`;
311
- }
312
-
313
- const result = await axios.get(url, {
314
- responseType: 'arraybuffer',
315
- headers: {
316
- 'Authorization': `Bearer ${args.token}`
317
- }
318
- });
319
- return {
320
- success: true,
321
- message: '檔案下載成功',
322
- data: {
323
- size: result.data.length,
324
- contentType: result.headers['content-type'],
325
- base64: Buffer.from(result.data).toString('base64')
326
- }
327
- };
328
- } catch (error) {
329
- throw new Error(`檔案下載失敗: ${error.response?.data?.message || error.message}`);
330
- }
331
- },
332
-
333
- async member_token(args) {
334
- const user = args.user || process.env.JUDICIAL_USER;
335
- const password = args.password || process.env.JUDICIAL_PASSWORD;
336
-
337
- if (!user || !password) {
338
- throw new Error('未提供使用者帳號或密碼,且環境變數 JUDICIAL_USER 或 JUDICIAL_PASSWORD 未設定');
339
- }
340
-
341
- try {
342
- const result = await axios.post(`${OPENDATA_API_BASE}/api/MemberTokens`, {
343
- memberAccount: user,
344
- pwd: password
345
- });
346
- return {
347
- success: true,
348
- message: '開放平台會員授權成功',
349
- data: result.data
350
- };
351
- } catch (error) {
352
- throw new Error(`會員授權失敗: ${error.response?.data?.message || error.message}`);
353
- }
354
- }
355
- };
356
-
357
- module.exports = {
358
- TOOLS_CONFIG,
359
- TOOL_HANDLERS
360
- };