#
tokens: 8182/50000 9/9 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── juce-docs-mcp-server
│   ├── .gitignore
│   ├── package-lock.json
│   ├── package.json
│   ├── README-DEV.md
│   ├── README.md
│   ├── src
│   │   ├── index.ts
│   │   ├── juce-docs.ts
│   │   └── test-client.ts
│   └── tsconfig.json
├── LICENSE
└── README.md
```

# Files

--------------------------------------------------------------------------------
/juce-docs-mcp-server/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Dependencies
 2 | node_modules/
 3 | npm-debug.log*
 4 | yarn-debug.log*
 5 | yarn-error.log*
 6 | 
 7 | # Build outputs
 8 | dist/
 9 | build/
10 | *.js.map
11 | 
12 | # TypeScript cache
13 | *.tsbuildinfo
14 | 
15 | # IDE and editor files
16 | .vscode/
17 | .idea/
18 | *.swp
19 | *.swo
20 | .DS_Store
21 | 
22 | # Environment variables
23 | .env
24 | .env.local
25 | .env.*.local
26 | 
27 | # Logs
28 | logs/
29 | *.log 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # mcp-servers-jos
 2 | 
 3 | [Model Context
 4 | Protocol](https://modelcontextprotocol.io/tutorials/building-mcp-with-llms)
 5 | (MCP) servers by [JOS](https://github.com/josmithiii), starting from
 6 | an [MCP Template](https://github.com/josmithiii/mcp-template)
 7 | 
 8 | ## Contents
 9 | 
10 | * [juce-docs-mcp-server](./juce-docs-mcp-server/README.md) - access JUCE Framework C++ class documentation
11 | 
12 | 
```

--------------------------------------------------------------------------------
/juce-docs-mcp-server/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # JUCE Documentation MCP Server
  2 | 
  3 | An MCP (Model Context Protocol) server that provides access to JUCE Framework C++ class documentation.
  4 | 
  5 | ## Features
  6 | 
  7 | - Fetch documentation for specific JUCE classes
  8 | - List all available JUCE classes
  9 | - Search for classes by name
 10 | - Format documentation as markdown
 11 | - Expose documentation through MCP resources and tools
 12 | 
 13 | ## Installation
 14 | 
 15 | ```bash
 16 | # Clone the repository
 17 | git clone https://github.com/josmithiii/mcp-servers-jos.git
 18 | cd mcp-servers-jos/juce-docs-mcp-server
 19 | 
 20 | # Install dependencies
 21 | npm install
 22 | 
 23 | # Build the project
 24 | npm run build
 25 | ```
 26 | 
 27 | ## Usage
 28 | 
 29 | ### Running the Server
 30 | 
 31 | ```bash
 32 | npm start
 33 | ```
 34 | 
 35 | This starts the MCP server using `stdio` as the transport mechanism, which allows it to be used with MCP clients like Claude Desktop App, Continue, or other MCP-compatible applications.
 36 | 
 37 | ### Adding the MCP service to Cursor (tested 2025-03-11)
 38 | 
 39 | 1. Open Cursor / Settings / Cursor Settings
 40 | 2. Select MCP
 41 | 3. Set the `Name` to JUCE Docs (or whatever), and set the `Type` to `Command`
 42 | 3. Set the `Command` to `node /path/to/juce-docs-mcp-server/dist/index.js`,
 43 |    replacing `/path/to/juce-docs-mcp-server` with the actual path into your clone
 44 | 5. Restart Cursor to apply the changes (it will internally run `node .../dist/index.js`)
 45 | 
 46 | Note that Cursor sends MCP requests to _your local server_ that you started with `npm start` above.
 47 | 
 48 | ### Available Resources
 49 | 
 50 | - `juce://class/{className}` - Get documentation for a specific JUCE class
 51 | - `juce://classes` - List all available JUCE classes
 52 | 
 53 | ### Available Tools
 54 | 
 55 | - `/search-juce-classes` - Search for JUCE classes by name
 56 | - `/get-juce-class-docs` - Get documentation for a specific JUCE class
 57 | 
 58 | ### Available Prompts
 59 | 
 60 | - `explore-juce` - Interactive exploration of JUCE framework components
 61 |   - Use without arguments for an overview of main components
 62 |   - Add a topic to explore specific functionality (e.g., `explore-juce audio`)
 63 | 
 64 | ### Resources and Tools
 65 | 
 66 | In addition to prompts that direct your LLM (such as in Cursor) to use
 67 | the MCP internally, you can also query it directly via "resource" and
 68 | "tool" names:
 69 | 
 70 | 1. **Resources** look like URLs that directly fetch specific content. They
 71 |    follow a URI-like pattern with the format `protocol://path`. These
 72 |    are defined in the server as direct resource endpoints.  Example:
 73 |    `juce://classes`
 74 | 
 75 | 2. **Tools** use names beginning with `/` and support a following
 76 |    argument, i.e., `/tool-name arg-string`, and provide interactive
 77 |    commands that perform an action. MCP tools start with `/` to
 78 |    distinguish them from resources. This is similar to how slash
 79 |    commands work in many applications such as `Claude Code` or
 80 |    `aider`.  Note that in an IDE chat, the `arg-string` can include
 81 |    spaces and is terminated by end-of-line (according to Claude 3.7).
 82 | 
 83 | In summary, when connected to an MCP client (such as via Cursor chat),
 84 | you can access "resources" in the format `protocol://path` and "tools"
 85 | in the format `/tool-name arg string`.
 86 | 
 87 | ## Examples
 88 | 
 89 | 1. List all available classes: `juce://classes`
 90 | 2. Get documentation for a specific class: `juce://class/ValueTree`
 91 | 3. Search for all Audio classes: `/search-juce-classes Audio`
 92 | 4. Get documentation for specific classes: `/get-juce-class-docs AudioProcessor`
 93 | 
 94 | ## Changing the JUCE Doc URL
 95 | 
 96 | In `juce-docs-mcp-server/src/juce-docs.ts`, edit the line
 97 |  ```
 98 |  const BASE_URL = 'https://ccrma.stanford.edu/~jos/juce_modules';
 99 |  ```
100 | More up-to-date possibilities include 
101 |  ```
102 |  const BASE_URL = 'https://docs.juce.com/develop';
103 |  const BASE_URL = 'https://docs.juce.com/master';
104 |  ```
105 | 
106 | ## Tips for Effective JUCE Development
107 | 
108 | When working on a JUCE project, here's how to get the most out of the JUCE Documentation MCP Server:
109 | 
110 | ### Quick Reference Workflows
111 | 
112 | 1. **Exploring Components**
113 |    - Start with `/search-juce-classes` followed by a general category (Audio, GUI, etc.)
114 |    - Use `explore-juce audio` (or other domain) to get an overview of related classes
115 | 
116 | 2. **Implementation Help**
117 |    - When implementing a specific feature, use `juce://class/ClassName` to get detailed documentation
118 |    - Look for code examples in the class documentation
119 | 
120 | 3. **Method Reference**
121 |    - The class documentation includes all methods with signatures and descriptions
122 |    - Use this when you need to understand parameter types or return values
123 | 
124 | ### Integration with Your Development Process
125 | 
126 | 1. **Keep Cursor Open Alongside Your IDE**
127 |    - Have Cursor with the MCP server running in a separate window
128 |    - This gives you instant access to documentation without leaving your code editor
129 | 
130 | 2. **Use During Planning Phases**
131 |    - Before implementing a feature, explore available classes with `/search-juce-classes`
132 |    - This helps you understand the JUCE approach before writing code
133 | 
134 | 3. **Debugging Assistance**
135 |    - When encountering unexpected behavior, check the class documentation
136 |    - Look for notes about edge cases or implementation details
137 | 
138 | ### Specific JUCE Development Tips
139 | 
140 | 1. **Audio Processing**
141 |    - Start with `AudioProcessor` for plugin development
142 |    - Use `AudioSource` for playback applications
143 |    - Check `dsp::` namespace classes for efficient signal processing
144 | 
145 | 2. **GUI Development**
146 |    - Base all custom components on the `Component` class
147 |    - Use `AudioAppComponent` to combine audio and GUI functionality
148 |    - Look at `LookAndFeel` for styling
149 | 
150 | 3. **Plugin Development**
151 |    - Reference `AudioProcessor` and `AudioProcessorEditor` for the core plugin architecture
152 |    - Check `AudioProcessorValueTreeState` for parameter management
153 | 
154 | ## Implementation Details
155 | 
156 | The server fetches documentation from the JUCE documentation hosted at Stanford CCRMA
157 | (https://ccrma.stanford.edu/~jos/juce_modules/), but of course you can change that, as noted above.
158 | It processes the HTML documentation in real-time:
159 | 
160 | 1. Class list is fetched from the annotated class list page
161 | 2. Individual class documentation is parsed from class-specific pages
162 | 3. Documentation is formatted as markdown for consistent display
163 | 4. Results are cached in memory during server runtime
164 | 
165 | ## Error Handling
166 | 
167 | Common issues and solutions:
168 | 
169 | 1. **Class Not Found**: If a class name is invalid or not found, the server will return a clear error message
170 | 2. **Connection Issues**: If the JUCE documentation site is unreachable, check your internet connection
171 | 3. **Server Start Failure**: Ensure the correct Node.js version is installed and the build step completed successfully
172 | 4. **Cursor Integration**: If the server isn't working in Cursor, verify the command path in MCP settings is correct
173 | 
174 | ## Development
175 | 
176 | ```bash
177 | # Run in development mode with auto-recompilation
178 | npm run dev
179 | ```
180 | 
181 | [Developer Notes](./README-DEV.md)
182 | 
183 | ## License
184 | 
185 | MIT
186 | 
```

--------------------------------------------------------------------------------
/juce-docs-mcp-server/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "NodeNext",
 5 |     "moduleResolution": "NodeNext",
 6 |     "esModuleInterop": true,
 7 |     "strict": true,
 8 |     "outDir": "dist",
 9 |     "sourceMap": true,
10 |     "declaration": true,
11 |     "skipLibCheck": true
12 |   },
13 |   "include": ["src/**/*"],
14 |   "exclude": ["node_modules", "dist"]
15 | } 
```

--------------------------------------------------------------------------------
/juce-docs-mcp-server/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "juce-docs-mcp-server",
 3 |   "version": "1.0.0",
 4 |   "description": "MCP server for JUCE Framework documentation",
 5 |   "type": "module",
 6 |   "main": "dist/index.js",
 7 |   "scripts": {
 8 |     "build": "tsc",
 9 |     "start": "node dist/index.js",
10 |     "dev": "tsc -w",
11 |     "test": "tsc && node dist/test-client.js"
12 |   },
13 |   "keywords": [
14 |     "mcp",
15 |     "juce",
16 |     "documentation"
17 |   ],
18 |   "author": "",
19 |   "license": "ISC",
20 |   "dependencies": {
21 |     "@modelcontextprotocol/sdk": "^1.6.1",
22 |     "cheerio": "^1.0.0-rc.12",
23 |     "node-fetch": "^2.6.9",
24 |     "zod": "^3.22.4"
25 |   },
26 |   "devDependencies": {
27 |     "@types/node": "^20.10.5",
28 |     "@types/node-fetch": "^2.6.9",
29 |     "typescript": "^5.3.3"
30 |   }
31 | }
32 | 
```

--------------------------------------------------------------------------------
/juce-docs-mcp-server/README-DEV.md:
--------------------------------------------------------------------------------

```markdown
 1 | # JUCE Documentation MCP Server - Development Notes
 2 | 
 3 | Development History and Technical Details
 4 | 
 5 | ## Project Genesis
 6 | 
 7 | The project started with the following prompt in Cursor while editing
 8 | a fresh clone of the [MCP Template](https://github.com/josmithiii/mcp-template.git):
 9 | 
10 | > I want to create an MCP server which provides an API for retrieving C++ class documentation for the JUCE Framework, using this URL for the lookups:
11 | > https://ccrma.stanford.edu/~jos/juce_modules/index.html<br/>
12 | > For example, documentation for the ValueTree class can be accessed as<br/>
13 | > https://ccrma.stanford.edu/~jos/juce_modules/classValueTree.html
14 | 
15 | That's it!  (However, the template includes a couple of important files that guide the generation.)
16 | 
17 | ## Architecture Overview
18 | 
19 | The key components of the project are:
20 | 
21 | 1. **juce-docs.ts**: Contains utility functions for fetching and parsing JUCE documentation from the Stanford CCRMA website.
22 |    - Handles HTML parsing and markdown conversion
23 |    - Manages class list fetching and caching
24 |    - Provides search functionality
25 | 
26 | 2. **index.ts**: The main MCP server implementation that exposes:
27 |    - Resources (`juce://class/{className}`, `juce://classes`)
28 |    - Tools (`search-classes`, `get-class-docs`)
29 |    - Prompts (`explore-juce`)
30 | 
31 | 3. **test-client.ts**: A test client that verifies the server functionality
32 |    - Tests resource endpoints
33 |    - Tests tool invocations
34 |    - Tests prompt handling
35 | 
36 | ## Implementation Notes
37 | 
38 | ### HTML Parsing Strategy
39 | 
40 | The server parses Doxygen-generated HTML documentation:
41 | 1. Class list is extracted from the annotated list page
42 | 2. Individual class pages are parsed for detailed documentation
43 | 3. HTML is converted to markdown for better display in LLM clients
44 | 
45 | ### Caching
46 | 
47 | - Class list is cached in memory during server runtime
48 | - Individual class documentation is fetched on demand
49 | - No persistent caching between server restarts (currently)
50 | 
51 | ### Error Handling Strategy
52 | 
53 | The server implements robust error handling:
54 | 1. Network request timeouts and retries
55 | 2. Invalid class name validation
56 | 3. HTML parsing error recovery
57 | 4. Clear error messages for client display
58 | 
59 | ## Development Workflow
60 | 
61 | 1. Make changes to source files
62 | 2. Run `npm run dev` for development with auto-recompilation
63 | 3. Test changes using the test client
64 | 4. Build with `npm run build` for production
65 | 
66 | ## Future Improvements
67 | 
68 | Potential enhancements to consider:
69 | 
70 | 1. Persistent caching of documentation
71 | 2. Support for method-level documentation lookup
72 | 3. Integration with other documentation sources
73 | 4. Enhanced search capabilities (e.g., method search)
74 | 5. Support for code examples and snippets
75 | 
76 | ## Project Status Summary
77 | 
78 | The MCP server is working correctly and provides:
79 | 
80 | 1. Access to JUCE Framework C++ class documentation
81 | 2. Resources for retrieving specific class documentation
82 | 3. Tools for searching and exploring classes
83 | 4. A prompt for guided framework exploration
84 | 5. Verified functionality through test client
85 | 
86 | The server successfully makes JUCE documentation directly accessible from LLM applications, enhancing the development workflow when working with the JUCE Framework. 
87 | 
```

--------------------------------------------------------------------------------
/juce-docs-mcp-server/src/test-client.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { Client } from "@modelcontextprotocol/sdk/client/index.js";
  2 | import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
  3 | import { spawn } from "child_process";
  4 | 
  5 | async function main() {
  6 |   try {
  7 |     console.log("Starting test client for JUCE Documentation MCP Server...");
  8 |     
  9 |     // Start the server process
 10 |     const serverProcess = spawn("node", ["dist/index.js"], {
 11 |       stdio: ["pipe", "pipe", process.stderr]
 12 |     });
 13 |     
 14 |     // Create a transport that communicates with the server process
 15 |     const transport = new StdioClientTransport({
 16 |       command: "node",
 17 |       args: ["dist/index.js"]
 18 |     });
 19 |     
 20 |     // Create an MCP client
 21 |     const client = new Client(
 22 |       {
 23 |         name: "JUCE Docs Test Client",
 24 |         version: "1.0.0"
 25 |       },
 26 |       {
 27 |         capabilities: {
 28 |           resources: {},
 29 |           tools: {}
 30 |         }
 31 |       }
 32 |     );
 33 |     
 34 |     // Connect to the server
 35 |     await client.connect(transport);
 36 |     console.log("Connected to server");
 37 |     
 38 |     // Test listing resources
 39 |     console.log("\nListing resources...");
 40 |     const resources = await client.listResources();
 41 |     console.log("Available resources:", resources);
 42 |     
 43 |     // Test listing tools
 44 |     console.log("\nListing tools...");
 45 |     const tools = await client.listTools();
 46 |     console.log("Available tools:", tools);
 47 |     
 48 |     // Test reading the class list resource
 49 |     console.log("\nReading class list...");
 50 |     const classList = await client.readResource({ uri: "juce://classes" });
 51 |     if (classList.contents && classList.contents[0] && classList.contents[0].text) {
 52 |       const text = classList.contents[0].text as string;
 53 |       console.log("Class list:", text.substring(0, 200) + "...");
 54 |     }
 55 |     
 56 |     // Test reading a specific class resource
 57 |     console.log("\nReading ValueTree class documentation...");
 58 |     const valueTreeDocs = await client.readResource({ uri: "juce://class/ValueTree" });
 59 |     if (valueTreeDocs.contents && valueTreeDocs.contents[0] && valueTreeDocs.contents[0].text) {
 60 |       const text = valueTreeDocs.contents[0].text as string;
 61 |       console.log("ValueTree docs:", text.substring(0, 200) + "...");
 62 |     }
 63 |     
 64 |     // Test searching for classes
 65 |     console.log("\nSearching for 'Audio' classes...");
 66 |     const searchResult = await client.callTool({
 67 |       name: "search-classes",
 68 |       arguments: {
 69 |         query: "Audio"
 70 |       }
 71 |     });
 72 |     
 73 |     // Type assertion for content
 74 |     const content = searchResult.content as Array<{type: string, text: string}>;
 75 |     if (content && content.length > 0) {
 76 |       console.log("Search results:", content[0].text.substring(0, 200) + "...");
 77 |     }
 78 |     
 79 |     // Test getting class documentation
 80 |     console.log("\nGetting AudioBuffer class documentation...");
 81 |     const audioDocs = await client.callTool({
 82 |       name: "get-class-docs",
 83 |       arguments: {
 84 |         className: "AudioBuffer"
 85 |       }
 86 |     });
 87 |     
 88 |     // Type assertion for content
 89 |     const audioContent = audioDocs.content as Array<{type: string, text: string}>;
 90 |     if (audioContent && audioContent.length > 0) {
 91 |       console.log("AudioBuffer docs:", audioContent[0].text.substring(0, 200) + "...");
 92 |     }
 93 |     
 94 |     console.log("\nAll tests completed successfully!");
 95 |     
 96 |     // Clean up
 97 |     serverProcess.kill();
 98 |     process.exit(0);
 99 |   } catch (error) {
100 |     console.error("Error in test client:", error);
101 |     process.exit(1);
102 |   }
103 | }
104 | 
105 | main(); 
```

--------------------------------------------------------------------------------
/juce-docs-mcp-server/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
  2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  3 | import { z } from "zod";
  4 | import {
  5 |   fetchClassDocumentation,
  6 |   fetchClassList,
  7 |   searchClasses,
  8 |   formatClassDocumentation
  9 | } from "./juce-docs.js";
 10 | 
 11 | // Create an MCP server
 12 | const server = new McpServer({
 13 |   name: "JUCE Documentation Server",
 14 |   version: "1.0.0"
 15 | });
 16 | 
 17 | // Resource for getting documentation for a specific class
 18 | server.resource(
 19 |   "class-docs",
 20 |   new ResourceTemplate("juce://class/{className}", { list: undefined }),
 21 |   async (uri, { className }) => {
 22 |     console.log(`Fetching documentation for class: ${className}`);
 23 |     
 24 |     // Ensure className is a string
 25 |     const classNameStr = Array.isArray(className) ? className[0] : className;
 26 |     const doc = await fetchClassDocumentation(classNameStr);
 27 |     
 28 |     if (!doc) {
 29 |       return {
 30 |         contents: [{
 31 |           uri: uri.href,
 32 |           text: `Documentation for class '${classNameStr}' not found.`
 33 |         }]
 34 |       };
 35 |     }
 36 |     
 37 |     const markdown = formatClassDocumentation(doc);
 38 |     
 39 |     return {
 40 |       contents: [{
 41 |         uri: uri.href,
 42 |         text: markdown
 43 |       }]
 44 |     };
 45 |   }
 46 | );
 47 | 
 48 | // Resource for listing all available classes
 49 | server.resource(
 50 |   "class-list",
 51 |   "juce://classes",
 52 |   async (uri) => {
 53 |     console.log("Fetching list of all JUCE classes");
 54 |     
 55 |     const classes = await fetchClassList();
 56 |     
 57 |     return {
 58 |       contents: [{
 59 |         uri: uri.href,
 60 |         text: `# JUCE Classes\n\n${classes.map(c => `- [${c}](juce://class/${c})`).join('\n')}`
 61 |       }]
 62 |     };
 63 |   }
 64 | );
 65 | 
 66 | // Tool for searching classes
 67 | server.tool(
 68 |   "search-juce-classes",
 69 |   { query: z.string() },
 70 |   async ({ query }) => {
 71 |     console.log(`Searching for classes matching: ${query}`);
 72 |     
 73 |     const results = await searchClasses(query);
 74 |     
 75 |     if (results.length === 0) {
 76 |       return {
 77 |         content: [{ 
 78 |           type: "text", 
 79 |           text: `No classes found matching '${query}'.` 
 80 |         }]
 81 |       };
 82 |     }
 83 |     
 84 |     const markdown = `# Search Results for '${query}'\n\n${results.map(c => `- [${c}](juce://class/${c})`).join('\n')}`;
 85 |     
 86 |     return {
 87 |       content: [{ type: "text", text: markdown }]
 88 |     };
 89 |   }
 90 | );
 91 | 
 92 | // Tool for getting class documentation
 93 | server.tool(
 94 |   "get-juce-class-docs",
 95 |   { className: z.string() },
 96 |   async ({ className }) => {
 97 |     console.log(`Fetching documentation for class: ${className}`);
 98 |     
 99 |     const doc = await fetchClassDocumentation(className);
100 |     
101 |     if (!doc) {
102 |       return {
103 |         content: [{ 
104 |           type: "text", 
105 |           text: `Documentation for class '${className}' not found.` 
106 |         }]
107 |       };
108 |     }
109 |     
110 |     const markdown = formatClassDocumentation(doc);
111 |     
112 |     return {
113 |       content: [{ type: "text", text: markdown }]
114 |     };
115 |   }
116 | );
117 | 
118 | // Prompt for exploring JUCE documentation
119 | server.prompt(
120 |   "explore-juce",
121 |   { topic: z.string().optional() },
122 |   ({ topic }) => ({
123 |     messages: [{
124 |       role: "user",
125 |       content: {
126 |         type: "text",
127 |         text: topic 
128 |           ? `Please help me understand the JUCE ${topic} functionality. What classes should I look at?` 
129 |           : "Please help me explore the JUCE framework. What are the main components and classes I should know about?"
130 |       }
131 |     }]
132 |   })
133 | );
134 | 
135 | // Start the server
136 | async function main() {
137 |   try {
138 |     console.log("Starting JUCE Documentation MCP Server...");
139 |     
140 |     const transport = new StdioServerTransport();
141 |     await server.connect(transport);
142 |     
143 |     console.log("Server connected and ready to receive requests.");
144 |   } catch (error) {
145 |     console.error("Error starting server:", error);
146 |     process.exit(1);
147 |   }
148 | }
149 | 
150 | main(); 
```

--------------------------------------------------------------------------------
/juce-docs-mcp-server/src/juce-docs.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import fetch from 'node-fetch';
  2 | import * as cheerio from 'cheerio';
  3 | 
  4 | const BASE_URL = 'https://ccrma.stanford.edu/~jos/juce_modules';
  5 | // const BASE_URL = 'https://docs.juce.com/develop';
  6 | // const BASE_URL = 'https://docs.juce.com/master';
  7 | const INDEX_URL = `${BASE_URL}/annotated.html`;
  8 | const CLASS_URL_PATTERN = `${BASE_URL}/class{className}.html`;
  9 | 
 10 | /**
 11 |  * Fetches the HTML content from a URL
 12 |  */
 13 | async function fetchHtml(url: string): Promise<string> {
 14 |   try {
 15 |     const response = await fetch(url);
 16 |     if (!response.ok) {
 17 |       throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
 18 |     }
 19 |     return await response.text();
 20 |   } catch (error) {
 21 |     console.error(`Error fetching ${url}:`, error);
 22 |     throw error;
 23 |   }
 24 | }
 25 | 
 26 | /**
 27 |  * Fetches the list of all JUCE classes from the index page
 28 |  */
 29 | export async function fetchClassList(): Promise<string[]> {
 30 |   try {
 31 |     const html = await fetchHtml(INDEX_URL);
 32 |     const $ = cheerio.load(html);
 33 |     
 34 |     // Extract class names from the class list page
 35 |     const classes: string[] = [];
 36 |     
 37 |     // Look for links in the class list
 38 |     $('.directory tr.even, .directory tr.odd').each((_, element) => {
 39 |       const link = $(element).find('td.entry a');
 40 |       const href = link.attr('href');
 41 |       if (href && href.startsWith('class') && href.endsWith('.html')) {
 42 |         // Extract class name from href (e.g., "classValueTree.html" -> "ValueTree")
 43 |         const className = href.replace(/^class/, '').replace(/\.html$/, '');
 44 |         classes.push(className);
 45 |       }
 46 |     });
 47 |     
 48 |     return classes;
 49 |   } catch (error) {
 50 |     console.error('Error fetching class list:', error);
 51 |     throw error;
 52 |   }
 53 | }
 54 | 
 55 | /**
 56 |  * Represents the structure of JUCE class documentation
 57 |  */
 58 | export interface ClassDocumentation {
 59 |   className: string;
 60 |   description: string;
 61 |   methods: MethodDocumentation[];
 62 |   properties: PropertyDocumentation[];
 63 |   inheritance?: string;
 64 |   url: string;
 65 | }
 66 | 
 67 | export interface MethodDocumentation {
 68 |   name: string;
 69 |   signature: string;
 70 |   description: string;
 71 | }
 72 | 
 73 | export interface PropertyDocumentation {
 74 |   name: string;
 75 |   type: string;
 76 |   description: string;
 77 | }
 78 | 
 79 | /**
 80 |  * Fetches and parses documentation for a specific JUCE class
 81 |  */
 82 | export async function fetchClassDocumentation(className: string): Promise<ClassDocumentation | null> {
 83 |   try {
 84 |     const url = CLASS_URL_PATTERN.replace('{className}', className);
 85 |     const html = await fetchHtml(url);
 86 |     const $ = cheerio.load(html);
 87 |     
 88 |     // Extract class description
 89 |     const description = $('.contents .textblock').first().text().trim();
 90 |     
 91 |     // Extract methods
 92 |     const methods: MethodDocumentation[] = [];
 93 |     $('.memitem').each((_, element) => {
 94 |       const nameElement = $(element).find('.memname');
 95 |       if (nameElement.length) {
 96 |         const name = nameElement.text().trim().split('(')[0].trim();
 97 |         const signature = nameElement.parent().text().trim();
 98 |         const description = $(element).find('.memdoc').text().trim();
 99 |         
100 |         methods.push({
101 |           name,
102 |           signature,
103 |           description
104 |         });
105 |       }
106 |     });
107 |     
108 |     // Extract properties (this is simplified and might need adjustment)
109 |     const properties: PropertyDocumentation[] = [];
110 |     $('.fieldtable tr').each((_, element) => {
111 |       const nameElement = $(element).find('.fieldname');
112 |       if (nameElement.length) {
113 |         const name = nameElement.text().trim();
114 |         const type = $(element).find('.fieldtype').text().trim();
115 |         const description = $(element).find('.fielddoc').text().trim();
116 |         
117 |         properties.push({
118 |           name,
119 |           type,
120 |           description
121 |         });
122 |       }
123 |     });
124 |     
125 |     // Extract inheritance information
126 |     let inheritance: string | undefined;
127 |     $('.inheritance').each((_, element) => {
128 |       inheritance = $(element).text().trim();
129 |     });
130 |     
131 |     return {
132 |       className,
133 |       description,
134 |       methods,
135 |       properties,
136 |       inheritance,
137 |       url
138 |     };
139 |   } catch (error) {
140 |     console.error(`Error fetching documentation for ${className}:`, error);
141 |     return null;
142 |   }
143 | }
144 | 
145 | /**
146 |  * Searches for classes matching a query string
147 |  */
148 | export async function searchClasses(query: string): Promise<string[]> {
149 |   try {
150 |     const allClasses = await fetchClassList();
151 |     const lowerQuery = query.toLowerCase();
152 |     
153 |     return allClasses.filter(className => 
154 |       className.toLowerCase().includes(lowerQuery)
155 |     );
156 |   } catch (error) {
157 |     console.error('Error searching classes:', error);
158 |     throw error;
159 |   }
160 | }
161 | 
162 | /**
163 |  * Formats class documentation as markdown
164 |  */
165 | export function formatClassDocumentation(doc: ClassDocumentation): string {
166 |   let markdown = `# ${doc.className}\n\n`;
167 |   
168 |   if (doc.inheritance) {
169 |     markdown += `**Inheritance:** ${doc.inheritance}\n\n`;
170 |   }
171 |   
172 |   markdown += `${doc.description}\n\n`;
173 |   markdown += `[View Online Documentation](${doc.url})\n\n`;
174 |   
175 |   if (doc.methods.length > 0) {
176 |     markdown += `## Methods\n\n`;
177 |     doc.methods.forEach(method => {
178 |       markdown += `### ${method.name}\n\n`;
179 |       markdown += `\`\`\`cpp\n${method.signature}\n\`\`\`\n\n`;
180 |       markdown += `${method.description}\n\n`;
181 |     });
182 |   }
183 |   
184 |   if (doc.properties.length > 0) {
185 |     markdown += `## Properties\n\n`;
186 |     doc.properties.forEach(prop => {
187 |       markdown += `### ${prop.name}\n\n`;
188 |       markdown += `**Type:** ${prop.type}\n\n`;
189 |       markdown += `${prop.description}\n\n`;
190 |     });
191 |   }
192 |   
193 |   return markdown;
194 | } 
195 | 
```