• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

orion-ecs / keen-eye / 20801994901

08 Jan 2026 01:11AM UTC coverage: 87.325% (-0.04%) from 87.36%
20801994901

push

github

tyevco
fix(mcp): Fix JSON serialization for state and capture tools

Fixes #837, #838, #839

- Add typed argument records for capture commands (SaveScreenshotArgs,
  GetScreenshotBytesArgs, StartRecordingArgs) to replace anonymous types
  that weren't compatible with source-generated JSON
- Update RemoteCaptureController to use the new typed args
- Fix SerializeComponent to convert complex types to JSON-safe primitives:
  - Enums to strings
  - TimeSpan to milliseconds
  - DateTime to ISO 8601 strings
  - Nested structs/classes to dictionaries
- Add IncludeComponentData option to EntityQuery for state_query_entities
- Update state_query_entities MCP tool to expose includeComponentData param

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

9156 of 12261 branches covered (74.68%)

Branch coverage included in aggregate %.

8 of 70 new or added lines in 4 files covered. (11.43%)

7 existing lines in 4 files now uncovered.

158433 of 179654 relevant lines covered (88.19%)

1.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

14.29
/src/KeenEyes.TestBridge.Client/RemoteCaptureController.cs
1
using KeenEyes.TestBridge.Capture;
2
using KeenEyes.TestBridge.Ipc.Protocol;
3

4
namespace KeenEyes.TestBridge.Client;
5

6
/// <summary>
7
/// Remote implementation of <see cref="ICaptureController"/> that communicates over IPC.
8
/// </summary>
9
internal sealed class RemoteCaptureController(TestBridgeClient client) : ICaptureController
1✔
10
{
11
    /// <inheritdoc />
12
    public bool IsAvailable => client.SendRequestAsync<bool>("capture.isAvailable", null, CancellationToken.None)
1✔
13
        .GetAwaiter().GetResult();
1✔
14

15
    /// <inheritdoc />
16
    public bool IsRecording => client.SendRequestAsync<bool>("capture.isRecording", null, CancellationToken.None)
1✔
17
        .GetAwaiter().GetResult();
1✔
18

19
    /// <inheritdoc />
20
    public int RecordedFrameCount => client.SendRequestAsync<int>("capture.recordedFrameCount", null, CancellationToken.None)
×
21
        .GetAwaiter().GetResult();
×
22

23
    /// <inheritdoc />
24
    public async Task<FrameCapture> CaptureFrameAsync()
25
    {
26
        var result = await client.SendRequestAsync<FrameCapture>("capture.captureFrame", null, CancellationToken.None);
×
27
        return result;
×
28
    }
×
29

30
    /// <inheritdoc />
31
    public async Task<string> SaveScreenshotAsync(string filePath, ImageFormat format = ImageFormat.Png)
32
    {
NEW
33
        var args = new SaveScreenshotArgs { FilePath = filePath, Format = format.ToString() };
×
NEW
34
        var result = await client.SendRequestAsync<string>("capture.saveScreenshot", args, CancellationToken.None);
×
35
        return result ?? throw new InvalidOperationException("Failed to save screenshot");
×
36
    }
×
37

38
    /// <inheritdoc />
39
    public async Task<byte[]> GetScreenshotBytesAsync(ImageFormat format = ImageFormat.Png)
40
    {
41
        // Server returns base64-encoded bytes
NEW
42
        var args = new GetScreenshotBytesArgs { Format = format.ToString() };
×
NEW
43
        var base64 = await client.SendRequestAsync<string>("capture.getScreenshotBytes", args, CancellationToken.None);
×
UNCOV
44
        if (string.IsNullOrEmpty(base64))
×
45
        {
46
            throw new InvalidOperationException("Failed to get screenshot bytes");
×
47
        }
48

49
        return Convert.FromBase64String(base64);
×
50
    }
×
51

52
    /// <inheritdoc />
53
    public async Task<(int Width, int Height)> GetFrameSizeAsync()
54
    {
55
        var result = await client.SendRequestAsync<FrameSizeResult>("capture.getFrameSize", null, CancellationToken.None);
×
56
        if (result == null)
×
57
        {
58
            throw new InvalidOperationException("Failed to get frame size");
×
59
        }
60

61
        return (result.Width, result.Height);
×
62
    }
×
63

64
    /// <inheritdoc />
65
    public async Task StartRecordingAsync(int maxFrames = 300)
66
    {
NEW
67
        var args = new StartRecordingArgs { MaxFrames = maxFrames };
×
NEW
68
        await client.SendRequestAsync("capture.startRecording", args, CancellationToken.None);
×
UNCOV
69
    }
×
70

71
    /// <inheritdoc />
72
    public async Task<IReadOnlyList<FrameCapture>> StopRecordingAsync()
73
    {
74
        var result = await client.SendRequestAsync<FrameCapture[]>("capture.stopRecording", null, CancellationToken.None);
×
75
        return result ?? [];
×
76
    }
×
77
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc