Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,19 @@ When configuring a client manually, ensure the command includes the `mcp` subcom
- Xcode 16.x or later
- Node.js 18.x or later

## Skill
## Skills

XcodeBuildMCP now includes an optional agent skill. Some clients (e.g., Cursor, Claude Code) hide MCP tool schemas behind search/progressive disclosure, which can reduce tool discovery and usage. The skill provides a concise overview of available tools to counter that. If your client already exposes tools up front, you likely don’t need it; only use it if your agent isn’t reaching for XcodeBuildMCP tools.
XcodeBuildMCP now includes two optional agent skills:

- **MCP Skill**: Primes the agent with instructions on how to use the MCP server's tools (optional when using the MCP server).

- **CLI Skill**: Primes the agent with instructions on how to navigate the CLI (recommended when using the CLI).


To install, copy and paste the command below into a terminal and follow the on-screen instructions.

To install, download and run the installer in a terminal, then choose your client when prompted:
```bash
curl -fsSL https://raw.githubusercontent.com/cameroncooke/XcodeBuildMCP/main/scripts/install-skill.sh -o install-skill.sh
bash install-skill.sh
curl -fsSL https://raw.githubusercontent.com/cameroncooke/XcodeBuildMCP/main/scripts/install-skill.sh -o install-skill.sh && bash install-skill.sh
```

For further information on how to install the skill, see: [docs/SKILLS.md](docs/SKILLS.md)
Expand Down
44 changes: 34 additions & 10 deletions docs/SKILLS.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,54 @@
# XcodeBuildMCP Skill

This repository bundles a minimal skill that summarizes XcodeBuildMCP workflows and tools to help steer clients to use MCP tools, this is espeically important for clients that progressively load or hide MCP tools behind search interfaces (i.e. Cursor, Claude Code).
XcodeBuildMCP now includes two optional agent skills:

## Install (Claude Code)
- **MCP Skill**: Primes the agent with instructions on how to use the MCP server's tools (optional when using the MCP server).

- **CLI Skill**: Primes the agent with instructions on how to navigate the CLI (recommended when using the CLI).

## Easiest way to install

Install via the interactive installer and follow the on-screen instructions.

```bash
curl -fsSL https://raw.githubusercontent.com/cameroncooke/XcodeBuildMCP/main/scripts/install-skill.sh -o install-skill.sh && bash install-skill.sh
```

## Automated installation

Useful for CI/CD pipelines or for agentic installation. `--skill` should be set to either `mcp` or `cli` to install the appropriate skill.

### Install (Claude Code)

```bash
curl -fsSL https://raw.githubusercontent.com/cameroncooke/XcodeBuildMCP/main/scripts/install-skill.sh | bash -s -- --claude
curl -fsSL https://raw.githubusercontent.com/cameroncooke/XcodeBuildMCP/main/scripts/install-skill.sh -o install-skill.sh && bash install-skill.sh --claude --remove-conflict --skill <mcp|cli>
```

## Install (Cursor)
### Install (Cursor)

```bash
curl -fsSL https://raw.githubusercontent.com/cameroncooke/XcodeBuildMCP/main/scripts/install-skill.sh | bash -s -- --cursor
curl -fsSL https://raw.githubusercontent.com/cameroncooke/XcodeBuildMCP/main/scripts/install-skill.sh -o install-skill.sh && bash install-skill.sh --cursor --remove-conflict --skill <mcp|cli>
```

## Install (Codex CLI)
### Install (Codex CLI)

```bash
curl -fsSL https://raw.githubusercontent.com/cameroncooke/XcodeBuildMCP/main/scripts/install-skill.sh -o install-skill.sh && bash install-skill.sh --codex --remove-conflict --skill <mcp|cli>
```

### Install (Other Clients)

For other clients if you know the path to the skills directory you can pass the `--dest` flag.

```bash
curl -fsSL https://raw.githubusercontent.com/cameroncooke/XcodeBuildMCP/main/scripts/install-skill.sh | bash -s -- --codex
curl -fsSL https://raw.githubusercontent.com/cameroncooke/XcodeBuildMCP/main/scripts/install-skill.sh -o install-skill.sh && bash install-skill.sh --dest /path/to/skills --remove-conflict --skill <mcp|cli>
```

## Install (Other Clients)
## Unsupporting Clients

Some MCP clients do not yet support skills. Use the skill content as a concise, static instruction block:
Some MCP clients that do not yet support skills. Use the skill content as a concise, static instruction prompt:

1. Open `skills/xcodebuildmcp/SKILL.md`.
1. Open `skills/xcodebuildmcp[-cli]/SKILL.md`.
2. Copy the body (everything below the YAML frontmatter).
3. Paste it into the client’s global or project-level instructions/rules area.

Expand Down
153 changes: 127 additions & 26 deletions scripts/install-skill.sh
Original file line number Diff line number Diff line change
@@ -1,16 +1,78 @@
#!/usr/bin/env bash
set -euo pipefail

# Colors and formatting
if [[ -t 1 ]] && [[ "${TERM:-}" != "dumb" ]]; then
BOLD='\033[1m'
DIM='\033[2m'
RESET='\033[0m'
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
else
BOLD=''
DIM=''
RESET=''
RED=''
GREEN=''
YELLOW=''
BLUE=''
CYAN=''
fi

# Symbols
CHECK="${GREEN}✓${RESET}"
CROSS="${RED}✗${RESET}"
ARROW="${CYAN}→${RESET}"
WARN="${YELLOW}!${RESET}"

print_header() {
printf "\n"
printf "${BOLD}${BLUE}╭──────────────────────────────────╮${RESET}\n"
printf "${BOLD}${BLUE}│${RESET} ${BOLD}XcodeBuildMCP Skill Installer${RESET} ${BOLD}${BLUE}│${RESET}\n"
printf "${BOLD}${BLUE}╰──────────────────────────────────╯${RESET}\n"
}

print_success() {
printf " ${CHECK} ${GREEN}%s${RESET}\n" "$1"
}

print_error() {
printf " ${CROSS} ${RED}%s${RESET}\n" "$1" >&2
}

print_warning() {
printf " ${WARN} ${YELLOW}%s${RESET}\n" "$1"
}

print_info() {
printf " ${ARROW} %s\n" "$1"
}

print_step() {
printf "\n${BOLD}%s${RESET}\n" "$1"
}

usage() {
cat <<'EOF'
Usage: install-skill.sh --codex|--claude|--cursor|--dest <path> [--skill <mcp|cli>] [--ref <git-ref>] [--remove-conflict]
cat <<EOF
${BOLD}Usage:${RESET} install-skill.sh --codex|--claude|--cursor|--dest <path> [options]

Installs (or replaces) the XcodeBuildMCP skill. If --skill is omitted, the
installer will ask which skill to install.
If the script is run from a local checkout, it installs the local skill file.
Otherwise it downloads the skill from the provided --ref or from main.
${BOLD}Options:${RESET}
--codex Install to Codex skills directory
--claude Install to Claude skills directory
--cursor Install to Cursor skills directory
--dest <path> Install to custom directory
--skill <mcp|cli> Skill to install (prompted if omitted)
--ref <git-ref> Git ref to download from (default: main)
--remove-conflict Auto-remove conflicting skill
-h, --help Show this help message

If no destination is provided, the installer will prompt for a client.
${BOLD}Description:${RESET}
Installs the XcodeBuildMCP skill for your AI coding assistant.
If run from a local checkout, installs the local skill file.
Otherwise downloads from GitHub.
EOF
}

Expand Down Expand Up @@ -79,54 +141,70 @@ while [[ $# -gt 0 ]]; do
done

prompt_for_destination() {
print_step "Select Target Client"
while true; do
printf "Which client should receive the skill?\n"
printf "1) Codex\n"
printf "2) Claude\n"
printf "3) Cursor\n"
read -r -p "Enter 1, 2, or 3: " selection
printf "\n"
printf " ${CYAN}[1]${RESET} Codex\n"
printf " ${CYAN}[2]${RESET} Claude\n"
printf " ${CYAN}[3]${RESET} Cursor\n"
printf "\n"
printf " ${DIM}Enter your choice${RESET} ${BOLD}[1-3]:${RESET} "
read -r selection
case "${selection}" in
1)
destination="${HOME}/.codex/skills/public"
print_success "Selected Codex"
return 0
;;
2)
destination="${HOME}/.claude/skills"
print_success "Selected Claude"
return 0
;;
3)
destination="${HOME}/.cursor/skills"
print_success "Selected Cursor"
return 0
;;
*)
echo "Invalid selection. Please enter 1, 2, or 3."
print_error "Invalid selection. Please enter 1, 2, or 3."
;;
esac
done
}

prompt_for_skill() {
print_step "Select Skill Type"
while true; do
printf "Which skill would you like to install?\n"
printf "1) XcodeBuildMCP (MCP server)\n"
printf "2) XcodeBuildMCP CLI\n"
read -r -p "Enter 1 or 2: " selection
printf "\n"
printf " ${CYAN}[1]${RESET} XcodeBuildMCP ${DIM}(MCP server)${RESET}\n"
printf " ${DIM}Full MCP integration with all tools${RESET}\n"
printf "\n"
printf " ${CYAN}[2]${RESET} XcodeBuildMCP CLI\n"
printf " ${DIM}Lightweight CLI-based commands${RESET}\n"
printf "\n"
printf " ${DIM}Enter your choice${RESET} ${BOLD}[1-2]:${RESET} "
read -r selection
case "${selection}" in
1)
skill_choice="mcp"
print_success "Selected MCP server skill"
return 0
;;
2)
skill_choice="cli"
print_success "Selected CLI skill"
return 0
;;
*)
echo "Invalid selection. Please enter 1 or 2."
print_error "Invalid selection. Please enter 1 or 2."
;;
esac
done
}

print_header

if [[ -z "${destination}" ]]; then
prompt_for_destination
fi
Expand Down Expand Up @@ -165,18 +243,28 @@ if [[ -n "${skill_ref_override}" ]]; then
skill_ref="${skill_ref_override}"
fi

print_step "Installing"

if [[ -e "${alt_dir}" ]]; then
if [[ "${remove_conflict}" == "true" ]]; then
rm -r "${alt_dir}"
print_info "Removed conflicting skill: ${alt_label}"
else
printf "%s\n" "Only one skill can be installed at a time because the MCP and CLI skills conflict."
read -r -p "Found ${alt_label} at ${alt_dir}. Remove it to continue? [y/N]: " confirm
printf "\n"
print_warning "Conflict detected!"
printf " ${DIM}Only one skill can be installed at a time.${RESET}\n"
printf " ${DIM}Found:${RESET} ${alt_label}\n"
printf " ${DIM}Path:${RESET} ${alt_dir}\n"
printf "\n"
printf " ${BOLD}Remove existing skill to continue?${RESET} ${DIM}[y/N]:${RESET} "
read -r confirm
case "${confirm}" in
y|Y|yes|YES)
rm -r "${alt_dir}"
print_success "Removed ${alt_label}"
;;
*)
echo "Aborting to avoid installing both skills."
print_error "Installation cancelled"
exit 1
;;
esac
Expand All @@ -185,6 +273,7 @@ fi

if [[ -e "${skill_dir}" ]]; then
rm -r "${skill_dir}"
print_info "Replacing existing installation"
fi
mkdir -p "${skill_dir}"

Expand All @@ -194,16 +283,28 @@ local_skill_path="${repo_root}/${skill_path}"

if [[ -f "${local_skill_path}" ]]; then
cp "${local_skill_path}" "${skill_dir}/SKILL.md"
print_info "Installed from local checkout"
else
if ! curl -fsSL "${primary_url}" -o "${skill_dir}/SKILL.md"; then
print_info "Downloading from GitHub..."
if ! curl -fsSL "${primary_url}" -o "${skill_dir}/SKILL.md" 2>/dev/null; then
if [[ "${skill_ref}" != "main" ]]; then
printf "%s\n" "Release tag ${skill_ref} not found. Falling back to main."
curl -fsSL "${fallback_url}" -o "${skill_dir}/SKILL.md"
print_warning "Tag ${skill_ref} not found, falling back to main"
if ! curl -fsSL "${fallback_url}" -o "${skill_dir}/SKILL.md" 2>/dev/null; then
print_error "Failed to download skill"
exit 1
fi
else
printf "%s\n" "Failed to download ${primary_url}." >&2
print_error "Failed to download skill"
exit 1
fi
fi
fi

printf 'Installed %s to %s\n' "${skill_label}" "${skill_dir}"
printf "\n"
printf "${BOLD}${GREEN}╭─────────────────────────────────────╮${RESET}\n"
printf "${BOLD}${GREEN}│${RESET} ${CHECK} ${BOLD}Installation Complete${RESET} ${BOLD}${GREEN}│${RESET}\n"
printf "${BOLD}${GREEN}╰─────────────────────────────────────╯${RESET}\n"
printf "\n"
printf " ${BOLD}Skill:${RESET} %s\n" "${skill_label}"
printf " ${BOLD}Location:${RESET} %s\n" "${skill_dir}"
printf "\n"
12 changes: 10 additions & 2 deletions scripts/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,14 @@ if [[ "$SKIP_VERSION_UPDATE" == "false" ]]; then
README_SKILL_INSTALL_URL_REGEX='https://raw.githubusercontent.com/cameroncooke/XcodeBuildMCP/[^/]+/scripts/install-skill.sh'
run sed_inplace "s#${README_SKILL_INSTALL_URL_REGEX}#https://raw.githubusercontent.com/cameroncooke/XcodeBuildMCP/v${VERSION}/scripts/install-skill.sh#g" README.md

# Update skill installer URL in docs/SKILLS.md
if [[ -f docs/SKILLS.md ]]; then
echo "📝 Updating skill installer URL in docs/SKILLS.md..."
run sed_inplace "s#${README_SKILL_INSTALL_URL_REGEX}#https://raw.githubusercontent.com/cameroncooke/XcodeBuildMCP/v${VERSION}/scripts/install-skill.sh#g" docs/SKILLS.md
else
echo "⚠️ docs/SKILLS.md not found; skipping update"
fi

# server.json update
echo ""
if [[ -f server.json ]]; then
Expand All @@ -331,9 +339,9 @@ if [[ "$SKIP_VERSION_UPDATE" == "false" ]]; then
echo ""
echo "📦 Committing version changes..."
if [[ -f server.json ]]; then
run git add package.json package-lock.json README.md server.json
run git add package.json package-lock.json README.md docs/SKILLS.md server.json
else
run git add package.json package-lock.json README.md
run git add package.json package-lock.json README.md docs/SKILLS.md
Comment on lines +342 to +344
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The release script will crash if docs/SKILLS.md is missing because git add is called unconditionally while the script runs with set -e.
Severity: HIGH

Suggested Fix

Make the git add command for docs/SKILLS.md conditional, similar to the check performed before modifying the file. For example, wrap the git add calls in an if [[ -f docs/SKILLS.md ]] block to prevent the script from failing if the file does not exist.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: scripts/release.sh#L342-L344

Potential issue: The `release.sh` script includes a check to see if `docs/SKILLS.md`
exists before attempting to modify it. However, it later unconditionally tries to stage
this file using `git add`. Because the script runs with `set -e`, if `docs/SKILLS.md` is
missing, the `git add` command will fail with a non-zero exit code, causing the entire
release script to terminate prematurely. This creates an inconsistency where the script
anticipates the file might be missing but doesn't handle it correctly during the staging
phase, leading to a failed release.

Did we get this right? 👍 / 👎 to inform future reviews.

fi
run git commit -m "Release v$VERSION"
else
Expand Down
Loading