oct.mcp.validator module

Contents

oct.mcp.validator module#

Purpose#

Input Validator (Layer 2) for the oct-mcp six-layer pipeline. Defines one Pydantic model per MCP tool (9 models for the Phase 5A read-only tool manifest). All models enforce:

  • Unknown-field rejection (model_config = ConfigDict(extra="forbid"))

  • Integer field clamping via ge/le constraints

  • Enum allowlists (e.g. for profile)

  • Shell-metacharacter rejection for all string fields

  • Path containment enforcement for all paths fields

Responsibilities#

Diagnostics#

Domain: MCP Levels:

L2 — lifecycle: validation outcome L4 — deep trace: individual field values

Contracts#

  • This module does not import click or the MCP SDK.

  • All models are instantiated with model_validate(data, context=context) where context carries {"project_root": Path}.

  • validate_tool_args() raises ValueError for unknown tool names before Pydantic even runs.

  • Shell metacharacters are rejected by a @field_validator applied to every str field in every model via the _ShellSafe mixin.

class oct.mcp.validator.CleanArgs(*, path: str | None = None, confirm: bool = False, dry_run: bool = True, json_output: bool = True)[source]#

Bases: BaseModel

Arguments for the oct_clean tool (oct clean).

OI-502: json_output added for model uniformity.

confirm: bool#
dry_run: bool#
json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

path: str | None#
classmethod path_safe(v: str | None, info: ValidationInfo) str | None[source]#
class oct.mcp.validator.DepsArgs(*, json_output: bool = True)[source]#

Bases: BaseModel

Arguments for the oct_deps tool (oct deps).

OI-502: previously exposed a check boolean that the CLI does not accept. Removed to restore parity; callers that want pin-auditing should use oct deps --audit-pins directly (not yet modelled).

json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class oct.mcp.validator.DiagArgs(*, json_output: bool = True, subcommand: Literal['validate-config', 'list-domains'] = 'validate-config')[source]#

Bases: BaseModel

Arguments for the oct_diag tool (oct diag).

OI-502: the historical Literal included show-config and list-checks but the CLI exposes only validate-config, list-domains, and set-level. Narrowed to the two zero-argument subcommands; set-level takes positional arguments that are out of scope for this read-only tool.

json_output is retained for model uniformity but the executor does not emit --json because the diag subcommands don’t accept it (yet — see OI-534 for strict-mode output).

json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

subcommand: Literal['validate-config', 'list-domains']#
class oct.mcp.validator.DocsArgs(*, sphinx: bool = False, doxygen: bool = False, all_docs: bool = False, clean: bool = False, dry_run: bool = True, confirm: bool = False, json_output: bool = True)[source]#

Bases: BaseModel

Arguments for the oct_docs tool (oct docs).

OI-502: json_output added for model uniformity; the CLI does not accept --json so the executor silently omits it.

all_docs: bool#
clean: bool#
confirm: bool#
doxygen: bool#
dry_run: bool#
json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

sphinx: bool#
class oct.mcp.validator.ExportSourceArgs(*, confirm: bool = False, verbose: bool = False, single_dir: bool = False, warn_secrets: bool = True, block_secrets: bool = False, json_output: bool = True)[source]#

Bases: BaseModel

Arguments for the oct_export_source tool (oct export-source).

OI-502: json_output added for model uniformity.

block_secrets: bool#
confirm: bool#
json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

single_dir: bool#
verbose: bool#
warn_secrets: bool#
class oct.mcp.validator.FormatArgs(*, paths: list[str] = [], apply: bool = False, confirm: bool = False, changed: bool = False, verbose: bool = False, json_output: bool = True)[source]#

Bases: BaseModel

Arguments for the oct_format tool (oct format).

Double-guard: confirm=True AND apply=True are both required before changes are written. With apply=False the executor passes --dry-run; with apply=True it passes --fix.

OI-502: jobs removed (CLI argparser exposes no --jobs). json_output added so the executor can request --json output consistently with other read models.

apply: bool#
changed: bool#
confirm: bool#
json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

paths: list[str]#
classmethod paths_inside_project(v: list[str], info: ValidationInfo) list[str][source]#
verbose: bool#
class oct.mcp.validator.GitChangelogArgs(*, confirm: bool = False, dry_run: bool = True, since: str | None = None, version: str | None = None)[source]#

Bases: BaseModel

Arguments for the oct_git_changelog tool (oct git changelog).

confirm: bool#
dry_run: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

classmethod ref_shell_safe(v: str | None) str | None[source]#
since: str | None#
version: str | None#
class oct.mcp.validator.GitCheckArgs(*, json_output: bool = True, verbose: bool = False)[source]#

Bases: BaseModel

Arguments for the oct_git_check tool (oct git check).

json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

verbose: bool#
class oct.mcp.validator.GitCommitArgs(*, message: str, confirm: bool = False, force: bool = False, json_output: bool = True)[source]#

Bases: BaseModel

Arguments for the oct_git_commit tool (oct git commit).

Note: there is deliberately no no_verify field. Bypassing pre-commit hooks is never permitted via MCP (D-5B-6).

OI-502 / FS-502: message was previously run through check_shell_metacharacters(), which rejected Conventional Commits syntax such as scoped types (feat(core): add x) and breaking-change markers (feat!: drop y). Git receives the message as a single -m <message> argv element — never as a shell-interpreted string — so shell-metacharacter filtering was both over-broad and protection-for-nothing. Validation now delegates to oct.git.conventional.validate_commit_message(), which is also what oct git commit enforces at the CLI.

confirm: bool#
force: bool#
json_output: bool#
message: str#
classmethod message_conventional(v: str) str[source]#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class oct.mcp.validator.GitHooksInstallArgs(*, confirm: bool = False, force: bool = False, json_output: bool = True)[source]#

Bases: BaseModel

Arguments for the oct_git_hooks_install tool (oct git hooks install).

OI-502: json_output added for model uniformity.

confirm: bool#
force: bool#
json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class oct.mcp.validator.GitInitArgs(*, confirm: bool = False, dry_run: bool = True, github: bool = False, no_hooks: bool = False, json_output: bool = True)[source]#

Bases: BaseModel

Arguments for the oct_git_init tool (oct git init).

OI-502: json_output added for model uniformity.

confirm: bool#
dry_run: bool#
github: bool#
json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

no_hooks: bool#
class oct.mcp.validator.GitStatusArgs(*, json_output: bool = True, verbose: bool = False)[source]#

Bases: BaseModel

Arguments for the oct_git_status tool (oct git status).

json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

verbose: bool#
class oct.mcp.validator.HealthArgs(*, json_output: bool = True, verbose: bool = False)[source]#

Bases: BaseModel

Arguments for the oct_health tool (oct health).

json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

verbose: bool#
class oct.mcp.validator.InstallHooksArgs(*, confirm: bool = False, force: bool = False, json_output: bool = True)[source]#

Bases: BaseModel

Arguments for the oct_install_hooks tool (oct install-hooks).

OI-502: json_output added for model uniformity.

confirm: bool#
force: bool#
json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class oct.mcp.validator.LintArgs(*, paths: list[str] = [], json_output: bool = True, jobs: Annotated[int, Ge(ge=1), Le(le=8)] = 1, changed: bool = False, fix_headers: bool = False, profile: Literal['proto', 'compact', 'strict'] | None = None)[source]#

Bases: BaseModel

Arguments for the oct_lint tool (oct lint).

changed: bool#
fix_headers: bool#
jobs: int#
json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

paths: list[str]#
classmethod paths_inside_project(v: list[str], info: ValidationInfo) list[str][source]#
profile: _LINT_PROFILES | None#
class oct.mcp.validator.NewArgs(*, path: str, confirm: bool = False, force: bool = False, create_test: bool = False, dry_run: bool = True, verbose: bool = False, json_output: bool = True)[source]#

Bases: BaseModel

Arguments for the oct_new tool (oct new).

OI-502: json_output added for model uniformity.

confirm: bool#
create_test: bool#
dry_run: bool#
force: bool#
json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

path: str#
classmethod path_safe(v: str, info: ValidationInfo) str[source]#
verbose: bool#
class oct.mcp.validator.OctTestArgs(*, json_output: bool = True, paths: list[str] = [], changed: bool = False)[source]#

Bases: BaseModel

Arguments for the oct_test tool (oct test).

OI-502: previously exposed a jobs integer that the CLI does not accept. Removed to restore parity. Note that pytest-xdist is now declared in the oct[test] and oct[dev] extras, so parallel runs are available — invoke them via the existing pass-through args (e.g. oct test -- -n auto). A first-class jobs field can be re-added if/when MCP clients need to drive parallelism programmatically.

changed: bool#
json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

paths: list[str]#
classmethod paths_inside_project(v: list[str], info: ValidationInfo) list[str][source]#
class oct.mcp.validator.ScaffoldArgs(*, name: str, confirm: bool = False, dry_run: bool = True, json_output: bool = True)[source]#

Bases: BaseModel

Arguments for the oct_scaffold tool (oct scaffold).

OI-502: json_output added for model uniformity.

confirm: bool#
dry_run: bool#
json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

name: str#
classmethod name_shell_safe(v: str) str[source]#
class oct.mcp.validator.SkeletonArgs(*, json_output: bool = True)[source]#

Bases: BaseModel

Arguments for the oct_skeleton tool (oct export-skeleton).

OI-502: previously exposed an output_dir field that the CLI does not accept. Removed to restore model/CLI parity; the CLI always writes into _skeleton_code-* under the project root.

json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class oct.mcp.validator.TypecheckArgs(*, json_output: bool = True, paths: list[str] = [])[source]#

Bases: BaseModel

Arguments for the oct_typecheck tool (oct typecheck).

json_output: bool#
model_config = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

paths: list[str]#
classmethod paths_inside_project(v: list[str], info: ValidationInfo) list[str][source]#
oct.mcp.validator.check_shell_metacharacters(value: str) str[source]#

Raise ValueError if value contains any shell metacharacter.

Called by field validators in every model that handles free-form string input. Never called on boolean or integer fields.

oct.mcp.validator.validate_path_in_project(path_str: str, project_root: Path | None) str[source]#

Validate that path_str resolves inside project_root.

Relative paths are resolved against project_root (not the process cwd) so that "src" is interpreted as <project_root>/src. Absolute paths are resolved as-is.

Raises ValueError on path traversal attempts. When project_root is None, the check is skipped (permissive fallback for contexts where the root is not yet bound).

oct.mcp.validator.validate_tool_args(tool_name: str, raw_args: dict, project_root: Path | None = None) BaseModel[source]#

Validate raw_args against the model for tool_name.

Parameters:
  • tool_name – One of the 20 tool names in the manifest (e.g. "oct_lint").

  • raw_args – Unvalidated dict from the MCP client.

  • project_root – Absolute project root path, threaded through to path-containment validators via Pydantic’s model_validate context.

Return type:

A fully validated Pydantic model instance.

Raises:
  • ValueError – If tool_name is not in the tool manifest.

  • pydantic.ValidationError – If raw_args fails any field validator.