[{"data":1,"prerenderedAt":741},["ShallowReactive",2],{"blog-webmcp-en":3},{"slug":4,"published":5,"minutes":6,"category":7,"tags":8,"keywords":14,"about":21,"sources":29,"cover":53,"og":54,"expertise":55,"locales":56,"lang":57,"title":60,"description":61,"coverAlt":62,"metaTitle":63,"takeaways":64,"faq":70,"toc":83,"blocks":108,"others":468},"webmcp","2026-09-30",11,"web",[9,10,11,12,13],"WebMCP","Agent tools","JSON Schema","Chrome","MCP",[4,15,16,17,18,19,20],"webmcp vs mcp","document.modelContext registerTool","webmcp declarative api","browser agent tools","webmcp origin trial","webmcp chrome 149",[22,24,27],{"name":9,"url":23},"https:\u002F\u002Fgithub.com\u002Fwebmachinelearning\u002Fwebmcp",{"name":25,"url":26},"Model Context Protocol","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FModel_Context_Protocol",{"name":11,"url":28},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FJSON_Schema",[30,33,36,39,42,45,48,50],{"title":31,"url":32},"Chrome for Developers: WebMCP (get started)","https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fai\u002Fwebmcp",{"title":34,"url":35},"Chrome for Developers: Imperative API","https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fai\u002Fwebmcp\u002Fimperative-api",{"title":37,"url":38},"Chrome for Developers: Declarative API","https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fai\u002Fwebmcp\u002Fdeclarative-api",{"title":40,"url":41},"Chrome for Developers: WebMCP versus MCP","https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fai\u002Fwebmcp\u002Fcompare-mcp",{"title":43,"url":44},"Chrome for Developers: WebMCP tool security","https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fai\u002Fwebmcp\u002Fsecure-tools",{"title":46,"url":47},"Chrome for Developers: Build effective tools","https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fai\u002Fwebmcp\u002Fbuild-tools",{"title":49,"url":23},"WebMCP explainer and specification draft",{"title":51,"url":52},"WebMCP implementation status","https:\u002F\u002Fgithub.com\u002Fwebmachinelearning\u002Fwebmcp\u002Fblob\u002Fmain\u002Fimplementation-status.md","\u002Fimages\u002Fblog\u002Fwebmcp\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fwebmcp\u002Fog.jpg","ai-engineer",[57,58,59],"en","de","hu","WebMCP: publishing tools instead of pixels","WebMCP lets a page publish typed, callable tools to an in-browser agent. What the standard does, how much of it ships today, and where a plain MCP server is still the better call.","A wide panel of tiny squares, a slot in its edge passing one shaped block to a receiver.","WebMCP: publishing tools instead of pixels · Balázs Csorba",[65,66,67,68,69],"WebMCP is a browser API proposal, not a library: Chrome 149 and Edge 150 ship it as an origin trial, and Firefox and Safari are at the standards-position stage.","Tools are tab-bound, run the page's own code, and are gated by the tools Permissions Policy rather than by an agent guessing at the DOM.","Chrome's guidance caps a tool description at 500 characters and a tool's output at 1.5K, so the tool catalogue is a context-window budget.","All four tool annotations default to false, which makes a tool's safety properties exactly as honest as the page that sets them.","There is no discovery mechanism: a client has to visit the site to learn that tools exist, which changes what the standard is commercially worth.",[71,74,77,80],{"q":72,"a":73},"What is WebMCP?","WebMCP is a proposed web standard that lets a page publish typed, callable tools to an AI agent running in the browser. A page registers a name, a natural-language description, a JSON Schema for the input and an execute function, and the browser mediates calls between the agent and that function. The tools live only as long as the tab is open.",{"q":75,"a":76},"Is WebMCP a replacement for MCP?","No. Chrome's own comparison page treats the two as partners rather than rivals: MCP exposes backend capabilities to any client, persistently, while WebMCP exposes live page state to a browser-integrated agent, ephemerally. Most production setups end up using both, with MCP for business logic and WebMCP for the interface in front of the user.",{"q":78,"a":79},"Do I need the Chrome origin trial to use WebMCP locally?","No. For local development, the chrome:\u002F\u002Fflags\u002F#enable-webmcp-testing flag enables it without a token. Serving real users on Chrome 149 or later does require registering for the origin trial, and Edge 150 runs a separate trial with its own registration.",{"q":81,"a":82},"What is the biggest limitation today?","Browser support, followed by discoverability. The implementation status file in the specification repository lists origin trials in Chrome and Edge, experimental support in Brave's Leo chat and support in ChatGPT Desktop, and nothing shipped in Firefox or Safari. On top of that, the API is not designed for headless browsing, and a client can only find out that a site has tools by visiting it.",[84,87,90,93,96,99,102,105],{"id":85,"title":86},"what-it-is","What it is",{"id":88,"title":89},"how-it-works","How it works",{"id":91,"title":92},"getting-started","Getting started",{"id":94,"title":95},"tool-design","Designing tools that agents pick",{"id":97,"title":98},"security","Security and annotations",{"id":100,"title":101},"where-it-shingles","Where it falls short",{"id":103,"title":104},"verdict","Verdict",{"id":106,"title":107},"sources","Sources",[109,113,116,119,131,190,191,194,200,203,204,207,209,216,220,223,225,243,250,251,254,282,285,317,318,321,343,346,356,357,360,406,409,410,419,431,440,441],{"type":110,"content":111},"paragraph",[112],"WebMCP is a proposed web standard that lets a page publish typed, callable tools to an AI agent instead of leaving the agent to infer intent from the DOM. The proposal is small, the browser support is thin, and the design decision is the right one. Treat it as a specification to track and a progressive enhancement to build behind feature detection, not as an integration to depend on this quarter.",{"type":110,"content":114},[115],"It sits beside Model Context Protocol rather than against it. An MCP server exposes backend capabilities to any client, anywhere; WebMCP exposes the live, signed-in, in-tab state of a page to a browser-integrated agent, and disappears when the tab closes. The interesting engineering question is not which protocol wins. It is which one can make existing front-end logic reachable without writing a server for it.",{"type":117,"level":118,"id":85,"text":86},"heading",2,{"type":110,"content":120},[121,122,126,127,130],"The specification lives in the W3C Web Machine Learning Community Group and is written up in the WebMCP explainer on GitHub, where the Chrome and Edge teams are the visible implementers. Chrome ships it behind an origin trial from Chrome 149, with a local development flag at ",{"tag":123,"children":124},"code",[125],"chrome:\u002F\u002Fflags\u002F#enable-webmcp-testing",". The API is one object on ",{"tag":123,"children":128},[129],"Document",", and it comes in two forms.",{"type":132,"ordered":133,"items":134},"list",false,[135,155,157,173,175,188],[136,139,140,143,144,143,147,150,151,154],{"tag":123,"children":137},[138],"document.modelContext"," exposes ",{"tag":123,"children":141},[142],"registerTool()",", ",{"tag":123,"children":145},[146],"getTools()",{"tag":123,"children":148},[149],"executeTool()"," and a ",{"tag":123,"children":152},[153],"toolchange"," event.",[156],"A tool is a name, a natural-language description, a JSON Schema for its input, and an execute function that runs inside the page.",[158,159,143,162,143,165,168,169,172],"The declarative API turns an annotated form into a tool, using ",{"tag":123,"children":160},[161],"toolname",{"tag":123,"children":163},[164],"tooldescription",{"tag":123,"children":166},[167],"toolparamdescription"," and ",{"tag":123,"children":170},[171],"toolautosubmit",".",[174],"Tools are ephemeral. They exist only while the page is open and they run with the page's own cookies and session.",[176,177,180,181,184,185,172],"Both APIs are gated by the ",{"tag":123,"children":178},[179],"tools"," Permissions Policy, which defaults to ",{"tag":123,"children":182},[183],"self",", so cross-origin iframes stay off unless the host adds ",{"tag":123,"children":186},[187],"allow=\"tools\"",[189],"Tool metadata sits in the model's context window, which makes the number of registered tools a per-page budget rather than only a code concern.",{"type":117,"level":118,"id":88,"text":89},{"type":110,"content":192},[193],"Registration is one call from page script; invocation is the browser mediating between the agent and the page. The browser never hands the agent a DOM handle. It parses the arguments, calls your execute function, and passes the return value back as the tool result. That is the whole ergonomics story in one sentence: the page decides what is callable, and the browser decides who may call it.",{"type":195,"src":196,"alt":197,"wide":198,"caption":199},"image","\u002Fimages\u002Fblog\u002Fwebmcp\u002Fdiagram-7.webp","WebMCP tool lifecycle A page registers a tool on document.modelContext. The browser mediates: it lists the tools to the agent, the agent invokes one, the execute function runs in the page, the page updates its own DOM and state, and the result travels back through the browser to the agent.",true,"The browser sits between the agent and the page. Nothing else in the loop can widen that boundary.",{"type":110,"content":201},[202],"The practical difference from actuation shows up in the error path. A click on a mis-rendered element fails silently or fires the wrong handler. A tool call with a bad argument hits your own validation and returns a message the model can read and act on. Chrome's guidance makes this explicit and advises validating strictly in code while keeping the schema loose, because a schema rejection is a dead end for the agent.",{"type":117,"level":118,"id":91,"text":92},{"type":110,"content":205},[206],"The imperative API is the one to reach for, because it is the only one that can touch application state. A minimal read-only tool looks like this.",{"type":123,"code":208},"\u002F\u002F Feature-detect: the API is behind a flag or an origin trial.\nif (typeof document.modelContext?.registerTool !== 'function') return;\n\nconst controller = new AbortController();\n\nawait document.modelContext.registerTool({\n  name: 'get_order_status',\n  description: 'Return the shipping status of one order for the signed-in customer.',\n  inputSchema: {\n    type: 'object',\n    properties: {\n      orderNumber: { type: 'string', description: 'Order number as shown in the order list.' },\n    },\n    required: ['orderNumber'],\n    additionalProperties: false,\n  },\n  annotations: { readOnlyHint: true },\n  execute: async ({ orderNumber }) => {\n    const order = await orders.findForCustomer(orderNumber);\n    \u002F\u002F A message the model can act on beats a thrown Error.\n    if (!order) return `No order ${orderNumber} is visible to this account.`;\n    return `Order ${orderNumber}: ${order.status}, arriving ${order.eta}.`;\n  },\n}, { signal: controller.signal });\n\n\u002F\u002F Unregister on route change. From Chrome 153 aborting no longer breaks a\n\u002F\u002F call that is already running.\ncontroller.abort();",{"type":110,"content":210},[211,212,215],"Three details matter more than the rest. The description is the only thing the model reads to decide whether to call the tool, and Chrome's guidance caps it at 500 characters. The execute function should reuse the function the visible button already calls, so there is one code path and no chance of the interface and the tool disagreeing. The ",{"tag":123,"children":213},[214],"AbortSignal"," is the unregister path, not a timeout.",{"type":117,"level":217,"id":218,"text":219},3,"declarative-forms","Declarative forms",{"type":110,"content":221},[222],"The declarative API is for plain forms and nothing else. Annotate a form element and the browser derives the tool definition from the markup and the field names.",{"type":123,"code":224},"\u003Cform toolname=\"create_support_request\"\n      tooldescription=\"Submits a support request for the signed-in customer.\"\n      action=\"\u002Fsupport\"\n      toolautosubmit>\n  \u003Clabel for=\"subject\">Subject\u003C\u002Flabel>\n  \u003Cinput id=\"subject\" name=\"subject\" type=\"text\">\n\n  \u003Clabel for=\"reason\">Reason\u003C\u002Flabel>\n  \u003Cselect id=\"reason\" name=\"reason\" required\n          toolparamdescription=\"Determines which team handles the request.\">\n    \u003Coption value=\"returns\">Return a purchase\u003C\u002Foption>\n    \u003Coption value=\"delivery\">Where is my parcel\u003C\u002Foption>\n    \u003Coption value=\"site\">The website is broken\u003C\u002Foption>\n  \u003C\u002Fselect>\n\n  \u003Cbutton type=\"submit\">Submit\u003C\u002Fbutton>\n\u003C\u002Fform>",{"type":110,"content":226},[227,228,230,231,234,235,238,239,242],"Without ",{"tag":123,"children":229},[171]," the agent fills the form and the human presses submit, which is the right default for anything with a cost attached. With it, the browser submits and navigates, and ",{"tag":123,"children":232},[233],"respondWith()"," on the ",{"tag":123,"children":236},[237],"SubmitEvent"," lets the page return a result to the model instead. The ",{"tag":123,"children":240},[241],"agentInvoked"," flag tells the page which of the two paths it is on.",{"type":244,"variant":245,"title":246,"body":247},"callout","note","The cheap half of the standard",[248],[249],"The declarative API cannot reach a JavaScript-only feature, because there is no JavaScript in the tool definition. OpenAI's site-tools documentation states that its built-in browser does not implement the declarative API at all, so a form-only integration is invisible to ChatGPT Work and Codex today.",{"type":117,"level":118,"id":94,"text":95},{"type":110,"content":252},[253],"The published best-practice guidance is unusually prescriptive, and it is worth following literally. The framing is that a tool description is code that a probabilistic reader has to interpret once per session, and the character budgets exist to keep the whole catalogue inside that reader's attention.",{"type":255,"head":256,"rows":261},"table",[257,259],[258],"Budget",[260],"Recommended limit",[262,267,272,277],[263,265],[264],"Tool description",[266],"500 characters",[268,270],[269],"Parameter description",[271],"150 characters",[273,275],[274],"Tool name and parameter name",[276],"30 characters each",[278,280],[279],"One tool's output",[281],"1.5K characters",{"type":110,"content":283},[284],"The advice above the table is the harder half: one tool per function, no overlap between tools, and registration tied to page state rather than a static catalogue loaded on every page. Overlapping tools are the most common reason an agent picks the wrong one.",{"type":132,"ordered":133,"items":286},[287,293,298,303,312],[288,292],{"tag":289,"children":290},"strong",[291],"Single responsibility."," One function per tool, and no second tool that does almost the same thing.",[294,297],{"tag":289,"children":295},[296],"Register for the current state."," Abort the controller when the route or modal changes, so the agent does not see tools that no longer apply.",[299,302],{"tag":289,"children":300},[301],"Accept raw input."," If the user said 11:00 to 15:00, take the string and normalise it in code. Do not make the model do arithmetic.",[304,307,308,311],{"tag":289,"children":305},[306],"Use readable enum values."," \"express\" beats ",{"tag":123,"children":309},[310],"shipping_id = 1",", because the model reads the value, not your database.",[313,316],{"tag":289,"children":314},[315],"Return actionable errors."," Say what to do instead, not what broke internally. A tool that fails should still be usable.",{"type":117,"level":118,"id":97,"text":98},{"type":110,"content":319},[320],"Annotations are the main lever a page has over how a host treats its tools, and all four default to false. That default is the safe one, but it means the safety properties of a tool are exactly as honest as the page that registers it.",{"type":132,"ordered":133,"items":322},[323,328,333,338],[324,327],{"tag":123,"children":325},[326],"readOnlyHint"," the tool reads and changes nothing. Set it on every lookup.",[329,332],{"tag":123,"children":330},[331],"untrustedContentHint"," the return value contains user-generated or externally sourced data and needs delimiting before it reaches the model.",[334,337],{"tag":123,"children":335},[336],"consequentialHint"," the call books, pays, sends or deletes. Clients can use it to force a confirmation prompt.",[339,342],{"tag":123,"children":340},[341],"debugging"," from Chrome 156, marks a developer tool so general-purpose agents can filter it out.",{"type":110,"content":344},[345],"The Chrome security guidance treats indirect prompt injection as unsolved. Models are probabilistic, repeatable attacks against agentic systems exist, and a tool's return value is a channel an attacker can write to. The recommended mitigations are the annotations, the character budgets and origin isolation, which is mitigation rather than a fix. The same page notes that an extension holding host permissions can already drive the page with arbitrary JavaScript, with or without WebMCP.",{"type":244,"variant":347,"title":348,"body":349},"warn","exposedTo is the sharp edge",[350],[351,352,355],"The ",{"tag":123,"children":353},[354],"exposedTo"," option lists secure origins allowed to discover and execute a tool, and the guidance is blunt about it: a read-only tool can still leak user data, and a write tool acts on the user's behalf. Expose only to origins you would hand the credentials to.",{"type":117,"level":118,"id":100,"text":101},{"type":110,"content":358},[359],"The weaknesses come first, because they are the reason this is a watch item rather than a dependency. Support is effectively one engine family. The specification's own status file lists an origin trial in Chrome 149 and Edge 150, experimental support in Brave's Leo chat, support in ChatGPT Desktop, and standards-position entries in Firefox and WebKit with no implementation behind them. On top of that, Chrome's documentation names headless browsing as out of scope, warns that complex interfaces will need a refactor to keep application and interface state in sync, and points out that clients have to visit a site to discover it has tools at all. There is no registry, no manifest and no index.",{"type":255,"head":361,"rows":369},[362,364,365,367],[363],"",[9],[366],"MCP server",[368],"DOM actuation",[370,379,388,397],[371,373,375,377],[372],"Lifecycle",[374],"Tab-bound, gone on navigation",[376],"Persistent daemon",[378],"One request",[380,382,384,386],[381],"Sees",[383],"Live DOM, cookies, session",[385],"Only what you expose",[387],"Whatever the agent scrapes",[389,391,393,395],[390],"Runs your code",[392],"Yes, in the page",[394],"No, server-side only",[396],"No",[398,400,402,404],[399],"Reachable by",[401],"One engine family today",[403],"Any MCP client",[405],"Any browser",{"type":110,"content":407},[408],"The discoverability point deserves emphasis because it breaks the usual business case. A backend MCP server can be found by an agent that never visits the site. A WebMCP tool cannot. The only realistic discovery path today is a user opening the page, which means WebMCP competes on the quality of a session that has already started rather than on reach.",{"type":117,"level":118,"id":103,"text":104},{"type":110,"content":411},[412,413,418],"WebMCP is a well-drawn API for a real problem, and it is drawn better than most teams would have shipped it themselves. It is not yet a dependency. Build the tool layer behind a feature check, keep the human interface as the primary path, and revisit when a second engine ships or when the declarative half stops being Chromium-only. For the mechanics of getting a page agent-ready today, the site's own guide to ",{"tag":414,"to":415,"children":416},"link","\u002Fblog\u002Fwebmcp-agent-ready-website-guide",[417],"making a website agent-ready with declared tools"," covers the implementation detail in depth.",{"type":132,"ordered":198,"items":420},[421,423,425,427,429],[422],"Adopt it on product surfaces where a signed-in user and an agent are looking at the same page: booking, checkout, support requests, settings.",[424],"Skip it for read-only content work. A Markdown copy of the page is cheaper and needs no browser at all.",[426],"Skip it if your flows are mostly forms you cannot annotate, or if you cannot keep the interface in sync with the tool path.",[428],"Do not build an MCP server expecting WebMCP to replace it. They are different layers, and the useful architecture uses both.",[430],"Treat the character budgets and the single-responsibility rule as hard constraints on the catalogue, not as suggestions.",{"type":244,"variant":432,"title":433,"body":434},"tip","The cheapest first experiment",[435],[436,437,439],"Pick one existing button handler, wrap it in ",{"tag":123,"children":438},[142]," with the annotations set honestly, and measure whether the agent takes the tool path instead of the click path. That single measurement answers the adoption question faster than any amount of specification reading.",{"type":117,"level":118,"id":106,"text":107},{"type":132,"ordered":198,"items":442},[443,447,450,453,456,459,462,465],[444],{"tag":445,"href":32,"children":446},"a",[31],[448],{"tag":445,"href":35,"children":449},[34],[451],{"tag":445,"href":38,"children":452},[37],[454],{"tag":445,"href":41,"children":455},[40],[457],{"tag":445,"href":44,"children":458},[43],[460],{"tag":445,"href":47,"children":461},[46],[463],{"tag":445,"href":23,"children":464},[49],[466],{"tag":445,"href":52,"children":467},[51],[469,544,600,673],{"slug":470,"published":471,"minutes":472,"category":7,"tags":473,"keywords":479,"about":491,"sources":500,"cover":537,"og":538,"expertise":539,"locales":540,"lang":57,"title":541,"description":542,"coverAlt":543},"schema-org-structured-data-b2b-shop","2026-10-17",10,[474,475,476,477,478],"Schema.org","Structured data","B2B e-commerce","Technical SEO","Nuxt",[480,481,482,483,484,485,486,487,488,489,490],"schema.org b2b shop","product structured data b2b","merchant listing structured data","product snippet vs merchant listing","unitpricespecification referencequantity","productgroup variants structured data","organization schema vatid","b2b prices behind login seo","json-ld nuxt schema org","merchant return policy structured data","mpn gtin structured data",[492,494,497],{"name":474,"url":493},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSchema.org",{"name":495,"url":496},"JSON-LD","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FJSON-LD",{"name":498,"url":499},"Business-to-business","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FBusiness-to-business",[501,504,507,510,513,516,519,522,525,528,531,534],{"title":502,"url":503},"Google Search Central: Introduction to Product structured data","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Fproduct",{"title":505,"url":506},"Google Search Central: Merchant listing (Product, Offer) structured data","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Fmerchant-listing",{"title":508,"url":509},"Google Search Central: Product snippet structured data","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Fproduct-snippet",{"title":511,"url":512},"Google Search Central: Product variant structured data","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Fproduct-variants",{"title":514,"url":515},"Google Search Central: Organization structured data","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Forganization",{"title":517,"url":518},"Google Search Central: Merchant return policy structured data","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Freturn-policy",{"title":520,"url":521},"Google Search Central: General structured data guidelines","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Fsd-policies",{"title":523,"url":524},"Google Search Central: Generate structured data with JavaScript","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Fgenerate-structured-data-with-javascript",{"title":526,"url":527},"Schema.org: Offer","https:\u002F\u002Fschema.org\u002FOffer",{"title":529,"url":530},"Schema.org: UnitPriceSpecification","https:\u002F\u002Fschema.org\u002FUnitPriceSpecification",{"title":532,"url":533},"Unhead: Product schema (defineProduct)","https:\u002F\u002Funhead.unjs.io\u002Fdocs\u002Fschema-org\u002Fapi\u002Fschema\u002Fproduct",{"title":535,"url":536},"Nuxt SEO: Schema.org identity setup","https:\u002F\u002Fnuxtseo.com\u002Fdocs\u002Fschema-org\u002Fguides\u002Fsetup-identity","\u002Fimages\u002Fblog\u002Fschema-org-structured-data-b2b-shop\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fschema-org-structured-data-b2b-shop\u002Fog.jpg","b2b-ecommerce-developer",[57,58,59],"Schema.org structured data for B2B shops: prices behind logins, units and variants","Product markup for B2B shops: snippet or merchant listing, which price to publish, part numbers, unit pricing, variants, Organization and SSR in Nuxt.","A product rectangle with a framed outline and a small price slot hidden behind a low wall.",{"slug":545,"published":471,"minutes":546,"category":7,"tags":547,"keywords":552,"about":564,"sources":572,"cover":594,"og":595,"expertise":539,"locales":596,"lang":57,"title":597,"description":598,"coverAlt":599},"ai-assistant-b2b-shop-customer-support",9,[476,548,549,550,551],"AI assistant","Customer support","LLM agents","EU AI Act",[553,554,555,556,557,558,559,560,561,562,563],"ai assistant b2b shop","ai customer support b2b ecommerce","llm chatbot order status","ai agent customer service tools","chatbot liability air canada","klarna ai assistant results","human handoff chatbot design","eu ai act chatbot disclosure","owasp excessive agency","ai support resolution rate","b2b customer portal chatbot",[565,568,571],{"name":566,"url":567},"Customer service","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCustomer_service",{"name":569,"url":570},"Chatbot","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FChatbot",{"name":498,"url":499},[573,576,579,582,585,588,591],{"title":574,"url":575},"Klarna: AI assistant handles two-thirds of customer service chats in its first month (press release, 2024)","https:\u002F\u002Fwww.klarna.com\u002Finternational\u002Fpress\u002Fklarna-ai-assistant-handles-two-thirds-of-customer-service-chats-in-its-first-month\u002F",{"title":577,"url":578},"Entrepreneur: Klarna is hiring customer service agents again, according to its CEO (May 2025)","https:\u002F\u002Fwww.entrepreneur.com\u002Fbusiness-news\u002Fklarna-ceo-reverses-course-by-hiring-more-humans-not-ai\u002F491396",{"title":580,"url":581},"ABA Business Law Today: BC tribunal confirms companies remain liable for information provided by AI chatbot","https:\u002F\u002Fwww.americanbar.org\u002Fgroups\u002Fbusiness_law\u002Fresources\u002Fbusiness-law-today\u002F2024-february\u002Fbc-tribunal-confirms-companies-remain-liable-information-provided-ai-chatbot\u002F",{"title":583,"url":584},"OWASP GenAI Security Project: LLM06:2025 Excessive Agency","https:\u002F\u002Fgenai.owasp.org\u002Fllmrisk\u002Fllm062025-excessive-agency\u002F",{"title":586,"url":587},"OWASP GenAI Security Project: LLM05:2025 Improper Output Handling","https:\u002F\u002Fgenai.owasp.org\u002Fllmrisk\u002Fllm052025-improper-output-handling\u002F",{"title":589,"url":590},"EU AI Act, Article 50: Transparency obligations","https:\u002F\u002Fartificialintelligenceact.eu\u002Farticle\u002F50\u002F",{"title":592,"url":593},"Intercom: Fin pricing and billable outcomes","https:\u002F\u002Ffin.ai\u002Fpricing","\u002Fimages\u002Fblog\u002Fai-assistant-b2b-shop-customer-support\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fai-assistant-b2b-shop-customer-support\u002Fog.jpg",[57,58,59],"An AI assistant for B2B shop support: tools, permissions and the human handoff","How to build an AI support assistant for a B2B shop: which requests to automate, tools that run in the customer context, handoff, AI Act and metrics.","A tall wall with one narrow gap; an arrow passes through it into a boxed enclosure of three squares, and another leads down to a tray.",{"slug":601,"published":471,"minutes":6,"category":7,"tags":602,"keywords":607,"about":619,"sources":629,"cover":666,"og":667,"expertise":668,"locales":669,"lang":57,"title":670,"description":671,"coverAlt":672},"vue-ai-chat-ui-streaming-accessibility",[603,478,604,605,606],"Vue","Accessibility","Streaming","AI chat",[608,609,610,611,612,613,614,615,616,617,618],"vue ai chat ui","nuxt llm streaming","sse vs fetch streaming","aria-live streaming text","accessible chat interface","abort fetch stream vue","dompurify markdown llm","role log chat","reduced motion chat ui","wcag 2.2 chat","screen reader streaming",[620,623,626],{"name":621,"url":622},"Web accessibility","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWeb_accessibility",{"name":624,"url":625},"Server-sent events","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FServer-sent_events",{"name":627,"url":628},"Vue.js","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FVue.js",[630,633,636,639,642,645,648,651,654,657,660,663],{"title":631,"url":632},"MDN: Using server-sent events","https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FServer-sent_events\u002FUsing_server-sent_events",{"title":634,"url":635},"MDN: AbortController","https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FAbortController",{"title":637,"url":638},"MDN: ReadableStream","https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FReadableStream",{"title":640,"url":641},"MDN: TextDecoderStream","https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FTextDecoderStream",{"title":643,"url":644},"MDN: ARIA log role","https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAccessibility\u002FARIA\u002FReference\u002FRoles\u002Flog_role",{"title":646,"url":647},"W3C: Understanding SC 4.1.3 Status Messages","https:\u002F\u002Fwww.w3.org\u002FWAI\u002FWCAG22\u002FUnderstanding\u002Fstatus-messages.html",{"title":649,"url":650},"W3C WAI: What's new in WCAG 2.2","https:\u002F\u002Fwww.w3.org\u002FWAI\u002Fstandards-guidelines\u002Fwcag\u002Fnew-in-22\u002F",{"title":652,"url":653},"MDN: prefers-reduced-motion","https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FCSS\u002F@media\u002Fprefers-reduced-motion",{"title":655,"url":656},"DOMPurify README","https:\u002F\u002Fgithub.com\u002Fcure53\u002FDOMPurify",{"title":658,"url":659},"markdown-it README","https:\u002F\u002Fgithub.com\u002Fmarkdown-it\u002Fmarkdown-it",{"title":661,"url":662},"AI SDK docs: useChat","https:\u002F\u002Fai-sdk.dev\u002Fdocs\u002Fai-sdk-ui\u002Fchatbot",{"title":664,"url":665},"Vue docs: nextTick","https:\u002F\u002Fvuejs.org\u002Fapi\u002Fgeneral.html#nexttick","\u002Fimages\u002Fblog\u002Fvue-ai-chat-ui-streaming-accessibility\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fvue-ai-chat-ui-streaming-accessibility\u002Fog.jpg","vue-nuxt-developer",[57,58,59],"Streaming AI chat in Vue and Nuxt: accessible by design","How to build a Vue or Nuxt AI chat UI: fetch streams, abort and retry, safe markdown rendering, aria-live announcements, keyboard support and reduced motion.","A message card with a careful caret, a dotted line opening into ripples and a check.",{"slug":674,"published":471,"minutes":546,"category":7,"tags":675,"keywords":680,"about":692,"sources":702,"cover":735,"og":736,"expertise":668,"locales":737,"lang":57,"title":738,"description":739,"coverAlt":740},"llm-translation-pipeline-i18n",[676,677,678,603,679],"Translation","i18n","LLM","Localization",[681,682,683,684,685,686,687,688,689,690,691],"llm translation pipeline","icu messageformat placeholders","vue i18n llm translation","translation memory llm","glossary enforcement translation","german informal sie du","hungarian translation quality llm","structured output json translation","machine translation post-editing","locale file validation","cldr plural categories",[693,696,699],{"name":694,"url":695},"Internationalization and localization","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FInternationalization_and_localization",{"name":697,"url":698},"ICU MessageFormat","https:\u002F\u002Funicode-org.github.io\u002Ficu\u002Fuserguide\u002Fformat_parse\u002Fmessages\u002F",{"name":700,"url":701},"Vue I18n","https:\u002F\u002Fvue-i18n.intlify.dev\u002F",[703,705,708,711,714,717,720,723,726,729,732],{"title":704,"url":698},"Unicode ICU User Guide: Formatting Messages",{"title":706,"url":707},"Vue I18n: Message Format Syntax","https:\u002F\u002Fvue-i18n.intlify.dev\u002Fguide\u002Fessentials\u002Fsyntax",{"title":709,"url":710},"Nuxt i18n: Getting started","https:\u002F\u002Fi18n.nuxtjs.org\u002Fdocs\u002Fgetting-started",{"title":712,"url":713},"Unicode CLDR: Plural rules","https:\u002F\u002Fcldr.unicode.org\u002Findex\u002Fcldr-spec\u002Fplural-rules",{"title":715,"url":716},"OpenAI: Structured outputs","https:\u002F\u002Fplatform.openai.com\u002Fdocs\u002Fguides\u002Fstructured-outputs",{"title":718,"url":719},"DeepL API: Glossaries","https:\u002F\u002Fdevelopers.deepl.com\u002Fdocs\u002Fapi-reference\u002Fglossaries",{"title":721,"url":722},"W3C: Localization vs. internationalization","https:\u002F\u002Fwww.w3.org\u002FInternational\u002Fquestions\u002Fqa-i18n",{"title":724,"url":725},"GNU gettext manual","https:\u002F\u002Fwww.gnu.org\u002Fsoftware\u002Fgettext\u002Fmanual\u002Fgettext.html",{"title":727,"url":728},"RFC 5646: Tags for Identifying Languages","https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc5646",{"title":730,"url":731},"Rat für deutsche Rechtschreibung","https:\u002F\u002Fwww.rechtschreibrat.com\u002F",{"title":733,"url":734},"Microsoft Writing Style Guide","https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fstyle-guide\u002Fwelcome\u002F","\u002Fimages\u002Fblog\u002Fllm-translation-pipeline-i18n\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fllm-translation-pipeline-i18n\u002Fog.jpg",[57,58,59],"An LLM translation pipeline for i18n: glossary, placeholders and review","How to translate product and website strings with LLMs without breaking placeholders, plurals or tone: glossary, ICU messages, QA checks and review gates.","Three source cards, one translated card with a glossary ribbon, then a checked clipboard.",1792261155027]