Help us shape the future of Go! Take the 2026 Go Developer Survey

mcpapps

package
v0.252.0 Latest Latest
Warning

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

Go to latest
Published: Oct 7, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package mcpapps is the MCP Apps host bridge: the frame-level half of a terminal or embedded host that renders an MCP App and answers the calls that App makes back through its postMessage channel.

It lives in pkg/ rather than a guarded surface because it expresses no permission. A view-initiated tools/call passes a Policy this package only holds an interface for; http/mcpverb compiles a guardfile into one. A Host with no Policy forwards nothing, so an undeclared widget reaches no upstream. See docs/mcpapps.md.

Index

Constants

View Source
const (
	MethodInitialize    = "ui/initialize"
	MethodInitialized   = "ui/notifications/initialized"
	MethodToolResult    = "ui/notifications/tool-result"
	MethodSizeChanged   = "ui/notifications/size-changed"
	MethodOpenLink      = "ui/open-link"
	MethodDownloadFile  = "ui/download-file"
	MethodToolsCall     = "tools/call"
	MethodToolsList     = "tools/list"
	MethodResourcesRead = "resources/read"
	MethodResourcesList = "resources/list"
	MethodProgress      = "notifications/progress"
)

The frame methods this host answers. A view sends the ui/* ones plus ordinary MCP; every other method gets a MethodNotFound reply rather than silence.

View Source
const (
	CodeMethodNotFound = -32601
	CodeInvalidParams  = -32602
	CodePolicyDenied   = -32001
	CodeUpstreamFailed = -32002
)

JSON-RPC codes this host returns. PolicyDenied is in the implementation- defined range and mirrors umbra's exitcode taxonomy.

View Source
const ProtocolVersion = "2026-01-26"

ProtocolVersion is the MCP Apps version this host speaks, read from `LATEST_PROTOCOL_VERSION` in @modelcontextprotocol/ext-apps spec.types.d.ts.

Variables

This section is empty.

Functions

func CSP

func CSP(s CSPSources) string

CSP builds the policy for one view. An undeclared family falls back to the spec's own default, so a missing declaration refuses rather than opens.

func WidgetURI

func WidgetURI(t mcpclient.Tool) string

WidgetURI returns the `ui://` resource a tool's view lives at, or "". It reads both `_meta` spellings a server may emit. See docs/mcpverb.md.

Types

type CSPSources

type CSPSources struct {
	// Resource feeds script-src, style-src, img-src, font-src, and media-src.
	Resource []string

	// Connect feeds connect-src and is what `can connect` declares. The other
	// three families have no guardfile verb yet.
	Connect []string

	// Frame feeds frame-src, and BaseURI feeds base-uri.
	Frame   []string
	BaseURI []string

	// Report names a collector path for violation reports. It is umbra's
	// addition rather than the spec's, and it grants nothing.
	Report string
}

CSPSources are the domains a view declared, one field per directive family the spec's `csp` object carries. See docs/mcpapps.md.

func (CSPSources) Declared

func (s CSPSources) Declared() bool

Declared reports whether the view named any domain. None of them selects the spec's omitted-`ui.csp` case, which is the tighter of the two.

type DownloadItem

type DownloadItem struct {
	URI      string
	MIMEType string

	// Raw is the item as the view sent it, so a handler saving inline bytes
	// reads them without this package modelling every content shape.
	Raw json.RawMessage
}

DownloadItem is one entry of a ui/download-file request, a link the host fetches or an inline resource. URI addresses both, and is what is gated.

type Frame

type Frame struct {
	JSONRPC string          `json:"jsonrpc"`
	ID      json.RawMessage `json:"id,omitempty"`
	Method  string          `json:"method,omitempty"`
	Params  json.RawMessage `json:"params,omitempty"`
}

Frame is one inbound JSON-RPC message from the view. ID is raw because a JSON-RPC id is a string or a number and this host only echoes it back.

func (Frame) IsRequest

func (f Frame) IsRequest() bool

IsRequest reports whether the frame expects exactly one reply. A frame with no id is a notification, and answering one is a protocol error.

type Host

type Host struct {
	// Info identifies this host to the view during ui/initialize.
	Info Implementation

	// Context is sent as `hostContext`. Omitting it entirely leaves the view's
	// SDK never-ready, so Handle substitutes a minimal one.
	Context HostContext

	// Instantiating is the tools/call result that created this view, pushed as
	// ui/notifications/tool-result when the view says it is initialized.
	Instantiating any

	// Session is the upstream a permitted call reaches. A nil Session refuses
	// every proxied frame rather than dropping it.
	Session Session

	// Policy gates every view-initiated call. A nil Policy permits none, which
	// is what makes an undeclared widget inert.
	Policy Policy

	// Emit posts an unsolicited frame to the view. The SDK calls it from its own
	// goroutine, so it must be safe concurrently. nil drops them.
	Emit func(Reply)

	// OpenLink opens a URL the policy permitted. nil leaves `openLinks`
	// undeclared, so a view learns rather than asking for what never happens.
	OpenLink func(ctx context.Context, url string) error

	// SaveFile hands permitted contents to the viewer. nil leaves
	// `downloadFile` undeclared, for the same reason.
	SaveFile func(ctx context.Context, items []DownloadItem) error
	// contains filtered or unexported fields
}

Host answers one view's frames. It holds no transport: a caller reads a postMessage frame from wherever it arrives and hands the bytes to Handle.

func (*Host) Handle

func (h *Host) Handle(ctx context.Context, raw []byte) []Reply

Handle answers one frame from the view, never returning a Go error. Every request yields exactly one reply, a notification only what it triggers.

func (*Host) HandleFrame

func (h *Host) HandleFrame(ctx context.Context, f Frame) []Reply

HandleFrame is Handle over an already-decoded frame.

func (*Host) HandleProgress

func (h *Host) HandleProgress(p mcpclient.Progress)

HandleProgress forwards one upstream progress notification to the view under its own token. Wire it as mcpclient.Options.OnProgress.

type HostContext

type HostContext map[string]any

HostContext is the environment the view renders against. A free map, because the spec's McpUiHostContext carries an index signature.

type Implementation

type Implementation struct {
	Name    string `json:"name"`
	Version string `json:"version,omitempty"`
}

Implementation names a party to the handshake, matching MCP's own shape.

type Policy

type Policy interface {
	// Tools is the surface the view may see and call: deny by absence, the
	// same rule the CLI leaves follow. See docs/mcpapps.md.
	Tools() []mcpclient.Tool

	// CheckToolCall reports why this view-initiated call is refused, or nil. It
	// runs after Tools membership, so it guards arguments rather than names.
	CheckToolCall(tool string, args map[string]any) error

	// CheckResourceRead reports why this view-initiated read is refused, or nil.
	CheckResourceRead(uri string) error

	// CheckOpenLink reports why opening this URL for the view is refused, or
	// nil. Opening a URL an untrusted view chose is its own grant.
	CheckOpenLink(url string) error

	// CheckSaveFile reports why handing this resource to the viewer to save is
	// refused, or nil. It gates the URI of a link and of an inline resource.
	CheckSaveFile(uri string) error
}

Policy decides what a view may reach. This package holds the interface and no rules: the guardfile is the policy, compiled above this by http/mcpverb.

type RPCError

type RPCError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    any    `json:"data,omitempty"`
}

RPCError is the error member of a reply.

type Reply

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

Reply is one outbound frame, carrying a result or an error and never both. Unexported members, because postMessage keeps an empty one. docs/mcpapps.md.

func Failure

func Failure(id json.RawMessage, code int, msg string, data any) Reply

Failure answers a request with an error, which is what a refused call gets: the view learns rather than waiting on a reply that never arrives.

func Notify

func Notify(method string, params any) Reply

Notify is an unsolicited Host to View frame, carrying no id.

func Result

func Result(id json.RawMessage, v any) Reply

Result answers a request with a value.

func (Reply) IsError

func (r Reply) IsError() bool

IsError reports whether this reply carries an error member.

func (Reply) MarshalJSON

func (r Reply) MarshalJSON() ([]byte, error)

MarshalJSON emits exactly one of result, error, or method+params.

func (Reply) Method

func (r Reply) Method() string

Method names the notification this reply is, or "" for a request answer.

type Session

type Session interface {
	Call(ctx context.Context, c mcpclient.Call) (mcpclient.Result, error)
	ReadResource(ctx context.Context, uri string) (*mcp.ReadResourceResult, error)
	ListResources(ctx context.Context) ([]mcpclient.Resource, error)
}

Session is the upstream this host proxies to: the subset of *mcpclient.Session a view can reach, which that type already satisfies.

Jump to

Keyboard shortcuts

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