mapbox-create-map-mcp 1.2.1 → 1.3.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/README.md +35 -1
- package/package.json +1 -1
- package/src/popui.js +209 -0
- package/src/server.js +240 -5
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ npx mapbox-create-map-mcp
|
|
|
33
33
|
|
|
34
34
|
### MCP 工具
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
#### CreateMap - 生成地图图层配置
|
|
37
37
|
|
|
38
38
|
输入参数:
|
|
39
39
|
- `title` (string, 必需): 地图标题
|
|
@@ -43,6 +43,40 @@ npx mapbox-create-map-mcp
|
|
|
43
43
|
- `center` (array, 可选): 中心点 [经度, 纬度]
|
|
44
44
|
- `zoom` (number, 可选): 缩放级别
|
|
45
45
|
|
|
46
|
+
#### CreatePopup - 生成弹框样式配置
|
|
47
|
+
|
|
48
|
+
输入参数:
|
|
49
|
+
- `name` (string, 必需): 弹框样式名称
|
|
50
|
+
- `htmlContent` (string, 必需): 弹框的HTML内容
|
|
51
|
+
- `theme` (string, 可选): 预设主题 `light` | `dark` | `minimal` | `custom`
|
|
52
|
+
- `closeButton` (boolean, 可选): 是否显示关闭按钮
|
|
53
|
+
- `closeOnClick` (boolean, 可选): 点击地图时是否关闭
|
|
54
|
+
- `closeOnMove` (boolean, 可选): 地图移动时是否关闭
|
|
55
|
+
- `anchor` (string, 可选): 锚点位置 `top` | `bottom` | `left` | `right` 等
|
|
56
|
+
- `offset` (array, 可选): 偏移量 [x, y]
|
|
57
|
+
- `maxWidth` (number, 可选): 最大宽度(px)
|
|
58
|
+
- `altitude` (number, 可选): 海拔高度(米)
|
|
59
|
+
- `className` (string, 可选): 自定义CSS类名
|
|
60
|
+
|
|
61
|
+
返回结果示例:
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"success": true,
|
|
65
|
+
"config": {
|
|
66
|
+
"name": "深色主题",
|
|
67
|
+
"closeButton": true,
|
|
68
|
+
"closeOnClick": true,
|
|
69
|
+
"closeOnMove": false,
|
|
70
|
+
"anchor": "top",
|
|
71
|
+
"offset": [0, 0],
|
|
72
|
+
"maxWidth": 240,
|
|
73
|
+
"className": "dark-popup",
|
|
74
|
+
"altitude": 0,
|
|
75
|
+
"htmlContent": "<div style=\"padding: 15px; background: #2d2d2d; color: #fff;\">...</div>"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
46
80
|
## JavaScript 库使用
|
|
47
81
|
|
|
48
82
|
### 基本使用
|
package/package.json
CHANGED
package/src/popui.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CreatePopup - 弹框样式配置生成工具
|
|
3
|
+
* 用于生成Mapbox弹框(Popup)的样式配置
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class CreatePopup {
|
|
7
|
+
constructor() {
|
|
8
|
+
/**
|
|
9
|
+
* 参数元数据定义
|
|
10
|
+
*/
|
|
11
|
+
this.metadata = {
|
|
12
|
+
name: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
required: true,
|
|
15
|
+
default: '默认样式',
|
|
16
|
+
description: '弹框样式名称'
|
|
17
|
+
},
|
|
18
|
+
closeButton: {
|
|
19
|
+
type: 'boolean',
|
|
20
|
+
required: false,
|
|
21
|
+
default: true,
|
|
22
|
+
description: '是否显示右上角的关闭按钮'
|
|
23
|
+
},
|
|
24
|
+
closeOnClick: {
|
|
25
|
+
type: 'boolean',
|
|
26
|
+
required: false,
|
|
27
|
+
default: true,
|
|
28
|
+
description: '点击地图任意地方时是否自动关闭Popup'
|
|
29
|
+
},
|
|
30
|
+
closeOnMove: {
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
required: false,
|
|
33
|
+
default: false,
|
|
34
|
+
description: '地图移动(拖动或缩放)时是否自动关闭Popup'
|
|
35
|
+
},
|
|
36
|
+
anchor: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
required: false,
|
|
39
|
+
default: 'top',
|
|
40
|
+
enum: ['auto', 'center', 'top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'],
|
|
41
|
+
description: 'Popup箭头朝向锚点位置'
|
|
42
|
+
},
|
|
43
|
+
offset: {
|
|
44
|
+
type: 'array',
|
|
45
|
+
required: false,
|
|
46
|
+
default: [0, 0],
|
|
47
|
+
description: '偏移量,用于微调Popup位置,格式为[x, y],单位为像素'
|
|
48
|
+
},
|
|
49
|
+
maxWidth: {
|
|
50
|
+
type: 'number',
|
|
51
|
+
required: false,
|
|
52
|
+
default: 240,
|
|
53
|
+
min: 100,
|
|
54
|
+
max: 800,
|
|
55
|
+
description: '弹框最大宽度,单位为像素(px)'
|
|
56
|
+
},
|
|
57
|
+
className: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
required: false,
|
|
60
|
+
default: '',
|
|
61
|
+
description: '给Popup添加自定义CSS class名'
|
|
62
|
+
},
|
|
63
|
+
altitude: {
|
|
64
|
+
type: 'number',
|
|
65
|
+
required: false,
|
|
66
|
+
default: 0,
|
|
67
|
+
min: 0,
|
|
68
|
+
max: 10000,
|
|
69
|
+
description: '弹窗在地图表面上方的海拔高度(米)'
|
|
70
|
+
},
|
|
71
|
+
htmlContent: {
|
|
72
|
+
type: 'string',
|
|
73
|
+
required: true,
|
|
74
|
+
default: '<div style="padding: 10px;"><h3>标题</h3><p>内容</p></div>',
|
|
75
|
+
description: '弹框的HTML内容,支持完整的HTML标签和内联样式'
|
|
76
|
+
},
|
|
77
|
+
theme: {
|
|
78
|
+
type: 'string',
|
|
79
|
+
required: false,
|
|
80
|
+
default: 'light',
|
|
81
|
+
enum: ['light', 'dark', 'minimal', 'custom'],
|
|
82
|
+
description: '预设主题样式:light(浅色)、dark(深色)、minimal(简约)、custom(自定义)'
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 预设主题样式模板
|
|
88
|
+
*/
|
|
89
|
+
this.themeTemplates = {
|
|
90
|
+
light: {
|
|
91
|
+
className: 'light-popup',
|
|
92
|
+
htmlWrapper: '<div style="padding: 10px; background: #fff; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">{{content}}</div>'
|
|
93
|
+
},
|
|
94
|
+
dark: {
|
|
95
|
+
className: 'dark-popup',
|
|
96
|
+
htmlWrapper: '<div style="padding: 15px; background: #2d2d2d; color: #fff; border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.3);">{{content}}</div>'
|
|
97
|
+
},
|
|
98
|
+
minimal: {
|
|
99
|
+
className: 'minimal-popup',
|
|
100
|
+
htmlWrapper: '<div style="padding: 8px 12px; background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 2px; font-size: 12px;">{{content}}</div>'
|
|
101
|
+
},
|
|
102
|
+
custom: {
|
|
103
|
+
className: '',
|
|
104
|
+
htmlWrapper: '{{content}}'
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 验证参数
|
|
111
|
+
*/
|
|
112
|
+
validate(config) {
|
|
113
|
+
const errors = [];
|
|
114
|
+
|
|
115
|
+
// 验证必填参数
|
|
116
|
+
if (!config.name || typeof config.name !== 'string') {
|
|
117
|
+
errors.push('name 是必填字段,且必须是字符串');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!config.htmlContent || typeof config.htmlContent !== 'string') {
|
|
121
|
+
errors.push('htmlContent 是必填字段,且必须是字符串');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 验证anchor枚举值
|
|
125
|
+
if (config.anchor) {
|
|
126
|
+
const validAnchors = ['auto', 'center', 'top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'];
|
|
127
|
+
if (!validAnchors.includes(config.anchor)) {
|
|
128
|
+
errors.push(`anchor 必须是以下值之一: ${validAnchors.join(', ')}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 验证theme枚举值
|
|
133
|
+
if (config.theme) {
|
|
134
|
+
const validThemes = ['light', 'dark', 'minimal', 'custom'];
|
|
135
|
+
if (!validThemes.includes(config.theme)) {
|
|
136
|
+
errors.push(`theme 必须是以下值之一: ${validThemes.join(', ')}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 验证offset格式
|
|
141
|
+
if (config.offset) {
|
|
142
|
+
if (!Array.isArray(config.offset) || config.offset.length !== 2) {
|
|
143
|
+
errors.push('offset 必须是包含两个数字的数组 [x, y]');
|
|
144
|
+
} else if (typeof config.offset[0] !== 'number' || typeof config.offset[1] !== 'number') {
|
|
145
|
+
errors.push('offset 数组中的值必须是数字');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 验证maxWidth范围
|
|
150
|
+
if (config.maxWidth !== undefined) {
|
|
151
|
+
if (typeof config.maxWidth !== 'number' || config.maxWidth < 100 || config.maxWidth > 800) {
|
|
152
|
+
errors.push('maxWidth 必须是100到800之间的数字');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 验证altitude范围
|
|
157
|
+
if (config.altitude !== undefined) {
|
|
158
|
+
if (typeof config.altitude !== 'number' || config.altitude < 0 || config.altitude > 10000) {
|
|
159
|
+
errors.push('altitude 必须是0到10000之间的数字');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return errors;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 创建弹框配置
|
|
168
|
+
* @param {Object} config - 弹框配置参数
|
|
169
|
+
* @returns {Promise<Object>} 弹框配置对象
|
|
170
|
+
*/
|
|
171
|
+
async create(config) {
|
|
172
|
+
// 验证参数
|
|
173
|
+
const errors = this.validate(config);
|
|
174
|
+
if (errors.length > 0) {
|
|
175
|
+
throw new Error(`参数验证失败: ${errors.join('; ')}`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 获取主题模板
|
|
179
|
+
const theme = config.theme || 'light';
|
|
180
|
+
const themeTemplate = this.themeTemplates[theme] || this.themeTemplates.light;
|
|
181
|
+
|
|
182
|
+
// 应用主题包装(如果htmlContent不是完整的HTML结构)
|
|
183
|
+
let finalHtmlContent = config.htmlContent;
|
|
184
|
+
if (theme !== 'custom' && !config.htmlContent.includes('style=')) {
|
|
185
|
+
finalHtmlContent = themeTemplate.htmlWrapper.replace('{{content}}', config.htmlContent);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 构建弹框配置对象
|
|
189
|
+
const popupConfig = {
|
|
190
|
+
name: config.name || this.metadata.name.default,
|
|
191
|
+
closeButton: config.closeButton !== undefined ? config.closeButton : this.metadata.closeButton.default,
|
|
192
|
+
closeOnClick: config.closeOnClick !== undefined ? config.closeOnClick : this.metadata.closeOnClick.default,
|
|
193
|
+
closeOnMove: config.closeOnMove !== undefined ? config.closeOnMove : this.metadata.closeOnMove.default,
|
|
194
|
+
anchor: config.anchor || this.metadata.anchor.default,
|
|
195
|
+
offset: config.offset || this.metadata.offset.default,
|
|
196
|
+
maxWidth: config.maxWidth || this.metadata.maxWidth.default,
|
|
197
|
+
className: config.className || themeTemplate.className || this.metadata.className.default,
|
|
198
|
+
altitude: config.altitude !== undefined ? config.altitude : this.metadata.altitude.default,
|
|
199
|
+
htmlContent: finalHtmlContent
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
success: true,
|
|
204
|
+
config: popupConfig
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export default CreatePopup;
|
package/src/server.js
CHANGED
|
@@ -19,9 +19,178 @@ try {
|
|
|
19
19
|
process.exit(1);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* CreatePopup - 弹框样式配置生成工具
|
|
24
|
+
*/
|
|
25
|
+
class CreatePopup {
|
|
26
|
+
constructor() {
|
|
27
|
+
this.metadata = {
|
|
28
|
+
name: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
required: true,
|
|
31
|
+
default: '默认样式',
|
|
32
|
+
description: '弹框样式名称'
|
|
33
|
+
},
|
|
34
|
+
closeButton: {
|
|
35
|
+
type: 'boolean',
|
|
36
|
+
required: false,
|
|
37
|
+
default: true,
|
|
38
|
+
description: '是否显示右上角的关闭按钮'
|
|
39
|
+
},
|
|
40
|
+
closeOnClick: {
|
|
41
|
+
type: 'boolean',
|
|
42
|
+
required: false,
|
|
43
|
+
default: true,
|
|
44
|
+
description: '点击地图任意地方时是否自动关闭Popup'
|
|
45
|
+
},
|
|
46
|
+
closeOnMove: {
|
|
47
|
+
type: 'boolean',
|
|
48
|
+
required: false,
|
|
49
|
+
default: false,
|
|
50
|
+
description: '地图移动(拖动或缩放)时是否自动关闭Popup'
|
|
51
|
+
},
|
|
52
|
+
anchor: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
required: false,
|
|
55
|
+
default: 'top',
|
|
56
|
+
enum: ['auto', 'center', 'top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'],
|
|
57
|
+
description: 'Popup箭头朝向锚点位置'
|
|
58
|
+
},
|
|
59
|
+
offset: {
|
|
60
|
+
type: 'array',
|
|
61
|
+
required: false,
|
|
62
|
+
default: [0, 0],
|
|
63
|
+
description: '偏移量,用于微调Popup位置,格式为[x, y],单位为像素'
|
|
64
|
+
},
|
|
65
|
+
maxWidth: {
|
|
66
|
+
type: 'number',
|
|
67
|
+
required: false,
|
|
68
|
+
default: 240,
|
|
69
|
+
min: 100,
|
|
70
|
+
max: 800,
|
|
71
|
+
description: '弹框最大宽度,单位为像素(px)'
|
|
72
|
+
},
|
|
73
|
+
className: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
required: false,
|
|
76
|
+
default: '',
|
|
77
|
+
description: '给Popup添加自定义CSS class名'
|
|
78
|
+
},
|
|
79
|
+
altitude: {
|
|
80
|
+
type: 'number',
|
|
81
|
+
required: false,
|
|
82
|
+
default: 0,
|
|
83
|
+
min: 0,
|
|
84
|
+
max: 10000,
|
|
85
|
+
description: '弹窗在地图表面上方的海拔高度(米)'
|
|
86
|
+
},
|
|
87
|
+
htmlContent: {
|
|
88
|
+
type: 'string',
|
|
89
|
+
required: true,
|
|
90
|
+
default: '<div style="padding: 10px;"><h3>{name}</h3><p>{description}</p></div>',
|
|
91
|
+
description: '弹框的HTML内容,支持{fieldName}占位符动态绑定GeoJSON要素的properties属性。例如: "<h3>{name}</h3><p>{address}</p>"'
|
|
92
|
+
},
|
|
93
|
+
theme: {
|
|
94
|
+
type: 'string',
|
|
95
|
+
required: false,
|
|
96
|
+
default: 'light',
|
|
97
|
+
enum: ['light', 'dark', 'minimal', 'custom'],
|
|
98
|
+
description: '预设主题样式:light(浅色)、dark(深色)、minimal(简约)、custom(自定义)'
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
this.themeTemplates = {
|
|
103
|
+
light: {
|
|
104
|
+
className: 'light-popup',
|
|
105
|
+
htmlWrapper: '<div style="padding: 10px; background: #fff; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">{{content}}</div>'
|
|
106
|
+
},
|
|
107
|
+
dark: {
|
|
108
|
+
className: 'dark-popup',
|
|
109
|
+
htmlWrapper: '<div style="padding: 15px; background: #2d2d2d; color: #fff; border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.3);">{{content}}</div>'
|
|
110
|
+
},
|
|
111
|
+
minimal: {
|
|
112
|
+
className: 'minimal-popup',
|
|
113
|
+
htmlWrapper: '<div style="padding: 8px 12px; background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 2px; font-size: 12px;">{{content}}</div>'
|
|
114
|
+
},
|
|
115
|
+
custom: {
|
|
116
|
+
className: '',
|
|
117
|
+
htmlWrapper: '{{content}}'
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
validate(config) {
|
|
123
|
+
const errors = [];
|
|
124
|
+
if (!config.name || typeof config.name !== 'string') {
|
|
125
|
+
errors.push('name 是必填字段,且必须是字符串');
|
|
126
|
+
}
|
|
127
|
+
if (!config.htmlContent || typeof config.htmlContent !== 'string') {
|
|
128
|
+
errors.push('htmlContent 是必填字段,且必须是字符串');
|
|
129
|
+
}
|
|
130
|
+
if (config.anchor) {
|
|
131
|
+
const validAnchors = ['auto', 'center', 'top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'];
|
|
132
|
+
if (!validAnchors.includes(config.anchor)) {
|
|
133
|
+
errors.push(`anchor 必须是以下值之一: ${validAnchors.join(', ')}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (config.theme) {
|
|
137
|
+
const validThemes = ['light', 'dark', 'minimal', 'custom'];
|
|
138
|
+
if (!validThemes.includes(config.theme)) {
|
|
139
|
+
errors.push(`theme 必须是以下值之一: ${validThemes.join(', ')}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (config.offset) {
|
|
143
|
+
if (!Array.isArray(config.offset) || config.offset.length !== 2) {
|
|
144
|
+
errors.push('offset 必须是包含两个数字的数组 [x, y]');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (config.maxWidth !== undefined) {
|
|
148
|
+
if (typeof config.maxWidth !== 'number' || config.maxWidth < 100 || config.maxWidth > 800) {
|
|
149
|
+
errors.push('maxWidth 必须是100到800之间的数字');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (config.altitude !== undefined) {
|
|
153
|
+
if (typeof config.altitude !== 'number' || config.altitude < 0 || config.altitude > 10000) {
|
|
154
|
+
errors.push('altitude 必须是0到10000之间的数字');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return errors;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async create(config) {
|
|
161
|
+
const errors = this.validate(config);
|
|
162
|
+
if (errors.length > 0) {
|
|
163
|
+
throw new Error(`参数验证失败: ${errors.join('; ')}`);
|
|
164
|
+
}
|
|
165
|
+
const theme = config.theme || 'light';
|
|
166
|
+
const themeTemplate = this.themeTemplates[theme] || this.themeTemplates.light;
|
|
167
|
+
let finalHtmlContent = config.htmlContent;
|
|
168
|
+
if (theme !== 'custom' && !config.htmlContent.includes('style=')) {
|
|
169
|
+
finalHtmlContent = themeTemplate.htmlWrapper.replace('{{content}}', config.htmlContent);
|
|
170
|
+
}
|
|
171
|
+
const popupConfig = {
|
|
172
|
+
name: config.name || this.metadata.name.default,
|
|
173
|
+
closeButton: config.closeButton !== undefined ? config.closeButton : this.metadata.closeButton.default,
|
|
174
|
+
closeOnClick: config.closeOnClick !== undefined ? config.closeOnClick : this.metadata.closeOnClick.default,
|
|
175
|
+
closeOnMove: config.closeOnMove !== undefined ? config.closeOnMove : this.metadata.closeOnMove.default,
|
|
176
|
+
anchor: config.anchor || this.metadata.anchor.default,
|
|
177
|
+
offset: config.offset || this.metadata.offset.default,
|
|
178
|
+
maxWidth: config.maxWidth || this.metadata.maxWidth.default,
|
|
179
|
+
className: config.className || themeTemplate.className || this.metadata.className.default,
|
|
180
|
+
altitude: config.altitude !== undefined ? config.altitude : this.metadata.altitude.default,
|
|
181
|
+
htmlContent: finalHtmlContent
|
|
182
|
+
};
|
|
183
|
+
return {
|
|
184
|
+
success: true,
|
|
185
|
+
config: popupConfig
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
22
190
|
class McpServer {
|
|
23
191
|
constructor() {
|
|
24
192
|
this.createMap = new CreateMap();
|
|
193
|
+
this.createPopup = new CreatePopup();
|
|
25
194
|
this.serverInfo = {
|
|
26
195
|
name: 'mapbox-create-map-mcp',
|
|
27
196
|
version: '1.0.0',
|
|
@@ -213,6 +382,64 @@ class McpServer {
|
|
|
213
382
|
},
|
|
214
383
|
required: ['title', 'description', 'layers']
|
|
215
384
|
}
|
|
385
|
+
},
|
|
386
|
+
// CreatePopup 工具
|
|
387
|
+
{
|
|
388
|
+
name: 'CreatePopup',
|
|
389
|
+
description: '根据配置生成Mapbox弹框(Popup)样式配置。支持多种预设主题(浅色、深色、简约),返回完整的弹框配置对象。',
|
|
390
|
+
inputSchema: {
|
|
391
|
+
type: 'object',
|
|
392
|
+
properties: {
|
|
393
|
+
name: {
|
|
394
|
+
type: 'string',
|
|
395
|
+
description: '弹框样式名称'
|
|
396
|
+
},
|
|
397
|
+
closeButton: {
|
|
398
|
+
type: 'boolean',
|
|
399
|
+
description: '是否显示右上角的关闭按钮'
|
|
400
|
+
},
|
|
401
|
+
closeOnClick: {
|
|
402
|
+
type: 'boolean',
|
|
403
|
+
description: '点击地图任意地方时是否自动关闭Popup'
|
|
404
|
+
},
|
|
405
|
+
closeOnMove: {
|
|
406
|
+
type: 'boolean',
|
|
407
|
+
description: '地图移动(拖动或缩放)时是否自动关闭Popup'
|
|
408
|
+
},
|
|
409
|
+
anchor: {
|
|
410
|
+
type: 'string',
|
|
411
|
+
enum: ['auto', 'center', 'top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'],
|
|
412
|
+
description: 'Popup箭头朝向锚点位置'
|
|
413
|
+
},
|
|
414
|
+
offset: {
|
|
415
|
+
type: 'array',
|
|
416
|
+
items: { type: 'number' },
|
|
417
|
+
description: '偏移量,用于微调Popup位置,格式为[x, y],单位为像素'
|
|
418
|
+
},
|
|
419
|
+
maxWidth: {
|
|
420
|
+
type: 'number',
|
|
421
|
+
description: '弹框最大宽度,单位为像素(px),范围100-800'
|
|
422
|
+
},
|
|
423
|
+
className: {
|
|
424
|
+
type: 'string',
|
|
425
|
+
description: '给Popup添加自定义CSS class名'
|
|
426
|
+
},
|
|
427
|
+
altitude: {
|
|
428
|
+
type: 'number',
|
|
429
|
+
description: '弹窗在地图表面上方的海拔高度(米),范围0-10000'
|
|
430
|
+
},
|
|
431
|
+
htmlContent: {
|
|
432
|
+
type: 'string',
|
|
433
|
+
description: '弹框的HTML内容,支持完整的HTML标签和内联样式。支持使用{fieldName}占位符动态绑定GeoJSON要素的properties属性,点击要素时占位符会被替换为实际值。例如:"<h3>{name}</h3><p>地址: {address}</p><p>类型: {type}</p>",当点击的要素properties为{"name":"北京站","address":"北京市东城区","type":"火车站"}时,弹框会显示"<h3>北京站</h3><p>地址: 北京市东城区</p><p>类型: 火车站</p>"。常用占位符: {name}名称, {id}标识, {address}地址, {value}数值, {description}描述等。'
|
|
434
|
+
},
|
|
435
|
+
theme: {
|
|
436
|
+
type: 'string',
|
|
437
|
+
enum: ['light', 'dark', 'minimal', 'custom'],
|
|
438
|
+
description: '预设主题样式: light(浅色), dark(深色), minimal(简约), custom(自定义)'
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
required: ['name', 'htmlContent']
|
|
442
|
+
}
|
|
216
443
|
}
|
|
217
444
|
]
|
|
218
445
|
};
|
|
@@ -249,12 +476,20 @@ class McpServer {
|
|
|
249
476
|
async handleToolsCall(params) {
|
|
250
477
|
const { name, arguments: args } = params;
|
|
251
478
|
|
|
252
|
-
if (name !== 'CreateMap') {
|
|
253
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
479
|
try {
|
|
257
|
-
|
|
480
|
+
let result;
|
|
481
|
+
|
|
482
|
+
switch (name) {
|
|
483
|
+
case 'CreateMap':
|
|
484
|
+
result = await this.createMap.create(args);
|
|
485
|
+
break;
|
|
486
|
+
case 'CreatePopup':
|
|
487
|
+
result = await this.createPopup.create(args);
|
|
488
|
+
break;
|
|
489
|
+
default:
|
|
490
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
491
|
+
}
|
|
492
|
+
|
|
258
493
|
return {
|
|
259
494
|
content: [
|
|
260
495
|
{
|