probe

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Sep 15, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

The 2026-07-28 spec-readiness pass. It runs only against a remote that already answered a conformant initialize, and adds a handful of read-only, list-level JSON-RPC calls: a stateless tools/list, server/discover, a header-mismatch check, a subscriptions/listen existence check, one GET, a resources/read of a sentinel URI that cannot exist, and a fetch of the public OAuth protected-resource metadata. No call authenticates and no call executes a tool, so the zero-key pledge holds by construction; the sentinel read mirrors what mcp-spec-check ships as a standard check.

Ruleset: strategy/mcp-readiness-2026-07/RULESET.md in the RoninForge planning repo, compiled from the 2026-07-28 draft changelog and re-diffed against the published final text on 2026-08-03 with no verdict-bearing rule changed. RulesetVersion pins which revision of the rules produced a verdict. The numeric error codes still accept both the RC and the renumbered final values, and must keep doing so: servers built against the RC are real and still enforcing, so narrowing to the final numbers would silently reclassify them as not enforcing at all.

Package probe is the keyless MCP-server health engine. For a given server it runs only public, unauthenticated signals - repository liveness (GitHub API), package publication (npm, PyPI, Docker Hub anon), and remote reachability (a plain GET plus a capability-only MCP `initialize` handshake) - and classifies the server as healthy, degraded, dead, or unknown.

It never authenticates to a probed server and never executes a tool. Any GitHub token supplied is used solely against the public GitHub API as a higher-rate-limit measurement instrument, exactly as a human running `gh` would. Nothing here touches a user secret. This keeps the zero-key pledge true by construction.

Index

Constants

View Source
const (
	ReadinessReady          = "ready"
	ReadinessNeedsMigration = "needs-migration"
	ReadinessAtRisk         = "at-risk"
)

Readiness verdict values, per the ruleset: first match wins in the order at-risk, ready, needs-migration. A server with no conformant remote gets no ReadinessSignal at all, which is the "unknown" bucket.

View Source
const DefaultUserAgent = "akashi (mcp health probe; keyless; +https://roninforge.org)"

DefaultUserAgent identifies akashi politely to every third party it probes.

View Source
const HTTPClientTimeout = 60 * time.Second

HTTPClientTimeout is a whole-request backstop on every probe client.

It is NOT the probe's normal budget: RemoteTimeout, RequestTimeout and the per-server context all expire long before it, so in ordinary operation it never fires. It exists because a context deadline cannot protect a call that never receives the context, and at least one such call is on the hot path. The MCP Go SDK tears a failed session down in streamableClientConn.Close, which issues its session-delete request on a context-free client, by design: cleanup must not be skipped just because the caller's context was already cancelled. Against an endpoint that completes a TCP handshake and then never answers, that request has nothing to bound it.

Lived consequence, 2026-08-03: one such endpoint hung a worker on the final server of a 19,788-server census. The per-server 60s deadline had already expired and could not help. The pool drained, the WaitGroup never completed, and the run sat blocked in probeAll for 95 minutes with no summary written, after four and a half hours of good work.

View Source
const RulesetVersion = "2026-07-28"

RulesetVersion identifies the readiness ruleset a verdict was computed under.

Re-diffed against the final specification text on 2026-08-03: every verdict-bearing rule is unchanged from the RC, the error renumbering landed exactly as the ruleset anticipated (and the probe already accepts both numbers), and the only removal absent from the ruleset is not keylessly observable. So no rule changed here, only the name: what was provisional is now confirmed.

Censuses already stamped "2026-07-28-rc" keep that stamp. It names the revision that actually computed them, and rewriting a published record to look tidier is the one edit a provenance dataset must never make.

Variables

This section is empty.

Functions

This section is empty.

Types

type Check

type Check struct {
	Name   string `json:"name"`
	Status Status `json:"status"`
	Detail string `json:"detail,omitempty"`
}

Check is one line in the human-readable report.

type Engine

type Engine struct {
	HTTP *http.Client
	// UserAgent is sent on every request.
	UserAgent string
	// GitHubToken, when set, is sent only to api.github.com to raise the rate
	// limit. It is never sent to a probed server.
	GitHubToken string
	// Now supplies the clock; overridable in tests.
	Now func() time.Time
	// RemoteTimeout bounds each remote-endpoint request.
	RemoteTimeout time.Duration
	// RequestTimeout bounds each registry/repo/package request.
	RequestTimeout time.Duration
	// ProbeTools runs the go-sdk tools/list conformance probe on reachable,
	// conformant remotes. Default true; a bulk scan can disable it for speed.
	ProbeTools bool
	// ProbeReadiness runs the 2026-07-28 spec-readiness pass against the
	// first conformant remote. Default true.
	ProbeReadiness bool
	// ValidateServerJSON validates a registry server's server.json against its
	// declared JSON Schema. Default true.
	ValidateServerJSON bool
	// contains filtered or unexported fields
}

Engine runs keyless probes. The zero value is not usable; call NewEngine.

func NewEngine

func NewEngine() *Engine

NewEngine returns an Engine with sane defaults.

func (*Engine) ProbeServer

func (e *Engine) ProbeServer(ctx context.Context, s registry.Server) Result

ProbeServer runs the full keyless check set against one server and returns a classified Result.

type PackageSignal

type PackageSignal struct {
	Type       string `json:"type"` // npm|pypi|oci
	ID         string `json:"id"`
	Status     string `json:"status"` // published|missing|unpublished|unprobed|error
	HTTPStatus int    `json:"httpStatus,omitempty"`
	Latest     string `json:"latest,omitempty"`
	Detail     string `json:"detail,omitempty"`
}

PackageSignal is the raw package probe result for one entrypoint.

type ReadinessSignal added in v0.4.0

type ReadinessSignal struct {
	RulesetVersion string `json:"rulesetVersion"`
	URL            string `json:"url"`

	// Stateless core (B2, B3): does the server answer without a handshake,
	// and does it implement the new required discovery RPC.
	StatelessAccepted bool     `json:"statelessAccepted"`
	DiscoverSupported bool     `json:"discoverSupported"`
	DiscoverVersions  []string `json:"discoverVersions,omitempty"`

	// Sessions (B1): whether the initialize response minted an
	// Mcp-Session-Id, and whether requests are rejected without it.
	SessionIssued   bool `json:"sessionIssued"`
	SessionRequired bool `json:"sessionRequired"`

	// Streams (B4, D2): the new listen RPC, and what a plain GET returns.
	// "legacy-sse" means the pre-Streamable-HTTP endpoint event was seen.
	SubscriptionsListen bool   `json:"subscriptionsListen"`
	GetStream           string `json:"getStream,omitempty"` // legacy-sse|sse|405|other

	// Capability-derived (B6, D1).
	TasksExperimental bool `json:"tasksExperimental"`
	LoggingDeclared   bool `json:"loggingDeclared"`

	// tools/list result conformance (B8, B11). Advisory, never verdict-blocking.
	ResultTypePresent bool `json:"resultTypePresent"`
	CacheMetaPresent  bool `json:"cacheMetaPresent"`

	// Routing headers (B10, B13). The enforced values record which numeric
	// code came back, distinguishing RC-beta builds from final builds.
	HeaderEnforcement string `json:"headerEnforcement,omitempty"` // enforced-32020|enforced-32001|ignored|unknown

	// Resource error code migration (B12). 0 when not tested (no resources
	// capability declared).
	ResourceNotFoundCode int `json:"resourceNotFoundCode,omitempty"`

	// Auth metadata (A1): public RFC 9728 protected-resource metadata.
	ProtectedResourceMetadata bool `json:"protectedResourceMetadata"`

	// Declared layer (D2): the transport the registry declares for this
	// remote, verdict-bearing when it is the deprecated "sse".
	DeclaredTransport string `json:"declaredTransport,omitempty"`

	Verdict  string   `json:"verdict"`
	Reasons  []string `json:"reasons,omitempty"`
	Warnings []string `json:"warnings,omitempty"`
}

ReadinessSignal is the raw 2026-07-28 readiness evidence for one server, gathered from its first conformant remote, plus the verdict derived from it. Every field is an observable; the verdict is a pure function of them.

type RemoteSignal

type RemoteSignal struct {
	URL         string `json:"url"`
	Type        string `json:"type,omitempty"`
	Status      string `json:"status"` // reachable|unreachable|server_error|not_found
	HTTPStatus  int    `json:"httpStatus,omitempty"`
	Conformance string `json:"conformance,omitempty"` // initialize_ok|auth_gated|reachable_nonconformant
	Probe       string `json:"probe,omitempty"`       // get|initialize
	IDEchoed    *bool  `json:"idEchoed,omitempty"`    // did the initialize response echo the JSON-RPC id
	Detail      string `json:"detail,omitempty"`
	// tools/list probe, run with the official MCP client after a conformant
	// initialize. A completed session that lists tools is the strongest keyless
	// proof that this is a real, working MCP server.
	ToolsStatus string   `json:"toolsStatus,omitempty"` // ok|empty|error|connect_failed
	ToolCount   int      `json:"toolCount,omitempty"`
	ToolNames   []string `json:"toolNames,omitempty"`
	// initialize-response evidence, persisted only on a conformant handshake
	// (Conformance == initialize_ok). These are the raw observables a spec-
	// readiness classification is computed from downstream; the probe records,
	// it does not judge.
	ProtocolVersion string   `json:"protocolVersion,omitempty"` // version the server answered with
	Capabilities    []string `json:"capabilities,omitempty"`    // sorted top-level server capability keys
	SessionIssued   *bool    `json:"sessionIssued,omitempty"`   // response carried an Mcp-Session-Id header
	// contains filtered or unexported fields
}

RemoteSignal is the raw remote-endpoint probe result.

type RepoSignal

type RepoSignal struct {
	Kind       string `json:"kind"`   // github|other|none
	Status     string `json:"status"` // alive|archived|missing|error|unprobed|none
	HTTPStatus int    `json:"httpStatus,omitempty"`
	Archived   bool   `json:"archived,omitempty"`
	PushedAt   string `json:"pushedAt,omitempty"`
	AgeDays    *int   `json:"ageDays,omitempty"`
	Stars      *int   `json:"stars,omitempty"`
	License    string `json:"license,omitempty"`
	URL        string `json:"url,omitempty"`
	Detail     string `json:"detail,omitempty"`
}

RepoSignal is the raw repository probe result.

type Result

type Result struct {
	Name              string   `json:"name"`
	Title             string   `json:"title,omitempty"`
	Description       string   `json:"description,omitempty"`
	RegistryStatus    string   `json:"registryStatus,omitempty"`
	Version           string   `json:"version,omitempty"`
	Verdict           Verdict  `json:"verdict"`
	Reasons           []string `json:"reasons"`
	Checks            []Check  `json:"checks"`
	Signals           Signals  `json:"signals"`
	AliveEntrypoints  int      `json:"aliveEntrypoints"`
	ProbedEntrypoints int      `json:"probedEntrypoints"`
	CheckedAt         string   `json:"checkedAt"` // YYYY-MM-DD, UTC
	// Readiness is the 2026-07-28 spec-readiness pass, run against the first
	// conformant remote. Nil when the server has no keylessly reachable MCP
	// endpoint: that is the "unknown" bucket, absence of evidence recorded as
	// absence. Orthogonal to Verdict, which stays a pure health measure.
	Readiness *ReadinessSignal `json:"readiness,omitempty"`
}

Result is the per-server probe outcome: a verdict, the reasons behind it, a display-ready check list, and the raw signals for machine consumers. Title and Description are carried through unchanged from the registry (see registry.Server) so a downstream index page has a human title and a meta description without a second registry lookup; both are empty when the target was not resolved from the registry (a bare repo or remote URL) or when the registry itself has no title for that server.

func (Result) OK

func (r Result) OK() bool

OK reports whether the verdict is one a badge should render green.

type ServerJSONSignal added in v0.2.0

type ServerJSONSignal struct {
	Status string   `json:"status"` // valid|invalid|no_schema|absent|error
	Schema string   `json:"schema,omitempty"`
	Errors []string `json:"errors,omitempty"`
}

ServerJSONSignal is the result of validating the server's published server.json against its declared JSON Schema.

type Signals

type Signals struct {
	Repo       RepoSignal       `json:"repo"`
	Packages   []PackageSignal  `json:"packages"`
	Remotes    []RemoteSignal   `json:"remotes"`
	ServerJSON ServerJSONSignal `json:"serverJson"`
}

Signals is the full raw evidence behind a verdict.

type Status

type Status string

Status is a single check outcome for display.

const (
	Pass Status = "pass"
	Warn Status = "warn"
	Fail Status = "fail"
	Skip Status = "skip"
)

Status values for a single check line.

type Verdict

type Verdict string

Verdict is the overall health classification of a server.

const (
	Healthy  Verdict = "healthy"  // at least one live entrypoint and nothing broken
	Degraded Verdict = "degraded" // usable, but something is broken
	Dead     Verdict = "dead"     // registry-deleted, or every probed entrypoint is broken
	Unknown  Verdict = "unknown"  // only un-probeable entrypoints declared
)

Verdict values, from best to worst health.

Jump to

Keyboard shortcuts

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