mcp

package
v0.9.659 Latest Latest
Warning

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

Go to latest
Published: Aug 5, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package mcp implements the Model Context Protocol server side for Coremetry. MCP is JSON-RPC 2.0 over an HTTP+SSE transport, designed so external LLM clients (Claude Desktop, Anthropic API tool-calling integrations, internal copilots) can discover and invoke a server's tools, list its resources, and read its prompt templates.

What Coremetry exposes (built up across v0.6.4–v0.6.7):

v0.6.4 — core protocol scaffolding: JSON-RPC framing,
         HTTP+SSE transport, session lifecycle, initialize +
         ping methods. tools/resources/prompts capabilities
         advertised but their registries are empty.
v0.6.5 — tools/list + tools/call wired to a Coremetry tools
         registry (list_services, search_logs, get_trace,
         query_metric, list_problems, …). Salt-okunur —
         mutation tool'u bugüne dek hiç eklenmedi (v0.9.14
         audit'i: yazma gelirse audit_log source alanı şart).
v0.6.6 — resources/list + resources/read exposing
         traces/logs/metrics as MCP resources.
v0.6.7 — prompts/list + prompts/get exposing Coremetry's
         curated system prompts (Explain trace, Suggest
         runbook, Compare deploys, …) as MCP prompts so an
         external LLM can take the same systematic approach
         an operator sees in /problems.

Auth: the HTTP handlers in this package are wrapped by the same auth middleware as /api/*. Browser sessions work via the JWT cookie; programmatic clients (Claude Desktop, internal LLMs) obtain a token via POST /api/auth/login then carry it in the Authorization: Bearer header. This avoids inventing a separate API key system — viewer/editor/admin roles apply to MCP calls exactly as they do to REST.

Transport choice: this implementation uses the original HTTP+SSE transport (MCP spec 2024-11-05). Newer Streamable-HTTP (2025-03-26) is similar but uses a single endpoint + session header; can layer it on later without breaking existing clients.

References:

Index

Constants

View Source
const (
	ErrParse          = -32700
	ErrInvalidRequest = -32600
	ErrMethodNotFound = -32601
	ErrInvalidParams  = -32602
	ErrInternal       = -32603
	// ErrRateLimited (v0.9.14) — tools/call kapısının reddi; server-
	// error bandının tepesi. HTTP 429 yerine JSON-RPC hatası: LLM
	// tool-use döngüleri bunu sonuç olarak görür, bekleyip yeniden
	// dener.
	ErrRateLimited = -32000
)

Error codes — covers JSON-RPC base + MCP-specific.

View Source
const ProtocolVersion = "2024-11-05"

ProtocolVersion is what we advertise during initialize. Clients echoing a different version are negotiated against this — if the client's version isn't supported we still reply with ours per the MCP spec: "the server SHOULD respond with its own version" and let the client decide whether to proceed.

View Source
const ProtocolVersionStreamable = "2025-03-26"

ProtocolVersionStreamable — Streamable-HTTP transport'unun (v0.9.14) initialize yanıtında bildirdiği sürüm. SSE yolu 2024-11-05'te kalır; iki transport aynı dispatch gövdesini paylaşır, yalnız el-sıkışma sürümü ayrışır.

Variables

This section is empty.

Functions

func ExtractURITemplateValue added in v0.6.6

func ExtractURITemplateValue(template, concrete string) string

ExtractURITemplateValue pulls the placeholder value out of a concrete URI matched against its template. Exported so concrete Reader implementations don't have to re-implement the slicing. Returns "" if the template has no placeholder or the URI doesn't match.

Types

type CapPromptsBag

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

type CapResourcesBag

type CapResourcesBag struct {
	Subscribe   bool `json:"subscribe,omitempty"`
	ListChanged bool `json:"listChanged,omitempty"`
}

type CapToolsBag

type CapToolsBag struct {
	// ListChanged: server emits notifications/tools/list_changed
	// when its tool catalogue mutates. v0.6.4 returns false
	// (tools registry is empty + immutable); v0.6.5 flips this
	// if we ever support hot-loading tools.
	ListChanged bool `json:"listChanged,omitempty"`
}

type ClientInfo

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

type Error

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

Error follows JSON-RPC 2.0 — standard codes are -32700..-32603, implementation-defined server errors are -32099..-32000. MCP defines its own codes in the same -32xxx space which we hand out below.

type Prompt added in v0.6.7

type Prompt struct {
	Name        string
	Description string
	Arguments   []PromptArgument
	Renderer    PromptRenderer
}

Prompt is a server-curated LLM prompt template. prompts/list surfaces them so the client can show "/coremetry-explain- trace [trace_id]" in its slash-command menu; prompts/get with args runs the Renderer to produce the final messages the client feeds to its model.

Coremetry's prompts close over chstore/logstore so calling "explain_trace" actually fetches the trace data and embeds it into the user message — the LLM doesn't have to make a follow-up tool call before reasoning. This is the same pattern as the in-app "✨ Explain" button, just exposed via MCP for external clients.

type PromptArgument added in v0.6.7

type PromptArgument struct {
	Name        string
	Description string
	Required    bool
}

PromptArgument describes one input slot for prompts/get. Type is always implicitly string in the MCP spec; the description is what gets shown to the user when their client renders the argument form.

type PromptContent added in v0.6.7

type PromptContent struct {
	Type string `json:"type"`
	Text string `json:"text,omitempty"`
}

PromptContent is the body of a message. Type is currently always "text" for Coremetry's prompts — image / resource content can layer on later.

type PromptMessage added in v0.6.7

type PromptMessage struct {
	Role    string        `json:"role"`
	Content PromptContent `json:"content"`
}

PromptMessage is one role+content pair the Renderer emits. MCP prompts can return system + user + assistant messages; the client typically replays them as the conversation seed.

type PromptRenderer added in v0.6.7

type PromptRenderer func(ctx context.Context, args map[string]string) ([]PromptMessage, error)

PromptRenderer takes the prompts/get arguments and returns the resolved message set. Errors bubble up as JSON-RPC errors. Receives the raw arg map so the renderer picks its own decode.

type Request

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

Request is the incoming JSON-RPC message. ID is left as RawMessage so we can echo it back verbatim — clients send numbers, strings, or null per spec and we shouldn't coerce.

func (*Request) IsNotification

func (r *Request) IsNotification() bool

IsNotification — JSON-RPC spec: a request without an id is a notification, no response expected. notifications/initialized is the canonical example.

type Resource added in v0.6.6

type Resource struct {
	URI         string
	Name        string
	Description string
	MimeType    string
	// Reader returns the content for this URI. ctx is the live
	// request context; the URI is passed through unchanged so
	// the same Reader can serve a template family.
	Reader ResourceReader
}

Resource is a named read-only data reference. Where tools are "invoke an action with args", resources are "fetch the current state of <URI>". Browsers in Claude Desktop list resources as pinned references the user can click to attach; LLMs read them via resources/read.

URI scheme is opaque to the protocol — we use coremetry:// for everything Coremetry exposes (services, problems, etc.). MimeType hints how the client renders the read response; "application/json" is standard for our payloads.

type ResourceReader added in v0.6.6

type ResourceReader func(ctx context.Context, uri string) (text string, err error)

ResourceReader is the function signature both concrete and templated resources share. uri is the full URI being read. Returns either a text payload or an error; binary blobs can layer on later if needed (image/png, audio, etc.) — the spec supports them via a `blob` field on the content envelope.

type ResourceTemplate added in v0.6.6

type ResourceTemplate struct {
	URITemplate string
	Name        string
	Description string
	MimeType    string
	Reader      ResourceReader
}

ResourceTemplate is a URI pattern with {placeholder} segments. LLMs match against the pattern to discover that "coremetry://service/{name}" can be parameterised. The Reader receives the concrete URI; its handler parses out the placeholder values.

type Response

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

Response is the outgoing reply. Either Result or Error is set, never both. omitempty on both fields keeps the wire form clean.

type Server

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

Server is the MCP server. One instance per Coremetry process; holds the live session map + the registries that v0.6.5+ will populate.

The handler funcs HandleSSE + HandleMessage are wired onto the existing api.Server mux as /api/mcp/sse and /api/mcp/messages behind the standard auth middleware. Session state is in-memory only; a session is local to the pod the client first connected to. In distributed deploy + multi-replica api, a sticky-session LB rule (or session affinity at the ingress) is required — documented in v0.6.4's CHANGELOG.

func New

func New(name, version string) *Server

New constructs an MCP server with the given serverInfo. Name + version surface in the initialize response — clients (and Claude Desktop's debug UI) display them.

func (*Server) HandleMessage

func (s *Server) HandleMessage(w http.ResponseWriter, r *http.Request)

HandleMessage is the POST side of the transport. Client sends one JSON-RPC request; we respond synchronously via the SSE stream (not in the HTTP response body — per MCP spec, the POST returns 202 Accepted and the response rides the SSE channel).

This split lets the server send notifications + responses to unsolicited events back on the same SSE stream, which is what makes "live tool call" streaming work in v0.6.5+.

func (*Server) HandleSSE

func (s *Server) HandleSSE(w http.ResponseWriter, r *http.Request)

HandleSSE implements the GET side of the HTTP+SSE transport. The first event emitted is an `endpoint` event whose data is the absolute path the client should POST messages to — including the session id query param. All subsequent events are `message` events carrying JSON-RPC responses for requests the client POSTed.

func (*Server) HandleStreamable added in v0.9.14

func (s *Server) HandleStreamable(w http.ResponseWriter, r *http.Request)

HandleStreamable — Streamable-HTTP transport'u (v0.9.14, MCP spec 2025-03-26), STATELESS kipte: tek POST = tek JSON-RPC isteği, yanıt DOĞRUDAN gövdede (SSE kanalı yok). Session hiç üretilmez (Mcp-Session-Id başlığı dönülmez) — istemci sessionless çalışır. Bu, çok-pod'lu kurulumdaki pod-lokal session kırılganlığını (audit EK BULGU) kökten çözer: her POST bağımsızdır, LB'de hangi pod'a düşerse düşsün. Claude Code'un birincil `--transport http` yolu budur; SSE yolu eski istemciler için aynen kalır.

func (*Server) PromptCount added in v0.6.7

func (s *Server) PromptCount() int

PromptCount reports how many prompts are registered. Useful in boot logs so the operator can confirm the registry wired up.

func (*Server) RegisterPrompt added in v0.6.7

func (s *Server) RegisterPrompt(p Prompt)

RegisterPrompt adds a prompt to the registry. Name uniqueness enforced — duplicate registration logs and overwrites.

func (*Server) RegisterResource added in v0.6.6

func (s *Server) RegisterResource(r Resource)

RegisterResource adds a concrete (fixed-URI) resource. URI uniqueness enforced — duplicate registration logs a warning and overwrites.

func (*Server) RegisterResourceTemplate added in v0.6.6

func (s *Server) RegisterResourceTemplate(rt ResourceTemplate)

RegisterResourceTemplate adds a URI-pattern resource. The pattern uses {placeholder} segments per RFC 6570 level 1 — we match prefix + suffix around each placeholder without pulling in a full URI Template engine. Good enough for the patterns Coremetry exposes (single placeholder near the end).

func (*Server) RegisterTool added in v0.6.5

func (s *Server) RegisterTool(t Tool)

RegisterTool adds a tool to the registry. Caller MUST do this before serving requests — there's no list_changed notification yet (tools/list returns a static snapshot). Last writer wins on a duplicate name; we log because that's almost certainly a bug.

func (*Server) ResourceCount added in v0.6.6

func (s *Server) ResourceCount() (int, int)

ResourceCount reports the static + template totals so boot logs can confirm registration. Returns (static, templates).

func (*Server) SetToolCallGate added in v0.9.14

func (s *Server) SetToolCallGate(g ToolCallGate)

SetToolCallGate wires the optional rate-limit gate. Boot'ta bir kez çağrılır (api.SetMCP), sonrasında değişmez.

func (*Server) ToolCount added in v0.6.5

func (s *Server) ToolCount() int

ToolCount reports how many tools are registered. Useful in boot logs so the operator can confirm the registry wired up.

type ServerCapabilities

type ServerCapabilities struct {
	Tools     *CapToolsBag     `json:"tools,omitempty"`
	Resources *CapResourcesBag `json:"resources,omitempty"`
	Prompts   *CapPromptsBag   `json:"prompts,omitempty"`
	Logging   *struct{}        `json:"logging,omitempty"`
}

ServerCapabilities is what we tell the client we support. Each optional bag is "exists or doesn't" semantics — an empty struct signals "feature supported, no sub-features yet". As tools / resources / prompts come online in later releases the corresponding pointer gets non-nil.

v0.6.4 advertises tools/resources/prompts as supported (so clients run their listing dance against us) even though the registries are empty — keeps client codepaths warm and means the v0.6.5+ rollouts don't need a protocol bump.

type ServerInfo

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

type Tool added in v0.6.5

type Tool struct {
	Name        string
	Description string
	InputSchema map[string]any
	Handler     ToolHandler
}

Tool is an MCP tool definition. Tools are the LLM's callable surface — discoverable via tools/list, invoked via tools/call. The input schema is a JSON Schema object the client uses to validate args before invocation (and to render a form UI in Claude Desktop / similar inspectors).

The handler receives the raw params JSON as a RawMessage — concrete tool implementations decode into their own typed args struct. Return value is anything json.Marshal-able; the server wraps it in the tools/call response envelope.

Schema example (mirrors JSON Schema draft-2020-12 enough for every MCP client):

InputSchema: map[string]any{
    "type": "object",
    "properties": map[string]any{
        "service": map[string]any{"type": "string"},
        "range_ns": map[string]any{"type": "integer", "minimum": 0},
    },
    "required": []string{"service"},
}

type ToolCallGate added in v0.9.14

type ToolCallGate func(ctx context.Context, tool string) error

ToolCallGate — tools/call öncesi çağrılır; hata dönerse çağrı JSON-RPC -32000 ile reddedilir (HTTP 429 DEĞİL: istemci kütüphaneleri JSON-RPC hatasını LLM'e tool sonucu olarak gösterir, model bekleyip devam edebilir). initialize/tools/list/prompts kapı dışıdır (ucuz keşif).

type ToolHandler added in v0.6.5

type ToolHandler func(ctx context.Context, args json.RawMessage) (any, error)

ToolHandler runs one tools/call invocation. Returns a value the server marshals into the response, OR an error which is converted into a JSON-RPC error with code ErrInternal. The raw args are passed through so the handler picks its own decode (tagged struct, manual map dispatch, etc.).

Jump to

Keyboard shortcuts

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