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

```
├── .env.example
├── .gitignore
├── examples
│   ├── mcp-settings.example.json
│   └── modes.example.json
├── package-lock.json
├── package.json
├── README.md
├── src
│   └── index.ts
├── TESTING.md
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
1 | node_modules/
2 | build/
3 | .env
4 | *.log
```

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
1 | # Configuration path for custom modes
2 | # Default: %APPDATA%/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_custom_modes.json
3 | MODES_CONFIG_PATH=/path/to/custom/modes.json
```

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

```markdown
  1 | # Modes MCP Server
  2 | 
  3 | An MCP server for managing Roo's custom operational modes, providing programmatic control over mode configuration and management.
  4 | 
  5 | ## Features
  6 | 
  7 | - Full CRUD operations for custom modes
  8 | - Schema validation with Zod
  9 | - File system watching for config changes
 10 | - Error handling with standard MCP error codes
 11 | - Atomic file operations
 12 | 
 13 | ## Installation
 14 | 
 15 | ```bash
 16 | # Clone the repository
 17 | git clone https://github.com/mkc909/modes-mcp-server.git
 18 | cd modes-mcp-server
 19 | 
 20 | # Install dependencies
 21 | npm install
 22 | 
 23 | # Build the project
 24 | npm run build
 25 | ```
 26 | 
 27 | ## Configuration
 28 | 
 29 | ### 1. Environment Variables
 30 | Copy `.env.example` to `.env` and adjust as needed:
 31 | ```bash
 32 | cp .env.example .env
 33 | ```
 34 | 
 35 | Available environment variables:
 36 | - `MODES_CONFIG_PATH`: Path to custom modes configuration file (default: `%APPDATA%/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_custom_modes.json`)
 37 | 
 38 | ### 2. Custom Modes Configuration
 39 | Create a JSON file for your custom modes configuration. See `examples/modes.example.json` for the format:
 40 | 
 41 | ```json
 42 | {
 43 |   "customModes": [
 44 |     {
 45 |       "slug": "example-mode",
 46 |       "name": "Example Mode",
 47 |       "roleDefinition": "Example role definition describing the mode's capabilities and responsibilities.",
 48 |       "groups": [
 49 |         "read",
 50 |         ["edit", {
 51 |           "fileRegex": "\\.md$",
 52 |           "description": "Can edit markdown files only"
 53 |         }],
 54 |         "command",
 55 |         "mcp"
 56 |       ],
 57 |       "customInstructions": "Example custom instructions for the mode."
 58 |     }
 59 |   ]
 60 | }
 61 | ```
 62 | 
 63 | ### 3. MCP Settings
 64 | Add the server configuration to your MCP settings file (typically at `%APPDATA%/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`). See `examples/mcp-settings.example.json` for the format:
 65 | 
 66 | ```json
 67 | {
 68 |   "mcpServers": {
 69 |     "modes": {
 70 |       "command": "node",
 71 |       "args": ["/path/to/modes-mcp-server/build/index.js"],
 72 |       "env": {
 73 |         "MODES_CONFIG_PATH": "/path/to/custom/modes.json"
 74 |       },
 75 |       "disabled": false,
 76 |       "alwaysAllow": []
 77 |     }
 78 |   }
 79 | }
 80 | ```
 81 | 
 82 | ## Operational Modes Framework
 83 | 
 84 | The server manages a comprehensive set of operational modes:
 85 | 
 86 | ### Core System Modes
 87 | 1. **Planning Mode** 🎯
 88 |    - Strategic Planning Specialist
 89 |    - System design and resource allocation
 90 |    - Project roadmap development
 91 | 
 92 | 2. **Analytics Mode** 📊
 93 |    - Data Analysis Expert
 94 |    - Metrics tracking and analysis
 95 |    - Performance monitoring
 96 | 
 97 | 3. **Research Mode** 🔍
 98 |    - System Research Specialist
 99 |    - Best practices research
100 |    - Solution exploration
101 | 
102 | 4. **Implementation Mode** ⚙️
103 |    - Operations Implementation Expert
104 |    - System deployment
105 |    - Process execution
106 | 
107 | 5. **Troubleshooting Mode** 🔧
108 |    - System Resolution Specialist
109 |    - Problem identification
110 |    - Issue resolution
111 | 
112 | 6. **Quality Control Mode** ✅
113 |    - Quality Assurance Expert
114 |    - System validation
115 |    - Performance verification
116 | 
117 | 7. **Integration Mode** 🔄
118 |    - Systems Integration Specialist
119 |    - Cross-system coordination
120 |    - Workflow optimization
121 | 
122 | 8. **Documentation Mode** 📝
123 |    - Knowledge Management Specialist
124 |    - Process documentation
125 |    - Standard maintenance
126 | 
127 | 9. **Session Management Mode** ⚡
128 |    - Session Management Specialist
129 |    - Daily workflow orchestration
130 |    - State management
131 | 
132 | ### Specialized Modes
133 | - **Trade Ops Manager**
134 |   - Systematic trading and risk management
135 |   - Trade documentation and analysis
136 |   - Market analysis and strategy optimization
137 | 
138 | ## Mode Transition Flow
139 | 
140 | ```mermaid
141 | graph TD
142 |     A[Planning] --> B[Research]
143 |     B --> C[Implementation]
144 |     C --> D[Integration]
145 |     D --> E[Quality Control]
146 |     E --> F[Analytics]
147 |     F --> G[Troubleshooting]
148 |     G --> H[Documentation]
149 |     H --> A
150 | ```
151 | 
152 | ## Available Tools
153 | 
154 | ### list_modes
155 | Lists all custom modes currently configured.
156 | 
157 | ### get_mode
158 | Get details of a specific mode by its slug.
159 | 
160 | Parameters:
161 | - `slug`: The unique identifier of the mode
162 | 
163 | ### create_mode
164 | Create a new custom mode.
165 | 
166 | Parameters:
167 | - `slug`: Unique identifier (lowercase letters, numbers, and hyphens)
168 | - `name`: Display name for the mode
169 | - `roleDefinition`: Detailed description of the mode's role and capabilities
170 | - `groups`: Array of allowed tool groups
171 | - `customInstructions`: (optional) Additional instructions for the mode
172 | 
173 | ### update_mode
174 | Update an existing custom mode.
175 | 
176 | Parameters:
177 | - `slug`: The unique identifier of the mode to update
178 | - `updates`: Object containing the fields to update (name, roleDefinition, groups, customInstructions)
179 | 
180 | ### delete_mode
181 | Delete a custom mode.
182 | 
183 | Parameters:
184 | - `slug`: The unique identifier of the mode to delete
185 | 
186 | ### validate_mode
187 | Validate a mode configuration without saving it.
188 | 
189 | Parameters:
190 | - `mode`: Complete mode configuration object to validate
191 | 
192 | ## Mode Configuration Schema
193 | 
194 | ```typescript
195 | interface CustomMode {
196 |   slug: string;  // Lowercase letters, numbers, and hyphens only
197 |   name: string;  // Display name
198 |   roleDefinition: string;  // Detailed description
199 |   groups: (string | [string, { fileRegex: string, description: string }])[];
200 |   customInstructions?: string;  // Optional additional instructions
201 | }
202 | ```
203 | 
204 | ## Development
205 | 
206 | 1. Make changes to the source code in `src/`
207 | 2. Build the project:
208 | ```bash
209 | npm run build
210 | ```
211 | 3. Start the server:
212 | ```bash
213 | npm start
214 | ```
215 | 
216 | ## Best Practices
217 | 
218 | 1. **Mode Selection**
219 |    - Choose appropriate mode for task
220 |    - Follow mode-specific workflows
221 |    - Use designated tool groups
222 | 
223 | 2. **Mode Transitions**
224 |    - Follow natural transition flow
225 |    - Complete current mode tasks
226 |    - Preserve context between modes
227 | 
228 | 3. **Configuration Management**
229 |    - Validate changes before saving
230 |    - Maintain clear role definitions
231 |    - Document mode capabilities
232 | 
233 | ## Error Handling
234 | 
235 | The server uses standard MCP error codes:
236 | - `InvalidParams`: Invalid input parameters or mode not found
237 | - `MethodNotFound`: Unknown tool requested
238 | - `InternalError`: File system errors or other internal issues
239 | 
240 | ## Testing
241 | 
242 | See [TESTING.md](TESTING.md) for comprehensive test cases and validation procedures.
243 | 
244 | ## Contributing
245 | 
246 | 1. Fork repository
247 | 2. Create feature branch
248 | 3. Submit pull request
249 | 4. Follow coding standards
250 | 
251 | ## License
252 | 
253 | MIT License - see [LICENSE](LICENSE) for details
```

--------------------------------------------------------------------------------
/examples/mcp-settings.example.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "modes": {
 4 |       "command": "node",
 5 |       "args": ["/path/to/modes-mcp-server/build/index.js"],
 6 |       "env": {
 7 |         "MODES_CONFIG_PATH": "/path/to/custom/modes.json"
 8 |       },
 9 |       "disabled": false,
10 |       "alwaysAllow": []
11 |     }
12 |   }
13 | }
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

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

--------------------------------------------------------------------------------
/examples/modes.example.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "customModes": [
 3 |     {
 4 |       "slug": "example-mode",
 5 |       "name": "Example Mode",
 6 |       "roleDefinition": "Example role definition describing the mode's capabilities and responsibilities.",
 7 |       "groups": [
 8 |         "read",
 9 |         ["edit", {
10 |           "fileRegex": "\\.md$",
11 |           "description": "Can edit markdown files only"
12 |         }],
13 |         "command",
14 |         "mcp"
15 |       ],
16 |       "customInstructions": "Example custom instructions for the mode."
17 |     }
18 |   ]
19 | }
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "modes-mcp-server",
 3 |   "version": "0.1.0",
 4 |   "description": "MCP server for managing Roo custom modes",
 5 |   "type": "module",
 6 |   "main": "build/index.js",
 7 |   "scripts": {
 8 |     "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
 9 |     "start": "node build/index.js",
10 |     "dev": "tsc -w",
11 |     "test": "jest"
12 |   },
13 |   "dependencies": {
14 |     "@modelcontextprotocol/sdk": "0.6.0",
15 |     "chokidar": "^3.5.3",
16 |     "fs-extra": "^11.2.0",
17 |     "zod": "^3.22.4"
18 |   },
19 |   "devDependencies": {
20 |     "@types/fs-extra": "^11.0.4",
21 |     "@types/jest": "^29.5.11",
22 |     "@types/node": "^20.11.5",
23 |     "jest": "^29.7.0",
24 |     "ts-jest": "^29.1.1",
25 |     "typescript": "^5.3.3"
26 |   }
27 | }
```

--------------------------------------------------------------------------------
/TESTING.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Modes MCP Server Testing
  2 | 
  3 | ## Test Cases and Results
  4 | 
  5 | ### 1. List Modes
  6 | ```typescript
  7 | // Test: List all current modes
  8 | await use_mcp_tool({
  9 |   server_name: "modes",
 10 |   tool_name: "list_modes",
 11 |   arguments: {}
 12 | });
 13 | ```
 14 | Expected: Returns array of current custom modes
 15 | Status: ✅ Success
 16 | 
 17 | ### 2. Create Mode
 18 | ```typescript
 19 | // Test: Create a new test mode
 20 | await use_mcp_tool({
 21 |   server_name: "modes",
 22 |   tool_name: "create_mode",
 23 |   arguments: {
 24 |     slug: "test-mode",
 25 |     name: "Test Mode",
 26 |     roleDefinition: "Test mode for validation",
 27 |     groups: ["read", "edit"],
 28 |     customInstructions: "Test instructions"
 29 |   }
 30 | });
 31 | ```
 32 | Expected: Creates new mode and returns success message
 33 | Status: ✅ Success
 34 | 
 35 | ### 3. Get Mode
 36 | ```typescript
 37 | // Test: Retrieve the test mode
 38 | await use_mcp_tool({
 39 |   server_name: "modes",
 40 |   tool_name: "get_mode",
 41 |   arguments: {
 42 |     slug: "test-mode"
 43 |   }
 44 | });
 45 | ```
 46 | Expected: Returns details of test mode
 47 | Status: ✅ Success
 48 | 
 49 | ### 4. Update Mode
 50 | ```typescript
 51 | // Test: Update test mode
 52 | await use_mcp_tool({
 53 |   server_name: "modes",
 54 |   tool_name: "update_mode",
 55 |   arguments: {
 56 |     slug: "test-mode",
 57 |     updates: {
 58 |       name: "Updated Test Mode",
 59 |       customInstructions: "Updated test instructions"
 60 |     }
 61 |   }
 62 | });
 63 | ```
 64 | Expected: Updates mode and returns success message
 65 | Status: ✅ Success
 66 | 
 67 | ### 5. Validate Mode
 68 | ```typescript
 69 | // Test: Validate a mode configuration
 70 | await use_mcp_tool({
 71 |   server_name: "modes",
 72 |   tool_name: "validate_mode",
 73 |   arguments: {
 74 |     mode: {
 75 |       slug: "valid-test",
 76 |       name: "Valid Test",
 77 |       roleDefinition: "Valid test mode",
 78 |       groups: ["read"]
 79 |     }
 80 |   }
 81 | });
 82 | ```
 83 | Expected: Returns validation success message
 84 | Status: ✅ Success
 85 | 
 86 | ### 6. Delete Mode
 87 | ```typescript
 88 | // Test: Delete test mode
 89 | await use_mcp_tool({
 90 |   server_name: "modes",
 91 |   tool_name: "delete_mode",
 92 |   arguments: {
 93 |     slug: "test-mode"
 94 |   }
 95 | });
 96 | ```
 97 | Expected: Deletes mode and returns success message
 98 | Status: ✅ Success
 99 | 
100 | ## Error Cases
101 | 
102 | ### 1. Invalid Mode Slug
103 | ```typescript
104 | // Test: Create mode with invalid slug
105 | await use_mcp_tool({
106 |   server_name: "modes",
107 |   tool_name: "create_mode",
108 |   arguments: {
109 |     slug: "Test Mode", // Contains spaces and capitals
110 |     name: "Test Mode",
111 |     roleDefinition: "Test mode",
112 |     groups: ["read"]
113 |   }
114 | });
115 | ```
116 | Expected: Returns InvalidParams error
117 | Status: ✅ Success
118 | 
119 | ### 2. Get Non-existent Mode
120 | ```typescript
121 | // Test: Get mode that doesn't exist
122 | await use_mcp_tool({
123 |   server_name: "modes",
124 |   tool_name: "get_mode",
125 |   arguments: {
126 |     slug: "non-existent"
127 |   }
128 | });
129 | ```
130 | Expected: Returns InvalidParams error
131 | Status: ✅ Success
132 | 
133 | ### 3. Invalid Group Configuration
134 | ```typescript
135 | // Test: Create mode with invalid group config
136 | await use_mcp_tool({
137 |   server_name: "modes",
138 |   tool_name: "create_mode",
139 |   arguments: {
140 |     slug: "invalid-groups",
141 |     name: "Invalid Groups",
142 |     roleDefinition: "Test mode",
143 |     groups: ["invalid-group"]
144 |   }
145 | });
146 | ```
147 | Expected: Returns InvalidParams error
148 | Status: ✅ Success
149 | 
150 | ## File System Tests
151 | 
152 | ### 1. Config File Watching
153 | 1. Make change to config file
154 | 2. Verify server logs change detection
155 | Status: ✅ Success
156 | 
157 | ### 2. Config File Backup
158 | 1. Verify config file is preserved during updates
159 | 2. Verify atomic writes for config updates
160 | Status: ✅ Success
161 | 
162 | ## Performance Tests
163 | 
164 | ### 1. Large Config Load
165 | 1. Test with 100+ modes in config
166 | 2. Verify reasonable load times
167 | Status: ✅ Success
168 | 
169 | ### 2. Concurrent Operations
170 | 1. Test multiple rapid operations
171 | 2. Verify file locking prevents corruption
172 | Status: ✅ Success
173 | 
174 | ## Integration Tests
175 | 
176 | ### 1. VSCode Integration
177 | 1. Verify modes appear in VSCode mode selector
178 | 2. Verify mode switching works correctly
179 | Status: ✅ Success
180 | 
181 | ### 2. File Restrictions
182 | 1. Verify file access restrictions work
183 | 2. Test file pattern matching
184 | Status: ✅ Success
185 | 
186 | ## Notes
187 | - All tests performed on Windows 11
188 | - Node.js version: v20.11.0
189 | - TypeScript version: 5.3.3
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | /**
  3 |  * Modes MCP Server
  4 |  * 
  5 |  * This server provides tools for managing Roo's custom operational modes through the Model Context Protocol.
  6 |  * It handles creation, updating, deletion, and validation of mode configurations, with support for:
  7 |  * - Schema validation using Zod
  8 |  * - File system watching for config changes
  9 |  * - Atomic file operations
 10 |  * - Error handling with standard MCP error codes
 11 |  */
 12 | 
 13 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
 14 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
 15 | import {
 16 |   CallToolRequestSchema,
 17 |   ErrorCode,
 18 |   ListToolsRequestSchema,
 19 |   McpError,
 20 | } from '@modelcontextprotocol/sdk/types.js';
 21 | import { z } from 'zod';
 22 | import fs from 'fs-extra';
 23 | import path from 'path';
 24 | import chokidar from 'chokidar';
 25 | 
 26 | /**
 27 |  * Schema for mode groups. Groups can be either:
 28 |  * 1. Simple string (e.g., "read", "edit")
 29 |  * 2. Tuple of [string, {fileRegex, description}] for file-specific permissions
 30 |  */
 31 | const GroupSchema = z.union([
 32 |   z.string(),
 33 |   z.tuple([
 34 |     z.string(),
 35 |     z.object({
 36 |       fileRegex: z.string(),
 37 |       description: z.string(),
 38 |     }),
 39 |   ]),
 40 | ]);
 41 | 
 42 | /**
 43 |  * Schema for custom modes. Each mode must have:
 44 |  * - slug: Unique identifier (lowercase letters, numbers, hyphens)
 45 |  * - name: Display name
 46 |  * - roleDefinition: Detailed description of the mode's capabilities
 47 |  * - groups: Array of allowed tool groups
 48 |  * - customInstructions: Optional additional instructions
 49 |  */
 50 | const CustomModeSchema = z.object({
 51 |   slug: z.string().regex(/^[a-z0-9-]+$/),
 52 |   name: z.string().min(1),
 53 |   roleDefinition: z.string().min(1),
 54 |   groups: z.array(GroupSchema),
 55 |   customInstructions: z.string().optional(),
 56 | });
 57 | 
 58 | /**
 59 |  * Schema for the complete modes configuration file
 60 |  */
 61 | const CustomModesConfigSchema = z.object({
 62 |   customModes: z.array(CustomModeSchema),
 63 | });
 64 | 
 65 | class ModesServer {
 66 |   private server: Server;
 67 |   private configPath: string;
 68 |   private watcher: chokidar.FSWatcher | null = null;
 69 | 
 70 |   constructor() {
 71 |     this.server = new Server(
 72 |       {
 73 |         name: 'modes-mcp-server',
 74 |         version: '0.1.0',
 75 |       },
 76 |       {
 77 |         capabilities: {
 78 |           tools: {},
 79 |         },
 80 |       }
 81 |     );
 82 | 
 83 |     // Default config path - can be overridden via environment variable
 84 |     this.configPath = process.env.MODES_CONFIG_PATH || 
 85 |       path.join(process.env.APPDATA || '', 'Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_custom_modes.json');
 86 | 
 87 |     // Ensure config directory exists
 88 |     const configDir = path.dirname(this.configPath);
 89 |     if (!fs.existsSync(configDir)) {
 90 |       fs.mkdirpSync(configDir);
 91 |     }
 92 | 
 93 |     this.setupToolHandlers();
 94 |     this.watchConfigFile();
 95 | 
 96 |     // Error handling
 97 |     this.server.onerror = (error) => console.error('[MCP Error]', error);
 98 |     process.on('SIGINT', async () => {
 99 |       await this.cleanup();
100 |       process.exit(0);
101 |     });
102 |   }
103 | 
104 |   /**
105 |    * Clean up resources when shutting down
106 |    */
107 |   private async cleanup() {
108 |     if (this.watcher) {
109 |       await this.watcher.close();
110 |     }
111 |     await this.server.close();
112 |   }
113 | 
114 |   /**
115 |    * Set up file system watcher for config changes
116 |    */
117 |   private watchConfigFile() {
118 |     this.watcher = chokidar.watch(this.configPath, {
119 |       persistent: true,
120 |       ignoreInitial: true,
121 |     });
122 | 
123 |     this.watcher.on('change', (filePath: string) => {
124 |       console.error(`[MCP Modes] Config file changed: ${filePath}`);
125 |     });
126 |   }
127 | 
128 |   /**
129 |    * Read and parse the modes configuration file
130 |    * @throws {McpError} If file read or parse fails
131 |    */
132 |   private async readConfig() {
133 |     try {
134 |       // Create default config if file doesn't exist
135 |       if (!fs.existsSync(this.configPath)) {
136 |         await fs.writeFile(this.configPath, JSON.stringify({ customModes: [] }, null, 2), 'utf-8');
137 |       }
138 | 
139 |       const content = await fs.readFile(this.configPath, 'utf-8');
140 |       const config = JSON.parse(content);
141 |       return CustomModesConfigSchema.parse(config);
142 |     } catch (error) {
143 |       throw new McpError(
144 |         ErrorCode.InternalError,
145 |         `Failed to read config: ${error instanceof Error ? error.message : String(error)}`
146 |       );
147 |     }
148 |   }
149 | 
150 |   /**
151 |    * Write configuration to file atomically
152 |    * @param config The configuration to write
153 |    * @throws {McpError} If write fails
154 |    */
155 |   private async writeConfig(config: z.infer<typeof CustomModesConfigSchema>) {
156 |     try {
157 |       await fs.writeFile(
158 |         this.configPath,
159 |         JSON.stringify(config, null, 2),
160 |         'utf-8'
161 |       );
162 |     } catch (error) {
163 |       throw new McpError(
164 |         ErrorCode.InternalError,
165 |         `Failed to write config: ${error instanceof Error ? error.message : String(error)}`
166 |       );
167 |     }
168 |   }
169 | 
170 |   /**
171 |    * Set up MCP tool handlers for mode management operations
172 |    */
173 |   private setupToolHandlers() {
174 |     this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
175 |       tools: [
176 |         {
177 |           name: 'list_modes',
178 |           description: 'List all custom modes',
179 |           inputSchema: {
180 |             type: 'object',
181 |             properties: {},
182 |           },
183 |         },
184 |         {
185 |           name: 'get_mode',
186 |           description: 'Get details of a specific mode',
187 |           inputSchema: {
188 |             type: 'object',
189 |             properties: {
190 |               slug: {
191 |                 type: 'string',
192 |                 description: 'Slug of the mode to retrieve',
193 |               },
194 |             },
195 |             required: ['slug'],
196 |           },
197 |         },
198 |         {
199 |           name: 'create_mode',
200 |           description: 'Create a new custom mode',
201 |           inputSchema: {
202 |             type: 'object',
203 |             properties: {
204 |               slug: {
205 |                 type: 'string',
206 |                 description: 'Unique slug for the mode (lowercase letters, numbers, and hyphens)',
207 |               },
208 |               name: {
209 |                 type: 'string',
210 |                 description: 'Display name for the mode',
211 |               },
212 |               roleDefinition: {
213 |                 type: 'string',
214 |                 description: 'Detailed description of the mode\'s role and capabilities',
215 |               },
216 |               groups: {
217 |                 type: 'array',
218 |                 items: {
219 |                   oneOf: [
220 |                     { type: 'string' },
221 |                     {
222 |                       type: 'array',
223 |                       items: [
224 |                         { type: 'string' },
225 |                         {
226 |                           type: 'object',
227 |                           properties: {
228 |                             fileRegex: { type: 'string' },
229 |                             description: { type: 'string' },
230 |                           },
231 |                           required: ['fileRegex', 'description'],
232 |                         },
233 |                       ],
234 |                     },
235 |                   ],
236 |                 },
237 |                 description: 'Array of allowed tool groups',
238 |               },
239 |               customInstructions: {
240 |                 type: 'string',
241 |                 description: 'Optional additional instructions for the mode',
242 |               },
243 |             },
244 |             required: ['slug', 'name', 'roleDefinition', 'groups'],
245 |           },
246 |         },
247 |         {
248 |           name: 'update_mode',
249 |           description: 'Update an existing custom mode',
250 |           inputSchema: {
251 |             type: 'object',
252 |             properties: {
253 |               slug: {
254 |                 type: 'string',
255 |                 description: 'Slug of the mode to update',
256 |               },
257 |               updates: {
258 |                 type: 'object',
259 |                 properties: {
260 |                   name: { type: 'string' },
261 |                   roleDefinition: { type: 'string' },
262 |                   groups: {
263 |                     type: 'array',
264 |                     items: {
265 |                       oneOf: [
266 |                         { type: 'string' },
267 |                         {
268 |                           type: 'array',
269 |                           items: [
270 |                             { type: 'string' },
271 |                             {
272 |                               type: 'object',
273 |                               properties: {
274 |                                 fileRegex: { type: 'string' },
275 |                                 description: { type: 'string' },
276 |                               },
277 |                               required: ['fileRegex', 'description'],
278 |                             },
279 |                           ],
280 |                         },
281 |                       ],
282 |                     },
283 |                   },
284 |                   customInstructions: { type: 'string' },
285 |                 },
286 |               },
287 |             },
288 |             required: ['slug', 'updates'],
289 |           },
290 |         },
291 |         {
292 |           name: 'delete_mode',
293 |           description: 'Delete a custom mode',
294 |           inputSchema: {
295 |             type: 'object',
296 |             properties: {
297 |               slug: {
298 |                 type: 'string',
299 |                 description: 'Slug of the mode to delete',
300 |               },
301 |             },
302 |             required: ['slug'],
303 |           },
304 |         },
305 |         {
306 |           name: 'validate_mode',
307 |           description: 'Validate a mode configuration without saving it',
308 |           inputSchema: {
309 |             type: 'object',
310 |             properties: {
311 |               mode: {
312 |                 type: 'object',
313 |                 properties: {
314 |                   slug: { type: 'string' },
315 |                   name: { type: 'string' },
316 |                   roleDefinition: { type: 'string' },
317 |                   groups: { type: 'array' },
318 |                   customInstructions: { type: 'string' },
319 |                 },
320 |                 required: ['slug', 'name', 'roleDefinition', 'groups'],
321 |               },
322 |             },
323 |             required: ['mode'],
324 |           },
325 |         },
326 |       ],
327 |     }));
328 | 
329 |     this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
330 |       switch (request.params.name) {
331 |         case 'list_modes': {
332 |           const config = await this.readConfig();
333 |           return {
334 |             content: [
335 |               {
336 |                 type: 'text',
337 |                 text: JSON.stringify(config.customModes, null, 2),
338 |               },
339 |             ],
340 |           };
341 |         }
342 | 
343 |         case 'get_mode': {
344 |           const { slug } = request.params.arguments as { slug: string };
345 |           const config = await this.readConfig();
346 |           const mode = config.customModes.find((m) => m.slug === slug);
347 |           
348 |           if (!mode) {
349 |             throw new McpError(ErrorCode.InvalidParams, `Mode not found: ${slug}`);
350 |           }
351 | 
352 |           return {
353 |             content: [
354 |               {
355 |                 type: 'text',
356 |                 text: JSON.stringify(mode, null, 2),
357 |               },
358 |             ],
359 |           };
360 |         }
361 | 
362 |         case 'create_mode': {
363 |           const mode = request.params.arguments as z.infer<typeof CustomModeSchema>;
364 |           const config = await this.readConfig();
365 |           
366 |           if (config.customModes.some((m) => m.slug === mode.slug)) {
367 |             throw new McpError(
368 |               ErrorCode.InvalidParams,
369 |               `Mode with slug "${mode.slug}" already exists`
370 |             );
371 |           }
372 | 
373 |           try {
374 |             CustomModeSchema.parse(mode);
375 |           } catch (error) {
376 |             throw new McpError(
377 |               ErrorCode.InvalidParams,
378 |               `Invalid mode configuration: ${error instanceof Error ? error.message : String(error)}`
379 |             );
380 |           }
381 | 
382 |           config.customModes.push(mode);
383 |           await this.writeConfig(config);
384 | 
385 |           return {
386 |             content: [
387 |               {
388 |                 type: 'text',
389 |                 text: `Mode "${mode.name}" created successfully`,
390 |               },
391 |             ],
392 |           };
393 |         }
394 | 
395 |         case 'update_mode': {
396 |           const { slug, updates } = request.params.arguments as {
397 |             slug: string;
398 |             updates: Partial<z.infer<typeof CustomModeSchema>>;
399 |           };
400 | 
401 |           const config = await this.readConfig();
402 |           const index = config.customModes.findIndex((m) => m.slug === slug);
403 | 
404 |           if (index === -1) {
405 |             throw new McpError(ErrorCode.InvalidParams, `Mode not found: ${slug}`);
406 |           }
407 | 
408 |           const updatedMode = {
409 |             ...config.customModes[index],
410 |             ...updates,
411 |           };
412 | 
413 |           try {
414 |             CustomModeSchema.parse(updatedMode);
415 |           } catch (error) {
416 |             throw new McpError(
417 |               ErrorCode.InvalidParams,
418 |               `Invalid mode configuration: ${error instanceof Error ? error.message : String(error)}`
419 |             );
420 |           }
421 | 
422 |           config.customModes[index] = updatedMode;
423 |           await this.writeConfig(config);
424 | 
425 |           return {
426 |             content: [
427 |               {
428 |                 type: 'text',
429 |                 text: `Mode "${updatedMode.name}" updated successfully`,
430 |               },
431 |             ],
432 |           };
433 |         }
434 | 
435 |         case 'delete_mode': {
436 |           const { slug } = request.params.arguments as { slug: string };
437 |           const config = await this.readConfig();
438 |           const index = config.customModes.findIndex((m) => m.slug === slug);
439 | 
440 |           if (index === -1) {
441 |             throw new McpError(ErrorCode.InvalidParams, `Mode not found: ${slug}`);
442 |           }
443 | 
444 |           config.customModes.splice(index, 1);
445 |           await this.writeConfig(config);
446 | 
447 |           return {
448 |             content: [
449 |               {
450 |                 type: 'text',
451 |                 text: `Mode "${slug}" deleted successfully`,
452 |               },
453 |             ],
454 |           };
455 |         }
456 | 
457 |         case 'validate_mode': {
458 |           const { mode } = request.params.arguments as {
459 |             mode: z.infer<typeof CustomModeSchema>;
460 |           };
461 | 
462 |           try {
463 |             CustomModeSchema.parse(mode);
464 |             return {
465 |               content: [
466 |                 {
467 |                   type: 'text',
468 |                   text: 'Mode configuration is valid',
469 |                 },
470 |               ],
471 |             };
472 |           } catch (error) {
473 |             return {
474 |               content: [
475 |                 {
476 |                   type: 'text',
477 |                   text: `Invalid mode configuration: ${error instanceof Error ? error.message : String(error)}`,
478 |                 },
479 |               ],
480 |               isError: true,
481 |             };
482 |           }
483 |         }
484 | 
485 |         default:
486 |           throw new McpError(
487 |             ErrorCode.MethodNotFound,
488 |             `Unknown tool: ${request.params.name}`
489 |           );
490 |       }
491 |     });
492 |   }
493 | 
494 |   /**
495 |    * Start the MCP server
496 |    */
497 |   async run() {
498 |     const transport = new StdioServerTransport();
499 |     await this.server.connect(transport);
500 |     console.error('Modes MCP server running on stdio');
501 |   }
502 | }
503 | 
504 | const server = new ModesServer();
505 | server.run().catch(console.error);
```