The pctx.json file defines your MCP server aggregation, authentication, and runtime configuration.
By default, pctx looks for ./pctx.json in the current working directory.
Override with the --config flag:
pctx --config /path/to/config.json startInitialize a new configuration:
pctx initThis creates a basic pctx.json and prompts you to add upstream MCP servers.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string |
Yes | - | Name of your MCP server instance |
version |
string |
Yes | "0.1.0" |
Version of your MCP server |
description |
string |
No | - | Optional description of your MCP server |
disclosure |
ToolDisclosure |
No | "catalog" |
Tool disclosure mode (see below) |
servers |
array[ServerConfig] |
Yes | - | List of upstream MCP server configurations (see below) |
logger |
LoggerConfig |
No | - | Logger configuration (see below) |
telemetry |
TelemetryConfig |
No | - | OpenTelemetry configuration (see below) |
The disclosure field controls which set of code-mode tools are exposed to the AI agent. It determines how the agent discovers and invokes upstream tools.
| Value | Default | Description |
|---|---|---|
"catalog" |
Yes | Agent uses list_tools → get_tool_details → execute_typescript to discover and call tools |
"filesystem" |
No | Agent uses execute_bash → execute_typescript; tool details are read from the filesystem |
"sidecar" |
No | Upstream tool descriptions are surfaced directly; agent calls execute_typescript to invoke them |
Example:
{
"disclosure": "filesystem"
}Each server in the servers array is either an HTTP server or a stdio server.
HTTP server fields:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Unique identifier used as TypeScript namespace |
url |
string |
Yes | HTTP(S) URL of the MCP server endpoint |
auth |
AuthConfig |
No | Authentication configuration (see below) |
Stdio server fields:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Unique identifier used as TypeScript namespace |
command |
string |
Yes | Command to execute the MCP server. Can be a single command or a full command line with arguments |
args |
array[string] |
No | Arguments passed to the command. If omitted and command contains spaces, it will be shell-parsed |
env |
map[string]string |
No | Environment variables for the process |
Examples (stdio):
With explicit args array:
{
"name": "local_tools",
"command": "node",
"args": ["./dist/server.js"],
"env": {
"NODE_ENV": "development"
}
}With command-line string (auto-parsed):
{
"name": "memory",
"command": "npx -y @modelcontextprotocol/server-memory"
}The second format is convenient for simple commands - the full command line is automatically parsed into command and arguments.
The name will be case converted to camelCase and used as the TypeScript namespace for accessing that server's tools:
// Server name: "g_drive"
await gDrive.getSheet({ sheetId: "abc" });
// Server name: "slack"
await slack.sendMessage({ channel: "#general", text: "hi" });Requirements:
- Must be unique within the configuration
- Should be a valid TypeScript identifier (alphanumeric, underscores, no spaces) to avoid clashes after case conversion
- Keep it short and descriptive
The auth field supports two types of authentication BearerToken | Custom:
| Field | Type | Required | Description |
|---|---|---|---|
type |
"bearer" |
Yes | Constant designating this object as a bearer token config |
token |
SecretString |
Yes | Secret string value (see below for syntax) of the bearer token. Bearer prefix is added automatically |
Example:
{
"type": "bearer",
"token": "${env:API_TOKEN}"
}This adds an Authorization: Bearer <token> header to all requests.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"headers" |
Yes | Constant designating this object as a headers config |
headers |
map[string]SecretString |
Yes | Map of header name to Secret string value (see below for syntax) |
Example:
{
"type": "headers",
"headers": {
"x-api-key": "${env:API_KEY}",
"x-custom-header": "static-value"
}
}Use this for API key authentication or any custom header requirements.
The optional logger field controls logging behavior for the pctx server MPC server. This configuration applies
to pctx start and MCP server modes; other commands like pctx add use the CLI verbosity controls (-v/-vv/-q).
Logs always write to stderr to keep stdout clean for JSON-RPC traffic; stdout is reserved for JSON-RPC responses.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
enabled |
boolean |
No | true |
Enable or disable logging |
level |
LogLevel |
No | "info" |
Minimum log level to display (see levels below) |
format |
LoggerFormat |
No | "compact" |
Output format for log messages (see formats below) |
colors |
boolean |
No | true |
Enable or disable colorized output |
Valid values for level (in order of increasing severity):
"trace"- Most verbose, shows all logs including detailed execution traces"debug"- Detailed debugging information"info"- General informational messages (default)"warn"- Warning messages for potentially problematic situations"error"- Error messages only
Valid values for format:
"compact"- Condensed single-line format (default)"pretty"- Human-readable multi-line format with indentation"json"- Structured JSON format for log aggregation tools
Minimal logging (errors only):
{
"logger": {
"level": "error"
}
}Debug mode with pretty formatting:
{
"logger": {
"enabled": true,
"level": "debug",
"format": "pretty",
"colors": true
}
}JSON logging for production (no colors):
{
"logger": {
"level": "info",
"format": "json",
"colors": false
}
}Disable logging completely:
{
"logger": {
"enabled": false
}
}The optional telemetry field enables OpenTelemetry (OTLP) integration for distributed tracing and metrics collection. This allows you to observe and monitor the behavior of your MCP server and its interactions with upstream servers.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
traces |
TracesConfig |
No | - | Distributed tracing configuration |
metrics |
MetricsConfig |
No | - | Metrics collection configuration |
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
enabled |
boolean |
No | false |
Enable or disable trace collection |
exporters |
array[ExporterConfig] |
No | [] |
List of OTLP trace exporters (see below) |
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
enabled |
boolean |
No | false |
Enable or disable metrics collection |
exporters |
array[ExporterConfig] |
No | [] |
List of OTLP metrics exporters (see below) |
Each exporter in the exporters array has the following fields:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string |
Yes | - | Identifier for this exporter |
url |
string |
Yes | - | OTLP endpoint URL (see protocol-specific format below) |
protocol |
"http" | "grpc" |
Yes | - | Protocol to use for OTLP export |
timeout |
number |
No | 10000 |
Request timeout in milliseconds |
auth |
AuthConfig |
No | - | Authentication configuration (see below) |
The auth field supports multiple authentication methods:
Bearer Token Authentication:
| Field | Type | Required | Description |
|---|---|---|---|
type |
"bearer" |
Yes | Authentication type |
token |
SecretString |
Yes | Bearer token (supports secret string syntax) |
Basic Authentication:
| Field | Type | Required | Description |
|---|---|---|---|
type |
"basic" |
Yes | Authentication type |
username |
SecretString |
Yes | Username (supports secret string syntax) |
password |
SecretString |
Yes | Password (supports secret string syntax) |
Custom Headers:
| Field | Type | Required | Description |
|---|---|---|---|
type |
"headers" |
Yes | Authentication type |
headers |
map[string]SecretString |
Yes | Custom headers (support secret string syntax) |
HTTP Protocol:
- For traces: Include the full path including
/v1/traces - For metrics: Include the full path including
/v1/metrics - Example:
http://localhost:4318/v1/traces
gRPC Protocol:
- Use the base URL without path
- Example:
http://localhost:4317
Basic tracing configuration:
{
"telemetry": {
"traces": {
"enabled": true,
"exporters": [
{
"name": "tempo",
"url": "http://localhost:4318/v1/traces",
"protocol": "http"
}
]
}
}
}Traces and metrics with gRPC:
{
"telemetry": {
"traces": {
"enabled": true,
"exporters": [
{
"name": "otlp-traces",
"url": "http://localhost:4317",
"protocol": "grpc"
}
]
},
"metrics": {
"enabled": true,
"exporters": [
{
"name": "otlp-metrics",
"url": "http://localhost:4317",
"protocol": "grpc"
}
]
}
}
}With bearer token authentication:
{
"telemetry": {
"traces": {
"enabled": true,
"exporters": [
{
"name": "grafana-cloud",
"url": "https://otlp-gateway.grafana.net/otlp/v1/traces",
"protocol": "http",
"auth": {
"type": "bearer",
"token": "${env:GRAFANA_CLOUD_TOKEN}"
}
}
]
}
}
}With basic authentication:
{
"telemetry": {
"traces": {
"enabled": true,
"exporters": [
{
"name": "secure-collector",
"url": "https://collector.example.com/v1/traces",
"protocol": "http",
"auth": {
"type": "basic",
"username": "${env:OTEL_USERNAME}",
"password": "${env:OTEL_PASSWORD}"
}
}
]
}
}
}With custom headers:
{
"telemetry": {
"traces": {
"enabled": true,
"exporters": [
{
"name": "custom-collector",
"url": "https://collector.example.com/v1/traces",
"protocol": "http",
"auth": {
"type": "headers",
"headers": {
"X-API-Key": "${env:OTEL_API_KEY}",
"X-Custom-Header": "custom-value"
}
}
}
]
}
}
}Multiple exporters:
{
"telemetry": {
"traces": {
"enabled": true,
"exporters": [
{
"name": "local-tempo",
"url": "http://localhost:4318/v1/traces",
"protocol": "http"
},
{
"name": "production-collector",
"url": "https://otel-collector.company.com:4317",
"protocol": "grpc",
"timeout": 5000,
"auth": {
"type": "headers",
"headers": {
"x-api-key": "${keychain:otel-api-key}"
}
}
}
]
}
}
}For a complete example with OpenTelemetry Collector, Tempo, Prometheus, and Grafana, see the telemetry example.
Both token and header values support a secret string syntax for secure credential management.
Format: ${env:VARIABLE_NAME}
{
"token": "${env:MCP_API_TOKEN}"
}The value is read from the environment variable at runtime.
Example:
export MCP_API_TOKEN="sk_test_123"
pctx startFormat: ${keychain:KEY_NAME}
{
"token": "${keychain:mcp-api-key}"
}Reads from your OS keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service).
Format: ${command:shell command}
{
"token": "${command:aws secretsmanager get-secret-value --secret-id my-token --query SecretString --output text}"
}Executes the command and uses its stdout as the value (whitespace is trimmed).
Use cases:
- AWS Secrets Manager
- Azure Key Vault
- HashiCorp Vault
- 1Password CLI
- Pass (password store)
- Custom secret management scripts
Examples:
AWS Secrets Manager:
{
"type": "bearer",
"token": "${command:aws secretsmanager get-secret-value --secret-id mcp-token --query SecretString --output text}"
}1Password CLI:
{
"type": "bearer",
"token": "${command:op read op://vault/item/field}"
}Secret strings support interpolation with multiple parts:
{
"headers": {
"authorization": "ApiKey ${keychain:api-key}",
"x-custom": "prefix-${env:SUFFIX}"
}
}You can use plain text values, but this is not recommended for production:
{
"token": "sk_test_hardcoded_token"
}Warning: Never commit credentials to version control. Use secret strings instead.
{
"name": "my-ai-agent",
"version": "1.0.0",
"description": "MCP server aggregation for my AI agent",
"logger": {
"enabled": true,
"level": "info",
"format": "compact",
"colors": true
},
"telemetry": {
"traces": {
"enabled": true,
"exporters": [
{
"name": "tempo",
"url": "http://localhost:4318/v1/traces",
"protocol": "http"
}
]
},
"metrics": {
"enabled": true,
"exporters": [
{
"name": "prometheus",
"url": "http://localhost:4318/v1/metrics",
"protocol": "http"
}
]
}
},
"servers": [
{
"name": "stripe",
"url": "https://mcp.stripe.com",
"auth": {
"type": "bearer",
"token": "${env:STRIPE_MCP_KEY}"
}
},
{
"name": "gdrive",
"url": "https://mcp.gdrive.example.com",
"auth": {
"type": "headers",
"headers": {
"x-api-key": "${keychain:gdrive-api-key}"
}
}
},
{
"name": "internal",
"url": "https://internal-mcp.company.com",
"auth": {
"type": "bearer",
"token": "${command:vault kv get -field=token secret/mcp}"
}
},
{
"name": "public",
"url": "https://public-mcp.example.com"
},
{
"name": "memory",
"command": "npx -y @modelcontextprotocol/server-memory"
},
{
"name": "local_tools",
"command": "node",
"args": ["./dist/server.js"],
"env": {
"NODE_ENV": "production"
}
}
]
}The server configurations can be added, removed, and listed via the CLI, see CLI Docs for details.
Check:
- URL is correct and accessible
- Server is running
- Network/firewall allows the connection
The server returned 401/403. Add authentication:
pctx add my-server https://mcp.example.com \
--bearer '${env:TOKEN}'When starting with pctx mcp start --stdio, a missing or unreadable config file
returns a JSON-RPC error on stdout and then exits. Ensure pctx.json exists or
pass the correct path with -c.
The specified environment variable isn't set:
# Check what's needed
cat pctx.json | grep env:
# Set the variable
export API_TOKEN="your-token"
# Or add to .env and source it
echo "API_TOKEN=your-token" >> .env
source .envThe keychain entry doesn't exist. Create it:
# macOS
security add-generic-password -s pctx -a my-key -w "my-value"Or use a different secret method.
The external command returned non-zero exit or empty output:
# Test the command directly
aws secretsmanager get-secret-value --secret-id my-token
# Check authentication for the tool
aws sts get-caller-identityWhen configuring stdio servers, you have two options:
Option 1: Shell-style command string (auto-parsed)
{
"name": "memory",
"command": "npx -y @modelcontextprotocol/server-memory"
}The command is automatically parsed into executable and arguments using shell-style parsing.
Option 2: Explicit command and args
{
"name": "memory",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"]
}Use explicit args when:
- Your command has complex quoting requirements
- You want to be explicit about argument boundaries
- You're programmatically generating the configuration
Note: If both command contains spaces AND args is provided, the args array takes precedence and no parsing occurs.