src/web/mcp/mcp-web-client/README.md
An all-in-one proxy server and web client for interacting with Netdata's Model Context Protocol (MCP) server using various LLM providers. The proxy server handles API key management, serves the web interface, and provides comprehensive usage accounting.
For a production setup, install the LLM proxy server as a system service:
# Clone or download the repository
cd /path/to/mcp-web-client
# Run the installation script (requires root)
sudo ./install.sh
This will:
/opt/llm-proxyllm-proxy/opt/llm-proxy/logsAfter installation:
Create your configuration:
sudo nano /opt/llm-proxy/llm-proxy-config.json
(The service will create a template on first run)
Start the service:
sudo systemctl start llm-proxy
sudo systemctl enable llm-proxy # Enable auto-start on boot
Check status and logs:
sudo systemctl status llm-proxy
sudo journalctl -u llm-proxy -f
Access the web interface at: http://localhost:8081
For development or testing, you can run directly from the source directory:
cd /path/to/mcp-web-client
node llm-proxy.js
Configuration will be created in the current directory as llm-proxy-config.json.
Logs will be written to ./logs/.
The proxy server is the single entry point that:
The proxy server needs a configuration file with your API keys. The location depends on how you run it:
/opt/llm-proxy/llm-proxy-config.json./llm-proxy-config.json (current directory)On first run without a config file, the server will create a template and exit.
Edit the configuration file to add your API keys:
{
"port": 8081,
"allowedOrigins": "*",
"providers": {
"openai": {
"apiKey": "sk-YOUR-OPENAI-KEY",
"models": [
// Models are automatically populated from built-in definitions
]
},
"anthropic": {
"apiKey": "sk-ant-YOUR-ANTHROPIC-KEY",
"models": [
// Models are automatically populated from built-in definitions
]
},
"google": {
"apiKey": "YOUR-GOOGLE-AI-KEY",
"models": [
// Models are automatically populated from built-in definitions
]
},
"ollama": {
"endpoint": "http://localhost:11434",
"models": [
// Models are automatically discovered from your Ollama installation
]
}
}
}
Start the service (system installation) or run directly (development):
# For system service:
sudo systemctl restart llm-proxy
# For development:
node llm-proxy.js
You should see output like:
============================================================
LLM Proxy Server & MCP Web Client
============================================================
š Server Started Successfully!
============================================================
š Available Services:
⢠Web UI: http://localhost:8081/
⢠Models API: http://localhost:8081/models
⢠Proxy Endpoint: http://localhost:8081/proxy/<provider>/<path>
š Accounting:
⢠Log directory: /opt/llm-proxy/logs (or ./logs for development)
⢠Today's log: llm-accounting-2024-01-15.jsonl
⢠Format: JSON Lines (JSONL)
The web client is served directly by the proxy server. Simply open:
http://localhost:8081/
Or if accessing remotely:
http://YOUR_SERVER_IP:8081/
Click the settings icon (āļø) in the bottom left
ws://localhost:19999/ws/mcp?api_key=YOUR_API_KEY
http://localhost:8081)node llm-proxy.js [options]
Options:
--help, -h Show help message
--show-models Display all configured models with pricing and status
--update-config Update configuration with latest model definitions
--sync Sync configuration with built-in MODEL_DEFINITIONS
CRITICAL: The proxy server enforces strict validation of all model configurations to prevent silent failures and ensure accurate cost tracking.
The proxy server uses model information from two sources:
Configuration file (llm-proxy-config.json):
Built-in MODEL_DEFINITIONS (in llm-proxy.js):
NEW: The proxy server now enforces strict validation of all model configurations. Models that don't meet the requirements are automatically rejected and will not be available for use.
Each provider has specific pricing field requirements:
input and output pricing. Must NOT have cacheRead or cacheWrite.input, output, and cacheRead pricing. Must NOT have cacheWrite.input, output, cacheRead, and cacheWrite.All models must also have:
id (string)contextWindow (positive number)pricing object with the required fields/models endpoint.ā Configuration validation errors:
- openai model "gpt-4": Invalid pricing.cacheRead: must be a number >= 0
- google model "gemini-pro": Invalid pricing: Google models should not have cacheRead
- anthropic model "claude-3": Missing required fields (cacheWrite)
The --show-models command helps you understand the state of your configuration:
node llm-proxy.js --show-models
Output shows a status column for each model:
Example output:
š¢ OPENAI
---------------------------------------------------
Model ID Status Context Input $/MTok Output $/MTok
gpt-4o same 128000 $2.00 $8.00
gpt-4-turbo different 128000 $10.00 $30.00
custom-model not in code 8192 $5.00 $10.00
gpt-4o-mini not in config 128000 $0.40 $1.60
IMPORTANT: All models must follow the strict validation requirements listed above.
Edit your configuration file to manage your models:
{
"providers": {
"openai": {
"apiKey": "sk-...",
"models": [
{
"id": "gpt-4o",
"contextWindow": 128000,
"pricing": {
"input": 2.50, // Update when prices change
"output": 10.00,
"cacheRead": 0.625 // Required for OpenAI models
}
},
// Add new models here - must follow validation rules
{
"id": "my-custom-openai-model",
"contextWindow": 100000,
"pricing": {
"input": 5.00,
"output": 20.00,
"cacheRead": 5.00 // Required for OpenAI models
}
}
]
}
}
}
When providers change their pricing:
pricing object for affected modelsThe --sync command overwrites your model list with built-in definitions:
node llm-proxy.js --update-config --sync
Warning: This will:
Use --sync only when you want to reset to defaults or after major updates.
For some providers, the proxy can fetch available models directly:
/v1/models endpoint/v1/models endpoint/api/tags endpoint (discovers all locally installed models)To check which models are actually available with your API key:
node llm-proxy.js --update-config --sync --check-availability
For Ollama, the proxy automatically discovers all models installed locally and makes them available through the web interface. Model names with multiple colons (e.g., "llama3.3:latest", "hermes3:70b") are fully supported.
You can configure multiple instances of the same provider type with different settings:
{
"providers": {
"openai-gpt4": {
"apiKey": "sk-YOUR-API-KEY",
"type": "openai",
"baseUrl": "https://api.openai.com",
"models": [
{
"id": "gpt-4o",
"contextWindow": 128000,
"pricing": { "input": 2.50, "output": 10.00, "cacheRead": 0.625 }
}
]
},
"openai-o1": {
"apiKey": "sk-YOUR-API-KEY",
"type": "openai",
"baseUrl": "https://api.openai.com",
"models": [
{
"id": "o1-mini",
"contextWindow": 128000,
"endpoint": "responses",
"supportsTools": false,
"pricing": { "input": 3.00, "output": 12.00, "cacheRead": 0.75 }
}
]
},
"ollama-local": {
"type": "ollama",
"endpoint": "http://localhost:11434",
"models": []
},
"ollama-remote": {
"type": "ollama",
"endpoint": "http://remote-server:11434",
"models": []
}
}
}
This allows you to:
Each model can have custom settings:
{
"id": "o1-mini",
"contextWindow": 128000,
"endpoint": "responses", // Use /v1/responses instead of /v1/chat/completions
"supportsTools": false, // Disable tool/function calling for this model
"pricing": {
"input": 3.00,
"output": 12.00,
"cacheRead": 0.75
}
}
Model Configuration Options:
endpoint: Specify which API endpoint to use ("responses" for o1/o3 models)supportsTools: Enable/disable native tool calling (MCP tools)
true: Model supports function/tool calling (default for most models)false: Disable tools for models that don't support them (e.g., o1 series)Note: The web client automatically handles these configurations:
supportsTools: falseModel pricing is stored in the built-in MODEL_DEFINITIONS and includes:
All pricing in MODEL_DEFINITIONS is manually maintained based on official provider pricing:
Note: The proxy does NOT fetch pricing from provider APIs as this information is not available programmatically. Prices must be updated manually in the code when providers change their rates.
To ensure accurate cost tracking:
The accounting logs ONLY use pricing from your configuration file. With strict validation enabled, models without proper pricing information are automatically rejected and cannot be used. This ensures you have full control and awareness of all pricing used in your system, and prevents requests to improperly configured models.
You can configure default MCP servers that will be automatically available in the web client:
{
"mcpServers": [
{
"id": "local_netdata",
"name": "Local Netdata",
"url": "ws://localhost:19999/mcp?api_key=YOUR_API_KEY"
},
{
"id": "remote_server",
"name": "Remote Server",
"url": "ws://remote.example.com:19999/mcp?api_key=YOUR_API_KEY"
}
]
}
These servers will be automatically loaded when users access the web client. Users can still add additional servers through the UI, which are stored in their browser's localStorage.
Note: If no MCP servers are configured, the proxy will automatically provide a default server pointing to ws://localhost:19999/mcp to ensure the web client can start properly. You'll need to configure the API key through the UI or add your own servers to the configuration.
The proxy server provides:
GET / - Serves the web client interface (index.html)GET /*.js, GET /*.css - Serves web client static filesGET /models - Returns available providers and models with pricing (JSON API)GET /mcp-servers - Returns configured MCP servers (JSON API)POST /proxy/<provider>/<api-path> - Proxies requests to LLM providersAll LLM requests are logged to:
/opt/llm-proxy/logs/llm-accounting-YYYY-MM-DD.jsonl./logs/llm-accounting-YYYY-MM-DD.jsonlLog format:
{
"timestamp": "2024-01-15T10:30:00Z",
"clientIp": "127.0.0.1",
"provider": "openai",
"model": "gpt-4",
"endpoint": "/v1/chat/completions",
"statusCode": 200,
"duration": 1523,
"requestBytes": 1024,
"responseBytes": 2048,
"decompressedBytes": 8192,
"tokens": {
"prompt": 150,
"completion": 50,
"cachedRead": 0,
"cacheCreation": 0
},
"unitPricing": {
"input": 30,
"output": 60,
"cacheRead": 30,
"cacheWrite": 30
},
"costs": {
"input": 0.0045,
"output": 0.003,
"cacheRead": 0,
"cacheWrite": 0
},
"totalCost": 0.0075
}
Status codes:
200-299: Successful responses400-599: HTTP errors from provider0: Network/connection failureallowedOrigins in production for better securityllm-proxy user with restricted permissionssudo journalctl -u llm-proxy -e./logs directory is writablenode llm-proxy.js)http://localhost:8081)node llm-proxy.js --sync --update-config to sync models (development)node llm-proxy.js --show-models to compare config vs code definitions/opt/llm-proxy/logs permissions./logs directory exists and is writable/tmp/llm-accounting-backup.jsonl