import "github.com/kinorai/omnifeed/internal/transport/mcp"
Package mcp implements a minimal Model Context Protocol server.
The server speaks JSON-RPC 2.0 over two transports:
The server is dual-era. MCP 2026-07-28 removed the initialize handshake: every request carries its protocol version itself (the MCP-Protocol-Version header plus `_meta`), capabilities are fetched via server/discover, and the required Mcp-Method/Mcp-Name routing headers must match the body. Requests in that shape get the modern treatment (resultType, cache hints, serverInfo in `_meta`, HTTP 404 for unknown methods). Everything else — including all initialize-era clients back to 2024-11-05 — gets the exact legacy behavior, byte-compatible with what this server always returned. The era is decided per request, so old and new clients coexist on the same endpoint.
For backwards compatibility with older clients that only speak the deprecated dual-endpoint SSE shape, the server also exposes /mcp/sse as a legacy alias — same handler as the GET path of /mcp. New clients should target /mcp; /mcp/sse is preserved for compat and may eventually be removed.
The server is a pure transport: it owns JSON-RPC framing, auth, and SSE keepalive, and dispatches tools/list and tools/call against the Tool slice it was configured with. The tools themselves (fetch_url, web_search) live in the tools subpackage and are wired in by main.
func InvalidParams(msg string) error
InvalidParams returns a ParamError with the given message.
Config configures the Server.
Tools is the ordered list surfaced by tools/list and dispatched by tools/call. Authenticator gates the HTTP transport (POST /mcp and GET /mcp/sse). The stdio transport is unaffected because it runs as a local subprocess and inherits trust from its parent. If nil, auth.AlwaysAllow is used.
type Config struct {
Tools []Tool
Authenticator auth.Authenticator
Logger *slog.Logger
}
ParamError marks a tools/call failure as a caller mistake (JSON-RPC -32602 invalid params) instead of an internal error. Handlers return it via InvalidParams.
type ParamError struct {
// contains filtered or unexported fields
}
func (e ParamError) Error() string
Server is a JSON-RPC 2.0 MCP server.
type Server struct {
// contains filtered or unexported fields
}
func New(cfg Config) *Server
New constructs the server.
func (s *Server) Register(mux *http.ServeMux)
Register attaches the MCP HTTP routes behind the configured authenticator.
Both routes share the same bearer-token check. Origin validation (the DNS-rebinding guard the transport spec requires) is applied one level up: main wraps every HTTP mux in auth.OriginGuard, so the loader and search transports in the same binary are covered by the same guard.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP handles MCP-over-HTTP/SSE per the Streamable HTTP transport spec. Each POST to the path is a single JSON-RPC request; the response is returned in the body. Optionally, GET on the same path opens an SSE stream for server-initiated messages (none today — kept open for future server notifications).
This is the minimum-viable implementation: synchronous request/response.
func (s *Server) ServeStdio(ctx context.Context, in io.Reader, out io.Writer) error
ServeStdio reads JSON-RPC messages from in, dispatches them, and writes responses to out. Notifications (id absent) produce no response.
Blocks until ctx is canceled or in returns EOF.
Tool is one MCP tool: the schema surfaced by tools/list plus the handler invoked by tools/call. The transport stays generic — domain-specific behavior lives in the handlers (see the tools subpackage), so adding a tool never touches the JSON-RPC plumbing.
type Tool struct {
Name string
Description string
InputSchema map[string]any
// Annotations are optional MCP ToolAnnotations surfaced in tools/list —
// behavioral hints like readOnlyHint and openWorldHint that let clients
// decide how much friction to put in front of a call (a read-only tool can
// be auto-approved). nil sends no annotations.
Annotations map[string]any
// Meta is serialized as the tool's `_meta` object in tools/list — the MCP
// escape hatch for client-specific hints. fetch_url uses it to declare
// `anthropic/maxResultSizeChars`, which raises Claude Code's per-tool text
// cap; clients that don't know the key ignore it. nil sends no `_meta`.
Meta map[string]any
Handle func(ctx context.Context, args map[string]any) (ToolResult, error)
}
ToolResult is what a Tool handler returns: the text content plus optional metadata serialized into the response _meta field.
type ToolResult struct {
Text string
Meta map[string]string
}
Generated by gomarkdoc