# 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);
```