Documentation
¶
Index ¶
- Constants
- Variables
- func ArgsHash(args any) string
- func ArgsHashRaw(raw json.RawMessage) string
- func Canonicalize(v any) (string, error)
- func FlushAll()
- func NewSessionID() string
- func Watch(server *mcp.Server, opts Options) *mcp.Server
- func WrapTool[In, Out any](handler mcp.ToolHandlerFor[In, Out]) mcp.ToolHandlerFor[In, Out]
- type Options
- type Outcome
- type Payload
- type ToolInfo
Constants ¶
const DefaultEndpoint = "https://api.getmcpulse.com"
DefaultEndpoint is where payloads go when Endpoint is not set.
const Unhashable = "000000000000"
Unhashable is what an argument set hashes to when it cannot be serialised.
const WireVersion = 1
WireVersion is bumped only for a breaking change; the API rejects anything else.
Variables ¶
var ErrNotJSON = errors.New("mcpulse: value cannot be represented as JSON")
ErrNotJSON is returned for anything JSON cannot represent: NaN, an infinity, a channel, a cycle.
Functions ¶
func ArgsHash ¶
ArgsHash is a short, one-way fingerprint of a call's arguments.
This is the only thing MCPulse ever learns about what was passed to a tool, and it is deliberately not enough to learn anything: 12 hex characters of a SHA-256 over the RFC 8785 canonical form, with no way back. All the product asks of it is "were these two calls made with the same arguments or different ones" — which is what separates a model retrying a reworded request from a client paging through results.
func ArgsHashRaw ¶
func ArgsHashRaw(raw json.RawMessage) string
ArgsHashRaw hashes arguments still in their wire form.
The Go MCP SDK hands tool arguments over as json.RawMessage, so this is the path a real call takes: decoding here rather than accepting the SDK's typed struct means the hash is computed from what the client actually sent, before defaults are applied or unknown fields are dropped.
func Canonicalize ¶
Canonicalize returns the RFC 8785 canonical JSON form of v.
It is written for the shapes encoding/json produces when decoding into any — map[string]any, []any, float64, string, bool, nil — which is what arrives from the wire. Other Go values are marshalled to JSON and re-read first, so a struct canonicalises as the JSON it would have become.
func FlushAll ¶
func FlushAll()
FlushAll sends everything buffered and stops accepting more.
Call it from a defer in main if the server shuts down without a signal — Go cannot hook process exit, so this is the one part of the SDK a Go server may have to invoke by hand.
func NewSessionID ¶
func NewSessionID() string
NewSessionID identifies one run of the customer's server, so calls can be grouped and a cost-per-session worked out.
Random rather than derived — there is nothing about the process worth encoding here, and anything derived from the machine would be an identifier we did not intend to collect.
func Watch ¶
Watch starts recording what this MCP server does.
server := mcp.NewServer(&mcp.Implementation{Name: "my-server"}, nil)
mcpulse.Watch(server, mcpulse.Options{Key: "mp_live_…"})
The server is instrumented in place and handed straight back, so the call can be dropped around an existing one without moving anything else.
Nothing here is allowed to break the server it is measuring. If anything goes wrong attaching, the server is returned untouched and the process carries on without analytics, because a customer's tool failing over our telemetry is worse than no telemetry.
func WrapTool ¶
func WrapTool[In, Out any](handler mcp.ToolHandlerFor[In, Out]) mcp.ToolHandlerFor[In, Out]
WrapTool wraps a tool handler so MCPulse can tell a crash from a rejected argument set.
mcp.AddTool(server, tool, mcpulse.WrapTool(myHandler))
This is optional, and what it buys is precision. The Go SDK validates arguments against the input schema and, on failure, returns a CallToolResult with IsError set — the same shape a handler that returned an error produces. From outside the handler the two are identical, and reading the difference back out of an error string is not an interface anyone promised to keep.
Without it a call that failed validation is reported as tool_error rather than bad_args, and nothing else changes: timing, argument hashes, response sizes and emptiness all work from Watch alone.
Types ¶
type Options ¶
type Options struct {
// Key is the ingest key, mp_live_…, minted per MCP in the dashboard.
Key string
// Endpoint overrides where payloads are sent. Point this at a local API
// while developing.
Endpoint string
// Disabled makes Watch a no-op — useful in tests and CI.
//
// Negative rather than an Enabled bool so the zero value of Options is the
// working one: mcpulse.Options{Key: k} must not be silently switched off.
Disabled bool
// Debug logs what is being sent, and why a send failed, to stderr.
Debug bool
}
Options is everything Watch accepts.
type Outcome ¶
type Outcome string
Outcome is how a tool call ended. Exactly one of these, always.
const ( // OutcomeOK means the tool ran and returned a result. OutcomeOK Outcome = "ok" // OutcomeBadArgs means arguments failed validation and the handler never ran. OutcomeBadArgs Outcome = "bad_args" // OutcomeToolError means the tool ran and returned isError: true. OutcomeToolError Outcome = "tool_error" // OutcomeCrashed means the tool threw. OutcomeCrashed Outcome = "crashed" )
type Payload ¶
type Payload struct {
V int `json:"v"`
Type string `json:"type"`
SessionID string `json:"session_id"`
ClientName string `json:"client_name"`
// startup
Tools []ToolInfo `json:"tools,omitempty"`
// call
ToolName string `json:"tool_name,omitempty"`
StartedAt string `json:"started_at,omitempty"`
DurationMS int64 `json:"duration_ms,omitempty"`
Outcome Outcome `json:"outcome,omitempty"`
ResponseBytes int `json:"response_bytes,omitempty"`
IsEmpty bool `json:"is_empty,omitempty"`
ArgsHash string `json:"args_hash,omitempty"`
}
Payload is a startup or a call record. One struct rather than two, because the batch is heterogeneous and omitempty keeps each shape to its own fields.