Documentation
¶
Index ¶
- Constants
- Variables
- func ServeMetrics(ctx context.Context, opts MetricsServerOptions) error
- func ServeStreamableHTTP(ctx context.Context, opts StreamableHTTPOptions) error
- func WithProgressToken(ctx context.Context, token ProgressToken) context.Context
- type Activator
- type AuditEvent
- type Auditor
- type Enforcement
- type ExtraHandler
- type InitializeParams
- type InlineMetricsOptions
- type InvalidParamsError
- type MetricsServerOptions
- type Notifier
- type ProgressToken
- type Prompt
- type PromptArgument
- type PromptMessage
- type PromptMessagePart
- type RPCError
- type Request
- type RequestMeta
- type Resource
- type ResourceContents
- type ResourceProvider
- type ResourceTemplate
- type ResourceUpdateDelta
- type Response
- type Server
- func (s *Server) ActivateGroup(groupName string, descriptors []ToolDescriptor) error
- func (s *Server) ActivateTier1Tool(name string) error
- func (s *Server) AddNotifier(n Notifier) func()
- func (s *Server) ClientInfo() (name, version string)
- func (s *Server) DispatchMessage(ctx context.Context, msg []byte) ([]byte, error)
- func (s *Server) HasResourceSubscription(uri string) bool
- func (s *Server) InFlightToolCalls() int
- func (s *Server) InflightCount() int
- func (s *Server) IsReadyCached() bool
- func (s *Server) NegotiatedProtocolVersion() string
- func (s *Server) Notify(method string, params any) error
- func (s *Server) NotifyResourceUpdated(uri string, delta ResourceUpdateDelta)
- func (s *Server) Run(ctx context.Context, r io.Reader, w io.Writer) error
- func (s *Server) ServeHTTP(ctx context.Context, bind string, authenticator authn.Authenticator, ...) error
- func (s *Server) ServeHTTPListener(ctx context.Context, ln net.Listener, authenticator authn.Authenticator, ...) error
- func (s *Server) SetNotifier(n Notifier)
- func (s *Server) SetReadyCached(ready bool)
- type StreamableHTTPOptions
- type StreamableSessionFactory
- type StreamableSessionRuntime
- type Tool
- type ToolCallParams
- type ToolDescriptor
- type ToolHandler
- type ToolHints
Constants ¶
const ServerInstructions = `` /* 1244-byte string literal not displayed */
ServerInstructions is returned in the initialize response to teach MCP clients how to navigate the server. Agentic clients consume this as part of their system prompt, so it trades some verbosity for clarity.
Variables ¶
var SupportedProtocolVersions = []string{
"2025-06-18",
"2025-03-26",
"2024-11-05",
}
SupportedProtocolVersions lists MCP protocol versions this server can negotiate, newest first. The first entry is returned as the default when the client does not send a protocolVersion. When a client requests an unsupported version, we echo back the newest supported version — clients that cannot downgrade will treat that as an error and disconnect, which is the spec-compliant behaviour.
Functions ¶
func ServeMetrics ¶ added in v0.6.0
func ServeMetrics(ctx context.Context, opts MetricsServerOptions) error
func ServeStreamableHTTP ¶ added in v0.6.0
func ServeStreamableHTTP(ctx context.Context, opts StreamableHTTPOptions) error
func WithProgressToken ¶
func WithProgressToken(ctx context.Context, token ProgressToken) context.Context
WithProgressToken attaches the client-supplied progressToken (if any) to ctx so downstream tool handlers can emit notifications/progress keyed off the same value.
Types ¶
type Activator ¶ added in v0.6.0
type Activator interface {
// IsGroupAllowed reports whether a Tier 2 group may be activated.
IsGroupAllowed(group string) bool
// OnActivate is called when tools are dynamically registered.
OnActivate(names []string)
}
Activator handles dynamic tool activation (group enable, visibility toggle). A nil Activator means activation is unrestricted.
type AuditEvent ¶ added in v0.6.0
type Auditor ¶ added in v0.6.0
type Auditor interface {
RecordAudit(AuditEvent) error
}
Auditor records non-read-only tool-call events for compliance and audit trail purposes. RecordAudit returns an error when persistence fails so the server can make the failure observable (log + metric) and optionally fail the call when AuditDurabilityMode is "fail_closed".
Rationale: a void return means persistence errors are silently lost. Returning an error makes audit degradation visible without mandating that every deployment fail-close on it — the server's AuditDurabilityMode field controls the actual behavior.
type Enforcement ¶ added in v0.6.0
type Enforcement interface {
// FilterTool reports whether a tool should be listed in tools/list.
FilterTool(name string, hints ToolHints) bool
// BeforeCall runs before the tool handler. It may:
// - block the call by returning a non-nil error
// - short-circuit with a result (e.g., dry-run preview)
// - return (nil, noop, nil) to proceed normally
// The returned release function must be called when the call completes.
//
// schema is the tool's advertised InputSchema (may be nil) — the
// pipeline uses it for runtime JSON-schema validation. Pipelines that
// receive nil skip validation and behave as before.
BeforeCall(ctx context.Context, name string, args map[string]any, hints ToolHints, schema map[string]any, lookupHandler func(string) (ToolHandler, bool)) (result any, release func(), err error)
// AfterCall post-processes a successful tool result (e.g., truncation).
AfterCall(result any) (any, error)
}
Enforcement handles the tool-call enforcement pipeline. The server delegates all filtering, gating, and post-processing to this interface, keeping the protocol core free of domain logic.
A nil Enforcement means no filtering or enforcement.
type ExtraHandler ¶ added in v0.6.1
ExtraHandler is a neutral pattern+handler pair that transports mount on their internal mux before ListenAndServe. The field exists so debug or observability handlers owned by cmd/ can be plugged onto the same listener as /mcp without forcing internal/mcp to import anything outside the stdlib.
Intentionally minimal: no middleware hooks, no auth toggles, no priority field. The only consumer today is the -tags=pprof build in cmd/clockify-mcp/, which mounts /debug/pprof/* against http.DefaultServeMux. Adding more knobs here means internal/mcp starts caring about debug concerns it shouldn't care about.
type InitializeParams ¶
type InlineMetricsOptions ¶ added in v1.0.1
type InlineMetricsOptions struct {
// Enabled: when false (default), /metrics is not mounted on the main
// HTTP listener. Set MCP_HTTP_INLINE_METRICS_ENABLED=1 to opt in.
Enabled bool
// AuthMode controls auth for the inline /metrics endpoint.
// "inherit_main_bearer": require the same bearer token as /mcp (default
// when Enabled=true and no explicit auth mode is set).
// "static_bearer": require a separate BearerToken below.
// "none": unauthenticated — operator must opt in explicitly; startup warns.
AuthMode string
// BearerToken is used when AuthMode == "static_bearer".
BearerToken string
// MainBearerToken is the /mcp token reused when AuthMode == "inherit_main_bearer".
// Populated by ServeHTTP from the top-level bearerToken argument.
MainBearerToken string
}
InlineMetricsOptions controls whether and how /metrics is exposed on the main HTTP listener when using the legacy HTTP transport (MCP_TRANSPORT=http).
The dedicated metrics listener (MCP_METRICS_BIND / ServeMetrics) is the preferred enterprise pattern because it separates the metrics scrape surface from the MCP API surface. Inline metrics on the main listener require explicit operator opt-in via MCP_HTTP_INLINE_METRICS_ENABLED=1 and an explicit auth mode — they are disabled by default.
type InvalidParamsError ¶
InvalidParamsError is returned from Enforcement.BeforeCall when the tool's input arguments fail schema validation. The tools/call dispatch translates it into a JSON-RPC -32602 (invalid params) response, with Pointer exposed under error.data.pointer so clients can locate the offending field.
Pointer is an RFC 6901 JSON Pointer (e.g. "/workspace_id"). An empty pointer means the root value itself was rejected.
func (*InvalidParamsError) Error ¶
func (e *InvalidParamsError) Error() string
type MetricsServerOptions ¶ added in v0.6.0
type Notifier ¶
Notifier delivers server-initiated notifications (e.g. tools/list_changed) to the connected client. Transports implement this: the stdio transport writes through the shared JSON encoder, while the legacy HTTP POST-only transport logs + counts drops until a real SSE channel is wired by the Streamable HTTP transport rewrite.
type ProgressToken ¶
type ProgressToken = any
ProgressToken is the opaque client-supplied token echoed back on every notifications/progress. Either a string or a number per the MCP spec.
func ProgressTokenFromContext ¶
func ProgressTokenFromContext(ctx context.Context) (ProgressToken, bool)
ProgressTokenFromContext returns the progressToken supplied in the current tools/call _meta, or (nil, false) when the client did not opt in.
type Prompt ¶
type Prompt struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Arguments []PromptArgument `json:"arguments,omitempty"`
Messages []PromptMessage `json:"messages"`
}
Prompt is a registered prompt template with canned messages whose bodies may contain `{{name}}` placeholders substituted at prompts/get time.
type PromptArgument ¶
type PromptArgument struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Required bool `json:"required,omitempty"`
}
PromptArgument describes one substitution variable a prompt accepts.
type PromptMessage ¶
type PromptMessage struct {
Role string `json:"role"`
Content PromptMessagePart `json:"content"`
}
PromptMessage is one turn in a prompt's canned message sequence. Role is typically "user" or "assistant"; content mirrors the MCP content-part shape.
type PromptMessagePart ¶
PromptMessagePart is a single content part inside a PromptMessage. Only text content is supported in this server; clients that want images or resource links can request a richer prompt via a follow-up tool call.
type RequestMeta ¶
type RequestMeta struct {
ProgressToken any `json:"progressToken,omitempty"`
}
RequestMeta is the MCP _meta object that can attach side-channel hints to any request. progressToken is the only field used today — clients supply one to opt into notifications/progress from long-running tool handlers.
type Resource ¶
type Resource struct {
URI string `json:"uri"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
MimeType string `json:"mimeType,omitempty"`
}
Resource describes a concrete, static MCP resource. Dynamic (parametric) resources should be surfaced via ResourceTemplate instead.
type ResourceContents ¶
type ResourceContents struct {
URI string `json:"uri"`
MimeType string `json:"mimeType,omitempty"`
Text string `json:"text,omitempty"`
Blob string `json:"blob,omitempty"`
}
ResourceContents is one chunk of resource body. Text and Blob are mutually exclusive — text/* content uses Text, binary content uses base64 in Blob.
type ResourceProvider ¶
type ResourceProvider interface {
ListResources(ctx context.Context) ([]Resource, error)
ListResourceTemplates(ctx context.Context) ([]ResourceTemplate, error)
ReadResource(ctx context.Context, uri string) ([]ResourceContents, error)
}
ResourceProvider backs the MCP resources/* method family. Implementations live outside the protocol core (tools.Service implements it for Clockify). A nil ResourceProvider on Server means the resources capability is off.
type ResourceTemplate ¶
type ResourceTemplate struct {
URITemplate string `json:"uriTemplate"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
MimeType string `json:"mimeType,omitempty"`
}
ResourceTemplate describes a parametric MCP resource using RFC 6570 URI template syntax — e.g. `clockify://workspace/{workspaceId}/entry/{entryId}`.
type ResourceUpdateDelta ¶
type ResourceUpdateDelta struct {
// Format is one of FormatNone / FormatMerge / FormatFull /
// FormatDeleted. Empty means do not emit a delta envelope — legacy
// payload shape {"uri": "..."} is used.
Format string
// Patch is the wire-format payload for FormatMerge / FormatFull. It
// is emitted verbatim under the "patch" key. Pre-decoded (already
// a Go value) so marshalling the notification doesn't require a
// re-parse step.
Patch any
}
ResourceUpdateDelta carries the optional delta envelope the server can attach to a notifications/resources/updated payload. When Format is empty the legacy payload shape is emitted ({"uri": ...}); otherwise the envelope is merged into the notification params so MCP clients can apply a minimal JSON Merge Patch (RFC 7396) against their cached resource state instead of re-fetching the whole document.
Format values are defined in internal/jsonmergepatch (FormatNone / FormatMerge / FormatFull / FormatDeleted). The protocol core does not interpret them; it passes the envelope through to the notifier. Format validation and payload shape are the tools-layer caller's responsibility.
This extension is additive and backwards compatible: clients that only read the `uri` field keep working. No MCP protocol version bump is required. See docs/adr/013-resource-delta-sync.md.
type Server ¶
type Server struct {
Version string
Enforcement Enforcement // nil = no filtering or enforcement
Activator Activator // nil = activation unrestricted
ToolTimeout time.Duration // per-call timeout; 0 = default 45s
ReadyChecker func(ctx context.Context) error // optional upstream health check for /ready
// ResourceProvider backs resources/* method handlers. nil disables the
// resources capability (server omits it from initialize.result.capabilities).
ResourceProvider ResourceProvider
// MaxInFlightToolCalls bounds the number of concurrently-running
// tools/call goroutines spawned by the stdio dispatch loop.
// Acquired before the goroutine is created so bursty input cannot
// amplify goroutine count. 0 = unlimited.
MaxInFlightToolCalls int
MaxMessageSize int64
// StrictHostCheck enables DNS rebinding protection on the HTTP
// transport: the inbound Host header must match either a loopback
// literal or one of the configured allowed-origin hostnames. Defaults
// to off so that reverse-proxy deployments that rewrite Host are
// unaffected; flip on (MCP_STRICT_HOST_CHECK=1) in localhost-bound
// production deployments to get the strict guarantee.
StrictHostCheck bool
// ExtraHTTPHandlers carries optional handlers that the legacy HTTP
// transport mounts on its mux before ListenAndServe. Used by the
// -tags=pprof build in cmd/clockify-mcp/ to attach /debug/pprof/*
// without forcing internal/mcp to depend on net/http/pprof. nil or
// empty = no extras registered, which is the default production path.
ExtraHTTPHandlers []ExtraHandler
Auditor Auditor
AuditTenantID string
AuditSubject string
AuditSessionID string
AuditTransport string
// AuditDurabilityMode controls what happens when audit persistence fails
// for a non-read-only successful tool call.
//
// "best_effort" (default): log the error and increment the failure metric;
// do not fail the tool call. The mutation already happened; the operator
// is alerted but the client sees success.
//
// "fail_closed": return an error to the caller so the client knows the
// mutation's audit trail is incomplete. The mutation still happened, but
// reporting success when the audit write failed is suppressed.
// Read-only operations are never affected regardless of this setting.
AuditDurabilityMode string
// contains filtered or unexported fields
}
func NewServer ¶
func NewServer(version string, descriptors []ToolDescriptor, enforcement Enforcement, activator Activator) *Server
func (*Server) ActivateGroup ¶ added in v0.6.0
func (s *Server) ActivateGroup(groupName string, descriptors []ToolDescriptor) error
ActivateGroup registers a group of tool descriptors dynamically and sends a tools/list_changed notification to the client.
func (*Server) ActivateTier1Tool ¶ added in v0.6.0
ActivateTier1Tool marks a single registered tool as visible.
func (*Server) AddNotifier ¶
AddNotifier registers a notification sink and returns a function that removes it. Multiple notifiers can coexist; Notify fans out to all of them. Transports that multiplex clients (gRPC Exchange streams) should call AddNotifier per-stream and defer the returned remove function.
func (*Server) ClientInfo ¶
ClientInfo returns the client name and version sent during initialize.
func (*Server) DispatchMessage ¶
DispatchMessage parses a single JSON-RPC message from raw bytes, invokes the central handler, and returns the serialized response. It is intended for non-stdio transports (gRPC sub-module, custom bridges) that own their own concurrency model and framing.
Parse and validation errors are converted to JSON-RPC error responses mirroring the stdio loop. A notification (no id, no result, no error) returns (nil, nil); the caller must skip sending on the wire in that case.
This method does NOT apply the stdio dispatch-layer toolCallSem. Callers that need backpressure on tools/call must implement their own bound.
func (*Server) HasResourceSubscription ¶
HasResourceSubscription reports whether any client is currently subscribed to uri. The tool layer calls this before re-reading a resource in emitResourceUpdate so unsubscribed mutations don't pay for a redundant ReadResource round-trip. Concurrent-safe (delegates to the internal sync.Map).
func (*Server) InFlightToolCalls ¶
InFlightToolCalls reports the current depth of the stdio dispatch semaphore. Returns 0 when the semaphore is disabled.
func (*Server) InflightCount ¶
InflightCount returns the number of tracked in-flight tools/call requests. Used by tests to verify the map is cleaned up.
func (*Server) IsReadyCached ¶ added in v0.6.0
IsReadyCached reports whether the last cached readiness probe resulted in success. Scrapers should prefer /ready for fresh probes; this method only reads the cached value so /metrics does not trigger upstream calls on every scrape.
func (*Server) NegotiatedProtocolVersion ¶ added in v0.6.0
NegotiatedProtocolVersion returns the MCP protocol version agreed with the client, or empty string before initialize runs.
func (*Server) Notify ¶
Notify forwards a server-initiated notification through all registered notifiers. Returns nil when no notifiers are installed.
func (*Server) NotifyResourceUpdated ¶
func (s *Server) NotifyResourceUpdated(uri string, delta ResourceUpdateDelta)
NotifyResourceUpdated publishes notifications/resources/updated if the URI has an active subscription. Transports/tool handlers call this after a mutation that invalidates a cached resource view. Safe to call before the notifier is wired — the call silently no-ops.
When delta.Format is non-empty the notification params include the envelope:
{
"uri": "clockify://workspace/ws/entry/id",
"format": "merge",
"patch": { "description": "new text", "billable": true }
}
Empty delta preserves the legacy payload shape {"uri": "..."} so existing clients and tests remain unchanged.
func (*Server) Run ¶
Run processes JSON-RPC requests from r and writes responses to w. It respects ctx cancellation for graceful shutdown — when ctx is cancelled, the loop exits even if stdin is blocking.
func (*Server) ServeHTTP ¶ added in v0.6.0
func (s *Server) ServeHTTP(ctx context.Context, bind string, authenticator authn.Authenticator, bearerToken string, allowedOrigins []string, allowAnyOrigin bool, maxBodySize int64, inlineMetrics InlineMetricsOptions) error
ServeHTTP starts an HTTP server that wraps the MCP server's handle() method. Auth is delegated to the supplied authn.Authenticator — for legacy deployments operators pass a static-bearer authenticator built from MCP_BEARER_TOKEN; enterprise deployments can pass OIDC/forward_auth/mTLS authenticators through the same seam. bearerToken remains non-empty only because inline-metrics inheritance reuses it; nil authenticator + empty bearerToken is rejected so the handler never runs unauthenticated.
When allowAnyOrigin is false and allowedOrigins is empty, cross-origin requests are rejected (secure default).
inlineMetrics controls whether /metrics is mounted on the main listener. The default (InlineMetricsOptions{}) leaves /metrics absent — use the dedicated metrics listener (ServeMetrics) for the recommended pattern.
func (*Server) ServeHTTPListener ¶ added in v1.0.1
func (s *Server) ServeHTTPListener(ctx context.Context, ln net.Listener, authenticator authn.Authenticator, bearerToken string, allowedOrigins []string, allowAnyOrigin bool, maxBodySize int64, inlineMetrics InlineMetricsOptions) error
ServeHTTPListener is the listener-injection counterpart of ServeHTTP. It takes ownership of an already-open net.Listener (callers should use net.Listen("tcp", "127.0.0.1:0") to get an ephemeral port) and runs the same middleware stack until ctx is cancelled. Primarily for tests that need to know the bound port before the server starts accepting.
func (*Server) SetNotifier ¶
SetNotifier installs a notification sink, removing any previously installed via SetNotifier. Transports that own a single client (stdio, legacy HTTP) use this for backwards compatibility. Internally delegates to AddNotifier.
func (*Server) SetReadyCached ¶ added in v1.0.0
SetReadyCached updates the cached readiness state. Transports that lack an HTTP readiness endpoint (gRPC) call this after verifying upstream connectivity so IsReadyCached reflects their state.
type StreamableHTTPOptions ¶ added in v0.6.0
type StreamableHTTPOptions struct {
Version string
Bind string
// Listener, if non-nil, is used in place of net.Listen("tcp", Bind).
// Primarily for tests that need to know the bound port before the
// server starts accepting. Bind is still consulted for the logged
// startup address when Listener is nil.
Listener net.Listener
MaxBodySize int64
AllowedOrigins []string
AllowAnyOrigin bool
StrictHostCheck bool
SessionTTL time.Duration
ReadyChecker func(context.Context) error
Authenticator authn.Authenticator
ControlPlane controlplane.Store
Factory StreamableSessionFactory
// ProtectedResource is the unauthenticated handler for the
// /.well-known/oauth-protected-resource metadata document. When
// non-nil it is mounted at the canonical RFC 9728 path. nil =
// endpoint omitted (e.g. server does not advertise OAuth 2.1
// resource discovery).
ProtectedResource http.Handler
// ExtraHandlers mounts optional handlers on the streamable HTTP
// mux before ListenAndServe — counterpart to Server.ExtraHTTPHandlers
// for the streamable transport. Used by -tags=pprof to attach
// /debug/pprof/* alongside /mcp. nil = no extras, default path.
ExtraHandlers []ExtraHandler
// IdleGraceAfterDisconnect is the maximum time a session with zero
// active SSE subscribers may sit before the reaper evicts it early.
// Guards against orphaned-subscriber leaks where a client drops TCP
// mid-stream without DELETEing the session: SessionTTL alone would
// hold the entry for up to 30 minutes. Zero uses the 5 minute default.
IdleGraceAfterDisconnect time.Duration
}
type StreamableSessionFactory ¶ added in v0.6.0
type StreamableSessionRuntime ¶ added in v0.6.0
type ToolCallParams ¶
type ToolCallParams struct {
Name string `json:"name"`
Arguments map[string]any `json:"arguments,omitempty"`
Meta *RequestMeta `json:"_meta,omitempty"`
}
type ToolDescriptor ¶
type ToolDescriptor struct {
Tool Tool
Handler ToolHandler
ReadOnlyHint bool
DestructiveHint bool
IdempotentHint bool
}