gatewaycompat

package
v3.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package gatewaycompat rewrites the human-readable text this server lists — tool, prompt, resource and resource-template descriptions and titles, and the description and title annotations embedded in tool schemas — according to operator-defined substitutions.

It exists because MCP gateways validate a server's catalog before admitting it, and their rules are the gateway operator's to choose: one production gateway (IBM mcp-context-forge before 0.7.0) refused any tool whose description contained a semicolon. This server keeps its own text clean of the characters known to be rejected (cmd/audit_gateway_chars gates that), but the next gateway rule is not this project's to predict. The substitution knob lets the operator comply with a rule the day they meet it, without waiting for a release.

The knob rewrites catalog metadata and nothing else: names, URIs, schema constraints (pattern, const, enum values, defaults) and tool-call payloads are never touched, because those are contract, not prose.

Index

Constants

View Source
const (
	MaxSubstitutions     = 32
	MaxSubstitutionBytes = 256
)

MaxSubstitutions and MaxSubstitutionBytes bound the configuration, and growthFactor and growthAllowance bound its effect.

The knob is the only setting that writes operator-chosen prose into the channel a model reads as instructions, and it is an ordinary environment variable rather than a flag. Bounding it keeps a compliance tool a compliance tool: a gateway rule is satisfied by a handful of short rewrites, so a configuration that needs hundreds of rules, or a paragraph per rule, is not the use this exists for.

The two effect bounds exist because a length limit per rule does not bound the result. A rule replacing a single frequent letter with 200 bytes stays well inside every configuration limit and still multiplies the served catalog. So the ceiling is relative to the text being rewritten: output may not exceed growthFactor times the input, nor the input plus growthAllowance bytes, whichever is larger. The allowance is what keeps short titles rewritable.

View Source
const EnvVar = "GITLAB_MCP_DESCRIPTION_SUBSTITUTIONS"

EnvVar configures the substitutions in both stdio and HTTP modes. The value is a comma-separated list of old=new pairs applied in order to every listed description and title. A backslash escapes a literal comma, equals sign or backslash inside either half, so a semicolon can become a comma:

GITLAB_MCP_DESCRIPTION_SUBSTITUTIONS=';=\,'

Whitespace is significant: "; = " substitutes "; " (semicolon, space) with " " (a single space). An empty or unset value disables the middleware.

Variables

This section is empty.

Functions

func Apply

func Apply(subs []Substitution, text string) string

Apply runs every substitution over text, in declared order, unless doing so would grow the text past the ceiling growthLimit sets — in which case text is returned as written.

The refusal is all-or-nothing. Applying the rules that fit would serve a third text nobody configured, and the unrewritten one is the text this project's own gateway-character audit vouches for.

Each rule's result length is computed before the rule runs, so an oversized string is never built.

func Middleware

func Middleware(subs []Substitution) mcp.Middleware

Middleware returns a receiving middleware that applies subs to the four catalog listings a gateway validates: tools/list, prompts/list, resources/list and resources/templates/list. Every other method passes through untouched — a prompt's message content or a tool call's result is payload, and rewriting payload would change what the server does rather than how it introduces itself.

Results are cloned before modification: list results return pointers shared with the server's registries, so an in-place edit would corrupt the catalog for every other session of this process.

func RewriteSchemaProse

func RewriteSchemaProse(v any, rewrite func(text string) string) bool

RewriteSchemaProse walks a decoded JSON schema value, passes every prose string — the value of a description or title keyword, however deeply nested — to rewrite, stores what it returns, and reports whether anything changed. Everything that is not prose survives verbatim: names, patterns, and the data keywords (default, const, enum, examples), whose subtrees are never entered.

It is exported for cmd/audit_gateway_chars, which scans the same strings this package rewrites: sharing the walk is what keeps "what the audit checks" and "what the knob can fix" the same set by construction.

Types

type Substitution

type Substitution struct {
	// Old is the literal text to replace. Never empty.
	Old string
	// New is the literal replacement. May be empty, which deletes Old.
	New string
}

Substitution is one ordered old→new text replacement.

func FromEnv

func FromEnv() ([]Substitution, error)

FromEnv parses EnvVar. An unset or empty variable is not an error: it returns nil, nil, and the caller installs nothing.

An active configuration is announced at WARN, once per process. A rewritten catalog is otherwise indistinguishable at runtime from an unrewritten one, which makes "the descriptions the model reads are not the ones this build ships" a fact with no local evidence.

func ParseSubstitutions

func ParseSubstitutions(value string) ([]Substitution, error)

ParseSubstitutions parses a comma-separated list of old=new pairs.

Within a pair, the first unescaped equals sign separates old from new; later unescaped equals signs in new are literal. Backslash escapes a comma, an equals sign or a backslash; any other escape is an error, because a silently absorbed typo here would ship a substitution the operator did not write. An empty old is an error for the same reason: ReplaceAll with an empty pattern inserts new between every pair of characters.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL