Documentation
¶
Overview ¶
Package common holds helpers shared across mcpkit examples — the seam where "every example needs this now" updates land in one place instead of N copy-paste edits. See examples/CONVENTIONS.md for the conventions these helpers encode.
Index ¶
- Constants
- func MCPServerOptions(addr, prefix string) []server.Option
- func MCPServerURL() string
- func NewMCPLogger(prefix string, extraRules ...demokit.ColorRule) *log.Logger
- func PrintRPCError(err error, wantReason string)
- func RunServer(cfg ServerConfig) error
- func ServerURL() string
- func SetupRenderer(demo *demokit.Demo)
- func SplitLines(s string) []string
- func WireRecipe(s *demokit.StepDef, curl, goSource string) *demokit.StepDef
- func WithMCPLogging(logger *log.Logger) []server.Option
- type ServerConfig
- type TelemetryFlags
- type WireFlags
Constants ¶
const DefaultMCPServerURL = "http://localhost:3101"
DefaultMCPServerURL is the default endpoint for apps/compat-style MCP fixtures (one MCP server per fixture, always on :3101). MCPServerURL returns this unless overridden via the env var.
const DefaultServerURL = "http://localhost:8080"
DefaultServerURL is the canonical mcpkit example server URL. Walkthroughs default to this; override via --url flag or MCPKIT_SERVER_URL env var (see ServerURL).
const ServerURLEnv = "MCPKIT_SERVER_URL"
ServerURLEnv is the env var name that overrides the default server URL when no --url flag is passed.
Variables ¶
This section is empty.
Functions ¶
func MCPServerOptions ¶
MCPServerOptions returns the canonical baseline server options every non-UI example wants: WithListen + the canonical color logger wired to both transport request logging and dispatch middleware.
Callers append example-specific options:
opts := common.MCPServerOptions(*addr, "[mcp] ") opts = append(opts, server.WithListTTLMs(60000)) srv := server.NewServer(info, opts...)
If you need the logger handle (custom tinted log lines, extra rules), use NewMCPLogger + WithMCPLogging instead.
func MCPServerURL ¶
func MCPServerURL() string
MCPServerURL returns the endpoint an apps/compat-style fixture's walkthrough should connect to. Same env-var override as ServerURL, different default port (:3101 vs :8080). Every compat fixture's walkthrough.go calls this — keeps the :3101 default in one place instead of duplicating a serverURLFor3101 helper per fixture.
func NewMCPLogger ¶
NewMCPLogger returns the canonical demokit ColorLogger used across mcpkit examples. The five built-in rules tint:
- error= and ERROR markers (red)
- [http] → outbound transport (gray / dim blue)
- [http] ← inbound transport (cyan / blue)
- MCP <method> dispatch (bright green / green)
extraRules append to the canonical set so callers can tint example-specific log lines without losing the shared baseline.
func PrintRPCError ¶
PrintRPCError formats a *client.RPCError for an error-path demo step, printing code / message / data on separate lines so the wire shape is visible in stdout (not just buried in the conformance suite).
If wantReason is non-empty, it's compared against err.Data["reason"] (when err.Data is a JSON object) and a WARN line is printed on mismatch — useful for spec-validation demos where a regression in the data shape should surface in the demo run, not just in tests.
Pass "" for wantReason when the demo just wants to render the error without asserting a specific reason field.
func RunServer ¶
func RunServer(cfg ServerConfig) error
RunServer wires up the canonical mcpkit-example server lifecycle:
- Build baseline options (WithListen + canonical color logger via MCPServerOptions, or WithListen + WithMCPLogging(cfg.Logger) when a pre-built logger is supplied).
- Append cfg.Options.
- Construct *server.Server with core.ServerInfo{Name, Version}.
- Invoke cfg.Register (if set) for tool/middleware registration.
- Log "[<name>] listening on <addr>".
- Call srv.ListenAndServe(WithStreamableHTTP(true), cfg.TransportOptions...).
Behaves like a normal blocking serve — returns whatever ListenAndServe returns (typically a graceful-shutdown nil or a bind error). Callers that need to log additional info (extra tool names, temp dirs, etc.) should log before calling RunServer; the helper's own "listening on" line stays minimal and uniform across examples.
For examples whose serve loop diverges substantially from this shape (mux setup with side endpoints, parallel webhook listeners), build the server manually with NewMCPLogger + MCPServerOptions and call srv.ListenAndServe directly — see examples/events/discord/ for the canonical exception.
func ServerURL ¶
func ServerURL() string
ServerURL returns the endpoint a non-UI example's walkthrough should connect to. Precedence (highest first):
- --url <addr> on the command line
- $MCPKIT_SERVER_URL env var
- DefaultServerURL ("http://localhost:8080")
Non-UI examples (auth, tasks, file-inputs, etc.) call common.ServerURL(). apps/compat fixtures, which always bind :3101, call common.MCPServerURL() instead.
func SetupRenderer ¶
SetupRenderer wires the renderer matching demokit's --mode (or the bare --tui / --note aliases):
--mode=tui / --tui → tui.New() (Lipgloss boxes) --mode=notebook / --note → notebookbridge.New() (cell-based UI) default → demokit's PlainRenderer
Also registers the demokit/web package so every walkthrough gets `--doc bundle` (generates a self-contained playable HTML page from a recorded trace) and `--serve <addr>` (live HTTP+WS player) for free. The recipe to publish a playable trace:
# Record a trace once (author at the keyboard; press Enter to # advance each step). Commit the resulting JSON as the # source-of-truth for downstream bundle generation: go run . --demo --record .walkthrough.trace.json # Bundle the trace + the player into one HTML page (+ sibling # JS/CSS assets) ready to host on gh-pages / docs.site -- no # server needed at build time, the trace contains everything: go run . --demo --doc bundle --from .walkthrough.trace.json --out walkthrough/index.html
Non-interactive recording (`--non-interactive`) is currently unsafe for walkthroughs with input steps: demokit silently omits inputs that have no Default declared (panyam/demokit#59 tracks the fail-fast fix). Until that ships, prefer interactive recording.
`make note` target shells out to `--note`, which demokit.Mode() resolves to "notebook" — so notebook mode is wired once here and applies to all walkthroughs uniformly. notebookbridge renders output cells with horizontal-only borders (no vertical bars) so streamed output is clean to mouse-select and copy.
func SplitLines ¶
SplitLines splits a multi-line string on '\n'. Wraps the stdlib idiom to keep walkthrough.go bodies focused on the demo narrative rather than line-iteration mechanics — `for _, line := range common.SplitLines(s)` reads cleanly in step Run() callbacks where the typical use is "print each line of a multi-line value with consistent indent."
func WireRecipe ¶
WireRecipe attaches a tabbed "Reproduce on the wire" block with two variants -- the wire-level curl form (default) and the equivalent *client.Client Go form -- to a walkthrough step. The TUI renders it as a tabbed switcher (press tab to flip between curl and Go); the horizontal-only borders configured in SetupRenderer mean copy-paste of inner content rows picks up only the content, no side chars.
In markdown / HTML output, curl is rendered as the default variant (matching the convention from examples/file-inputs); pass `--variant=go` or `--variant=all` to override.
Returns the step so calls keep chaining.
func WithMCPLogging ¶
WithMCPLogging returns the standard server options that wire a logger to both transport-level request logging and the MCP dispatch middleware path. Use directly when you need the logger handle for example-specific tints; otherwise prefer MCPServerOptions which bundles listen + logging into one call.
Types ¶
type ServerConfig ¶
type ServerConfig struct {
// Name is the server name reported via core.ServerInfo (required).
Name string
// Version is reported via core.ServerInfo. Defaults to "0.1.0" when
// empty — examples rarely care about the value and benefit from a
// stable default.
Version string
// Addr is the TCP listen address passed to server.WithListen
// (required). Defaults are intentionally NOT applied here so a
// missing addr surfaces at startup instead of binding ":8080"
// silently.
Addr string
// LogPrefix is the canonical color-logger prefix passed to
// MCPServerOptions. Defaults to "[mcp] " when empty.
//
// Ignored when Logger is non-nil — the caller is then responsible
// for the prefix attached to the logger they pass in.
LogPrefix string
// Logger is an optional pre-built logger. When set, RunServer skips
// MCPServerOptions and wires logging via WithMCPLogging(Logger) so
// callers that need custom color rules (NewMCPLogger extras) keep
// their handle. When nil, the canonical 5-rule logger is used.
Logger *log.Logger
// Options are additional server options appended to the canonical
// baseline (WithListen + logging) before NewServer is called. Use
// for construct-time wiring like WithExtension, WithAuth,
// WithListCacheControl, etc.
Options []server.Option
// Register is invoked after NewServer for tool/middleware/extension
// registration that requires the constructed *server.Server. Called
// before ListenAndServe so registrations are live by the time the
// transport starts accepting connections. May be nil.
Register func(*server.Server)
// TransportOptions are appended after server.WithStreamableHTTP(true)
// when calling ListenAndServe. Use for serve-time wiring like
// WithStatelessMode, WithSSE, WithEventStore, etc.
TransportOptions []server.TransportOption
// Wire, when non-nil, applies --wire / --server-wire selection
// (from RegisterWireFlags or WireFromArgs) to the transport. It is
// applied AFTER TransportOptions so the CLI flag overrides any
// hardcoded WithStatelessMode. When no wire flag was passed the
// helper appends nothing and the server keeps its
// MCPKIT_STATELESS_MODE / stateless.DefaultMode wire. nil disables
// the feature entirely (the pre-Wire behavior).
Wire *WireFlags
// TracerProvider, when non-nil, wires SEP-414 trace middleware
// into the server via server.WithTracerProvider. Pass the result
// of commonotel.SetupTelemetry directly — it's already wrapped
// in mcpotel.NewProvider so no adapter call is needed at the
// example call site. A nil value (or core.NoopTracerProvider{})
// is the default and adds zero overhead.
TracerProvider core.TracerProvider
// MeterProvider, when non-nil, wires the issue 7 metrics
// middleware into the server via server.WithMeterProvider so the
// dispatch path emits the canonical MCP server metrics
// (mcp.tool.calls / mcp.jsonrpc.errors / mcp.tool.duration /
// mcp.sessions.active). Pass the result of commonotel.SetupMetrics
// directly — it's already wrapped in mcpotel.NewMeterProvider so
// no adapter call is needed at the example call site. A nil value
// (or core.NoopMeterProvider{}) is the default and adds zero
// overhead.
MeterProvider core.MeterProvider
}
ServerConfig is the canonical config for a non-UI mcpkit example server. Name and Addr are required; everything else has a sensible default. See RunServer for the wiring contract.
type TelemetryFlags ¶
type TelemetryFlags struct {
// Exporter is the --exporter flag value: "", "stdout", or
// "otlp". Default "" so no example prints spans uninvited.
Exporter *string
// OTLPEndpoint is the --otlp-endpoint flag value. Default is
// commonotel.DefaultOTLPEndpoint (matches docker/observability/).
// Only consulted when Exporter=="otlp".
OTLPEndpoint *string
}
TelemetryFlags holds pointers populated by RegisterTelemetryFlags. Pass Exporter and OTLPEndpoint into commonotel.SetupTelemetry's WithExporter / WithOTLPEndpoint options after flag.Parse.
The struct shape (rather than two free pointer returns) lets a caller pass the whole thing to a helper without re-typing each field, and gives room for future flags (sampling rate, logs exporter, metrics exporter) without churning the function signature.
func ExporterFromArgs ¶
func ExporterFromArgs() *TelemetryFlags
ExporterFromArgs scans os.Args for --exporter / --otlp-endpoint without calling flag.Parse. Mirrors the ad-hoc pattern common.ServerURL uses for --url. Returns a *TelemetryFlags whose pointers are locally allocated, so the API matches what RegisterTelemetryFlags returns — call sites deref the same way (`*tel.Exporter`) regardless of which helper populated it.
Use from walkthroughs (runDemo) that don't otherwise call flag.Parse. For binaries that already call flag.Parse (any serve() using common.RunServer with extra example-specific flags), prefer RegisterTelemetryFlags so demokit.FilterArgs sees the registered flag set.
Recognized argv shapes:
--exporter=otlp --otlp-endpoint=localhost:4317 --exporter otlp --otlp-endpoint localhost:4317
Defaults match RegisterTelemetryFlags: exporter="", endpoint= commonotel.DefaultOTLPEndpoint.
func RegisterTelemetryFlags ¶
func RegisterTelemetryFlags(fs *flag.FlagSet) *TelemetryFlags
RegisterTelemetryFlags wires the uniform --exporter + --otlp-endpoint flag pair onto fs. Call before flag.Parse.
Example wiring inside serve():
addr := flag.String("addr", ":8080", "listen address")
tel := common.RegisterTelemetryFlags(flag.CommandLine)
flag.CommandLine.Parse(demokit.FilterArgs(os.Args[1:],
demokit.BoolFlag("--serve"),
demokit.ValueFlag("--exporter"),
demokit.ValueFlag("--otlp-endpoint"),
))
tp, shutdown, err := commonotel.SetupTelemetry(ctx,
commonotel.WithServiceName("..."),
commonotel.WithExporter(*tel.Exporter),
commonotel.WithOTLPEndpoint(*tel.OTLPEndpoint),
)
The --exporter / --otlp-endpoint flag names MUST be registered as demokit.ValueFlag so FilterArgs doesn't strip them — see examples/CONVENTIONS.md §Telemetry wiring.
type WireFlags ¶
type WireFlags struct {
// Wire is the primary --wire value: "" | legacy | dual | stateless.
// "" means "make no selection" — both sides fall through to their
// env var / package default.
Wire *string
// ServerWire is the --server-wire override: "" | legacy | dual |
// stateless. Beats Wire for the server side only.
ServerWire *string
// ClientWire is the --client-wire override: "" | legacy | adaptive |
// stateless. Beats Wire for the client side only.
ClientWire *string
}
WireFlags holds the wire-selection flag values. The primary --wire drives BOTH halves of a demo binary (server and client) via a sensible pairing; --server-wire / --client-wire override one side when a demo genuinely needs them decoupled.
Pairing applied by --wire:
--wire=legacy server ModeLegacyOnly + client ClientModeLegacyOnly --wire=dual server ModeDual + client ClientModeAdaptive --wire=stateless server ModeStateless + client ClientModeStateless
dual is the only value that maps asymmetrically: a Dual server speaks both wires, so the client side pairs to Adaptive (probe then fall back), not a nonexistent "dual" client mode.
func RegisterWireFlags ¶
RegisterWireFlags wires --wire / --server-wire / --client-wire onto fs. Call before flag.Parse. Mirrors RegisterTelemetryFlags.
The flag names MUST be registered as demokit.ValueFlag in any binary that filters os.Args through demokit.FilterArgs, so they survive the filter — see examples/CONVENTIONS.md §Wire selection.
func WireFromArgs ¶
func WireFromArgs() *WireFlags
WireFromArgs scans os.Args for --wire / --server-wire / --client-wire without calling flag.Parse. Mirrors ExporterFromArgs, for walkthroughs (runDemo) that don't otherwise parse flags. Returns a *WireFlags whose pointers are locally allocated so call sites resolve it identically to the RegisterWireFlags return.
Recognized argv shapes (space- or =-separated):
--wire=stateless --server-wire dual --client-wire=adaptive
func (*WireFlags) ClientOption ¶
func (w *WireFlags) ClientOption() (client.ClientOption, bool)
ClientOption resolves the client wire into client.WithClientMode. ok=true only when a wire was explicitly selected; ok=false (nil option) means the client keeps its env/default mode. Unrecognized tokens warn and fall through, matching ServerTransportOption.
func (*WireFlags) ServerTransportOption ¶
func (w *WireFlags) ServerTransportOption() (server.TransportOption, bool)
ServerTransportOption resolves the server wire into server.WithStatelessMode. ok=true only when a wire was explicitly selected; ok=false (with a nil option) means "make no selection" so the caller appends nothing and the server keeps its env/default wire.
An unrecognized token logs a warning and returns ok=false rather than binding a surprising mode — a CLI typo falls through to the default instead of silently flipping the wire.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package otel ships SetupTelemetry — env-gated TracerProvider wiring that every mcpkit example uses to get a uniform, opt-in observability surface (issue 666).
|
Package otel ships SetupTelemetry — env-gated TracerProvider wiring that every mcpkit example uses to get a uniform, opt-in observability surface (issue 666). |