[{"data":1,"prerenderedAt":1755},["ShallowReactive",2],{"navigation_docs":3,"-advanced-mcp-apps-internals":287,"-advanced-mcp-apps-internals-surround":1750},[4,40,70,99,122,156,189,258],{"title":5,"path":6,"stem":7,"children":8,"page":39},"Getting Started","\u002Fgetting-started","1.getting-started",[9,14,19,24,29,34],{"title":10,"path":11,"stem":12,"icon":13},"Introduction","\u002Fgetting-started\u002Fintroduction","1.getting-started\u002F1.introduction","i-lucide-book-open",{"title":15,"path":16,"stem":17,"icon":18},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation","i-lucide-download",{"title":20,"path":21,"stem":22,"icon":23},"Configuration","\u002Fgetting-started\u002Fconfiguration","1.getting-started\u002F3.configuration","i-lucide-settings",{"title":25,"path":26,"stem":27,"icon":28},"Inspector","\u002Fgetting-started\u002Finspector","1.getting-started\u002F4.inspector","i-lucide-circuit-board",{"title":30,"path":31,"stem":32,"icon":33},"Connection","\u002Fgetting-started\u002Fconnection","1.getting-started\u002F5.connection","i-lucide-plug",{"title":35,"path":36,"stem":37,"icon":38},"Agent Skills","\u002Fgetting-started\u002Fagent-skills","1.getting-started\u002F6.agent-skills","i-lucide-sparkles",false,{"title":41,"path":42,"stem":43,"children":44,"page":39},"Tools","\u002Ftools","2.tools",[45,50,55,60,65],{"title":46,"path":47,"stem":48,"icon":49},"Overview","\u002Ftools\u002Foverview","2.tools\u002F0.overview","i-lucide-wrench",{"title":51,"path":52,"stem":53,"icon":54},"Schema, handler & returns","\u002Ftools\u002Fschema-handler","2.tools\u002F1.schema-handler","i-lucide-braces",{"title":56,"path":57,"stem":58,"icon":59},"Annotations & input examples","\u002Ftools\u002Fannotations","2.tools\u002F2.annotations","i-lucide-badge-info",{"title":61,"path":62,"stem":63,"icon":64},"Errors & caching","\u002Ftools\u002Ferrors-caching","2.tools\u002F3.errors-caching","i-lucide-shield",{"title":66,"path":67,"stem":68,"icon":69},"Groups, files & dynamic registration","\u002Ftools\u002Fgroups-organization","2.tools\u002F4.groups-organization","i-lucide-tags",{"title":71,"path":72,"stem":73,"children":74,"page":39},"Resources","\u002Fresources","3.resources",[75,79,84,89,94],{"title":46,"path":76,"stem":77,"icon":78},"\u002Fresources\u002Foverview","3.resources\u002F0.overview","i-lucide-package",{"title":80,"path":81,"stem":82,"icon":83},"Static resources & structure","\u002Fresources\u002Fstatic-and-structure","3.resources\u002F1.static-and-structure","i-lucide-file-stack",{"title":85,"path":86,"stem":87,"icon":88},"Templates & handlers","\u002Fresources\u002Ftemplates-and-handlers","3.resources\u002F2.templates-and-handlers","i-lucide-git-branch",{"title":90,"path":91,"stem":92,"icon":93},"Metadata, content & errors","\u002Fresources\u002Fcontent-metadata-errors","3.resources\u002F3.content-metadata-errors","i-lucide-layers",{"title":95,"path":96,"stem":97,"icon":98},"Groups & organization","\u002Fresources\u002Forganization","3.resources\u002F4.organization","i-lucide-folder-tree",{"title":100,"path":101,"stem":102,"children":103,"page":39},"Prompts","\u002Fprompts","4.prompts",[104,108,113,117],{"title":46,"path":105,"stem":106,"icon":107},"\u002Fprompts\u002Foverview","4.prompts\u002F0.overview","i-lucide-message-square",{"title":109,"path":110,"stem":111,"icon":112},"Authoring & structure","\u002Fprompts\u002Fauthoring","4.prompts\u002F1.authoring","i-lucide-pen-line",{"title":114,"path":115,"stem":116,"icon":93},"Input, handler & messages","\u002Fprompts\u002Finput-handler-messages","4.prompts\u002F2.input-handler-messages",{"title":118,"path":119,"stem":120,"icon":121},"Patterns & advanced","\u002Fprompts\u002Fpatterns-advanced","4.prompts\u002F3.patterns-advanced","i-lucide-line-chart",{"title":123,"path":124,"stem":125,"children":126,"page":39},"Handlers","\u002Fhandlers","5.handlers",[127,131,136,141,146,151],{"title":46,"path":128,"stem":129,"icon":130},"\u002Fhandlers\u002Foverview","5.handlers\u002F0.overview","i-lucide-server",{"title":132,"path":133,"stem":134,"icon":135},"Default & custom handlers","\u002Fhandlers\u002Fdefault-and-custom","5.handlers\u002F1.default-and-custom","i-lucide-toggle-left",{"title":137,"path":138,"stem":139,"icon":140},"Structure & options","\u002Fhandlers\u002Fstructure-and-options","5.handlers\u002F2.structure-and-options","i-lucide-sliders-horizontal",{"title":142,"path":143,"stem":144,"icon":145},"Examples & routing","\u002Fhandlers\u002Fexamples-routing","5.handlers\u002F3.examples-routing","i-lucide-route",{"title":147,"path":148,"stem":149,"icon":150},"Sharing & practices","\u002Fhandlers\u002Fsharing-practices","5.handlers\u002F4.sharing-practices","i-lucide-share-2",{"title":152,"path":153,"stem":154,"icon":155},"Multi-handler organization","\u002Fhandlers\u002Forganization","5.handlers\u002F5.organization","i-lucide-network",{"title":157,"path":158,"stem":159,"children":160,"page":39},"Apps","\u002Fapps","6.apps",[161,165,170,175,179,184],{"title":46,"path":162,"stem":163,"icon":164},"\u002Fapps\u002Foverview","6.apps\u002F0.overview","i-lucide-app-window",{"title":166,"path":167,"stem":168,"icon":169},"Authoring & defineMcpApp","\u002Fapps\u002Fauthoring","6.apps\u002F1.authoring","i-lucide-code-2",{"title":171,"path":172,"stem":173,"icon":174},"useMcpApp() bridge","\u002Fapps\u002Fuse-mcp-app","6.apps\u002F2.use-mcp-app","i-lucide-message-circle",{"title":176,"path":177,"stem":178,"icon":64},"CSP & build pipeline","\u002Fapps\u002Fcsp-and-wiring","6.apps\u002F3.csp-and-wiring",{"title":180,"path":181,"stem":182,"icon":183},"Testing & publishing","\u002Fapps\u002Ftesting-publishing","6.apps\u002F4.testing-publishing","i-lucide-rocket",{"title":185,"path":186,"stem":187,"icon":188},"Patterns & limits","\u002Fapps\u002Fpatterns-reference","6.apps\u002F5.patterns-reference","i-lucide-book-marked",{"title":190,"path":191,"stem":192,"children":193,"page":39},"Advanced Topics","\u002Fadvanced","7.advanced",[194,199,204,209,214,219,223,228,233,238,243,248,253],{"title":195,"path":196,"stem":197,"icon":198},"Custom Paths","\u002Fadvanced\u002Fcustom-paths","7.advanced\u002F1.custom-paths","i-lucide-folder",{"title":200,"path":201,"stem":202,"icon":203},"Logging","\u002Fadvanced\u002Flogging","7.advanced\u002F10.logging","i-lucide-scroll-text",{"title":205,"path":206,"stem":207,"icon":208},"MCP Apps Internals","\u002Fadvanced\u002Fmcp-apps-internals","7.advanced\u002F11.mcp-apps-internals","i-lucide-cog",{"title":210,"path":211,"stem":212,"icon":213},"Listing Definitions","\u002Fadvanced\u002Flisting-definitions","7.advanced\u002F12.listing-definitions","i-lucide-list",{"title":215,"path":216,"stem":217,"icon":218},"llms.txt discovery","\u002Fadvanced\u002Fllms-txt","7.advanced\u002F13.llms-txt","i-lucide-file-text",{"title":220,"path":221,"stem":222,"icon":64},"Middleware","\u002Fadvanced\u002Fmiddleware","7.advanced\u002F2.middleware",{"title":224,"path":225,"stem":226,"icon":227},"TypeScript","\u002Fadvanced\u002Ftypescript","7.advanced\u002F3.typescript","i-lucide-type",{"title":229,"path":230,"stem":231,"icon":232},"Hooks","\u002Fadvanced\u002Fhooks","7.advanced\u002F4.hooks","i-lucide-webhook",{"title":234,"path":235,"stem":236,"icon":237},"MCP Evals","\u002Fadvanced\u002Fevals","7.advanced\u002F5.evals","i-lucide-flask-conical",{"title":239,"path":240,"stem":241,"icon":242},"Sessions","\u002Fadvanced\u002Fsessions","7.advanced\u002F6.sessions","i-lucide-database",{"title":244,"path":245,"stem":246,"icon":247},"Dynamic Definitions","\u002Fadvanced\u002Fdynamic-definitions","7.advanced\u002F7.dynamic-definitions","i-lucide-toggle-right",{"title":249,"path":250,"stem":251,"icon":252},"Code Mode","\u002Fadvanced\u002Fcode-mode","7.advanced\u002F8.code-mode","i-lucide-code",{"title":254,"path":255,"stem":256,"icon":257},"Elicitation","\u002Fadvanced\u002Felicitation","7.advanced\u002F9.elicitation","i-lucide-message-square-quote",{"title":259,"path":260,"stem":261,"children":262,"page":39},"Examples","\u002Fexamples","8.examples",[263,268,273,278,283],{"title":264,"path":265,"stem":266,"icon":267},"Authentication","\u002Fexamples\u002Fauthentication","8.examples\u002F1.authentication","i-lucide-shield-check",{"title":269,"path":270,"stem":271,"icon":272},"API Integration","\u002Fexamples\u002Fapi-integration","8.examples\u002F2.api-integration","i-lucide-globe",{"title":274,"path":275,"stem":276,"icon":277},"Common Patterns","\u002Fexamples\u002Fcommon-patterns","8.examples\u002F3.common-patterns","i-lucide-lightbulb",{"title":279,"path":280,"stem":281,"icon":282},"File Operations","\u002Fexamples\u002Ffile-operations","8.examples\u002F4.file-operations","i-lucide-file",{"title":284,"path":285,"stem":286,"icon":107},"Prompt Examples","\u002Fexamples\u002Fprompt-examples","8.examples\u002F5.prompt-examples",{"id":288,"title":205,"body":289,"description":1741,"extension":1742,"links":1743,"meta":1744,"navigation":1745,"path":206,"seo":1746,"stem":207,"__hash__":1749},"docs\u002F7.advanced\u002F11.mcp-apps-internals.md",{"type":290,"value":291,"toc":1721},"minimark",[292,301,306,319,385,388,431,445,450,453,548,567,571,582,641,647,654,769,773,776,780,787,816,819,918,929,933,936,954,957,961,972,980,987,996,1131,1144,1148,1152,1166,1203,1218,1222,1229,1235,1294,1314,1423,1426,1478,1502,1515,1519,1526,1621,1624,1628,1642,1657,1669,1673,1717],[293,294,295,296,300],"p",{},"This page covers the moving parts behind ",[297,298,299],"a",{"href":162},"MCP Apps",": the build pipeline, the host bridge, the security model, and patterns you can compose on top.",[302,303,305],"h2",{"id":304},"build-pipeline","Build Pipeline",[293,307,308,309,313,314,318],{},"For each ",[310,311,312],"code",{},"app\u002Fmcp\u002F*.vue"," file, the Nuxt module emits ",[315,316,317],"strong",{},"three artifacts"," at build time and registers them on the configured handler:",[320,321,326],"pre",{"className":322,"code":323,"language":324,"meta":325,"style":325},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight",".nuxt\u002Fmcp-apps\u002F\n├── color-picker.app.ts       # McpAppDefinition (the parsed defineMcpApp call)\n├── color-picker.tool.ts      # McpToolDefinition wrapping the app\n├── color-picker.resource.ts  # McpResourceDefinition serving the HTML\n└── color-picker.html         # Single-file Vue bundle (vite-plugin-singlefile)\n","bash","",[310,327,328,337,351,362,373],{"__ignoreMap":325},[329,330,333],"span",{"class":331,"line":332},"line",1,[329,334,336],{"class":335},"sBMFI",".nuxt\u002Fmcp-apps\u002F\n",[329,338,340,343,347],{"class":331,"line":339},2,[329,341,342],{"class":335},"├──",[329,344,346],{"class":345},"sfazB"," color-picker.app.ts",[329,348,350],{"class":349},"sHwdD","       # McpAppDefinition (the parsed defineMcpApp call)\n",[329,352,354,356,359],{"class":331,"line":353},3,[329,355,342],{"class":335},[329,357,358],{"class":345}," color-picker.tool.ts",[329,360,361],{"class":349},"      # McpToolDefinition wrapping the app\n",[329,363,365,367,370],{"class":331,"line":364},4,[329,366,342],{"class":335},[329,368,369],{"class":345}," color-picker.resource.ts",[329,371,372],{"class":349},"  # McpResourceDefinition serving the HTML\n",[329,374,376,379,382],{"class":331,"line":375},5,[329,377,378],{"class":335},"└──",[329,380,381],{"class":345}," color-picker.html",[329,383,384],{"class":349},"         # Single-file Vue bundle (vite-plugin-singlefile)\n",[293,386,387],{},"The pipeline runs in three phases:",[389,390,391,410,425],"ol",{},[392,393,394,397,398,401,402,405,406,409],"li",{},[315,395,396],{},"Parse"," — extract the ",[310,399,400],{},"defineMcpApp({ … })"," call from ",[310,403,404],{},"\u003Cscript setup>"," and pull out only the imports that are referenced inside the macro arguments. The macro is then ",[315,407,408],{},"stripped"," from the browser bundle.",[392,411,412,415,416,424],{},[315,413,414],{},"Bundle"," — call Vite programmatically with ",[297,417,421],{"href":418,"rel":419},"https:\u002F\u002Fgithub.com\u002Frichardtallent\u002Fvite-plugin-singlefile",[420],"nofollow",[310,422,423],{},"vite-plugin-singlefile"," to produce one self-contained HTML file (Vue runtime, your code, scoped CSS, assets) per SFC.",[392,426,427,430],{},[315,428,429],{},"Emit"," — write the three TypeScript files plus the HTML, then add them to Nuxt's auto-import + handler registration so they behave like any other tool \u002F resource.",[432,433,436,437,440,441,444],"callout",{"color":434,"icon":435},"info","i-lucide-info","Output lives under ",[310,438,439],{},"\u003CbuildDir>\u002Fmcp-apps\u002F",". It's regenerated on every build, and the dev server watches ",[310,442,443],{},"app\u002Fmcp\u002F**"," so changes hot-reload.",[446,447,449],"h3",{"id":448},"what-gets-inlined-into-the-html","What Gets Inlined Into The HTML",[293,451,452],{},"When the LLM calls the tool, the toolkit takes the bundled HTML and injects:",[320,454,458],{"className":455,"code":456,"language":457,"meta":325,"style":325},"language-html shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cmeta http-equiv=\"Content-Security-Policy\" content=\"…\">\n\u003Cscript type=\"application\u002Fjson\" id=\"__mcp_app_data__\">\n  { \"base\": \"#2563eb\", \"swatches\": [ … ] }\n\u003C\u002Fscript>\n","html",[310,459,460,500,533,539],{"__ignoreMap":325},[329,461,462,466,470,474,477,480,483,485,488,490,492,495,497],{"class":331,"line":332},[329,463,465],{"class":464},"sMK4o","\u003C",[329,467,469],{"class":468},"swJcz","meta",[329,471,473],{"class":472},"spNyl"," http-equiv",[329,475,476],{"class":464},"=",[329,478,479],{"class":464},"\"",[329,481,482],{"class":345},"Content-Security-Policy",[329,484,479],{"class":464},[329,486,487],{"class":472}," content",[329,489,476],{"class":464},[329,491,479],{"class":464},[329,493,494],{"class":345},"…",[329,496,479],{"class":464},[329,498,499],{"class":464},">\n",[329,501,502,504,507,510,512,514,517,519,522,524,526,529,531],{"class":331,"line":339},[329,503,465],{"class":464},[329,505,506],{"class":468},"script",[329,508,509],{"class":472}," type",[329,511,476],{"class":464},[329,513,479],{"class":464},[329,515,516],{"class":345},"application\u002Fjson",[329,518,479],{"class":464},[329,520,521],{"class":472}," id",[329,523,476],{"class":464},[329,525,479],{"class":464},[329,527,528],{"class":345},"__mcp_app_data__",[329,530,479],{"class":464},[329,532,499],{"class":464},[329,534,535],{"class":331,"line":353},[329,536,538],{"class":537},"sTEyZ","  { \"base\": \"#2563eb\", \"swatches\": [ … ] }\n",[329,540,541,544,546],{"class":331,"line":364},[329,542,543],{"class":464},"\u003C\u002F",[329,545,506],{"class":468},[329,547,499],{"class":464},[293,549,550,551,554,555,558,559,562,563,566],{},"The first ",[310,552,553],{},"useMcpApp()"," call reads ",[310,556,557],{},"#__mcp_app_data__"," synchronously, so ",[310,560,561],{},"data.value"," is ",[315,564,565],{},"already populated on the first paint"," — no fetch, no waterfall.",[302,568,570],{"id":569},"the-host-bridge","The Host Bridge",[293,572,573,574,577,578,581],{},"The iframe and the host communicate over ",[310,575,576],{},"postMessage"," using a JSON-RPC 2.0 envelope. The toolkit ships a singleton ",[310,579,580],{},"useHostBridge()"," (internal) that:",[389,583,584,595,605,619,630],{},[392,585,586,587,590,591,594],{},"Performs the ",[310,588,589],{},"ui\u002Finitialize"," handshake to negotiate capabilities and receive ",[310,592,593],{},"HostContext",".",[392,596,597,598,601,602,594],{},"Routes incoming ",[310,599,600],{},"tool-result"," messages back into ",[310,603,604],{},"data",[392,606,607,608,611,612,611,615,618],{},"Dispatches outbound ",[310,609,610],{},"callTool",", ",[310,613,614],{},"prompt",[310,616,617],{},"openLink"," requests to the host.",[392,620,621,622,625,626,629],{},"Falls back to the legacy ",[310,623,624],{},"mcp-ui"," envelope (",[310,627,628],{},"{ type, payload }",") when talking to older hosts.",[392,631,632,633,636,637,640],{},"Detects the ",[315,634,635],{},"ChatGPT Apps SDK"," (",[310,638,639],{},"window.openai",") and uses its native APIs when available.",[293,642,643,644,646],{},"You don't talk to it directly — ",[310,645,553],{}," composes the public surface.",[293,648,649,650,653],{},"The full round-trip when the LLM calls ",[310,651,652],{},"color-picker",":",[389,655,656,665,678,688,694,708,717,734,747,755],{},[392,657,658,661,662,594],{},[315,659,660],{},"Host → Server"," — ",[310,663,664],{},"tools\u002Fcall color-picker { base }",[392,666,667,670,671,674,675,594],{},[315,668,669],{},"Server"," — runs ",[310,672,673],{},"handler()"," and produces ",[310,676,677],{},"structuredContent",[392,679,680,683,684,687],{},[315,681,682],{},"Server → Host"," — returns the bundled HTML with the data inlined and a ",[310,685,686],{},"ui:\u002F\u002F"," resource reference.",[392,689,690,693],{},[315,691,692],{},"Host → Iframe"," — mounts the iframe inline, sandboxed.",[392,695,696,661,699,701,702,558,705,707],{},[315,697,698],{},"Iframe",[310,700,553],{}," reads the inline ",[310,703,704],{},"\u003Cscript id=\"__mcp_app_data__\">",[310,706,604],{}," is populated on first paint.",[392,709,710,713,714,716],{},[315,711,712],{},"Iframe → Host"," — sends ",[310,715,589],{}," to negotiate capabilities.",[392,718,719,721,722,636,724,611,727,611,730,733],{},[315,720,692],{}," — replies with ",[310,723,593],{},[310,725,726],{},"theme",[310,728,729],{},"displayMode",[310,731,732],{},"containerDimensions",", …).",[392,735,736,738,739,661,743,746],{},[315,737,712],{}," ",[740,741,742],"em",{},"(optional)",[310,744,745],{},"ui\u002FcallTool { name, params }"," for in-place refreshes.",[392,748,749,751,752,594],{},[315,750,660],{}," — forwards the call as ",[310,753,754],{},"tools\u002Fcall name { params }",[392,756,757,760,761,763,764,766,767,594],{},[315,758,759],{},"Server → Host → Iframe"," — new ",[310,762,677],{}," flows back as a ",[310,765,600],{}," and replaces ",[310,768,604],{},[302,770,772],{"id":771},"security-model","Security Model",[293,774,775],{},"MCP Apps run in a sandboxed iframe loaded from the same origin as your MCP endpoint. The toolkit hardens the surface in three layers.",[446,777,779],{"id":778},"_1-default-csp","1. Default CSP",[293,781,782,783,786],{},"Every app HTML gets a CSP ",[310,784,785],{},"\u003Cmeta>"," that:",[788,789,790,793,800],"ul",{},[392,791,792],{},"Blocks all third-party scripts. Only the inline bundle script may execute.",[392,794,795,796,799],{},"Blocks ",[310,797,798],{},"\u003Cform>"," action targets.",[392,801,802,803,611,806,611,809,611,812,815],{},"Disallows ",[310,804,805],{},"connect-src",[310,807,808],{},"img-src",[310,810,811],{},"style-src",[310,813,814],{},"font-src"," external origins until you explicitly allow them.",[293,817,818],{},"You opt into external resources per app:",[320,820,824],{"className":821,"code":822,"language":823,"meta":325,"style":325},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","defineMcpApp({\n  csp: {\n    resourceDomains: ['https:\u002F\u002Fimages.example.com'], \u002F\u002F img \u002F style \u002F font \u002F link\n    connectDomains: ['https:\u002F\u002Fapi.example.com'],     \u002F\u002F fetch \u002F XHR \u002F WebSocket\n  },\n  \u002F\u002F …\n})\n","ts",[310,825,826,838,848,875,898,903,909],{"__ignoreMap":325},[329,827,828,832,835],{"class":331,"line":332},[329,829,831],{"class":830},"s2Zo4","defineMcpApp",[329,833,834],{"class":537},"(",[329,836,837],{"class":464},"{\n",[329,839,840,843,845],{"class":331,"line":339},[329,841,842],{"class":468},"  csp",[329,844,653],{"class":464},[329,846,847],{"class":464}," {\n",[329,849,850,853,855,858,861,864,866,869,872],{"class":331,"line":353},[329,851,852],{"class":468},"    resourceDomains",[329,854,653],{"class":464},[329,856,857],{"class":537}," [",[329,859,860],{"class":464},"'",[329,862,863],{"class":345},"https:\u002F\u002Fimages.example.com",[329,865,860],{"class":464},[329,867,868],{"class":537},"]",[329,870,871],{"class":464},",",[329,873,874],{"class":349}," \u002F\u002F img \u002F style \u002F font \u002F link\n",[329,876,877,880,882,884,886,889,891,893,895],{"class":331,"line":364},[329,878,879],{"class":468},"    connectDomains",[329,881,653],{"class":464},[329,883,857],{"class":537},[329,885,860],{"class":464},[329,887,888],{"class":345},"https:\u002F\u002Fapi.example.com",[329,890,860],{"class":464},[329,892,868],{"class":537},[329,894,871],{"class":464},[329,896,897],{"class":349},"     \u002F\u002F fetch \u002F XHR \u002F WebSocket\n",[329,899,900],{"class":331,"line":375},[329,901,902],{"class":464},"  },\n",[329,904,906],{"class":331,"line":905},6,[329,907,908],{"class":349},"  \u002F\u002F …\n",[329,910,912,915],{"class":331,"line":911},7,[329,913,914],{"class":464},"}",[329,916,917],{"class":537},")\n",[293,919,920,921,924,925,928],{},"The same allow-list is mirrored into ",[310,922,923],{},"_meta.ui.csp"," and ",[310,926,927],{},"_meta['openai\u002FwidgetCSP']"," for hosts that enforce CSP themselves.",[446,930,932],{"id":931},"_2-domain-validation","2. Domain Validation",[293,934,935],{},"CSP origins are validated at build time. The toolkit rejects:",[788,937,938,941,951],{},[392,939,940],{},"Non-string or empty values.",[392,942,943,944,947,948,594],{},"URL schemes other than ",[310,945,946],{},"http(s):\u002F\u002F"," or ",[310,949,950],{},"ws(s):\u002F\u002F",[392,952,953],{},"Strings that contain quotes, semicolons, or whitespace.",[293,955,956],{},"If a domain looks suspicious, the build fails — you can't accidentally ship an injection vector via misconfiguration.",[446,958,960],{"id":959},"_3-iframe-isolation","3. Iframe Isolation",[293,962,963,964,967,968,971],{},"The iframe runs as if it were a third-party page on your origin: no cookies, no ",[310,965,966],{},"localStorage"," from the parent app, no shared module graph. This is ",[315,969,970],{},"by design"," — apps must declare what they need, and they cannot reach into the parent Nuxt runtime.",[432,973,975,976,979],{"color":974,"icon":64},"warning","Pass ",[310,977,978],{},"csp: false"," only when you fully control every byte the iframe loads. Stripping the CSP turns off the only line of defense against compromised dependencies.",[302,981,983,984],{"id":982},"custom-_meta","Custom ",[310,985,986],{},"_meta",[293,988,989,990,993,994,653],{},"The handler returns a regular MCP ",[310,991,992],{},"CallToolResult",", so you can attach any host-specific metadata via ",[310,995,986],{},[320,997,999],{"className":821,"code":998,"language":823,"meta":325,"style":325},"defineMcpApp({\n  _meta: {\n    'openai\u002FwidgetAccessible': true,\n    'openai\u002FtoolInvocation\u002Finvoking': 'Loading stays…',\n    'openai\u002FtoolInvocation\u002Finvoked': 'Stays loaded',\n  },\n  handler: async () => ({ structuredContent: { … } }),\n})\n",[310,1000,1001,1009,1018,1037,1058,1078,1082,1124],{"__ignoreMap":325},[329,1002,1003,1005,1007],{"class":331,"line":332},[329,1004,831],{"class":830},[329,1006,834],{"class":537},[329,1008,837],{"class":464},[329,1010,1011,1014,1016],{"class":331,"line":339},[329,1012,1013],{"class":468},"  _meta",[329,1015,653],{"class":464},[329,1017,847],{"class":464},[329,1019,1020,1023,1026,1028,1030,1034],{"class":331,"line":353},[329,1021,1022],{"class":464},"    '",[329,1024,1025],{"class":468},"openai\u002FwidgetAccessible",[329,1027,860],{"class":464},[329,1029,653],{"class":464},[329,1031,1033],{"class":1032},"sfNiH"," true",[329,1035,1036],{"class":464},",\n",[329,1038,1039,1041,1044,1046,1048,1051,1054,1056],{"class":331,"line":364},[329,1040,1022],{"class":464},[329,1042,1043],{"class":468},"openai\u002FtoolInvocation\u002Finvoking",[329,1045,860],{"class":464},[329,1047,653],{"class":464},[329,1049,1050],{"class":464}," '",[329,1052,1053],{"class":345},"Loading stays…",[329,1055,860],{"class":464},[329,1057,1036],{"class":464},[329,1059,1060,1062,1065,1067,1069,1071,1074,1076],{"class":331,"line":375},[329,1061,1022],{"class":464},[329,1063,1064],{"class":468},"openai\u002FtoolInvocation\u002Finvoked",[329,1066,860],{"class":464},[329,1068,653],{"class":464},[329,1070,1050],{"class":464},[329,1072,1073],{"class":345},"Stays loaded",[329,1075,860],{"class":464},[329,1077,1036],{"class":464},[329,1079,1080],{"class":331,"line":905},[329,1081,902],{"class":464},[329,1083,1084,1087,1089,1092,1095,1098,1100,1103,1106,1108,1111,1114,1116,1119,1122],{"class":331,"line":911},[329,1085,1086],{"class":830},"  handler",[329,1088,653],{"class":464},[329,1090,1091],{"class":472}," async",[329,1093,1094],{"class":464}," ()",[329,1096,1097],{"class":472}," =>",[329,1099,636],{"class":537},[329,1101,1102],{"class":464},"{",[329,1104,1105],{"class":468}," structuredContent",[329,1107,653],{"class":464},[329,1109,1110],{"class":464}," {",[329,1112,1113],{"class":537}," … ",[329,1115,914],{"class":464},[329,1117,1118],{"class":464}," }",[329,1120,1121],{"class":537},")",[329,1123,1036],{"class":464},[329,1125,1127,1129],{"class":331,"line":1126},8,[329,1128,914],{"class":464},[329,1130,917],{"class":537},[293,1132,1133,1134,1137,1138,1140,1141,1143],{},"The toolkit auto-fills ",[310,1135,1136],{},"_meta.ui.resourceUri"," (so hosts can re-fetch the HTML on demand) and ",[310,1139,923],{},". Anything you put in ",[310,1142,986],{}," is merged on top.",[302,1145,1147],{"id":1146},"advanced-patterns","Advanced Patterns",[446,1149,1151],{"id":1150},"re-using-server-logic","Re-using server logic",[293,1153,1154,1155,611,1158,1161,1162,1165],{},"Apps share ",[310,1156,1157],{},"server\u002Fapi\u002F",[310,1159,1160],{},"server\u002Futils\u002F",", and ",[310,1163,1164],{},"shared\u002F"," with the rest of your Nuxt app. A typical layout:",[320,1167,1169],{"className":322,"code":1168,"language":324,"meta":325,"style":325},"app\u002Fmcp\u002Fcolor-picker.vue          # UI + handler that calls $fetch('\u002Fapi\u002Fpalette')\nserver\u002Fapi\u002Fpalette.get.ts         # The actual data endpoint (callable by humans + tools)\nserver\u002Futils\u002Fpalette.ts           # Shared generators \u002F helpers\nshared\u002Ftypes\u002Fpalette.ts           # Types auto-imported by both the SFC and the endpoint\n",[310,1170,1171,1179,1187,1195],{"__ignoreMap":325},[329,1172,1173,1176],{"class":331,"line":332},[329,1174,1175],{"class":335},"app\u002Fmcp\u002Fcolor-picker.vue",[329,1177,1178],{"class":349},"          # UI + handler that calls $fetch('\u002Fapi\u002Fpalette')\n",[329,1180,1181,1184],{"class":331,"line":339},[329,1182,1183],{"class":335},"server\u002Fapi\u002Fpalette.get.ts",[329,1185,1186],{"class":349},"         # The actual data endpoint (callable by humans + tools)\n",[329,1188,1189,1192],{"class":331,"line":353},[329,1190,1191],{"class":335},"server\u002Futils\u002Fpalette.ts",[329,1193,1194],{"class":349},"           # Shared generators \u002F helpers\n",[329,1196,1197,1200],{"class":331,"line":364},[329,1198,1199],{"class":335},"shared\u002Ftypes\u002Fpalette.ts",[329,1201,1202],{"class":349},"           # Types auto-imported by both the SFC and the endpoint\n",[293,1204,1205,1206,1209,1210,1213,1214,1217],{},"A regular Nuxt page, an external client, or the MCP App handler all hit ",[310,1207,1208],{},"\u002Fapi\u002Fpalette"," with the exact same contract — and types under ",[310,1211,1212],{},"shared\u002Ftypes\u002F"," resolve globally without an ",[310,1215,1216],{},"import"," statement.",[446,1219,1221],{"id":1220},"multiple-handlers","Multiple handlers",[293,1223,1224,1225,1228],{},"Apps are attributed to a named MCP handler at build time, exactly like tools and resources under ",[310,1226,1227],{},"server\u002Fmcp\u002Fhandlers\u002F\u003Cname>\u002F",". Two ways to control attribution:",[293,1230,1231,1234],{},[315,1232,1233],{},"Sub-folder convention"," — drop the SFC under a sub-directory matching the handler name:",[320,1236,1238],{"className":322,"code":1237,"language":324,"meta":325,"style":325},"app\u002Fmcp\u002F\n├── color-picker.vue          # → handler 'apps' (default)\n├── finder\u002F\n│   └── stay-finder.vue       # → handler 'finder' (\u002Fmcp\u002Ffinder)\n└── checkout\u002F\n    └── stay-checkout.vue     # → handler 'checkout' (\u002Fmcp\u002Fcheckout)\n",[310,1239,1240,1245,1255,1262,1276,1283],{"__ignoreMap":325},[329,1241,1242],{"class":331,"line":332},[329,1243,1244],{"class":335},"app\u002Fmcp\u002F\n",[329,1246,1247,1249,1252],{"class":331,"line":339},[329,1248,342],{"class":335},[329,1250,1251],{"class":345}," color-picker.vue",[329,1253,1254],{"class":349},"          # → handler 'apps' (default)\n",[329,1256,1257,1259],{"class":331,"line":353},[329,1258,342],{"class":335},[329,1260,1261],{"class":345}," finder\u002F\n",[329,1263,1264,1267,1270,1273],{"class":331,"line":364},[329,1265,1266],{"class":335},"│",[329,1268,1269],{"class":345},"   └──",[329,1271,1272],{"class":345}," stay-finder.vue",[329,1274,1275],{"class":349},"       # → handler 'finder' (\u002Fmcp\u002Ffinder)\n",[329,1277,1278,1280],{"class":331,"line":375},[329,1279,378],{"class":335},[329,1281,1282],{"class":345}," checkout\u002F\n",[329,1284,1285,1288,1291],{"class":331,"line":905},[329,1286,1287],{"class":335},"    └──",[329,1289,1290],{"class":345}," stay-checkout.vue",[329,1292,1293],{"class":349},"     # → handler 'checkout' (\u002Fmcp\u002Fcheckout)\n",[293,1295,1296,661,1299,1302,1303,1306,1307,1310,1311,1313],{},[315,1297,1298],{},"Explicit override",[310,1300,1301],{},"attachTo"," (plus ",[310,1304,1305],{},"group"," \u002F ",[310,1308,1309],{},"tags",") on ",[310,1312,831],{}," win over the folder default:",[320,1315,1320],{"className":1316,"code":1317,"filename":1318,"language":1319,"meta":325,"style":325},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript setup lang=\"ts\">\ndefineMcpApp({\n  attachTo: 'finder',\n  group: 'stays',\n  tags: ['searchable'],\n  \u002F\u002F ...\n})\n\u003C\u002Fscript>\n","app\u002Fmcp\u002Fstay-finder.vue","vue",[310,1321,1322,1344,1352,1368,1384,1404,1409,1415],{"__ignoreMap":325},[329,1323,1324,1326,1328,1331,1334,1336,1338,1340,1342],{"class":331,"line":332},[329,1325,465],{"class":464},[329,1327,506],{"class":468},[329,1329,1330],{"class":472}," setup",[329,1332,1333],{"class":472}," lang",[329,1335,476],{"class":464},[329,1337,479],{"class":464},[329,1339,823],{"class":345},[329,1341,479],{"class":464},[329,1343,499],{"class":464},[329,1345,1346,1348,1350],{"class":331,"line":339},[329,1347,831],{"class":830},[329,1349,834],{"class":537},[329,1351,837],{"class":464},[329,1353,1354,1357,1359,1361,1364,1366],{"class":331,"line":353},[329,1355,1356],{"class":468},"  attachTo",[329,1358,653],{"class":464},[329,1360,1050],{"class":464},[329,1362,1363],{"class":345},"finder",[329,1365,860],{"class":464},[329,1367,1036],{"class":464},[329,1369,1370,1373,1375,1377,1380,1382],{"class":331,"line":364},[329,1371,1372],{"class":468},"  group",[329,1374,653],{"class":464},[329,1376,1050],{"class":464},[329,1378,1379],{"class":345},"stays",[329,1381,860],{"class":464},[329,1383,1036],{"class":464},[329,1385,1386,1389,1391,1393,1395,1398,1400,1402],{"class":331,"line":375},[329,1387,1388],{"class":468},"  tags",[329,1390,653],{"class":464},[329,1392,857],{"class":537},[329,1394,860],{"class":464},[329,1396,1397],{"class":345},"searchable",[329,1399,860],{"class":464},[329,1401,868],{"class":537},[329,1403,1036],{"class":464},[329,1405,1406],{"class":331,"line":905},[329,1407,1408],{"class":349},"  \u002F\u002F ...\n",[329,1410,1411,1413],{"class":331,"line":911},[329,1412,914],{"class":464},[329,1414,917],{"class":537},[329,1416,1417,1419,1421],{"class":331,"line":1126},[329,1418,543],{"class":464},[329,1420,506],{"class":468},[329,1422,499],{"class":464},[293,1424,1425],{},"Then add the matching handler index file:",[320,1427,1430],{"className":821,"code":1428,"filename":1429,"language":823,"meta":325,"style":325},"import { defineMcpHandler } from '@nuxtjs\u002Fmcp-toolkit\u002Fserver'\n\nexport default defineMcpHandler({})\n","server\u002Fmcp\u002Fhandlers\u002Ffinder\u002Findex.ts",[310,1431,1432,1455,1461],{"__ignoreMap":325},[329,1433,1434,1437,1439,1442,1444,1447,1449,1452],{"class":331,"line":332},[329,1435,1216],{"class":1436},"s7zQu",[329,1438,1110],{"class":464},[329,1440,1441],{"class":537}," defineMcpHandler",[329,1443,1118],{"class":464},[329,1445,1446],{"class":1436}," from",[329,1448,1050],{"class":464},[329,1450,1451],{"class":345},"@nuxtjs\u002Fmcp-toolkit\u002Fserver",[329,1453,1454],{"class":464},"'\n",[329,1456,1457],{"class":331,"line":339},[329,1458,1460],{"emptyLinePlaceholder":1459},true,"\n",[329,1462,1463,1466,1469,1471,1473,1476],{"class":331,"line":353},[329,1464,1465],{"class":1436},"export",[329,1467,1468],{"class":1436}," default",[329,1470,1441],{"class":830},[329,1472,834],{"class":537},[329,1474,1475],{"class":464},"{}",[329,1477,917],{"class":537},[293,1479,1480,1481,1484,1485,1488,1489,1492,1493,924,1496,1499,1500,594],{},"With ",[310,1482,1483],{},"defaultHandlerStrategy: 'orphans'"," (the default) the app no longer leaks into ",[310,1486,1487],{},"\u002Fmcp"," — it only shows up on ",[310,1490,1491],{},"\u002Fmcp\u002Ffinder",". Need a manual cross-cut? ",[310,1494,1495],{},"getMcpTools({ handler: 'finder' })",[310,1497,1498],{},"getMcpResources({ handler: 'finder' })"," return the raw definitions for further filtering. See ",[297,1501,123],{"href":128},[432,1503,1504,611,1506,1161,1508,1510,1511,1514],{"color":434,"icon":435},[310,1505,1301],{},[310,1507,1305],{},[310,1509,1309],{}," must be ",[315,1512,1513],{},"literals",". The toolkit reads them statically at build time so the routing decision is deterministic across dev, build, and deploy. Dynamic expressions fail the build with a clear error.",[446,1516,1518],{"id":1517},"per-host-adaptation","Per-host adaptation",[293,1520,1521,1522,1525],{},"Use ",[310,1523,1524],{},"hostContext"," to opt into host-specific affordances:",[320,1527,1529],{"className":821,"code":1528,"language":823,"meta":325,"style":325},"const isChatGpt = computed(() => typeof window !== 'undefined' && 'openai' in window)\nconst supportsFullscreen = computed(() => hostContext.value?.displayMode !== undefined)\n",[310,1530,1531,1583],{"__ignoreMap":325},[329,1532,1533,1536,1539,1541,1544,1546,1549,1551,1554,1557,1560,1562,1565,1567,1570,1572,1575,1577,1580],{"class":331,"line":332},[329,1534,1535],{"class":472},"const",[329,1537,1538],{"class":537}," isChatGpt ",[329,1540,476],{"class":464},[329,1542,1543],{"class":830}," computed",[329,1545,834],{"class":537},[329,1547,1548],{"class":464},"()",[329,1550,1097],{"class":472},[329,1552,1553],{"class":464}," typeof",[329,1555,1556],{"class":537}," window ",[329,1558,1559],{"class":464},"!==",[329,1561,1050],{"class":464},[329,1563,1564],{"class":345},"undefined",[329,1566,860],{"class":464},[329,1568,1569],{"class":464}," &&",[329,1571,1050],{"class":464},[329,1573,1574],{"class":345},"openai",[329,1576,860],{"class":464},[329,1578,1579],{"class":464}," in",[329,1581,1582],{"class":537}," window)\n",[329,1584,1585,1587,1590,1592,1594,1596,1598,1600,1603,1605,1608,1611,1614,1616,1619],{"class":331,"line":339},[329,1586,1535],{"class":472},[329,1588,1589],{"class":537}," supportsFullscreen ",[329,1591,476],{"class":464},[329,1593,1543],{"class":830},[329,1595,834],{"class":537},[329,1597,1548],{"class":464},[329,1599,1097],{"class":472},[329,1601,1602],{"class":537}," hostContext",[329,1604,594],{"class":464},[329,1606,1607],{"class":537},"value",[329,1609,1610],{"class":464},"?.",[329,1612,1613],{"class":537},"displayMode ",[329,1615,1559],{"class":464},[329,1617,1618],{"class":464}," undefined",[329,1620,917],{"class":537},[293,1622,1623],{},"Avoid hard-coding behaviours per host whenever you can — the bridge already smooths over the major differences.",[446,1625,1627],{"id":1626},"testing-apps","Testing apps",[293,1629,1630,1631,1634,1635,1638,1639,1641],{},"Server-side: the ",[310,1632,1633],{},"handler"," is a plain async function. Import the parsed app definition from ",[310,1636,1637],{},".nuxt\u002Fmcp-apps\u002F\u003Cname>.app.ts"," (or import the SFC's ",[310,1640,831],{}," arguments via the parser) and call the handler directly with mock input.",[293,1643,1644,1645,1648,1649,1652,1653,1656],{},"Iframe-side: render the SFC with ",[310,1646,1647],{},"@vue\u002Ftest-utils"," and stub the host bridge by injecting ",[310,1650,1651],{},"window.parent.postMessage"," listeners. The toolkit's own test suite (",[310,1654,1655],{},"packages\u002Fnuxt-mcp-toolkit\u002Ftest\u002Fapps-handshake.test.ts",") shows the pattern.",[432,1658,1659,1660,1665,1666,1668],{"color":434,"icon":435},"Most regressions in MCP Apps come from forgetting that ",[315,1661,1662,1664],{},[310,1663,1633],{}," runs server-side and the template runs client-side",". Treat them as two halves of an API: one produces a contract (",[310,1667,677],{},"), the other consumes it.",[302,1670,1672],{"id":1671},"limits-footguns","Limits & Footguns",[788,1674,1675,1684,1692,1702,1708],{},[392,1676,1677,1680,1681,594],{},[315,1678,1679],{},"One handler per app."," If you need a second tool from the same UI, declare it elsewhere and call it via ",[310,1682,1683],{},"callTool('other-tool', …)",[392,1685,1686,1691],{},[315,1687,1688,1689],{},"No top-level await in ",[310,1690,404],{}," of an app — the macro must be statically analysable.",[392,1693,1694,1701],{},[315,1695,1696,1697,1700],{},"Only relative imports + auto-imports + the ",[310,1698,1699],{},"#shared"," alias"," in the SFC. Anything that pulls in the Nuxt runtime won't bundle.",[392,1703,1704,1707],{},[315,1705,1706],{},"Keep payloads small."," The data is inlined into the HTML; large payloads (>1 MB) noticeably slow first paint.",[392,1709,1710,1716],{},[315,1711,1712,1713,594],{},"Style with ",[310,1714,1715],{},"scoped"," Global styles leak across apps because every app loads its own copy of Vue's style runtime.",[1718,1719,1720],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}",{"title":325,"searchDepth":339,"depth":339,"links":1722},[1723,1726,1727,1732,1734,1740],{"id":304,"depth":339,"text":305,"children":1724},[1725],{"id":448,"depth":353,"text":449},{"id":569,"depth":339,"text":570},{"id":771,"depth":339,"text":772,"children":1728},[1729,1730,1731],{"id":778,"depth":353,"text":779},{"id":931,"depth":353,"text":932},{"id":959,"depth":353,"text":960},{"id":982,"depth":339,"text":1733},"Custom _meta",{"id":1146,"depth":339,"text":1147,"children":1735},[1736,1737,1738,1739],{"id":1150,"depth":353,"text":1151},{"id":1220,"depth":353,"text":1221},{"id":1517,"depth":353,"text":1518},{"id":1626,"depth":353,"text":1627},{"id":1671,"depth":339,"text":1672},"How the toolkit bundles, serves, and connects MCP Apps — and the patterns you can build on top.","md",null,{},{"icon":208},{"title":1747,"description":1748},"MCP Apps Internals & Advanced Patterns","Understand the build pipeline, the host bridge protocol, security defaults, and the advanced patterns possible with MCP Apps in @nuxtjs\u002Fmcp-toolkit.","rPxipHcQyw1QmXBoVsuepgpIjU8LPxewceHkT0IEJQc",[1751,1753],{"title":200,"path":201,"stem":202,"description":1752,"icon":203,"children":-1},"Stream logs to MCP clients and capture structured wide events with useMcpLogger().",{"title":210,"path":211,"stem":212,"description":1754,"icon":213,"children":-1},"Read the toolkit's discovered tools, resources, and prompts from your own server routes — without duplicating their names and descriptions.",1787670842627]