Skip to content

Commit bdb9d6f

Browse files
authored
Merge pull request #7 from mapbox/filter-tools
Filter tools
2 parents 9d0d66a + 67f4d68 commit bdb9d6f

File tree

6 files changed

+460
-29
lines changed

6 files changed

+460
-29
lines changed

TOOL_CONFIGURATION.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Tool Configuration Guide
2+
3+
The Mapbox MCP Devkit Server supports command-line configuration to enable or disable specific tools at startup.
4+
5+
## Command-Line Options
6+
7+
### --enable-tools
8+
9+
Enable only specific tools (exclusive mode). When this option is used, only the listed tools will be available.
10+
11+
```bash
12+
<command> --enable-tools list_styles_tool,create_style_tool
13+
```
14+
15+
### --disable-tools
16+
17+
Disable specific tools. All other tools will remain enabled.
18+
19+
```bash
20+
<command> --disable-tools delete_style_tool,update_style_tool
21+
```
22+
23+
## Available Tools
24+
25+
The following tools are available in the Mapbox MCP Devkit Server:
26+
27+
### Style Management Tools
28+
29+
- `list_styles_tool` - List all Mapbox styles
30+
- `create_style_tool` - Create a new Mapbox style
31+
- `retrieve_style_tool` - Retrieve details of a specific style
32+
- `update_style_tool` - Update an existing Mapbox style
33+
- `delete_style_tool` - Delete a Mapbox style
34+
- `preview_style_tool` - Generate a preview image of a Mapbox style
35+
36+
### Visualization Tools
37+
38+
- `geojson_preview_tool` - Generate a preview map with GeoJSON data overlay
39+
40+
### Token Management Tools
41+
42+
- `create_token_tool` - Create a new Mapbox access token
43+
- `list_tokens_tool` - List all Mapbox access tokens
44+
45+
### Geographic Tools
46+
47+
- `bounding_box_tool` - Calculate bounding box for given coordinates
48+
- `country_bounding_box_tool` - Get bounding box for a specific country
49+
- `coordinate_conversion_tool` - Convert between different coordinate formats
50+
51+
## Usage Examples
52+
53+
### Node.js
54+
55+
```bash
56+
node dist/index.js --enable-tools list_styles_tool,create_style_tool,preview_style_tool
57+
```
58+
59+
### NPX
60+
61+
```bash
62+
npx @mapbox/mcp-devkit-server --disable-tools delete_style_tool,update_style_tool
63+
```
64+
65+
### Docker
66+
67+
```bash
68+
docker run mapbox/mcp-devkit-server --enable-tools geojson_preview_tool,preview_style_tool,coordinate_conversion_tool
69+
```
70+
71+
### Claude Desktop App Configuration
72+
73+
In your Claude Desktop configuration file:
74+
75+
```json
76+
{
77+
"mcpServers": {
78+
"mapbox-devkit": {
79+
"command": "node",
80+
"args": [
81+
"/path/to/mcp-devkit-server/dist/index.js",
82+
"--enable-tools",
83+
"list_styles_tool,create_style_tool,preview_style_tool"
84+
],
85+
"env": {
86+
"MAPBOX_ACCESS_TOKEN": "your-mapbox-token-here"
87+
}
88+
}
89+
}
90+
}
91+
```
92+
93+
## Example Configurations
94+
95+
### Enable only read-only tools (safe mode)
96+
97+
```bash
98+
node dist/index.js --enable-tools list_styles_tool,retrieve_style_tool,list_tokens_tool,preview_style_tool
99+
```
100+
101+
### Enable only style management tools
102+
103+
```bash
104+
node dist/index.js --enable-tools list_styles_tool,create_style_tool,retrieve_style_tool,update_style_tool,delete_style_tool,preview_style_tool
105+
```
106+
107+
### Disable dangerous operations
108+
109+
```bash
110+
node dist/index.js --disable-tools delete_style_tool,create_token_tool
111+
```
112+
113+
## Notes
114+
115+
- If both `--enable-tools` and `--disable-tools` are provided, `--enable-tools` takes precedence
116+
- Tool names must match exactly (case-sensitive)
117+
- Multiple tools can be specified using comma separation
118+
- Invalid tool names are silently ignored
119+
- Arguments are passed after the main command, regardless of how the server is invoked
120+
- All tools require a valid Mapbox access token set in the `MAPBOX_ACCESS_TOKEN` environment variable

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"dxt_version": "0.1",
33
"name": "@mapbox/mcp-devkit-server",
44
"display_name": "Mapbox MCP DevKit Server",
5-
"version": "0.2.3",
5+
"version": "0.3.0",
66
"description": "Mapbox MCP devkit server",
77
"author": {
88
"name": "Mapbox, Inc."

src/config/toolConfig.test.ts

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
import {
2+
describe,
3+
it,
4+
expect,
5+
beforeEach,
6+
afterAll,
7+
jest
8+
} from '@jest/globals';
9+
import {
10+
parseToolConfigFromArgs,
11+
filterTools,
12+
ToolConfig
13+
} from './toolConfig.js';
14+
15+
// Mock getVersionInfo to avoid import.meta.url issues in Jest
16+
jest.mock('../utils/versionUtils.js', () => ({
17+
getVersionInfo: jest.fn(() => ({
18+
name: 'Mapbox MCP devkit server',
19+
version: '1.0.0',
20+
sha: 'mock-sha',
21+
tag: 'mock-tag',
22+
branch: 'mock-branch'
23+
}))
24+
}));
25+
26+
describe('Tool Configuration', () => {
27+
// Save original argv
28+
const originalArgv = process.argv;
29+
30+
beforeEach(() => {
31+
// Reset argv before each test
32+
process.argv = [...originalArgv];
33+
});
34+
35+
afterAll(() => {
36+
// Restore original argv
37+
process.argv = originalArgv;
38+
});
39+
40+
describe('parseToolConfigFromArgs', () => {
41+
it('should return empty config when no arguments provided', () => {
42+
process.argv = ['node', 'index.js'];
43+
const config = parseToolConfigFromArgs();
44+
expect(config).toEqual({});
45+
});
46+
47+
it('should parse --enable-tools with single tool', () => {
48+
process.argv = ['node', 'index.js', '--enable-tools', 'list_styles_tool'];
49+
const config = parseToolConfigFromArgs();
50+
expect(config).toEqual({
51+
enabledTools: ['list_styles_tool']
52+
});
53+
});
54+
55+
it('should parse --enable-tools with multiple tools', () => {
56+
process.argv = [
57+
'node',
58+
'index.js',
59+
'--enable-tools',
60+
'list_styles_tool,create_style_tool,preview_style_tool'
61+
];
62+
const config = parseToolConfigFromArgs();
63+
expect(config).toEqual({
64+
enabledTools: [
65+
'list_styles_tool',
66+
'create_style_tool',
67+
'preview_style_tool'
68+
]
69+
});
70+
});
71+
72+
it('should trim whitespace from tool names', () => {
73+
process.argv = [
74+
'node',
75+
'index.js',
76+
'--enable-tools',
77+
'list_styles_tool , create_style_tool , preview_style_tool'
78+
];
79+
const config = parseToolConfigFromArgs();
80+
expect(config).toEqual({
81+
enabledTools: [
82+
'list_styles_tool',
83+
'create_style_tool',
84+
'preview_style_tool'
85+
]
86+
});
87+
});
88+
89+
it('should parse --disable-tools with single tool', () => {
90+
process.argv = [
91+
'node',
92+
'index.js',
93+
'--disable-tools',
94+
'delete_style_tool'
95+
];
96+
const config = parseToolConfigFromArgs();
97+
expect(config).toEqual({
98+
disabledTools: ['delete_style_tool']
99+
});
100+
});
101+
102+
it('should parse --disable-tools with multiple tools', () => {
103+
process.argv = [
104+
'node',
105+
'index.js',
106+
'--disable-tools',
107+
'delete_style_tool,update_style_tool'
108+
];
109+
const config = parseToolConfigFromArgs();
110+
expect(config).toEqual({
111+
disabledTools: ['delete_style_tool', 'update_style_tool']
112+
});
113+
});
114+
115+
it('should parse both --enable-tools and --disable-tools', () => {
116+
process.argv = [
117+
'node',
118+
'index.js',
119+
'--enable-tools',
120+
'list_styles_tool',
121+
'--disable-tools',
122+
'delete_style_tool'
123+
];
124+
const config = parseToolConfigFromArgs();
125+
expect(config).toEqual({
126+
enabledTools: ['list_styles_tool'],
127+
disabledTools: ['delete_style_tool']
128+
});
129+
});
130+
131+
it('should handle missing value for --enable-tools', () => {
132+
process.argv = ['node', 'index.js', '--enable-tools'];
133+
const config = parseToolConfigFromArgs();
134+
expect(config).toEqual({});
135+
});
136+
137+
it('should handle missing value for --disable-tools', () => {
138+
process.argv = ['node', 'index.js', '--disable-tools'];
139+
const config = parseToolConfigFromArgs();
140+
expect(config).toEqual({});
141+
});
142+
143+
it('should ignore unknown arguments', () => {
144+
process.argv = [
145+
'node',
146+
'index.js',
147+
'--unknown-arg',
148+
'value',
149+
'--enable-tools',
150+
'list_styles_tool'
151+
];
152+
const config = parseToolConfigFromArgs();
153+
expect(config).toEqual({
154+
enabledTools: ['list_styles_tool']
155+
});
156+
});
157+
});
158+
159+
describe('filterTools', () => {
160+
// Mock tools for testing
161+
const mockTools = [
162+
{ name: 'list_styles_tool', description: 'List styles' },
163+
{ name: 'create_style_tool', description: 'Create style' },
164+
{ name: 'delete_style_tool', description: 'Delete style' },
165+
{ name: 'preview_style_tool', description: 'Preview style' }
166+
] as any;
167+
168+
it('should return all tools when no config provided', () => {
169+
const config: ToolConfig = {};
170+
const filtered = filterTools(mockTools, config);
171+
expect(filtered).toEqual(mockTools);
172+
});
173+
174+
it('should filter tools based on enabledTools', () => {
175+
const config: ToolConfig = {
176+
enabledTools: ['list_styles_tool', 'create_style_tool']
177+
};
178+
const filtered = filterTools(mockTools, config);
179+
expect(filtered).toHaveLength(2);
180+
expect(filtered.map((t) => t.name)).toEqual([
181+
'list_styles_tool',
182+
'create_style_tool'
183+
]);
184+
});
185+
186+
it('should filter tools based on disabledTools', () => {
187+
const config: ToolConfig = {
188+
disabledTools: ['delete_style_tool', 'preview_style_tool']
189+
};
190+
const filtered = filterTools(mockTools, config);
191+
expect(filtered).toHaveLength(2);
192+
expect(filtered.map((t) => t.name)).toEqual([
193+
'list_styles_tool',
194+
'create_style_tool'
195+
]);
196+
});
197+
198+
it('should prioritize enabledTools over disabledTools', () => {
199+
const config: ToolConfig = {
200+
enabledTools: ['list_styles_tool'],
201+
disabledTools: ['list_styles_tool', 'create_style_tool']
202+
};
203+
const filtered = filterTools(mockTools, config);
204+
expect(filtered).toHaveLength(1);
205+
expect(filtered.map((t) => t.name)).toEqual(['list_styles_tool']);
206+
});
207+
208+
it('should handle non-existent tool names gracefully', () => {
209+
const config: ToolConfig = {
210+
enabledTools: ['list_styles_tool', 'non_existent_tool']
211+
};
212+
const filtered = filterTools(mockTools, config);
213+
expect(filtered).toHaveLength(1);
214+
expect(filtered.map((t) => t.name)).toEqual(['list_styles_tool']);
215+
});
216+
217+
it('should return empty array when enabledTools is empty', () => {
218+
const config: ToolConfig = {
219+
enabledTools: []
220+
};
221+
const filtered = filterTools(mockTools, config);
222+
expect(filtered).toHaveLength(0);
223+
});
224+
225+
it('should return all tools when disabledTools is empty', () => {
226+
const config: ToolConfig = {
227+
disabledTools: []
228+
};
229+
const filtered = filterTools(mockTools, config);
230+
expect(filtered).toEqual(mockTools);
231+
});
232+
});
233+
});

0 commit comments

Comments
 (0)