---
title: Map MCP Protocol
source: https://vjmap.com/en/guide/aimcp.html
---

# Map MCP Protocol

Traditional CAD map applications are operated by humans. Users need to learn complex CAD operation commands and map management workflows, and the operation cost is relatively high. Wouldn't it be very convenient if AI could help us operate maps?

If all CAD map applications could be intelligently transformed so that AI can understand and operate map data, everyone would have a virtual "map intelligent assistant." We would only need to "chat" with the map intelligent assistant like chatting, express our map requirements in natural language, and the intelligent assistant would automatically operate the map application to fulfill our needs. This would greatly improve people's work efficiency and experience in map processing, CAD drawing analysis, spatial data management, and more!

`VJMap` has implemented the technology of AI replacing humans to operate CAD map applications based on the MCP protocol, and applied it to our VJMap engine, achieving intelligent transformation of the VJMap system. Using MCP protocol tools, they are naturally supported for AI recognition and control, including map browsing, CAD drawing queries, spatial data extraction, graphic drawing, and other operations. It also provides a frontend SDK that supports rapid AI integration for existing map businesses and intelligentization. It currently supports frontend frameworks such as Vue, React, and Angular.

Since it is implemented based on the standard MCP protocol, it has universality and broad applicability. It can control map applications through various types of MCP Hosts—for example, through AI dialog boxes on web pages to control map applications, or through IDE tools such as `Trae` and `Cursor`, or through intelligent agent platforms such as `Dify` and `Coze`, or even through mobile apps, WeChat mini-programs, and other methods to remotely control your map application. We can chat with AI and let AI help us operate various map applications to fulfill our map-related needs.

## Demo

![chatmcp.gif](https://vjmap.com/blogimages/chatmcp.gif)

[VJMap Cloud Management Platform](https://vjmap.com/app/cloud/#/) https://vjmap.com/app/cloud/#/

![image-20250803183709135](https://vjmap.com/blogimages/202508031837207.png)

![image-20250803180303071](https://vjmap.com/blogimages/202508031803174.png)

Click the MCP address in the upper-left corner of the interface to view the MCP address for this session.

![image-20250803175643751](https://vjmap.com/blogimages/202508031756861.png)

**You can copy the current session's `MCP address` to other `MCP clients` such as `cursor`, `trae`, or `cherry studio` to ask questions about the current map.**

Example of invoking map-related operations via MCP address in `cursor`:
![mcpcursor.gif](https://vjmap.com/blogimages/mcpcursor.gif)

## MCP Architecture Principles and Flow

The VJMap MCP system adopts a server-frontend separation architecture and implements bidirectional communication through the MCP (Model Context Protocol). The system supports mixed invocation of server-side tools and frontend tools to implement complex map operations and interactions.

### Overall Architecture

![image-20250803182907607](https://vjmap.com/blogimages/202508031829665.png)

## Backend MCP Tools

| Tool Name | Category | Type | Description | Main Parameters |
| ---------------------- | ------- | ---- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| **listmaps** | map | Backend | Get map list information, supports getting all maps or version information for specified maps | `mapid`, `version`, `mapIds`, `workspace`, `pagination`, `curPage`, `pageCount` |
| **createMap** | drawing | Backend | Create new map tool, supports two creation methods: 1. Create map via fileid and mapid; 2. Create via combine into new map API | `createType`, `mapid`, `fileid`, `uploadname`, `geom`, `sourceMapid`, `sourceVersion`, `sourceClipbounds`, `fourParameter`, `layers`, `workspace` |
| **deletemap** | delete | Backend | Delete map function, supports deleting specified version or all versions. Important: Delete operations are irreversible, use with caution! | `mapid`, `version`, `retainVersionMaxCount`, `workspace` |
| **getMapImage** | map | Backend | Get map image based on bounds and layer list, returns binary PNG format. Supports layer visibility, auto-calculates image aspect ratio | `mapid`, `version`, `bounds`, `pictureWidth`, `layerOn`, `layerOff`, `backgroundColor`, `transparent`, `styleName`, `workspace`, `returnBase64` |
| **fullSearch** | query | Backend | CAD drawing full-text search, supports searching text content, layers, block names, etc. | `query`, `map_ver`, `workspace`, `type`, `bounding_box`, `time_range`, `limit`, `offset`, `fields`, `facet` |
| **queryFeatures** | query | Backend | Query map entities, supports conditional query, rectangle bounds query, point query, and expression query | `mapid`, `version`, `queryType`, `sql`, `bounds`, `point`, `radius`, `expression`, `workspace` |
| **extractTable** | query | Backend | Automatically extract table data from CAD drawings. When querying drawing info, call this tool first to extract table data for analysis | `mapid`, `version`, `bounds`, `workspace` |
| **sqlDocHelper** | query | Backend | Get SQL writing documentation and table structure for querying map entities, provides reference for AI to generate SQL | No parameters |
| **createDwgDocHelper** | drawing | Backend | Get parameter description and entity type definition for creating drawings, provides reference for AI to generate graphic entity JSON arrays | No parameters |

## Frontend Tools

| Tool Name | Category | Type | Description | Main Parameters |
| ----------------------------- | ------- | ---- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| **map_get_info** | map | Frontend | Get current map details including zoom, center, bearing, pitch, bounds, layer info, workspace name, thumbnail URL. Coordinates use CAD system. | No parameters |
| **map_open_or_switch** | map | Frontend | Open or switch map based on mapid and version. Updates map context to backend after opening. | `mapid`, `version`, `isKeepOldLayers`, `isVectorStyle`, `isSetCenter`, `isFitBounds`, `layeron`, `layeroff`, `backcolor` |
| **map_view_control** | map | Frontend | Map view control - zoom, pan, rotate, pitch, zoom to bounds. All coordinates use CAD (x,y) | `operation`, `zoom`, `center`, `bearing`, `pitch`, `bounds`, `duration` |
| **map_execute_code** | runcode | Frontend | Execute custom JavaScript for complex map operations (requires VJMap help documentation MCP) | `code`, `description` |
| **map_draw_geojson** | draw | Frontend | Draw features from GeoJSON, supports all standard GeoJSON geometry types. Style in properties | `geojsonData` |
| **map_delete_drawn_features** | draw | Frontend | Delete drawn features, supports deleting specified instance or clearing all drawn layers | `instanceId`, `clearAll` |
| **map_create_markers** | draw | Frontend | Create one or more markers from GeoJSON, style from properties, supports custom style, drag, popup | `geojsonData` |
| **map_delete_markers** | draw | Frontend | Delete markers on map, supports deleting specified marker or clearing all | `markerId`, `clearAll` |

**Users can customize business-related MCP tools through the SDK.**

## MCP URL Composition

The VJMap MCP system URL supports tool filtering. You can control available tool categories and specific tools through URL parameters.

### URL Format

```
/ai/mcp?sessionId={sessionId}&include_categories={categories}&exclude_categories={categories}&include_tools={tools}&exclude_tools={tools}&token={token}
```

Official default MCP URL:

```
https://vjmap.com/server/ai/mcp?sessionId={sessionId}&include_categories=query,map,draw,custom&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MiwiVXNlcm5hbWUiOiJhZG1pbjEiLCJOaWNrTmFtZSI6ImFkbWluMSIsIkF1dGhvcml0eUlkIjoiYWRtaW4iLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjo0ODEzMjY3NjM3LCJpc3MiOiJ2am1hcCIsIm5iZiI6MTY1OTY2NjYzN30.cDXCH2ElTzU2sQU36SNHWoTYTAc4wEkVIXmBAIzWh6M
```

If only backend MCP tools are needed without frontend interaction, sessionId can be left empty:

```
https://vjmap.com/server/ai/mcp?include_categories=query,map&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MiwiVXNlcm5hbWUiOiJhZG1pbjEiLCJOaWNrTmFtZSI6ImFkbWluMSIsIkF1dGhvcml0eUlkIjoiYWRtaW4iLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjo0ODEzMjY3NjM3LCJpc3MiOiJ2am1hcCIsIm5iZiI6MTY1OTY2NjYzN30.cDXCH2ElTzU2sQU36SNHWoTYTAc4wEkVIXmBAIzWh6M
```

### Parameter Description

| Parameter Name | Type | Required | Description | Example |
| -------------------- | ------ | ---- | -------------------------------------- | ----------------------------------------- |
| `sessionId` | string | Yes | Session ID for current session | `session_123456` |
| `token` | string | Yes | Access token for authentication | `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...` |
| `include_categories` | string | No | Included tool categories, comma-separated | `query,map,drawing` |
| `exclude_categories` | string | No | Excluded tool categories, comma-separated | `delete,utility` |
| `include_tools` | string | No | Included tool names, comma-separated | `listmaps,fullSearch` |
| `exclude_tools` | string | No | Excluded tool names, comma-separated | `deletemap` |

### Tool Categories

| Category Name | Category Value | Description | Included Tools |
| -------------- | --------- | ------------------ | ------------------------------------------------------------ |
| Map Management | `map` | Basic map CRUD | `listmaps`, `getMapImage`, `map_get_info`, `map_open_or_switch`, `map_view_control` |
| Data Query | `query` | Data query and search | `fullSearch`, `queryFeatures`, `extractTable`, `sqlDocHelper` |
| Graphic Processing | `drawing` | Graphic creation and processing | `createMap`, `createDwgDocHelper`, `map_draw_geojson`, `map_create_markers` |
| Delete Tools | `delete` | Map deletion | `deletemap` |
| Drawing Tools | `draw` | Frontend drawing | `map_draw_geojson`, `map_delete_drawn_features`, `map_create_markers`, `map_delete_markers` |
| Code Execution | `runcode` | Code execution | `map_execute_code` |
| Frontend Custom | `custom` | Frontend custom category | |

### Usage Examples

#### Example 1: Include only query and map tools
```
/ai/mcp?sessionId=session_123456&include_categories=query,map&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**Note:** Only enables query tools and map tools.

#### Example 2: Exclude delete tools
```
/ai/mcp?sessionId=session_123456&exclude_categories=delete&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**Note:** Enables all tools except delete, ensuring users cannot perform delete operations.

#### Example 3: Include only specific tools
```
/ai/mcp?sessionId=session_123456&include_tools=listmaps,fullSearch,map_get_info&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**Note:** Only enables `listmaps`, `fullSearch`, `map_get_info`.

#### Example 4: Exclude specific tools
```
/ai/mcp?sessionId=session_123456&exclude_tools=deletemap,map_execute_code&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**Note:** Excludes `deletemap` and `map_execute_code` which may have security risks.

#### Example 5: Combined include and exclude
```
/ai/mcp?sessionId=session_123456&include_categories=query,map&exclude_tools=deletemap&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

### Notes

1. **Priority:** `include_tools` and `exclude_tools` have higher priority than `include_categories` and `exclude_categories`
2. **Conflicts:** If a tool is in both `include_tools` and `exclude_tools`, `exclude_tools` takes precedence
3. **Empty values:** If a parameter is empty or not provided, system uses default (enable all tools)
4. **Security:** Use `exclude_categories=delete` or `exclude_tools=deletemap` in production to prevent accidental deletes
5. **URL encoding:** Encode special characters in parameter values

Effect when adding MCP address in `cursor` for Q&A:
![image-20250803191926787](https://vjmap.com/blogimages/202508031919853.png)

```json
{
  "mcpServers": {
    "vjmap": {
      "url": "https://vjmap.com/server/ai/mcp?include_categories=query,map&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MiwiVXNlcm5hbWUiOiJhZG1pbjEiLCJOaWNrTmFtZSI6ImFkbWluMSIsIkF1dGhvcml0eUlkIjoiYWRtaW4iLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjo0ODEzMjY3NjM3LCJpc3MiOiJ2am1hcCIsIm5iZiI6MTY1OTY2NjYzN30.cDXCH2ElTzU2sQU36SNHWoTYTAc4wEkVIXmBAIzWh6M"
    }
  }
}
```

When frontend interaction is needed, copy the MCP address from the MCP tools in the AI dialog.

![image-20250803192608314](https://vjmap.com/blogimages/202508031926432.png)

```json
{
  "mcpServers": {
    "vjmap": {
      "url": "https://vjmap.com/server/ai/mcp?sessionId=session-6323558e&include_categories=query,map,draw,custom&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MSwiVXNlcm5hbWUiOiJyb290MSIsIk5pY2tOYW1lIjoicm9vdDEiLCJBdXRob3JpdHlJZCI6InJvb3QiLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjoxOTQyMzg5NTc0LCJpc3MiOiJ2am1hcCIsIm5iZiI6MTYyNzAyODU3NH0.l1pP9FXu6ARDaaa-6ma0lp7ftbIk2t6rgmSmTqXry10"
    }
  }
}
```
Note: sessionId is different for each session; generate the address based on the current session.

## MCP Frontend SDK and Custom Tool Example

```html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>vjmap demo</title>
    <link rel="stylesheet" type="text/css" href="https://vjmap.com/server/_demo/js/vjmap/vjmap.min.css">
    <script type="text/javascript" src="https://vjmap.com/server/_demo/js/vjmap/vjmap.min.js"></script>
</head>

<body style=" margin: 0;overflow: hidden;background-color:white;font-size: 16px">
    <div id="map" style="left:0;right:0;top:0;bottom:0;position: absolute;z-index: 0;"></div>
</body>
<script>
    (async () => {
        document.body.style.background = "#022B4F"; // dark background
        const env = {
            serviceUrl: "https://vjmap.com/server/api/v1",
            accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MiwiVXNlcm5hbWUiOiJhZG1pbjEiLCJOaWNrTmFtZSI6ImFkbWluMSIsIkF1dGhvcml0eUlkIjoiYWRtaW4iLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjo0ODEzMjY3NjM3LCJpc3MiOiJ2am1hcCIsIm5iZiI6MTY1OTY2NjYzN30.cDXCH2ElTzU2sQU36SNHWoTYTAc4wEkVIXmBAIzWh6M",
            exampleMapId: "sys_zp"
        };
        let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
        // Open map
        let res = await svc.openMap({
            mapid: env.exampleMapId, // map ID (ensure it exists or upload to create)
            mapopenway: vjmap.MapOpenWay.GeomRender, // geometry render
            style: vjmap.openMapDarkStyle() // dark style for dark container
        })
        if (res.error) {
            message.error(res.error)
        }
        // Map extent
        let mapExtent = vjmap.GeoBounds.fromString(res.bounds);
        // Create projection
        let prj = new vjmap.GeoProjection(mapExtent);

        // Create map
        let map = new vjmap.Map({
            container: 'map', // container ID
            style: svc.rasterStyle(), // raster tile style
            center: prj.toLngLat(mapExtent.center()), // center
            zoom: 2,
            renderWorldCopies: false
        });
        // Attach service and projection
        map.attach(svc, prj);
        // Fit map bounds
        map.fitMapBounds();
        await map.onLoad(); // wait for map load



        let enableAiMcpChat = true;
        if (enableAiMcpChat) {
            if (typeof VjmapMcpSdk !== "object") {
                let svc = map.getService();
                const _url = svc.baseUrl() + "version";
                // @ts-ignore
                let res = await svc._get(_url, {});
                let version = '';
                if (res && "data" in res) {
                    let data = res["data"];
                    version = data.version
                }
                // Bypass cache when version changes
                // vjchat.umd.js does not bundle vjmap; expose globally
                window.vjmap = vjmap;
                // Load scripts if not present
                await vjmap.addScript([{
                    src: "https://vjmap.com/server/_cloud/lib/vjmap-mcp-sdk.umd.js" + `?ver=${version}`
                }])
                await vjmap.addScript([{
                    src: "https://vjmap.com/server/_cloud/lib/ai-chat-lib.umd.js" + `?ver=${version}`
                }])

                let apiUrl = map.getService().baseUrl();
                let token = map.getService().accessToken;

                // Initialize MCP connection
                console.log('Initializing MCP...')
                let mcpInstance = await VjmapMcpSdk.initializeMCP({
                    apiBase: apiUrl,
                    autoConnect: true,
                    mapInstance: map
                })

                const sessionId = mcpInstance.state.sessionId
                // Register map tools
                await mcpInstance.registerMapTools()

                // Register custom tool example
                await registerCustomTool(mcpInstance, VjmapMcpSdk.z)

                chatContainer = document.createElement('div')
                chatContainer.id = 'ai-chat-container'
                chatContainer.style.cssText = `
                            position: absolute;
                            top: 0;
                            left: 0;
                            width: 100%;
                            height: 100%;
                            pointer-events: none;
                            z-index: 1000;
                        `

                // Allow chat container children to receive events
                const style = document.createElement('style')
                style.textContent = `
                            #ai-chat-container > * {
                            pointer-events: auto !important;
                            }
                        `
                if (!document.head.querySelector('style[data-ai-chat]')) {
                    style.setAttribute('data-ai-chat', 'true')
                    document.head.appendChild(style)
                }
                document.body.appendChild(chatContainer)
                let chatLib = AiChatLib.createAiChatLib({
                    container: chatContainer,
                    apiUrl: apiUrl,
                    token: token,
                    sessionId: sessionId,
                    mcpAddresses: [
                        '{apiUrl}/ai/mcp?sessionId={sessionId}&include_categories=query,map,draw,custom&token={token}'
                    ],
                    systemPrompt: '',
                    quickQuestions: [
                        'Describe what is drawn in the current map',
                        'Locate a table in the current map',
                        "Find text containing 'map' and add point markers at those positions"
                    ],
                    maxHistoryCount: 10,
                    window: {
                        width: 500,
                        height: 780,
                        right: 3,
                        theme: 'dark',
                        draggable: true,
                        title: 'VJMap AI Q&A'
                    },
                    debug: true,
                    autoFocus: true
                })
            }
        }
    })();

    // flashPos implementation
    const flashPos = (bounds) => {
        map.fitMapBounds(vjmap.GeoBounds.fromArray(bounds), { padding: 300 })
        return new Promise((resolve) => {
            const routePath = vjmap.GeoBounds.fromArray(bounds).toPointArray();
            routePath.push(routePath[0])
            let geoLineDatas = [];
            geoLineDatas.push({
                points: map.toLngLat(routePath),
                properties: {
                    opacity: 1.0
                }
            })

            let polylines = new vjmap.Polyline({
                data: geoLineDatas,
                lineColor: 'yellow',
                lineWidth: 3,
                lineOpacity: ['get', 'opacity'],
                isHoverPointer: false,
                isHoverFeatureState: false
            });
            polylines.addTo(map);

            vjmap.createAnimation({
                from: 1,
                to: 10,
                duration: 1000,
                onUpdate: (e) => {
                    const data = polylines.getData();
                    if (data && data.features && data.features[0]) {
                        data.features[0].properties.opacity = parseInt(e.toString()) % 2 ? 1.0 : 0;
                        polylines.setData(data);
                    }
                },
                onStop: () => {
                    polylines.remove()
                    resolve({})
                },
                onComplete: () => {
                    polylines.remove()
                    resolve({})
                }
            })
        })
    }

    // Register custom tool example
    async function registerCustomTool(mcpInstance, z) {
        if (!mcpInstance) return

        // Define tool input schema with zod
        const inputSchema = z.object({
            bounds: z.array(z.number()).length(4).describe('Bounds array [minX, minY, maxX, maxY]')
        })

        // Create tool definition
        const tool = VjmapMcpSdk.createTool(
            'map_flash_position',
            'Flash the given bounds on the map',
            inputSchema,
            'custom'
        )

        // Tool handler
        const handler = async (args) => {
            try {
                const validatedArgs = inputSchema.parse(args)
                const { bounds } = validatedArgs

                await flashPos(bounds)

                return {
                    content: [{
                        type: 'text',
                        text: `Flashed bounds on map: [${bounds.join(', ')}]`
                    }]
                }
            } catch (error) {
                return {
                    content: [{
                        type: 'text',
                        text: `Tool failed: ${error instanceof Error ? error.message : String(error)}`
                    }],
                    isError: true
                }
            }
        }

        mcpInstance.registerTool(tool, handler, "custom")

        // Register tool on server so backend can see it
        try {
            const serviceManager = mcpInstance.getServiceManager()
            if (serviceManager) {
                await serviceManager.registerToolsToServer()
                console.log('Custom tool registered on server')
            }
        } catch (error) {
            console.error('Failed to register custom tool on server:', error)
        }
    }


</script>

</html>
```

In the above code, `ai-chat-lib.umd.js` is the AI chat UI library.

In the above code, `vjmap-mcp-sdk.umd.js` is the vjmap MCP SDK library. It is mainly used for server communication and provides interfaces for users to customize tools. This library can also be installed via `npm`. Usage is as follows:

### VJMap MCP SDK Installation

```bash
npm install vjmcpsdk
```

### VJMap MCP SDK Usage

```js
import vjmap from vjmap
import { initializeMCP, createTool, z } from vjmcpsdk

let apiUrl = map.getService().baseUrl();
let token = map.getService().accessToken;

let mcpInstance = await initializeMCP({
    apiBase: apiUrl,
    autoConnect: true,
    mapInstance: map
})

const sessionId = mcpInstance.state.sessionId
await mcpInstance.registerMapTools()
await registerCustomTool(mcpInstance, z)

// flashPos function implementation
const flashPos = (bounds) => {
    map.fitMapBounds(vjmap.GeoBounds.fromArray(bounds), { padding: 300 })
    return new Promise((resolve) => {
        const routePath = vjmap.GeoBounds.fromArray(bounds).toPointArray();
        routePath.push(routePath[0])
        let geoLineDatas = [];
        geoLineDatas.push({
            points: map.toLngLat(routePath),
            properties: { opacity: 1.0 }
        })
        let polylines = new vjmap.Polyline({
            data: geoLineDatas,
            lineColor: 'yellow',
            lineWidth: 3,
            lineOpacity: ['get', 'opacity'],
            isHoverPointer: false,
            isHoverFeatureState: false
        });
        polylines.addTo(map);
        vjmap.createAnimation({
            from: 1,
            to: 10,
            duration: 1000,
            onUpdate: (e) => {
                const data = polylines.getData();
                if (data && data.features && data.features[0]) {
                    data.features[0].properties.opacity = parseInt(e.toString()) % 2 ? 1.0 : 0;
                    polylines.setData(data);
                }
            },
            onStop: () => { polylines.remove(); resolve({}) },
            onComplete: () => { polylines.remove(); resolve({}) }
        })
    })
}

async function registerCustomTool(mcpInstance, z) {
    if (!mcpInstance) return
    const inputSchema = z.object({
        bounds: z.array(z.number()).length(4).describe('Bounds array [minX, minY, maxX, maxY]')
    })
    const tool = createTool(
        'map_flash_position',
        'Flash the given bounds on the map',
        inputSchema,
        'custom'
    )
    const handler = async (args) => {
        try {
            const validatedArgs = inputSchema.parse(args)
            const { bounds } = validatedArgs
            await flashPos(bounds)
            return {
                content: [{ type: 'text', text: `Flashed bounds on map: [${bounds.join(', ')}]` }]
            }
        } catch (error) {
            return {
                content: [{ type: 'text', text: `Tool failed: ${error instanceof Error ? error.message : String(error)}` }],
                isError: true
            }
        }
    }
    mcpInstance.registerTool(tool, handler, "custom")
    try {
        const serviceManager = mcpInstance.getServiceManager()
        if (serviceManager) {
            await serviceManager.registerToolsToServer()
            console.log('Custom tool registered on server')
        }
    } catch (error) {
        console.error('Failed to register custom tool on server:', error)
    }
}
```

## Backend Configuration

Configure in `data/ai/aisvr_config.yaml` in the backend data directory.

![image-20250803190902021](https://vjmap.com/blogimages/202508031909069.png)
