Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | 2x 2x 21x 2x 27x 27x 27x 27x 27x 27x 1x 27x 2x 7x 7x 7x 2x 6x 6x 18x 6x 2x 1x 3x 1x 2x 5x 5x 1x 4x 1x 1x 3x 2x 1x 2x 3x 3x 3x 3x | /**
* Configuration generator for various MCP clients
*/
import { StdioServerConfig, WizardConfig, MCP_CLIENT_INFO, ROLE_PRESETS } from './types';
import { CODEX_SERVER_NAME, codexConfigPreview } from '../install/codex';
/**
* Escape a string for safe use in shell double-quoted context.
* Escapes: $ ` " \ and newlines
*/
function shellEscape(value: string): string {
return value
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\$/g, '\\$')
.replace(/`/g, '\\`')
.replace(/\n/g, '\\n');
}
/**
* Generate MCP server configuration object
*/
export function generateServerConfig(config: WizardConfig): StdioServerConfig {
// Normalize instance URL to avoid double-appending /api/v4 later
const normalizedUrl = config.instanceUrl.replace(/\/+$/, '').replace(/\/api\/v4$/i, '');
const env: Record<string, string> = {
GITLAB_API_URL: normalizedUrl,
GITLAB_TOKEN: config.token,
};
// Add preset if mapped from role
const presetName = ROLE_PRESETS[config.role];
Eif (presetName) {
env.GITLAB_MCP_PRESET = presetName;
}
// Add read-only mode if selected
if (config.readOnly) {
env.GITLAB_READ_ONLY_MODE = 'true';
}
return {
command: 'npx',
args: ['-y', '@structured-world/gitlab-mcp@latest'],
env,
};
}
/**
* Generate JSON configuration for mcpServers format (most clients)
*/
export function generateMcpServersJson(config: WizardConfig, serverName = 'gitlab'): string {
const serverConfig = generateServerConfig(config);
const mcpServers = {
mcpServers: {
[serverName]: serverConfig,
},
};
return JSON.stringify(mcpServers, null, 2);
}
/**
* Generate Claude Code CLI command
*/
export function generateClaudeCodeCommand(config: WizardConfig, serverName = 'gitlab'): string {
const serverConfig = generateServerConfig(config);
// Build environment variable flags with proper shell escaping
const envFlags = Object.entries(serverConfig.env)
.map(([key, value]) => `--env ${key}="${shellEscape(value)}"`)
.join(' ');
// claude mcp add <name> <command> [args...] --env KEY=VALUE
return `claude mcp add ${serverName} ${serverConfig.command} ${serverConfig.args.join(' ')} ${envFlags}`;
}
/** `codex mcp add` command for a local server, shell-escaped like the Claude Code one. */
export function generateCodexCommand(
serverConfig: StdioServerConfig,
serverName = CODEX_SERVER_NAME,
): string {
const envFlags = Object.entries(serverConfig.env).map(
([key, value]) => `--env ${key}="${shellEscape(value)}" `,
);
return `codex mcp add ${serverName} ${envFlags.join('')}-- ${serverConfig.command} ${serverConfig.args.join(' ')}`;
}
/**
* Generate configuration based on client type
*/
export function generateClientConfig(config: WizardConfig): {
type: 'json' | 'cli' | 'instructions';
content: string;
configPath?: string;
cliCommand?: string;
} {
const clientInfo = MCP_CLIENT_INFO[config.client];
// Claude Code supports CLI installation
if (config.client === 'claude-code') {
return {
type: 'cli',
content: generateMcpServersJson(config),
cliCommand: generateClaudeCodeCommand(config),
configPath: clientInfo.configPath,
};
}
// Codex keeps TOML; its CLI writes the entry, the preview shows the result.
if (config.client === 'codex') {
const serverConfig = generateServerConfig(config);
return {
type: 'cli',
content: codexConfigPreview(serverConfig),
cliCommand: generateCodexCommand(serverConfig),
configPath: clientInfo.configPath,
};
}
// Most clients use mcpServers JSON format
if (config.client !== 'generic') {
return {
type: 'json',
content: generateMcpServersJson(config),
configPath: clientInfo.configPath,
};
}
// Generic: just provide the server config
return {
type: 'instructions',
content: generateMcpServersJson(config),
};
}
/**
* Generate Claude Deep Link for one-click installation
* Format: claude://settings/mcp/add?config=BASE64
*
* Uses URL-safe Base64 encoding to handle characters that may be
* problematic in query strings (+, /, =)
*/
export function generateClaudeDeepLink(config: WizardConfig, serverName = 'gitlab'): string {
const serverConfig = generateServerConfig(config);
const configObject = {
name: serverName,
...serverConfig,
};
// URL-safe Base64: replace + with -, / with _, and remove = padding
const base64Config = Buffer.from(JSON.stringify(configObject))
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
return `claude://settings/mcp/add?config=${base64Config}`;
}
|