From 0d6b77ae4c31a9a7e7e8a225006265e4dfe90212 Mon Sep 17 00:00:00 2001 From: Snider Date: Wed, 15 Apr 2026 19:02:31 +0100 Subject: [PATCH] Add MCP core route resolution tool --- pkg/mcp/mcp_test.go | 27 +++++++++++++++++++ pkg/mcp/subsystem.go | 1 + pkg/mcp/tools_display.go | 58 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 pkg/mcp/tools_display.go diff --git a/pkg/mcp/mcp_test.go b/pkg/mcp/mcp_test.go index 86326dba..5c885acd 100644 --- a/pkg/mcp/mcp_test.go +++ b/pkg/mcp/mcp_test.go @@ -141,6 +141,33 @@ func TestSubsystem_Good_CallTool_BrowserOpenFile(t *testing.T) { assert.Equal(t, "/tmp/readme.txt", browserPlatform.lastPath) } +func TestSubsystem_Good_CallTool_SchemeResolve(t *testing.T) { + c := core.New( + core.WithServiceLock(), + ) + c.Action("display.resolveScheme", func(_ context.Context, opts core.Options) core.Result { + return core.Result{ + Value: map[string]any{ + "content_type": "text/html", + "body": "core://store", + "route": "store", + "url": opts.String("url"), + }, + OK: true, + } + }) + require.True(t, c.ServiceStartup(context.Background(), nil).OK) + + sub := New(c) + server := mcp.NewServer(&mcp.Implementation{Name: "test", Version: "0.1.0"}, nil) + sub.RegisterTools(server) + + result, err := sub.CallTool(context.Background(), "scheme_resolve", map[string]any{"url": "core://store?q=theme"}) + require.NoError(t, err) + assert.Contains(t, result, "core://store") + assert.Contains(t, result, "\"route\":\"store\"") +} + type aliasDialogPlatform struct { last dialog.MessageDialogOptions } diff --git a/pkg/mcp/subsystem.go b/pkg/mcp/subsystem.go index e1504f23..61fd20ed 100644 --- a/pkg/mcp/subsystem.go +++ b/pkg/mcp/subsystem.go @@ -44,6 +44,7 @@ func New(c *core.Core) *Subsystem { func (s *Subsystem) Name() string { return "display" } func (s *Subsystem) RegisterTools(server *mcp.Server) { + s.registerDisplayTools(server) s.registerWebviewTools(server) s.registerWindowTools(server) s.registerLayoutTools(server) diff --git a/pkg/mcp/tools_display.go b/pkg/mcp/tools_display.go new file mode 100644 index 00000000..44211162 --- /dev/null +++ b/pkg/mcp/tools_display.go @@ -0,0 +1,58 @@ +// pkg/mcp/tools_display.go +package mcp + +import ( + "context" + + core "dappco.re/go/core" + coreerr "dappco.re/go/core/log" + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +// --- scheme_resolve --- + +type SchemeResolveInput struct { + URL string `json:"url"` +} + +type SchemeResolveOutput struct { + URL string `json:"url"` + Route string `json:"route"` + ContentType string `json:"content_type"` + Body string `json:"body"` +} + +func (s *Subsystem) schemeResolve(_ context.Context, _ *mcp.CallToolRequest, input SchemeResolveInput) (*mcp.CallToolResult, SchemeResolveOutput, error) { + result := s.core.Action("display.resolveScheme").Run(context.Background(), core.NewOptions( + core.Option{Key: "url", Value: input.URL}, + )) + if !result.OK { + if err, ok := result.Value.(error); ok { + return nil, SchemeResolveOutput{}, err + } + return nil, SchemeResolveOutput{}, coreerr.E("mcp.schemeResolve", "display.resolveScheme failed", nil) + } + + payload, ok := result.Value.(map[string]any) + if !ok { + return nil, SchemeResolveOutput{}, coreerr.E("mcp.schemeResolve", "unexpected result type", nil) + } + + output := SchemeResolveOutput{ + URL: stringValue(payload, "url"), + Route: stringValue(payload, "route"), + ContentType: stringValue(payload, "content_type"), + Body: stringValue(payload, "body"), + } + if output.URL == "" { + output.URL = input.URL + } + return nil, output, nil +} + +func (s *Subsystem) registerDisplayTools(server *mcp.Server) { + addTool(s, server, &mcp.Tool{ + Name: "scheme_resolve", + Description: `Resolve a core:// route or page URL through the display service. Example: {"url":"core://store?q=theme"}`, + }, s.schemeResolve) +}