Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions internal/mcpserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ const mcpAppMIMEType = "text/html;profile=mcp-app"
//go:embed widget/app.html
var widgetHTML string

//go:embed widget/app.js
var widgetJS string

// serverInstructions is surfaced to connected MCP hosts to guide tool use.
const serverInstructions = `SaltyBytes finds REAL recipes from around the web and manages the user's saved recipe collection.
Typical flow: search_recipes to find candidates -> preview_recipe on the chosen result -> save_recipe when the user wants to keep it.
Expand All @@ -33,9 +30,6 @@ Every tool renders an interactive widget in the conversation; prefer letting the
// CSP blocks an external image.
func widgetResourceMeta(cfg *config.Config) mcp.Meta {
resourceDomains := []string{
// The widget's own JavaScript is served from the API origin (ChatGPT's
// sandbox blocks inline scripts, so app.html loads it via <script src>).
strings.TrimRight(cfg.EnvVars.PublicBaseURL, "/"),
fmt.Sprintf("https://%s.s3.amazonaws.com", cfg.EnvVars.S3Bucket),
fmt.Sprintf("https://%s.s3.%s.amazonaws.com", cfg.EnvVars.S3Bucket, cfg.EnvVars.AWSRegion),
}
Expand Down Expand Up @@ -118,16 +112,3 @@ func NewHandler(cfg *config.Config, deps *Deps) http.Handler {
ResourceMetadataURL: deps.OAuth.Issuer() + "/.well-known/oauth-protected-resource/mcp",
})(mcpHandler)
}

// WidgetScript serves the widget's JavaScript as a standalone, public asset.
// ChatGPT's widget sandbox does not execute inline scripts, so the widget HTML
// loads this via <script src> from the API origin (which is allow-listed in the
// widget CSP's resourceDomains). No auth; short cache.
func WidgetScript() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
w.Header().Set("Cache-Control", "public, max-age=300")
w.Header().Set("X-Content-Type-Options", "nosniff")
_, _ = w.Write([]byte(widgetJS))
}
}
11 changes: 3 additions & 8 deletions internal/mcpserver/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,14 +219,9 @@ func TestServerLifecycle_ToolsAndWidget(t *testing.T) {
if len(res.Contents) != 1 || res.Contents[0].MIMEType != mcpAppMIMEType {
t.Fatalf("unexpected widget resource: %+v", res.Contents)
}
// The widget loads its JS externally (ChatGPT's sandbox blocks inline
// scripts), so the HTML references the script and the markers live in the JS.
if !strings.Contains(res.Contents[0].Text, "mcp-widget.js") {
t.Fatal("widget HTML should load its JS via <script src> (mcp-widget.js)")
}
for _, marker := range []string{"ui/initialize", "ui/notifications/tool-result", "tools/call", "window.openai"} {
if !strings.Contains(widgetJS, marker) {
t.Fatalf("widget JS missing protocol marker %q", marker)
for _, marker := range []string{"ui/initialize", "ui/notifications/tool-result", "tools/call"} {
if !strings.Contains(res.Contents[0].Text, marker) {
t.Fatalf("widget HTML missing protocol marker %q", marker)
}
}

Expand Down
Loading
Loading