$ErrorActionPreference = "Stop" Set-StrictMode -Version Latest $repo = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path $expectedTools = @( "win_helper_version", "win_helper_self_check", "win_helper_scan_windows", "win_helper_dump_uia", "isphere_receive_messages", "isphere_search_contacts", "isphere_search_groups", "isphere_receive_files" ) $tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("isphere-go-mcp-verify-" + [guid]::NewGuid().ToString("N")) $goMcpExe = Join-Path $tempRoot "isphere-mcp.exe" $harnessDir = Join-Path $repo ("runs\go-mcp-harness-" + [guid]::NewGuid().ToString("N")) $harnessMain = Join-Path $harnessDir "main.go" function Assert-True([bool]$Condition, [string]$Message) { if (-not $Condition) { throw $Message } } function Invoke-Step([string]$Name, [scriptblock]$Body) { Write-Host "## $Name" & $Body } try { New-Item -ItemType Directory -Force -Path $tempRoot | Out-Null New-Item -ItemType Directory -Force -Path $harnessDir | Out-Null Push-Location $repo try { Invoke-Step "build C# helper" { & powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $repo "scripts\build-win-helper.ps1") | Out-Host if ($LASTEXITCODE -ne 0) { throw "build-win-helper.ps1 failed with exit code $LASTEXITCODE" } } Invoke-Step "build Go MCP binary" { & go build -o $goMcpExe ./cmd/isphere-mcp if ($LASTEXITCODE -ne 0) { throw "go build ./cmd/isphere-mcp failed with exit code $LASTEXITCODE" } Assert-True (Test-Path -LiteralPath $goMcpExe) "Go MCP binary was not created: $goMcpExe" } } finally { Pop-Location } Invoke-Step "write temporary SDK harness" { @" package main import ( "context" "crypto/cipher" "crypto/des" "encoding/base64" "encoding/json" "fmt" "os" "path/filepath" "reflect" "sort" "strings" "time" "github.com/modelcontextprotocol/go-sdk/mcp" "isphere-ai-bridge/internal/mcpserver" ) var expectedTools = []string{ "win_helper_version", "win_helper_self_check", "win_helper_scan_windows", "win_helper_dump_uia", "isphere_receive_messages", "isphere_search_contacts", "isphere_search_groups", "isphere_receive_files", } var forbiddenTerms = []string{"send", "login", "conversation", "upload", "download", "autologin"} func main() { ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() if err := os.Unsetenv("ISPHERE_PACKET_LOG_FILE"); err != nil { fail("server/env", err) } serverTransport, clientTransport := mcp.NewInMemoryTransports() server, err := mcpserver.NewServerFromEnv() if err != nil { fail("server/config", err) } serverErr := make(chan error, 1) go func() { serverErr <- server.Run(ctx, serverTransport) }() client := mcp.NewClient(&mcp.Implementation{Name: "verify-go-mcp", Version: "0.1.0"}, nil) session, err := client.Connect(ctx, clientTransport, nil) if err != nil { fail("initialize/connect", err) } defer func() { _ = session.Close() cancel() select { case <-serverErr: case <-time.After(2 * time.Second): fail("server shutdown", fmt.Errorf("server did not stop")) } }() listResult, err := session.ListTools(ctx, &mcp.ListToolsParams{}) if err != nil { fail("tools/list", err) } toolNames := make([]string, 0, len(listResult.Tools)) for _, tool := range listResult.Tools { toolNames = append(toolNames, tool.Name) lower := strings.ToLower(tool.Name) for _, term := range forbiddenTerms { if strings.Contains(lower, term) { fail("tools/list", fmt.Errorf("forbidden action term %q found in tool %q", term, tool.Name)) } } } sort.Strings(toolNames) want := append([]string(nil), expectedTools...) sort.Strings(want) if len(toolNames) != 8 || !reflect.DeepEqual(toolNames, want) { fail("tools/list", fmt.Errorf("tool names = %#v, want %#v", toolNames, want)) } callResult, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "win_helper_version", Arguments: map[string]any{}}) if err != nil { fail("tools/call win_helper_version", err) } if callResult.IsError { fail("tools/call win_helper_version", fmt.Errorf("tool returned isError=true: %#v", callResult.Content)) } structured, ok := callResult.StructuredContent.(map[string]any) if !ok { payload, _ := json.Marshal(callResult.StructuredContent) if err := json.Unmarshal(payload, &structured); err != nil { fail("tools/call win_helper_version", fmt.Errorf("structuredContent was not object: %T", callResult.StructuredContent)) } } if structured["ok"] != true { fail("tools/call win_helper_version", fmt.Errorf("ok = %#v, want true", structured["ok"])) } data, ok := structured["data"].(map[string]any) if !ok { fail("tools/call win_helper_version", fmt.Errorf("data missing or not object: %#v", structured["data"])) } helperName, _ := data["helper_name"].(string) if helperName != "ISphereWinHelper" { fail("tools/call win_helper_version", fmt.Errorf("helper_name = %q, want ISphereWinHelper", helperName)) } receiveResult, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "isphere_receive_messages", Arguments: map[string]any{ "source_preference": "local_readonly", "preview": true, "cursor": "", "include_attachment_metadata": true, "limit": 5, }}) if err != nil { fail("tools/call isphere_receive_messages", err) } if receiveResult.IsError { fail("tools/call isphere_receive_messages", fmt.Errorf("tool returned isError=true: %#v", receiveResult.Content)) } var receivePayload map[string]any receiveEncoded, _ := json.Marshal(receiveResult.StructuredContent) if err := json.Unmarshal(receiveEncoded, &receivePayload); err != nil { fail("tools/call isphere_receive_messages", fmt.Errorf("decode structuredContent %s: %w", receiveEncoded, err)) } if receivePayload["ok"] != true { fail("tools/call isphere_receive_messages", fmt.Errorf("ok = %#v, want true", receivePayload["ok"])) } messagesValue, ok := receivePayload["messages"].([]any) if !ok { fail("tools/call isphere_receive_messages", fmt.Errorf("messages missing or not array: %s", receiveEncoded)) } if len(messagesValue) != 0 { fail("tools/call isphere_receive_messages", fmt.Errorf("default empty source returned %d messages, want 0", len(messagesValue))) } defaultContactCount := verifySearchContacts(ctx, session, "sender", "tools/call isphere_search_contacts default", 0) defaultGroupCount := verifySearchGroups(ctx, session, "project", "tools/call isphere_search_groups default", 0) defaultFileCount := verifyReceiveFiles(ctx, session, "report", "tools/call isphere_receive_files default", 0, "", "") configuredReceiveCount, configuredContactCount, configuredGroupCount, configuredFileCount := verifyConfiguredReceiveSource(ctx) configuredDirReceiveCount, configuredDirContactCount, configuredDirGroupCount, configuredDirFileCount := verifyConfiguredDirectorySource(ctx) result := map[string]any{ "ok": true, "transport": "sdk_in_memory", "initialized": true, "tool_count": len(toolNames), "tools": toolNames, "helper_name": helperName, "receive_message_count": len(messagesValue), "contact_count": defaultContactCount, "group_count": defaultGroupCount, "file_count": defaultFileCount, "configured_receive_message_count": configuredReceiveCount, "configured_contact_count": configuredContactCount, "configured_group_count": configuredGroupCount, "configured_file_count": configuredFileCount, "configured_dir_receive_message_count": configuredDirReceiveCount, "configured_dir_contact_count": configuredDirContactCount, "configured_dir_group_count": configuredDirGroupCount, "configured_dir_file_count": configuredDirFileCount, "packet_log_file_configured": os.Getenv("ISPHERE_PACKET_LOG_FILE") != "", "packet_log_dir_configured": os.Getenv("ISPHERE_PACKET_LOG_DIR") != "", "real_isphere_login_required": false, "python_runtime_required": false, "action_tools_present": false, } encoded, _ := json.Marshal(result) fmt.Println(string(encoded)) } func verifyConfiguredReceiveSource(ctx context.Context) (int, int, int, int) { plaintext := "--------------------------------------------------------------------------------------------------------------------------------------------\n" + "2026/7/7 15:30:07\n" + "\n" + " redacted-report.docx\n" + " \n" + " FILE_TRANSFER_CANCEL\n" + " \n" + " \n" + "" line, err := encryptPacketLogLineForHarness(plaintext) if err != nil { fail("fixture/encrypt", err) } file, err := os.CreateTemp("", "isphere-packet-log-*.txt") if err != nil { fail("fixture/create", err) } fixturePath := file.Name() defer os.Remove(fixturePath) if _, err := file.WriteString("\n" + line + "\n"); err != nil { _ = file.Close() fail("fixture/write", err) } if err := file.Close(); err != nil { fail("fixture/close", err) } oldValue, hadOldValue := os.LookupEnv("ISPHERE_PACKET_LOG_FILE") if err := os.Setenv("ISPHERE_PACKET_LOG_FILE", fixturePath); err != nil { fail("fixture/env", err) } defer func() { if hadOldValue { _ = os.Setenv("ISPHERE_PACKET_LOG_FILE", oldValue) } else { _ = os.Unsetenv("ISPHERE_PACKET_LOG_FILE") } }() serverCtx, cancel := context.WithCancel(ctx) defer cancel() serverTransport, clientTransport := mcp.NewInMemoryTransports() server, err := mcpserver.NewServerFromEnv() if err != nil { fail("fixture/server_config", err) } serverErr := make(chan error, 1) go func() { serverErr <- server.Run(serverCtx, serverTransport) }() client := mcp.NewClient(&mcp.Implementation{Name: "verify-go-mcp-fixture", Version: "0.1.0"}, nil) session, err := client.Connect(serverCtx, clientTransport, nil) if err != nil { cancel() fail("fixture/connect", err) } defer func() { _ = session.Close() cancel() select { case <-serverErr: case <-time.After(2 * time.Second): fail("fixture/server_shutdown", fmt.Errorf("server did not stop")) } }() callResult, err := session.CallTool(serverCtx, &mcp.CallToolParams{Name: "isphere_receive_messages", Arguments: map[string]any{ "conversation_id": "project-room@conference.imopenfire1-lanzhou|sender@imopenfire1-lanzhou", "query": "report", "since": "2026-07-07T11:30:00Z", "source_preference": "auto", "preview": true, "cursor": "", "include_attachment_metadata": true, "limit": 5, }}) if err != nil { fail("fixture/call isphere_receive_messages", err) } if callResult.IsError { fail("fixture/call isphere_receive_messages", fmt.Errorf("tool returned isError=true: %#v", callResult.Content)) } var receivePayload map[string]any receiveEncoded, _ := json.Marshal(callResult.StructuredContent) if err := json.Unmarshal(receiveEncoded, &receivePayload); err != nil { fail("fixture/decode", fmt.Errorf("decode structuredContent %s: %w", receiveEncoded, err)) } messagesValue, ok := receivePayload["messages"].([]any) if !ok { fail("fixture/decode", fmt.Errorf("messages missing or not array: %s", receiveEncoded)) } if len(messagesValue) != 1 { fail("fixture/assert", fmt.Errorf("configured source returned %d messages, want 1", len(messagesValue))) } message, ok := messagesValue[0].(map[string]any) if !ok { fail("fixture/assert", fmt.Errorf("message was not object: %#v", messagesValue[0])) } if message["id"] != "msg-fixture-1" || message["text"] != "redacted-report.docx" || message["subject"] != "FILE_TRANSFER_CANCEL" { fail("fixture/assert", fmt.Errorf("unexpected message: %#v", message)) } if message["message_id"] != "msg-fixture-1" || message["content_text"] != "redacted-report.docx" || message["content_type"] != "file" || message["source"] != "local_readonly" || message["raw_ref"] != "packetlog_message" { fail("fixture/assert_contract", fmt.Errorf("unexpected contract message fields: %#v", message)) } attachments, ok := message["attachments"].([]any) if !ok || len(attachments) != 1 { fail("fixture/assert_contract", fmt.Errorf("attachments = %#v, want one", message["attachments"])) } attachment, ok := attachments[0].(map[string]any) if !ok || attachment["file_id"] != "msg-fixture-1:redacted-report.docx" || attachment["file_name"] != "redacted-report.docx" || attachment["download_ref"] != nil { fail("fixture/assert_contract", fmt.Errorf("unexpected attachment: %#v", attachments[0])) } if message["conversation_id"] != "project-room@conference.imopenfire1-lanzhou|sender@imopenfire1-lanzhou" { fail("fixture/assert", fmt.Errorf("conversation_id = %#v", message["conversation_id"])) } contactCount := verifySearchContacts(ctx, session, "sender", "fixture/call isphere_search_contacts", 1) groupCount := verifySearchGroups(ctx, session, "project", "fixture/call isphere_search_groups", 1) fileCount := verifyReceiveFiles(ctx, session, "report", "fixture/call isphere_receive_files", 1, "msg-fixture-1:redacted-report.docx", "redacted-report.docx") return len(messagesValue), contactCount, groupCount, fileCount } func verifyConfiguredDirectorySource(ctx context.Context) (int, int, int, int) { plaintext := "--------------------------------------------------------------------------------------------------------------------------------------------\n" + "2026/7/7 15:30:07\n" + "\n" + " redacted-dir-report.docx\n" + " \n" + " FILE_TRANSFER_CANCEL\n" + " \n" + " \n" + "" line, err := encryptPacketLogLineForHarness(plaintext) if err != nil { fail("dir_fixture/encrypt", err) } dir, err := os.MkdirTemp("", "isphere-packet-log-dir-*") if err != nil { fail("dir_fixture/create", err) } defer os.RemoveAll(dir) if err := os.WriteFile(filepath.Join(dir, "b-ignored.tmp"), []byte("ignored\n"), 0o600); err != nil { fail("dir_fixture/write_ignored", err) } if err := os.WriteFile(filepath.Join(dir, "a-packet-reader.log"), []byte("\n"+line+"\n"), 0o600); err != nil { fail("dir_fixture/write", err) } oldFileValue, hadOldFileValue := os.LookupEnv("ISPHERE_PACKET_LOG_FILE") oldDirValue, hadOldDirValue := os.LookupEnv("ISPHERE_PACKET_LOG_DIR") _ = os.Unsetenv("ISPHERE_PACKET_LOG_FILE") if err := os.Setenv("ISPHERE_PACKET_LOG_DIR", dir); err != nil { fail("dir_fixture/env", err) } defer func() { if hadOldFileValue { _ = os.Setenv("ISPHERE_PACKET_LOG_FILE", oldFileValue) } else { _ = os.Unsetenv("ISPHERE_PACKET_LOG_FILE") } if hadOldDirValue { _ = os.Setenv("ISPHERE_PACKET_LOG_DIR", oldDirValue) } else { _ = os.Unsetenv("ISPHERE_PACKET_LOG_DIR") } }() serverCtx, cancel := context.WithCancel(ctx) defer cancel() serverTransport, clientTransport := mcp.NewInMemoryTransports() server, err := mcpserver.NewServerFromEnv() if err != nil { fail("dir_fixture/server_config", err) } serverErr := make(chan error, 1) go func() { serverErr <- server.Run(serverCtx, serverTransport) }() client := mcp.NewClient(&mcp.Implementation{Name: "verify-go-mcp-dir-fixture", Version: "0.1.0"}, nil) session, err := client.Connect(serverCtx, clientTransport, nil) if err != nil { cancel() fail("dir_fixture/connect", err) } defer func() { _ = session.Close() cancel() select { case <-serverErr: case <-time.After(2 * time.Second): fail("dir_fixture/server_shutdown", fmt.Errorf("server did not stop")) } }() callResult, err := session.CallTool(serverCtx, &mcp.CallToolParams{Name: "isphere_receive_messages", Arguments: map[string]any{ "conversation_id": "project-room@conference.imopenfire1-lanzhou|sender@imopenfire1-lanzhou", "query": "dir-report", "since": "2026-07-07T11:30:00Z", "source_preference": "auto", "preview": true, "cursor": "", "include_attachment_metadata": true, "limit": 5, }}) if err != nil { fail("dir_fixture/call isphere_receive_messages", err) } if callResult.IsError { fail("dir_fixture/call isphere_receive_messages", fmt.Errorf("tool returned isError=true: %#v", callResult.Content)) } var receivePayload map[string]any receiveEncoded, _ := json.Marshal(callResult.StructuredContent) if err := json.Unmarshal(receiveEncoded, &receivePayload); err != nil { fail("dir_fixture/decode", fmt.Errorf("decode structuredContent %s: %w", receiveEncoded, err)) } messagesValue, ok := receivePayload["messages"].([]any) if !ok || len(messagesValue) != 1 { fail("dir_fixture/assert", fmt.Errorf("messages = %#v, want one", receivePayload["messages"])) } message, ok := messagesValue[0].(map[string]any) if !ok { fail("dir_fixture/assert", fmt.Errorf("message was not object: %#v", messagesValue[0])) } if message["id"] != "msg-dir-fixture-1" || message["text"] != "redacted-dir-report.docx" || message["subject"] != "FILE_TRANSFER_CANCEL" { fail("dir_fixture/assert", fmt.Errorf("unexpected message: %#v", message)) } if message["message_id"] != "msg-dir-fixture-1" || message["content_text"] != "redacted-dir-report.docx" || message["content_type"] != "file" || message["source"] != "local_readonly" || message["raw_ref"] != "packetlog_message" { fail("dir_fixture/assert_contract", fmt.Errorf("unexpected contract message fields: %#v", message)) } attachments, ok := message["attachments"].([]any) if !ok || len(attachments) != 1 { fail("dir_fixture/assert_contract", fmt.Errorf("attachments = %#v, want one", message["attachments"])) } attachment, ok := attachments[0].(map[string]any) if !ok || attachment["file_id"] != "msg-dir-fixture-1:redacted-dir-report.docx" || attachment["file_name"] != "redacted-dir-report.docx" || attachment["download_ref"] != nil { fail("dir_fixture/assert_contract", fmt.Errorf("unexpected attachment: %#v", attachments[0])) } contactCount := verifySearchContacts(ctx, session, "sender", "dir_fixture/call isphere_search_contacts", 1) groupCount := verifySearchGroups(ctx, session, "project", "dir_fixture/call isphere_search_groups", 1) fileCount := verifyReceiveFiles(ctx, session, "dir-report", "dir_fixture/call isphere_receive_files", 1, "msg-dir-fixture-1:redacted-dir-report.docx", "redacted-dir-report.docx") return len(messagesValue), contactCount, groupCount, fileCount } func verifySearchContacts(ctx context.Context, session *mcp.ClientSession, query string, step string, wantCount int) int { callResult, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "isphere_search_contacts", Arguments: map[string]any{ "query": query, "source_preference": "local_readonly", "include_inactive": true, "cursor": "", "limit": 5, }}) if err != nil { fail(step, err) } if callResult.IsError { fail(step, fmt.Errorf("tool returned isError=true: %#v", callResult.Content)) } var contactsPayload map[string]any encoded, _ := json.Marshal(callResult.StructuredContent) if err := json.Unmarshal(encoded, &contactsPayload); err != nil { fail(step, fmt.Errorf("decode structuredContent %s: %w", encoded, err)) } if contactsPayload["ok"] != true { fail(step, fmt.Errorf("ok = %#v, want true", contactsPayload["ok"])) } contactsValue, ok := contactsPayload["contacts"].([]any) if !ok { fail(step, fmt.Errorf("contacts missing or not array: %s", encoded)) } if len(contactsValue) != wantCount { fail(step, fmt.Errorf("contacts count = %d, want %d: %s", len(contactsValue), wantCount, encoded)) } if wantCount > 0 { contact, ok := contactsValue[0].(map[string]any) if !ok { fail(step, fmt.Errorf("contact was not object: %#v", contactsValue[0])) } if contact["contact_id"] != "sender@imopenfire1-lanzhou" || contact["source"] != "local_readonly" { fail(step, fmt.Errorf("unexpected contact: %#v", contact)) } } return len(contactsValue) } func verifySearchGroups(ctx context.Context, session *mcp.ClientSession, query string, step string, wantCount int) int { callResult, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "isphere_search_groups", Arguments: map[string]any{ "query": query, "source_preference": "local_readonly", "include_archived": true, "cursor": "", "limit": 5, }}) if err != nil { fail(step, err) } if callResult.IsError { fail(step, fmt.Errorf("tool returned isError=true: %#v", callResult.Content)) } var groupsPayload map[string]any encoded, _ := json.Marshal(callResult.StructuredContent) if err := json.Unmarshal(encoded, &groupsPayload); err != nil { fail(step, fmt.Errorf("decode structuredContent %s: %w", encoded, err)) } if groupsPayload["ok"] != true { fail(step, fmt.Errorf("ok = %#v, want true", groupsPayload["ok"])) } groupsValue, ok := groupsPayload["groups"].([]any) if !ok { fail(step, fmt.Errorf("groups missing or not array: %s", encoded)) } if len(groupsValue) != wantCount { fail(step, fmt.Errorf("groups count = %d, want %d: %s", len(groupsValue), wantCount, encoded)) } if wantCount > 0 { group, ok := groupsValue[0].(map[string]any) if !ok { fail(step, fmt.Errorf("group was not object: %#v", groupsValue[0])) } if group["group_id"] != "project-room@conference.imopenfire1-lanzhou" || group["source"] != "local_readonly" { fail(step, fmt.Errorf("unexpected group: %#v", group)) } } return len(groupsValue) } func verifyReceiveFiles(ctx context.Context, session *mcp.ClientSession, nameContains string, step string, wantCount int, wantFileID string, wantFileName string) int { callResult, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "isphere_receive_files", Arguments: map[string]any{ "mode": "list", "name_contains": nameContains, "source_preference": "local_readonly", "preview": true, "cursor": "", "output_dir": "", "limit": 5, }}) if err != nil { fail(step, err) } if callResult.IsError { fail(step, fmt.Errorf("tool returned isError=true: %#v", callResult.Content)) } var filesPayload map[string]any encoded, _ := json.Marshal(callResult.StructuredContent) if err := json.Unmarshal(encoded, &filesPayload); err != nil { fail(step, fmt.Errorf("decode structuredContent %s: %w", encoded, err)) } if filesPayload["ok"] != true || filesPayload["mode"] != "list" { fail(step, fmt.Errorf("ok/mode mismatch: %s", encoded)) } filesValue, ok := filesPayload["files"].([]any) if !ok { fail(step, fmt.Errorf("files missing or not array: %s", encoded)) } if len(filesValue) != wantCount { fail(step, fmt.Errorf("files count = %d, want %d: %s", len(filesValue), wantCount, encoded)) } if wantCount > 0 { file, ok := filesValue[0].(map[string]any) if !ok { fail(step, fmt.Errorf("file was not object: %#v", filesValue[0])) } if file["file_id"] != wantFileID || file["file_name"] != wantFileName || file["source"] != "local_readonly" { fail(step, fmt.Errorf("unexpected file: %#v", file)) } if file["download_ref"] != nil || file["saved_path"] != nil || file["sha256"] != nil { fail(step, fmt.Errorf("download/cache fields should be nil: %#v", file)) } } return len(filesValue) } func encryptPacketLogLineForHarness(plaintext string) (string, error) { block, err := des.NewCipher([]byte("hyhccdtm")) if err != nil { return "", err } plain := padPKCS7ForHarness([]byte(plaintext), des.BlockSize) ciphertext := make([]byte, len(plain)) iv := []byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF} cipher.NewCBCEncrypter(block, iv).CryptBlocks(ciphertext, plain) return base64.StdEncoding.EncodeToString(ciphertext), nil } func padPKCS7ForHarness(data []byte, blockSize int) []byte { pad := blockSize - len(data)%blockSize out := append([]byte(nil), data...) for i := 0; i < pad; i++ { out = append(out, byte(pad)) } return out } func fail(step string, err error) { payload, _ := json.Marshal(map[string]any{"ok": false, "step": step, "error": err.Error()}) fmt.Fprintln(os.Stderr, string(payload)) os.Exit(1) } "@ | Set-Content -LiteralPath $harnessMain -Encoding UTF8 } $harnessJson = $null Invoke-Step "run MCP SDK harness initialize/tools/list/tools/call" { Push-Location $harnessDir try { $output = & go run . if ($LASTEXITCODE -ne 0) { throw "go run SDK harness failed with exit code $LASTEXITCODE" } $output | ForEach-Object { Write-Host $_ } $lastLine = @($output | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) | Select-Object -Last 1 $script:harnessJson = $lastLine | ConvertFrom-Json } finally { Pop-Location } } Assert-True ($script:harnessJson.ok -eq $true) "SDK harness did not return ok=true" Assert-True ($script:harnessJson.helper_name -eq "ISphereWinHelper") "SDK harness helper_name mismatch: $($script:harnessJson.helper_name)" Assert-True ($script:harnessJson.tool_count -eq 8) "SDK harness tool_count mismatch: $($script:harnessJson.tool_count)" Assert-True ($script:harnessJson.receive_message_count -eq 0) "SDK harness receive_message_count mismatch: $($script:harnessJson.receive_message_count)" Assert-True ($script:harnessJson.contact_count -eq 0) "SDK harness contact_count mismatch: $($script:harnessJson.contact_count)" Assert-True ($script:harnessJson.group_count -eq 0) "SDK harness group_count mismatch: $($script:harnessJson.group_count)" Assert-True ($script:harnessJson.file_count -eq 0) "SDK harness file_count mismatch: $($script:harnessJson.file_count)" Assert-True ($script:harnessJson.configured_receive_message_count -eq 1) "SDK harness configured_receive_message_count mismatch: $($script:harnessJson.configured_receive_message_count)" Assert-True ($script:harnessJson.configured_contact_count -eq 1) "SDK harness configured_contact_count mismatch: $($script:harnessJson.configured_contact_count)" Assert-True ($script:harnessJson.configured_group_count -eq 1) "SDK harness configured_group_count mismatch: $($script:harnessJson.configured_group_count)" Assert-True ($script:harnessJson.configured_file_count -eq 1) "SDK harness configured_file_count mismatch: $($script:harnessJson.configured_file_count)" Assert-True ($script:harnessJson.configured_dir_receive_message_count -eq 1) "SDK harness configured_dir_receive_message_count mismatch: $($script:harnessJson.configured_dir_receive_message_count)" Assert-True ($script:harnessJson.configured_dir_contact_count -eq 1) "SDK harness configured_dir_contact_count mismatch: $($script:harnessJson.configured_dir_contact_count)" Assert-True ($script:harnessJson.configured_dir_group_count -eq 1) "SDK harness configured_dir_group_count mismatch: $($script:harnessJson.configured_dir_group_count)" Assert-True ($script:harnessJson.configured_dir_file_count -eq 1) "SDK harness configured_dir_file_count mismatch: $($script:harnessJson.configured_dir_file_count)" [ordered]@{ ok = $true verification = "go_mcp_sdk_harness" go_mcp_binary_built = (Test-Path -LiteralPath $goMcpExe) helper_name = $script:harnessJson.helper_name tool_count = $script:harnessJson.tool_count tools = $script:harnessJson.tools receive_message_count = $script:harnessJson.receive_message_count contact_count = $script:harnessJson.contact_count group_count = $script:harnessJson.group_count file_count = $script:harnessJson.file_count configured_receive_message_count = $script:harnessJson.configured_receive_message_count configured_contact_count = $script:harnessJson.configured_contact_count configured_group_count = $script:harnessJson.configured_group_count configured_file_count = $script:harnessJson.configured_file_count configured_dir_receive_message_count = $script:harnessJson.configured_dir_receive_message_count configured_dir_contact_count = $script:harnessJson.configured_dir_contact_count configured_dir_group_count = $script:harnessJson.configured_dir_group_count configured_dir_file_count = $script:harnessJson.configured_dir_file_count packet_log_file_configured = $script:harnessJson.packet_log_file_configured packet_log_dir_configured = $script:harnessJson.packet_log_dir_configured python_runtime_required = $false real_isphere_login_required = $false action_tools_present = $false } | ConvertTo-Json -Depth 8 -Compress } finally { if (Test-Path -LiteralPath $tempRoot) { Remove-Item -LiteralPath $tempRoot -Recurse -Force } if (Test-Path -LiteralPath $harnessDir) { Remove-Item -LiteralPath $harnessDir -Recurse -Force } }