Skip to content

Commit 96da376

Browse files
committed
Add session identifiers to new resource
1 parent 8317fae commit 96da376

13 files changed

Lines changed: 326 additions & 168 deletions

.smithery/index.cjs

Lines changed: 149 additions & 149 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"xcodebuildmcp-doctor": "build/doctor-cli.js"
1313
},
1414
"scripts": {
15-
"build": "npm run generate:version && npm run generate:loaders && npx smithery build",
15+
"build": "npm run generate:version && npm run generate:loaders && npx smithery build && tsup",
1616
"dev": "npm run generate:version && npm run generate:loaders && npx smithery dev",
1717
"build:tsup": "npm run generate:version && npm run generate:loaders && tsup",
1818
"dev:tsup": "npm run build:tsup && tsup --watch",

src/core/generated-resources.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ export const RESOURCE_LOADERS = {
1010
const module = await import('../mcp/resources/doctor.js');
1111
return module.default;
1212
},
13+
'session-status': async () => {
14+
const module = await import('../mcp/resources/session-status.js');
15+
return module.default;
16+
},
1317
simulators: async () => {
1418
const module = await import('../mcp/resources/simulators.js');
1519
return module.default;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2+
import { getDefaultDebuggerManager } from '../../../utils/debugger/index.ts';
3+
import { activeLogSessions } from '../../../utils/log_capture.ts';
4+
import { activeDeviceLogSessions } from '../../../utils/log-capture/device-log-sessions.ts';
5+
import sessionStatusResource, { sessionStatusResourceLogic } from '../session-status.ts';
6+
7+
describe('session-status resource', () => {
8+
beforeEach(async () => {
9+
activeLogSessions.clear();
10+
activeDeviceLogSessions.clear();
11+
await getDefaultDebuggerManager().disposeAll();
12+
});
13+
14+
afterEach(async () => {
15+
activeLogSessions.clear();
16+
activeDeviceLogSessions.clear();
17+
await getDefaultDebuggerManager().disposeAll();
18+
});
19+
20+
describe('Export Field Validation', () => {
21+
it('should export correct uri', () => {
22+
expect(sessionStatusResource.uri).toBe('xcodebuildmcp://session-status');
23+
});
24+
25+
it('should export correct description', () => {
26+
expect(sessionStatusResource.description).toBe(
27+
'Runtime session state for log capture and debugging',
28+
);
29+
});
30+
31+
it('should export correct mimeType', () => {
32+
expect(sessionStatusResource.mimeType).toBe('text/plain');
33+
});
34+
35+
it('should export handler function', () => {
36+
expect(typeof sessionStatusResource.handler).toBe('function');
37+
});
38+
});
39+
40+
describe('Handler Functionality', () => {
41+
it('should return empty status when no sessions exist', async () => {
42+
const result = await sessionStatusResourceLogic();
43+
44+
expect(result.contents).toHaveLength(1);
45+
const parsed = JSON.parse(result.contents[0].text);
46+
47+
expect(parsed.logging.simulator.activeSessionIds).toEqual([]);
48+
expect(parsed.logging.device.activeSessionIds).toEqual([]);
49+
expect(parsed.debug.currentSessionId).toBe(null);
50+
expect(parsed.debug.sessionIds).toEqual([]);
51+
});
52+
});
53+
});
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Session Status Resource Plugin
3+
*
4+
* Provides read-only runtime session state for log capture and debugging.
5+
*/
6+
7+
import { log } from '../../utils/logging/index.ts';
8+
import { getSessionRuntimeStatusSnapshot } from '../../utils/session-status.ts';
9+
10+
export async function sessionStatusResourceLogic(): Promise<{ contents: Array<{ text: string }> }> {
11+
try {
12+
log('info', 'Processing session status resource request');
13+
const status = getSessionRuntimeStatusSnapshot();
14+
15+
return {
16+
contents: [
17+
{
18+
text: JSON.stringify(status, null, 2),
19+
},
20+
],
21+
};
22+
} catch (error) {
23+
const errorMessage = error instanceof Error ? error.message : String(error);
24+
log('error', `Error in session status resource handler: ${errorMessage}`);
25+
26+
return {
27+
contents: [
28+
{
29+
text: `Error retrieving session status: ${errorMessage}`,
30+
},
31+
],
32+
};
33+
}
34+
}
35+
36+
export default {
37+
uri: 'xcodebuildmcp://session-status',
38+
name: 'session-status',
39+
description: 'Runtime session state for log capture and debugging',
40+
mimeType: 'text/plain',
41+
async handler(): Promise<{ contents: Array<{ text: string }> }> {
42+
return sessionStatusResourceLogic();
43+
},
44+
};

src/mcp/tools/logging/__tests__/start_device_log_cap.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ import {
1010
createMockExecutor,
1111
createMockFileSystemExecutor,
1212
} from '../../../../test-utils/mock-executors.ts';
13-
import plugin, {
14-
start_device_log_capLogic,
15-
activeDeviceLogSessions,
16-
} from '../start_device_log_cap.ts';
13+
import plugin, { start_device_log_capLogic } from '../start_device_log_cap.ts';
14+
import { activeDeviceLogSessions } from '../../../../utils/log-capture/device-log-sessions.ts';
1715
import { sessionStore } from '../../../../utils/session-store.ts';
1816

1917
describe('start_device_log_cap plugin', () => {

src/mcp/tools/logging/__tests__/stop_device_log_cap.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import { describe, it, expect, beforeEach } from 'vitest';
55
import { EventEmitter } from 'events';
66
import * as z from 'zod';
77
import plugin, { stop_device_log_capLogic } from '../stop_device_log_cap.ts';
8-
import { activeDeviceLogSessions, type DeviceLogSession } from '../start_device_log_cap.ts';
8+
import {
9+
activeDeviceLogSessions,
10+
type DeviceLogSession,
11+
} from '../../../../utils/log-capture/device-log-sessions.ts';
912
import { createMockFileSystemExecutor } from '../../../../test-utils/mock-executors.ts';
1013

1114
// Note: Logger is allowed to execute normally (integration testing pattern)

src/mcp/tools/logging/start_device_log_cap.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ import {
1818
createSessionAwareTool,
1919
getSessionAwareToolSchemaShape,
2020
} from '../../../utils/typed-tool-factory.ts';
21+
import {
22+
activeDeviceLogSessions,
23+
type DeviceLogSession,
24+
} from '../../../utils/log-capture/device-log-sessions.ts';
2125

2226
/**
2327
* Log file retention policy for device logs:
@@ -32,17 +36,6 @@ const DEVICE_LOG_FILE_PREFIX = 'xcodemcp_device_log_';
3236
// - Devices use 'xcrun devicectl' with console output only (no OSLog streaming)
3337
// The different command structures and output formats make sharing infrastructure complex.
3438
// However, both follow similar patterns for session management and log retention.
35-
export interface DeviceLogSession {
36-
process: ChildProcess;
37-
logFilePath: string;
38-
deviceUuid: string;
39-
bundleId: string;
40-
logStream?: fs.WriteStream;
41-
hasEnded: boolean;
42-
}
43-
44-
export const activeDeviceLogSessions = new Map<string, DeviceLogSession>();
45-
4639
const EARLY_FAILURE_WINDOW_MS = 5000;
4740
const INITIAL_OUTPUT_LIMIT = 8_192;
4841
const DEFAULT_JSON_RESULT_WAIT_MS = 8000;

src/mcp/tools/logging/stop_device_log_cap.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
import * as fs from 'fs';
88
import * as z from 'zod';
99
import { log } from '../../../utils/logging/index.ts';
10-
import { activeDeviceLogSessions, type DeviceLogSession } from './start_device_log_cap.ts';
10+
import {
11+
activeDeviceLogSessions,
12+
type DeviceLogSession,
13+
} from '../../../utils/log-capture/device-log-sessions.ts';
1114
import { ToolResponse } from '../../../types/common.ts';
1215
import { getDefaultFileSystemExecutor, getDefaultCommandExecutor } from '../../../utils/command.ts';
1316
import { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts';

src/utils/debugger/debugger-manager.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ export class DebuggerManager {
7575
return this.currentSessionId;
7676
}
7777

78+
listSessions(): DebugSessionInfo[] {
79+
return Array.from(this.sessions.values()).map((session) => ({ ...session.info }));
80+
}
81+
7882
findSessionForSimulator(simulatorId: string): DebugSessionInfo | null {
7983
if (!simulatorId) return null;
8084
if (this.currentSessionId) {

0 commit comments

Comments
 (0)