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/leconstraintsEnum allowlists (e.g. for
profile)Shell-metacharacter rejection for all string fields
Path containment enforcement for all
pathsfields
Responsibilities#
Provide
validate_tool_args()as the single entry point: takes a tool name and a rawdictand returns a validatedBaseModelinstance, or raisespydantic.ValidationErroron failure.Define
check_shell_metacharacters()for reuse by tests.Define
validate_path_in_project()for reuse by tests.
Diagnostics#
Domain: MCP Levels:
L2 — lifecycle: validation outcome L4 — deep trace: individual field values
Contracts#
This module does not import
clickor the MCP SDK.All models are instantiated with
model_validate(data, context=context)wherecontextcarries{"project_root": Path}.validate_tool_args()raisesValueErrorfor unknown tool names before Pydantic even runs.Shell metacharacters are rejected by a @field_validator applied to every
strfield in every model via the_ShellSafemixin.
- class oct.mcp.validator.CleanArgs(*, path: str | None = None, confirm: bool = False, dry_run: bool = True, json_output: bool = True)[source]#
Bases:
BaseModelArguments for the
oct_cleantool (oct clean).OI-502:
json_outputadded 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#
- class oct.mcp.validator.DepsArgs(*, json_output: bool = True)[source]#
Bases:
BaseModelArguments for the
oct_depstool (oct deps).OI-502: previously exposed a
checkboolean that the CLI does not accept. Removed to restore parity; callers that want pin-auditing should useoct deps --audit-pinsdirectly (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:
BaseModelArguments for the
oct_diagtool (oct diag).OI-502: the historical
Literalincludedshow-configandlist-checksbut the CLI exposes onlyvalidate-config,list-domains, andset-level. Narrowed to the two zero-argument subcommands;set-leveltakes positional arguments that are out of scope for this read-only tool.json_outputis retained for model uniformity but the executor does not emit--jsonbecause 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:
BaseModelArguments for the
oct_docstool (oct docs).OI-502:
json_outputadded for model uniformity; the CLI does not accept--jsonso 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:
BaseModelArguments for the
oct_export_sourcetool (oct export-source).OI-502:
json_outputadded 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:
BaseModelArguments for the
oct_formattool (oct format).Double-guard:
confirm=TrueANDapply=Trueare both required before changes are written. Withapply=Falsethe executor passes--dry-run; withapply=Trueit passes--fix.OI-502:
jobsremoved (CLI argparser exposes no--jobs).json_outputadded so the executor can request--jsonoutput 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]#
- verbose: bool#
- class oct.mcp.validator.GitChangelogArgs(*, confirm: bool = False, dry_run: bool = True, since: str | None = None, version: str | None = None)[source]#
Bases:
BaseModelArguments for the
oct_git_changelogtool (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].
- since: str | None#
- version: str | None#
- class oct.mcp.validator.GitCheckArgs(*, json_output: bool = True, verbose: bool = False)[source]#
Bases:
BaseModelArguments for the
oct_git_checktool (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:
BaseModelArguments for the
oct_git_committool (oct git commit).Note: there is deliberately no
no_verifyfield. Bypassing pre-commit hooks is never permitted via MCP (D-5B-6).OI-502 / FS-502:
messagewas previously run throughcheck_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 tooct.git.conventional.validate_commit_message(), which is also whatoct git commitenforces at the CLI.- confirm: bool#
- force: bool#
- json_output: bool#
- message: str#
- 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:
BaseModelArguments for the
oct_git_hooks_installtool (oct git hooks install).OI-502:
json_outputadded 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:
BaseModelArguments for the
oct_git_inittool (oct git init).OI-502:
json_outputadded 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:
BaseModelArguments for the
oct_git_statustool (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:
BaseModelArguments for the
oct_healthtool (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:
BaseModelArguments for the
oct_install_hookstool (oct install-hooks).OI-502:
json_outputadded 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:
BaseModelArguments for the
oct_linttool (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]#
- 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:
BaseModelArguments for the
oct_newtool (oct new).OI-502:
json_outputadded 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#
- verbose: bool#
- class oct.mcp.validator.OctTestArgs(*, json_output: bool = True, paths: list[str] = [], changed: bool = False)[source]#
Bases:
BaseModelArguments for the
oct_testtool (oct test).OI-502: previously exposed a
jobsinteger that the CLI does not accept. Removed to restore parity. Note thatpytest-xdistis now declared in theoct[test]andoct[dev]extras, so parallel runs are available — invoke them via the existing pass-throughargs(e.g.oct test -- -n auto). A first-classjobsfield 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]#
- class oct.mcp.validator.ScaffoldArgs(*, name: str, confirm: bool = False, dry_run: bool = True, json_output: bool = True)[source]#
Bases:
BaseModelArguments for the
oct_scaffoldtool (oct scaffold).OI-502:
json_outputadded 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#
- class oct.mcp.validator.SkeletonArgs(*, json_output: bool = True)[source]#
Bases:
BaseModelArguments for the
oct_skeletontool (oct export-skeleton).OI-502: previously exposed an
output_dirfield 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:
BaseModelArguments for the
oct_typechecktool (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]#
- oct.mcp.validator.check_shell_metacharacters(value: str) str[source]#
Raise
ValueErrorif 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
ValueErroron path traversal attempts. Whenproject_rootisNone, 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_validatecontext.
- 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.