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
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ ESM TypeScript project (`type: module`). Key layers:
- No `.js` imports in `src/` (enforced by ESLint)
- No barrel imports from `utils/index` - import from specific submodules (e.g., `src/utils/execution/index.ts`, `src/utils/logging/index.ts`)


## Rendering and Streaming Contract
- Streaming fragments are transient output only. They MUST NOT be used as internal state, cached for final responses, or promoted into final MCP/JSON/CLI text output.
- Non-streaming runtimes/output modes, including MCP final responses, MUST render only from the final structured result and next-step metadata. If final output needs data, add it to the final result type instead of reading it from fragments.
- Only streaming-capable renderers may observe fragment callbacks, and only to print live progress. Their fragment handling must not affect final structured output or final rendered text.

## Test Conventions
- Vitest with colocated `__tests__/` directories using `*.test.ts`
- Smoke tests in `src/smoke-tests/__tests__/` (separate Vitest config, serial execution)
Expand Down Expand Up @@ -88,6 +94,7 @@ When reading issues:
- Use shared lock and atomic-write helpers for mutable shared files.
- Prefer one-record-per-file registries over shared aggregate files.
- Cleanup must verify ownership before deleting shared artifacts.
- User-facing artifact/log paths in final text or structured output must use `displayPath()` from `src/utils/build-preflight.ts`, so paths are cwd-relative when possible or `~/...` instead of absolute home paths. Keep stored files at their real absolute paths; only normalize response/display values.

## Style
- Keep answers short and concise
Expand All @@ -98,6 +105,7 @@ When reading issues:
## Docs
- Do not commit transient investigation notes, prompt exports, or scratch analysis docs after the work is complete.
- If an investigation leaves unresolved follow-up work, move it to a GitHub issue instead of preserving the transient doc in the branch.
- Structured output JSON schemas are auto-published to the website/public schema mirror when merged; do not manually update public schema copies unless explicitly asked.

### Changelog
Location: `CHANGELOG.md`
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

### Changed

- Updated Xcode IDE `call-tool` output to save raw remote responses as transient workspace-state JSON artifacts, summarize text output without embedding large relayed payloads, and support `--output json` / `--output jsonl` through the generic CLI output path.
- Centralized workspace log retention and startup/shutdown filesystem cleanup so XcodeBuildMCP-owned logs are pruned consistently while preserving active daemon and simulator OSLog outputs.
- Removed internal streaming-fragment context flags so final tool state now comes from explicit structured outputs instead of transient progress fragments ([#360](https://github.com/getsentry/XcodeBuildMCP/issues/360)).

## [2.5.0-beta.1]

Expand Down
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ When reading issues:
- Use shared lock and atomic-write helpers for mutable shared files.
- Prefer one-record-per-file registries over shared aggregate files.
- Cleanup must verify ownership before deleting shared artifacts.
- User-facing artifact/log paths in final text or structured output must use `displayPath()` from `src/utils/build-preflight.ts`, so paths are cwd-relative when possible or `~/...` instead of absolute home paths. Keep stored files at their real absolute paths; only normalize response/display values.

## Style
- Keep answers short and concise
Expand All @@ -39,6 +40,7 @@ When reading issues:
## Docs
- Do not commit transient investigation notes, prompt exports, or scratch analysis docs after the work is complete.
- If an investigation leaves unresolved follow-up work, move it to a GitHub issue instead of preserving the transient doc in the branch.
- Structured output JSON schemas are auto-published to the website/public schema mirror when merged; do not manually update public schema copies unless explicitly asked.

### Changelog
Location: `CHANGELOG.md`
Expand All @@ -62,6 +64,12 @@ Use these sections under `## [Unreleased]`:
- **Internal changes (from issues)**: `Fixed foo bar ([#123](https://github.com/cameroncook/XcodeBuildMCP/issues/123))`
- **External contributions**: `Added feature X ([#456](https://github.com/cameroncook/XcodeBuildMCP/pull/456) by [@username](https://github.com/username))`


## Rendering and Streaming Contract
- Streaming fragments are transient output only. They MUST NOT be used as internal state, cached for final responses, or promoted into final MCP/JSON/CLI text output.
- Non-streaming runtimes/output modes, including MCP final responses, MUST render only from the final structured result and next-step metadata. If final output needs data, add it to the final result type instead of reading it from fragments.
- Only streaming-capable renderers may observe fragment callbacks, and only to print live progress. Their fragment handling must not affect final structured output or final rendered text.

## Test Execution Rules
- When running long test suites (snapshot tests, smoke tests), ALWAYS write full output to a log file and read it afterwards. NEVER pipe through `tail` or `grep` directly — that loses output you may need to debug failures.
- Pattern: `DEVICE_ID=... npm run test:snapshot 2>&1 | tee /tmp/snapshot-results.txt` then read `/tmp/snapshot-results.txt` with the native read tool.
Expand Down
2 changes: 1 addition & 1 deletion manifests/tools/xcode_ide_call_tool.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ names:
description: Call a remote Xcode IDE MCP tool.
outputSchema:
schema: xcodebuildmcp.output.xcode-bridge-call-result
version: "1"
version: '2'
routing:
stateful: true
annotations:
Expand Down
2 changes: 1 addition & 1 deletion manifests/tools/xcode_ide_list_tools.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ names:
description: "Lists Xcode-IDE-only MCP capabilities (Use for: SwiftUI previews image capture, code snippet execution, issue Navigator/build logs, and window/tab context)."
outputSchema:
schema: xcodebuildmcp.output.xcode-bridge-tool-list
version: "1"
version: "2"
routing:
stateful: true
annotations:
Expand Down
41 changes: 41 additions & 0 deletions schemas/structured-output/xcodebuildmcp.output.error/1.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.error/1.schema.json",
"type": "object",
"additionalProperties": false,
"allOf": [
{
"$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency"
}
],
"properties": {
"schema": {
"const": "xcodebuildmcp.output.error"
},
"schemaVersion": {
"const": "1"
},
"didError": {
"const": true
},
"error": {
"type": "string",
"minLength": 1
},
"data": {
"type": "object",
"additionalProperties": false,
"properties": {
"category": {
"enum": ["runtime", "validation", "schema"]
},
"code": {
"type": "string",
"minLength": 1
}
},
"required": ["category", "code"]
}
},
"required": ["schema", "schemaVersion", "didError", "error", "data"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.xcode-bridge-call-result/2.schema.json",
"type": "object",
"additionalProperties": false,
"allOf": [
{
"$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency"
}
],
"$defs": {
"relayedContentItem": {
"type": "object",
"additionalProperties": true,
"properties": {
"type": { "type": "string" }
},
"required": ["type"]
},
"artifacts": {
"type": "object",
"additionalProperties": false,
"properties": {
"rawResponseJsonPath": { "type": "string" }
},
"required": ["rawResponseJsonPath"]
}
},
"properties": {
"schema": { "const": "xcodebuildmcp.output.xcode-bridge-call-result" },
"schemaVersion": { "const": "2" },
"didError": { "type": "boolean" },
"error": { "type": ["string", "null"] },
"data": {
"type": "object",
"additionalProperties": false,
"properties": {
"remoteTool": { "type": "string" },
"succeeded": { "type": "boolean" },
"content": {
"type": "array",
"items": { "$ref": "#/$defs/relayedContentItem" }
},
"artifacts": { "$ref": "#/$defs/artifacts" }
},
"required": ["remoteTool", "succeeded", "content"]
}
},
"required": ["schema", "schemaVersion", "didError", "error", "data"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.xcode-bridge-tool-list/2.schema.json",
"type": "object",
"additionalProperties": false,
"allOf": [
{
"$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency"
}
],
"$defs": {
"artifacts": {
"type": "object",
"additionalProperties": false,
"properties": {
"rawResponseJsonPath": { "type": "string" }
},
"required": ["rawResponseJsonPath"]
}
},
"properties": {
"schema": { "const": "xcodebuildmcp.output.xcode-bridge-tool-list" },
"schemaVersion": { "const": "2" },
"didError": { "type": "boolean" },
"error": { "type": ["string", "null"] },
"data": {
"type": "object",
"additionalProperties": false,
"properties": {
"toolCount": { "type": "integer", "minimum": 0 },
"artifacts": { "$ref": "#/$defs/artifacts" }
},
"required": ["toolCount"]
}
},
"required": ["schema", "schemaVersion", "didError", "error", "data"]
}
31 changes: 25 additions & 6 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ function findTopLevelCommand(argv: string[]): string | undefined {
return undefined;
}

function findGlobalSocketOverride(argv: string[]): string | undefined {
for (let index = 0; index < argv.length; index += 1) {
const token = argv[index];
if (token === '--socket') {
const value = argv[index + 1];
return value && !value.startsWith('-') ? value : undefined;
}

if (token.startsWith('--socket=')) {
const value = token.slice('--socket='.length);
return value || undefined;
}
}

return undefined;
}

async function buildLightweightYargsApp(): Promise<ReturnType<typeof import('yargs').default>> {
const yargs = (await import('yargs')).default;
const { hideBin } = await import('yargs/helpers');
Expand Down Expand Up @@ -120,10 +137,12 @@ async function main(): Promise<void> {

const { workspaceRoot, workspaceKey } = result;

const defaultSocketPath = getSocketPath({
cwd: result.runtime.cwd,
projectConfigPath: result.configPath,
});
const socketPath =
findGlobalSocketOverride(process.argv.slice(2)) ??
getSocketPath({
cwd: result.runtime.cwd,
projectConfigPath: result.configPath,
});

const cliExposedWorkflowIds = await listCliWorkflowIdsFromManifest({
excludeWorkflows: ['session-management', 'workflow-discovery'],
Expand All @@ -133,7 +152,7 @@ async function main(): Promise<void> {

// CLI uses a manifest-resolved catalog plus daemon-backed xcode-ide dynamic tools.
const catalog = await buildCliToolCatalog({
socketPath: defaultSocketPath,
socketPath,
workspaceRoot,
cliExposedWorkflowIds,
discoveryMode,
Expand All @@ -142,7 +161,7 @@ async function main(): Promise<void> {
const yargsApp = buildYargsApp({
catalog,
runtimeConfig: result.runtime.config,
defaultSocketPath,
defaultSocketPath: socketPath,
workspaceRoot,
workspaceKey,
workflowNames: cliExposedWorkflowIds,
Expand Down
Loading
Loading