The chatbot trap Open any product changelog from the last year and you'll find the same move: a chat bubble in the corner, a sparkle icon, a launch post with the word AI in the headline. The widget answers questions about the product. Sometimes it drafts an email. Everyone ships it, everyone calls it AI-native, and almost everyone is solving the wrong layer. A chat widget is a conversation about your product. It sits on top of the UI and paraphrases your docs. The model never touches your domain. Ask it to actually do the thing — issue the credit note, reconcile the bank line, file the quarterly return — and it hands you a link and wishes you luck. You didn't build an AI-native product. You bolted a support bot onto a CRUD app. The tell is simple: if you removed the chat widget, would the software be any more or less operable by an agent? If the answer is no, the AI is decoration. The real question Stop asking "can a user chat about my product." Ask "can an agent operate my product." Operate means run state-changing work against live data, unattended, with the same guarantees your API gives a first-class integration. Not "summarize my invoices." Create one, email it, mark it paid, and issue a correction if the client disputes it — as discrete, callable actions with typed inputs and typed results. That reframing moves the work down a layer, out of the presentation tier and into your domain. And it exposes an uncomfortable fact: most products have no clean surface for a caller that isn't a human clicking buttons or a bespoke integration someone hand-wired. The agent needs something in between — narrow, typed, safe to call, safe to retry. The pattern: expose your domain as typed tools The pattern: expose each domain operation as a tool — a named function with a typed input schema, a typed output schema, and a one-line description the model reads to decide when to call it. The model doesn't parse your HTML or guess your REST routes. It sees a catalog of capabilities and picks one. This is what the Model Context Protocol (MCP) standardizes. Instead of every product inventing its own agent interface, MCP defines a transport and a shape: tools, resources, and prompts — all discoverable, with tools callable, over stdio or Streamable HTTP. Write your domain as MCP tools once, and any MCP-capable client can drive it. The shape of the pattern, not production code: server.registerTool( "create_invoice", { description: "Create a draft invoice for a client with line items. Returns the invoice id and computed totals.", inputSchema: { clientId: z.string(), currency: z.enum(["EUR", "USD"]), lines: z .array( z.object({ description: z.string(), quantity: z.number().positive(), unitPrice: z.number().nonnegative(), taxRate: z.number().min(0).max(1), }), ) .min(1), idempotencyKey: z.string().uuid(), }, outputSchema: { invoiceId: z.string(), status: z.literal("draft"), subtotal: z.number(), taxTotal: z.number(), total: z.number(), }, }, async ({ clientId, currency, lines, idempotencyKey }, extra) => { const actor = await requireScope(extra, "invoices:write"); // per-agent auth const out = await invoices.create({ clientId, currency, lines, idempotencyKey, actor }); return { content: [{ type: "text", text: JSON.stringify(out) }], structuredContent: out, // validated against outputSchema before it leaves the server }; }, ); Enter fullscreen mode Exit fullscreen mode The description is not a comment — it's part of the contract. The model reads it to route. The schemas are not documentation — they're the guardrails that keep a probabilistic caller inside a deterministic system. And note the return shape: the SDK validates structuredContent against your outputSchema before it leaves the server. That validation is the whole point. The hard parts engineers hit The tool registration is the easy 20%. The 80% is everything that makes an agent-driven call safe, and this is where most AI-native launches quietly fall apart. Per-agent auth and scoping. A human session and an agent session are not the same threat model. Each agent identity needs its own credential and its own scope — invoices:write should not imply payroll:read. Bearer keys or an OAuth 2.1 authorization-code flow with PKCE both work; what matters is that the tool layer checks scope per call, not per connection, and that you can revoke one agent without nuking the others. Idempotency. An agent will retry. It times out, it re-plans, it fires the same call twice because a network blip ate the response. If create_invoice isn't idempotent, your customer now has two invoices and one very confused client. Every state-changing tool needs an idempotency key and server-side dedup — the boring plumbing that human UIs get away with skipping because humans don't retry at machine speed. Typed contracts. The outputSchema is what lets the model chain calls without hallucinating the shape of your data. Return structured JSON, not prose. For list operations, return real pagination — { data, total, limit, offset } — so the agent can page deterministically instead of guessing whether it saw everything. Audit trail. When a non-human actor mutates financial or fiscal state, "who did this and why" is not optional. Every tool call needs an actor, a timestamp, and enough context to reconstruct the decision. This is also your incident-response surface when an agent does something dumb — and it will. Deterministic, recoverable errors. A stack trace is useless to a model. A well-designed error tells the agent what went wrong and what to do next: client_not_found (go create the client first), rate_limited (back off), validation_failed with the offending field. The model can recover from a typed error. It cannot recover from a 500 and a wall of HTML. Get these five right and you have an agent-native product. Skip them and you have a demo that works once on stage. A real example: an ERP an agent can run At Frihet — an AI-native ERP — we treat the MCP server as a first-class product surface. It's open source at github.com/Frihet-io/frihet-mcp, ships on npm as @frihet/mcp-server, and exposes 157 tools across the ERP domain: invoices, expenses, clients, CRM, products, quotes, banking, e-invoicing, time tracking, payroll, period close, and Spanish and Canary Islands tax models. Every tool is a CRUD operation over the REST API that returns typed JSON via an outputSchema; list tools return paginated { data, total, limit, offset }. An agent can create an invoice with line items, email it as a PDF, mark it paid, and issue a credit note if the client disputes it — one chained flow, four discrete tools. It can log and search expenses, draft and send quotes, categorize bank transactions and match them to invoices. The fiscal surface is where the domain depth shows. It prepares Spanish tax filings — quarterly IVA (Modelo 303) and IRPF (Modelo 130), the annual 390 and 347 summaries — plus IGIC for the Canary Islands. It checks VeriFactu submission status and resubmits rejected records, generates EN16931 e-invoices, and covers TicketBAI. The auth model is the one from the hard-parts section: an API key as a Bearer token (keys are prefixed fri_), or a browser OAuth login that provisions one — pick per client. That's not incidental. It's the difference between a demo and something you'd let run against production books. The point isn't the tool count. It's that the domain is exposed as typed, structured, paginated operations — so an agent operates the ERP, it doesn't chat about it. Is your product agent-native? A short checklist. If you can't answer yes, you have a chat widget, not an AI-native product. Tools, not text. Can an agent list your core capabilities as typed functions with input and output schemas? Operate, not observe. Can it change state — create, send, correct — not just read and summarize? Scoped identity. Does each agent get its own credential with per-call scope, independently revocable? Idempotent writes. Does every mutating call take an idempotency key and dedup server-side? Structured results. Do you return typed JSON with real pagination, not prose the model has to parse? Recoverable errors. Do failures name what went wrong and what to do next, deterministically? Audit trail. Can you reconstruct who-did-what for every non-human mutation? AI-native isn't a layer you paint on top. It's the interface underneath — your domain, exposed as tools an agent can call. Build that surface and the chatbot becomes a rounding error. Skip it and the chatbot is all you ever had.
AI-native isn't a chatbot. It's an MCP server.
Full Article
Original Source
Read the full article at Dev →KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.