code-moniker 0.4.0

Standalone CLI / linter for the code-moniker symbol graph: file and directory probes, project-wide architecture rules.
Documentation
fragment = "cli-mcp"

[aliases]
mcp_root = """
  source ~ '**/module:mcp'
  OR source ~ '**/module:mcp/**'
  OR source ~ '**/lang:rs/module:/^(context|mod|server|tests)$/'
  OR source ~ '**/lang:rs/module:/^(context|mod|server|tests)$/**'
  OR source ~ '**/lang:rs/dir:tools/**'
"""
mcp_server = """
  source ~ '**/module:mcp/module:server'
  OR source ~ '**/module:mcp/module:server/**'
  OR source ~ '**/lang:rs/module:server'
  OR source ~ '**/lang:rs/module:server/**'
"""
mcp_context = """
  source ~ '**/module:mcp/module:context'
  OR source ~ '**/module:mcp/module:context/**'
  OR source ~ '**/lang:rs/module:context'
  OR source ~ '**/lang:rs/module:context/**'
"""
mcp_tools = """
  source ~ '**/module:mcp/module:tools'
  OR source ~ '**/module:mcp/module:tools/**'
  OR source ~ '**/lang:rs/dir:tools/**'
"""
mcp_tools_root_def = "uri ~ '**/module:mcp/module:tools'"
mcp_tests = """
  source ~ '**/module:mcp/module:tests'
  OR source ~ '**/module:mcp/module:tests/**'
  OR source ~ '**/lang:rs/module:tests'
  OR source ~ '**/lang:rs/module:tests/**'
"""
workspace_target = "target ~ '**/external_pkg:code_moniker_workspace/**'"
workspace_runtime_target = """
  target ~ '**/external_pkg:code_moniker_workspace/path:registry/path:LocalWorkspaceRegistry'
  OR target ~ '**/external_pkg:code_moniker_workspace/path:registry/path:LocalWorkspaceOptions'
  OR target ~ '**/external_pkg:code_moniker_workspace/path:snapshot/path:WorkspaceRequest'
  OR target ~ '**/external_pkg:code_moniker_workspace/path:snapshot/path:WorkspaceTransition'
  OR target ~ '**/external_pkg:code_moniker_workspace/path:source/path:LocalResourceCache'
"""
http_runtime_target = "target ~ '**/path:/^(TcpListener|JoinHandle)$/'"

[[rust.module.where]]
id       = "mcp-tools-avoid-duplicate-descendant-fn-names"
severity = "warn"
expr     = """
  $mcp_tools_root_def
  => count(
       pairs(descendants(fn)),
       a.name = b.name AND a.parent != b.parent
     ) = 0
"""
message  = "MCP tool modules contain duplicate descendant `fn` names across nested owners."
rationale = """
Sibling tool modules should not grow parallel private helpers with the same function names.
Duplicate names inside one owner are allowed for language patterns, but duplicates across owners
usually indicate a shared helper boundary or intentionally mirrored protocol shape that should
be documented.
"""

[[refs.where]]
id       = "mcp-server-owns-runtime-wiring"
severity = "error"
expr     = """
  $mcp_root
  AND (kind = 'imports_symbol' OR kind = 'imports_module' OR kind = 'uses_type')
  AND $http_runtime_target
  => $mcp_server OR $mcp_tests
"""
message  = "MCP socket/thread runtime wiring belongs to `cli::mcp::server`."
rationale = """
The CLI/TUI may opt in to MCP, but socket lifetime and stream framing are global runtime concerns.
Keeping those imports in server prevents individual tools from opening listeners or depending on
TCP details.
"""

[[refs.where]]
id       = "mcp-workspace-access-is-tool-owned"
severity = "error"
expr     = """
  $mcp_root
  AND NOT ($mcp_tools OR $mcp_tests)
  AND (kind = 'imports_symbol' OR kind = 'imports_module' OR kind = 'uses_type' OR kind = 'calls' OR kind = 'method_call')
  => NOT $workspace_target
"""
message  = "`cli::mcp` runtime modules must not access workspace internals; workspace facts come from the daemon contract."
rationale = """
MCP is a daemon client surface over code-moniker. Context and runtime may carry session
options and daemon handles, but they must not construct workspace resources or recreate
workspace read paths. Tool modules shape MCP presentation; workspace facts come from
`code-moniker-query` requests answered by the workspace daemon.
"""

[[refs.where]]
id       = "mcp-does-not-own-workspace-runtime"
severity = "error"
expr     = """
  $mcp_root
  AND NOT $mcp_tests
  AND (kind = 'imports_symbol' OR kind = 'imports_module' OR kind = 'uses_type' OR kind = 'calls' OR kind = 'method_call')
  => NOT $workspace_runtime_target
"""
message  = "MCP must consume the daemon contract; it must not construct or refresh workspace runtime resources."
rationale = """
The workspace daemon owns index lifecycle, refresh, linkage, notes, and rule execution for an
attached workspace. MCP tools may issue typed query/command requests; they must not construct
`LocalWorkspaceRegistry`, create source caches, or drive `WorkspaceRequest`, otherwise the
daemon contract becomes optional and feature parity cannot be enforced.
"""

[[refs.where]]
id       = "mcp-daemon-contract-is-context-only"
severity = "error"
expr     = """
  $mcp_root
  AND NOT $mcp_tests
  AND (kind = 'imports_symbol' OR kind = 'imports_module' OR kind = 'uses_type' OR kind = 'calls' OR kind = 'method_call')
  AND ($daemon_client_target OR $daemon_target)
  => $mcp_server OR $mcp_context
"""
message  = "The daemon handle is injected through MCP context; tools must use `context.query()`/`context.command()`."
rationale = """
The daemon contract is the boundary between MCP presentation and workspace runtime. The server
creates or receives the daemon client, context exposes typed query/command helpers, and tools
format daemon DTOs. If tools import daemon internals or clients directly, MCP grows local
facades and bypasses the single protocol every consumer must eventually share.
"""

[[views]]
id = "cli-mcp"
title = "CLI MCP agent surface"
scope = "."
intent = "Understand the MCP runtime and tool boundaries before changing the agent-facing protocol surface."
summary = """
The MCP module exposes code-moniker workspace facts to agents. The SDK-backed runtime and
workspace-backed tools are separate boundaries; new features should extend a concrete tool domain
instead of teaching the server layer about workspace resources.
"""

[[views.boundaries]]
id = "runtime"
owns = [
  "server lifetime",
  "listener startup",
  "SDK Streamable HTTP service ownership",
]
forbids = [
  "tool-specific workspace reads",
]
rationale = """
The runtime starts and owns the SDK service. It may carry context, but tool semantics must stay
behind the registry so stream transport stays replaceable.
"""
symbols = [
  "module:server/fn:router",
  "module:server/struct:CodeMonikerMcp",
]
rules = [
 "mcp-does-not-own-workspace-runtime",
 "mcp-server-owns-runtime-wiring",
 "mcp-shared-index-contract-is-context-only",
 "mcp-workspace-access-is-tool-owned",
]
forbid_rules = [
 "mcp-does-not-own-workspace-runtime",
 "mcp-workspace-access-is-tool-owned",
]

[[views.boundaries]]
id = "tools"
owns = [
  "tool registry",
  "workspace-backed LMNAV rendering",
  "paging and scope arguments",
]
rationale = """
Tools are the only MCP components allowed to decide which workspace resource they need.
Adding a feature usually means enriching an existing tool domain rather than adding runtime
knowledge to server or dispatch.
"""
symbols = [
  "module:tools/trait:McpTool",
  "module:tools/struct:ToolRegistry",
  "module:tools/module:read/struct:ReadTool",
  "module:tools/module:symbols/struct:SymbolsTool",
  "module:tools/module:rules/struct:RulesTool",
]
rules = [
  "mcp-does-not-own-workspace-runtime",
  "mcp-shared-index-contract-is-context-only",
  "mcp-workspace-access-is-tool-owned",
]

[[views.gotchas]]
id = "paging-preserves-scope"
rationale = """
Every paged LMNAV output must preserve its scope arguments in the next call. If a cursor drops
path, language, kind, name, profile, rule, or severity filters, an agent silently changes the
question while following pagination.
"""
symbols = [
  "module:tools/module:scope/struct:Paging",
  "module:tools/module:read/fn:append_read_next_call",
  "module:tools/module:symbols/fn:append_symbols_next_call",
  "module:tools/module:rules/fn:append_rules_next_call",
]
check = "cargo test -p code-moniker --features mcp mcp::tests --lib"

[[views.gotchas]]
id = "view-moniker-display-is-opt-in"
rationale = """
Views are a map for agents, not a dump of internal state. Evidence may expose monikers when
explicitly requested with moniker_format, but the default output should stay readable and
focused on the boundary.
"""
symbols = [
  "module:tools/module:read/struct:ReadRequest",
  "module:tools/module:read/fn:read_resource",
]