Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions docs/public/schemas/mcp-gateway-config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -298,12 +298,21 @@
"minLength": 1
},
"headers": {
"type": "object",
"description": "Additional HTTP headers sent with every OTLP export request (e.g., for authentication). Values may contain variable expressions using '${VARIABLE_NAME}' syntax.",
"additionalProperties": {
"type": "string"
},
"default": {}
"description": "Additional HTTP headers sent with every OTLP export request (e.g., for authentication). Accepts either an object mapping header names to values, or a multi-line string where each line uses 'name=value' syntax (splitting on the first '=' character). Values may contain variable expressions using '${VARIABLE_NAME}' syntax.",
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The opentelemetry.headers schema change removed the previous default: {} annotation. If the published schema is consumed by generators/docs that use defaults, consider adding "default": {} at the headers property level to preserve prior behavior and match other object properties in the schema.

Suggested change
"description": "Additional HTTP headers sent with every OTLP export request (e.g., for authentication). Accepts either an object mapping header names to values, or a multi-line string where each line uses 'name=value' syntax (splitting on the first '=' character). Values may contain variable expressions using '${VARIABLE_NAME}' syntax.",
"description": "Additional HTTP headers sent with every OTLP export request (e.g., for authentication). Accepts either an object mapping header names to values, or a multi-line string where each line uses 'name=value' syntax (splitting on the first '=' character). Values may contain variable expressions using '${VARIABLE_NAME}' syntax.",
"default": {},

Copilot uses AI. Check for mistakes.
"oneOf": [
{
"type": "object",
"description": "Object form: keys are header names, values are header values.",
"additionalProperties": {
"type": "string"
}
},
{
"type": "string",
"description": "String form: each line is 'name=value'. Lines not containing '=' are ignored. Leading and trailing whitespace on names and values is trimmed.",
"minLength": 1
}
]
},
"traceId": {
"type": "string",
Expand Down
81 changes: 76 additions & 5 deletions docs/src/content/docs/reference/mcp-gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ sidebar:

# MCP Gateway Specification

**Version**: 1.11.0
**Version**: 1.12.0
**Status**: Draft Specification
**Latest Version**: [mcp-gateway](/gh-aw/reference/mcp-gateway/)
**JSON Schema**: [mcp-gateway-config.schema.json](/gh-aw/schemas/mcp-gateway-config.schema.json)
Expand Down Expand Up @@ -458,12 +458,12 @@ The optional `opentelemetry` object in the gateway configuration enables the gat
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `endpoint` | string | Yes (when `opentelemetry` is present) | OTLP/HTTP endpoint URL for the OpenTelemetry collector (e.g., `https://collector.example.com:4318/v1/traces`). MUST use HTTPS. Supports variable expressions. |
| `headers` | object | No | Additional HTTP headers sent with every export request to the collector endpoint. Commonly used for authentication (e.g., `Authorization: "Bearer ${OTEL_TOKEN}"`). Values MAY contain variable expressions. |
| `headers` | object \| string | No | Additional HTTP headers sent with every export request to the collector endpoint. Commonly used for authentication (e.g., `Authorization: "Bearer ${OTEL_TOKEN}"`). Accepts either an **object** mapping header names to string values, or a **multi-line string** where each line uses `name=value` syntax. Values MAY contain variable expressions. See Section 4.1.3.6.1 for details. |
| `traceId` | string | No | Parent trace ID for context propagation. When set, the gateway attaches all emitted spans as children of this trace, enabling correlation with an existing distributed trace. MUST be a 32-character lowercase hex string (128-bit W3C trace ID format). Supports variable expressions. |
| `spanId` | string | No | Parent span ID for context propagation. When set together with `traceId`, the gateway sets this span as the direct parent of its root span. MUST be a 16-character lowercase hex string (64-bit W3C span ID format). Ignored when `traceId` is not set. Supports variable expressions. |
| `serviceName` | string | No | Logical service name reported in the `service.name` resource attribute of all emitted spans. Identifies the gateway in the tracing backend. Defaults to `"mcp-gateway"` when not specified. |

**Configuration Example**:
**Configuration Example (object form)**:

```json
{
Expand All @@ -484,6 +484,23 @@ The optional `opentelemetry` object in the gateway configuration enables the gat
}
```

**Configuration Example (string form)**:

```json
{
"gateway": {
"port": 8080,
"domain": "localhost",
"apiKey": "${MCP_GATEWAY_API_KEY}",
"opentelemetry": {
"endpoint": "https://collector.example.com:4318/v1/traces",
"headers": "Authorization=Bearer ${OTEL_TOKEN}\nX-Custom-Header=value",
"serviceName": "my-mcp-gateway"
}
}
}
```

**Tracing Behavior**:

When `opentelemetry` is configured, the gateway MUST:
Expand Down Expand Up @@ -513,8 +530,48 @@ The gateway MUST NOT fail to start if the OpenTelemetry collector endpoint is un
- `spanId`, when provided, MUST be a 16-character lowercase hex string
- `spanId` SHOULD only be set when `traceId` is also set; if `spanId` is provided without `traceId` the gateway SHOULD log a warning and ignore `spanId`
- Export failures MUST NOT propagate errors to MCP clients
- When `headers` is provided as a string, the gateway MUST parse it as described in Section 4.1.3.6.1

**Compliance Test**: T-OTEL-001 through T-OTEL-012 (Section 10.1.10)

##### 4.1.3.6.1 Headers String Format

**Compliance Test**: T-OTEL-001 through T-OTEL-009 (Section 10.1.10)
The `headers` field in the `opentelemetry` configuration MAY be specified as a multi-line string using HTTP header-like `name=value` syntax as an alternative to the object form.

**Parsing Rules**:

The implementation MUST apply the following rules when parsing a `headers` string:

1. Split the string into lines using newline (`\n`) and/or carriage-return-newline (`\r\n`) as line separators
2. For each line, locate the first `=` character; the text before it is the header name and the text after it is the header value
3. Trim leading and trailing ASCII whitespace from both the header name and the header value
4. Lines that do not contain an `=` character MUST be ignored
5. Lines where the trimmed header name is empty MUST be ignored
6. Variable expressions (`${VARIABLE_NAME}`) in values MUST be expanded in the same manner as object-form values

**Examples**:

The following string form:

```
Authorization=Bearer ${OTEL_TOKEN}
X-Custom-Header=value
```

is semantically equivalent to the object form:

```json
{
"Authorization": "Bearer ${OTEL_TOKEN}",
"X-Custom-Header": "value"
}
```

**Notes**:

- Header names are case-sensitive in the string form; implementations SHOULD preserve the casing as provided
- Values may contain additional `=` characters; only the first `=` on each line acts as the name/value separator
- Duplicate header names within a single string: the last occurrence MUST take precedence
Comment on lines +572 to +574
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The note says header names are case-sensitive in the string form. HTTP field names are case-insensitive (RFC 9110), so this is likely to confuse implementers and can make duplicate-handling differ based on casing (e.g., Authorization vs authorization). Consider clarifying that casing should be preserved when emitting requests, but comparisons for duplicates/merging should be performed case-insensitively (or explicitly justify deviating from HTTP semantics).

Suggested change
- Header names are case-sensitive in the string form; implementations SHOULD preserve the casing as provided
- Values may contain additional `=` characters; only the first `=` on each line acts as the name/value separator
- Duplicate header names within a single string: the last occurrence MUST take precedence
- Header names are case-insensitive for comparison, duplicate detection, and merging, consistent with HTTP field-name semantics; implementations SHOULD preserve the casing as provided when emitting requests or retaining source fidelity
- Values may contain additional `=` characters; only the first `=` on each line acts as the name/value separator
- Duplicate header names within a single string are determined case-insensitively; the last occurrence MUST take precedence

Copilot uses AI. Check for mistakes.

#### 4.1.3a Top-Level Configuration Fields

Expand Down Expand Up @@ -1456,11 +1513,13 @@ A conforming implementation MUST pass the following test categories:
- **T-OTEL-003**: Reject `opentelemetry` configuration with missing `endpoint` field
- **T-OTEL-004**: Reject `opentelemetry` configuration with a non-HTTPS endpoint
- **T-OTEL-005**: Span emitted for each MCP tool invocation with required attributes (`mcp.server`, `mcp.method`, `mcp.tool`, `http.status_code`)
- **T-OTEL-006**: Configured `headers` are sent with every OTLP export request
- **T-OTEL-006**: Configured `headers` (object form) are sent with every OTLP export request
- **T-OTEL-007**: W3C `traceparent` context propagated when both `traceId` and `spanId` are configured
- **T-OTEL-008**: Gateway generates random `spanId` in `traceparent` when only `traceId` is provided
- **T-OTEL-009**: Export failure does not affect MCP request processing or gateway availability
- **T-OTEL-010**: `serviceName` is reflected in `service.name` resource attribute of emitted spans
- **T-OTEL-011**: Configured `headers` (string form) are correctly parsed and sent with every OTLP export request
- **T-OTEL-012**: Lines without `=` in string-form `headers` are ignored; lines with empty trimmed name are ignored; duplicate header names use the last occurrence

### 10.2 Compliance Checklist

Expand Down Expand Up @@ -1817,6 +1876,18 @@ Content-Type: application/json

## Change Log

### Version 1.12.0 (Draft)

- **Changed**: `headers` field in `opentelemetry` configuration now accepts two forms (Section 4.1.3.6)
- **Object form** (existing): keys are header names, values are header values — fully backward-compatible
- **String form** (new): a multi-line string where each line uses `name=value` syntax, providing a concise alternative for simple header lists
- **Added**: Section 4.1.3.6.1 — Headers String Format
- Parsing rules for `name=value` lines: split on first `=`, trim whitespace, skip lines without `=` or with empty name, last occurrence wins for duplicate names
- Equivalence example showing string form vs object form
- Notes on case sensitivity and duplicate name handling
- **Added**: Compliance tests T-OTEL-011 and T-OTEL-012 for string-form header parsing
- **Updated**: JSON Schema `opentelemetryConfig.headers` changed from a plain `object` type to `oneOf [object, string]`

### Version 1.11.0 (Draft)

- **Added**: `opentelemetry` field to gateway configuration (Section 4.1.3, 4.1.3.6)
Expand Down
21 changes: 15 additions & 6 deletions pkg/workflow/schemas/mcp-gateway-config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,21 @@
"minLength": 1
},
"headers": {
"type": "object",
"description": "Additional HTTP headers sent with every OTLP export request (e.g., for authentication). Values may contain variable expressions using '${VARIABLE_NAME}' syntax.",
"additionalProperties": {
"type": "string"
},
"default": {}
"description": "Additional HTTP headers sent with every OTLP export request (e.g., for authentication). Accepts either an object mapping header names to values, or a multi-line string where each line uses 'name=value' syntax (splitting on the first '=' character). Values may contain variable expressions using '${VARIABLE_NAME}' syntax.",
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The schema previously annotated opentelemetry.headers with a default: {}; after switching to oneOf, that default annotation is now missing. If tooling relies on schema defaults for generated configs/docs (and to stay consistent with other headers/env objects in this schema), consider restoring "default": {} at the headers property level (it can still coexist with oneOf).

Suggested change
"description": "Additional HTTP headers sent with every OTLP export request (e.g., for authentication). Accepts either an object mapping header names to values, or a multi-line string where each line uses 'name=value' syntax (splitting on the first '=' character). Values may contain variable expressions using '${VARIABLE_NAME}' syntax.",
"description": "Additional HTTP headers sent with every OTLP export request (e.g., for authentication). Accepts either an object mapping header names to values, or a multi-line string where each line uses 'name=value' syntax (splitting on the first '=' character). Values may contain variable expressions using '${VARIABLE_NAME}' syntax.",
"default": {},

Copilot uses AI. Check for mistakes.
"oneOf": [
{
"type": "object",
"description": "Object form: keys are header names, values are header values.",
"additionalProperties": {
"type": "string"
}
},
{
"type": "string",
"description": "String form: each line is 'name=value'. Lines not containing '=' are ignored. Leading and trailing whitespace on names and values is trimmed.",
"minLength": 1
}
]
},
"traceId": {
"type": "string",
Expand Down
Loading