mcp

package
v0.52.0 Latest Latest
Warning

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

Go to latest
Published: Aug 1, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package mcp exposes Draugr to AI coding agents over the Model Context Protocol.

The reason this exists is narrower than "agents are popular". An agent asked to check a change for security problems will do it one way or another: if Draugr isn't callable it improvises — shells out to whatever scanner it can find, picks its own scope, and reads raw tool output in its own context window. That improvised answer has no recorded scope, no organizational risk context, and no relationship to what CI will decide. Being callable is what makes the agent's answer and the pipeline's answer the same answer.

Two design rules follow from that:

  • **Read-only by default.** Scanning clones repositories, executes external tools and reaches the network. An agent triggering that unprompted is a bad surprise, so `scan` is registered only when the operator opts in. Everything else is safe to call freely.
  • **Return decisions, not data.** A tool that hands back raw scanner output has moved the problem into the agent's context window rather than solving it. These tools return prioritized, deduplicated, normalized results — the same thing a person sees.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewServer

func NewServer(opts Options) (*mcp.Server, error)

NewServer builds the MCP server. Tools are registered here rather than discovered so the exposed surface is a deliberate, reviewable list.

Types

type CheckToolsInput added in v0.39.0

type CheckToolsInput struct {
	Path string `json:"path,omitempty" jsonschema:"path to a Saga descriptor; without it, every tool Draugr knows about is checked"`
}

CheckToolsInput optionally narrows the check to what one descriptor actually needs.

type CheckToolsOutput added in v0.39.0

type CheckToolsOutput struct {
	Ready   bool         `json:"ready" jsonschema:"true when nothing required is missing"`
	Tools   []ToolStatus `json:"tools"`
	Missing []string     `json:"missing,omitempty" jsonschema:"required tools that are absent"`
	// Remedy is the single command that installs everything missing.
	Remedy string `json:"remedy,omitempty"`
	Note   string `json:"note"`
}

CheckToolsOutput is the answer to "can Draugr actually scan here?".

func CheckToolsTool added in v0.39.0

CheckToolsTool reports which external scanners are present.

This exists because of what Draugr does when one is missing: the control can't run, and a scan that can't run is not a pass. An assistant that hits that needs to say what's wrong and what fixes it — which is a question about the machine, and answering it is free.

It deliberately stops there. Draugr will not install anything on a user's behalf over MCP: that's a write to their machine, and their client already has a permission model for running commands which is stronger than anything this server could offer. Report the command; let the person approve it where they already approve such things.

type Control

type Control struct {
	Name            string   `json:"name" jsonschema:"the control's name, as used under config.controllers in a Saga"`
	Scope           string   `json:"scope" jsonschema:"whether the control runs per component or once for the project"`
	Purpose         string   `json:"purpose" jsonschema:"what the control checks for"`
	DefaultScanners []string `json:"defaultScanners" jsonschema:"scanners that run when the control is enabled"`
	OptInScanners   []string `` /* 134-byte string literal not displayed */
}

Control is one control an agent could enable.

type ControlsOutput

type ControlsOutput struct {
	Controls []Control `json:"controls"`
	// Hint is where the agent should put what it learned. Returning capability without saying
	// how to use it just moves the guesswork somewhere else.
	Hint string `json:"hint"`
}

ControlsOutput is the list_controls result.

func ListControls

func ListControls(reg *engine.Registry) ControlsOutput

ListControls reports the controls this build registers.

type Counts

type Counts struct {
	Critical int `json:"critical"`
	High     int `json:"high"`
	Medium   int `json:"medium"`
	Low      int `json:"low"`
}

Counts tallies the whole report, not just what was returned — a narrowed list shouldn't make the rest of the backlog look like it vanished.

type EmptyInput

type EmptyInput struct{}

EmptyInput is the argument type for tools that take none. The SDK derives a schema from the input type, and an empty struct is how you say "no arguments" rather than "any arguments".

type Finding

type Finding struct {
	Priority string  `json:"priority,omitempty" jsonschema:"P1 (act now) through P4 (lowest)"`
	Severity string  `json:"severity" jsonschema:"critical, high, medium or low"`
	Score    float64 `json:"score,omitempty" jsonschema:"CVSS-style numeric severity, when the scanner gave one"`
	RuleID   string  `json:"ruleId"`
	Scanner  string  `json:"scanner,omitempty"`
	Location string  `json:"location,omitempty" jsonschema:"file:line, image reference, or endpoint"`
	Message  string  `json:"message"`
	HelpURI  string  `json:"helpUri,omitempty" jsonschema:"where the rule is documented; fetch this rather than guessing what the rule means"`
}

Finding is one prioritized result, flattened to what a reader needs to act.

type Options

type Options struct {
	// Scan says whether a client may start scans. The zero value is ScanOff.
	Scan ScanMode
	// Registry supplies the controllers and scanners. Required.
	Registry *engine.Registry
	// Root is the directory searched for Saga descriptors to expose as resources. Empty means
	// the working directory.
	Root string
}

Options configures the server's exposed surface.

type ScanInput

type ScanInput struct {
	Path        string `json:"path" jsonschema:"path to the Saga descriptor to scan"`
	MinPriority string `json:"minPriority,omitempty" jsonschema:"only return findings at this priority or above: p1, p2, p3 or p4"`
	Limit       int    `json:"limit,omitempty" jsonschema:"maximum findings to return; defaults to 20"`
}

ScanInput selects what to scan.

type ScanMode

type ScanMode string

ScanMode says whether, and on what terms, a client may start a scan.

const (
	// ScanOff doesn't register the tool at all. The default: an assistant can't set off work
	// like that because it was curious, and the read-only tools are where the value starts.
	ScanOff ScanMode = "off"
	// ScanAsk registers it and asks the user to approve each call, through the client. This is
	// the mode to want — permission granted for the scan in front of you rather than for every
	// scan this session — but it needs a client that implements elicitation, and many don't.
	ScanAsk ScanMode = "ask"
	// ScanAlways registers it and runs without asking. Right for a sandbox or CI, where there's
	// nobody to ask.
	ScanAlways ScanMode = "always"
)

The scan modes. A scan clones repositories, executes external tools and reaches the network, so the question isn't only "may it" but "who agrees to it, and when".

func ParseScanMode

func ParseScanMode(s string) (ScanMode, error)

ParseScanMode validates a mode name.

type ScanOutput

type ScanOutput struct {
	Verdict string `json:"verdict" jsonschema:"pass or fail, by the same policy the CI gate applies"`
	SummarizeOutput
}

ScanOutput is the verdict plus the ranked findings behind it.

type SchemaOutput

type SchemaOutput struct {
	Schema  map[string]any `json:"schema" jsonschema:"the JSON Schema for a Saga descriptor"`
	Version string         `json:"draugrVersion" jsonschema:"the Draugr build this schema came from"`
	Hint    string         `json:"hint"`
}

SchemaOutput carries the descriptor's JSON Schema.

Schema is a decoded object rather than raw bytes so a client receives the schema itself: MCP validates a tool's output against the schema derived from this type, and a []byte field derives as an array, which the schema document then fails to be.

func GetSchema

func GetSchema() (SchemaOutput, error)

GetSchema returns the schema embedded in this binary — the one that will actually be enforced, as opposed to whatever is published on the web for some other version.

type SummarizeInput

type SummarizeInput struct {
	Path string `json:"path" jsonschema:"path to a results.sarif or report.json produced by draugr scan"`
	// MinPriority narrows the list the way --min-priority does on the CLI.
	MinPriority string `json:"minPriority,omitempty" jsonschema:"only return findings at this priority or above: p1, p2, p3 or p4"`
	// Limit caps how many findings come back. Context is the scarce resource here.
	Limit int `json:"limit,omitempty" jsonschema:"maximum findings to return; defaults to 20"`
}

SummarizeInput points at a report already on disk.

type SummarizeOutput

type SummarizeOutput struct {
	Total    int       `json:"total" jsonschema:"findings in the report before any filtering"`
	Returned int       `json:"returned"`
	Counts   Counts    `json:"counts"`
	Findings []Finding `json:"findings"`
	Note     string    `json:"note,omitempty"`
}

SummarizeOutput is the ranked answer to "what should I fix?".

func SummarizeReportTool

SummarizeReportTool reads a report from disk and returns it ranked.

type ToolStatus added in v0.39.0

type ToolStatus struct {
	Name      string `json:"name"`
	Installed bool   `json:"installed"`
	Version   string `json:"version,omitempty"`
	Path      string `json:"path,omitempty"`
	Category  string `json:"category,omitempty" jsonschema:"scanner (backs a control) or utility (supporting, e.g. git)"`
	Optional  bool   `json:"optional,omitempty" jsonschema:"absence of an optional tool is not a problem"`
	// Install is the command a person would run. Draugr will not run it for you.
	Install string `json:"install,omitempty"`
}

ToolStatus is one external tool Draugr would execute.

type ValidateInput

type ValidateInput struct {
	Path    string `json:"path,omitempty" jsonschema:"path to a Saga descriptor on disk"`
	Content string `json:"content,omitempty" jsonschema:"the descriptor's YAML content, for validating an edit before writing it"`
}

ValidateInput accepts a descriptor either by path or inline.

type ValidateOutput

type ValidateOutput struct {
	Valid bool `json:"valid"`
	// Error is why it isn't, in the same words the CLI would use.
	Error string `json:"error,omitempty"`
	// Components and Controls describe what the descriptor actually asks for, which is how an
	// agent checks that a descriptor says what it intended rather than merely parsing.
	Components []string `json:"components,omitempty"`
	Controls   []string `json:"controls,omitempty"`
}

ValidateOutput reports whether the descriptor is usable.

func ValidateSagaTool

ValidateSagaTool validates a descriptor from a path or inline content.

Jump to

Keyboard shortcuts

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