edge

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jun 17, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package edge is the client-facing side of the gateway.

It serves the Streamable HTTP MCP endpoint and guards it: every request is first validated for a permitted Origin (a DNS-rebinding defense, design §8) and then authenticated by a static API key resolved to a client identity (ADR-0003). The request guard is ordinary net/http middleware so it composes in front of the MCP handler; on success it carries the authenticated domain.Identity forward in the request context, and on failure it rejects at the door without invoking the wrapped handler.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IdentityFromContext

func IdentityFromContext(ctx context.Context) (domain.Identity, bool)

IdentityFromContext returns the authenticated identity the request guard placed in ctx, and whether one was present. Handlers downstream of Guard.Wrap use it to attribute a request to its client.

func NewHandler

func NewHandler(cfg Config) http.Handler

NewHandler builds the client-facing http.Handler: the request guard (Origin/DNS-rebinding validation + API-key authentication) wrapping a per-connection Streamable HTTP MCP server.

Types

type ClientKey

type ClientKey struct {
	// ID is the client identity (for example "claude-desktop").
	ID string
	// Key is the secret API-key value that authenticates that identity.
	Key string
}

ClientKey binds a configured client identity to its resolved API-key value. The composition root resolves each client's api_key_env to a value at startup and hands the pairs here; this package never reads environment itself.

type Config

type Config struct {
	// ServerName and ServerVersion identify the gateway to clients during the MCP
	// initialize handshake.
	ServerName    string
	ServerVersion string
	// Guard authenticates and Origin-checks every request before it reaches the
	// MCP handler. It is required: NewHandler wraps the MCP handler with it.
	Guard *Guard
	// NewSession builds the per-connection Session for an authenticated identity.
	NewSession SessionFactory
	// SessionTimeout closes a client session that has received no requests for this
	// long, which also reclaims its downstream sessions. Zero leaves idle sessions
	// open (the registry idle reaper still reclaims downstream subprocesses).
	SessionTimeout time.Duration
}

Config wires the client-facing MCP server.

type Guard

type Guard struct {
	// contains filtered or unexported fields
}

Guard is the edge request guard: Origin/DNS-rebinding validation followed by constant-time API-key authentication. It is safe for concurrent use, and its allowed origins and client keys are hot-reloadable: Reload swaps them atomically behind a read-write mutex so a SIGHUP config reload never drops a request in flight (design §5).

func NewGuard

func NewGuard(allowedOrigins []string, clients []ClientKey) *Guard

NewGuard builds a request guard from the permitted Origins and the resolved client keys. An Origin entry may end in "*" to match any Origin sharing the prefix before it (for example "vscode-webview://*"); other entries match exactly. The inputs are copied so later mutation by the caller cannot change the guard's decisions.

func (*Guard) Reload

func (g *Guard) Reload(allowedOrigins []string, clients []ClientKey)

Reload atomically replaces the guard's allowed origins and client keys for a SIGHUP/file-watch config reload (design §5). Requests already in flight finish against whichever snapshot they read; subsequent requests see the new one. It is safe to call concurrently with request handling.

func (*Guard) Wrap

func (g *Guard) Wrap(next http.Handler) http.Handler

Wrap returns a handler that applies the guard before delegating to next. A request with a disallowed Origin is rejected with 403; a request without a valid Bearer API key is rejected with 401 (and a WWW-Authenticate challenge). Only a request that passes both checks reaches next, with the authenticated identity injected into its context.

type Session

type Session interface {
	// ListTools returns one page of the client's namespaced, policy-filtered
	// catalog plus the cursor for the next page ("" when the page is the last).
	// The cursor is opaque and is carried to and from the client verbatim.
	ListTools(ctx context.Context, cursor string) (page domain.Catalog, next string, err error)
	// Call resolves a namespaced tool name and runs the invocation through the
	// security pipeline, returning the downstream result. The result's Content is
	// the downstream CallToolResult as (possibly redacted) JSON bytes. It returns a
	// non-nil result or a non-nil error, never both nil.
	Call(ctx context.Context, name string, args json.RawMessage) (*domain.Result, error)
	// Close releases the connection's downstream sessions. It is called once, when
	// the client's MCP session ends.
	Close() error
}

Session is one client connection's view of the gateway: the policy-filtered tool catalog it may see and the pipeline-guarded invocation of those tools. The composition root supplies an implementation per connection (composing a per-connection dispatcher, the security stage chain, aggregation, and policy); this package translates between that implementation and the MCP wire, and so imports neither registry, aggregate, nor policy (ADR-0014).

type SessionFactory

type SessionFactory func(domain.Identity) (Session, error)

SessionFactory builds a Session for an authenticated client identity. It is called once per client connection, after the request guard has authenticated the identity.

Jump to

Keyboard shortcuts

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