feat: add go helper client and mcp tools
This commit is contained in:
122
internal/tools/winhelper.go
Normal file
122
internal/tools/winhelper.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
|
||||
"isphere-ai-bridge/internal/helperclient"
|
||||
)
|
||||
|
||||
const (
|
||||
ToolNameVersion = "win_helper_version"
|
||||
ToolNameSelfCheck = "win_helper_self_check"
|
||||
ToolNameScanWindows = "win_helper_scan_windows"
|
||||
ToolNameDumpUIA = "win_helper_dump_uia"
|
||||
)
|
||||
|
||||
type HelperCaller interface {
|
||||
Call(ctx context.Context, op string, args map[string]any) (*helperclient.HelperResponse, error)
|
||||
}
|
||||
|
||||
type VersionArgs struct{}
|
||||
|
||||
type SelfCheckArgs struct{}
|
||||
|
||||
type ScanWindowsArgs struct {
|
||||
IncludeAllVisible bool `json:"include_all_visible,omitempty" jsonschema:"include all visible desktop windows, not only likely iSphere candidates"`
|
||||
}
|
||||
|
||||
type DumpUIAArgs struct {
|
||||
Hwnd string `json:"hwnd,omitempty" jsonschema:"window handle to dump, for example 0x001A0B2C"`
|
||||
MaxDepth int `json:"max_depth,omitempty" jsonschema:"maximum UI Automation tree depth"`
|
||||
IncludeText bool `json:"include_text,omitempty" jsonschema:"include text values when available"`
|
||||
MaxChildren int `json:"max_children,omitempty" jsonschema:"maximum children per node"`
|
||||
}
|
||||
|
||||
func RegisterWinHelperTools(server *mcp.Server, caller HelperCaller) {
|
||||
if caller == nil {
|
||||
caller = helperclient.Client{}
|
||||
}
|
||||
|
||||
mcp.AddTool[VersionArgs, map[string]any](server, &mcp.Tool{
|
||||
Name: ToolNameVersion,
|
||||
Description: "Return ISphereWinHelper version and protocol metadata.",
|
||||
}, func(ctx context.Context, _ *mcp.CallToolRequest, _ VersionArgs) (*mcp.CallToolResult, map[string]any, error) {
|
||||
return callHelper(ctx, caller, "version", map[string]any{})
|
||||
})
|
||||
|
||||
mcp.AddTool[SelfCheckArgs, map[string]any](server, &mcp.Tool{
|
||||
Name: ToolNameSelfCheck,
|
||||
Description: "Run ISphereWinHelper read-only desktop and UI Automation availability checks.",
|
||||
}, func(ctx context.Context, _ *mcp.CallToolRequest, _ SelfCheckArgs) (*mcp.CallToolResult, map[string]any, error) {
|
||||
return callHelper(ctx, caller, "self_check", map[string]any{})
|
||||
})
|
||||
|
||||
mcp.AddTool[ScanWindowsArgs, map[string]any](server, &mcp.Tool{
|
||||
Name: ToolNameScanWindows,
|
||||
Description: "List visible desktop windows or likely iSphere window candidates in read-only mode.",
|
||||
}, func(ctx context.Context, _ *mcp.CallToolRequest, input ScanWindowsArgs) (*mcp.CallToolResult, map[string]any, error) {
|
||||
return callHelper(ctx, caller, "scan_windows", map[string]any{
|
||||
"include_all_visible": input.IncludeAllVisible,
|
||||
})
|
||||
})
|
||||
|
||||
mcp.AddTool[DumpUIAArgs, map[string]any](server, &mcp.Tool{
|
||||
Name: ToolNameDumpUIA,
|
||||
Description: "Dump a read-only UI Automation tree for a specified window handle.",
|
||||
}, func(ctx context.Context, _ *mcp.CallToolRequest, input DumpUIAArgs) (*mcp.CallToolResult, map[string]any, error) {
|
||||
return callHelper(ctx, caller, "dump_uia", map[string]any{
|
||||
"hwnd": input.Hwnd,
|
||||
"max_depth": float64(input.MaxDepth),
|
||||
"include_text": input.IncludeText,
|
||||
"max_children": float64(input.MaxChildren),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func callHelper(ctx context.Context, caller HelperCaller, op string, args map[string]any) (*mcp.CallToolResult, map[string]any, error) {
|
||||
response, err := caller.Call(ctx, op, args)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return nil, helperResponseToMap(response), nil
|
||||
}
|
||||
|
||||
func helperResponseToMap(response *helperclient.HelperResponse) map[string]any {
|
||||
if response == nil {
|
||||
return map[string]any{"ok": false}
|
||||
}
|
||||
|
||||
out := map[string]any{
|
||||
"protocol": response.Protocol,
|
||||
"request_id": response.RequestID,
|
||||
"op": response.Op,
|
||||
"ok": response.OK,
|
||||
"data": decodeRawData(response.Data),
|
||||
"warnings": response.Warnings,
|
||||
}
|
||||
if response.Error != nil {
|
||||
out["error"] = map[string]any{
|
||||
"code": response.Error.Code,
|
||||
"message": response.Error.Message,
|
||||
}
|
||||
} else {
|
||||
out["error"] = nil
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func decodeRawData(data map[string]json.RawMessage) map[string]any {
|
||||
decoded := map[string]any{}
|
||||
for key, raw := range data {
|
||||
var value any
|
||||
if err := json.Unmarshal(raw, &value); err != nil {
|
||||
decoded[key] = string(raw)
|
||||
continue
|
||||
}
|
||||
decoded[key] = value
|
||||
}
|
||||
return decoded
|
||||
}
|
||||
Reference in New Issue
Block a user