dist/codex/nlweb-protocol/skills/nlweb-chatgpt-appsdk/SKILL.md
Integrate NLWeb with ChatGPT's Apps SDK — the Node.js MCP server in `openai-apps-sdk-integration/`, the `nlweb-list` tool, the React widget at `ui://widget/nlweb-list.html`, and the port-8100 AppSDK adapter that translates NLWeb's message list to OpenAI Apps SDK envelopes. Use when publishing an NLWeb site as a ChatGPT app or wiring NLWeb results into an Apps SDK widget.
npx skillsauth add orcaqubits/agentic-commerce-claude-plugins nlweb-chatgpt-appsdkInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fetch live docs:
openai-apps-sdk-integration/nlweb_server_node/ in the live repo — this is a separate Node.js/TypeScript MCP server.nlweb-list.html expects from the tool result envelope.NLWeb's native /mcp returns generic MCP tool results — a list of Schema.org objects. ChatGPT's Apps SDK expects a richer envelope that pairs tool output with a UI resource (widget). The integration ships two extra pieces on top of the base NLWeb server:
openai-apps-sdk-integration/nlweb_server_node/ — a Node.js/TypeScript MCP server that registers an nlweb-list tool and serves a React widget at ui://widget/nlweb-list.html. ChatGPT discovers this and renders the widget when the tool returns results.
AppSDK Adapter (port 8100, appsdk_adapter_server.py) — an aiohttp server that takes legacy NLWeb message-list responses and re-shapes them into the OpenAI Apps SDK envelope on the fly. Useful if you already have NLWeb running and want to plug ChatGPT in without rewriting clients.
Native MCP clients (Claude, Gemini) ignore both — they hit the canonical /mcp on port 8000.
| Path | Use When | |------|----------| | Node.js MCP server with widget | You want the full ChatGPT app experience with the React widget | | Port-8100 AppSDK adapter | You only want translation; you don't need a widget |
For a new build, use path 1. For retrofitting an existing NLWeb deployment, path 2 is lower-risk.
nlweb-list ToolThe Node.js server registers a single MCP tool, nlweb-list, with a schema like:
{
"name": "nlweb-list",
"description": "Query the site and render a list of results as a card carousel.",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"site": { "type": "string" }
},
"required": ["query"]
}
}
(Verify exact schema in the live repo.)
The tool internally calls the NLWeb /ask endpoint and returns:
ui://widget/nlweb-list.htmlChatGPT's Apps SDK runtime renders the widget, passes the tool output into it via the SDK's window.openai-style API (or its successor — Apps SDK is rapidly evolving).
The widget lives in the Node.js project and is bundled into a single HTML file served at ui://widget/nlweb-list.html. It renders the Schema.org results as cards (image, name, description, score). Typical features:
webserver/appsdk_adapter_server.py runs as a separate process alongside the main NLWeb server. It:
/ask to the main server on port 8000Existing clients still hit :8000/ask; only ChatGPT clients hit :8100/ask. They share the same backend data and config.
The Node.js MCP server uses the same protocol version (2024-11-05) as NLWeb's Python /mcp. Pin to this; ChatGPT updates its Apps SDK runtime frequently — re-test on every OpenAI release.
Publishing as a public ChatGPT app requires going through OpenAI's review process. The dev experience:
OpenAI's policies change — always check the current submission docs.
cd openai-apps-sdk-integration/nlweb_server_node
npm install
npm run build
npm start
(Verify exact commands against the live package.json.)
Configure the underlying NLWeb URL via env var (typically NLWEB_ASK_URL=http://localhost:8000/ask).
Then add the MCP server to ChatGPT's developer mode pointing at the Node.js server's URL.
# Main NLWeb on :8000
python AskAgent/python/app-aiohttp.py
# AppSDK adapter on :8100
python AskAgent/python/webserver/appsdk_adapter_server.py
Point ChatGPT at the :8100 endpoint instead of :8000.
If you fork the React widget:
ChatGPT picks tools based on the description. For an NLWeb-backed app, write the description to be specific about the domain (e.g., "Search recipes from Joy of Cooking" not "Generic NL search"). Site operators frequently fork the Node.js server to customize this.
A single NLWeb deployment can serve both audiences:
/mcpBoth share the same retrieval and ranking. No data duplication.
mode=generate. Use path 1 for streaming-heavy use cases.window.openai-style runtime frequently. Pin a tested SDK version in the React build.Always re-fetch nlweb-chatgpt-integration.md and the Node.js project's README.md before customizing — this is one of the fastest-moving parts of NLWeb.
development
Build with Spree's headless Next.js storefront — the official `spree/storefront` repo (Next.js 16 App Router with Server Actions and Turbopack, React 19 Server Components, Tailwind CSS 4, TypeScript 5, `@spree/sdk`, Sentry), server-only auth (httpOnly JWT cookies + publishable key), MeiliSearch faceted catalog, one-page checkout with Apple/Google Pay/Klarna/Affirm/SEPA, multi-region market routing, GA4 + JSON-LD SEO, and Vercel/Docker deployment. Use when forking or customizing the storefront, or evaluating headless adoption.
tools
Build Spree extensions as Rails engines — gem scaffolding, `bin/rails g spree:extension`, mounting routes/migrations/assets, the modern `prepend` decorator pattern (`*_decorator.rb` with `self.prepended(base)`), generators (`spree:model_decorator`, `spree:controller_decorator`), the four customization surfaces in preference order (Events > Webhooks > Dependencies > Decorators), Spree::Dependencies for swapping service objects, gem release/versioning, and the deprecated Deface engine. Use when building a reusable Spree extension or adding non-trivial customization to an app.
development
Build with Spree's event bus and Webhooks 2.0 — `Spree::Events` publication, `Spree::Subscriber` DSL with `subscribes_to` and `on`, wildcard matching, lifecycle events (`{model}.created/.updated/.deleted` via `publishes_lifecycle_events`), the canonical event catalog (order.*, payment.*, shipment.*, product.*), Webhooks 2.0 endpoints, HMAC-SHA256 signing (`X-Spree-Webhook-Signature`), exponential-backoff retries, and Sidekiq job orchestration. Use when wiring event-driven business logic, building webhook consumers, or replacing ActiveSupport callback chains.
tools
Cross-cutting Spree development patterns — the customization preference hierarchy (Events > Webhooks > Dependencies > Decorators), `Spree::Dependencies` service-object swapping, the `_decorator.rb` + `prepend` + `self.prepended` idiom, idempotent subscribers and webhook receivers, multi-store scoping discipline, prefixed IDs, calculator polymorphism (shipping/promotion/tax share the base), service-object composition with `dry-monads` or simple results, why to avoid `class_eval` reopening and Deface, and Spree-on-Rails idioms (Hotwire/Turbo Stimulus, ActiveStorage, Action Cable, Sidekiq). Use when designing the architecture of a Spree extension or solving cross-cutting concerns.