mcp

package
v0.0.0-...-891f58f Latest Latest
Warning

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

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

Documentation

Overview

Package mcp exposes a subset of Authorizer's gRPC methods as MCP tools. Which methods are exposed is declared at the proto layer via the custom option `authorizer.v1.mcp_tool` — the scanner reads it at startup to build the tool registry. No service-by-service hand-registration.

Package mcp serves a curated subset of Authorizer's gRPC methods to LLM clients via the Model Context Protocol, over Streamable HTTP (the deployable transport) or stdio (development only). See the design note on Server.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Options

type Options struct {
	// Name is the MCP server's reported implementation name.
	Name string
	// Version is the MCP server's reported implementation version.
	Version string
	// Bearer, when set, is propagated as `Authorization: Bearer <value>`
	// metadata on every gRPC dispatch. This is how MCP-side identity
	// reaches the gRPC handlers (security audit H1). The bearer should be
	// a token issued for the user the MCP host is acting on behalf of.
	Bearer string
	// AuthorizerURL, when set, is propagated as `x-authorizer-url` metadata
	// on every gRPC dispatch so JWT issuer validation resolves the host the
	// bearer token was minted by (not the in-process "bufconn" authority).
	// Required for identity-bearing tools when Bearer is set.
	AuthorizerURL string
}

Options configures the MCP server.

type Server

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

Server wraps an MCP server that bridges to an in-process gRPC server.

Two transports, with very different security models.

Handler() serves Streamable HTTP and is the deployable one. It carries no ambient authority: every tool call is authenticated by the caller's own bearer token, whose audience must name this MCP server (token.ValidateMCPAccessToken), and it runs on a gRPC server whose auth interceptor accepts nothing else — no cookies, no admin secret, no admin service. The route wrapper rejects a bad credential with a 401 so clients can start discovery or refresh.

RunStdio has no auth of its own and relies entirely on the OS-level trust boundary of the subprocess: an MCP host spawns `authorizer mcp` as a child, and only that process can write to its stdin. Identity is the process-wide --mcp-bearer, so one process serves exactly one user. That is why it is a development transport and is deprecated for removal in 2.5.0.

The earlier "stdio is the ONLY supported transport" constraint, and the TestServer_StdioOnly guard that enforced it, named their own exit condition: implement an auth interceptor for MCP first, then allow a network transport. That is what interceptors.MCPTokenResolver and the sole-authority guard are.

func New

func New(log *zerolog.Logger, grpcSrv *grpc.Server, opts Options) (*Server, error)

New builds an MCP server that exposes every gRPC method on `grpcSrv` whose proto annotation has `(authorizer.v1.mcp_tool).exposed = true`. The gRPC server is served over an in-process bufconn — same pattern as the REST gateway — so MCP tool invocations become local method calls with no extra network hop.

func (*Server) Handler

func (s *Server) Handler() http.Handler

Handler serves MCP over Streamable HTTP.

Stateless + JSONResponse, for two independent reasons. The main HTTP listener sets WriteTimeout: 60s, which would kill a long-lived SSE stream mid-flight; and a stateless server needs no sticky sessions, so an Authorizer deployment can scale horizontally without the MCP surface pinning a client to one replica. In this mode the SDK answers GET with 405 + Allow, which is spec-conformant — every exposed tool is request/response, so no server→client stream is needed.

Authentication is NOT done here. It happens twice, on purpose: the route wrapper rejects a bad credential with a 401 so the client knows to refresh or start discovery, and the in-process gRPC interceptor resolves the identity that handlers actually run under. See the route registration in internal/server.

func (*Server) MCPServer

func (s *Server) MCPServer() *mcp.Server

MCPServer exposes the underlying *mcp.Server. Used by tests to drive the server with an in-memory transport pair.

func (*Server) RunStdio

func (s *Server) RunStdio(ctx context.Context) error

RunStdio serves MCP over stdio (the default Claude Code transport). Blocks until ctx is cancelled or the client disconnects.

This is the only `Run*` method on the Server. See the type comment for why adding a non-stdio transport is intentionally a code-level non-feature.

type ToolBinding

type ToolBinding struct {
	// Name surfaced to MCP clients (e.g. "get_meta"). Defaults to
	// snake_case(method) unless the proto annotation overrides it.
	Name string
	// Description from the RPC's leading comment, surfaced to the MCP host.
	Description string
	// Destructive hints to the MCP host that user confirmation is warranted.
	Destructive bool

	// FullMethod is the gRPC method name in `/pkg.Service/Method` form.
	// Used directly with grpc.ClientConn.Invoke.
	FullMethod string
	// InputDescriptor / OutputDescriptor are the proto message descriptors
	// for request/response. Used by the dispatcher to construct dynamic
	// proto.Message instances for JSON unmarshalling/marshalling.
	InputDescriptor  protoreflect.MessageDescriptor
	OutputDescriptor protoreflect.MessageDescriptor
}

ToolBinding is one MCP-exposed RPC: a tool name, plus enough metadata to dispatch a JSON-arg invocation back to the gRPC server.

func Scan

func Scan(srv *grpc.Server) ([]ToolBinding, error)

Scan walks the supplied gRPC server's registered services and returns the set of methods marked `(authorizer.v1.mcp_tool).exposed = true`. Methods that aren't exposed (the default) are silently skipped.

Jump to

Keyboard shortcuts

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