mcp

package
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package mcp implements the MCP server mode: a thin, stateless adapter that exposes the same core sensors as MCP tools (PRD section 10). It reimplements no audit logic — every tool call translates to the same engine the CLI uses — and relies on prompt caching to neutralize the stateless overhead.

Skeleton: this declares the [Server] contract. No transport (stdio/HTTP) or tool dispatch is implemented yet.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewServer

func NewServer() *mcpsdk.Server

NewServer builds the codefit MCP server with its tools registered. Each tool is a THIN adapter: it hands the SDK's typed request to the core handler that already exists and is tested, and returns the core's result as structured output. No audit logic lives here — the MCP layer only connects the protocol to the engine (PRD §15). The server is stateless: every tool call is independent and carries everything it needs.

func Serve

func Serve(ctx context.Context) error

Serve runs the codefit MCP server over the stdio transport until ctx is cancelled. (HTTP/SSE is deferred; the SDK abstracts the transport, so it is added later without a refactor.)

Types

type BaselineAcceptRequest

type BaselineAcceptRequest struct {
	Root         string   `json:"root"`
	Fingerprints []string `json:"fingerprints"`
	Reason       string   `json:"reason"`
}

BaselineAcceptRequest marks baseline items as acknowledged by a human (a false positive or accepted debt). reason is mandatory. SAFETY: the agent must call this ONLY when the human decided so — codefit records by:"human" but cannot verify it; the skill enforces the discipline.

type BaselineAcceptResponse

type BaselineAcceptResponse struct {
	Accepted []string `json:"accepted"`
	Note     string   `json:"note"`
}

BaselineAcceptResponse reports which fingerprints were acknowledged.

func HandleBaselineAccept

func HandleBaselineAccept(req BaselineAcceptRequest) (BaselineAcceptResponse, error)

HandleBaselineAccept records a human's decision to accept items. It only touches the baseline file.

type BaselineDelta

type BaselineDelta struct {
	New               int        `json:"new"`
	Changed           int        `json:"changed"`
	Known             int        `json:"known"`
	Acknowledged      int        `json:"acknowledged"`
	Gone              int        `json:"gone"`
	AffirmationsShown int        `json:"affirmations_shown"`
	GoneCandidates    []GoneItem `json:"gone_candidates,omitempty"`
	Note              string     `json:"note"`
}

BaselineDelta is scan-all's account of how the current scan compares to the committed baseline. The agent acts on new + changed (and unaccepted affirmations); known surface is silenced but counted.

type BaselineListRequest

type BaselineListRequest struct {
	Root     string `json:"root"`
	Language string `json:"language,omitempty"`
	Filter   string `json:"filter,omitempty"`
}

BaselineListRequest reads the current baseline so the agent can reference items in accept/prune WITHOUT reading the raw .codefit-baseline file. Filter is "" (all), "known" (not yet accepted), or "acknowledged". Language is accepted for tool uniformity but unused — listing reads the file, it does not scan code.

type BaselineListResponse

type BaselineListResponse struct {
	Items        []baseline.Entry       `json:"items"`
	Count        int                    `json:"count"`
	AuthzHelpers []baseline.AuthzHelper `json:"authz_helpers,omitempty"`
	Note         string                 `json:"note"`
}

BaselineListResponse is the projected baseline: per item just fp+file+category+ state (+reason/date if acknowledged), small enough not to truncate. AuthzHelpers lists the project's registered custom authz helpers (read-only visibility) so the agent can see — and propose unregistering — them without reading the file.

func HandleBaselineList

func HandleBaselineList(req BaselineListRequest) (BaselineListResponse, error)

HandleBaselineList returns the baseline entries. A missing baseline is NOT an error: it returns an empty list with a note pointing to scan-all. Read-only.

type BaselinePruneRequest

type BaselinePruneRequest struct {
	Root         string   `json:"root"`
	Language     string   `json:"language"`
	Fingerprints []string `json:"fingerprints,omitempty"`
}

BaselinePruneRequest removes baseline items that no longer exist in the code (gone). With Fingerprints set it prunes only those (if confirmed gone); empty prunes all confirmed-gone items.

type BaselinePruneResponse

type BaselinePruneResponse struct {
	Pruned []string `json:"pruned"`
	Note   string   `json:"note"`
}

BaselinePruneResponse reports which fingerprints were pruned.

func HandleBaselinePrune

func HandleBaselinePrune(req BaselinePruneRequest) (BaselinePruneResponse, error)

HandleBaselinePrune re-scans to confirm which baseline items are gone, then removes them. Stateless: it recomputes the current surface; it never edits code.

type BaselineRegisterAuthzHelperRequest added in v0.1.2

type BaselineRegisterAuthzHelperRequest struct {
	Root       string `json:"root"`
	Language   string `json:"language"`
	HelperName string `json:"helper_name"`
	Reason     string `json:"reason"`
}

BaselineRegisterAuthzHelperRequest registers a project-specific authz helper so codefit recognizes it on later scans (known_authz_detected reflects it). reason is mandatory. SAFETY: registering silences the AUTHZ gap on EVERY item that calls the helper — far more reach than accepting one item. The agent must call this ONLY when the human approved it; codefit records by:"human" but cannot verify it (the skill enforces the discipline). It does NOT clear the IDOR/ownership gap (ADR 0013, ADR 0006 amended).

type BaselineRegisterAuthzHelperResponse added in v0.1.2

type BaselineRegisterAuthzHelperResponse struct {
	Registered bool   `json:"registered"`
	Note       string `json:"note"`
}

BaselineRegisterAuthzHelperResponse reports the outcome.

func HandleBaselineRegisterAuthzHelper added in v0.1.2

func HandleBaselineRegisterAuthzHelper(req BaselineRegisterAuthzHelperRequest) (BaselineRegisterAuthzHelperResponse, error)

HandleBaselineRegisterAuthzHelper records a human-approved custom authz helper in the baseline. It only touches the baseline file.

type BaselineUnregisterAuthzHelperRequest added in v0.1.2

type BaselineUnregisterAuthzHelperRequest struct {
	Root       string `json:"root"`
	Language   string `json:"language"`
	HelperName string `json:"helper_name"`
}

BaselineUnregisterAuthzHelperRequest reverses a registration (the developer's decision is always reversible). The next scan stops recognizing the helper.

type BaselineUnregisterAuthzHelperResponse added in v0.1.2

type BaselineUnregisterAuthzHelperResponse struct {
	Unregistered bool   `json:"unregistered"`
	Note         string `json:"note"`
}

BaselineUnregisterAuthzHelperResponse reports the outcome.

func HandleBaselineUnregisterAuthzHelper added in v0.1.2

func HandleBaselineUnregisterAuthzHelper(req BaselineUnregisterAuthzHelperRequest) (BaselineUnregisterAuthzHelperResponse, error)

HandleBaselineUnregisterAuthzHelper removes a registered helper. It only touches the baseline file.

type CheckCVEsRequest added in v0.1.4

type CheckCVEsRequest struct {
	Root string `json:"root"`
}

CheckCVEsRequest is the input to codefit-check-cves: a project root. The dependency manifests are auto-detected under it.

type CheckCVEsResponse added in v0.1.4

type CheckCVEsResponse struct {
	Vulnerable          []VulnerableDependency `json:"vulnerable"`
	DependenciesScanned int                    `json:"dependencies_scanned"`
	Notes               []string               `json:"notes"`
}

CheckCVEsResponse reports the vulnerable dependencies, how many were scanned, and any honest notes (a manifest present without its lockfile, or none found).

func HandleCheckCVEs added in v0.1.4

func HandleCheckCVEs(req CheckCVEsRequest) (CheckCVEsResponse, error)

HandleCheckCVEs reads the project's dependency manifests (exact versions from lockfiles / go.mod only), queries OSV.dev for known vulnerabilities, and returns the vulnerable dependencies. A thin adapter: it parses manifests, queries the client, and reshapes the result — no detection logic here. When no version resolves (no lockfile), it returns the honest note WITHOUT querying OSV.

type ConfirmSurfaceRequest

type ConfirmSurfaceRequest struct {
	Confirmations []surface.Confirmation `json:"confirmations"`
}

ConfirmSurfaceRequest carries the agent's verdicts back to codefit.

type ConfirmSurfaceResponse

type ConfirmSurfaceResponse struct {
	Findings  []findings.Finding     `json:"findings"`
	Dismissed []surface.Confirmation `json:"dismissed"`
	Uncertain []surface.Confirmation `json:"uncertain"`
	Invalid   []surface.Confirmation `json:"invalid"`
}

ConfirmSurfaceResponse is the integration result: probabilistic findings plus the traceability buckets. Stateless — built purely from the verdicts sent.

func HandleConfirmSurface

func HandleConfirmSurface(req ConfirmSurfaceRequest) ConfirmSurfaceResponse

HandleConfirmSurface integrates the agent's verdicts: codefit-confirm-surface. It recomputes each surface id to validate the verdict before integrating, and keeps no session.

type CoverageRequest

type CoverageRequest struct {
	Language string `json:"language"`
}

CoverageRequest is the input to codefit-coverage.

type CoverageResponse

type CoverageResponse struct {
	Manifest coverage.Manifest `json:"manifest"`
}

CoverageResponse carries the coverage manifest: what is audited deterministically vs reasoned over surface vs not covered.

func HandleCoverage

func HandleCoverage(req CoverageRequest) (CoverageResponse, error)

HandleCoverage returns the coverage manifest for the language. Thin adapter: the manifest is the provider's single source of truth.

type DBSection added in v0.2.0

type DBSection struct {
	Measured bool                   `json:"measured"`
	Note     string                 `json:"note,omitempty"`
	Findings []findings.Finding     `json:"findings,omitempty"`
	Surface  []findings.SurfaceItem `json:"surface,omitempty"`
	Score    int                    `json:"score"`
}

DBSection is the database dimension's result inside scan-all. Measured=false with a Note is the honest "not audited" state (disabled / no parser / schema read or parse failure) — a db failure is SOFT here, reported but never fatal to the security result (ADR 0020). Findings are affirmations (e.g. DB-050); Surface are questions; both are already filtered by the baseline.

type FileInput

type FileInput struct {
	Path    string `json:"path"`
	Content string `json:"content"`
}

FileInput is one source file a surface tool reasons over (path + content), so the tool stays stateless — the caller passes the bytes, codefit reads nothing.

type FrontierPending

type FrontierPending struct {
	Count     int                       `json:"count"`
	Note      string                    `json:"note,omitempty"`
	Endpoints []report.FrontierEndpoint `json:"endpoints,omitempty"`
}

FrontierPending declares the endpoints codefit did NOT resolve locally: the data left the handler body, so codefit concluded nothing and the agent must follow it in the code. They are named (not detailed) with a Note explaining why they are not detailed and how to fetch any of them. This is not hiding — it is prioritising while declaring the rest.

type GoneItem

type GoneItem struct {
	Fingerprint string `json:"fingerprint"`
	Category    string `json:"category"`
	File        string `json:"file"`
	Snippet     string `json:"snippet,omitempty"`
}

GoneItem names a baseline item no longer present in the code — a prune candidate.

type ResolvedClean

type ResolvedClean struct {
	Count     int                            `json:"count"`
	Note      string                         `json:"note,omitempty"`
	Endpoints []report.ResolvedCleanEndpoint `json:"endpoints,omitempty"`
}

ResolvedClean declares the endpoints codefit resolved locally and found clean (controls present, no gap). They are NAMED with a verification fact, not detailed. This is an affirmation — codefit looked and it is clean — not a generic "not detailed" bucket; that is why it is separate from FrontierPending.

type ScanAllRequest

type ScanAllRequest struct {
	Root     string `json:"root"`
	Language string `json:"language"`
}

ScanAllRequest is the input to codefit-scan-all: a project root and language. codefit walks the project, runs the deterministic sensor and the surface queries, and returns the complete per-endpoint picture.

type ScanAllResponse

type ScanAllResponse struct {
	Summary ScanAllSummary `json:"summary"`
	// Score is the per-dimension breakdown plus the weighted global (ADR 0021). It
	// is ALWAYS present: by_dimension carries every weighted dimension, with the
	// unaudited ones (review/complexity/tests, and db when it did not run) as null —
	// an honest statement that the dimension exists but was not measured. The score
	// reflects deterministic AFFIRMATIONS only, not mapped surface.
	Score           scoring.ScoreSummary    `json:"score"`
	Baseline        BaselineDelta           `json:"baseline"`
	Actionable      []report.EndpointReport `json:"actionable"`
	ResolvedClean   ResolvedClean           `json:"resolved_clean"`
	FrontierPending FrontierPending         `json:"frontier_pending"`
	// DB is the parallel database-structure section — the db dimension's findings
	// and surface, baseline-filtered. It is NON-endpoint (a table has no route), so
	// it is its own section, not one of the three endpoint buckets. Nil when the
	// project has no database.schema_paths configured, so a project without a
	// database yields a response byte-identical to before db was wired (ADR 0020).
	DB *DBSection `json:"db,omitempty"`
}

ScanAllResponse is the agent-first synthesis as an ACTIONABLE summary, not the raw item dump. It has three buckets, one per resolution level, all decided by facts codefit already computes (ADR 0008):

  • Actionable — resolved locally AND has a gap: full detail, the agent acts.
  • ResolvedClean — resolved locally, NO gap: named + a verification fact; codefit checked and the controls are present.
  • FrontierPending — not resolved locally (the data left the handler body): named; the agent follows it in the code.

ResolvedClean and FrontierPending are kept DISTINCT on purpose: one affirms codefit verified the controls, the other states codefit could not conclude — epistemological opposites the agent must distinguish (flattening them would be the old frontier-wording error). Full detail of any named endpoint is always one codefit-scan-endpoint call away. Note on the baseline layer: when a baseline exists, the three buckets are FILTERED to what is not yet tracked (new/changed surface, and unaccepted deterministic affirmations). So on an unchanged re-scan all three buckets can be empty even though Summary.CertainConcerns (computed before filtering) is > 0 — the difference is exactly the "known" surface the baseline is silencing.

func HandleScanAll

func HandleScanAll(req ScanAllRequest) (ScanAllResponse, error)

HandleScanAll runs the full audit over the project and returns the actionable summary plus the named frontier-pending list. It reuses the real security sensor (the deterministic rules plus the three surface queries already run together there), groups the result by endpoint, and partitions by the local-resolution fact — it adds no detection, only the aggregation and the split.

type ScanAllSummary

type ScanAllSummary struct {
	Endpoints             int `json:"endpoints"`
	DeterministicFindings int `json:"deterministic_findings"`
	SurfaceItems          int `json:"surface_items"`
	CertainConcerns       int `json:"certain_concerns"`
}

ScanAllSummary is the at-a-glance count, not a judgment.

type ScanDBRequest added in v0.2.0

type ScanDBRequest struct {
	Root     string `json:"root"`
	Language string `json:"language"`
}

ScanDBRequest is the input to codefit-scan-db: a project root and language.

type ScanDBResponse added in v0.2.0

type ScanDBResponse struct {
	Measured bool                   `json:"measured"`
	Note     string                 `json:"note,omitempty"`
	Findings []findings.Finding     `json:"findings"`
	Surface  []findings.SurfaceItem `json:"surface"`
	Score    int                    `json:"score"`
}

ScanDBResponse is the standalone DB-structure result. Measured distinguishes "audited" from "not audited": when false, Note says why (no schema_paths, no schema parser, or the sensor is disabled) and Findings/Surface are empty — it is NOT a "clean" result. Score is a plain dimension score (0-100), like codefit-scan-security; this tool does not compute by_dimension.

func HandleScanDB added in v0.2.0

func HandleScanDB(req ScanDBRequest) (ScanDBResponse, error)

HandleScanDB runs the DB sensor over the project and returns its structural findings + surface. A thin adapter: it resolves the provider, loads the config, and delegates to the DB sensor (which reads the schema, runs the core rules, and stamps identity). Standalone — it touches nothing in scan-all.

type ScanEndpointRequest

type ScanEndpointRequest struct {
	Root     string `json:"root"`
	Language string `json:"language"`
	File     string `json:"file"`
}

ScanEndpointRequest is the input to codefit-scan-endpoint: a project root, language, and the file (relative to root, as it appears in scan-all's File fields) to re-analyse on demand.

type ScanEndpointResponse

type ScanEndpointResponse struct {
	File      string                  `json:"file"`
	Found     bool                    `json:"found"`
	Endpoints []report.EndpointReport `json:"endpoints,omitempty"`
	Note      string                  `json:"note,omitempty"`
}

ScanEndpointResponse carries the full per-endpoint detail for one file: every handler in it with all its concerns (signals, reason_to_review, certainty, fact fields) — the same concern contract as scan-all. Found is false when the file has no auditable concerns (not a route handler, or nothing to enumerate).

func HandleScanEndpoint

func HandleScanEndpoint(req ScanEndpointRequest) (ScanEndpointResponse, error)

HandleScanEndpoint re-analyses a single file on demand and returns its endpoints with full detail. STATELESS: it re-runs the static analysis over the project and filters to the requested file — it retrieves nothing stored. codefit does not keep the surface items waiting to be asked for; it recomputes the request. Static analysis is cheap, and re-running the same pipeline guarantees the detail here is identical to what scan-all would have shown for that endpoint (ADR 0008).

type ScanRequest

type ScanRequest struct {
	Root     string `json:"root"`
	Language string `json:"language"`
}

ScanRequest is the input to codefit-scan-security: a project root and language.

type ScanResponse

type ScanResponse struct {
	Findings []findings.Finding     `json:"findings"`
	Surface  []findings.SurfaceItem `json:"surface"`
	Score    int                    `json:"score"`
	Blocked  bool                   `json:"blocked"`
}

ScanResponse is the deterministic + surface result over the project (the §11 contract): flat findings and surface, the dimension score, and whether the project is blocked (an unconsented critical security finding).

func HandleScanSecurity

func HandleScanSecurity(req ScanRequest) (ScanResponse, error)

HandleScanSecurity runs the security sensor over the project and returns the flat findings + surface. A thin adapter: it resolves the provider, runs the sensor (the deterministic rules + the surface queries, already wired there), and returns its result — no detection logic lives here.

type SurfaceIDORRequest

type SurfaceIDORRequest struct {
	Files []FileInput `json:"files"`
}

SurfaceIDORRequest is the input to codefit-surface-idor.

type SurfaceResponse

type SurfaceResponse struct {
	Surface []findings.SurfaceItem `json:"surface"`
}

SurfaceResponse is the §11 surface contract: the items the agent must reason.

func HandleSurfaceAuthz

func HandleSurfaceAuthz(req SurfaceIDORRequest) (SurfaceResponse, error)

HandleSurfaceAuthz enumerates the broken-authorization surface across the given files. It orders the items so the actionable ones — where no known authz helper was detected — come FIRST, the rest after (where a check exists, the agent verifies sufficiency). It does NOT reduce the list: the complete enumeration is preserved, only reordered, so findings surface instead of being buried in volume. Ordering by a fact is not a severity judgment — codefit does not say "no check is worse", it just surfaces the fact for the agent.

func HandleSurfaceIDOR

func HandleSurfaceIDOR(req SurfaceIDORRequest) (SurfaceResponse, error)

HandleSurfaceIDOR enumerates the IDOR surface across the given files and returns it in the canonical JSON contract. Files that are not handled by a provider (or carry no IDOR surface) contribute nothing.

func HandleSurfaceNPlus1 added in v0.2.2

func HandleSurfaceNPlus1(req SurfaceIDORRequest) (SurfaceResponse, error)

HandleSurfaceNPlus1 enumerates the N+1 (DB-201) surface and orders by STRUCTURAL CERTAINTY, reusing the queryable facts — it does NOT filter (ADR 0005): a loop over three literal elements is enumerated exactly like a loop over an unbounded query result.

1st  local + sequential await — the worst, most certain shape
2nd  local + Promise.all-wrapped (concurrent) — still N queries
3rd  frontier (a service/repository call — codefit cannot see the query;
     kept last, honest, never dropped)

func HandleSurfaceOverfetch

func HandleSurfaceOverfetch(req SurfaceIDORRequest) (SurfaceResponse, error)

HandleSurfaceOverfetch enumerates the over-fetching surface and orders by STRUCTURAL CERTAINTY, reusing the queryable facts — it does NOT filter:

1st  local find, no select   (structurally confirmed over-fetch — actionable)
2nd  local find, with select (limited)
3rd  frontier (service — codefit cannot see the find; kept last, honest)

The complete enumeration is preserved; only the order changes, lowering the uncertain (frontier) without dropping them — the unchecked-first principle on the local/frontier axis (ADR 0005). Ordering by a fact is not severity: codefit ranks by what it can assert with most certainty, the agent judges.

type Tool

type Tool string

Tool is the name of an MCP tool codefit exposes. All tools use the codefit- prefix; the codefit-surface-* family enumerates surface for the agent to reason about rather than detecting (PRD section 11).

const (
	ToolScanSecurity                  Tool = "codefit-scan-security"
	ToolScanDB                        Tool = "codefit-scan-db"
	ToolCheckCVEs                     Tool = "codefit-check-cves"
	ToolCheckPractices                Tool = "codefit-check-practices"
	ToolScanTests                     Tool = "codefit-scan-tests"
	ToolSurfaceIDOR                   Tool = "codefit-surface-idor"
	ToolSurfaceAuthz                  Tool = "codefit-surface-authz"
	ToolSurfaceOverfetch              Tool = "codefit-surface-overfetch"
	ToolSurfaceNPlus1                 Tool = "codefit-surface-nplus1"
	ToolConfirmSurface                Tool = "codefit-confirm-surface"
	ToolReviewCode                    Tool = "codefit-review-code"
	ToolScanAll                       Tool = "codefit-scan-all"
	ToolScanEndpoint                  Tool = "codefit-scan-endpoint"
	ToolBaselineList                  Tool = "codefit-baseline-list"
	ToolBaselineAccept                Tool = "codefit-baseline-accept"
	ToolBaselinePrune                 Tool = "codefit-baseline-prune"
	ToolBaselineRegisterAuthzHelper   Tool = "codefit-baseline-register-authz-helper"
	ToolBaselineUnregisterAuthzHelper Tool = "codefit-baseline-unregister-authz-helper"
	ToolCoverage                      Tool = "codefit-coverage"
)

type VulnerableDependency added in v0.1.4

type VulnerableDependency struct {
	Name            string              `json:"name"`
	Version         string              `json:"version"`
	Ecosystem       string              `json:"ecosystem"`
	Vulnerabilities []cve.Vulnerability `json:"vulnerabilities"`
}

VulnerableDependency is one dependency that OSV.dev reports as vulnerable, with every vulnerability affecting its exact installed version.

Jump to

Keyboard shortcuts

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