mcpkit

package module
v0.0.4 Latest Latest
Warning

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

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

README

MCPKit

Production-grade MCP (Model Context Protocol) server library for Go.

MCPKit handles the transport, protocol negotiation, session management, and auth so you can focus on registering tools, resources, and prompts. Supports both HTTP+SSE (MCP 2024-11-05) and Streamable HTTP (MCP 2025-03-26) transports.

Quick Start

package main

import (
    "context"
    "log"
    "time"
    "github.com/panyam/mcpkit"
)

func main() {
    srv := mcpkit.NewServer(
        mcpkit.ServerInfo{Name: "my-server", Version: "0.1.0"},
        mcpkit.WithListen(":8787"),
        mcpkit.WithToolTimeout(30 * time.Second),
    )

    srv.RegisterTool(
        mcpkit.ToolDef{
            Name:        "greet",
            Description: "Say hello",
            InputSchema: map[string]any{
                "type": "object",
                "properties": map[string]any{
                    "name": map[string]any{"type": "string"},
                },
                "required": []string{"name"},
            },
        },
        func(ctx context.Context, req mcpkit.ToolRequest) (mcpkit.ToolResult, error) {
            var args struct {
                Name string `json:"name"`
            }
            if err := req.Bind(&args); err != nil {
                return mcpkit.ErrorResult(err.Error()), nil
            }
            return mcpkit.TextResult("Hello, " + args.Name + "!"), nil
        },
    )

    // Streamable HTTP (recommended) — responses in HTTP body
    srv.ListenAndServe(mcpkit.WithStreamableHTTP(true))
}

Transports

Single endpoint, responses in HTTP body. Session via Mcp-Session-Id header.

srv.ListenAndServe(mcpkit.WithStreamableHTTP(true))
HTTP+SSE (MCP 2024-11-05) — legacy

Long-lived SSE stream + POST endpoint. Session tied to SSE connection.

srv.ListenAndServe() // SSE is the default
Both simultaneously
srv.ListenAndServe(mcpkit.WithStreamableHTTP(true), mcpkit.WithSSE(true))
// SSE at /mcp/sse + /mcp/message, Streamable HTTP at /mcp
Transport options
handler := srv.Handler(
    mcpkit.WithPrefix("/custom"),                       // URL prefix (default: /mcp)
    mcpkit.WithPublicURL("https://proxy.example.com"),  // for reverse proxy
    mcpkit.WithMaxSessions(100),                        // limit concurrent sessions
    mcpkit.WithKeepalivePeriod(15 * time.Second),       // SSE keepalive interval
    mcpkit.WithStreamableHTTP(true),                    // enable Streamable HTTP
)

Capabilities

Capability Methods
Tools tools/list, tools/call
Resources resources/list, resources/read, resources/templates/list
Prompts prompts/list, prompts/get
Logging logging/setLevel, notifications/message via EmitLog()
Cancellation notifications/cancelled with context propagation
Pagination Cursor-based pagination for all list methods

Capabilities are auto-advertised in the initialize response when the corresponding handlers are registered. Logging is always advertised.

Protocol Support

  • MCP 2025-11-25 and 2024-11-05 with automatic version negotiation
  • Initialization gating: requests rejected until initialize + notifications/initialized handshake completes
  • Tool error semantics: handler errors → isError: true in result (not JSON-RPC errors)

Testing

make test         # Unit tests (83 tests)
make testconf     # MCP conformance suite (requires Node.js)
make testall      # Both unit + conformance
make smoke        # Curl-based transport tests (SSE + Streamable HTTP)
make audit        # Security: govulncheck + gosec + gitleaks + race detection
make serve        # Start SSE test server on :8787
make serve-streamable  # Streamable HTTP on :8787
Conformance Suite

Validated against the official MCP conformance test suite. Current status: 20/30 scenarios passing (remaining require progress, sampling, elicitation, subscriptions, and Streamable HTTP SSE streaming — tracked in conformance/baseline.yml).

bash scripts/conformance-test.sh                    # full suite
bash scripts/conformance-test.sh tools-call-simple-text  # single scenario

When a feature's conformance scenario starts passing, remove it from conformance/baseline.yml — stale entries cause CI failure.

Manual testing
# Start test server (Streamable HTTP)
STREAMABLE=1 go run ./cmd/testserver

# MCP Inspector
npx @modelcontextprotocol/inspector
# Point it at http://localhost:8787/mcp

Stack Dependencies

Core module (github.com/panyam/mcpkit):

  • servicekit v0.0.14 — SSE connection/hub, graceful shutdown, HTTP middleware

Sub-module (github.com/panyam/mcpkit/auth) — separate go.mod:

  • oneauth — JWT/OIDC validation (only pulled in when you import this sub-module)

Documentation

Index

Constants

View Source
const (
	ErrCodeParse          = -32700
	ErrCodeInvalidRequest = -32600
	ErrCodeMethodNotFound = -32601
	ErrCodeInvalidParams  = -32602
	ErrCodeInternal       = -32603
)

Standard JSON-RPC error codes.

View Source
const ErrCodeCancelled = -32800

ErrCodeCancelled is the JSON-RPC error code for a cancelled request.

Variables

This section is empty.

Functions

func EmitLog added in v0.0.4

func EmitLog(ctx context.Context, level LogLevel, logger string, data any)

EmitLog sends a notifications/message to the connected client if the session's log level allows it. Safe to call even if no session context is present (no-op).

level is the severity of the message. logger is an optional logger name (typically the tool or subsystem name). data is the log payload (string, map, etc.).

Usage in a tool handler:

func myHandler(ctx context.Context, req mcpkit.ToolRequest) (mcpkit.ToolResult, error) {
    mcpkit.EmitLog(ctx, mcpkit.LogInfo, "my-tool", "processing started")
    // ... do work ...
    return mcpkit.TextResult("done"), nil
}

func Notify added in v0.0.4

func Notify(ctx context.Context, method string, params any) bool

Notify sends an arbitrary server-to-client JSON-RPC notification. Returns false if no notification sender is available in the context. This is the low-level API; prefer EmitLog for logging notifications.

Types

type AuthError

type AuthError struct {
	Code    int
	Message string
}

AuthError is returned when authentication fails.

func (*AuthError) Error

func (e *AuthError) Error() string

type AuthValidator

type AuthValidator interface {
	Validate(r *http.Request) error
}

AuthValidator validates an HTTP request and returns claims on success.

type ClientCapabilities

type ClientCapabilities struct {
	Sampling    *struct{} `json:"sampling,omitempty"`
	Roots       *RootsCap `json:"roots,omitempty"`
	Elicitation *struct{} `json:"elicitation,omitempty"`
}

ClientCapabilities describes features the client supports.

type ClientInfo

type ClientInfo struct {
	Name    string `json:"name"`
	Version string `json:"version"`
}

ClientInfo identifies the MCP client from the initialize request.

type Content

type Content struct {
	Type     string           `json:"type"`
	Text     string           `json:"text,omitempty"`
	MimeType string           `json:"mimeType,omitempty"`
	Data     string           `json:"data,omitempty"`
	Resource *ResourceContent `json:"resource,omitempty"`
}

Content is a single content item in a tool result. Supports text, image, audio, and embedded resource types per MCP spec.

type Dispatcher

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

Dispatcher routes JSON-RPC requests to the appropriate handler.

func NewDispatcher

func NewDispatcher(info ServerInfo) *Dispatcher

NewDispatcher creates a dispatcher with the given server identity.

func (*Dispatcher) Dispatch

func (d *Dispatcher) Dispatch(ctx context.Context, req *Request) *Response

Dispatch routes a JSON-RPC request and returns the response. Returns nil for notifications (no response expected).

func (*Dispatcher) NegotiatedVersion

func (d *Dispatcher) NegotiatedVersion() string

NegotiatedVersion returns the protocol version negotiated during initialization.

func (*Dispatcher) RegisterPrompt added in v0.0.3

func (d *Dispatcher) RegisterPrompt(def PromptDef, handler PromptHandler)

RegisterPrompt adds a prompt to the dispatcher.

func (*Dispatcher) RegisterResource added in v0.0.3

func (d *Dispatcher) RegisterResource(def ResourceDef, handler ResourceHandler)

RegisterResource adds a resource to the dispatcher.

func (*Dispatcher) RegisterResourceTemplate added in v0.0.3

func (d *Dispatcher) RegisterResourceTemplate(def ResourceTemplate, handler TemplateHandler)

RegisterResourceTemplate adds a URI template resource to the dispatcher.

func (*Dispatcher) RegisterTool

func (d *Dispatcher) RegisterTool(def ToolDef, handler ToolHandler)

RegisterTool adds a tool to the dispatcher.

type Error

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

Error is a JSON-RPC 2.0 error object.

type LogLevel added in v0.0.4

type LogLevel int

LogLevel represents MCP log severity levels (syslog-based, ascending severity). Used with logging/setLevel to control the minimum level of log notifications sent to the client, and with EmitLog to specify the severity of a message.

const (
	LogDebug     LogLevel = iota // debug: detailed debugging information
	LogInfo                      // info: general informational messages
	LogNotice                    // notice: normal but significant events
	LogWarning                   // warning: warning conditions
	LogError                     // error: error conditions
	LogCritical                  // critical: critical conditions
	LogAlert                     // alert: action must be taken immediately
	LogEmergency                 // emergency: system is unusable
)

func ParseLogLevel added in v0.0.4

func ParseLogLevel(s string) (LogLevel, bool)

ParseLogLevel converts a string to a LogLevel. Returns the level and true on success, or (LogDebug, false) for unknown strings.

func (LogLevel) String added in v0.0.4

func (l LogLevel) String() string

String returns the MCP wire name for the log level.

type LogMessage added in v0.0.4

type LogMessage struct {
	Level  string `json:"level"`
	Logger string `json:"logger,omitempty"`
	Data   any    `json:"data"`
}

LogMessage is the params payload for a notifications/message notification.

type NotifyFunc added in v0.0.4

type NotifyFunc func(method string, params any)

NotifyFunc sends a server-to-client JSON-RPC notification. method is the notification method (e.g., "notifications/message"). params will be JSON-marshaled as the notification's params field. This type is reusable for all server→client notifications (logging, progress, etc.).

type Option

type Option func(*serverOptions)

Option configures a Server.

func WithAllowedRoots

func WithAllowedRoots(roots ...string) Option

WithAllowedRoots restricts tool cwd to the given directory prefixes.

func WithAuth

func WithAuth(v AuthValidator) Option

WithAuth sets a custom auth validator (e.g. JWT via mcpkit/auth).

func WithBearerToken

func WithBearerToken(token string) Option

WithBearerToken sets a static bearer token for authentication. Uses constant-time comparison to prevent timing attacks.

func WithListen

func WithListen(addr string) Option

WithListen sets the HTTP listen address.

func WithToolTimeout

func WithToolTimeout(d time.Duration) Option

WithToolTimeout sets the maximum duration for tool execution.

type PromptArgument added in v0.0.3

type PromptArgument struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Required    bool   `json:"required,omitempty"`
}

PromptArgument describes a single argument to a prompt.

type PromptDef added in v0.0.3

type PromptDef struct {
	// Name is the prompt identifier used in prompts/get.
	Name string `json:"name"`

	// Title is an optional display title.
	Title string `json:"title,omitempty"`

	// Description explains what this prompt does.
	Description string `json:"description,omitempty"`

	// Arguments defines the parameters this prompt accepts.
	Arguments []PromptArgument `json:"arguments,omitempty"`
}

PromptDef describes a prompt exposed via MCP.

type PromptHandler added in v0.0.3

type PromptHandler func(ctx context.Context, req PromptRequest) (PromptResult, error)

PromptHandler generates prompt messages, optionally using arguments.

type PromptMessage added in v0.0.3

type PromptMessage struct {
	Role    string  `json:"role"`
	Content Content `json:"content"` // reuses Content from tool.go
}

PromptMessage is a single message in a prompt result.

type PromptRequest added in v0.0.3

type PromptRequest struct {
	Name      string
	Arguments map[string]string
}

PromptRequest is the validated input passed to a PromptHandler.

type PromptResult added in v0.0.3

type PromptResult struct {
	Description string          `json:"description,omitempty"`
	Messages    []PromptMessage `json:"messages"`
}

PromptResult is the response from a prompt handler.

type Request

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

Request is a JSON-RPC 2.0 request envelope.

func (*Request) IsNotification

func (r *Request) IsNotification() bool

IsNotification returns true if this request has no ID (JSON-RPC notification).

type ResourceContent

type ResourceContent struct {
	URI      string `json:"uri"`
	MimeType string `json:"mimeType,omitempty"`
	Text     string `json:"text,omitempty"`
	Blob     string `json:"blob,omitempty"`
}

ResourceContent is an embedded resource reference in a tool result.

type ResourceDef added in v0.0.3

type ResourceDef struct {
	// URI uniquely identifies this resource.
	URI string `json:"uri"`

	// Name is a human-readable short name.
	Name string `json:"name"`

	// Title is an optional display title.
	Title string `json:"title,omitempty"`

	// Description explains what this resource provides.
	Description string `json:"description,omitempty"`

	// MimeType is the MIME type of the resource content.
	MimeType string `json:"mimeType,omitempty"`
}

ResourceDef describes a resource exposed via MCP.

type ResourceHandler added in v0.0.3

type ResourceHandler func(ctx context.Context, req ResourceRequest) (ResourceResult, error)

ResourceHandler reads a resource by URI.

type ResourceReadContent added in v0.0.3

type ResourceReadContent struct {
	URI      string `json:"uri"`
	MimeType string `json:"mimeType,omitempty"`
	Text     string `json:"text,omitempty"`
	Blob     string `json:"blob,omitempty"`
}

ResourceReadContent is a single content item returned by resources/read. Either Text or Blob is set, not both.

type ResourceRequest added in v0.0.3

type ResourceRequest struct {
	URI string
}

ResourceRequest is the validated input passed to a ResourceHandler.

type ResourceResult added in v0.0.3

type ResourceResult struct {
	Contents []ResourceReadContent `json:"contents"`
}

ResourceResult is the response from a resource handler.

type ResourceTemplate added in v0.0.3

type ResourceTemplate struct {
	// URITemplate is an RFC 6570 URI template (e.g., "file:///{path}").
	URITemplate string `json:"uriTemplate"`

	// Name is a human-readable short name.
	Name string `json:"name"`

	// Title is an optional display title.
	Title string `json:"title,omitempty"`

	// Description explains what this template provides.
	Description string `json:"description,omitempty"`

	// MimeType is the default MIME type for resources matching this template.
	MimeType string `json:"mimeType,omitempty"`
}

ResourceTemplate describes a parameterized resource URI template.

type Response

type Response struct {
	JSONRPC string          `json:"jsonrpc"`
	ID      json.RawMessage `json:"id"`
	Result  json.RawMessage `json:"result,omitempty"`
	Error   *Error          `json:"error,omitempty"`
}

Response is a JSON-RPC 2.0 response.

func NewErrorResponse

func NewErrorResponse(id json.RawMessage, code int, message string) *Response

NewErrorResponse creates an error response for the given request ID.

func NewErrorResponseWithData

func NewErrorResponseWithData(id json.RawMessage, code int, message string, data any) *Response

NewErrorResponseWithData creates an error response with additional structured data. Used for protocol errors that carry machine-readable context (e.g., supported versions).

func NewResponse

func NewResponse(id json.RawMessage, result any) *Response

NewResponse creates a success response for the given request ID.

type RootsCap

type RootsCap struct {
	ListChanged bool `json:"listChanged,omitempty"`
}

RootsCap describes the client's roots capability.

type SSEData

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

SSEData represents SSE event data that is either raw text or pre-encoded JSON. MCP uses both: the "endpoint" event carries a plain URL string, while "message" events carry JSON-RPC response objects. This type implements json.Marshaler so the servicekit JSONCodec passes the bytes through without double-encoding.

func SSEJSON

func SSEJSON(j json.RawMessage) SSEData

SSEJSON creates an SSEData containing pre-encoded JSON bytes.

func SSEText

func SSEText(s string) SSEData

SSEText creates an SSEData containing raw text that will not be JSON-encoded.

type Server

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

Server is an MCP server that can run over multiple transports.

func NewServer

func NewServer(info ServerInfo, opts ...Option) *Server

NewServer creates an MCP server with the given identity and options.

func (*Server) CheckAuth

func (s *Server) CheckAuth(r *http.Request) error

CheckAuth validates an HTTP request against the server's auth configuration. Returns nil if no auth is configured or if the request is valid.

func (*Server) Dispatch

func (s *Server) Dispatch(ctx context.Context, req *Request) *Response

Dispatch routes a JSON-RPC request through the server's dispatch layer.

func (*Server) Handler

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

Handler returns an http.Handler implementing MCP transports. By default, only the legacy SSE transport is enabled. Use WithStreamableHTTP(true) to enable the Streamable HTTP transport (MCP 2025-03-26). Both transports can be enabled simultaneously for backward compatibility.

func (*Server) ListenAndServe

func (s *Server) ListenAndServe(opts ...TransportOption) error

ListenAndServe starts the HTTP transport(s) with graceful shutdown support. On SIGTERM/SIGINT it stops accepting new connections, closes active sessions, drains in-flight requests, and exits.

func (*Server) RegisterPrompt added in v0.0.3

func (s *Server) RegisterPrompt(def PromptDef, handler PromptHandler)

RegisterPrompt adds a prompt to the server.

func (*Server) RegisterResource added in v0.0.3

func (s *Server) RegisterResource(def ResourceDef, handler ResourceHandler)

RegisterResource adds a resource to the server.

func (*Server) RegisterResourceTemplate added in v0.0.3

func (s *Server) RegisterResourceTemplate(def ResourceTemplate, handler TemplateHandler)

RegisterResourceTemplate adds a URI template resource to the server.

func (*Server) RegisterTool

func (s *Server) RegisterTool(def ToolDef, handler ToolHandler)

RegisterTool adds a tool to the server.

type ServerInfo

type ServerInfo struct {
	Name         string `json:"name"`
	Version      string `json:"version"`
	Title        string `json:"title,omitempty"`
	Description  string `json:"description,omitempty"`
	Instructions string `json:"instructions,omitempty"`
	WebsiteURL   string `json:"websiteUrl,omitempty"`
}

ServerInfo identifies this MCP server in the initialize response.

type TemplateHandler added in v0.0.3

type TemplateHandler func(ctx context.Context, uri string, params map[string]string) (ResourceResult, error)

TemplateHandler reads a resource matched by a URI template. The uri parameter is the full resolved URI, params contains the extracted template variables.

type ToolDef

type ToolDef struct {
	// Name is the tool identifier used in tools/call.
	Name string `json:"name"`

	// Description is a human-readable summary of what the tool does.
	Description string `json:"description"`

	// InputSchema is the JSON Schema for the tool's arguments.
	// Typically a map[string]any with "type": "object", "properties": {...}, "required": [...].
	InputSchema any `json:"inputSchema"`
}

ToolDef describes a tool exposed via MCP.

type ToolHandler

type ToolHandler func(ctx context.Context, req ToolRequest) (ToolResult, error)

ToolHandler is the function signature for tool implementations.

type ToolRequest

type ToolRequest struct {
	// Name of the tool being called.
	Name string

	// Arguments is the raw JSON arguments from the tools/call params.
	Arguments json.RawMessage

	// RequestID is the JSON-RPC request ID.
	RequestID json.RawMessage
}

ToolRequest is the validated input passed to a ToolHandler.

func (*ToolRequest) Bind

func (r *ToolRequest) Bind(v any) error

Bind unmarshals the tool arguments into the provided struct.

type ToolResult

type ToolResult struct {
	// Content is the list of content items to return.
	Content []Content `json:"content"`

	// IsError indicates the tool execution failed (but the JSON-RPC call itself succeeded).
	IsError bool `json:"isError,omitempty"`
}

ToolResult is the response from a tool handler.

func ErrorResult

func ErrorResult(text string) ToolResult

ErrorResult creates a ToolResult marked as an error with the given message.

func TextResult

func TextResult(text string) ToolResult

TextResult creates a ToolResult with a single text content item.

type TransportOption

type TransportOption func(*transportConfig)

TransportOption configures the HTTP transports.

func WithKeepalivePeriod

func WithKeepalivePeriod(d time.Duration) TransportOption

WithKeepalivePeriod sets the interval for SSE keepalive comments.

func WithMaxSessions

func WithMaxSessions(n int) TransportOption

WithMaxSessions limits the number of concurrent sessions.

func WithPrefix

func WithPrefix(p string) TransportOption

WithPrefix sets the URL path prefix for transport endpoints.

func WithPublicURL

func WithPublicURL(u string) TransportOption

WithPublicURL sets the public base URL used in the SSE endpoint event. Use this when the server is behind a reverse proxy.

func WithSSE

func WithSSE(enabled bool) TransportOption

WithSSE enables or disables the legacy SSE transport (MCP 2024-11-05).

func WithStreamableHTTP

func WithStreamableHTTP(enabled bool) TransportOption

WithStreamableHTTP enables or disables the Streamable HTTP transport (MCP 2025-03-26).

Directories

Path Synopsis
cmd
testserver command
testserver is a minimal MCP server for manual testing and conformance validation.
testserver is a minimal MCP server for manual testing and conformance validation.
examples
common module
experimental
ext/events module
ext/protogen module
ext
auth module
otel module
protogen module
tasks module
ui module

Jump to

Keyboard shortcuts

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