# Tool Discovery & Visibility Logic (MCP server + CLI)
This document describes how XcodeBuildMCP discovers workflows/tools and decides which ones are visible in:
- the **MCP server** (`xcodebuildmcp mcp`), and
- the **CLI** (`node build/cli.js` / `xcodebuildmcp ...`).
It also documents the current and intended **visibility filtering** behavior (post workflow-selection filtering).
## Terminology
- **Workflow**: a directory under `src/mcp/tools/<workflow>/` containing an `index.ts` with workflow metadata and tool modules.
- **Tool**: a `PluginMeta` exported from a workflow module with `name`, `schema`, and `handler`.
- **Workflow selection**: picking which workflows are active (coarse-grained inclusion).
- **Visibility filtering**: hiding specific tools even if their workflow is enabled (fine-grained exclusion).
- **Dynamic tools**: tools registered at runtime that do not come from static workflows (e.g. proxied Xcode Tools).
## Where workflows/tools come from (source of truth)
Workflows are discovered via generated loaders in `src/core/generated-plugins.ts` (the `WORKFLOW_LOADERS` map). At runtime, `loadWorkflowGroups()` imports each workflow module via these loaders and collects tools from it (`src/core/plugin-registry.ts`).
Key properties of this design:
- Workflows are “discoverable” by enumerating `Object.keys(WORKFLOW_LOADERS)`.
- Tools within a workflow are whatever `index.ts` exports (excluding `workflow` itself).
- A single tool name can appear in multiple workflows (re-exports). This matters for workflow management and hiding.
## MCP server: registration pipeline
At MCP server startup:
1) Runtime config is loaded (`bootstrapRuntime` → `initConfigStore`) from:
- config file (project config),
- env (`XCODEBUILDMCP_*`),
- explicit overrides.
2) Enabled workflows are taken from config (`enabledWorkflows`).
3) `registerWorkflows(enabledWorkflows)` runs, which calls `applyWorkflowSelection(...)` (`src/utils/tool-registry.ts`).
4) Workflow selection uses `selectWorkflowsForMcp(...)` (`src/visibility/exposure.ts`) which:
- includes auto-include workflows whose predicates pass (e.g., `session-management` with no predicates is always included),
- includes explicitly requested workflows from config,
- defaults to `defaultEnabled: true` workflows (e.g., `simulator`) when `enabledWorkflows` is empty,
- filters all selected workflows by availability + predicates.
5) For each selected workflow, each tool is considered for registration, then filtered by `shouldExposeTool(workflowName, toolName)` (`src/utils/tool-registry.ts`).
6) If visible, the tool is registered via `server.registerTool(...)`.
### Runtime workflow management (MCP)
The `manage-workflows` tool updates the enabled workflow list at runtime and re-applies workflow selection (`src/mcp/tools/workflow-discovery/manage_workflows.ts` → `applyWorkflowSelection(...)`).
Important nuance:
- Because tools can be re-exported across workflows, disabling one workflow may not remove a tool if another enabled workflow still provides that same tool name.
- Visibility filtering operates on the tool name (and workflow name) at registration time; it is layered after workflow selection.
## CLI: discovery and help output
The CLI has two related but distinct concepts:
1) **Command registration / `--help` tree** (yargs commands)
2) **Tool listing** (`xcodebuildmcp tools`) which is driven by a manifest
### CLI command registration (yargs)
CLI mode builds a tool catalog using `buildCliToolCatalog()` which enables **all workflows** returned by `listWorkflowDirectoryNames()` except `session-management` and `workflow-discovery` (`src/cli/cli-tool-catalog.ts`).
The catalog is built via `buildToolCatalog(...)` (`src/runtime/tool-catalog.ts`), which:
- loads workflow groups via `loadWorkflowGroups()`,
- resolves selected workflows (as above),
- applies `shouldExposeTool(...)` as a per-tool visibility filter,
- generates CLI names and disambiguates collisions.
Yargs then registers workflow command groups and per-tool subcommands from the catalog (`src/cli/register-tool-commands.ts`).
Additionally, the CLI registers workflow command groups from workflow metadata even if there are currently zero visible tools in that workflow (so the workflow still appears in `--help` output).
### CLI `tools` command (manifest-driven)
`xcodebuildmcp tools` reads `build/tools-manifest.json` (`src/cli/commands/tools.ts`) which is generated by `npm run generate:tools-manifest` (invoked by `npm run build:tsup`).
Key implications:
- The manifest is a **static analysis** snapshot (canonical source for docs/CLI listing).
- Dynamic runtime tools (see below) are **not** represented in the manifest.
## Dynamic tools: Xcode Tools via `xcrun mcpbridge`
When the `xcode-ide` workflow is enabled, XcodeBuildMCP can proxy Xcode’s IDE tool service through `xcrun mcpbridge` and register proxied tools at runtime.
Properties:
- Proxied tools are registered dynamically and prefixed as `xcode_tools_<RemoteToolName>` (see `src/integrations/xcode-tools-bridge/registry.ts`).
- Proxied tools are not part of any static workflow and are not present in `build/tools-manifest.json`.
- Proxied tool registration can change over time if Xcode’s tool catalog changes (the bridge supports `tools/listChanged`).
## Visibility filtering (post-selection hiding)
### Current behavior (implemented)
`shouldExposeTool(...)` is the single, shared visibility gate for:
- MCP server tool registration (`src/utils/tool-registry.ts`)
- CLI catalog building (`src/runtime/tool-catalog.ts`)
Currently it is used to hide Xcode IDE bridge **debug** tools unless debugging is enabled:
- `xcode_tools_bridge_status`
- `xcode_tools_bridge_sync`
- `xcode_tools_bridge_disconnect`
These tools are gated behind `debug: true` / `XCODEBUILDMCP_DEBUG=true` (`src/utils/tool-visibility.ts`).
### Intended behavior (documented policy)
We also want a broader “Xcode agent mode” visibility filter where specific XcodeBuildMCP tools are hidden when:
- `runningUnderXcode === true`, AND
- Xcode Tools MCP is enabled/available
This is documented in `docs/dev/XCODE_IDE_TOOL_CONFLICTS.md`.
Design principle:
- Workflow selection stays the **primary** “what areas are enabled” control.
- Visibility filtering is a **secondary** control to reduce confusion/duplication and align behavior with the environment (e.g. “inside Xcode, builds/tests should run inside Xcode”).
## Layering summary (what wins)
1) **Workflow selection** decides which workflows are in play.
2) **Visibility filtering** can hide individual tools even within enabled workflows.
3) **Dynamic tools** may appear even if there are no static tools in a workflow (e.g. proxied `xcode_tools_*`).