backend

package
v0.48.0 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

Documentation

Overview

Package backend defines the Session interface for a single persistent backend connection and provides the HTTP-based implementation used in production. It is internal to pkg/vmcp/session.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewHTTPConnector

func NewHTTPConnector(registry vmcpauth.OutgoingAuthRegistry, opts ...HTTPConnectorOption) func(
	ctx context.Context,
	target *vmcp.BackendTarget,
	identity *auth.Identity,
	sessionHint string,
	sink ListChangedSink,
) (Session, *vmcp.CapabilityList, error)

NewHTTPConnector returns a function that creates an HTTP-based (streamable-HTTP or SSE) persistent backend Session for each backend.

registry provides the authentication strategy for outgoing backend requests. Pass a registry configured with the "unauthenticated" strategy to disable auth.

A single secrets.EnvironmentProvider is constructed once per connector and shared across every session it creates; its lifetime matches the connector's. It is consumed by BuildHeaderForwardTripper to resolve secret-backed entries in target.HeaderForward.

The returned function's sink parameter, when non-nil, enables persistent backend-notification consumption for this backend connection — see createMCPClient for what that does and does not enable (nil-sink callers are completely unaffected: no OnNotification handler is registered and no standalone GET stream is opened).

Types

type ChangeKind added in v0.41.0

type ChangeKind string

ChangeKind identifies which capability class a backend reported changed via ListChangedSink. Using a typed constant (rather than a bare string) means a typo is a compile error at the producer and consumer instead of a silent no-op.

const (
	// KindTools is reported when a backend emits notifications/tools/list_changed.
	KindTools ChangeKind = "tools"

	// KindResources is reported when a backend emits
	// notifications/resources/list_changed. Per MCP 2025-11-25 there is no
	// separate wire method for resource TEMPLATE changes, so this kind also
	// covers a resync of the backend's resource templates (see
	// resyncSessionResources in pkg/vmcp/server).
	KindResources ChangeKind = "resources"

	// KindPrompts is reported when a backend emits
	// notifications/prompts/list_changed.
	KindPrompts ChangeKind = "prompts"
)

type HTTPConnectorOption added in v0.45.0

type HTTPConnectorOption func(*httpConnectorConfig)

HTTPConnectorOption configures the persistent HTTP backend connector.

func WithDialControl added in v0.48.0

func WithDialControl(control func(network, address string, c syscall.RawConn) error) HTTPConnectorOption

WithDialControl installs a per-connection Control hook on the dialer used to open every backend connection at session init (the MCP handshake and capability listing). The hook fires after DNS resolution and before the TCP handshake, receiving the resolved peer IP in address — which is why it defeats DNS-rebinding attacks that a host-name–based check cannot: a hostname can legitimately resolve to a blocked IP after the name-based check passes.

It is the session-init twin of pkg/vmcp/client.WithDialControl (which guards the aggregation and tool-call paths); the two share the same signature and the same standard 30 s dial timeouts. A nil control (the default) leaves the dial path byte-for-byte identical to before this hook existed.

The signature matches net.Dialer.Control exactly.

Security limitations embedders must understand:

  • Per-TCP-dial, not per-request: the hook fires once per TCP connection. A pooled connection is reused without re-invoking the hook until it is recycled. Because each backend gets its own isolated transport and connection pool, a reused connection is always one this hook already approved on its first dial — reuse cannot reach an unclassified peer. This connector does not offer per-request re-classification.
  • Proxy transparency: when http.ProxyFromEnvironment selects a proxy (HTTP_PROXY/HTTPS_PROXY set), the dial target is the proxy server, so the hook receives the proxy's IP, not the backend's. Embedders relying on this hook for SSRF or IP allow-listing must either unset the proxy env vars or additionally validate the backend URL's host before dialing.
  • Both IP families: the address argument may be an IPv4 or IPv6 literal (host:port form); embedders must handle both — including IPv4-mapped IPv6 such as ::ffff:127.0.0.1 — in their check. See the OWASP SSRF Prevention Cheat Sheet for the full set of ranges to deny (loopback, RFC 1918, link-local 169.254/16, CGNAT 100.64/10, IPv6 ULA).

func WithRequestTimeoutResolver added in v0.45.0

func WithRequestTimeoutResolver(resolver func(workloadID string) time.Duration) HTTPConnectorOption

WithRequestTimeoutResolver configures the timeout used for each backend operation. The resolver receives the backend workload ID and may return a workload-specific duration. A nil resolver, or a non-positive result, uses the 30-second default.

The resolver may be called concurrently and must therefore be safe for concurrent use. SSE connection lifetimes remain unbounded, but individual operations on those connections are bounded through request contexts.

type ListChangedSink added in v0.41.0

type ListChangedSink func(ctx context.Context, backendWorkloadID string, kind ChangeKind)

ListChangedSink is invoked when a persistent backend connection observes a notification this package consumes asynchronously (outside any in-flight call): notifications/tools/list_changed (kind=KindTools), notifications/resources/list_changed (kind=KindResources, which also covers resource templates — MCP 2025-11-25 has no separate wire method for those), and notifications/prompts/list_changed (kind=KindPrompts).

The sink is invoked on the mcpcompat client's receive-loop goroutine (see Client.dispatch in toolhive-core), so implementations MUST be non-blocking: they must hand the work off (e.g. set a dirty flag / signal a worker) and return immediately. A sink that does real work (network I/O, cache purges) inline stalls that backend's entire notification delivery and lets a misbehaving backend amplify one notification into unbounded work. The ctx passed here is only for the hand-off; long-lived resync work must run under a caller-owned, cancellable context, not this one.

type Session

type Session interface {
	// CallTool invokes toolName on the backend.
	// arguments contains the tool input parameters.
	// meta contains protocol-level metadata (_meta) forwarded from the client.
	CallTool(
		ctx context.Context,
		toolName string,
		arguments map[string]any,
		meta map[string]any,
	) (*vmcp.ToolCallResult, error)

	// ReadResource retrieves the resource identified by uri from the backend.
	ReadResource(ctx context.Context, uri string) (*vmcp.ResourceReadResult, error)

	// GetPrompt retrieves the named prompt from the backend.
	// arguments contains the prompt input parameters.
	GetPrompt(
		ctx context.Context,
		name string,
		arguments map[string]any,
	) (*vmcp.PromptGetResult, error)

	// Close releases all resources held by this session. Implementations must
	// be idempotent: calling Close multiple times returns nil.
	Close() error

	// SessionID returns the backend-assigned session ID (if any).
	// Returns "" if the backend did not assign a session ID.
	SessionID() string
}

Session abstracts a persistent, initialised MCP connection to a single backend server. It is created once per backend during session creation and reused for the lifetime of the parent MultiSession.

Each Session is bound to exactly one backend at creation time — callers do not need to pass a routing target to individual method calls.

Caller validation happens at the MultiSession level, not here. These methods perform the actual I/O operations without authentication checks.

Implementations must be safe for concurrent use.

Jump to

Keyboard shortcuts

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