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).
- Workflow: a directory under
src/mcp/tools/<workflow>/containing anindex.tswith workflow metadata and tool modules. - Tool: a
PluginMetaexported from a workflow module withname,schema, andhandler. - 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).
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.tsexports (excludingworkflowitself). - A single tool name can appear in multiple workflows (re-exports). This matters for workflow management and hiding.
At MCP server startup:
-
Runtime config is loaded (
bootstrapRuntime→initConfigStore) from:- config file (project config),
- env (
XCODEBUILDMCP_*), - explicit overrides.
-
Enabled workflows are taken from config (
enabledWorkflows). -
registerWorkflows(enabledWorkflows)runs, which callsapplyWorkflowSelection(...)(src/utils/tool-registry.ts). -
Workflow selection uses
selectWorkflowsForMcp(...)(src/visibility/exposure.ts) which:- includes auto-include workflows whose predicates pass (e.g.,
session-managementwith no predicates is always included), - includes explicitly requested workflows from config,
- defaults to
defaultEnabled: trueworkflows (e.g.,simulator) whenenabledWorkflowsis empty, - filters all selected workflows by availability + predicates.
- includes auto-include workflows whose predicates pass (e.g.,
-
For each selected workflow, each tool is considered for registration, then filtered by
shouldExposeTool(workflowName, toolName)(src/utils/tool-registry.ts). -
If visible, the tool is registered via
server.registerTool(...).
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.
The CLI has two related but distinct concepts:
- Command registration /
--helptree (yargs commands) - Tool listing (
xcodebuildmcp tools) which is driven by a manifest
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).
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.
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>(seesrc/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).
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_statusxcode_tools_bridge_syncxcode_tools_bridge_disconnect
These tools are gated behind debug: true / XCODEBUILDMCP_DEBUG=true (src/utils/tool-visibility.ts).
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”).
- Workflow selection decides which workflows are in play.
- Visibility filtering can hide individual tools even within enabled workflows.
- Dynamic tools may appear even if there are no static tools in a workflow (e.g. proxied
xcode_tools_*).