Skip to main content

Configuration & Arguments

Learn how to configure your MCP server using command-line arguments and environment variables from the client configuration.

How Configuration Works​

When you configure an MCP server in Claude Desktop (or other MCP clients), you can pass arguments and environment variables:

{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["/path/to/server.js", "--api-key", "secret123", "--debug"],
"env": {
"DATABASE_URL": "postgresql://localhost/mydb",
"LOG_LEVEL": "debug"
}
}
}
}

Your server receives:

  • Command-line arguments via process.argv
  • Environment variables via process.env

Parsing Command-Line Arguments​

Simple Manual Parsing​

import 'reflect-metadata';
import { createServer, MCPServer, Tool, Param } from '@mcpkit-dev/core';

function parseArgs() {
const args = process.argv.slice(2);
const config: Record<string, string | boolean> = {};

for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg.startsWith('--')) {
const key = arg.slice(2);
const nextArg = args[i + 1];
if (nextArg && !nextArg.startsWith('--')) {
config[key] = nextArg;
i++;
} else {
config[key] = true;
}
}
}
return config;
}

const config = parseArgs();

@MCPServer({
name: 'my-server',
version: '1.0.0',
})
class MyServer {
private apiKey = config['api-key'] as string;

@Tool({ description: 'Call external API' })
async callApi(@Param({ name: 'query' }) query: string) {
return `Called API with key: ${this.apiKey ? '***' : 'none'}`;
}
}

const server = createServer(MyServer);
await server.listen();

Using Constructor Parameters​

For better encapsulation, pass configuration through the constructor:

import 'reflect-metadata';
import { createServer, MCPServer, Tool, Param } from '@mcpkit-dev/core';

interface ServerConfig {
apiKey: string;
debug: boolean;
baseUrl?: string;
}

@MCPServer({
name: 'my-server',
version: '1.0.0',
})
class MyServer {
constructor(private config: ServerConfig) {
if (config.debug) {
console.error('[DEBUG] Server initialized with config');
}
}

@Tool({ description: 'Fetch data from API' })
async fetchData(@Param({ name: 'endpoint' }) endpoint: string) {
const url = `${this.config.baseUrl ?? 'https://api.example.com'}/${endpoint}`;
// Use this.config.apiKey for authentication
return { url, authenticated: !!this.config.apiKey };
}

@Tool({ description: 'Get current config (redacted)' })
async getConfig() {
return {
apiKey: this.config.apiKey ? '***' : 'not set',
debug: this.config.debug,
baseUrl: this.config.baseUrl ?? 'default',
};
}
}

// Parse arguments
const args = process.argv.slice(2);
const config: ServerConfig = {
apiKey: '',
debug: false,
};

for (let i = 0; i < args.length; i++) {
switch (args[i]) {
case '--api-key':
config.apiKey = args[++i];
break;
case '--debug':
config.debug = true;
break;
case '--base-url':
config.baseUrl = args[++i];
break;
}
}

// Pass config to createServer
const server = createServer(MyServer, config);
await server.listen();

Using Commander.js​

For more complex argument parsing, use a library like Commander:

npm install commander
import 'reflect-metadata';
import { createServer, MCPServer, Tool, Param } from '@mcpkit-dev/core';
import { program } from 'commander';

program
.option('--api-key <key>', 'API key for external service')
.option('--debug', 'Enable debug mode', false)
.option('--base-url <url>', 'Base URL for API', 'https://api.example.com')
.option('--timeout <ms>', 'Request timeout in milliseconds', '5000')
.parse();

const options = program.opts();

@MCPServer({
name: 'my-server',
version: '1.0.0',
})
class MyServer {
@Tool({ description: 'Check server configuration' })
async checkConfig() {
return {
debug: options.debug,
baseUrl: options.baseUrl,
timeout: parseInt(options.timeout),
hasApiKey: !!options.apiKey,
};
}
}

const server = createServer(MyServer);
await server.listen();

Environment Variables​

Environment variables are often preferred for secrets since they don't appear in process listings.

Client Configuration​

{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["/path/to/server.js"],
"env": {
"API_KEY": "secret123",
"DATABASE_URL": "postgresql://localhost/mydb",
"DEBUG": "true",
"LOG_LEVEL": "info"
}
}
}
}

Server Implementation​

import 'reflect-metadata';
import { createServer, MCPServer, Tool, Param } from '@mcpkit-dev/core';

interface Config {
apiKey: string;
databaseUrl: string;
debug: boolean;
logLevel: 'debug' | 'info' | 'warn' | 'error';
}

function loadConfig(): Config {
return {
apiKey: process.env.API_KEY ?? '',
databaseUrl: process.env.DATABASE_URL ?? '',
debug: process.env.DEBUG === 'true',
logLevel: (process.env.LOG_LEVEL as Config['logLevel']) ?? 'info',
};
}

const config = loadConfig();

// Validate required config
if (!config.apiKey) {
console.error('ERROR: API_KEY environment variable is required');
process.exit(1);
}

@MCPServer({
name: 'my-server',
version: '1.0.0',
})
class MyServer {
@Tool({ description: 'Query the database' })
async queryDatabase(@Param({ name: 'query' }) query: string) {
if (config.debug) {
console.error(`[DEBUG] Executing query: ${query}`);
}
// Use config.databaseUrl to connect
return { success: true, query };
}
}

const server = createServer(MyServer);
await server.listen();

Combining Arguments and Environment Variables​

A common pattern is to use environment variables as defaults that can be overridden by command-line arguments:

import 'reflect-metadata';
import { createServer, MCPServer, Tool } from '@mcpkit-dev/core';

interface Config {
apiKey: string;
baseUrl: string;
debug: boolean;
maxRetries: number;
}

function loadConfig(): Config {
const args = process.argv.slice(2);

// Start with environment variables as defaults
const config: Config = {
apiKey: process.env.API_KEY ?? '',
baseUrl: process.env.BASE_URL ?? 'https://api.example.com',
debug: process.env.DEBUG === 'true',
maxRetries: parseInt(process.env.MAX_RETRIES ?? '3'),
};

// Override with command-line arguments
for (let i = 0; i < args.length; i++) {
switch (args[i]) {
case '--api-key':
config.apiKey = args[++i];
break;
case '--base-url':
config.baseUrl = args[++i];
break;
case '--debug':
config.debug = true;
break;
case '--max-retries':
config.maxRetries = parseInt(args[++i]);
break;
}
}

return config;
}

const config = loadConfig();

@MCPServer({
name: 'my-server',
version: '1.0.0',
})
class MyServer {
@Tool({ description: 'Show current configuration' })
async showConfig() {
return {
baseUrl: config.baseUrl,
debug: config.debug,
maxRetries: config.maxRetries,
hasApiKey: !!config.apiKey,
};
}
}

const server = createServer(MyServer);
await server.listen();

Configuration with Validation​

Use Zod to validate your configuration:

import 'reflect-metadata';
import { createServer, MCPServer, Tool } from '@mcpkit-dev/core';
import { z } from 'zod';

const ConfigSchema = z.object({
apiKey: z.string().min(1, 'API_KEY is required'),
baseUrl: z.string().url().default('https://api.example.com'),
debug: z.boolean().default(false),
maxRetries: z.number().int().min(0).max(10).default(3),
timeout: z.number().int().min(1000).max(30000).default(5000),
});

type Config = z.infer<typeof ConfigSchema>;

function loadConfig(): Config {
const raw = {
apiKey: process.env.API_KEY,
baseUrl: process.env.BASE_URL,
debug: process.env.DEBUG === 'true',
maxRetries: process.env.MAX_RETRIES ? parseInt(process.env.MAX_RETRIES) : undefined,
timeout: process.env.TIMEOUT ? parseInt(process.env.TIMEOUT) : undefined,
};

const result = ConfigSchema.safeParse(raw);

if (!result.success) {
console.error('Configuration error:');
result.error.issues.forEach(issue => {
console.error(` - ${issue.path.join('.')}: ${issue.message}`);
});
process.exit(1);
}

return result.data;
}

const config = loadConfig();

@MCPServer({
name: 'my-server',
version: '1.0.0',
})
class MyServer {
@Tool({ description: 'Get API status' })
async getStatus() {
return { configured: true, baseUrl: config.baseUrl };
}
}

const server = createServer(MyServer);
await server.listen();

Security Best Practices​

  1. Use environment variables for secrets - They don't appear in ps output
  2. Never log sensitive values - Redact API keys in logs
  3. Validate configuration on startup - Fail fast with clear error messages
  4. Use default values wisely - Don't default sensitive values
  5. Document required configuration - Help users configure correctly
// Good: Validate and fail fast
if (!config.apiKey) {
console.error('ERROR: API_KEY is required. Set it in your MCP client config:');
console.error(' "env": { "API_KEY": "your-key-here" }');
process.exit(1);
}

// Good: Redact in logs
console.error(`Connecting with API key: ${config.apiKey.slice(0, 4)}***`);

// Bad: Never do this
console.error(`API key: ${config.apiKey}`);