xiaozhi-client 1.5.0 → 1.6.0-beta.1
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/dist/adaptiveMCPPipe.js +9 -9
- package/dist/adaptiveMCPPipe.js.map +1 -1
- package/dist/autoCompletion.js +1 -1
- package/dist/autoCompletion.js.map +1 -1
- package/dist/cli.js +14 -14
- package/dist/cli.js.map +1 -1
- package/dist/configManager.d.ts +12 -1
- package/dist/configManager.js +1 -1
- package/dist/configManager.js.map +1 -1
- package/dist/mcpCommands.js +1 -1
- package/dist/mcpCommands.js.map +1 -1
- package/dist/mcpServerProxy.d.ts +5 -0
- package/dist/mcpServerProxy.js +4 -4
- package/dist/mcpServerProxy.js.map +1 -1
- package/dist/modelScopeMCPClient.js +2 -2
- package/dist/modelScopeMCPClient.js.map +1 -1
- package/dist/multiEndpointMCPPipe.js +6 -6
- package/dist/multiEndpointMCPPipe.js.map +1 -1
- package/dist/package.json +9 -2
- package/dist/streamableHttpMCPClient.js +2 -2
- package/dist/streamableHttpMCPClient.js.map +1 -1
- package/dist/templates/json5/mcpServers/calculator.js +106 -0
- package/dist/templates/json5/mcpServers/datetime.js +390 -0
- package/dist/templates/json5/package.json +13 -0
- package/dist/templates/json5/xiaozhi.config.json5 +41 -0
- package/dist/templates/jsonc/mcpServers/calculator.js +106 -0
- package/dist/templates/jsonc/mcpServers/datetime.js +390 -0
- package/dist/templates/jsonc/package.json +13 -0
- package/dist/templates/jsonc/xiaozhi.config.jsonc +41 -0
- package/dist/webServer.js +14 -14
- package/dist/webServer.js.map +1 -1
- package/docs/Architecture.md +68 -0
- package/package.json +23 -21
- package/templates/json5/mcpServers/calculator.js +106 -0
- package/templates/json5/mcpServers/datetime.js +390 -0
- package/templates/json5/package.json +13 -0
- package/templates/json5/xiaozhi.config.json5 +41 -0
- package/templates/jsonc/mcpServers/calculator.js +106 -0
- package/templates/jsonc/mcpServers/datetime.js +390 -0
- package/templates/jsonc/package.json +13 -0
- package/templates/jsonc/xiaozhi.config.jsonc +41 -0
- package/web/dist/assets/index-lAiGa4ms.js.map +1 -1
- package/web/README.md +0 -169
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# 项目架构
|
|
2
|
+
|
|
3
|
+
```mermaid
|
|
4
|
+
graph TB
|
|
5
|
+
subgraph ProjectComponents["🏗️ xiaozhi-client"]
|
|
6
|
+
Core["🔧 核心服务<br/>(Core Service)<br/>MCP Server/Client"]
|
|
7
|
+
Web["🌐 Web管理界面<br/>(Supervisor)"]
|
|
8
|
+
CLI["💻 命令行工具<br/>(CLI)"]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
subgraph Hardware["📱 硬件设备"]
|
|
12
|
+
ESP32["🎙️ xiaozhi-esp32<br/>(语音互动设备)"]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
subgraph MCPClients["💼 MCP客户端"]
|
|
16
|
+
Cursor["Cursor"]
|
|
17
|
+
Cherry["Cherry Studio"]
|
|
18
|
+
OtherClient["其他客户端"]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
subgraph External["☁️ 外部服务"]
|
|
22
|
+
XiaozhiMe["🌐 xiaozhi.me<br/>(原有服务端)"]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
subgraph MCPServices["🔌 MCP服务集群"]
|
|
26
|
+
direction LR
|
|
27
|
+
MCP1["🏠 Local MCP<br/>(本地服务)"]
|
|
28
|
+
MCP2["🌍 Remote MCP<br/>(远程服务)"]
|
|
29
|
+
MCP3["🤖 ModelScope MCP<br/>(模型服务)"]
|
|
30
|
+
MCPn["⚡ Other MCP<br/>(其他服务)"]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
%% 新的MCP客户端连接 (重要)
|
|
34
|
+
Cursor ==>|"MCP协议"| Core
|
|
35
|
+
Cherry ==>|"MCP协议"| Core
|
|
36
|
+
OtherClient ==>|"MCP协议"| Core
|
|
37
|
+
|
|
38
|
+
%% ESP32连接链路
|
|
39
|
+
ESP32 -.->|"直连"| Core
|
|
40
|
+
ESP32 -->|"原有链路"| XiaozhiMe
|
|
41
|
+
XiaozhiMe --> Core
|
|
42
|
+
|
|
43
|
+
%% Core Service到MCP服务
|
|
44
|
+
Core ==> MCP1
|
|
45
|
+
Core ==> MCP2
|
|
46
|
+
Core ==> MCP3
|
|
47
|
+
Core ==> MCPn
|
|
48
|
+
|
|
49
|
+
%% 项目内部管理连接
|
|
50
|
+
Web -.-> Core
|
|
51
|
+
CLI -.-> Core
|
|
52
|
+
|
|
53
|
+
%% 样式定义
|
|
54
|
+
classDef coreComponent stroke:#2980b9,stroke-width:3px,fill:#ebf3fd,color:#2c3e50
|
|
55
|
+
classDef hardware stroke:#e74c3c,stroke-width:2px,fill:#fdedec,color:#2c3e50
|
|
56
|
+
classDef external stroke:#8e44ad,stroke-width:2px,fill:#f4ecf7,color:#2c3e50
|
|
57
|
+
classDef mcp stroke:#27ae60,stroke-width:2px,fill:#eafaf1,color:#2c3e50
|
|
58
|
+
classDef mcpClient stroke:#f39c12,stroke-width:2px,fill:#fef9e7,color:#2c3e50
|
|
59
|
+
classDef primaryFlow stroke:#27ae60,stroke-width:4px
|
|
60
|
+
classDef secondaryFlow stroke:#95a5a6,stroke-width:2px
|
|
61
|
+
|
|
62
|
+
class Core,Web,CLI coreComponent
|
|
63
|
+
class ESP32 hardware
|
|
64
|
+
class XiaozhiMe external
|
|
65
|
+
class MCP1,MCP2,MCP3,MCPn mcp
|
|
66
|
+
class Cursor,Cherry,OtherClient mcpClient
|
|
67
|
+
|
|
68
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xiaozhi-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0-beta.1",
|
|
4
4
|
"description": "小智 AI 客户端 命令行工具",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -20,24 +20,6 @@
|
|
|
20
20
|
"xiaozhi": "./dist/cli.js",
|
|
21
21
|
"xiaozhi-client": "./dist/cli.js"
|
|
22
22
|
},
|
|
23
|
-
"scripts": {
|
|
24
|
-
"build": "pnpm run build:web && tsup",
|
|
25
|
-
"build:web": "cd web && pnpm install && pnpm run build",
|
|
26
|
-
"dev": "tsup --watch",
|
|
27
|
-
"test": "vitest run",
|
|
28
|
-
"test:watch": "vitest",
|
|
29
|
-
"test:coverage": "vitest run --coverage",
|
|
30
|
-
"test:ui": "vitest --ui",
|
|
31
|
-
"format": "biome format --write .",
|
|
32
|
-
"lint": "biome lint --write .",
|
|
33
|
-
"type:check": "tsc --noEmit",
|
|
34
|
-
"check": "biome check .",
|
|
35
|
-
"check:write": "biome check --write .",
|
|
36
|
-
"check:all": "pnpm check && pnpm type:check && pnpm spell:check && pnpm duplicate:check",
|
|
37
|
-
"spell:check": "cspell \"src/**/*.ts\" \"*.md\" \"*.json\"",
|
|
38
|
-
"duplicate:check": "jscpd src/",
|
|
39
|
-
"release": "semantic-release"
|
|
40
|
-
},
|
|
41
23
|
"keywords": [
|
|
42
24
|
"xiaozhi",
|
|
43
25
|
"mcp",
|
|
@@ -56,6 +38,8 @@
|
|
|
56
38
|
"dotenv": "^16.3.1",
|
|
57
39
|
"eventsource": "^4.0.0",
|
|
58
40
|
"express": "^5.1.0",
|
|
41
|
+
"json5": "^2.2.3",
|
|
42
|
+
"jsonc-parser": "^3.3.1",
|
|
59
43
|
"node-fetch": "2",
|
|
60
44
|
"omelette": "^0.4.17",
|
|
61
45
|
"ora": "^8.2.0",
|
|
@@ -73,7 +57,7 @@
|
|
|
73
57
|
"@semantic-release/release-notes-generator": "^14.0.3",
|
|
74
58
|
"@types/eventsource": "^3.0.0",
|
|
75
59
|
"@types/node": "^24.0.1",
|
|
76
|
-
"@types/node-fetch": "2",
|
|
60
|
+
"@types/node-fetch": "^2.6.12",
|
|
77
61
|
"@types/omelette": "^0.4.5",
|
|
78
62
|
"@types/ws": "^8.18.1",
|
|
79
63
|
"@vitest/coverage-v8": "^3.2.3",
|
|
@@ -87,5 +71,23 @@
|
|
|
87
71
|
"tsup": "^8.5.0",
|
|
88
72
|
"typescript": "^5.8.3",
|
|
89
73
|
"vitest": "^3.2.3"
|
|
74
|
+
},
|
|
75
|
+
"scripts": {
|
|
76
|
+
"build": "pnpm run build:web && tsup",
|
|
77
|
+
"build:web": "cd web && pnpm install && pnpm run build",
|
|
78
|
+
"dev": "tsup --watch",
|
|
79
|
+
"test": "vitest run",
|
|
80
|
+
"test:watch": "vitest",
|
|
81
|
+
"test:coverage": "vitest run --coverage",
|
|
82
|
+
"test:ui": "vitest --ui",
|
|
83
|
+
"format": "biome format --write .",
|
|
84
|
+
"lint": "biome lint --write .",
|
|
85
|
+
"type:check": "tsc --noEmit",
|
|
86
|
+
"check": "biome check .",
|
|
87
|
+
"check:write": "biome check --write .",
|
|
88
|
+
"check:all": "pnpm check && pnpm type:check && pnpm spell:check && pnpm duplicate:check",
|
|
89
|
+
"spell:check": "cspell \"src/**/*.ts\" \"*.md\" \"*.json\"",
|
|
90
|
+
"duplicate:check": "jscpd src/",
|
|
91
|
+
"release": "semantic-release"
|
|
90
92
|
}
|
|
91
|
-
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCP Calculator Server - JavaScript Implementation
|
|
5
|
+
* Provides mathematical calculation tools via MCP protocol
|
|
6
|
+
* Version: 0.2.0 - Using @modelcontextprotocol/sdk
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
// Simple logger utility
|
|
14
|
+
const logger = {
|
|
15
|
+
info: (message) => {
|
|
16
|
+
const timestamp = new Date().toISOString();
|
|
17
|
+
console.error(`${timestamp} - Calculator - INFO - ${message}`);
|
|
18
|
+
},
|
|
19
|
+
error: (message) => {
|
|
20
|
+
const timestamp = new Date().toISOString();
|
|
21
|
+
console.error(`${timestamp} - Calculator - ERROR - ${message}`);
|
|
22
|
+
},
|
|
23
|
+
debug: (message) => {
|
|
24
|
+
const timestamp = new Date().toISOString();
|
|
25
|
+
console.error(`${timestamp} - Calculator - DEBUG - ${message}`);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Create MCP server instance using the official SDK
|
|
30
|
+
const server = new McpServer({
|
|
31
|
+
name: "Calculator",
|
|
32
|
+
version: "0.2.0",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Register calculator tool using the SDK
|
|
36
|
+
server.tool(
|
|
37
|
+
"calculator",
|
|
38
|
+
"For mathematical calculation, always use this tool to calculate the result of a JavaScript expression. Math object and basic operations are available.",
|
|
39
|
+
{
|
|
40
|
+
javascript_expression: z
|
|
41
|
+
.string()
|
|
42
|
+
.describe("JavaScript expression to evaluate"),
|
|
43
|
+
},
|
|
44
|
+
async ({ javascript_expression }) => {
|
|
45
|
+
try {
|
|
46
|
+
// Simple and direct evaluation like Python version
|
|
47
|
+
// Note: In a production environment, you might want to add more security measures
|
|
48
|
+
const result = eval(javascript_expression);
|
|
49
|
+
logger.info(
|
|
50
|
+
`Calculating formula: ${javascript_expression}, result: ${result}`
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
content: [
|
|
55
|
+
{
|
|
56
|
+
type: "text",
|
|
57
|
+
text: JSON.stringify({
|
|
58
|
+
success: true,
|
|
59
|
+
result: result,
|
|
60
|
+
}),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
} catch (error) {
|
|
65
|
+
logger.error(`Calculation error: ${error.message}`);
|
|
66
|
+
return {
|
|
67
|
+
content: [
|
|
68
|
+
{
|
|
69
|
+
type: "text",
|
|
70
|
+
text: JSON.stringify({
|
|
71
|
+
success: false,
|
|
72
|
+
error: error.message,
|
|
73
|
+
}),
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
isError: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Start the server if this file is run directly
|
|
83
|
+
async function main() {
|
|
84
|
+
logger.info("Starting MCP Calculator server with SDK");
|
|
85
|
+
|
|
86
|
+
const transport = new StdioServerTransport();
|
|
87
|
+
await server.connect(transport);
|
|
88
|
+
|
|
89
|
+
logger.info("MCP Calculator server is running on stdio");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Run the server if this file is executed directly
|
|
93
|
+
import { fileURLToPath } from 'node:url';
|
|
94
|
+
import { resolve } from 'node:path';
|
|
95
|
+
|
|
96
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
97
|
+
const argFile = process.argv[1] ? resolve(process.argv[1]) : null;
|
|
98
|
+
|
|
99
|
+
if (argFile && currentFile === argFile) {
|
|
100
|
+
main().catch((error) => {
|
|
101
|
+
logger.error(`Failed to start server: ${error.message}`);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export default server;
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCP DateTime Server - JavaScript Implementation
|
|
5
|
+
* Provides date and time tools via MCP protocol
|
|
6
|
+
* Version: 0.2.0 - Using @modelcontextprotocol/sdk
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
// Simple logger utility
|
|
14
|
+
const logger = {
|
|
15
|
+
info: (message) => {
|
|
16
|
+
const timestamp = new Date().toISOString();
|
|
17
|
+
console.error(`${timestamp} - DateTime - INFO - ${message}`);
|
|
18
|
+
},
|
|
19
|
+
error: (message) => {
|
|
20
|
+
const timestamp = new Date().toISOString();
|
|
21
|
+
console.error(`${timestamp} - DateTime - ERROR - ${message}`);
|
|
22
|
+
},
|
|
23
|
+
debug: (message) => {
|
|
24
|
+
const timestamp = new Date().toISOString();
|
|
25
|
+
console.error(`${timestamp} - DateTime - DEBUG - ${message}`);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Create MCP server instance using the official SDK
|
|
30
|
+
const server = new McpServer({
|
|
31
|
+
name: "DateTime",
|
|
32
|
+
version: "0.2.0",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Register get_current_time tool
|
|
36
|
+
server.tool(
|
|
37
|
+
"get_current_time",
|
|
38
|
+
"Get the current time in various formats",
|
|
39
|
+
{
|
|
40
|
+
format: z
|
|
41
|
+
.string()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe(
|
|
44
|
+
"Time format: 'iso' (default), 'timestamp', 'locale', 'time-only'"
|
|
45
|
+
),
|
|
46
|
+
},
|
|
47
|
+
async ({ format = "iso" }) => {
|
|
48
|
+
try {
|
|
49
|
+
const now = new Date();
|
|
50
|
+
let result;
|
|
51
|
+
|
|
52
|
+
switch (format) {
|
|
53
|
+
case "timestamp":
|
|
54
|
+
result = now.getTime();
|
|
55
|
+
break;
|
|
56
|
+
case "locale":
|
|
57
|
+
result = now.toLocaleString();
|
|
58
|
+
break;
|
|
59
|
+
case "time-only":
|
|
60
|
+
result = now.toLocaleTimeString();
|
|
61
|
+
break;
|
|
62
|
+
case "iso":
|
|
63
|
+
default:
|
|
64
|
+
result = now.toISOString();
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
logger.info(
|
|
69
|
+
`Getting current time in format: ${format}, result: ${result}`
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
content: [
|
|
74
|
+
{
|
|
75
|
+
type: "text",
|
|
76
|
+
text: JSON.stringify({
|
|
77
|
+
success: true,
|
|
78
|
+
result: result,
|
|
79
|
+
format: format,
|
|
80
|
+
}),
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
} catch (error) {
|
|
85
|
+
logger.error(`Get current time error: ${error.message}`);
|
|
86
|
+
return {
|
|
87
|
+
content: [
|
|
88
|
+
{
|
|
89
|
+
type: "text",
|
|
90
|
+
text: JSON.stringify({
|
|
91
|
+
success: false,
|
|
92
|
+
error: error.message,
|
|
93
|
+
}),
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
isError: true,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Register get_current_date tool
|
|
103
|
+
server.tool(
|
|
104
|
+
"get_current_date",
|
|
105
|
+
"Get the current date in various formats",
|
|
106
|
+
{
|
|
107
|
+
format: z
|
|
108
|
+
.string()
|
|
109
|
+
.optional()
|
|
110
|
+
.describe(
|
|
111
|
+
"Date format: 'iso' (default), 'locale', 'date-only', 'yyyy-mm-dd'"
|
|
112
|
+
),
|
|
113
|
+
},
|
|
114
|
+
async ({ format = "iso" }) => {
|
|
115
|
+
try {
|
|
116
|
+
const now = new Date();
|
|
117
|
+
let result;
|
|
118
|
+
|
|
119
|
+
switch (format) {
|
|
120
|
+
case "locale":
|
|
121
|
+
result = now.toLocaleDateString();
|
|
122
|
+
break;
|
|
123
|
+
case "date-only":
|
|
124
|
+
result = now.toDateString();
|
|
125
|
+
break;
|
|
126
|
+
case "yyyy-mm-dd":
|
|
127
|
+
result = now.toISOString().split("T")[0];
|
|
128
|
+
break;
|
|
129
|
+
case "iso":
|
|
130
|
+
default:
|
|
131
|
+
result = now.toISOString();
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
logger.info(
|
|
136
|
+
`Getting current date in format: ${format}, result: ${result}`
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
content: [
|
|
141
|
+
{
|
|
142
|
+
type: "text",
|
|
143
|
+
text: JSON.stringify({
|
|
144
|
+
success: true,
|
|
145
|
+
result: result,
|
|
146
|
+
format: format,
|
|
147
|
+
}),
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
} catch (error) {
|
|
152
|
+
logger.error(`Get current date error: ${error.message}`);
|
|
153
|
+
return {
|
|
154
|
+
content: [
|
|
155
|
+
{
|
|
156
|
+
type: "text",
|
|
157
|
+
text: JSON.stringify({
|
|
158
|
+
success: false,
|
|
159
|
+
error: error.message,
|
|
160
|
+
}),
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
isError: true,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
// Register format_datetime tool
|
|
170
|
+
server.tool(
|
|
171
|
+
"format_datetime",
|
|
172
|
+
"Format a given date/time string or timestamp into specified format",
|
|
173
|
+
{
|
|
174
|
+
datetime: z.string().describe("Date/time string or timestamp to format"),
|
|
175
|
+
format: z
|
|
176
|
+
.string()
|
|
177
|
+
.optional()
|
|
178
|
+
.describe(
|
|
179
|
+
"Output format: 'iso', 'locale', 'timestamp', 'yyyy-mm-dd', 'custom'"
|
|
180
|
+
),
|
|
181
|
+
custom_format: z
|
|
182
|
+
.string()
|
|
183
|
+
.optional()
|
|
184
|
+
.describe("Custom format string (when format is 'custom')"),
|
|
185
|
+
},
|
|
186
|
+
async ({ datetime, format = "iso", custom_format }) => {
|
|
187
|
+
try {
|
|
188
|
+
let date;
|
|
189
|
+
|
|
190
|
+
// Try to parse the input datetime
|
|
191
|
+
if (!isNaN(Number(datetime))) {
|
|
192
|
+
// It's a timestamp
|
|
193
|
+
date = new Date(Number(datetime));
|
|
194
|
+
} else {
|
|
195
|
+
// It's a date string
|
|
196
|
+
date = new Date(datetime);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (isNaN(date.getTime())) {
|
|
200
|
+
throw new Error("Invalid date/time format");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let result;
|
|
204
|
+
switch (format) {
|
|
205
|
+
case "timestamp":
|
|
206
|
+
result = date.getTime();
|
|
207
|
+
break;
|
|
208
|
+
case "locale":
|
|
209
|
+
result = date.toLocaleString();
|
|
210
|
+
break;
|
|
211
|
+
case "yyyy-mm-dd":
|
|
212
|
+
result = date.toISOString().split("T")[0];
|
|
213
|
+
break;
|
|
214
|
+
case "custom":
|
|
215
|
+
if (custom_format) {
|
|
216
|
+
// Simple custom formatting (can be extended)
|
|
217
|
+
result = custom_format
|
|
218
|
+
.replace("YYYY", date.getFullYear())
|
|
219
|
+
.replace("MM", String(date.getMonth() + 1).padStart(2, "0"))
|
|
220
|
+
.replace("DD", String(date.getDate()).padStart(2, "0"))
|
|
221
|
+
.replace("HH", String(date.getHours()).padStart(2, "0"))
|
|
222
|
+
.replace("mm", String(date.getMinutes()).padStart(2, "0"))
|
|
223
|
+
.replace("ss", String(date.getSeconds()).padStart(2, "0"));
|
|
224
|
+
} else {
|
|
225
|
+
result = date.toISOString();
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
case "iso":
|
|
229
|
+
default:
|
|
230
|
+
result = date.toISOString();
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
logger.info(
|
|
235
|
+
`Formatting datetime: ${datetime} to format: ${format}, result: ${result}`
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
content: [
|
|
240
|
+
{
|
|
241
|
+
type: "text",
|
|
242
|
+
text: JSON.stringify({
|
|
243
|
+
success: true,
|
|
244
|
+
result: result,
|
|
245
|
+
original: datetime,
|
|
246
|
+
format: format,
|
|
247
|
+
}),
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
};
|
|
251
|
+
} catch (error) {
|
|
252
|
+
logger.error(`Format datetime error: ${error.message}`);
|
|
253
|
+
return {
|
|
254
|
+
content: [
|
|
255
|
+
{
|
|
256
|
+
type: "text",
|
|
257
|
+
text: JSON.stringify({
|
|
258
|
+
success: false,
|
|
259
|
+
error: error.message,
|
|
260
|
+
}),
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
isError: true,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// Register add_time tool
|
|
270
|
+
server.tool(
|
|
271
|
+
"add_time",
|
|
272
|
+
"Add or subtract time from a given date/time",
|
|
273
|
+
{
|
|
274
|
+
datetime: z.string().describe("Base date/time string or timestamp"),
|
|
275
|
+
amount: z
|
|
276
|
+
.number()
|
|
277
|
+
.describe("Amount to add (positive) or subtract (negative)"),
|
|
278
|
+
unit: z
|
|
279
|
+
.string()
|
|
280
|
+
.describe(
|
|
281
|
+
"Time unit: 'milliseconds', 'seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years'"
|
|
282
|
+
),
|
|
283
|
+
},
|
|
284
|
+
async ({ datetime, amount, unit }) => {
|
|
285
|
+
try {
|
|
286
|
+
let date;
|
|
287
|
+
|
|
288
|
+
// Try to parse the input datetime
|
|
289
|
+
if (!isNaN(Number(datetime))) {
|
|
290
|
+
date = new Date(Number(datetime));
|
|
291
|
+
} else {
|
|
292
|
+
date = new Date(datetime);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (isNaN(date.getTime())) {
|
|
296
|
+
throw new Error("Invalid date/time format");
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Calculate the new date based on unit
|
|
300
|
+
const newDate = new Date(date);
|
|
301
|
+
|
|
302
|
+
switch (unit.toLowerCase()) {
|
|
303
|
+
case "milliseconds":
|
|
304
|
+
newDate.setTime(newDate.getTime() + amount);
|
|
305
|
+
break;
|
|
306
|
+
case "seconds":
|
|
307
|
+
newDate.setTime(newDate.getTime() + amount * 1000);
|
|
308
|
+
break;
|
|
309
|
+
case "minutes":
|
|
310
|
+
newDate.setTime(newDate.getTime() + amount * 60 * 1000);
|
|
311
|
+
break;
|
|
312
|
+
case "hours":
|
|
313
|
+
newDate.setTime(newDate.getTime() + amount * 60 * 60 * 1000);
|
|
314
|
+
break;
|
|
315
|
+
case "days":
|
|
316
|
+
newDate.setDate(newDate.getDate() + amount);
|
|
317
|
+
break;
|
|
318
|
+
case "weeks":
|
|
319
|
+
newDate.setDate(newDate.getDate() + amount * 7);
|
|
320
|
+
break;
|
|
321
|
+
case "months":
|
|
322
|
+
newDate.setMonth(newDate.getMonth() + amount);
|
|
323
|
+
break;
|
|
324
|
+
case "years":
|
|
325
|
+
newDate.setFullYear(newDate.getFullYear() + amount);
|
|
326
|
+
break;
|
|
327
|
+
default:
|
|
328
|
+
throw new Error(`Unsupported time unit: ${unit}`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const result = newDate.toISOString();
|
|
332
|
+
logger.info(`Adding ${amount} ${unit} to ${datetime}, result: ${result}`);
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
content: [
|
|
336
|
+
{
|
|
337
|
+
type: "text",
|
|
338
|
+
text: JSON.stringify({
|
|
339
|
+
success: true,
|
|
340
|
+
result: result,
|
|
341
|
+
original: datetime,
|
|
342
|
+
amount: amount,
|
|
343
|
+
unit: unit,
|
|
344
|
+
}),
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
};
|
|
348
|
+
} catch (error) {
|
|
349
|
+
logger.error(`Add time error: ${error.message}`);
|
|
350
|
+
return {
|
|
351
|
+
content: [
|
|
352
|
+
{
|
|
353
|
+
type: "text",
|
|
354
|
+
text: JSON.stringify({
|
|
355
|
+
success: false,
|
|
356
|
+
error: error.message,
|
|
357
|
+
}),
|
|
358
|
+
},
|
|
359
|
+
],
|
|
360
|
+
isError: true,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
// Start the server if this file is run directly
|
|
367
|
+
async function main() {
|
|
368
|
+
logger.info("Starting MCP DateTime server with SDK");
|
|
369
|
+
|
|
370
|
+
const transport = new StdioServerTransport();
|
|
371
|
+
await server.connect(transport);
|
|
372
|
+
|
|
373
|
+
logger.info("MCP DateTime server is running on stdio");
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Run the server if this file is executed directly
|
|
377
|
+
import { fileURLToPath } from 'node:url';
|
|
378
|
+
import { resolve } from 'node:path';
|
|
379
|
+
|
|
380
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
381
|
+
const argFile = process.argv[1] ? resolve(process.argv[1]) : null;
|
|
382
|
+
|
|
383
|
+
if (argFile && currentFile === argFile) {
|
|
384
|
+
main().catch((error) => {
|
|
385
|
+
logger.error(`Failed to start server: ${error.message}`);
|
|
386
|
+
process.exit(1);
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export default server;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hello-world",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "",
|
|
6
|
+
"keywords": [],
|
|
7
|
+
"author": "shenjingnan <sjn.code@gmail.com>",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
11
|
+
"zod": "^3.25.64"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
// 小智 AI 客户端配置文件 (JSON5 格式)
|
|
3
|
+
// JSON5 格式支持注释、尾随逗号等特性,更易于手写和维护
|
|
4
|
+
|
|
5
|
+
// MCP 接入点地址
|
|
6
|
+
// 请访问 xiaozhi.me 获取你的专属接入点地址
|
|
7
|
+
mcpEndpoint: "<请填写你的接入点地址(获取地址在 xiaozhi.me)>",
|
|
8
|
+
|
|
9
|
+
// MCP 服务配置
|
|
10
|
+
mcpServers: {
|
|
11
|
+
// 计算器服务
|
|
12
|
+
calculator: {
|
|
13
|
+
command: "node",
|
|
14
|
+
args: ["./mcpServers/calculator.js"],
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
// 日期时间服务
|
|
18
|
+
datetime: {
|
|
19
|
+
command: "node",
|
|
20
|
+
args: ["./mcpServers/datetime.js"],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
// 连接配置
|
|
25
|
+
connection: {
|
|
26
|
+
// 心跳检测间隔(毫秒)
|
|
27
|
+
heartbeatInterval: 30000,
|
|
28
|
+
|
|
29
|
+
// 心跳超时时间(毫秒)
|
|
30
|
+
heartbeatTimeout: 10000,
|
|
31
|
+
|
|
32
|
+
// 重连间隔(毫秒)
|
|
33
|
+
reconnectInterval: 5000,
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// Web UI 配置
|
|
37
|
+
webUI: {
|
|
38
|
+
// Web UI 端口号
|
|
39
|
+
port: 9999,
|
|
40
|
+
},
|
|
41
|
+
}
|