cesium-mcp-dev 1.139.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 GeoAgent Contributors
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,120 @@
1
+ # cesium-mcp-dev
2
+
3
+ > MCP Server for IDE AI assistants -- Cesium API documentation, code generation, and Entity builder.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/cesium-mcp-dev.svg)](https://www.npmjs.com/package/cesium-mcp-dev)
6
+ [![license](https://img.shields.io/npm/l/cesium-mcp-dev.svg)](LICENSE)
7
+
8
+ ## What is this?
9
+
10
+ `cesium-mcp-dev` helps AI coding assistants (GitHub Copilot, Cursor, Claude Code, etc.) write better CesiumJS code by providing API documentation lookup, code snippet generation, and Entity configuration building -- all through the MCP protocol.
11
+
12
+ ```
13
+ IDE AI Assistant <--MCP stdio--> cesium-mcp-dev --> API docs, code snippets, Entity configs
14
+ ```
15
+
16
+ ## Install & Run
17
+
18
+ ```bash
19
+ npx cesium-mcp-dev
20
+ ```
21
+
22
+ ## IDE Configuration
23
+
24
+ ### VS Code (Copilot)
25
+
26
+ In `.vscode/mcp.json`:
27
+
28
+ ```json
29
+ {
30
+ "servers": {
31
+ "cesium-dev": {
32
+ "command": "npx",
33
+ "args": ["cesium-mcp-dev"]
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ ### Cursor
40
+
41
+ In `.cursor/mcp.json`:
42
+
43
+ ```json
44
+ {
45
+ "mcpServers": {
46
+ "cesium-dev": {
47
+ "command": "npx",
48
+ "args": ["cesium-mcp-dev"]
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ ### Claude Desktop
55
+
56
+ ```json
57
+ {
58
+ "mcpServers": {
59
+ "cesium-dev": {
60
+ "command": "npx",
61
+ "args": ["cesium-mcp-dev"]
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ## MCP Tools (3)
68
+
69
+ ### `cesium_api_lookup`
70
+
71
+ Query Cesium API documentation by class name, method, or property.
72
+
73
+ ```
74
+ Input: "Viewer"
75
+ Output: Constructor signature, properties, methods, and usage examples
76
+ ```
77
+
78
+ **Covered classes**: Viewer, Entity, Camera, Cartesian3, Color, GeoJsonDataSource, ImageryLayer, Cesium3DTileset, Material, ScreenSpaceEventHandler, flyTo, setView (12 classes)
79
+
80
+ ### `cesium_code_gen`
81
+
82
+ Generate Cesium code from natural language descriptions.
83
+
84
+ ```
85
+ Input: "Add a red point marker at Tiananmen Square"
86
+ Output: Complete TypeScript code snippet ready to use
87
+ ```
88
+
89
+ **Built-in snippets**: fly to location, add marker, load GeoJSON, draw polyline, draw polygon, load 3D Tiles, switch basemap, click handler, screenshot, heatmap, trajectory animation (11 scenarios)
90
+
91
+ ### `cesium_entity_builder`
92
+
93
+ Build Entity configurations interactively by specifying type and properties.
94
+
95
+ ```
96
+ Input: type: "polygon", position: {lon: 116.4, lat: 39.9}, color: "#ff6600"
97
+ Output: Complete Entity definition with material, style, and ground clamping
98
+ ```
99
+
100
+ **Supported types**: point, billboard, label, polyline, polygon, model, ellipse, box (8 types)
101
+
102
+ ## Example Interactions
103
+
104
+ **AI assistant asks**: "How do I load a 3D Tiles model from Cesium Ion?"
105
+
106
+ The assistant calls `cesium_api_lookup` with `"Cesium3DTileset"`, gets the API reference, then calls `cesium_code_gen` with the user's specific requirements to generate ready-to-use code.
107
+
108
+ **AI assistant asks**: "Create an Entity for a semi-transparent blue polygon"
109
+
110
+ The assistant calls `cesium_entity_builder` with `type: "polygon", color: "#0066ff", opacity: 0.5` and gets complete code output.
111
+
112
+ ## Compatibility
113
+
114
+ | cesium-mcp-dev | Cesium |
115
+ |----------------|--------|
116
+ | 1.139.x | ~1.139.0 |
117
+
118
+ ## License
119
+
120
+ MIT
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,683 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z } from "zod";
7
+
8
+ // src/resources/api-docs.ts
9
+ var CESIUM_API_DOCS = [
10
+ {
11
+ name: "Viewer",
12
+ category: "class",
13
+ description: "Cesium \u5E94\u7528\u7684\u6838\u5FC3\u5BB9\u5668\uFF0C\u7BA1\u7406\u6240\u6709 Cesium \u90E8\u4EF6\uFF08Scene, Camera, DataSources \u7B49\uFF09\u3002",
14
+ ctor: "new Cesium.Viewer(container: string | Element, options?: Viewer.ConstructorOptions)",
15
+ properties: ["scene", "camera", "entities", "dataSources", "imageryLayers", "clock", "canvas", "container"],
16
+ methods: ["flyTo(target)", "zoomTo(target)", "destroy()", "render()", "resize()"],
17
+ example: `const viewer = new Cesium.Viewer('cesiumContainer', {
18
+ terrain: Cesium.Terrain.fromWorldTerrain(),
19
+ baseLayerPicker: false,
20
+ geocoder: false,
21
+ })`
22
+ },
23
+ {
24
+ name: "Entity",
25
+ category: "class",
26
+ description: "\u8868\u793A\u573A\u666F\u4E2D\u7684\u4E00\u4E2A\u7A7A\u95F4\u5BF9\u8C61\uFF0C\u53EF\u5305\u542B point, billboard, label, polyline, polygon, model \u7B49\u56FE\u5F62\u3002",
27
+ ctor: "new Cesium.Entity(options?: Entity.ConstructorOptions)",
28
+ properties: ["id", "name", "position", "description", "point", "billboard", "label", "polyline", "polygon", "model", "show"],
29
+ methods: ["isAvailable(time)", "merge(source)", "addProperty(name)"],
30
+ example: `viewer.entities.add({
31
+ name: 'My Point',
32
+ position: Cesium.Cartesian3.fromDegrees(116.4, 39.9, 0),
33
+ point: {
34
+ pixelSize: 10,
35
+ color: Cesium.Color.RED,
36
+ },
37
+ })`
38
+ },
39
+ {
40
+ name: "Camera",
41
+ category: "class",
42
+ description: "\u573A\u666F\u7684\u865A\u62DF\u76F8\u673A\uFF0C\u63A7\u5236\u89C6\u89D2\u4F4D\u7F6E\u3001\u65B9\u5411\u548C\u89C6\u91CE\u3002",
43
+ ctor: "\uFF08\u901A\u8FC7 viewer.camera \u83B7\u53D6\uFF09",
44
+ properties: ["position", "direction", "up", "right", "heading", "pitch", "roll", "frustum"],
45
+ methods: ["flyTo(options)", "setView(options)", "lookAt(target, offset)", "zoomIn(amount)", "zoomOut(amount)", "flyHome(duration)"],
46
+ example: `viewer.camera.flyTo({
47
+ destination: Cesium.Cartesian3.fromDegrees(116.4, 39.9, 50000),
48
+ orientation: {
49
+ heading: Cesium.Math.toRadians(0),
50
+ pitch: Cesium.Math.toRadians(-45),
51
+ roll: 0,
52
+ },
53
+ duration: 2,
54
+ })`
55
+ },
56
+ {
57
+ name: "Cartesian3",
58
+ category: "class",
59
+ description: "\u4E09\u7EF4\u7B1B\u5361\u5C14\u5750\u6807\u70B9\uFF0CCesium \u4E2D\u6700\u5E38\u7528\u7684\u4F4D\u7F6E\u8868\u793A\u3002",
60
+ ctor: "new Cesium.Cartesian3(x?: number, y?: number, z?: number)",
61
+ properties: ["x", "y", "z"],
62
+ methods: ["clone()", "equals(right)", "toString()"],
63
+ example: `// \u4ECE\u7ECF\u7EAC\u5EA6\u521B\u5EFA
64
+ const position = Cesium.Cartesian3.fromDegrees(116.4, 39.9, 100)
65
+
66
+ // \u4ECE\u5F27\u5EA6\u521B\u5EFA
67
+ const pos2 = Cesium.Cartesian3.fromRadians(lon, lat, height)
68
+
69
+ // \u8DDD\u79BB\u8BA1\u7B97
70
+ const distance = Cesium.Cartesian3.distance(pos1, pos2)`
71
+ },
72
+ {
73
+ name: "Color",
74
+ category: "class",
75
+ description: "\u989C\u8272\u5BF9\u8C61\uFF0C\u652F\u6301 RGBA \u548C\u5404\u79CD\u9884\u8BBE\u989C\u8272\u5E38\u91CF\u3002",
76
+ ctor: "new Cesium.Color(red?: number, green?: number, blue?: number, alpha?: number)",
77
+ properties: ["red", "green", "blue", "alpha"],
78
+ methods: ["withAlpha(alpha)", "toCssColorString()", "toBytes()"],
79
+ example: `// \u9884\u8BBE\u989C\u8272
80
+ Cesium.Color.RED
81
+ Cesium.Color.BLUE.withAlpha(0.5)
82
+
83
+ // CSS \u5B57\u7B26\u4E32
84
+ Cesium.Color.fromCssColorString('#3388ff')
85
+ Cesium.Color.fromCssColorString('rgba(255, 0, 0, 0.5)')`
86
+ },
87
+ {
88
+ name: "GeoJsonDataSource",
89
+ category: "class",
90
+ description: "\u52A0\u8F7D\u548C\u89E3\u6790 GeoJSON / TopoJSON \u6570\u636E\uFF0C\u81EA\u52A8\u521B\u5EFA\u5BF9\u5E94\u7684 Entity \u96C6\u5408\u3002",
91
+ ctor: "new Cesium.GeoJsonDataSource(name?: string)",
92
+ properties: ["name", "entities", "isLoading", "changedEvent"],
93
+ methods: ["load(data, options)", "process(data, options)"],
94
+ example: `const ds = await Cesium.GeoJsonDataSource.load('/data/boundary.geojson', {
95
+ stroke: Cesium.Color.fromCssColorString('#333'),
96
+ fill: Cesium.Color.BLUE.withAlpha(0.4),
97
+ strokeWidth: 2,
98
+ clampToGround: true,
99
+ })
100
+ viewer.dataSources.add(ds)
101
+ viewer.flyTo(ds)`
102
+ },
103
+ {
104
+ name: "ImageryLayer",
105
+ category: "class",
106
+ description: "\u5F71\u50CF\u56FE\u5C42\uFF0C\u7BA1\u7406\u5E95\u56FE\u548C\u53E0\u52A0\u5F71\u50CF\u3002",
107
+ ctor: "\uFF08\u901A\u8FC7 viewer.imageryLayers.addImageryProvider \u521B\u5EFA\uFF09",
108
+ properties: ["imageryProvider", "alpha", "brightness", "contrast", "show"],
109
+ methods: ["isBaseLayer()", "destroy()"],
110
+ example: `// \u6DFB\u52A0 TMS \u5E95\u56FE
111
+ const provider = new Cesium.UrlTemplateImageryProvider({
112
+ url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
113
+ maximumLevel: 18,
114
+ })
115
+ viewer.imageryLayers.addImageryProvider(provider)`
116
+ },
117
+ {
118
+ name: "Cesium3DTileset",
119
+ category: "class",
120
+ description: "\u52A0\u8F7D\u548C\u6E32\u67D3 3D Tiles \u6570\u636E\u96C6\uFF08\u5EFA\u7B51\u6A21\u578B\u3001\u70B9\u4E91\u7B49\uFF09\u3002",
121
+ ctor: "await Cesium.Cesium3DTileset.fromUrl(url, options)",
122
+ properties: ["root", "boundingSphere", "maximumScreenSpaceError", "show", "style"],
123
+ methods: ["makeStyleDirty()", "destroy()"],
124
+ example: `const tileset = await Cesium.Cesium3DTileset.fromUrl(
125
+ 'https://assets.cesium.com/xxx/tileset.json',
126
+ { maximumScreenSpaceError: 16 }
127
+ )
128
+ viewer.scene.primitives.add(tileset)
129
+ viewer.flyTo(tileset)`
130
+ },
131
+ {
132
+ name: "Material",
133
+ category: "class",
134
+ description: "\u5B9A\u4E49\u51E0\u4F55\u4F53\u8868\u9762\u7684\u6E32\u67D3\u6750\u8D28\uFF08\u989C\u8272\u3001\u7EB9\u7406\u3001\u53D1\u5149\u7B49\uFF09\u3002",
135
+ ctor: "\uFF08\u901A\u5E38\u901A\u8FC7 MaterialProperty \u914D\u7F6E\uFF09",
136
+ properties: ["type", "uniforms"],
137
+ methods: [],
138
+ example: `// \u6761\u7EB9\u6750\u8D28
139
+ entity.polygon.material = new Cesium.StripeMaterialProperty({
140
+ evenColor: Cesium.Color.WHITE,
141
+ oddColor: Cesium.Color.BLUE,
142
+ repeat: 5,
143
+ })
144
+
145
+ // \u53D1\u5149\u7EBF
146
+ entity.polyline.material = new Cesium.PolylineGlowMaterialProperty({
147
+ glowPower: 0.2,
148
+ color: Cesium.Color.CYAN,
149
+ })`
150
+ },
151
+ {
152
+ name: "flyTo",
153
+ category: "method",
154
+ description: "Camera.flyTo \u2014 \u5E73\u6ED1\u98DE\u884C\u5230\u76EE\u6807\u4F4D\u7F6E\u3002",
155
+ example: `viewer.camera.flyTo({
156
+ destination: Cesium.Cartesian3.fromDegrees(116.4, 39.9, 15000),
157
+ orientation: {
158
+ heading: Cesium.Math.toRadians(0),
159
+ pitch: Cesium.Math.toRadians(-45),
160
+ roll: 0,
161
+ },
162
+ duration: 2,
163
+ complete: () => console.log('\u98DE\u884C\u5B8C\u6210'),
164
+ })`
165
+ },
166
+ {
167
+ name: "setView",
168
+ category: "method",
169
+ description: "Camera.setView \u2014 \u7ACB\u5373\u8BBE\u7F6E\u76F8\u673A\u4F4D\u7F6E\uFF08\u65E0\u52A8\u753B\uFF09\u3002",
170
+ example: `viewer.camera.setView({
171
+ destination: Cesium.Cartesian3.fromDegrees(116.4, 39.9, 15000),
172
+ orientation: {
173
+ heading: 0,
174
+ pitch: -Cesium.Math.PI_OVER_FOUR,
175
+ roll: 0,
176
+ },
177
+ })`
178
+ },
179
+ {
180
+ name: "ScreenSpaceEventHandler",
181
+ category: "class",
182
+ description: "\u5904\u7406\u7528\u6237\u8F93\u5165\u4E8B\u4EF6\uFF08\u9F20\u6807\u70B9\u51FB\u3001\u79FB\u52A8\u3001\u60AC\u505C\u7B49\uFF09\u3002",
183
+ ctor: "new Cesium.ScreenSpaceEventHandler(canvas)",
184
+ properties: [],
185
+ methods: ["setInputAction(action, type)", "removeInputAction(type)", "destroy()"],
186
+ example: `const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas)
187
+ handler.setInputAction((movement: { position: Cesium.Cartesian2 }) => {
188
+ const picked = viewer.scene.pick(movement.position)
189
+ if (Cesium.defined(picked)) {
190
+ console.log('\u70B9\u51FB\u4E86:', picked.id?.name)
191
+ }
192
+ }, Cesium.ScreenSpaceEventType.LEFT_CLICK)`
193
+ }
194
+ ];
195
+
196
+ // src/resources/entity-templates.ts
197
+ var ENTITY_TEMPLATES = {
198
+ point: {
199
+ defaultSize: 10,
200
+ generate: (p) => `viewer.entities.add({
201
+ name: '${p.name}',
202
+ position: Cesium.Cartesian3.fromDegrees(${p.lon}, ${p.lat}, ${p.height}),
203
+ point: {
204
+ pixelSize: ${p.size},
205
+ color: Cesium.Color.fromCssColorString('${p.color}'),
206
+ outlineColor: Cesium.Color.WHITE,
207
+ outlineWidth: 1,
208
+ },
209
+ })`
210
+ },
211
+ billboard: {
212
+ defaultSize: 32,
213
+ generate: (p) => `viewer.entities.add({
214
+ name: '${p.name}',
215
+ position: Cesium.Cartesian3.fromDegrees(${p.lon}, ${p.lat}, ${p.height}),
216
+ billboard: {
217
+ image: 'path/to/icon.png', // \u66FF\u6362\u4E3A\u5B9E\u9645\u56FE\u6807\u8DEF\u5F84
218
+ width: ${p.size},
219
+ height: ${p.size},
220
+ color: Cesium.Color.fromCssColorString('${p.color}'),
221
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
222
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
223
+ },
224
+ })`
225
+ },
226
+ label: {
227
+ defaultSize: 14,
228
+ generate: (p) => `viewer.entities.add({
229
+ name: '${p.name}',
230
+ position: Cesium.Cartesian3.fromDegrees(${p.lon}, ${p.lat}, ${p.height}),
231
+ label: {
232
+ text: '${p.name}',
233
+ font: '${p.size}px sans-serif',
234
+ fillColor: Cesium.Color.fromCssColorString('${p.color}'),
235
+ style: Cesium.LabelStyle.FILL_AND_OUTLINE,
236
+ outlineWidth: 2,
237
+ outlineColor: Cesium.Color.BLACK,
238
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
239
+ pixelOffset: new Cesium.Cartesian2(0, -10),
240
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
241
+ },
242
+ })`
243
+ },
244
+ polyline: {
245
+ defaultSize: 3,
246
+ generate: (p) => `viewer.entities.add({
247
+ name: '${p.name}',
248
+ polyline: {
249
+ positions: Cesium.Cartesian3.fromDegreesArray([
250
+ ${p.lon}, ${p.lat},
251
+ ${p.lon + 0.01}, ${p.lat + 0.01}, // \u66FF\u6362\u4E3A\u5B9E\u9645\u5750\u6807
252
+ ]),
253
+ width: ${p.size},
254
+ material: Cesium.Color.fromCssColorString('${p.color}'),
255
+ clampToGround: true,
256
+ },
257
+ })`
258
+ },
259
+ polygon: {
260
+ defaultSize: 0,
261
+ generate: (p) => `viewer.entities.add({
262
+ name: '${p.name}',
263
+ polygon: {
264
+ hierarchy: Cesium.Cartesian3.fromDegreesArray([
265
+ ${p.lon - 5e-3}, ${p.lat - 5e-3},
266
+ ${p.lon + 5e-3}, ${p.lat - 5e-3},
267
+ ${p.lon + 5e-3}, ${p.lat + 5e-3},
268
+ ${p.lon - 5e-3}, ${p.lat + 5e-3},
269
+ ]),
270
+ material: Cesium.Color.fromCssColorString('${p.color}').withAlpha(0.4),
271
+ outline: true,
272
+ outlineColor: Cesium.Color.fromCssColorString('${p.color}'),
273
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
274
+ },
275
+ })`
276
+ },
277
+ model: {
278
+ defaultSize: 1,
279
+ generate: (p) => `viewer.entities.add({
280
+ name: '${p.name}',
281
+ position: Cesium.Cartesian3.fromDegrees(${p.lon}, ${p.lat}, ${p.height}),
282
+ model: {
283
+ uri: 'path/to/model.glb', // \u66FF\u6362\u4E3A\u5B9E\u9645\u6A21\u578B\u8DEF\u5F84
284
+ scale: ${p.size},
285
+ minimumPixelSize: 64,
286
+ color: Cesium.Color.fromCssColorString('${p.color}'),
287
+ colorBlendMode: Cesium.ColorBlendMode.MIX,
288
+ colorBlendAmount: 0.5,
289
+ },
290
+ })`
291
+ },
292
+ ellipse: {
293
+ defaultSize: 500,
294
+ generate: (p) => `viewer.entities.add({
295
+ name: '${p.name}',
296
+ position: Cesium.Cartesian3.fromDegrees(${p.lon}, ${p.lat}, ${p.height}),
297
+ ellipse: {
298
+ semiMajorAxis: ${p.size},
299
+ semiMinorAxis: ${p.size * 0.7},
300
+ material: Cesium.Color.fromCssColorString('${p.color}').withAlpha(0.3),
301
+ outline: true,
302
+ outlineColor: Cesium.Color.fromCssColorString('${p.color}'),
303
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
304
+ },
305
+ })`
306
+ },
307
+ box: {
308
+ defaultSize: 100,
309
+ generate: (p) => `viewer.entities.add({
310
+ name: '${p.name}',
311
+ position: Cesium.Cartesian3.fromDegrees(${p.lon}, ${p.lat}, ${p.height + p.size / 2}),
312
+ box: {
313
+ dimensions: new Cesium.Cartesian3(${p.size}, ${p.size}, ${p.size}),
314
+ material: Cesium.Color.fromCssColorString('${p.color}').withAlpha(0.6),
315
+ outline: true,
316
+ outlineColor: Cesium.Color.fromCssColorString('${p.color}'),
317
+ },
318
+ })`
319
+ }
320
+ };
321
+
322
+ // src/resources/code-snippets.ts
323
+ var CODE_SNIPPETS = [
324
+ {
325
+ title: "\u98DE\u884C\u5230\u6307\u5B9A\u4F4D\u7F6E",
326
+ keywords: ["\u98DE\u5230", "\u98DE\u884C", "flyto", "\u89C6\u89D2", "\u5B9A\u4F4D"],
327
+ code: `viewer.camera.flyTo({
328
+ destination: Cesium.Cartesian3.fromDegrees(116.397, 39.908, 15000),
329
+ orientation: {
330
+ heading: Cesium.Math.toRadians(0),
331
+ pitch: Cesium.Math.toRadians(-45),
332
+ roll: 0,
333
+ },
334
+ duration: 2,
335
+ })`
336
+ },
337
+ {
338
+ title: "\u6DFB\u52A0\u70B9\u6807\u8BB0",
339
+ keywords: ["\u6807\u8BB0", "marker", "\u70B9", "point", "\u56FE\u9489", "pin"],
340
+ code: `const entity: Cesium.Entity = viewer.entities.add({
341
+ name: '\u6211\u7684\u6807\u8BB0',
342
+ position: Cesium.Cartesian3.fromDegrees(116.397, 39.908, 0),
343
+ point: {
344
+ pixelSize: 12,
345
+ color: Cesium.Color.RED,
346
+ outlineColor: Cesium.Color.WHITE,
347
+ outlineWidth: 2,
348
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
349
+ },
350
+ label: {
351
+ text: '\u5929\u5B89\u95E8',
352
+ font: '14px sans-serif',
353
+ pixelOffset: new Cesium.Cartesian2(0, -20),
354
+ fillColor: Cesium.Color.WHITE,
355
+ style: Cesium.LabelStyle.FILL_AND_OUTLINE,
356
+ outlineWidth: 2,
357
+ outlineColor: Cesium.Color.BLACK,
358
+ },
359
+ })`
360
+ },
361
+ {
362
+ title: "\u52A0\u8F7D GeoJSON \u6570\u636E",
363
+ keywords: ["geojson", "\u56FE\u5C42", "layer", "\u52A0\u8F7D", "load", "\u6570\u636E"],
364
+ code: `const dataSource: Cesium.GeoJsonDataSource = await Cesium.GeoJsonDataSource.load(
365
+ '/path/to/data.geojson',
366
+ {
367
+ stroke: Cesium.Color.fromCssColorString('#3388ff'),
368
+ fill: Cesium.Color.fromCssColorString('#3388ff').withAlpha(0.3),
369
+ strokeWidth: 2,
370
+ clampToGround: true,
371
+ }
372
+ )
373
+ viewer.dataSources.add(dataSource)
374
+ await viewer.flyTo(dataSource)`
375
+ },
376
+ {
377
+ title: "\u7ED8\u5236\u6298\u7EBF",
378
+ keywords: ["\u7EBF", "line", "polyline", "\u6298\u7EBF", "\u8DEF\u5F84", "\u8DEF\u7EBF"],
379
+ code: `viewer.entities.add({
380
+ name: '\u6211\u7684\u8DEF\u7EBF',
381
+ polyline: {
382
+ positions: Cesium.Cartesian3.fromDegreesArray([
383
+ 116.39, 39.91,
384
+ 116.40, 39.90,
385
+ 116.41, 39.92,
386
+ ]),
387
+ width: 3,
388
+ material: new Cesium.PolylineGlowMaterialProperty({
389
+ glowPower: 0.2,
390
+ color: Cesium.Color.CYAN,
391
+ }),
392
+ clampToGround: true,
393
+ },
394
+ })`
395
+ },
396
+ {
397
+ title: "\u7ED8\u5236\u591A\u8FB9\u5F62",
398
+ keywords: ["\u591A\u8FB9\u5F62", "polygon", "\u9762", "\u533A\u57DF", "\u8303\u56F4"],
399
+ code: `viewer.entities.add({
400
+ name: '\u6211\u7684\u533A\u57DF',
401
+ polygon: {
402
+ hierarchy: Cesium.Cartesian3.fromDegreesArray([
403
+ 116.38, 39.90,
404
+ 116.42, 39.90,
405
+ 116.42, 39.93,
406
+ 116.38, 39.93,
407
+ ]),
408
+ material: Cesium.Color.fromCssColorString('#ff6600').withAlpha(0.3),
409
+ outline: true,
410
+ outlineColor: Cesium.Color.fromCssColorString('#ff6600'),
411
+ outlineWidth: 2,
412
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
413
+ },
414
+ })`
415
+ },
416
+ {
417
+ title: "\u52A0\u8F7D 3D Tiles",
418
+ keywords: ["3d", "tiles", "3dtiles", "\u6A21\u578B", "\u5EFA\u7B51", "tileset"],
419
+ code: `const tileset: Cesium.Cesium3DTileset = await Cesium.Cesium3DTileset.fromUrl(
420
+ 'https://assets.cesium.com/your-asset-id/tileset.json',
421
+ {
422
+ maximumScreenSpaceError: 16,
423
+ maximumMemoryUsage: 512,
424
+ }
425
+ )
426
+ viewer.scene.primitives.add(tileset)
427
+
428
+ // \u53EF\u9009\uFF1A\u8BBE\u7F6E 3D Tiles \u6837\u5F0F
429
+ tileset.style = new Cesium.Cesium3DTileStyle({
430
+ color: "color('white')",
431
+ show: true,
432
+ })
433
+
434
+ await viewer.flyTo(tileset)`
435
+ },
436
+ {
437
+ title: "\u5207\u6362\u5E95\u56FE",
438
+ keywords: ["\u5E95\u56FE", "basemap", "\u5F71\u50CF", "\u536B\u661F", "imagery"],
439
+ code: `// \u79FB\u9664\u5F53\u524D\u5E95\u56FE
440
+ viewer.imageryLayers.removeAll()
441
+
442
+ // \u6DFB\u52A0\u65B0\u5E95\u56FE \u2014 OpenStreetMap
443
+ viewer.imageryLayers.addImageryProvider(
444
+ new Cesium.UrlTemplateImageryProvider({
445
+ url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
446
+ maximumLevel: 18,
447
+ credit: new Cesium.Credit('OpenStreetMap'),
448
+ })
449
+ )
450
+
451
+ // \u6216\u4F7F\u7528 Cesium Ion \u5E95\u56FE
452
+ // viewer.imageryLayers.addImageryProvider(
453
+ // await Cesium.IonImageryProvider.fromAssetId(3) // Bing Maps
454
+ // )`
455
+ },
456
+ {
457
+ title: "\u9F20\u6807\u70B9\u51FB\u62FE\u53D6",
458
+ keywords: ["\u70B9\u51FB", "click", "\u62FE\u53D6", "pick", "\u9009\u62E9", "\u4EA4\u4E92"],
459
+ code: `const handler: Cesium.ScreenSpaceEventHandler = new Cesium.ScreenSpaceEventHandler(viewer.canvas)
460
+
461
+ handler.setInputAction((movement: { position: Cesium.Cartesian2 }) => {
462
+ // \u62FE\u53D6 Entity
463
+ const picked = viewer.scene.pick(movement.position)
464
+ if (Cesium.defined(picked) && picked.id instanceof Cesium.Entity) {
465
+ console.log('\u9009\u4E2D:', picked.id.name)
466
+ viewer.selectedEntity = picked.id
467
+ }
468
+
469
+ // \u83B7\u53D6\u5730\u7406\u5750\u6807
470
+ const cartesian = viewer.camera.pickEllipsoid(movement.position)
471
+ if (cartesian) {
472
+ const carto = Cesium.Cartographic.fromCartesian(cartesian)
473
+ const lon = Cesium.Math.toDegrees(carto.longitude)
474
+ const lat = Cesium.Math.toDegrees(carto.latitude)
475
+ console.log(\`\u5750\u6807: \${lon.toFixed(6)}, \${lat.toFixed(6)}\`)
476
+ }
477
+ }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
478
+
479
+ // \u6E05\u7406: handler.destroy()`
480
+ },
481
+ {
482
+ title: "\u622A\u56FE\u5BFC\u51FA",
483
+ keywords: ["\u622A\u56FE", "screenshot", "\u5BFC\u51FA", "\u56FE\u7247", "canvas"],
484
+ code: `// \u65B9\u6CD5 1: \u4F7F\u7528 canvas toDataURL
485
+ viewer.render()
486
+ const dataUrl: string = viewer.canvas.toDataURL('image/png')
487
+
488
+ // \u65B9\u6CD5 2: \u4E0B\u8F7D
489
+ const link = document.createElement('a')
490
+ link.download = 'cesium-screenshot.png'
491
+ link.href = dataUrl
492
+ link.click()`
493
+ },
494
+ {
495
+ title: "\u70ED\u529B\u56FE\u6548\u679C",
496
+ keywords: ["\u70ED\u529B", "heatmap", "\u70ED\u529B\u56FE", "\u5BC6\u5EA6"],
497
+ code: `// \u4F7F\u7528 canvas \u751F\u6210\u70ED\u529B\u56FE\u7EB9\u7406\u53E0\u52A0
498
+ // \u9700\u8981 heatmap.js \u6216\u81EA\u5B9A\u4E49\u5B9E\u73B0
499
+
500
+ // \u7B80\u5355\u7684\u70B9\u5BC6\u5EA6\u53EF\u89C6\u5316\uFF08\u4F7F\u7528\u4E0D\u540C\u5927\u5C0F\u7684\u70B9\uFF09
501
+ const points = [
502
+ { lon: 116.39, lat: 39.91, weight: 0.8 },
503
+ { lon: 116.40, lat: 39.90, weight: 0.5 },
504
+ { lon: 116.41, lat: 39.92, weight: 1.0 },
505
+ ]
506
+
507
+ for (const p of points) {
508
+ viewer.entities.add({
509
+ position: Cesium.Cartesian3.fromDegrees(p.lon, p.lat),
510
+ point: {
511
+ pixelSize: 10 + p.weight * 30,
512
+ color: Cesium.Color.RED.withAlpha(0.3 + p.weight * 0.4),
513
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
514
+ },
515
+ })
516
+ }`
517
+ },
518
+ {
519
+ title: "\u65F6\u95F4\u52A8\u753B/\u8F68\u8FF9\u64AD\u653E",
520
+ keywords: ["\u52A8\u753B", "animation", "\u8F68\u8FF9", "trajectory", "\u65F6\u95F4", "clock", "\u64AD\u653E"],
521
+ code: `// \u8BBE\u7F6E\u65F6\u949F
522
+ const start = Cesium.JulianDate.fromIso8601('2024-01-01T00:00:00Z')
523
+ const stop = Cesium.JulianDate.addHours(start, 1, new Cesium.JulianDate())
524
+
525
+ viewer.clock.startTime = start.clone()
526
+ viewer.clock.stopTime = stop.clone()
527
+ viewer.clock.currentTime = start.clone()
528
+ viewer.clock.multiplier = 60 // 60x \u901F\u5EA6
529
+
530
+ // \u521B\u5EFA\u91C7\u6837\u4F4D\u7F6E
531
+ const positionProperty = new Cesium.SampledPositionProperty()
532
+ positionProperty.addSample(start,
533
+ Cesium.Cartesian3.fromDegrees(116.39, 39.90, 100))
534
+ positionProperty.addSample(stop,
535
+ Cesium.Cartesian3.fromDegrees(116.42, 39.93, 100))
536
+
537
+ // \u6DFB\u52A0\u8FD0\u52A8\u5B9E\u4F53
538
+ viewer.entities.add({
539
+ name: '\u79FB\u52A8\u76EE\u6807',
540
+ position: positionProperty,
541
+ point: { pixelSize: 8, color: Cesium.Color.YELLOW },
542
+ path: {
543
+ resolution: 1,
544
+ material: Cesium.Color.CYAN.withAlpha(0.5),
545
+ width: 2,
546
+ leadTime: 0,
547
+ trailTime: 3600,
548
+ },
549
+ })`
550
+ }
551
+ ];
552
+
553
+ // src/index.ts
554
+ var server = new McpServer({
555
+ name: "cesium-mcp-dev",
556
+ version: "0.1.0"
557
+ });
558
+ server.tool(
559
+ "cesium_api_lookup",
560
+ "\u67E5\u8BE2 Cesium API \u6587\u6863\u3002\u8F93\u5165\u7C7B\u540D\u6216\u5173\u952E\u8BCD\uFF0C\u8FD4\u56DE\u5BF9\u5E94\u7684 API \u63CF\u8FF0\u3001\u6784\u9020\u53C2\u6570\u3001\u5E38\u7528\u65B9\u6CD5\u548C\u793A\u4F8B\u4EE3\u7801\u3002",
561
+ {
562
+ query: z.string().describe('Cesium \u7C7B\u540D\u6216\u5173\u952E\u8BCD\uFF0C\u5982 "Viewer"\u3001"Entity"\u3001"Color"\u3001"flyTo"'),
563
+ category: z.enum(["class", "method", "property", "all"]).default("all").describe("\u67E5\u8BE2\u7C7B\u522B")
564
+ },
565
+ async ({ query, category }) => {
566
+ const q = query.toLowerCase();
567
+ const matches = CESIUM_API_DOCS.filter((doc) => {
568
+ const nameMatch = doc.name.toLowerCase().includes(q);
569
+ const descMatch = doc.description.toLowerCase().includes(q);
570
+ const catMatch = category === "all" || doc.category === category;
571
+ return (nameMatch || descMatch) && catMatch;
572
+ }).slice(0, 5);
573
+ if (matches.length === 0) {
574
+ return {
575
+ content: [{
576
+ type: "text",
577
+ text: `\u672A\u627E\u5230\u4E0E "${query}" \u5339\u914D\u7684 Cesium API\u3002
578
+
579
+ \u53EF\u7528\u7684\u70ED\u95E8\u7C7B\uFF1AViewer, Entity, Camera, Color, Cartesian3, GeoJsonDataSource, ImageryLayer, Terrain, Material, Clock`
580
+ }]
581
+ };
582
+ }
583
+ const text = matches.map(
584
+ (doc) => `## ${doc.name}
585
+ **\u7C7B\u522B**: ${doc.category}
586
+ **\u63CF\u8FF0**: ${doc.description}
587
+ ` + (doc.ctor ? `**\u6784\u9020\u51FD\u6570**: \`${doc.ctor}\`
588
+ ` : "") + (doc.properties?.length ? `**\u5E38\u7528\u5C5E\u6027**: ${doc.properties.join(", ")}
589
+ ` : "") + (doc.methods?.length ? `**\u5E38\u7528\u65B9\u6CD5**: ${doc.methods.join(", ")}
590
+ ` : "") + (doc.example ? `
591
+ \`\`\`typescript
592
+ ${doc.example}
593
+ \`\`\`
594
+ ` : "")
595
+ ).join("\n---\n\n");
596
+ return { content: [{ type: "text", text }] };
597
+ }
598
+ );
599
+ server.tool(
600
+ "cesium_code_gen",
601
+ "\u6839\u636E\u81EA\u7136\u8BED\u8A00\u63CF\u8FF0\u751F\u6210 Cesium \u4EE3\u7801\u7247\u6BB5\u3002\u652F\u6301\u5E38\u89C1\u573A\u666F\uFF1A\u89C6\u56FE\u63A7\u5236\u3001\u56FE\u5C42\u52A0\u8F7D\u3001Entity \u521B\u5EFA\u3001\u6837\u5F0F\u8BBE\u7F6E\u7B49\u3002",
602
+ {
603
+ description: z.string().describe('\u7528\u81EA\u7136\u8BED\u8A00\u63CF\u8FF0\u9700\u8981\u5B9E\u73B0\u7684\u529F\u80FD\uFF0C\u5982 "\u521B\u5EFA\u4E00\u4E2A\u7EA2\u8272\u7684\u70B9\u6807\u8BB0\u5728\u5929\u5B89\u95E8"'),
604
+ language: z.enum(["typescript", "javascript"]).default("typescript").describe("\u4EE3\u7801\u8BED\u8A00")
605
+ },
606
+ async ({ description, language }) => {
607
+ const desc = description.toLowerCase();
608
+ const matched = CODE_SNIPPETS.filter(
609
+ (s) => s.keywords.some((k) => desc.includes(k))
610
+ );
611
+ if (matched.length === 0) {
612
+ const fallback = `// Cesium \u4EE3\u7801\u751F\u6210
613
+ // \u63CF\u8FF0: ${description}
614
+ //
615
+ // \u63D0\u793A: \u5C1D\u8BD5\u4F7F\u7528\u66F4\u5177\u4F53\u7684\u5173\u952E\u8BCD\uFF0C\u5982:
616
+ // - "\u98DE\u5230" / "flyTo" \u2014 \u89C6\u56FE\u98DE\u884C
617
+ // - "\u6807\u8BB0" / "marker" / "\u70B9" \u2014 \u6DFB\u52A0\u6807\u8BB0
618
+ // - "GeoJSON" / "\u56FE\u5C42" \u2014 \u52A0\u8F7D\u6570\u636E
619
+ // - "\u70ED\u529B\u56FE" / "heatmap" \u2014 \u70ED\u529B\u53EF\u89C6\u5316
620
+ // - "\u5E95\u56FE" / "basemap" \u2014 \u5207\u6362\u5E95\u56FE
621
+ // - "3D Tiles" \u2014 \u52A0\u8F7D 3D \u6A21\u578B`;
622
+ return { content: [{ type: "text", text: fallback }] };
623
+ }
624
+ const text = matched.slice(0, 3).map((s) => {
625
+ let code = s.code;
626
+ if (language === "javascript") {
627
+ code = code.replace(/: Cesium\.\w+/g, "").replace(/: string/g, "").replace(/: number/g, "").replace(/: boolean/g, "").replace(/as \w+/g, "");
628
+ }
629
+ return `### ${s.title}
630
+
631
+ \`\`\`${language}
632
+ ${code}
633
+ \`\`\``;
634
+ }).join("\n\n");
635
+ return { content: [{ type: "text", text }] };
636
+ }
637
+ );
638
+ server.tool(
639
+ "cesium_entity_builder",
640
+ "\u4EA4\u4E92\u5F0F\u6784\u5EFA Cesium Entity \u914D\u7F6E\u3002\u9009\u62E9 Entity \u7C7B\u578B\uFF08point/billboard/label/polyline/polygon/model\uFF09\uFF0C\u8FD4\u56DE\u5B8C\u6574\u7684 Entity JSON \u914D\u7F6E\u548C\u521B\u5EFA\u4EE3\u7801\u3002",
641
+ {
642
+ type: z.enum(["point", "billboard", "label", "polyline", "polygon", "model", "ellipse", "box"]).describe("Entity \u56FE\u5F62\u7C7B\u578B"),
643
+ name: z.string().optional().describe("Entity \u540D\u79F0"),
644
+ position: z.object({
645
+ longitude: z.number(),
646
+ latitude: z.number(),
647
+ height: z.number().default(0)
648
+ }).optional().describe("\u4F4D\u7F6E\uFF08\u7ECF\u7EAC\u5EA6\uFF09"),
649
+ color: z.string().default("#FF0000").describe("\u989C\u8272\uFF08CSS \u683C\u5F0F\uFF09"),
650
+ size: z.number().optional().describe("\u5C3A\u5BF8\uFF08\u50CF\u7D20\u6216\u7C73\uFF09")
651
+ },
652
+ async ({ type, name, position, color, size }) => {
653
+ const template = ENTITY_TEMPLATES[type];
654
+ if (!template) {
655
+ return { content: [{ type: "text", text: `\u4E0D\u652F\u6301\u7684 Entity \u7C7B\u578B: ${type}` }] };
656
+ }
657
+ const entityName = name || `My ${type.charAt(0).toUpperCase() + type.slice(1)}`;
658
+ const lon = position?.longitude ?? 116.397;
659
+ const lat = position?.latitude ?? 39.908;
660
+ const h = position?.height ?? 0;
661
+ const entitySize = size ?? template.defaultSize;
662
+ const code = template.generate({ name: entityName, lon, lat, height: h, color, size: entitySize });
663
+ const text = `## Entity: ${entityName}
664
+ **\u7C7B\u578B**: ${type}
665
+ **\u4F4D\u7F6E**: ${lon}, ${lat}, ${h}m
666
+ **\u989C\u8272**: ${color}
667
+ **\u5C3A\u5BF8**: ${entitySize}
668
+
669
+ \`\`\`typescript
670
+ ${code}
671
+ \`\`\``;
672
+ return { content: [{ type: "text", text }] };
673
+ }
674
+ );
675
+ async function main() {
676
+ const transport = new StdioServerTransport();
677
+ await server.connect(transport);
678
+ console.error(`[cesium-mcp-dev] MCP Server running (stdio), 3 tools registered`);
679
+ }
680
+ main().catch((err) => {
681
+ console.error("[cesium-mcp-dev] Fatal:", err);
682
+ process.exit(1);
683
+ });
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "cesium-mcp-dev",
3
+ "version": "1.139.0",
4
+ "description": "Cesium MCP for Development — IDE AI 助手的 Cesium API 文档查询与代码生成",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "cesium-mcp-dev": "./dist/index.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "scripts": {
23
+ "dev": "tsx src/index.ts",
24
+ "build": "tsup"
25
+ },
26
+ "dependencies": {
27
+ "@modelcontextprotocol/sdk": "^1.0.0",
28
+ "zod": "^3.22.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^20.0.0",
32
+ "tsup": "^8.5.1",
33
+ "tsx": "^4.0.0",
34
+ "typescript": "^5.0.0"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/gaopengbin/cesium-mcp.git",
39
+ "directory": "packages/cesium-mcp-dev"
40
+ },
41
+ "homepage": "https://github.com/gaopengbin/cesium-mcp/tree/main/packages/cesium-mcp-dev",
42
+ "license": "MIT"
43
+ }