fayzfayz sdk
PTEN

Your app as a data layer for AI

Every app Fayz generates is born agent-ready. It isn't a feature you switch on later: the same manifest that describes screens, routes and data also describes, as a typed contract, what an AI is allowed to read and do in that app. An agent that understands the contract can check the calendar, summarize the books or adjust inventory — without you writing an AI endpoint from scratch.

This page covers the aiTools contract, what you get for free, how the chat shell connects to an LLM, and how your own plugin joins this layer.

The aiTools contract

Any plugin can declare an aiTools field in its manifest (PluginManifest.aiTools?: PluginAITool[], in @fayz-ai/core). Each tool is a function an agent can call, described in a format LLMs understand natively — the same function calling shape OpenAI and Anthropic use:

type PluginAITool = {
  id: string            // 'agenda.create-appointment'
  name: string          // 'createAppointment' — the name the LLM calls
  description: string    // what the tool does, in natural language
  mode: 'read' | 'persist'   // does it only read data, or write/change state?
  parameters?: {         // JSON Schema: type:'object', properties, required
    type: 'object'
    properties: Record<string, { type: string; description?: string }>
    required?: string[]
  }
  permission?: { feature: string; action: 'read' | 'create' | ... }
  suggestions?: { label: string }[]   // suggestion chips in the chat
  category?: string
}

Two details matter:

  • mode separates reads from writes. read queries; persist creates or changes state. The shell uses this to filter tools, and so you can require confirmation before any persist.
  • permission binds the tool to access control. The same feature and permission that guards the screen guards the tool. An agent running as a given user only sees the tools that user could use.

14 of the 22 plugins already declare aiTools today — agenda, conversations, crm, dashboard, financial, forms, inventory, marketing, menu, orders, shop, tables, tasks and admin. Enable the plugin and you get its tools in the contract.

Real examples from the SDK

These are tools that exist in plugin code today — not invented examples:

PluginToolmodeWhat it does
agendalistAppointmentsreadLists appointments for a date or period, filterable by professional.
agendacreateAppointmentpersistCreates an appointment for a client with a professional and a service.
agendacheckAvailabilityreadChecks a professional's free slots on a given date.
menutoggleMenuItemAvailabilitypersistMarks a menu item as available or sold out.
financialgetRevenuereadReturns revenue for a period.
orderscreateOrderpersistOpens a new order.
inventorygetLowStockreadLists items below the reorder point.
conversationssendMessagepersistSends a message in a conversation.
dashboardgetKpiSummaryreadSummarizes the business KPIs.

What you get for free: core tools + registries

On top of what each plugin declares, the Fayz shell (@fayz-ai/saas) injects three core tools that are always present, defined in core-ai-tools.ts:

  • getBusinessSummary (read) — the business at a glance.
  • getTeamMembers (read) — who's on the team.
  • navigateTo (read) — takes the user to a screen.

And there's a multiplier: every non-readonly registry (entity) in your app automatically becomes a list<Entity> tool. Register products and you get listProducts without writing anything — the comment in the code says it literally: "Each registry gets a basic 'list' tool so plugins get AI capabilities for free." Your entities become queryable by an agent just by existing.

The chat shell — and the honest boundary

The shell ships the UI: a chat FAB and a panel (ChatFab, ChatPanel), with suggestion chips sourced from aiTools and filtering by permission and vertical via the useAITools hook. Users see the right tools and ready-made suggestions.

But useChat is bring-your-own-endpoint (BYO endpoint). It POSTs the conversation history to an apiEndpoint in OpenAI format that you configure. With no endpoint, the response is a demo mock.

The SDK does not execute tools. PluginAITool has no handler — it is a schema, not an implementation. Receiving the tool_call from the LLM, running the action against Supabase (honoring RLS) and returning the result is the job of the app owner's backend. The SDK gives you the contract and the UI; the execution loop is yours. Never assume Fayz "runs the agent" for you.

The target architecture, honest about what exists:

┌──────────────┐   history (tools stay in your backend)   ┌────────────────────┐
│  ChatPanel   │ ───────────────────────────────────────▶ │  your apiEndpoint  │
│  (shell UI)  │                                          │   (your backend)   │
│  useAITools  │ ◀─────────────────────────────────────── │  + LLM (Claude/…)  │
└──────────────┘        response / tool_call              └─────────┬──────────┘
       ▲                                                            │ runs the tool
       │ aiTools schemas                                            ▼
       │ (from the manifest)                              ┌────────────────────┐
       └───────────────────────────────────────────────── │  Supabase + RLS    │
                                                          │  (tenant data)     │
                                                          └────────────────────┘

The shell supplies the schemas (what exists) and the UI. Your endpoint supplies the LLM and the execution. RLS guarantees the agent only touches the right tenant's data.

Practical scenarios

Written as target architecture on top of what already exists — the schemas are ready; you just need to plug in the endpoint:

  • An assistant that checks the calendar and books. The LLM receives listAppointments, checkAvailability and createAppointment. "Can you fit Sarah in tomorrow morning?" → it calls checkAvailability, picks a slot and proposes createAppointment (which is persist — so you ask for confirmation).
  • Financial summaries over chat. getRevenue + getKpiSummary answer "how was this week?" with real tenant numbers, not estimates.
  • An inventory agent. getLowStock finds what's running out; a restock persist (once you declare one) closes the loop.

A real aiTool, in JSON

The schema your endpoint hands to the LLM — adapted from the real createAppointment in plugin-agenda:

{
  "name": "createAppointment",
  "description": "Creates a new appointment for a client with a specific professional and service.",
  "parameters": {
    "type": "object",
    "properties": {
      "client":       { "type": "string", "description": "Client name" },
      "professional": { "type": "string", "description": "Professional name" },
      "service":      { "type": "string", "description": "Service name" },
      "date":         { "type": "string", "description": "Date (YYYY-MM-DD)" },
      "time":         { "type": "string", "description": "Time (HH:MM)" }
    },
    "required": ["client", "professional", "service", "date", "time"]
  }
}

mode: 'persist' and permission stay in the manifest (they never reach the LLM) — they're what the shell and your backend use for gating and confirmation.

How your own plugin joins this layer

It's simple: declare aiTools in the manifest. fayz create plugin already emits the (empty) array — you just fill it in. A well-built tool:

  1. Has a clear verb-noun name (listLeads, createOrder).
  2. Declares an honest modepersist for anything that changes state.
  3. Describes parameters with a description on every field (the LLM reads them).
  4. Ties permission to the same feature as the screen.

Once that's done, your plugin shows up in the chat, in the suggestions and in the contract the agent reads. For the full field list, see the Plugin manifest reference. To connect an agent to this Dev Center, see Connect your agent.