Skip to content

Commit 0ba8923

Browse files
committed
add doc tool
1 parent 8fd7aa2 commit 0ba8923

5 files changed

Lines changed: 169 additions & 0 deletions

File tree

src/tools/__snapshots__/tool-naming-convention.test.ts.snap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ exports[`Tool Naming Convention should maintain consistent tool list (snapshot t
3737
"description": "Generate a geojson.io URL to visualize GeoJSON data. Returns only the URL link.",
3838
"toolName": "geojson_preview_tool",
3939
},
40+
{
41+
"className": "GetMapboxDocSourceTool",
42+
"description": "Fetch and return Mapbox documentation source collection content from llms.txt",
43+
"toolName": "get_mapbox_doc_source_tool",
44+
},
4045
{
4146
"className": "ListStylesTool",
4247
"description": "List styles for a Mapbox account. Use limit parameter to avoid large responses (recommended: limit=5-10). Use start parameter for pagination.",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { z } from 'zod';
2+
3+
export const GetMapboxDocSourceSchema = z.object({});
4+
5+
export type GetMapboxDocSourceInput = z.infer<typeof GetMapboxDocSourceSchema>;
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { GetMapboxDocSourceTool } from './GetMapboxDocSourceTool.js';
2+
3+
// Mock fetch for testing
4+
global.fetch = jest.fn();
5+
6+
describe('GetMapboxDocSourceTool', () => {
7+
let tool: GetMapboxDocSourceTool;
8+
const mockFetch = global.fetch as jest.MockedFunction<typeof fetch>;
9+
10+
beforeEach(() => {
11+
tool = new GetMapboxDocSourceTool();
12+
mockFetch.mockClear();
13+
});
14+
15+
it('should have correct name and description', () => {
16+
expect(tool.name).toBe('get_mapbox_doc_source_tool');
17+
expect(tool.description).toContain('Fetch and return Mapbox documentation');
18+
});
19+
20+
it('should successfully fetch documentation content', async () => {
21+
const mockContent = `# Mapbox Documentation
22+
23+
This is the Mapbox developer documentation for LLMs.
24+
25+
## Web SDKs
26+
- Mapbox GL JS for interactive maps
27+
- Mobile SDKs for iOS and Android
28+
29+
## APIs
30+
- Geocoding API for address search
31+
- Directions API for routing`;
32+
33+
mockFetch.mockResolvedValueOnce({
34+
ok: true,
35+
status: 200,
36+
text: () => Promise.resolve(mockContent)
37+
} as Response);
38+
39+
const result = await tool.run({});
40+
41+
expect(mockFetch).toHaveBeenCalledWith('https://docs.mapbox.com/llms.txt');
42+
expect(result.content).toHaveLength(1);
43+
expect(result.content[0].type).toBe('text');
44+
45+
if (result.content[0].type === 'text') {
46+
expect(result.content[0].text).toBe(mockContent);
47+
}
48+
expect(result.isError).toBe(false);
49+
});
50+
51+
it('should handle HTTP errors', async () => {
52+
mockFetch.mockResolvedValueOnce({
53+
ok: false,
54+
status: 404
55+
} as Response);
56+
57+
const result = await tool.run({});
58+
59+
expect(result.isError).toBe(true);
60+
expect(result.content[0].type).toBe('text');
61+
62+
if (result.content[0].type === 'text') {
63+
expect(result.content[0].text).toContain(
64+
'Failed to fetch Mapbox documentation'
65+
);
66+
expect(result.content[0].text).toContain('HTTP error! status: 404');
67+
}
68+
});
69+
70+
it('should handle network errors', async () => {
71+
mockFetch.mockRejectedValueOnce(new Error('Network error'));
72+
73+
const result = await tool.run({});
74+
75+
expect(result.isError).toBe(true);
76+
expect(result.content[0].type).toBe('text');
77+
78+
if (result.content[0].type === 'text') {
79+
expect(result.content[0].text).toContain(
80+
'Failed to fetch Mapbox documentation'
81+
);
82+
expect(result.content[0].text).toContain('Network error');
83+
}
84+
});
85+
86+
it('should handle unknown errors', async () => {
87+
mockFetch.mockRejectedValueOnce('Unknown error');
88+
89+
const result = await tool.run({});
90+
91+
expect(result.isError).toBe(true);
92+
expect(result.content[0].type).toBe('text');
93+
94+
if (result.content[0].type === 'text') {
95+
expect(result.content[0].text).toContain(
96+
'Failed to fetch Mapbox documentation'
97+
);
98+
expect(result.content[0].text).toContain('Unknown error occurred');
99+
}
100+
});
101+
102+
it('should work with empty input object', async () => {
103+
const mockContent = 'Test documentation content';
104+
105+
mockFetch.mockResolvedValueOnce({
106+
ok: true,
107+
status: 200,
108+
text: () => Promise.resolve(mockContent)
109+
} as Response);
110+
111+
const result = await tool.run({});
112+
113+
expect(result.isError).toBe(false);
114+
expect(result.content).toHaveLength(1);
115+
expect(result.content[0].type).toBe('text');
116+
});
117+
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { BaseTool } from '../BaseTool.js';
2+
import {
3+
GetMapboxDocSourceSchema,
4+
GetMapboxDocSourceInput
5+
} from './GetMapboxDocSourceTool.schema.js';
6+
7+
export class GetMapboxDocSourceTool extends BaseTool<
8+
typeof GetMapboxDocSourceSchema
9+
> {
10+
name = 'get_mapbox_doc_source_tool';
11+
description =
12+
'Fetch and return Mapbox documentation source collection content from llms.txt';
13+
14+
constructor() {
15+
super({ inputSchema: GetMapboxDocSourceSchema });
16+
}
17+
18+
protected async execute(
19+
_input: GetMapboxDocSourceInput
20+
): Promise<{ type: 'text'; text: string }> {
21+
try {
22+
const response = await fetch('https://docs.mapbox.com/llms.txt');
23+
24+
if (!response.ok) {
25+
throw new Error(`HTTP error! status: ${response.status}`);
26+
}
27+
28+
const content = await response.text();
29+
30+
return {
31+
type: 'text',
32+
text: content
33+
};
34+
} catch (error) {
35+
const errorMessage =
36+
error instanceof Error ? error.message : 'Unknown error occurred';
37+
throw new Error(`Failed to fetch Mapbox documentation: ${errorMessage}`);
38+
}
39+
}
40+
}

src/tools/toolRegistry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { CreateStyleTool } from './create-style-tool/CreateStyleTool.js';
55
import { CreateTokenTool } from './create-token-tool/CreateTokenTool.js';
66
import { DeleteStyleTool } from './delete-style-tool/DeleteStyleTool.js';
77
import { GeojsonPreviewTool } from './geojson-preview-tool/GeojsonPreviewTool.js';
8+
import { GetMapboxDocSourceTool } from './get-mapbox-doc-source-tool/GetMapboxDocSourceTool.js';
89
import { ListStylesTool } from './list-styles-tool/ListStylesTool.js';
910
import { ListTokensTool } from './list-tokens-tool/ListTokensTool.js';
1011
import { PreviewStyleTool } from './preview-style-tool/PreviewStyleTool.js';
@@ -29,6 +30,7 @@ export const ALL_TOOLS = [
2930
new BoundingBoxTool(),
3031
new CountryBoundingBoxTool(),
3132
new CoordinateConversionTool(),
33+
new GetMapboxDocSourceTool(),
3234
new StyleComparisonTool(),
3335
new TilequeryTool()
3436
] as const;

0 commit comments

Comments
 (0)