# === Signet MCP Management Server (src_mcp_server.rs) v1 ===
#  Dependencies: rmcp::model, rmcp, rmcp::service, rmcp::transport, serde_json, std::borrow, std::path, std::sync, std::fs, std::time, crate::policy, crate::vault, regex, serde_yaml
# MCP (Model Context Protocol) server for conversational policy management through Claude. Provides tools for managing policy rules, credentials, spending limits, preflights, and vault operations. Handles policy CRUD, validation, testing, signing, and enforcement configuration.

# Module invariants:
#   - Policy path is always {signet_dir}/policy.yaml
#   - Locked rules cannot be removed, edited, or reordered
#   - Unlocked rules cannot be placed before locked rules in reorder
#   - GATE action requires gate.requires_prior to be non-empty
#   - GATE gate.within must be 1-500
#   - ENSURE action requires valid ensure.check name
#   - ENSURE ensure.timeout must be 1-30
#   - Soft constraints can only have DENY or ASK actions
#   - Soft constraints require non-empty alternative (plan B)
#   - Preflight lockout must be 5-480 minutes
#   - Session reset only allowed via CLI to prevent spending limit bypass
#   - Credentials masked in response (show last 4 chars only)
#   - Policy auto-signed after all mutation operations
#   - All file operations silently ignore errors in save_policy and auto_sign_policy
#   - Default PolicyConfig: version=1, rules=[], default_action=Allow

class SignetMcpServer:
    """MCP server implementation for Signet policy management. Zero-sized struct implementing ServerHandler trait."""
    pass

class PolicyConfig:
    """Policy configuration structure containing version, rules, and default action. Default implementation defined in this module."""
    version: i32                             # required, Policy schema version
    rules: Vec<PolicyRule>                   # required, Ordered list of policy rules
    default_action: Decision                 # required, Default decision when no rules match

class Tool:
    """MCP tool definition with name, description, and JSON schema."""
    name: Cow<'static, str>                  # required
    description: Option<Cow<'static, str>>   # required
    input_schema: Arc<serde_json::Map<String, Value>> # required

class ServerInfo:
    """MCP server metadata including instructions and capabilities."""
    pass

class ListToolsResult:
    """Response for list_tools MCP request."""
    tools: Vec<Tool>                         # required
    next_cursor: Option<String>              # required
    meta: Option<serde_json::Value>          # required

class CallToolResult:
    """Response for call_tool MCP request."""
    pass

class Content:
    """MCP content item (text or other types)."""
    pass

def policy_path() -> PathBuf:
    """
    Returns the filesystem path to the policy YAML file in the signet directory.

    Postconditions:
      - Returns path to policy.yaml in signet directory

    Side effects: reads_file
    Idempotent: no
    """
    ...

def load_policy_raw() -> PolicyConfig:
    """
    Loads the policy configuration from the YAML file. Returns default PolicyConfig on file read or parse errors.

    Postconditions:
      - Returns valid PolicyConfig (default if file missing or invalid)

    Errors:
      - file_read_error (Silent - returns PolicyConfig::default()): Policy file cannot be read
      - yaml_parse_error (Silent - returns PolicyConfig::default()): YAML parsing fails

    Side effects: reads_file
    Idempotent: no
    """
    ...

def save_policy(
    config: &PolicyConfig,
) -> ():
    """
    Serializes and saves the policy configuration to the YAML file. Creates parent directories if needed. Silently ignores errors.

    Postconditions:
      - Policy written to disk if serialization and write succeed

    Errors:
      - serialization_error (Silent - no write occurs): serde_yaml::to_string fails
      - write_error (Silent - errors ignored): std::fs::write fails

    Side effects: writes_file
    Idempotent: no
    """
    ...

def auto_sign_policy() -> ():
    """
    Automatically signs the policy file with HMAC if vault is available. Silently skips if vault is unavailable or signing fails.

    Postconditions:
      - Policy signed if vault available and signing succeeds

    Errors:
      - vault_unavailable (Silent - no signing occurs): vault::try_load_vault() returns None
      - signing_error (Silent - error ignored): vault::sign_policy fails

    Side effects: writes_file, mutates_state
    Idempotent: no
    """
    ...

def make_tool(
    name: &'static str,
    desc: &'static str,
    schema: serde_json::Value,
) -> Tool:
    """
    Constructs an MCP Tool object with name, description, and JSON schema for tool inputs.

    Postconditions:
      - Returns populated Tool instance

    Side effects: none
    Idempotent: no
    """
    ...

def get_info(
    self: &SignetMcpServer,
) -> ServerInfo:
    """
    Returns MCP server metadata: instructions and capabilities. Part of ServerHandler trait implementation.

    Postconditions:
      - Returns ServerInfo with instructions and tool capabilities enabled

    Side effects: none
    Idempotent: no
    """
    ...

async def list_tools(
    self: &SignetMcpServer,
    params: Option<PaginatedRequestParams>,
    ctx: RequestContext<RoleServer>,
) -> Result<ListToolsResult, McpError>:
    """
    Returns the list of all available MCP tools for policy management. Async handler for MCP list_tools request.

    Postconditions:
      - Returns list of 23 tool definitions

    Side effects: none
    Idempotent: no
    """
    ...

async def call_tool(
    self: &SignetMcpServer,
    request: CallToolRequestParams,
    ctx: RequestContext<RoleServer>,
) -> Result<CallToolResult, McpError>:
    """
    Dispatches MCP tool calls to appropriate handlers based on tool name. Async handler for MCP call_tool request.

    Postconditions:
      - Returns CallToolResult with text content

    Side effects: mutates_state
    Idempotent: no
    """
    ...

def handle_list_rules() -> String:
    """
    Lists all policy rules with their properties: action, reason, conditions, gate/ensure config, lock status.

    Postconditions:
      - Returns formatted string listing all rules or default action message

    Side effects: reads_file
    Idempotent: no
    """
    ...

def handle_add_rule(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Adds a new policy rule. Validates action type, gate/ensure config, and rule uniqueness. Auto-signs policy after success.

    Preconditions:
      - Rule name must not already exist
      - Action must be ALLOW/DENY/ASK/GATE/ENSURE
      - GATE requires gate.requires_prior
      - ENSURE requires valid ensure.check

    Postconditions:
      - Rule added to policy
      - Policy saved to disk
      - Policy auto-signed

    Errors:
      - invalid_action (String error message): Action is not ALLOW, DENY, ASK, GATE, or ENSURE
      - duplicate_rule (String error message): Rule with same name already exists
      - missing_gate_config (String error message): GATE action without gate config or empty requires_prior
      - invalid_gate_within (String error message): gate.within not in range 1-500
      - missing_ensure_config (String error message): ENSURE action without ensure config
      - invalid_ensure_check (String error message): ensure.check validation fails
      - invalid_ensure_timeout (String error message): ensure.timeout not in range 1-30

    Side effects: reads_file, writes_file
    Idempotent: no
    """
    ...

def handle_remove_rule(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Removes a policy rule by name. Prevents removal of locked rules. Auto-signs policy after success.

    Preconditions:
      - Rule must exist
      - Rule must not be locked

    Postconditions:
      - Rule removed from policy if found and not locked
      - Policy saved and auto-signed

    Errors:
      - locked_rule (String error message): Rule is locked (self-protection)
      - rule_not_found (String error message): Rule with specified name does not exist

    Side effects: reads_file, writes_file
    Idempotent: no
    """
    ...

def handle_set_limit(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Creates or updates a spending limit rule for a category. Replaces existing limit rule with same category/amount.

    Postconditions:
      - Spending limit rule created/updated
      - Policy saved and auto-signed

    Side effects: reads_file, writes_file
    Idempotent: no
    """
    ...

def handle_status() -> String:
    """
    Shows vault status, policy rule count, credential count, session spending, and recent actions.

    Postconditions:
      - Returns formatted status string

    Side effects: reads_file
    Idempotent: no
    """
    ...

def handle_recent_actions(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Returns recent action log entries from vault with limit parameter.

    Preconditions:
      - Vault must be set up and unlocked

    Postconditions:
      - Returns formatted action log or error message

    Errors:
      - vault_unavailable (String error message): Vault not set up or locked

    Side effects: mutates_state
    Idempotent: no
    """
    ...

def handle_store_credential(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Stores a Tier 3 credential in the vault with compartment encryption.

    Preconditions:
      - Vault must be set up and unlocked

    Postconditions:
      - Credential stored in vault as Tier 3

    Errors:
      - vault_unavailable (String error message): Vault not set up or locked

    Side effects: mutates_state
    Idempotent: no
    """
    ...

def handle_list_credentials() -> String:
    """
    Lists credential names and tiers from vault (does not reveal values).

    Preconditions:
      - Vault must be set up and unlocked

    Postconditions:
      - Returns formatted list of credential names and tiers

    Errors:
      - vault_unavailable (String error message): Vault not set up or locked

    Side effects: mutates_state
    Idempotent: no
    """
    ...

def handle_delete_credential(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Deletes a credential from the vault by name.

    Preconditions:
      - Vault must be set up and unlocked

    Postconditions:
      - Credential deleted if found

    Errors:
      - vault_unavailable (String error message): Vault not set up or locked
      - credential_not_found (String message (not error)): Credential with specified name does not exist

    Side effects: mutates_state
    Idempotent: no
    """
    ...

def handle_validate() -> String:
    """
    Validates the policy file for errors: bad regex patterns, unknown functions, malformed conditions.

    Postconditions:
      - Returns validation report with errors or success message

    Errors:
      - policy_load_error (String error message): Policy file cannot be loaded

    Side effects: reads_file
    Idempotent: no
    """
    ...

def handle_test(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Dry-run test of a tool call against the policy without executing it. Returns decision, matched rule, reason, and eval time.

    Postconditions:
      - Returns evaluation result with decision, matched rule, reason, and timing

    Side effects: reads_file, mutates_state
    Idempotent: no
    """
    ...

def handle_reorder_rule(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Moves a rule to a new position in the policy. Prevents locked rules from being moved and prevents unlocked rules from being placed before locked rules.

    Preconditions:
      - Position must be >= 1
      - Rule must exist
      - Rule must not be locked

    Postconditions:
      - Rule moved to specified position (or first safe position)
      - Policy saved and auto-signed

    Errors:
      - invalid_position (String error message): Position is 0
      - rule_not_found (String error message): Rule with specified name does not exist
      - locked_rule (String error message): Rule is locked

    Side effects: reads_file, writes_file
    Idempotent: no
    """
    ...

def handle_edit_rule(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Edits properties of an existing rule. Prevents editing locked rules. Validates new action, gate, and ensure configs.

    Preconditions:
      - Rule must exist
      - Rule must not be locked

    Postconditions:
      - Rule properties updated
      - Policy saved and auto-signed

    Errors:
      - rule_not_found (String error message): Rule with specified name does not exist
      - locked_rule (String error message): Rule is locked
      - invalid_action (String error message): Action is not ALLOW, DENY, ASK, GATE, or ENSURE
      - invalid_gate_config (String error message): gate.requires_prior is empty
      - invalid_ensure_check (String error message): ensure.check validation fails

    Side effects: reads_file, writes_file
    Idempotent: no
    """
    ...

def handle_sign_policy() -> String:
    """
    Signs the policy file with HMAC for tamper detection using vault session key.

    Preconditions:
      - Vault must be set up and unlocked

    Postconditions:
      - Policy HMAC written to disk

    Errors:
      - vault_unavailable (String error message): Vault not set up or locked
      - signing_error (String error message): vault::sign_policy fails

    Side effects: writes_file
    Idempotent: no
    """
    ...

def handle_reset_session() -> String:
    """
    Rejects session reset requests from MCP to prevent spending limit bypass. Returns explanation message.

    Postconditions:
      - Returns rejection message

    Side effects: none
    Idempotent: no
    """
    ...

def handle_use_credential(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Requests a credential through policy-gated capability system. Enforces domain, purpose, amount, and one-time constraints. Returns masked credential value.

    Preconditions:
      - Vault must be set up and unlocked

    Postconditions:
      - Credential released if policy allows
      - Usage logged to vault

    Errors:
      - vault_unavailable (String error message): Vault not set up or locked
      - credential_denied (String error message): vault.request_capability() returns Err

    Side effects: mutates_state
    Idempotent: no
    """
    ...

def handle_condition_help() -> String:
    """
    Returns comprehensive documentation of all available condition functions with examples.

    Postconditions:
      - Returns formatted help text

    Side effects: none
    Idempotent: no
    """
    ...

def parse_soft_constraints(
    args: &serde_json::Map<String, Value>,
) -> Result<Vec<vault::SoftConstraint>, String>:
    """
    Parses and validates soft constraint array from preflight submission arguments.

    Preconditions:
      - constraints array must exist
      - Each constraint must have name, tool_pattern, conditions, action, reason, alternative

    Postconditions:
      - Returns validated SoftConstraint vector on success

    Errors:
      - missing_constraints (Err(String)): constraints key missing or not array
      - invalid_constraint_object (Err(String)): Constraint is not a JSON object
      - empty_name (Err(String)): Constraint name is empty
      - invalid_action (Err(String)): Action is not DENY or ASK
      - empty_alternative (Err(String)): Alternative (plan B) is empty
      - invalid_regex (Err(String)): tool_pattern is not valid regex

    Side effects: none
    Idempotent: no
    """
    ...

def handle_preflight_submit(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Files a preflight: declares task intent, risks, and self-imposed constraints before starting work. Constraints are HMAC-signed and locked for specified duration.

    Preconditions:
      - Vault must be set up and unlocked
      - No active locked preflight
      - Lockout must be 5-480 minutes
      - Max 20 constraints (implied by parse validation)

    Postconditions:
      - Preflight stored in vault with HMAC signature
      - Constraints active for lockout duration

    Errors:
      - empty_task (String error message): Task description is empty
      - invalid_lockout_min (String error message): Lockout < 5 minutes
      - invalid_lockout_max (String error message): Lockout > 480 minutes
      - constraint_validation_error (String error message): parse_soft_constraints returns Err
      - vault_unavailable (String error message): Vault not set up or locked
      - active_locked_preflight (String error message): Another preflight is locked and active
      - store_error (String error message): vault.store_preflight() returns Err

    Side effects: mutates_state
    Idempotent: no
    """
    ...

def handle_preflight_active() -> String:
    """
    Shows the active preflight: task, risks, constraints, violation count, lockout status.

    Preconditions:
      - Vault must be set up and unlocked

    Postconditions:
      - Returns formatted preflight details or 'no active' message

    Errors:
      - vault_unavailable (String error message): Vault not set up or locked

    Side effects: mutates_state
    Idempotent: no
    """
    ...

def handle_preflight_history(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Shows past preflights with their status, violations, and escalation state.

    Preconditions:
      - Vault must be set up and unlocked

    Postconditions:
      - Returns formatted preflight history or 'no history' message

    Errors:
      - vault_unavailable (String error message): Vault not set up or locked

    Side effects: mutates_state
    Idempotent: no
    """
    ...

def handle_preflight_violations(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Shows constraint violations for the active or a specified preflight.

    Preconditions:
      - Vault must be set up and unlocked

    Postconditions:
      - Returns formatted violation list or 'no violations' message

    Errors:
      - vault_unavailable (String error message): Vault not set up or locked
      - no_active_no_id (String error message): No preflight_id specified and no active preflight

    Side effects: mutates_state
    Idempotent: no
    """
    ...

def handle_preflight_test(
    args: &serde_json::Map<String, Value>,
) -> String:
    """
    Dry-run validation of preflight constraints without submitting. Tests constraint structure and logic.

    Postconditions:
      - Returns validation result

    Errors:
      - validation_error (String error message): parse_soft_constraints returns Err

    Side effects: none
    Idempotent: no
    """
    ...

async def run_server() -> Result<(), Box<dyn std::error::Error>>:
    """
    Runs the MCP server on stdio transport. Async main entry point.

    Postconditions:
      - Server running and serving requests on stdio

    Errors:
      - server_error (Box<dyn std::error::Error>): rmcp::serve_server or service.waiting() fails

    Side effects: network_call
    Idempotent: no
    """
    ...

# ── REQUIRED EXPORTS ──────────────────────────────────
# Your implementation module MUST export ALL of these names
# with EXACTLY these spellings. Tests import them by name.
# __all__ = ['SignetMcpServer', 'PolicyConfig', 'Tool', 'ServerInfo', 'ListToolsResult', 'CallToolResult', 'Content', 'policy_path', 'load_policy_raw', 'Silent - returns PolicyConfig::default()', 'save_policy', 'Silent - no write occurs', 'Silent - errors ignored', 'auto_sign_policy', 'Silent - no signing occurs', 'Silent - error ignored', 'make_tool', 'get_info', 'list_tools', 'call_tool', 'handle_list_rules', 'handle_add_rule', 'String error message', 'handle_remove_rule', 'handle_set_limit', 'handle_status', 'handle_recent_actions', 'handle_store_credential', 'handle_list_credentials', 'handle_delete_credential', 'String message (not error)', 'handle_validate', 'handle_test', 'handle_reorder_rule', 'handle_edit_rule', 'handle_sign_policy', 'handle_reset_session', 'handle_use_credential', 'handle_condition_help', 'parse_soft_constraints', 'Err(String)', 'handle_preflight_submit', 'handle_preflight_active', 'handle_preflight_history', 'handle_preflight_violations', 'handle_preflight_test', 'run_server', 'Box<dyn std::error::Error>']
