mcp

package
v0.2.8 Latest Latest
Warning

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

Go to latest
Published: Aug 10, 2026 License: Apache-2.0 Imports: 33 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.

Status: BUILT. NewServer registers the tool set and Serve runs it over the stdio transport; every tool is a thin adapter onto a core handler. The HTTP/SSE transport is the one part still unbuilt — `mcp serve --port` returns an explicit error rather than falling back silently.

Index

Constants

View Source
const (
	ScopeModeFull    = "full"
	ScopeModePartial = "partial"
)

The two scan modes. A response ALWAYS carries one of them: the field's presence is not conditional, so a consumer never has to infer the mode from an absence.

View Source
const ResponseBudgetBytes = 40_000

ResponseBudgetBytes is the byte budget codefit-scan-all declares for its own serialized response.

What is MEASURED (roadmap P0-4): a bisection run against a real MCP client (Claude Code, 2026-08-09), driving the v0.2.6 binary over stdio with controlled-size responses cut from trimmed copies of a real 317-file project. The real ceiling was bracketed, not pinpointed:

64 097 bytes  ACCEPTED   <- largest observed acceptance
74 195 bytes  REJECTED   "exceeds maximum allowed tokens"

40 000 was CHOSEN inside that bracket, not measured directly: 62% of the largest observed acceptance (64 097), leaving room for roughly a 60% increase in token density before approaching the rejected end (74 195), and it matches an earlier, independent data point — a 40 282-byte response that is known to have arrived (2026-08-04, before this bisection existed).

The assumption this number rests on, stated plainly: the client's limit is in TOKENS; this budget counts BYTES. The bytes-per-token ratio is content-dependent (identifiers, hex digests and deep paths run denser than prose), so the margin above is NOT fixed — a byte count under budget can still cross a token ceiling the same client would reject. And the measurement is of ONE client, ONE date, ONE content shape: other MCP clients (Cursor, VSCode, OpenCode) have their own limits, unmeasured here.

Measured consequence of moving this number down (2026-08-09, same real project, fresh baseline): payload 39 962 bytes, 19 of 174 endpoints withheld (5 actionable, 14 frontier_pending) — at the old 60 000 this same project fit entirely with 0 withheld. Real mid-sized projects now see a non-zero withheld count where they previously did not; each bucket's `count` stays the complete number and codefit-scan-endpoint still fetches full detail on request (ADR 0054), but this is a user-visible behaviour change, not a free tightening.

See ADR 0062 for the full record, including what this number does NOT fix: a byte budget cannot guarantee a token limit. The structural answer — a hard cap on entries per bucket, so response size stops being a function of project size — is roadmap P0-4's declared follow-up, not this change.

The budget is DECLARED in the response (see BudgetBlock) and enforced by withholding the lowest-ranked endpoints, never by truncating the payload: a clipped response that reads like a complete one is the one outcome forbidden (ADR 0054, same principle as ADR 0048).

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.)

func SupportedLanguageNames added in v0.2.8

func SupportedLanguageNames() []string

SupportedLanguageNames returns the canonical, deduplicated, sorted set of language names codefit-scan-all can resolve a security provider for — DERIVED from what languageProviders actually constructs (each entry's Language()), never a hand-written literal. This is the single source the nothing-measurable error message reads (D4/D5): a language and its aliases (ts/tsx) collapse to one canonical name here.

Types

type ActionableSection added in v0.2.7

type ActionableSection struct {
	Count    int    `json:"count"`
	Withheld int    `json:"withheld"`
	Note     string `json:"note,omitempty"`
	// Endpoints is the rendered prefix of the ranked list: hardest gap kind first
	// (affirmed → access → exposure → efficiency), then by certain-concern count.
	Endpoints []report.ActionableEndpoint `json:"endpoints,omitempty"`
}

ActionableSection declares the endpoints codefit resolved locally AND found a gap in — the ones the agent acts on. They are NAMED with what it takes to rank them (counts, categories, gap kinds, best certainty, whether an affirmation is present) and their deterministic concerns in full; the surface question and signals text is one codefit-scan-endpoint call away.

Count is the COMPLETE number codefit classified as actionable and never the number rendered — Withheld accounts for the difference. Reading Count off len(Endpoints) is exactly the bug this section's shape exists to make impossible.

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 BudgetBlock added in v0.2.7

type BudgetBlock struct {
	Bytes    int    `json:"bytes"`
	Withheld int    `json:"withheld"`
	Ordering string `json:"ordering"`
	Note     string `json:"note"`
}

BudgetBlock is scan-all's account of its own size. codefit's primary tool has to RETURN: a response an MCP client refuses is worth less than a smaller one that arrives. So the response declares the budget it is written to, and when the endpoint list does not fit it says exactly how many endpoints it is not showing and on what ordering it kept the ones it shows.

Withheld=0 still carries a Note, on purpose. "No mention of truncation" and "nothing was truncated" must not be the same bytes on the wire: that ambiguity is how a clipped response comes to read like a complete one (ADR 0048).

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"`
	Withheld  int                       `json:"withheld"`
	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"`
	Withheld  int                            `json:"withheld"`
	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"`
	// ChangedFiles narrows the audit to these project-relative paths (layer 0 of
	// the filtering pyramid). codefit does not ask git which files changed — it
	// has no power over the user's git, and the calling agent already knows what
	// it touched. Absent or empty means a FULL audit, never "audit nothing".
	//
	// A narrowed run declares itself in the response's scope block, cannot mark a
	// baseline item in an unopened file as gone, and leaves the DB dimension NOT
	// MEASURED unless a configured schema path is in scope.
	ChangedFiles []string `json:"changed_files,omitempty"`
}

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"`
	// Scope declares how much of the project this response describes: mode full or
	// partial, how many auditable files were in scope, and which requested paths
	// the audit never reached. It is ALWAYS present, so a consumer never infers
	// the mode from an absence and never reads a partial `blocked: false` as the
	// wider claim it is not.
	Scope ScopeBlock `json:"scope"`
	// 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"`
	// Budget declares the response's own byte budget and whether anything was
	// withheld to meet it. ALWAYS present, including when nothing was withheld: an
	// agent must be able to tell a complete list from a cut one by reading the
	// response, never by guessing (ADR 0054).
	Budget          BudgetBlock       `json:"budget"`
	Actionable      ActionableSection `json:"actionable"`
	ResolvedClean   ResolvedClean     `json:"resolved_clean"`
	FrontierPending FrontierPending   `json:"frontier_pending"`
	// Security reports whether the security dimension ran (D3b). It is ALWAYS
	// present — deliberately NOT `omitempty`, unlike DB: the db dimension may
	// legitimately not apply to a project (no database configured), but security
	// applies to every project, so an ABSENT section could only mean an older
	// codefit build. Measured=false with a Note is the honest "no provider for
	// this language" state, mirroring DBSection's own Measured/Note shape.
	Security SecuritySection `json:"security"`
	// 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"`
	// ChangedFiles narrows the audit to these project-relative paths (layer 0 of
	// the filtering pyramid). codefit does not ask git which files changed — it
	// has no power over the user's git, and the calling agent already knows what
	// it touched. Absent or empty means a FULL audit, never "audit nothing", and
	// a narrowed run declares itself in the response's scope block.
	ChangedFiles []string `json:"changed_files,omitempty"`
}

ScanRequest is the input to codefit-scan-security: a project root, a language, and optionally the files to narrow the audit to.

type ScanResponse

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

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). Scope declares how much of the project this result describes. It is ALWAYS present, so `blocked: false` is never read as a wider claim than it is.

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 ScopeBlock added in v0.2.7

type ScopeBlock struct {
	Mode           string   `json:"mode"`
	Requested      int      `json:"requested"`
	Audited        int      `json:"audited"`
	AuditableTotal int      `json:"auditable_total"`
	Unmatched      []string `json:"unmatched,omitempty"`
	Note           string   `json:"note"`
}

ScopeBlock is a scan's account of HOW MUCH of the project it looked at. It exists because a partial audit that is indistinguishable from a full one is a lying auditor: the whole point of narrowing is that the narrowing stays visible.

  • Requested is how many distinct files the caller asked for (canonical, so a path spelled twice counts once); 0 under a full scan.
  • Audited is how many of them the pass actually examined, and AuditableTotal how many it COULD have — the denominator is the whole project, never the scope, or "3 of 412" collapses into a self-flattering "3 of 3".
  • Unmatched are the requested paths the audit never reached: deleted, wrong extension, outside the project, inside a skipped directory. Without it an agent that passes three wrong paths gets "0 findings" and reads it as clean. Requested always equals Audited + len(Unmatched).
  • Note is MANDATORY when partial and FORBIDDEN when full (see Validate).

func (ScopeBlock) Validate added in v0.2.7

func (s ScopeBlock) Validate() error

Validate enforces the honesty invariant in BOTH directions: a partial scan must declare itself in prose, and a full scan must not carry a caveat it has no basis for. Handlers call it before returning, so a violation is a loud error rather than an unlabelled partial result an agent would read as a full one — the failure this whole block exists to prevent.

type SecuritySection added in v0.2.8

type SecuritySection struct {
	Measured bool   `json:"measured"`
	Note     string `json:"note,omitempty"`
}

SecuritySection reports whether the security dimension ran this pass (D3b). Measured=false is SOFT, exactly like DBSection: it never fails scan-all, it is reported. Note is empty when Measured is true (nothing to caveat) and non-empty otherwise, naming why (no provider resolved for the language) and what that means for the rest of the response (the schema may still have been audited; the code was not).

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