mcpbridge

package
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Jul 15, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package mcpbridge is a Model Context Protocol stdio server that reads a forge project's gen/mcp/manifest.json and exposes every RPC tool the manifest declares to an MCP client (Claude Code, Claude Desktop, the official MCP Inspector, Cline, etc.).

It is the host for the otherwise-orphan gen/mcp/manifest.json: forge codegen writes one MCP tool per Connect RPC, and this package turns that static descriptor into a live, callable MCP server. Both the `forge mcp serve` CLI subcommand and the standalone cmd/forge-mcp binary are thin wrappers over Server here, so the protocol behaviour stays defined once.

What it does:

  • Reads a loaded gen/mcp/manifest.json (Manifest).
  • Speaks MCP over stdio (JSON-RPC 2.0, one message per line — the shape Claude Code / Claude Desktop expect).
  • Implements initialize, tools/list, tools/call, ping.
  • tools/call ACTUALLY dispatches to the running Connect service over HTTP+JSON (the same transport `forge api curl` builds): POST to <addr><procedure> with the arguments map as the JSON body; the Connect response becomes structuredContent + a human-readable content block. Connect error envelopes become MCP isError:true results so agents see typed failures.

Why HTTP+JSON: Connect's JSON wire format lets a generic client call any procedure without compiling against the project's proto types — the bridge stays one implementation for every forge project. Streaming RPCs (server/client/bidi) are NOT supported because MCP tool calls are unary; streaming tools are excluded from tools/list and tools/call returns an explicit error if a client calls one by name anyway.

Auth: the bridge sets a verbatim Authorization header on every dispatched RPC when one is configured (Server.AuthHeader). A forge dev server that is NOT in AUTH_DEV_MODE runs the auth interceptor, so without a token every tools/call returns a clean Connect "unauthenticated" error — which itself proves the transport wiring. With a token, real calls succeed.

forge:exclude-contract mcpbridge is an MCP stdio-server glue package (bridges gen/mcp/manifest.json to a live MCP server over stdio/HTTP), not a bootstrap-wired Connect service. Opt out of the require-contract rule.

Index

Constants

View Source
const (
	// ProtocolVersion is the MCP revision this server targets. 2024-11-05
	// is the revision Claude Desktop / Claude Code and the official
	// Inspector negotiate; its Tool.inputSchema is an open object, which
	// is why $defs/$ref schemas pass through unmodified.
	ProtocolVersion = "2024-11-05"
	// ServerName is the identity this bridge reports in the MCP
	// initialize handshake.
	ServerName = "forge-mcp"
	// ServerVersion is this bridge implementation's own version, distinct
	// from the negotiated ProtocolVersion.
	ServerVersion = "0.1.0"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Manifest

type Manifest struct {
	Generated     string `json:"_generated"`
	SchemaVersion string `json:"schema_version"`
	Project       string `json:"project"`
	Tools         []Tool `json:"tools"`
}

Manifest mirrors the gen/mcp/manifest.json shape forge writes. We keep the read shape local (rather than importing the codegen emitter type) so the bridge is insulated from forge codegen churn — a project may run this against a manifest produced by a different forge version.

func LoadManifest

func LoadManifest(path string) (*Manifest, error)

LoadManifest reads and parses a gen/mcp/manifest.json file.

type Server

type Server struct {
	// Manifest is the loaded gen/mcp/manifest.json. Required.
	Manifest *Manifest
	// Addr is the Connect base URL (e.g. http://localhost:8080). Empty →
	// tools/call returns a clear configuration error rather than silently
	// failing to dial. Trailing slash is trimmed at Run so URL
	// concatenation with the procedure path always produces a clean URL.
	Addr string
	// AuthHeader, when non-empty, is set verbatim as the Authorization
	// header on every dispatched RPC. The bridge never inspects or
	// refreshes the token — the caller passes a current value.
	AuthHeader string
	// HTTP is the client used for all Connect dispatches. Required; its
	// Timeout applies per tools/call invocation.
	HTTP *http.Client
	// Logf receives diagnostics. nil → log.Printf (stderr).
	Logf func(format string, args ...any)
}

Server is an MCP stdio bridge over one project's manifest. Construct it directly (all fields exported) and call Run. Logf, when nil, defaults to the standard logger on stderr — stdout is reserved for MCP framing.

func (*Server) CallableToolCount

func (s *Server) CallableToolCount() int

CallableToolCount counts the tools the MCP surface actually advertises — manifest tools minus streaming RPCs, which tools/list excludes because the bridge cannot proxy streams.

func (*Server) Run

func (s *Server) Run(in io.Reader, out io.Writer) error

Run is the JSON-RPC over stdio main loop. Each line is one JSON-RPC 2.0 request; responses are framed the same way. The loop exits on stdin EOF, which is how MCP clients signal disconnect.

type Tool

type Tool struct {
	Name           string         `json:"name"`
	Description    string         `json:"description,omitempty"`
	InputSchema    map[string]any `json:"inputSchema"`
	OutputSchema   map[string]any `json:"outputSchema,omitempty"`
	Service        string         `json:"service,omitempty"`
	Method         string         `json:"method,omitempty"`
	Procedure      string         `json:"procedure,omitempty"`
	AuthRequired   bool           `json:"auth_required,omitempty"`
	IdempotencyKey bool           `json:"idempotency_key,omitempty"`
	Streaming      string         `json:"streaming,omitempty"`
}

Tool carries every field the MCP spec needs for tools/list plus the forge-specific routing metadata (service / method / procedure / auth_required / idempotency_key / streaming). MCP clients ignore the snake_case extras per the protocol's permissive unknown-field policy; the bridge strips them from the tools/list wire response anyway so even strict clients accept it.

Jump to

Keyboard shortcuts

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