Explorar o código

feat: add per-repository MCP configuration support #13

Implements flexible MCP server configuration system with:
- Repository-specific configs: ~/.config/agent-manager/mcp/{owner}/{repo}.json
- Global default config: ~/.config/agent-manager/mcp/default.json
- Hardcoded fallback (backward compatible)

New Components:
- McpConfigManager class for loading/managing MCP configs
- Environment variable substitution (${VAR_NAME} syntax)
- Secure storage with 600 file permissions
- Helper methods for creating and listing configurations

Changes:
- Updated Config.generateMcpConfig() to support owner/repo parameters
- Modified claude-client.ts to pass owner/repo for config loading
- Exported McpConfigManager from main index

Documentation:
- Added MCP Configuration System section to CLAUDE.md
- Added TypeScript Client section to README.md
- Created comprehensive MCP-CONFIG.md guide
- Added example configuration files

Security:
- Config files stored outside repos in ~/.config/agent-manager/mcp/
- Directory created with 700 permissions (owner-only)
- Files written with 600 permissions
- Permission warnings for insecure files
- Environment variable substitution for secrets

Fully backward compatible - existing hardcoded config still works.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude hai 9 meses
pai
achega
5f81f5b334

+ 41 - 0
CLAUDE.md

@@ -298,9 +298,50 @@ npm run build
 - Processes issues assigned to Claude
 - Responds to issue comments automatically
 - Environment-specific configuration via `.env`
+- **Per-repository MCP configuration support** - Different repositories can use different MCP servers
 
 See `client/README.md` for detailed documentation.
 
+### MCP Configuration System
+
+The client supports flexible MCP server configuration with three priority levels:
+
+1. **Repository-specific config**: `~/.config/agent-manager/mcp/{owner}/{repo}.json`
+2. **Global default config**: `~/.config/agent-manager/mcp/default.json`
+3. **Hardcoded fallback**: Built-in gogs-mcp configuration (automatic)
+
+**Key Features**:
+- Multiple MCP servers per repository
+- Environment variable substitution using `${VAR_NAME}` syntax
+- Secure storage with restricted file permissions (600)
+- Backward compatible with existing hardcoded configuration
+
+**Configuration Structure**:
+```json
+{
+  "mcpServers": {
+    "gogs": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/data/gogs-mcp/dist/index.js"],
+      "env": {}
+    },
+    "custom-mcp": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/path/to/custom-mcp/dist/index.js"],
+      "env": {
+        "API_KEY": "${MY_API_KEY}"
+      }
+    }
+  }
+}
+```
+
+**Security**: Configuration files are stored outside repositories in `~/.config/agent-manager/mcp/` with restricted permissions to protect secrets like API keys and JWT tokens.
+
+See `examples/MCP-CONFIG.md` for detailed configuration guide and examples.
+
 ## Code Modification Guidelines
 
 **Main Server (src/)**:

+ 84 - 1
README.md

@@ -406,8 +406,21 @@ node examples/with-callbacks.js
 │   ├── commandExecutor.js # Command execution with variable substitution
 │   ├── config.js          # Configuration loader (.env and commands.json)
 │   └── logger.js          # Logging utility
+├── client/                # TypeScript-based Claude Agent SDK client
+│   ├── src/
+│   │   ├── index.ts              # CLI entry point
+│   │   ├── claude-client.ts      # Main Claude Agent SDK client
+│   │   ├── config.ts             # Configuration management
+│   │   ├── mcpConfigManager.ts   # MCP configuration manager
+│   │   └── types.ts              # TypeScript type definitions
+│   └── dist/              # Compiled JavaScript (auto-generated)
+├── scripts/
+│   └── handle-issue-ts.sh # TypeScript issue handler script
 ├── examples/
-│   └── with-callbacks.js  # Example with custom callbacks
+│   ├── with-callbacks.js         # Example with custom callbacks
+│   ├── mcp-config-default.json   # Example default MCP config
+│   ├── mcp-config-multi-server.json  # Example multi-server MCP config
+│   └── MCP-CONFIG.md             # MCP configuration guide
 ├── .env.example           # Example environment configuration
 ├── commands.json.example  # Example command configuration
 ├── .env                   # Your environment configuration (not in git)
@@ -456,6 +469,76 @@ node examples/with-callbacks.js
 - Merges environment variables with file config
 - Supports backward compatibility with legacy .env commands
 
+## TypeScript Client for Issue Automation
+
+The `client/` directory contains a TypeScript-based Claude Agent SDK client for AI-powered issue automation.
+
+### Features
+
+- **AI-Powered Issue Processing**: Automatically processes Gogs issues assigned to Claude
+- **MCP Server Integration**: Connects to MCP servers for external API access
+- **Per-Repository Configuration**: Different repositories can use different MCP servers
+- **Environment Variable Substitution**: Support for secrets using `${VAR_NAME}` syntax
+- **Secure Configuration Storage**: MCP configs stored outside repositories with restricted permissions
+
+### Quick Start
+
+```bash
+cd client
+npm install
+npm run build
+```
+
+### MCP Configuration
+
+The client supports three levels of MCP server configuration:
+
+1. **Repository-specific**: `~/.config/agent-manager/mcp/{owner}/{repo}.json`
+2. **Global default**: `~/.config/agent-manager/mcp/default.json`
+3. **Hardcoded fallback**: Built-in gogs-mcp (automatic)
+
+**Example MCP Configuration:**
+
+```json
+{
+  "mcpServers": {
+    "gogs": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/data/gogs-mcp/dist/index.js"],
+      "env": {}
+    },
+    "database": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/path/to/db-mcp/dist/index.js"],
+      "env": {
+        "DB_HOST": "${DB_HOST}",
+        "DB_PASSWORD": "${DB_PASSWORD}"
+      }
+    }
+  }
+}
+```
+
+**Key Features:**
+- Multiple MCP servers per repository
+- Environment variable substitution for secrets
+- Secure storage with 600 file permissions
+- Backward compatible with existing setup
+
+For detailed MCP configuration guide, see `examples/MCP-CONFIG.md`.
+
+### Usage
+
+The TypeScript client is automatically invoked by the webhook server when issues are assigned to Claude. It can also be run manually:
+
+```bash
+node client/dist/index.js <owner> <repo> <pusher> <issue_number> <issue_title> <issue_body> <issue_action> [comment_body] [assignee]
+```
+
+See `client/README.md` for more details.
+
 ## Testing
 
 You can test the webhook server using curl:

+ 2 - 2
client/src/claude-client.ts

@@ -139,8 +139,8 @@ NEVER leave changes uncommitted or unpushed.`;
       // Load environment configuration
       Config.load(this.config.workingDirectory);
 
-      // Generate MCP config
-      const mcpConfig = Config.generateMcpConfig(this.config.workingDirectory);
+      // Generate MCP config (with owner/repo for per-repository config support)
+      const mcpConfig = Config.generateMcpConfig(this.config.workingDirectory, issue.owner, issue.repo);
       const mcpConfigPath = join(this.config.workingDirectory, '.mcp-gogs.json');
       writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
 

+ 14 - 2
client/src/config.ts

@@ -5,6 +5,7 @@
 import { readFileSync, existsSync } from 'fs';
 import { join } from 'path';
 import { MCPServerConfig } from './types.js';
+import { McpConfigManager } from './mcpConfigManager.js';
 
 export class Config {
   private static envVars: Map<string, string> = new Map();
@@ -63,11 +64,22 @@ export class Config {
   }
 
   /**
-   * Generate MCP configuration for gogs
+   * Generate MCP configuration for a repository
+   *
+   * Priority:
+   * 1. Repository-specific config: ~/.config/agent-manager/mcp/{owner}/{repo}.json
+   * 2. Global default config: ~/.config/agent-manager/mcp/default.json
+   * 3. Hardcoded fallback (gogs-mcp only)
    */
-  static generateMcpConfig(repoPath: string): MCPServerConfig {
+  static generateMcpConfig(repoPath: string, owner?: string, repo?: string): MCPServerConfig {
     const gogsMcpPath = this.getGogsMcpPath();
 
+    // If owner and repo are provided, try to load per-repository config
+    if (owner && repo) {
+      return McpConfigManager.getMcpConfig(owner, repo, gogsMcpPath);
+    }
+
+    // Fallback to hardcoded config
     return {
       mcpServers: {
         gogs: {

+ 1 - 0
client/src/index.ts

@@ -4,6 +4,7 @@
 
 export { ClaudeClient } from './claude-client.js';
 export { Config } from './config.js';
+export { McpConfigManager } from './mcpConfigManager.js';
 export type {
   IssueData,
   ClaudeClientConfig,

+ 227 - 0
client/src/mcpConfigManager.ts

@@ -0,0 +1,227 @@
+/**
+ * MCP Configuration Manager
+ *
+ * Manages per-repository MCP server configurations with support for:
+ * - Repository-specific configurations
+ * - Global default configuration
+ * - Environment variable substitution for secrets
+ * - Secure file storage with restricted permissions
+ */
+
+import { readFileSync, existsSync, mkdirSync, writeFileSync, statSync, chmodSync } from 'fs';
+import { join, dirname } from 'path';
+import { homedir } from 'os';
+import { MCPServerConfig } from './types.js';
+
+export class McpConfigManager {
+  private static readonly CONFIG_DIR = join(homedir(), '.config', 'agent-manager', 'mcp');
+  private static readonly DEFAULT_CONFIG_FILE = 'default.json';
+
+  /**
+   * Get the configuration directory path
+   */
+  static getConfigDir(): string {
+    return this.CONFIG_DIR;
+  }
+
+  /**
+   * Get the path to a repository-specific config file
+   */
+  static getRepoConfigPath(owner: string, repo: string): string {
+    return join(this.CONFIG_DIR, owner, `${repo}.json`);
+  }
+
+  /**
+   * Get the path to the global default config file
+   */
+  static getDefaultConfigPath(): string {
+    return join(this.CONFIG_DIR, this.DEFAULT_CONFIG_FILE);
+  }
+
+  /**
+   * Initialize the configuration directory structure
+   * Creates the base directory if it doesn't exist
+   */
+  static initializeConfigDir(): void {
+    if (!existsSync(this.CONFIG_DIR)) {
+      mkdirSync(this.CONFIG_DIR, { recursive: true, mode: 0o700 });
+      console.log(`Created MCP config directory: ${this.CONFIG_DIR}`);
+    }
+  }
+
+  /**
+   * Substitute environment variables in a string
+   * Supports ${VAR_NAME} syntax
+   */
+  private static substituteEnvVars(value: string): string {
+    return value.replace(/\$\{([^}]+)\}/g, (_, varName) => {
+      return process.env[varName] || '';
+    });
+  }
+
+  /**
+   * Recursively substitute environment variables in an object
+   */
+  private static substituteEnvVarsInObject(obj: any): any {
+    if (typeof obj === 'string') {
+      return this.substituteEnvVars(obj);
+    }
+
+    if (Array.isArray(obj)) {
+      return obj.map(item => this.substituteEnvVarsInObject(item));
+    }
+
+    if (obj && typeof obj === 'object') {
+      const result: any = {};
+      for (const [key, value] of Object.entries(obj)) {
+        result[key] = this.substituteEnvVarsInObject(value);
+      }
+      return result;
+    }
+
+    return obj;
+  }
+
+  /**
+   * Load and parse a configuration file
+   */
+  private static loadConfigFile(filePath: string): MCPServerConfig | null {
+    try {
+      // Check file permissions for security
+      const stats = statSync(filePath);
+      const mode = stats.mode & 0o777;
+
+      // Warn if file is readable by others (group or world)
+      if (mode & 0o077) {
+        console.warn(`Warning: MCP config file ${filePath} has insecure permissions (${mode.toString(8)}). Should be 600 or 400.`);
+      }
+
+      const content = readFileSync(filePath, 'utf-8');
+      const config = JSON.parse(content) as MCPServerConfig;
+
+      // Substitute environment variables in the config
+      const substituted = this.substituteEnvVarsInObject(config);
+
+      return substituted;
+    } catch (error) {
+      if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
+        return null; // File doesn't exist
+      }
+      console.error(`Error loading MCP config from ${filePath}:`, error);
+      return null;
+    }
+  }
+
+  /**
+   * Get MCP configuration for a specific repository
+   *
+   * Priority:
+   * 1. Repository-specific config: ~/.config/agent-manager/mcp/{owner}/{repo}.json
+   * 2. Global default config: ~/.config/agent-manager/mcp/default.json
+   * 3. Hardcoded fallback (current behavior)
+   */
+  static getMcpConfig(owner: string, repo: string, gogsMcpPath: string): MCPServerConfig {
+    // Ensure config directory exists
+    this.initializeConfigDir();
+
+    // Try repository-specific config
+    const repoConfigPath = this.getRepoConfigPath(owner, repo);
+    const repoConfig = this.loadConfigFile(repoConfigPath);
+
+    if (repoConfig) {
+      console.log(`Using repository-specific MCP config: ${repoConfigPath}`);
+      return repoConfig;
+    }
+
+    // Try global default config
+    const defaultConfigPath = this.getDefaultConfigPath();
+    const defaultConfig = this.loadConfigFile(defaultConfigPath);
+
+    if (defaultConfig) {
+      console.log(`Using global default MCP config: ${defaultConfigPath}`);
+      return defaultConfig;
+    }
+
+    // Fallback to hardcoded config
+    console.log('Using hardcoded default MCP config (gogs-mcp only)');
+    return {
+      mcpServers: {
+        gogs: {
+          type: 'stdio',
+          command: 'node',
+          args: [gogsMcpPath],
+          env: {}
+        }
+      }
+    };
+  }
+
+  /**
+   * Create a repository-specific configuration file
+   * This is a helper method for setting up new configurations
+   */
+  static createRepoConfig(owner: string, repo: string, config: MCPServerConfig): void {
+    const configPath = this.getRepoConfigPath(owner, repo);
+    const configDir = dirname(configPath);
+
+    // Create owner directory if it doesn't exist
+    if (!existsSync(configDir)) {
+      mkdirSync(configDir, { recursive: true, mode: 0o700 });
+    }
+
+    // Write config file
+    writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 0o600 });
+
+    console.log(`Created repository config: ${configPath}`);
+  }
+
+  /**
+   * Create the global default configuration file
+   */
+  static createDefaultConfig(config: MCPServerConfig): void {
+    this.initializeConfigDir();
+
+    const configPath = this.getDefaultConfigPath();
+    writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 0o600 });
+
+    console.log(`Created default MCP config: ${configPath}`);
+  }
+
+  /**
+   * List all repository-specific configurations
+   */
+  static listRepoConfigs(): Array<{ owner: string; repo: string; path: string }> {
+    const configs: Array<{ owner: string; repo: string; path: string }> = [];
+
+    if (!existsSync(this.CONFIG_DIR)) {
+      return configs;
+    }
+
+    try {
+      const { readdirSync } = require('fs');
+      const owners = readdirSync(this.CONFIG_DIR, { withFileTypes: true })
+        .filter((dirent: any) => dirent.isDirectory())
+        .map((dirent: any) => dirent.name);
+
+      for (const owner of owners) {
+        const ownerDir = join(this.CONFIG_DIR, owner);
+        const files = readdirSync(ownerDir, { withFileTypes: true })
+          .filter((dirent: any) => dirent.isFile() && dirent.name.endsWith('.json'))
+          .map((dirent: any) => dirent.name);
+
+        for (const file of files) {
+          const repo = file.replace(/\.json$/, '');
+          configs.push({
+            owner,
+            repo,
+            path: join(ownerDir, file)
+          });
+        }
+      }
+    } catch (error) {
+      console.error('Error listing repo configs:', error);
+    }
+
+    return configs;
+  }
+}

+ 309 - 0
examples/MCP-CONFIG.md

@@ -0,0 +1,309 @@
+# MCP Configuration Guide
+
+This document explains how to configure per-repository MCP (Model Context Protocol) servers for the Agent Manager.
+
+## Overview
+
+The Agent Manager supports three levels of MCP configuration:
+
+1. **Repository-specific configuration** - Unique MCP servers for each repository
+2. **Global default configuration** - Shared MCP servers for all repositories
+3. **Hardcoded fallback** - Built-in gogs-mcp server (automatic)
+
+## Configuration Priority
+
+When processing an issue, the system looks for MCP configuration in this order:
+
+1. `~/.config/agent-manager/mcp/{owner}/{repo}.json` - Repository-specific
+2. `~/.config/agent-manager/mcp/default.json` - Global default
+3. Hardcoded fallback (gogs-mcp only)
+
+## Configuration Directory
+
+All MCP configuration files are stored in:
+
+```
+~/.config/agent-manager/mcp/
+├── default.json                    # Global default config
+├── fszontagh/
+│   ├── agent-manager.json         # Config for fszontagh/agent-manager
+│   └── my-project.json            # Config for fszontagh/my-project
+└── otheruser/
+    └── their-repo.json            # Config for otheruser/their-repo
+```
+
+**Security Note:** Configuration files should have restrictive permissions (600 or 400) since they may contain secrets like API keys or JWT tokens.
+
+## Configuration File Format
+
+MCP configuration files use JSON format:
+
+```json
+{
+  "mcpServers": {
+    "server-name": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/path/to/mcp-server/dist/index.js"],
+      "env": {
+        "ENV_VAR": "value"
+      }
+    }
+  }
+}
+```
+
+### Server Types
+
+- `stdio` - Standard input/output communication (most common)
+- `http` - HTTP-based communication
+- `sse` - Server-Sent Events communication
+
+### Environment Variable Substitution
+
+Configuration files support `${VAR_NAME}` syntax for environment variables. This allows you to keep secrets out of the config files:
+
+```json
+{
+  "mcpServers": {
+    "api-server": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/path/to/api-mcp/dist/index.js"],
+      "env": {
+        "API_KEY": "${MY_API_KEY}",
+        "API_URL": "https://api.example.com"
+      }
+    }
+  }
+}
+```
+
+The `${MY_API_KEY}` will be replaced with the value from `process.env.MY_API_KEY` at runtime.
+
+## Example Configurations
+
+### Example 1: Global Default (All Repositories)
+
+File: `~/.config/agent-manager/mcp/default.json`
+
+```json
+{
+  "mcpServers": {
+    "gogs": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/data/gogs-mcp/dist/index.js"],
+      "env": {}
+    }
+  }
+}
+```
+
+### Example 2: Repository-Specific with Multiple Servers
+
+File: `~/.config/agent-manager/mcp/fszontagh/my-app.json`
+
+```json
+{
+  "mcpServers": {
+    "gogs": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/data/gogs-mcp/dist/index.js"],
+      "env": {}
+    },
+    "database": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/data/database-mcp/dist/index.js"],
+      "env": {
+        "DB_HOST": "${DB_HOST}",
+        "DB_USER": "${DB_USER}",
+        "DB_PASSWORD": "${DB_PASSWORD}",
+        "DB_NAME": "myapp"
+      }
+    },
+    "slack": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/data/slack-mcp/dist/index.js"],
+      "env": {
+        "SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}",
+        "SLACK_CHANNEL": "#deployments"
+      }
+    }
+  }
+}
+```
+
+### Example 3: Different Configs for Different Projects
+
+**Project A** (uses Gogs + PostgreSQL):
+```json
+{
+  "mcpServers": {
+    "gogs": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/data/gogs-mcp/dist/index.js"],
+      "env": {}
+    },
+    "postgres": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/data/postgres-mcp/dist/index.js"],
+      "env": {
+        "PGHOST": "${PGHOST}",
+        "PGUSER": "${PGUSER}",
+        "PGPASSWORD": "${PGPASSWORD}"
+      }
+    }
+  }
+}
+```
+
+**Project B** (uses Gogs + MongoDB + Redis):
+```json
+{
+  "mcpServers": {
+    "gogs": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/data/gogs-mcp/dist/index.js"],
+      "env": {}
+    },
+    "mongodb": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/data/mongodb-mcp/dist/index.js"],
+      "env": {
+        "MONGO_URI": "${MONGO_URI}"
+      }
+    },
+    "redis": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/data/redis-mcp/dist/index.js"],
+      "env": {
+        "REDIS_URL": "${REDIS_URL}"
+      }
+    }
+  }
+}
+```
+
+## Creating Configuration Files
+
+### Method 1: Manual Creation
+
+Create the directory structure and file:
+
+```bash
+mkdir -p ~/.config/agent-manager/mcp/owner
+nano ~/.config/agent-manager/mcp/owner/repo.json
+chmod 600 ~/.config/agent-manager/mcp/owner/repo.json
+```
+
+### Method 2: Using McpConfigManager API
+
+```typescript
+import { McpConfigManager } from './client/dist/index.js';
+
+// Create global default config
+McpConfigManager.createDefaultConfig({
+  mcpServers: {
+    gogs: {
+      type: 'stdio',
+      command: 'node',
+      args: ['/data/gogs-mcp/dist/index.js'],
+      env: {}
+    }
+  }
+});
+
+// Create repository-specific config
+McpConfigManager.createRepoConfig('owner', 'repo', {
+  mcpServers: {
+    gogs: {
+      type: 'stdio',
+      command: 'node',
+      args: ['/data/gogs-mcp/dist/index.js'],
+      env: {}
+    },
+    custom: {
+      type: 'stdio',
+      command: 'node',
+      args: ['/path/to/custom-mcp/dist/index.js'],
+      env: {
+        'API_KEY': process.env.API_KEY
+      }
+    }
+  }
+});
+```
+
+## Security Best Practices
+
+1. **File Permissions**: Always set configuration files to 600 (owner read/write only)
+   ```bash
+   chmod 600 ~/.config/agent-manager/mcp/owner/repo.json
+   ```
+
+2. **Use Environment Variables**: Never hardcode secrets in config files. Use `${VAR_NAME}` syntax instead.
+
+3. **Restrict Directory Access**: The MCP config directory should be readable only by the user running agent-manager:
+   ```bash
+   chmod 700 ~/.config/agent-manager/mcp
+   ```
+
+4. **Keep Secrets in .env**: Store actual secret values in the agent-manager `.env` file or system environment variables.
+
+## Troubleshooting
+
+### Configuration Not Loading
+
+Check the logs when an issue is processed. You should see one of:
+- `Using repository-specific MCP config: ~/.config/agent-manager/mcp/owner/repo.json`
+- `Using global default MCP config: ~/.config/agent-manager/mcp/default.json`
+- `Using hardcoded default MCP config (gogs-mcp only)`
+
+### Permission Warnings
+
+If you see:
+```
+Warning: MCP config file has insecure permissions (644). Should be 600 or 400.
+```
+
+Fix with:
+```bash
+chmod 600 ~/.config/agent-manager/mcp/owner/repo.json
+```
+
+### Environment Variables Not Substituted
+
+Ensure the environment variable is set in the agent-manager process environment:
+
+```bash
+# In .env or systemd service file
+export MY_API_KEY="secret-value"
+```
+
+Then restart the agent-manager service.
+
+## Examples
+
+Example configuration files are provided in `examples/`:
+
+- `mcp-config-default.json` - Simple single-server config
+- `mcp-config-multi-server.json` - Multiple servers with env var substitution
+
+## Migration from Hardcoded Config
+
+If you're currently using the default hardcoded gogs-mcp configuration, you can:
+
+1. **Keep using the default** - No action needed, it will continue to work
+2. **Create a global default** - Copy `examples/mcp-config-default.json` to `~/.config/agent-manager/mcp/default.json`
+3. **Create repo-specific configs** - For repositories that need custom MCP servers
+
+The system is fully backward compatible with the existing behavior.

+ 10 - 0
examples/mcp-config-default.json

@@ -0,0 +1,10 @@
+{
+  "mcpServers": {
+    "gogs": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/data/gogs-mcp/dist/index.js"],
+      "env": {}
+    }
+  }
+}

+ 30 - 0
examples/mcp-config-multi-server.json

@@ -0,0 +1,30 @@
+{
+  "mcpServers": {
+    "gogs": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/data/gogs-mcp/dist/index.js"],
+      "env": {}
+    },
+    "custom-api": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/path/to/custom-mcp-server/dist/index.js"],
+      "env": {
+        "API_KEY": "${CUSTOM_API_KEY}",
+        "API_URL": "https://api.example.com"
+      }
+    },
+    "database": {
+      "type": "stdio",
+      "command": "node",
+      "args": ["/path/to/database-mcp/dist/index.js"],
+      "env": {
+        "DB_HOST": "${DB_HOST}",
+        "DB_USER": "${DB_USER}",
+        "DB_PASSWORD": "${DB_PASSWORD}",
+        "DB_NAME": "myapp"
+      }
+    }
+  }
+}