Skip to content

Commit 0033302

Browse files
committed
fix: use open schema for dynamic tool outputs, return screenshots as images
- Change RawOutput.Result and EvaluateScriptOutput.Result from json.RawMessage to any — fixes MCP SDK schema validation errors where []byte generated an array-of-integers schema that rejected objects - Return screenshots as MCP ImageContent instead of multi-MB text data URLs - Remove unnecessary json.Marshal calls in all tool handlers - Remove debug CI step from simulator workflow - Bump version to 0.2.2
1 parent ad70e2c commit 0033302

3 files changed

Lines changed: 64 additions & 123 deletions

File tree

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "iwdp-mcp",
3-
"version": "0.2.1",
3+
"version": "0.2.2",
44
"description": "iOS Safari debugging via ios-webkit-debug-proxy — MCP server with full WebKit Inspector Protocol support",
55
"mcpServers": {
66
"iwdp-mcp": {

.github/workflows/test.yml

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -110,20 +110,6 @@ jobs:
110110
eval "$(./scripts/sim-setup.sh)"
111111
echo "IWDP_SIM_WS_URL=$IWDP_SIM_WS_URL" >> "$GITHUB_ENV"
112112
113-
- name: Debug iwdp connection
114-
run: |
115-
echo "=== iwdp version ==="
116-
ios_webkit_debug_proxy --version || true
117-
echo ""
118-
echo "=== Devices on port 9221 ==="
119-
curl -sf http://localhost:9221/json | python3 -m json.tool || echo "FAILED"
120-
echo ""
121-
echo "=== Pages on port 9222 ==="
122-
curl -sf http://localhost:9222/json | python3 -m json.tool || echo "FAILED"
123-
echo ""
124-
echo "=== Raw WebSocket test ==="
125-
go run ./scripts/ws-debug/main.go "$IWDP_SIM_WS_URL"
126-
127113
- name: Run simulator tests with coverage
128114
run: go test -tags=simulator ./... -v -count=1 -timeout=300s -coverprofile=coverage-simulator.out
129115

cmd/iwdp-mcp/main.go

Lines changed: 63 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"context"
5+
"encoding/base64"
56
"encoding/json"
67
"fmt"
78
"log"
@@ -38,7 +39,7 @@ func getClient(ctx context.Context) (*webkit.Client, error) {
3839
func main() {
3940
server := mcp.NewServer(&mcp.Implementation{
4041
Name: "iwdp-mcp",
41-
Version: "0.1.0",
42+
Version: "0.2.2",
4243
}, nil)
4344

4445
registerTools(server)
@@ -94,8 +95,8 @@ type EvaluateScriptInput struct {
9495
ReturnByValue bool `json:"return_by_value,omitempty" jsonschema:"return result by value instead of reference"`
9596
}
9697
type EvaluateScriptOutput struct {
97-
Result json.RawMessage `json:"result"`
98-
Type string `json:"type"`
98+
Result any `json:"result"`
99+
Type string `json:"type"`
99100
}
100101

101102
type CallFunctionInput struct {
@@ -373,7 +374,7 @@ type TypeTextInput struct {
373374

374375
// Generic output wrappers
375376
type RawOutput struct {
376-
Result json.RawMessage `json:"result"`
377+
Result any `json:"result"`
377378
}
378379

379380
type TextOutput struct {
@@ -390,6 +391,31 @@ type NodeIDOutput struct {
390391

391392
func ok() OKOutput { return OKOutput{OK: true} }
392393

394+
// imageResultFromDataURL converts a data URL (data:image/png;base64,...) to an
395+
// MCP ImageContent result so the image is delivered natively instead of as a
396+
// multi-megabyte text string.
397+
func imageResultFromDataURL(dataURL string) (*mcp.CallToolResult, error) {
398+
// Expected format: data:image/png;base64,<data>
399+
const prefix = "data:image/png;base64,"
400+
if !strings.HasPrefix(dataURL, prefix) {
401+
// Fallback: return as text
402+
return &mcp.CallToolResult{
403+
Content: []mcp.Content{&mcp.TextContent{Text: dataURL}},
404+
}, nil
405+
}
406+
b64Data := dataURL[len(prefix):]
407+
rawBytes, err := base64.StdEncoding.DecodeString(b64Data)
408+
if err != nil {
409+
return nil, fmt.Errorf("decoding screenshot base64: %w", err)
410+
}
411+
return &mcp.CallToolResult{
412+
Content: []mcp.Content{&mcp.ImageContent{
413+
Data: rawBytes,
414+
MIMEType: "image/png",
415+
}},
416+
}, nil
417+
}
418+
393419
func registerTools(server *mcp.Server) {
394420
// --- Device/Page management ---
395421
mcp.AddTool(server, &mcp.Tool{
@@ -499,7 +525,11 @@ func registerTools(server *mcp.Server) {
499525
if err != nil {
500526
return nil, TakeScreenshotOutput{}, err
501527
}
502-
return nil, TakeScreenshotOutput{DataURL: dataURL}, nil
528+
result, err := imageResultFromDataURL(dataURL)
529+
if err != nil {
530+
return nil, TakeScreenshotOutput{}, err
531+
}
532+
return result, TakeScreenshotOutput{DataURL: dataURL}, nil
503533
})
504534

505535
mcp.AddTool(server, &mcp.Tool{
@@ -513,7 +543,11 @@ func registerTools(server *mcp.Server) {
513543
if err != nil {
514544
return nil, TakeScreenshotOutput{}, err
515545
}
516-
return nil, TakeScreenshotOutput{DataURL: dataURL}, nil
546+
result, err := imageResultFromDataURL(dataURL)
547+
if err != nil {
548+
return nil, TakeScreenshotOutput{}, err
549+
}
550+
return result, TakeScreenshotOutput{DataURL: dataURL}, nil
517551
})
518552

519553
// --- Runtime ---
@@ -528,11 +562,7 @@ func registerTools(server *mcp.Server) {
528562
if err != nil {
529563
return nil, EvaluateScriptOutput{}, err
530564
}
531-
val, err := json.Marshal(result.Result)
532-
if err != nil {
533-
return nil, EvaluateScriptOutput{}, fmt.Errorf("marshaling evaluate result: %w", err)
534-
}
535-
return nil, EvaluateScriptOutput{Result: val, Type: result.Result.Type}, nil
565+
return nil, EvaluateScriptOutput{Result: result.Result, Type: result.Result.Type}, nil
536566
})
537567

538568
mcp.AddTool(server, &mcp.Tool{
@@ -546,11 +576,7 @@ func registerTools(server *mcp.Server) {
546576
if err != nil {
547577
return nil, RawOutput{}, err
548578
}
549-
val, err := json.Marshal(result.Result)
550-
if err != nil {
551-
return nil, RawOutput{}, fmt.Errorf("marshaling call_function result: %w", err)
552-
}
553-
return nil, RawOutput{Result: val}, nil
579+
return nil, RawOutput{Result: result.Result}, nil
554580
})
555581

556582
mcp.AddTool(server, &mcp.Tool{
@@ -564,11 +590,7 @@ func registerTools(server *mcp.Server) {
564590
if err != nil {
565591
return nil, RawOutput{}, err
566592
}
567-
val, err := json.Marshal(props)
568-
if err != nil {
569-
return nil, RawOutput{}, fmt.Errorf("marshaling properties: %w", err)
570-
}
571-
return nil, RawOutput{Result: val}, nil
593+
return nil, RawOutput{Result: props}, nil
572594
})
573595

574596
// --- DOM ---
@@ -583,11 +605,7 @@ func registerTools(server *mcp.Server) {
583605
if err != nil {
584606
return nil, RawOutput{}, err
585607
}
586-
val, err := json.Marshal(doc)
587-
if err != nil {
588-
return nil, RawOutput{}, fmt.Errorf("marshaling document: %w", err)
589-
}
590-
return nil, RawOutput{Result: val}, nil
608+
return nil, RawOutput{Result: doc}, nil
591609
})
592610

593611
mcp.AddTool(server, &mcp.Tool{
@@ -657,11 +675,7 @@ func registerTools(server *mcp.Server) {
657675
if err != nil {
658676
return nil, RawOutput{}, err
659677
}
660-
val, err := json.Marshal(listeners)
661-
if err != nil {
662-
return nil, RawOutput{}, fmt.Errorf("marshaling event listeners: %w", err)
663-
}
664-
return nil, RawOutput{Result: val}, nil
678+
return nil, RawOutput{Result: listeners}, nil
665679
})
666680

667681
mcp.AddTool(server, &mcp.Tool{
@@ -710,11 +724,7 @@ func registerTools(server *mcp.Server) {
710724
if err != nil {
711725
return nil, RawOutput{}, err
712726
}
713-
val, err := json.Marshal(props)
714-
if err != nil {
715-
return nil, RawOutput{}, fmt.Errorf("marshaling computed style: %w", err)
716-
}
717-
return nil, RawOutput{Result: val}, nil
727+
return nil, RawOutput{Result: props}, nil
718728
})
719729

720730
mcp.AddTool(server, &mcp.Tool{
@@ -728,11 +738,7 @@ func registerTools(server *mcp.Server) {
728738
if err != nil {
729739
return nil, RawOutput{}, err
730740
}
731-
val, err := json.Marshal(style)
732-
if err != nil {
733-
return nil, RawOutput{}, fmt.Errorf("marshaling inline styles: %w", err)
734-
}
735-
return nil, RawOutput{Result: val}, nil
741+
return nil, RawOutput{Result: style}, nil
736742
})
737743

738744
mcp.AddTool(server, &mcp.Tool{
@@ -746,11 +752,7 @@ func registerTools(server *mcp.Server) {
746752
if err != nil {
747753
return nil, RawOutput{}, err
748754
}
749-
val, err := json.Marshal(style)
750-
if err != nil {
751-
return nil, RawOutput{}, fmt.Errorf("marshaling style text: %w", err)
752-
}
753-
return nil, RawOutput{Result: val}, nil
755+
return nil, RawOutput{Result: style}, nil
754756
})
755757

756758
mcp.AddTool(server, &mcp.Tool{
@@ -764,11 +766,7 @@ func registerTools(server *mcp.Server) {
764766
if err != nil {
765767
return nil, RawOutput{}, err
766768
}
767-
val, err := json.Marshal(sheets)
768-
if err != nil {
769-
return nil, RawOutput{}, fmt.Errorf("marshaling stylesheets: %w", err)
770-
}
771-
return nil, RawOutput{Result: val}, nil
769+
return nil, RawOutput{Result: sheets}, nil
772770
})
773771

774772
mcp.AddTool(server, &mcp.Tool{
@@ -835,14 +833,9 @@ func registerTools(server *mcp.Server) {
835833
nm := sess.networkMonitor
836834
sess.mu.Unlock()
837835
if nm == nil {
838-
return nil, RawOutput{Result: json.RawMessage("[]")}, nil
836+
return nil, RawOutput{Result: []any{}}, nil
839837
}
840-
reqs := nm.GetRequests()
841-
val, err := json.Marshal(reqs)
842-
if err != nil {
843-
return nil, RawOutput{}, fmt.Errorf("marshaling network requests: %w", err)
844-
}
845-
return nil, RawOutput{Result: val}, nil
838+
return nil, RawOutput{Result: nm.GetRequests()}, nil
846839
})
847840

848841
mcp.AddTool(server, &mcp.Tool{
@@ -931,11 +924,7 @@ func registerTools(server *mcp.Server) {
931924
if err != nil {
932925
return nil, RawOutput{}, err
933926
}
934-
val, err := json.Marshal(cookies)
935-
if err != nil {
936-
return nil, RawOutput{}, fmt.Errorf("marshaling cookies: %w", err)
937-
}
938-
return nil, RawOutput{Result: val}, nil
927+
return nil, RawOutput{Result: cookies}, nil
939928
})
940929

941930
mcp.AddTool(server, &mcp.Tool{
@@ -978,11 +967,7 @@ func registerTools(server *mcp.Server) {
978967
if err != nil {
979968
return nil, RawOutput{}, err
980969
}
981-
val, err := json.Marshal(items)
982-
if err != nil {
983-
return nil, RawOutput{}, fmt.Errorf("marshaling local storage: %w", err)
984-
}
985-
return nil, RawOutput{Result: val}, nil
970+
return nil, RawOutput{Result: items}, nil
986971
})
987972

988973
mcp.AddTool(server, &mcp.Tool{
@@ -1026,11 +1011,7 @@ func registerTools(server *mcp.Server) {
10261011
if err != nil {
10271012
return nil, RawOutput{}, err
10281013
}
1029-
val, err := json.Marshal(items)
1030-
if err != nil {
1031-
return nil, RawOutput{}, fmt.Errorf("marshaling session storage: %w", err)
1032-
}
1033-
return nil, RawOutput{Result: val}, nil
1014+
return nil, RawOutput{Result: items}, nil
10341015
})
10351016

10361017
mcp.AddTool(server, &mcp.Tool{
@@ -1074,11 +1055,7 @@ func registerTools(server *mcp.Server) {
10741055
if err != nil {
10751056
return nil, RawOutput{}, err
10761057
}
1077-
val, err := json.Marshal(dbs)
1078-
if err != nil {
1079-
return nil, RawOutput{}, fmt.Errorf("marshaling indexed databases: %w", err)
1080-
}
1081-
return nil, RawOutput{Result: val}, nil
1058+
return nil, RawOutput{Result: dbs}, nil
10821059
})
10831060

10841061
mcp.AddTool(server, &mcp.Tool{
@@ -1149,14 +1126,9 @@ func registerTools(server *mcp.Server) {
11491126
cc := sess.consoleCollector
11501127
sess.mu.Unlock()
11511128
if cc == nil {
1152-
return nil, RawOutput{Result: json.RawMessage("[]")}, nil
1129+
return nil, RawOutput{Result: []any{}}, nil
11531130
}
1154-
msgs := cc.GetMessages()
1155-
val, err := json.Marshal(msgs)
1156-
if err != nil {
1157-
return nil, RawOutput{}, fmt.Errorf("marshaling console messages: %w", err)
1158-
}
1159-
return nil, RawOutput{Result: val}, nil
1131+
return nil, RawOutput{Result: cc.GetMessages()}, nil
11601132
})
11611133

11621134
mcp.AddTool(server, &mcp.Tool{
@@ -1216,11 +1188,7 @@ func registerTools(server *mcp.Server) {
12161188
if err != nil {
12171189
return nil, RawOutput{}, err
12181190
}
1219-
val, err := json.Marshal(map[string]interface{}{"breakpointId": bpID, "locations": locs})
1220-
if err != nil {
1221-
return nil, RawOutput{}, fmt.Errorf("marshaling breakpoint result: %w", err)
1222-
}
1223-
return nil, RawOutput{Result: val}, nil
1191+
return nil, RawOutput{Result: map[string]interface{}{"breakpointId": bpID, "locations": locs}}, nil
12241192
})
12251193

12261194
mcp.AddTool(server, &mcp.Tool{
@@ -1322,11 +1290,7 @@ func registerTools(server *mcp.Server) {
13221290
if err != nil {
13231291
return nil, RawOutput{}, err
13241292
}
1325-
val, err := json.Marshal(result)
1326-
if err != nil {
1327-
return nil, RawOutput{}, fmt.Errorf("marshaling evaluate_on_call_frame result: %w", err)
1328-
}
1329-
return nil, RawOutput{Result: val}, nil
1293+
return nil, RawOutput{Result: result}, nil
13301294
})
13311295

13321296
mcp.AddTool(server, &mcp.Tool{
@@ -1439,14 +1403,9 @@ func registerTools(server *mcp.Server) {
14391403
tc := sess.timelineCollector
14401404
sess.mu.Unlock()
14411405
if tc == nil {
1442-
return nil, RawOutput{Result: json.RawMessage("[]")}, nil
1443-
}
1444-
events := tc.GetEvents()
1445-
val, err := json.Marshal(events)
1446-
if err != nil {
1447-
return nil, RawOutput{}, fmt.Errorf("marshaling timeline events: %w", err)
1406+
return nil, RawOutput{Result: []any{}}, nil
14481407
}
1449-
return nil, RawOutput{Result: val}, nil
1408+
return nil, RawOutput{Result: tc.GetEvents()}, nil
14501409
})
14511410

14521411
// --- Memory & Heap ---
@@ -1481,7 +1440,7 @@ func registerTools(server *mcp.Server) {
14811440
if err != nil {
14821441
return nil, RawOutput{}, err
14831442
}
1484-
return nil, RawOutput{Result: json.RawMessage(result)}, nil
1443+
return nil, RawOutput{Result: result}, nil
14851444
})
14861445

14871446
mcp.AddTool(server, &mcp.Tool{
@@ -1629,11 +1588,7 @@ func registerTools(server *mcp.Server) {
16291588
if err != nil {
16301589
return nil, RawOutput{}, err
16311590
}
1632-
val, err := json.Marshal(obj)
1633-
if err != nil {
1634-
return nil, RawOutput{}, fmt.Errorf("marshaling animation object: %w", err)
1635-
}
1636-
return nil, RawOutput{Result: val}, nil
1591+
return nil, RawOutput{Result: obj}, nil
16371592
})
16381593

16391594
// --- Canvas ---

0 commit comments

Comments
 (0)