mapbox-create-map-mcp 1.0.0 → 1.1.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.
Files changed (4) hide show
  1. package/README.md +115 -118
  2. package/bin/cli.js +11 -0
  3. package/package.json +6 -2
  4. package/src/server.js +284 -0
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
- # @gis/mapbox-create-map
1
+ # mapbox-create-map-mcp
2
2
 
3
- 一个基于Mapbox的地理数据可视化工具(MCP),可以根据用户需求和地理数据生成一次性临时的地图可视化展示。
3
+ 一个基于Mapbox的地理数据可视化MCP工具,根据用户需求和地理数据生成图层配置信息。
4
4
 
5
5
  ## 安装
6
6
 
7
7
  ```bash
8
- npm install @gis/mapbox-create-map mapbox-gl
8
+ npm install mapbox-create-map-mcp
9
9
  ```
10
10
 
11
11
  ## 使用方法
@@ -13,172 +13,169 @@ npm install @gis/mapbox-create-map mapbox-gl
13
13
  ### 基本使用
14
14
 
15
15
  ```javascript
16
- import CreateMap from '@gis/mapbox-create-map';
16
+ import CreateMap from 'mapbox-create-map-mcp';
17
17
 
18
18
  // 初始化工具
19
- const createMap = new CreateMap('map-container-id', 'your-mapbox-access-token');
19
+ const createMap = new CreateMap();
20
20
 
21
21
  // 配置地图
22
22
  const config = {
23
23
  title: "我的地图标题",
24
24
  description: "地图描述信息",
25
- baseStyle: "light", // 底图样式
26
- center: [116.3974, 39.9092], // 中心点
27
- zoom: 10, // 缩放级别
25
+ baseStyle: "light",
26
+ center: [116.3974, 39.9092],
27
+ zoom: 10,
28
28
  layers: [
29
- // 图层配置
29
+ {
30
+ type: "point",
31
+ id: "my-points",
32
+ fileUrl: "https://example.com/points.geojson",
33
+ config: {
34
+ 'circle-color': '#FF0000',
35
+ 'circle-radius': 8,
36
+ 'circle-opacity': 0.8
37
+ }
38
+ }
30
39
  ]
31
40
  };
32
41
 
33
- // 生成地图配置信息
42
+ // 生成图层配置信息
34
43
  createMap.create(config)
35
44
  .then(result => {
36
- console.log("地图配置信息生成成功", result);
37
- // result 是 GeoJSON 格式的图层配置信息,可用于自定义渲染
45
+ console.log("生成成功", result);
46
+ // result 是 FeatureCollection 格式,包含图层配置信息
38
47
  })
39
48
  .catch(error => {
40
- console.error("地图配置信息生成失败", error);
49
+ console.error("生成失败", error);
41
50
  });
42
51
  ```
43
52
 
44
- ### 图层类型
53
+ ### 返回格式
45
54
 
46
- #### 点类型要素图层
47
-
48
- ##### 散点 (scatter)
49
55
  ```javascript
50
56
  {
51
- type: 'scatter',
52
- id: 'my-scatter-layer',
53
- data: geojsonData, // 或 fileUrl: 'path/to/data.geojson'
54
- color: '#3f87db', // 填充颜色
55
- size: 8, // 点大小,默认8px
56
- opacity: 100, // 透明度,0-100
57
- strokeColor: '#ffffff',// 描边颜色
58
- strokeWidth: 0 // 描边宽度
57
+ type: "FeatureCollection",
58
+ features: [
59
+ {
60
+ data: "https://example.com/points.geojson",
61
+ properties: {
62
+ name: "my-points",
63
+ type: "point",
64
+ style: {
65
+ type: "point",
66
+ "circle-color": "#FF0000",
67
+ "circle-radius": 8,
68
+ "circle-opacity": 0.8
69
+ }
70
+ }
71
+ }
72
+ ]
59
73
  }
60
74
  ```
61
75
 
62
- ##### 图标点 (icon)
63
- ```javascript
64
- {
65
- type: 'icon',
66
- id: 'my-icon-layer',
67
- data: geojsonData,
68
- iconImage: 'car-15', // 图标图片
69
- iconRotate: 0, // 图标旋转角度
70
- size: 24 // 图标大小
71
- }
72
- ```
76
+ ## 图层类型
73
77
 
74
- ##### 文字点 (text)
75
- ```javascript
76
- {
77
- type: 'text',
78
- id: 'my-text-layer',
79
- data: geojsonData,
80
- textColor: '#000000', // 文字颜色
81
- textSize: 12, // 字体大小
82
- textHaloColor: '#ffffff', // 文字描边颜色
83
- textHaloWidth: 2 // 文字描边宽度
84
- }
85
- ```
78
+ 仅支持三种几何类型:`point`、`linestring`、`polygon`
86
79
 
87
- ##### 热力点 (heatmap)
88
- ```javascript
89
- {
90
- type: 'heatmap',
91
- id: 'my-heatmap-layer',
92
- data: geojsonData,
93
- color: ['#3288bd', '#abdda4', '#fee08b', '#f46d43', '#d53e4f'], // 色带
94
- radius: 20, // 热力半径
95
- weight: 1 // 热力点权重
96
- }
97
- ```
80
+ ### point(点图层)
81
+
82
+ 对应 Mapbox circle 图层
98
83
 
99
- ##### 3D热力点 (heatmap3d)
100
84
  ```javascript
101
85
  {
102
- type: 'heatmap3d',
103
- id: 'my-heatmap3d-layer',
104
- data: geojsonData,
105
- color: ['#000', '#555', '#fff'], // 色带
106
- radius: 20, // 热力半径
107
- weight: 1 // 热力点权重
86
+ type: "point",
87
+ id: "my-points",
88
+ fileUrl: "path/to/data.geojson",
89
+ config: {
90
+ 'circle-color': '#0000FF', // 填充颜色
91
+ 'circle-radius': 5, // 半径(像素)
92
+ 'circle-opacity': 1, // 填充透明度 0-1
93
+ 'circle-stroke-color': '#000000', // 边框颜色
94
+ 'circle-stroke-width': 0, // 边框宽度(像素)
95
+ 'circle-stroke-opacity': 1, // 边框透明度 0-1
96
+ 'circle-blur': 0, // 模糊量(像素)
97
+ 'circle-translate': [0, 0], // 偏移量 [x, y]
98
+ 'circle-translate-anchor': 'map', // 偏移参考 'map' | 'viewport'
99
+ 'circle-pitch-scale': 'map', // 倾斜缩放 'map' | 'viewport'
100
+ 'circle-pitch-alignment': 'viewport' // 倾斜方向 'map' | 'viewport'
101
+ }
108
102
  }
109
103
  ```
110
104
 
111
- #### 线类型要素图层
105
+ ### linestring(线图层)
106
+
107
+ 对应 Mapbox line 图层
112
108
 
113
- ##### 线 (line)
114
109
  ```javascript
115
110
  {
116
- type: 'line',
117
- id: 'my-line-layer',
118
- data: geojsonData,
119
- color: '#3f87db', // 线颜色
120
- opacity: 100, // 透明度
121
- width: 2, // 线宽度
122
- dash: 'solid' // 实线或虚线 ('solid' 或 'dash')
111
+ type: "linestring",
112
+ id: "my-lines",
113
+ fileUrl: "path/to/data.geojson",
114
+ config: {
115
+ 'line-color': '#FF0000', // 线条颜色
116
+ 'line-width': 1, // 线条宽度
117
+ 'line-opacity': 1, // 透明度 0-1
118
+ 'line-blur': 0, // 模糊度
119
+ 'line-gap-width': 0, // 间隙宽度
120
+ 'line-offset': 0, // 偏移量
121
+ 'line-dasharray': null // 虚线样式,如 [2, 2]
122
+ }
123
123
  }
124
124
  ```
125
125
 
126
- #### 面类型要素图层
126
+ ### polygon(面图层)
127
127
 
128
- ##### 填充面 (fill)
129
- ```javascript
130
- {
131
- type: 'fill',
132
- id: 'my-fill-layer',
133
- data: geojsonData,
134
- color: '#3f87db', // 填充颜色
135
- opacity: 50, // 透明度
136
- strokeColor: '#ffffff',// 描边颜色
137
- strokeWidth: 1 // 描边宽度
138
- }
139
- ```
128
+ 对应 Mapbox fill 图层
140
129
 
141
- ##### 三维拉伸面 (extrusion)
142
130
  ```javascript
143
131
  {
144
- type: 'extrusion',
145
- id: 'my-extrusion-layer',
146
- data: geojsonData,
147
- color: '#3f87db', // 填充颜色
148
- opacity: 80, // 透明度
149
- height: 100 // 拉伸高度(米)
132
+ type: "polygon",
133
+ id: "my-polygons",
134
+ fileUrl: "path/to/data.geojson",
135
+ config: {
136
+ 'fill-color': '#FFFF00', // 填充颜色
137
+ 'fill-opacity': 1, // 填充透明度 0-1
138
+ 'fill-outline-color': '#000000', // 边框颜色
139
+ 'fill-antialias': true, // 抗锯齿
140
+ 'fill-z-offset': 0 // 高程偏移(米)
141
+ }
150
142
  }
151
143
  ```
152
144
 
153
- ### 底图样式
145
+ ## 底图样式
154
146
 
155
- - `whitesmoke`: 淡白色
156
- - `light`: 白色
157
- - `dark`: 黑色
158
- - `blue`: 靛青蓝
159
- - `darkblue`: 深蓝
160
- - `grey`: 灰色
161
- - `fresh`: 草绿色
147
+ - `standard`: 标准
148
+ - `light`: 浅色
149
+ - `dark`: 深色
162
150
  - `satellite`: 卫星图
163
151
 
164
152
  ## API
165
153
 
166
- ### CreateMap(container, accessToken)
167
-
168
- 构造函数,初始化CreateMap实例。
154
+ ### CreateMap()
169
155
 
170
- - `container`: DOM容器ID或DOM元素
171
- - `accessToken`: Mapbox访问令牌(可选,如果不传则需提前设置)
156
+ 构造函数,初始化实例。
172
157
 
173
158
  ### create(config)
174
159
 
175
- 创建地图。
160
+ 生成图层配置信息。
161
+
162
+ **参数:**
163
+ - `config.title` (string, 必需): 地图标题
164
+ - `config.description` (string, 必需): 地图描述
165
+ - `config.layers` (array, 必需): 图层配置数组
166
+ - `config.baseStyle` (string, 可选): 底图样式,默认 'light'
167
+ - `config.center` (array, 可选): 中心点 [经度, 纬度]
168
+ - `config.zoom` (number, 可选): 缩放级别
169
+
170
+ **返回:** Promise,resolve 后返回 FeatureCollection 格式的配置信息
171
+
172
+ ### metadata
176
173
 
177
- - `config`: 配置对象
174
+ 获取参数元数据定义,包含所有参数的类型、默认值、描述等信息。
178
175
 
179
- ### destroy()
176
+ ### layerMetadata
180
177
 
181
- 销毁地图实例,清理资源。
178
+ 获取各图层类型的配置属性元数据。
182
179
 
183
180
  ## 开发
184
181
 
@@ -186,19 +183,19 @@ createMap.create(config)
186
183
  # 安装依赖
187
184
  npm install
188
185
 
189
- # 开发模式(监听变化)
186
+ # 开发模式
190
187
  npm run dev
191
188
 
192
- # 构建生产版本
189
+ # 构建
193
190
  npm run build
194
191
 
195
- # 运行测试
192
+ # 测试
196
193
  npm test
197
194
 
198
- # 代码检查
199
- npm run lint
195
+ # 发布
196
+ npm run publish:npm
200
197
  ```
201
198
 
202
199
  ## 许可证
203
200
 
204
- MIT
201
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * mapbox-create-map-mcp CLI
5
+ * MCP 服务端入口
6
+ */
7
+
8
+ const { McpServer } = require('../src/server.js');
9
+
10
+ const server = new McpServer();
11
+ server.start();
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "mapbox-create-map-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A Mapbox-based MCP tool for creating geographic data visualizations",
5
5
  "main": "dist/index.js",
6
6
  "module": "src/index.js",
7
7
  "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "mapbox-create-map-mcp": "./bin/cli.js"
10
+ },
8
11
  "files": [
9
12
  "dist",
10
- "src"
13
+ "src",
14
+ "bin"
11
15
  ],
12
16
  "scripts": {
13
17
  "build": "webpack --mode production",
package/src/server.js ADDED
@@ -0,0 +1,284 @@
1
+ /**
2
+ * MCP Server - mapbox-create-map-mcp
3
+ * 实现 Model Context Protocol 服务端
4
+ */
5
+
6
+ const readline = require('readline');
7
+ const path = require('path');
8
+
9
+ // 尝试加载打包后的版本,如果不存在则提示构建
10
+ let CreateMap;
11
+ try {
12
+ const distPath = path.join(__dirname, '../dist/index.js');
13
+ const module = require(distPath);
14
+ // UMD格式导出的是MapboxCreateMap,默认导出在.default中
15
+ CreateMap = module.default || module;
16
+ } catch (e) {
17
+ console.error('Error: Please run "npm run build" first');
18
+ console.error(e.message);
19
+ process.exit(1);
20
+ }
21
+
22
+ class McpServer {
23
+ constructor() {
24
+ this.createMap = new CreateMap();
25
+ this.serverInfo = {
26
+ name: 'mapbox-create-map-mcp',
27
+ version: '1.0.0',
28
+ description: '基于Mapbox的地理数据可视化MCP工具'
29
+ };
30
+ }
31
+
32
+ /**
33
+ * 启动MCP服务
34
+ */
35
+ start() {
36
+ const rl = readline.createInterface({
37
+ input: process.stdin,
38
+ output: process.stdout,
39
+ terminal: false
40
+ });
41
+
42
+ rl.on('line', (line) => {
43
+ try {
44
+ const request = JSON.parse(line);
45
+ this.handleRequest(request);
46
+ } catch (error) {
47
+ this.sendError(-32700, 'Parse error', null);
48
+ }
49
+ });
50
+
51
+ rl.on('close', () => {
52
+ process.exit(0);
53
+ });
54
+
55
+ // 发送服务就绪信号
56
+ process.stderr.write('MCP Server started\n');
57
+ }
58
+
59
+ /**
60
+ * 处理MCP请求
61
+ */
62
+ async handleRequest(request) {
63
+ const { id, method, params } = request;
64
+
65
+ try {
66
+ let result;
67
+
68
+ switch (method) {
69
+ case 'initialize':
70
+ result = this.handleInitialize(params);
71
+ break;
72
+
73
+ case 'tools/list':
74
+ result = this.handleToolsList();
75
+ break;
76
+
77
+ case 'tools/call':
78
+ result = await this.handleToolsCall(params);
79
+ break;
80
+
81
+ case 'resources/list':
82
+ result = this.handleResourcesList();
83
+ break;
84
+
85
+ default:
86
+ this.sendError(-32601, `Method not found: ${method}`, id);
87
+ return;
88
+ }
89
+
90
+ this.sendResponse(id, result);
91
+ } catch (error) {
92
+ this.sendError(-32000, error.message, id);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * 处理初始化请求
98
+ */
99
+ handleInitialize(params) {
100
+ return {
101
+ protocolVersion: '2024-11-05',
102
+ capabilities: {
103
+ tools: {},
104
+ resources: {}
105
+ },
106
+ serverInfo: this.serverInfo
107
+ };
108
+ }
109
+
110
+ /**
111
+ * 处理工具列表请求
112
+ */
113
+ handleToolsList() {
114
+ const metadata = this.createMap.metadata;
115
+ const layerMetadata = this.createMap.layerMetadata;
116
+
117
+ return {
118
+ tools: [
119
+ {
120
+ name: 'CreateMap',
121
+ description: '根据配置生成地图图层配置信息,返回FeatureCollection格式的结果用于渲染',
122
+ inputSchema: {
123
+ type: 'object',
124
+ properties: {
125
+ title: {
126
+ type: 'string',
127
+ description: metadata.title.description
128
+ },
129
+ description: {
130
+ type: 'string',
131
+ description: metadata.description.description
132
+ },
133
+ baseStyle: {
134
+ type: 'string',
135
+ enum: metadata.baseStyle.enum,
136
+ default: metadata.baseStyle.default,
137
+ description: metadata.baseStyle.description
138
+ },
139
+ center: {
140
+ type: 'array',
141
+ items: { type: 'number' },
142
+ default: metadata.center.default,
143
+ description: metadata.center.description
144
+ },
145
+ zoom: {
146
+ type: 'number',
147
+ default: metadata.zoom.default,
148
+ description: metadata.zoom.description
149
+ },
150
+ layers: {
151
+ type: 'array',
152
+ description: metadata.layers.description,
153
+ items: {
154
+ type: 'object',
155
+ properties: {
156
+ id: { type: 'string', description: '图层唯一标识符' },
157
+ type: {
158
+ type: 'string',
159
+ enum: ['point', 'linestring', 'polygon'],
160
+ description: '图层类型'
161
+ },
162
+ data: { type: 'object', description: 'GeoJSON格式的地理数据' },
163
+ fileUrl: { type: 'string', description: '数据文件URL' },
164
+ config: {
165
+ type: 'object',
166
+ description: '图层样式配置,属性取决于type字段的值',
167
+ propertiesByType: {
168
+ point: {
169
+ description: '当type="point"时,config使用以下属性(对应Mapbox circle图层)',
170
+ properties: this.formatProperties(layerMetadata.point)
171
+ },
172
+ linestring: {
173
+ description: '当type="linestring"时,config使用以下属性(对应Mapbox line图层)',
174
+ properties: this.formatProperties(layerMetadata.linestring)
175
+ },
176
+ polygon: {
177
+ description: '当type="polygon"时,config使用以下属性(对应Mapbox fill图层)',
178
+ properties: this.formatProperties(layerMetadata.polygon)
179
+ }
180
+ }
181
+ }
182
+ },
183
+ required: ['id', 'type']
184
+ }
185
+ }
186
+ },
187
+ required: ['title', 'description', 'layers']
188
+ }
189
+ }
190
+ ]
191
+ };
192
+ }
193
+
194
+ /**
195
+ * 格式化属性元数据
196
+ */
197
+ formatProperties(meta) {
198
+ const props = {};
199
+ for (const key in meta) {
200
+ const prop = meta[key];
201
+ props[key] = {
202
+ type: prop.type,
203
+ default: prop.default,
204
+ description: prop.description
205
+ };
206
+ if (prop.enum) {
207
+ props[key].enum = prop.enum;
208
+ }
209
+ if (prop.min !== undefined) {
210
+ props[key].minimum = prop.min;
211
+ }
212
+ if (prop.max !== undefined) {
213
+ props[key].maximum = prop.max;
214
+ }
215
+ }
216
+ return props;
217
+ }
218
+
219
+ /**
220
+ * 处理工具调用请求
221
+ */
222
+ async handleToolsCall(params) {
223
+ const { name, arguments: args } = params;
224
+
225
+ if (name !== 'CreateMap') {
226
+ throw new Error(`Unknown tool: ${name}`);
227
+ }
228
+
229
+ try {
230
+ const result = await this.createMap.create(args);
231
+ return {
232
+ content: [
233
+ {
234
+ type: 'text',
235
+ text: JSON.stringify(result, null, 2)
236
+ }
237
+ ]
238
+ };
239
+ } catch (error) {
240
+ return {
241
+ content: [
242
+ {
243
+ type: 'text',
244
+ text: `Error: ${error.message}`
245
+ }
246
+ ],
247
+ isError: true
248
+ };
249
+ }
250
+ }
251
+
252
+ /**
253
+ * 处理资源列表请求
254
+ */
255
+ handleResourcesList() {
256
+ return { resources: [] };
257
+ }
258
+
259
+ /**
260
+ * 发送响应
261
+ */
262
+ sendResponse(id, result) {
263
+ const response = {
264
+ jsonrpc: '2.0',
265
+ id,
266
+ result
267
+ };
268
+ console.log(JSON.stringify(response));
269
+ }
270
+
271
+ /**
272
+ * 发送错误
273
+ */
274
+ sendError(code, message, id) {
275
+ const response = {
276
+ jsonrpc: '2.0',
277
+ id,
278
+ error: { code, message }
279
+ };
280
+ console.log(JSON.stringify(response));
281
+ }
282
+ }
283
+
284
+ module.exports = { McpServer };