@xiaozhi-client/datetime-mcp 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/LICENSE +21 -0
- package/README.md +48 -0
- package/dist/index.js +299 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 shenjingnan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# @xiaozhi-client/datetime-mcp
|
|
2
|
+
|
|
3
|
+
小智日期时间 MCP 服务,提供日期时间处理功能。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @xiaozhi-client/datetime-mcp
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 使用
|
|
12
|
+
|
|
13
|
+
### 在 xiaozhi.config.json 中配置
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"mcpServers": {
|
|
18
|
+
"datetime": {
|
|
19
|
+
"command": "npx",
|
|
20
|
+
"args": ["-y", "@xiaozhi-client/datetime-mcp"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 提供的工具
|
|
27
|
+
|
|
28
|
+
#### get_current_time
|
|
29
|
+
获取当前时间,支持多种格式(iso、timestamp、locale、time-only)
|
|
30
|
+
|
|
31
|
+
#### get_current_date
|
|
32
|
+
获取当前日期,支持多种格式(iso、locale、date-only、yyyy-mm-dd)
|
|
33
|
+
|
|
34
|
+
#### format_datetime
|
|
35
|
+
将给定的日期时间字符串或时间戳格式化为指定格式
|
|
36
|
+
|
|
37
|
+
#### add_time
|
|
38
|
+
在给定的日期时间基础上增加或减少时间
|
|
39
|
+
|
|
40
|
+
## 开发
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# 构建
|
|
44
|
+
pnpm --filter @xiaozhi-client/datetime-mcp build
|
|
45
|
+
|
|
46
|
+
# 本地运行
|
|
47
|
+
node dist/index.js
|
|
48
|
+
```
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
var logger = {
|
|
9
|
+
info: (message) => {
|
|
10
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
11
|
+
console.error(`${timestamp} - DateTime - INFO - ${message}`);
|
|
12
|
+
},
|
|
13
|
+
error: (message) => {
|
|
14
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
15
|
+
console.error(`${timestamp} - DateTime - ERROR - ${message}`);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var server = new McpServer({
|
|
19
|
+
name: "@xiaozhi-client/datetime-mcp",
|
|
20
|
+
version: "1.0.0"
|
|
21
|
+
});
|
|
22
|
+
server.tool(
|
|
23
|
+
"get_current_time",
|
|
24
|
+
"\u83B7\u53D6\u5F53\u524D\u65F6\u95F4\uFF0C\u652F\u6301\u591A\u79CD\u683C\u5F0F",
|
|
25
|
+
{
|
|
26
|
+
format: z.string().optional().describe("\u65F6\u95F4\u683C\u5F0F\uFF1A'iso'\uFF08\u9ED8\u8BA4\uFF09\u3001'timestamp'\u3001'locale'\u3001'time-only'")
|
|
27
|
+
},
|
|
28
|
+
async ({ format = "iso" }) => {
|
|
29
|
+
try {
|
|
30
|
+
const now = /* @__PURE__ */ new Date();
|
|
31
|
+
let result;
|
|
32
|
+
switch (format) {
|
|
33
|
+
case "timestamp":
|
|
34
|
+
result = now.getTime();
|
|
35
|
+
break;
|
|
36
|
+
case "locale":
|
|
37
|
+
result = now.toLocaleString();
|
|
38
|
+
break;
|
|
39
|
+
case "time-only":
|
|
40
|
+
result = now.toLocaleTimeString();
|
|
41
|
+
break;
|
|
42
|
+
case "iso":
|
|
43
|
+
default:
|
|
44
|
+
result = now.toISOString();
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
logger.info(`\u83B7\u53D6\u5F53\u524D\u65F6\u95F4\uFF0C\u683C\u5F0F\uFF1A${format}\uFF0C\u7ED3\u679C\uFF1A${result}`);
|
|
48
|
+
return {
|
|
49
|
+
content: [
|
|
50
|
+
{
|
|
51
|
+
type: "text",
|
|
52
|
+
text: JSON.stringify({
|
|
53
|
+
success: true,
|
|
54
|
+
result,
|
|
55
|
+
format
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
};
|
|
60
|
+
} catch (error) {
|
|
61
|
+
logger.error(`\u83B7\u53D6\u5F53\u524D\u65F6\u95F4\u9519\u8BEF\uFF1A${error.message}`);
|
|
62
|
+
return {
|
|
63
|
+
content: [
|
|
64
|
+
{
|
|
65
|
+
type: "text",
|
|
66
|
+
text: JSON.stringify({
|
|
67
|
+
success: false,
|
|
68
|
+
error: error.message
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
isError: true
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
server.tool(
|
|
78
|
+
"get_current_date",
|
|
79
|
+
"\u83B7\u53D6\u5F53\u524D\u65E5\u671F\uFF0C\u652F\u6301\u591A\u79CD\u683C\u5F0F",
|
|
80
|
+
{
|
|
81
|
+
format: z.string().optional().describe("\u65E5\u671F\u683C\u5F0F\uFF1A'iso'\uFF08\u9ED8\u8BA4\uFF09\u3001'locale'\u3001'date-only'\u3001'yyyy-mm-dd'")
|
|
82
|
+
},
|
|
83
|
+
async ({ format = "iso" }) => {
|
|
84
|
+
try {
|
|
85
|
+
const now = /* @__PURE__ */ new Date();
|
|
86
|
+
let result;
|
|
87
|
+
switch (format) {
|
|
88
|
+
case "locale":
|
|
89
|
+
result = now.toLocaleDateString();
|
|
90
|
+
break;
|
|
91
|
+
case "date-only":
|
|
92
|
+
result = now.toDateString();
|
|
93
|
+
break;
|
|
94
|
+
case "yyyy-mm-dd":
|
|
95
|
+
result = now.toISOString().split("T")[0];
|
|
96
|
+
break;
|
|
97
|
+
case "iso":
|
|
98
|
+
default:
|
|
99
|
+
result = now.toISOString();
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
logger.info(`\u83B7\u53D6\u5F53\u524D\u65E5\u671F\uFF0C\u683C\u5F0F\uFF1A${format}\uFF0C\u7ED3\u679C\uFF1A${result}`);
|
|
103
|
+
return {
|
|
104
|
+
content: [
|
|
105
|
+
{
|
|
106
|
+
type: "text",
|
|
107
|
+
text: JSON.stringify({
|
|
108
|
+
success: true,
|
|
109
|
+
result,
|
|
110
|
+
format
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
};
|
|
115
|
+
} catch (error) {
|
|
116
|
+
logger.error(`\u83B7\u53D6\u5F53\u524D\u65E5\u671F\u9519\u8BEF\uFF1A${error.message}`);
|
|
117
|
+
return {
|
|
118
|
+
content: [
|
|
119
|
+
{
|
|
120
|
+
type: "text",
|
|
121
|
+
text: JSON.stringify({
|
|
122
|
+
success: false,
|
|
123
|
+
error: error.message
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
isError: true
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
server.tool(
|
|
133
|
+
"format_datetime",
|
|
134
|
+
"\u5C06\u7ED9\u5B9A\u7684\u65E5\u671F\u65F6\u95F4\u5B57\u7B26\u4E32\u6216\u65F6\u95F4\u6233\u683C\u5F0F\u5316\u4E3A\u6307\u5B9A\u683C\u5F0F",
|
|
135
|
+
{
|
|
136
|
+
datetime: z.string().describe("\u8981\u683C\u5F0F\u5316\u7684\u65E5\u671F\u65F6\u95F4\u5B57\u7B26\u4E32\u6216\u65F6\u95F4\u6233"),
|
|
137
|
+
format: z.string().optional().describe("\u8F93\u51FA\u683C\u5F0F\uFF1A'iso'\u3001'locale'\u3001'timestamp'\u3001'yyyy-mm-dd'\u3001'custom'"),
|
|
138
|
+
custom_format: z.string().optional().describe("\u81EA\u5B9A\u4E49\u683C\u5F0F\u5B57\u7B26\u4E32\uFF08\u5F53 format \u4E3A 'custom' \u65F6\u4F7F\u7528\uFF09")
|
|
139
|
+
},
|
|
140
|
+
async ({ datetime, format = "iso", custom_format }) => {
|
|
141
|
+
try {
|
|
142
|
+
let date;
|
|
143
|
+
if (!isNaN(Number(datetime))) {
|
|
144
|
+
date = new Date(Number(datetime));
|
|
145
|
+
} else {
|
|
146
|
+
date = new Date(datetime);
|
|
147
|
+
}
|
|
148
|
+
if (isNaN(date.getTime())) {
|
|
149
|
+
throw new Error("\u65E0\u6548\u7684\u65E5\u671F\u65F6\u95F4\u683C\u5F0F");
|
|
150
|
+
}
|
|
151
|
+
let result;
|
|
152
|
+
switch (format) {
|
|
153
|
+
case "timestamp":
|
|
154
|
+
result = date.getTime();
|
|
155
|
+
break;
|
|
156
|
+
case "locale":
|
|
157
|
+
result = date.toLocaleString();
|
|
158
|
+
break;
|
|
159
|
+
case "yyyy-mm-dd":
|
|
160
|
+
result = date.toISOString().split("T")[0];
|
|
161
|
+
break;
|
|
162
|
+
case "custom":
|
|
163
|
+
if (custom_format) {
|
|
164
|
+
result = custom_format.replace("YYYY", String(date.getFullYear())).replace("MM", String(date.getMonth() + 1).padStart(2, "0")).replace("DD", String(date.getDate()).padStart(2, "0")).replace("HH", String(date.getHours()).padStart(2, "0")).replace("mm", String(date.getMinutes()).padStart(2, "0")).replace("ss", String(date.getSeconds()).padStart(2, "0"));
|
|
165
|
+
} else {
|
|
166
|
+
result = date.toISOString();
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
case "iso":
|
|
170
|
+
default:
|
|
171
|
+
result = date.toISOString();
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
logger.info(`\u683C\u5F0F\u5316\u65E5\u671F\u65F6\u95F4\uFF1A${datetime} \u4E3A\u683C\u5F0F\uFF1A${format}\uFF0C\u7ED3\u679C\uFF1A${result}`);
|
|
175
|
+
return {
|
|
176
|
+
content: [
|
|
177
|
+
{
|
|
178
|
+
type: "text",
|
|
179
|
+
text: JSON.stringify({
|
|
180
|
+
success: true,
|
|
181
|
+
result,
|
|
182
|
+
original: datetime,
|
|
183
|
+
format
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
};
|
|
188
|
+
} catch (error) {
|
|
189
|
+
logger.error(`\u683C\u5F0F\u5316\u65E5\u671F\u65F6\u95F4\u9519\u8BEF\uFF1A${error.message}`);
|
|
190
|
+
return {
|
|
191
|
+
content: [
|
|
192
|
+
{
|
|
193
|
+
type: "text",
|
|
194
|
+
text: JSON.stringify({
|
|
195
|
+
success: false,
|
|
196
|
+
error: error.message
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
],
|
|
200
|
+
isError: true
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
server.tool(
|
|
206
|
+
"add_time",
|
|
207
|
+
"\u5728\u7ED9\u5B9A\u7684\u65E5\u671F\u65F6\u95F4\u57FA\u7840\u4E0A\u589E\u52A0\u6216\u51CF\u5C11\u65F6\u95F4",
|
|
208
|
+
{
|
|
209
|
+
datetime: z.string().describe("\u57FA\u51C6\u65E5\u671F\u65F6\u95F4\u5B57\u7B26\u4E32\u6216\u65F6\u95F4\u6233"),
|
|
210
|
+
amount: z.number().describe("\u8981\u589E\u52A0\uFF08\u6B63\u6570\uFF09\u6216\u51CF\u5C11\uFF08\u8D1F\u6570\uFF09\u7684\u6570\u91CF"),
|
|
211
|
+
unit: z.string().describe("\u65F6\u95F4\u5355\u4F4D\uFF1A'milliseconds'\u3001'seconds'\u3001'minutes'\u3001'hours'\u3001'days'\u3001'weeks'\u3001'months'\u3001'years'")
|
|
212
|
+
},
|
|
213
|
+
async ({ datetime, amount, unit }) => {
|
|
214
|
+
try {
|
|
215
|
+
let date;
|
|
216
|
+
if (!isNaN(Number(datetime))) {
|
|
217
|
+
date = new Date(Number(datetime));
|
|
218
|
+
} else {
|
|
219
|
+
date = new Date(datetime);
|
|
220
|
+
}
|
|
221
|
+
if (isNaN(date.getTime())) {
|
|
222
|
+
throw new Error("\u65E0\u6548\u7684\u65E5\u671F\u65F6\u95F4\u683C\u5F0F");
|
|
223
|
+
}
|
|
224
|
+
const newDate = new Date(date);
|
|
225
|
+
switch (unit.toLowerCase()) {
|
|
226
|
+
case "milliseconds":
|
|
227
|
+
newDate.setTime(newDate.getTime() + amount);
|
|
228
|
+
break;
|
|
229
|
+
case "seconds":
|
|
230
|
+
newDate.setTime(newDate.getTime() + amount * 1e3);
|
|
231
|
+
break;
|
|
232
|
+
case "minutes":
|
|
233
|
+
newDate.setTime(newDate.getTime() + amount * 60 * 1e3);
|
|
234
|
+
break;
|
|
235
|
+
case "hours":
|
|
236
|
+
newDate.setTime(newDate.getTime() + amount * 60 * 60 * 1e3);
|
|
237
|
+
break;
|
|
238
|
+
case "days":
|
|
239
|
+
newDate.setDate(newDate.getDate() + amount);
|
|
240
|
+
break;
|
|
241
|
+
case "weeks":
|
|
242
|
+
newDate.setDate(newDate.getDate() + amount * 7);
|
|
243
|
+
break;
|
|
244
|
+
case "months":
|
|
245
|
+
newDate.setMonth(newDate.getMonth() + amount);
|
|
246
|
+
break;
|
|
247
|
+
case "years":
|
|
248
|
+
newDate.setFullYear(newDate.getFullYear() + amount);
|
|
249
|
+
break;
|
|
250
|
+
default:
|
|
251
|
+
throw new Error(`\u4E0D\u652F\u6301\u7684\u65F6\u95F4\u5355\u4F4D\uFF1A${unit}`);
|
|
252
|
+
}
|
|
253
|
+
const result = newDate.toISOString();
|
|
254
|
+
logger.info(`\u5728 ${datetime} \u57FA\u7840\u4E0A\u589E\u52A0 ${amount} ${unit}\uFF0C\u7ED3\u679C\uFF1A${result}`);
|
|
255
|
+
return {
|
|
256
|
+
content: [
|
|
257
|
+
{
|
|
258
|
+
type: "text",
|
|
259
|
+
text: JSON.stringify({
|
|
260
|
+
success: true,
|
|
261
|
+
result,
|
|
262
|
+
original: datetime,
|
|
263
|
+
amount,
|
|
264
|
+
unit
|
|
265
|
+
})
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
};
|
|
269
|
+
} catch (error) {
|
|
270
|
+
logger.error(`\u65F6\u95F4\u52A0\u51CF\u9519\u8BEF\uFF1A${error.message}`);
|
|
271
|
+
return {
|
|
272
|
+
content: [
|
|
273
|
+
{
|
|
274
|
+
type: "text",
|
|
275
|
+
text: JSON.stringify({
|
|
276
|
+
success: false,
|
|
277
|
+
error: error.message
|
|
278
|
+
})
|
|
279
|
+
}
|
|
280
|
+
],
|
|
281
|
+
isError: true
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
async function main() {
|
|
287
|
+
const transport = new StdioServerTransport();
|
|
288
|
+
await server.connect(transport);
|
|
289
|
+
logger.info("DateTime MCP \u670D\u52A1\u5DF2\u542F\u52A8");
|
|
290
|
+
}
|
|
291
|
+
main().catch((error) => {
|
|
292
|
+
logger.error(`\u542F\u52A8\u670D\u52A1\u5931\u8D25\uFF1A${error.message}`);
|
|
293
|
+
process.exit(1);
|
|
294
|
+
});
|
|
295
|
+
var src_default = server;
|
|
296
|
+
export {
|
|
297
|
+
src_default as default
|
|
298
|
+
};
|
|
299
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * MCP DateTime Server\n * 提供日期时间功能的 MCP 服务\n */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\n\n// 日志工具\nconst logger = {\n info: (message: string) => {\n const timestamp = new Date().toISOString();\n console.error(`${timestamp} - DateTime - INFO - ${message}`);\n },\n error: (message: string) => {\n const timestamp = new Date().toISOString();\n console.error(`${timestamp} - DateTime - ERROR - ${message}`);\n },\n};\n\n// 创建 MCP 服务器实例\nconst server = new McpServer({\n name: \"@xiaozhi-client/datetime-mcp\",\n version: \"1.0.0\",\n});\n\n// 注册获取当前时间工具\nserver.tool(\n \"get_current_time\",\n \"获取当前时间,支持多种格式\",\n {\n format: z\n .string()\n .optional()\n .describe(\"时间格式:'iso'(默认)、'timestamp'、'locale'、'time-only'\"),\n },\n async ({ format = \"iso\" }) => {\n try {\n const now = new Date();\n let result: string | number;\n\n switch (format) {\n case \"timestamp\":\n result = now.getTime();\n break;\n case \"locale\":\n result = now.toLocaleString();\n break;\n case \"time-only\":\n result = now.toLocaleTimeString();\n break;\n case \"iso\":\n default:\n result = now.toISOString();\n break;\n }\n\n logger.info(`获取当前时间,格式:${format},结果:${result}`);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n success: true,\n result,\n format,\n }),\n },\n ],\n };\n } catch (error) {\n logger.error(`获取当前时间错误:${error.message}`);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n success: false,\n error: error.message,\n }),\n },\n ],\n isError: true,\n };\n }\n }\n);\n\n// 注册获取当前日期工具\nserver.tool(\n \"get_current_date\",\n \"获取当前日期,支持多种格式\",\n {\n format: z\n .string()\n .optional()\n .describe(\"日期格式:'iso'(默认)、'locale'、'date-only'、'yyyy-mm-dd'\"),\n },\n async ({ format = \"iso\" }) => {\n try {\n const now = new Date();\n let result: string;\n\n switch (format) {\n case \"locale\":\n result = now.toLocaleDateString();\n break;\n case \"date-only\":\n result = now.toDateString();\n break;\n case \"yyyy-mm-dd\":\n result = now.toISOString().split(\"T\")[0];\n break;\n case \"iso\":\n default:\n result = now.toISOString();\n break;\n }\n\n logger.info(`获取当前日期,格式:${format},结果:${result}`);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n success: true,\n result,\n format,\n }),\n },\n ],\n };\n } catch (error) {\n logger.error(`获取当前日期错误:${error.message}`);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n success: false,\n error: error.message,\n }),\n },\n ],\n isError: true,\n };\n }\n }\n);\n\n// 注册格式化日期时间工具\nserver.tool(\n \"format_datetime\",\n \"将给定的日期时间字符串或时间戳格式化为指定格式\",\n {\n datetime: z.string().describe(\"要格式化的日期时间字符串或时间戳\"),\n format: z\n .string()\n .optional()\n .describe(\"输出格式:'iso'、'locale'、'timestamp'、'yyyy-mm-dd'、'custom'\"),\n custom_format: z\n .string()\n .optional()\n .describe(\"自定义格式字符串(当 format 为 'custom' 时使用)\"),\n },\n async ({ datetime, format = \"iso\", custom_format }) => {\n try {\n let date: Date;\n\n // 尝试解析输入的日期时间\n if (!isNaN(Number(datetime))) {\n // 是时间戳\n date = new Date(Number(datetime));\n } else {\n // 是日期字符串\n date = new Date(datetime);\n }\n\n if (isNaN(date.getTime())) {\n throw new Error(\"无效的日期时间格式\");\n }\n\n let result: string | number;\n switch (format) {\n case \"timestamp\":\n result = date.getTime();\n break;\n case \"locale\":\n result = date.toLocaleString();\n break;\n case \"yyyy-mm-dd\":\n result = date.toISOString().split(\"T\")[0];\n break;\n case \"custom\":\n if (custom_format) {\n // 简单的自定义格式化\n result = custom_format\n .replace(\"YYYY\", String(date.getFullYear()))\n .replace(\"MM\", String(date.getMonth() + 1).padStart(2, \"0\"))\n .replace(\"DD\", String(date.getDate()).padStart(2, \"0\"))\n .replace(\"HH\", String(date.getHours()).padStart(2, \"0\"))\n .replace(\"mm\", String(date.getMinutes()).padStart(2, \"0\"))\n .replace(\"ss\", String(date.getSeconds()).padStart(2, \"0\"));\n } else {\n result = date.toISOString();\n }\n break;\n case \"iso\":\n default:\n result = date.toISOString();\n break;\n }\n\n logger.info(`格式化日期时间:${datetime} 为格式:${format},结果:${result}`);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n success: true,\n result,\n original: datetime,\n format,\n }),\n },\n ],\n };\n } catch (error) {\n logger.error(`格式化日期时间错误:${error.message}`);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n success: false,\n error: error.message,\n }),\n },\n ],\n isError: true,\n };\n }\n }\n);\n\n// 注册时间加减工具\nserver.tool(\n \"add_time\",\n \"在给定的日期时间基础上增加或减少时间\",\n {\n datetime: z.string().describe(\"基准日期时间字符串或时间戳\"),\n amount: z.number().describe(\"要增加(正数)或减少(负数)的数量\"),\n unit: z\n .string()\n .describe(\"时间单位:'milliseconds'、'seconds'、'minutes'、'hours'、'days'、'weeks'、'months'、'years'\"),\n },\n async ({ datetime, amount, unit }) => {\n try {\n let date: Date;\n\n // 尝试解析输入的日期时间\n if (!isNaN(Number(datetime))) {\n date = new Date(Number(datetime));\n } else {\n date = new Date(datetime);\n }\n\n if (isNaN(date.getTime())) {\n throw new Error(\"无效的日期时间格式\");\n }\n\n // 根据单位计算新日期\n const newDate = new Date(date);\n\n switch (unit.toLowerCase()) {\n case \"milliseconds\":\n newDate.setTime(newDate.getTime() + amount);\n break;\n case \"seconds\":\n newDate.setTime(newDate.getTime() + amount * 1000);\n break;\n case \"minutes\":\n newDate.setTime(newDate.getTime() + amount * 60 * 1000);\n break;\n case \"hours\":\n newDate.setTime(newDate.getTime() + amount * 60 * 60 * 1000);\n break;\n case \"days\":\n newDate.setDate(newDate.getDate() + amount);\n break;\n case \"weeks\":\n newDate.setDate(newDate.getDate() + amount * 7);\n break;\n case \"months\":\n newDate.setMonth(newDate.getMonth() + amount);\n break;\n case \"years\":\n newDate.setFullYear(newDate.getFullYear() + amount);\n break;\n default:\n throw new Error(`不支持的时间单位:${unit}`);\n }\n\n const result = newDate.toISOString();\n logger.info(`在 ${datetime} 基础上增加 ${amount} ${unit},结果:${result}`);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n success: true,\n result,\n original: datetime,\n amount,\n unit,\n }),\n },\n ],\n };\n } catch (error) {\n logger.error(`时间加减错误:${error.message}`);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n success: false,\n error: error.message,\n }),\n },\n ],\n isError: true,\n };\n }\n }\n);\n\n// 启动服务器\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n logger.info(\"DateTime MCP 服务已启动\");\n}\n\nmain().catch((error) => {\n logger.error(`启动服务失败:${error.message}`);\n process.exit(1);\n});\n\nexport default server;\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xiaozhi-client/datetime-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "小智日期时间 MCP 服务,提供日期时间处理功能",
|
|
6
|
+
"bin": {
|
|
7
|
+
"xiaozhi-datetime-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
18
|
+
"zod": "^3.22.4"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@biomejs/biome": "^1.5.0",
|
|
22
|
+
"tsup": "^8.0.0",
|
|
23
|
+
"typescript": "^5.3.0"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"dev": "tsup --watch",
|
|
28
|
+
"type-check": "tsc --noEmit",
|
|
29
|
+
"lint": "biome check src/",
|
|
30
|
+
"lint:fix": "biome check --write src/"
|
|
31
|
+
}
|
|
32
|
+
}
|