# Welcome to nao Source: https://docs.getnao.io/index The \#1 Open-Source Analytics Agent nao is a framework to build and deploy analytics agents. Create the context of your analytics agent with nao-core CLI: data, metadata, modeling, rules, etc.\ Deploy a UI for anyone to chat with your agent and run analytics on your data. Star us on GitHub and contribute to the project ## Key Features ### For Data Teams Create a file-system like context for your agent. Add anything you want: data, metadata, docs, tools, MCPs. No limit. Works with any data warehouse, stack, type of context, LLM. Unit test your agent performance before deploying. Version the context and track performance over time. Self-host your analytics agent and use your own LLM keys to guarantee maximum security. ### For Business Users Ask questions in plain English, get analytics straight away. Create and customize visualizations directly in the chat interface. See the agent reasoning and sources clearly. Send feedback to the data team when an answer is right or wrong. ## Get Started Deploy your first analytics agent in 1 minute # Agent Settings Source: https://docs.getnao.io/nao-agent/chat/admin/agent-settings Choose the models for background tasks, tune per-model inference parameters, and cap Python execution Admins can choose which models run nao's background work, tune how the agent calls each model, and cap how long it is allowed to run Python. All three live in the admin panel under **Settings** -> **Project**. ## Default models Beyond answering chats, nao calls an LLM for a handful of background tasks that have no model picker of their own. The **Default models** card on **Settings** -> **Project** -> **Models** decides which model each of them uses. Two modes: * **One default for everything** - pick a single model for every background task. * **A model per task** - pick a model per task, so you can send cheap work to a small model and keep a capable one where accuracy matters. | Task | What it does | | --------------------------- | ----------------------------------------------------------------------------------------------------- | | **Live story refresh** | Rewrites [live story](/nao-agent/chat/capabilities/stories) narratives when their data refreshes | | **Title generation** | Names chats and automations from their first prompt | | **Conversation compaction** | Summarizes long conversations to keep them within the context window | | **Context recommendations** | Analyzes past chats to suggest [context improvements](/nao-agent/context-engineering/recommendations) | | **Other tasks** | Small background helpers such as natural-language schedule parsing and memory extraction | Leave a task on **nao default** to use the built-in choice. Title generation and compaction run on almost every conversation, so they are the two worth pointing at a cheap model first. If you later remove the provider or the model, the card flags the selection with a warning icon and nao falls back to another available model until you pick a new one. ## Model parameters Each enabled model can carry its own inference settings, stored per project and per provider. Models without overrides use the provider's defaults. 1. Go to **Settings** -> **Project** -> **Models**. 2. Add or edit a provider. 3. In **Enabled Models**, click the **...** button on an enabled model to open the **Model parameters** dialog. 4. Set the parameters you want and click **Save**. Custom models you added yourself have the same edit button. Clearing every field removes the overrides for that model. ### Available parameters The dialog only shows the controls the selected model actually accepts, so the list changes from model to model: | Parameter | Applies to | | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Thinking effort** | Models with adaptive thinking (recent Claude, GPT-5.x, Gemini 3.x, OpenRouter, Bedrock Claude 4.6+). The available levels depend on the model. | | **Thinking budget (tokens)** | Models with budget-based extended thinking (Claude 4.5 era, Gemini 2.5, legacy Bedrock Claude). | | **Temperature**, **Top P**, **Top K** | Sampling models. Upper bounds are model-specific, and **Top K** only appears where the model supports it. | | **Max output tokens** | Most models. | | Provider extras | Provider-specific controls, for example **Parallel tool calls**, **Send reasoning back**, **Speed** and **Inference geography** on Anthropic, **Text verbosity**, **Reasoning summary**, **Max tool calls** and **Service tier** on OpenAI, **Include thoughts**, **Safety threshold** and **Media resolution** on Google, **Safe prompt** and document limits on Mistral. | On Claude models (direct, or through Vertex or Bedrock), **Top P** and **Temperature** are mutually exclusive - when both are set, nao sends only the temperature. Values outside a model's accepted range are clamped before the call. Thinking effort and max output tokens directly change token spend. See [Budgets](/nao-agent/chat/admin/budgets) to cap that spend per provider. ## Python execution duration When the Python sandbox is enabled, you can cap how long a single Python execution may run. 1. Go to **Settings** -> **Project** -> **Agent**. 2. In the **Experimental** card, set **Python execution duration**. The value is in seconds, defaults to **30**, and must be between **1** and **600**. Code that runs longer is stopped. # Budgets Source: https://docs.getnao.io/nao-agent/chat/admin/budgets Cap LLM spend per provider and per user, with alerts and automatic cutoffs Admins can set a dollar limit on how much each configured LLM provider can spend over a fixed period, and how much any single user can spend against it. When a limit is reached, nao blocks further LLM calls across every channel (web chat, Slack, Microsoft Teams, Telegram, WhatsApp, Mattermost). Budgets are admin-only. Open the admin panel and go to **Settings** -> **Project** -> **Budgets**. ## Set a budget 1. Navigate to **Settings** -> **Project** -> **Budgets**. 2. Pick a provider (Anthropic, OpenAI, Azure OpenAI, Google, Mistral, AWS Bedrock, OpenRouter, Ollama). Providers declared in `nao_config.yaml` are listed here alongside the ones configured in the UI. 3. Enter a **project limit** in dollars, a **per-user limit**, or both. 4. Choose a reset period: **day**, **week**, or **month**. 5. Save. Each provider can have its own budget. Providers without a budget are unlimited. ### Per-user limits A project limit caps what the whole team can spend on a provider. A per-user limit caps what any one person can spend against that same provider, in the same period. The two are independent, and both are enforced: * A user who hits their own limit is blocked while everyone else keeps working. * When the project limit is reached, everyone is blocked regardless of individual headroom. Use a per-user limit to stop one heavy user (or a runaway automation) from consuming the whole team's budget before the period resets. Per-user limits are gated by the `user-budget` feature on an Enterprise license. Without it, only the project limit applies. See [Enterprise overview](/nao-agent/enterprise/overview#installing-the-license). ### Setting budgets in `nao_config.yaml` Budgets can also be declared next to the provider in your project config, so they are version-controlled with the rest of your context: ```yaml theme={null} llm: providers: - provider: anthropic api_key: ${{ env('ANTHROPIC_API_KEY') }} budget: limit: 500 # project-wide spend limit in USD for the period per_user_limit: 50 # per-user spend limit in USD for the period period: month # day, week, or month (defaults to month) ``` At least one of `limit` and `per_user_limit` is required. `period` defaults to `month`. These budgets show up on the Budgets page like any other. ## How spend is tracked For every LLM call, nao records the cost computed from the provider's token pricing and attributes it to the provider's current period. The Budgets page shows, per provider: * The configured limit and reset period * Current-period spend * Percentage of the limit used Spend resets to zero at the start of the next period. ## Alerts and cutoff Warnings and cutoffs are evaluated against whichever limit is closest to being reached - the project one or the user's own. * **80% to 100% used**: a banner appears at the top of the chat, warning that the limit is close. It names which budget is running out ("your Anthropic budget" or "your personal Anthropic budget") and when it resets. * **100% reached**: nao blocks new LLM calls for that provider until the next reset. Someone who hits only their personal limit is blocked on their own; when the project limit is reached, everyone is. * **On project cutoff**: nao emails the organization admin(s) so they can raise the limit or wait for the reset. The cutoff applies uniformly across channels: web chat, Slack, Microsoft Teams, Telegram, WhatsApp, and Mattermost all stop issuing calls to the blocked provider. ## Unblocking To resume traffic before the period resets, either: * Raise the limit (project or per-user) on the Budgets page, or * Switch the default model to another provider that still has budget available. To reduce spend rather than cap it, tune the per-model thinking effort and max output tokens under [Agent Settings](/nao-agent/chat/admin/agent-settings#model-parameters). # File Explorer Source: https://docs.getnao.io/nao-agent/chat/admin/file-explorer Browse, search, and edit your project's context files from the admin panel Open it from **Settings** -> **File Explorer**. Available to **Admin** and **Context Admin** roles only. Editing also needs a [connected context repository](/nao-agent/chat/admin/git), either connected from the UI or supplied by the deployment through `NAO_CONTEXT_SOURCE=git` - until then, every file is read-only. New to editing context files? The [Contributor Guide](/nao-agent/context-engineering/contributing) covers what belongs where, which files are safe to edit, and how the agent finds what you write. ## Browse Search covers file contents as well as names, and multi-word searches match in any order. Results truncate after 5 seconds of loading or 200 files searched. When you open a result that matched on content, nao jumps to the match and highlights it. You should toggle off the file contents search if your context repo is large. The content of files over 1 MB, non-UTF-8 files, `.git`, `.env`, and `.naoignore` is not searched. `.git` and `.env` are not browsable or writable at all. Markdown files open as the rendered page. Click **Source** to put the raw text beside it and edit with a live preview - nao remembers whether you keep it open. Each file also shows an estimated token count, so you can see what a context file actually costs the agent before deciding to split it. ## Edit Saving (`Cmd+S`) writes to your own private copy of the repository, not the live context files that the agent sees. Your team won't see your changes either until your pull request is merged. ## Commit and open a pull request Saved edits collect in the **Git** panel under the file tree, where you can see the changes and choose which to commit. Discarding changes permanently throws away uncommitted edits. Commits are authored as you, with nao as co-author. Committing from the main branch creates another branch so that pushing creates a PR that can then be reviewed. A branch's first push opens a pull request. Later pushes update that same pull request rather than opening another. Once it's merged, the files will reach the agent after a redeploy, a scheduled sync, or a pull. ## Why a file is read-only When a file can't be edited, nao says why. | What you see | What to do | | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | "This file is generated by nao sync. Add human notes in annotations.md." (or "Change its source instead.") | Put your notes in `annotations.md`, or change the source it's generated from | | "This file is rendered from a Jinja template. Edit the template instead." | Edit the `.j2` template it's rendered from | | "This path is replaced by nao sync. Change its source in nao\_config.yaml." | Change the source in `nao_config.yaml` | | "This file can't be edited because it isn't in the connected repository. Add it there to make it editable." | Commit the file to the context repository | | "No context repository is connected. Open repository setup to connect one." | [Connect a repository](/nao-agent/chat/admin/git) | | "Connect your GitHub account before using Git actions in the context explorer." | [Connect your own account](/nao-agent/chat/admin/git) | | "GitHub is not configured for this instance. Add the GitHub client credentials first." | Ask whoever deploys nao to set the [server keys](/nao-agent/chat/admin/git) | | "Add an access token or SSH deploy key to edit context files." | On a `NAO_CONTEXT_SOURCE=git` deployment, set `NAO_CONTEXT_GIT_TOKEN` or `NAO_CONTEXT_GIT_SSH_KEY` - see [Option B](/nao-agent/chat/admin/git#option-b-edit-the-deployments-own-repository) | | "Repository status is temporarily unavailable." | Retry - this is usually transient | | "No tracked nao\_config.yaml was found in the connected repository." | Commit a `nao_config.yaml` to the repository | | "Multiple nao projects were found in the connected repository." | Keep one `nao_config.yaml` per context repository | # Git Source: https://docs.getnao.io/nao-agent/chat/admin/git Connect the repository that stores your context files, so admins can edit them in the browser Connecting a context repository is what turns the [File Explorer](/nao-agent/chat/admin/file-explorer) into an editor. There are two ways to get there. Pick the one that matches how your deployment gets its context. ## Option A: connect a repository from the UI Use this when the deployment does not manage the context itself (`NAO_CONTEXT_SOURCE` is `local` or `api`). Set it up in **Settings** -> **Git**. Register a **GitHub OAuth App** or a **GitLab application**, so nao can act on your team's behalf, then set the credentials on the server and restart nao. Until then, the other steps stay disabled. * GitHub: `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` * GitLab: `GITLAB_CLIENT_ID` and `GITLAB_CLIENT_SECRET` (plus `GITLAB_BASE_URL` for a self-hosted instance) Point nao at the repository holding your context files. This doesn't overwrite your live project or push anything, and disconnecting it later deletes nothing. Only a project admin can disconnect it. Authorize nao with your personal GitHub or GitLab account. This is what commits and opens pull requests (or merge requests) **as you**, so your name is on the change and your own permissions apply. ## Option B: edit the deployment's own repository When the deployment already clones its context from Git (`NAO_CONTEXT_SOURCE=git`), nao can commit and open review requests against that same repository with **no OAuth app and no per-user authorization**. It reuses the credential the deployment already has: the access token in `NAO_CONTEXT_GIT_TOKEN`, or the SSH deploy key in `NAO_CONTEXT_GIT_SSH_KEY`. Nothing extra is required beyond the credential itself. Two optional variables cover the awkward cases: | Variable | When you need it | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `NAO_CONTEXT_GIT_PLATFORM` | Your host is a self-hosted GitHub, GitLab, or Bitbucket that nao cannot identify from the URL. Accepts `github`, `gitlab`, or `bitbucket`. | | `NAO_CONTEXT_GIT_SUBPATH` | The nao project lives in a subdirectory of the repository (a monorepo). | Commits are still authored as the nao user who made the change, with nao as co-author, even though the push uses the deployment's shared credential. In this mode, **Settings -> Git** shows a read-only summary of the deployment-managed repository rather than the connection wizard, because the source is controlled by the environment and not the UI. Editing in the File Explorer still works. See [Deployment-managed context files](/nao-agent/self-hosting/context-source). # Monitoring Source: https://docs.getnao.io/nao-agent/chat/admin/monitoring Monitor usage, costs, feedbacks Access the "Usage & Costs" dashboard from the admin panel to view analytics, usage metrics, user feedback, and chats replay tools. The Monitoring views are admin-only. Non-admin users are redirected away from protected routes. # Usage analytics In the Usage & Costs section, you can monitor: * Number of message sent by users to the chat * Total tokens consumed * Total cost of tokens consumed * Channel usage split across Web, Slack, and Microsoft Teams You can filter on a specific model, and look at a day / week / month granularity. ## Feedbacks monitoring In this section, you can see all postive and negative feedbacks sent by users in message threads. The **Feedbacks** table shows: * **Vote** - Thumbs up (👍) for positive feedback or thumbs down (👎) for negative feedback * **User** - Who provided the feedback * **Message** - The agent's response that was rated (truncated preview) * **Reason** - User's explanation for their feedback (if provided) * **Date** - When the feedback was given ## Chats replay In this section, admins can inspect historical chats at the project level and replay messages for debugging and quality checks. The **Chats Replay** view includes: * **Project-wide chat table** - Browse chats in the current project * **Filters** - Filter by column visibility, date, users, role, and tool state * **Replay panel** - Navigate previous and next feedbacks or tool errors from a selected chat * **Context window ring** - How much of the model's context window the selected chat is using, the same indicator users see in the chat input ### Tokens and cost per chat The chat table breaks each conversation's token use into **uncached** and **cache read**, shown as two figures above a split bar. Hover it for the exact totals. A separate **Cost** column gives the dollar cost of the chat, and both columns are sortable, so you can sort by spend to find the conversations worth looking at. A high cache-read share is the healthy case: it means the agent is re-reading a stable context instead of paying full price for it on every turn. A chat that is almost entirely uncached usually points at context that keeps changing between turns. Chats Replay is also available to the [Context Admin role](/nao-agent/chat/admin/setup#user-roles). If an admin or context admin opens the URL of a chat they do not own, nao redirects them to the Chats Replay view for that chat instead of showing a "Chat not found" page. Links to a colleague's chat therefore resolve straight to the replay panel, with the chat already selected. ## Asset analytics Every chat and story keeps a record of how it has been used, so you can see whether a report is actually read and by whom. Open it from: * A chat: the **Analytics** icon in the chat header. * A story: **Analytics** in the story header menu. The dialog groups events by person, most recent first, and has a search box to filter by name. Tabs depend on the asset: | Tab | Chats | Stories | What it shows | | ------------- | ----- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Views** | Yes | Yes | Who opened the asset, when, and how long they spent on it. Sessions are listed per viewer with a total time. For stories, the version viewed is shown. | | **Downloads** | Yes | Yes | Who exported the asset and in which format. Stories record PDF and HTML exports; chats record PNG chart images and CSV table exports, with the chart or table title. | | **Forks** | Yes | Yes | Who branched off the asset, and whether the fork was made from the whole asset or from a text selection. | | **Favorites** | No | Yes | Who favorited the story. The tab count reflects currently active favorites. | | **Refreshes** | No | Yes | When the story data was refreshed, and whether the refresh was manual or scheduled. | Repeat views by the same person on the same asset are collapsed inside a two-minute window, so refreshing a page does not inflate the count. Views coming from an embedded story, where there is no signed-in user, are grouped under **anonymous**. Asset analytics are visible to the owner of the chat or story, and to admins for any asset in the project. The same events are also queryable in natural language through the `v_analytics_event` view in admin mode (see below), which is the better route for project-wide questions such as "what are the most viewed stories this month?". ## Chat with your nao data (admin mode) Admin mode lets you ask questions about how your nao project is being used, in natural language, instead of reading the usage dashboards manually. In this mode the agent answers by running read-only SQL over nao's own application data. **Turn it on** from the chat, in two ways: * The **Chat with nao data** entry in the settings sidebar. * The **Admin mode** toggle in the chat input menu. When it's on, the chat input shows an amber border and an **Admin Mode** badge so it's clear you're querying internal data. Admin mode is available to admins only. **What it can read.** The agent queries a set of project-scoped, read-only views over nao's usage data. They are already filtered to the current project, so you never need to add a project filter: * `v_messages` - full message history: user prompts, assistant answers, tool calls and errors, feedback votes and explanations, the model used, and the source (Web, Slack, Teams, etc.). The richest view for adoption, tool errors, and downvotes. It also carries token counts: `total_tokens` alongside the input split (`input_total_tokens`, `input_no_cache_tokens`, `input_cache_read_tokens`, `input_cache_write_tokens`) and the output split (`output_total_tokens`, `output_text_tokens`, `output_reasoning_tokens`). These values repeat on every part of a message, so deduplicate by `message_id` before summing them. * `v_analytics_event` - asset engagement events (page views, downloads, forks, favorites, refreshes, view duration) for chats and stories. * `v_llm_inference` - one row per LLM call, with type and token usage. * `v_mcp_call_log` - MCP tool calls. * `v_memories` - saved memories. * `v_project` - project metadata. Ask things like "how many messages were sent this week by channel?", "which questions got the most downvotes?", or "what are the most downloaded stories?" and the agent writes the SQL for you. Queries are read-only (`SELECT` / `WITH` only) and cannot touch anything outside the allowlisted views. ## Logs Navigate to **Settings** -> **Logs** to view the agent's server-side logs in a terminal-style viewer. Features: * **Date range filter** - filter by preset ranges (last 15 minutes, 1 hour, 24 hours, 7 days) or a custom date and time range. Defaults to the last hour. * **Load older logs** - scroll to the top of the log viewer or click the header to load older entries. Pagination is cursor-based. * **Expandable rows** - click a log row to expand the full message text and see the `context` JSON payload when present. Truncated rows show a chevron indicator. **What's Next?** Configure users, authentication, and integrations Learn how to systematically improve answer quality # Setup Source: https://docs.getnao.io/nao-agent/chat/admin/setup Configure users, authentication, models, and prompts for nao chat As an admin, you can configure how your team accesses nao chat, manage integrations, choose models, and customize agent behavior. # Access the admin panel The admin views are exposed from the same server as the chat UI. 1. Start nao chat locally (see the [chat overview](/nao-agent/chat/overview) for details). 2. Sign in with an admin-capable account. 3. Navigate to the admin section from the UI navigation. The admin panel includes sections for: * **Project** - General project settings and Google OAuth * **Models** - LLM provider configuration * **Agent** - Agent settings and template prompts * **MCP Servers** - Model Context Protocol server configuration * **File Explorer** - Browse, search, and edit your context folder files * **Git** - Connect the repository that stores your context files (available to Admin and Context Admin roles) * **Slack** - Slack bot integration * **Telegram** - Telegram bot integration and user linking * **WhatsApp** - WhatsApp bot integration and user linking * **Mattermost** - Mattermost bot integration and user linking * **Team** - User management * **Usage & Costs** - Usage analytics, feedbacks, and chats replay (Chats Replay is also available to the Context Admin role) * **Recommendations** - Suggested context improvements mined from usage (available to Admin and Context Admin roles) # Add Users User management is available for deployed instances. If you're running nao locally with `nao chat`, user management is not required. For deployment instructions, see the [Deployment Guide](/nao-agent/self-hosting/deployment-guide). **To add a new user to a project:** 1. Navigate to **Team** in the left sidebar. 2. Click the **+** button in the top right of the Users panel. 3. Enter the user's email address 4. Pick a **role** (see below). 5. The user will then have to sign in with its email address and the generated password. If you want to let users sign up autonomously, you can setup Google OAuth - see in next section. The first user to sign up after deployment automatically becomes an admin. Additional users must be added through the admin interface. For more details on deploying nao, see the [Deployment Guide](/nao-agent/self-hosting/deployment-guide#step-5-customize-your-setup). ## User roles Each user belongs to one project role: * **Admin** - full access. Can manage users, integrations, models, prompts, budgets, and read every chat and story in the project. * **Context Admin** - uses nao like a Member, plus ownership of the project's context: **Recommendations**, **Chats Replay**, the **File Explorer**, and the **Git** setup page. They can edit context files, commit, and open pull requests, but get no other admin capabilities (LLM config, team management, usage & costs, logs, project settings) and can't disconnect the context repository. Use it for context owners who shouldn't have full admin. * **Member** - default role for new users. Can chat with the agent, create and share stories, edit memories scoped to their own conversations. * **Viewer** - read-only. Can open shared stories and shared chats sent to them, but cannot start a new chat, edit context, or change settings. Use this role for stakeholders who only consume insights. Change a user's role from **Team** -> click the user -> **Role**. Context Admin is a project-only role, so it isn't offered in the organization members list. ### Default role for new users By default, new users created through any onboarding path (admin invite to org or project, or messaging-provider auto-creation from Slack/Telegram/WhatsApp) get the **Member** role. Set the `DEFAULT_USER_ROLE` environment variable to change this: ```bash theme={null} DEFAULT_USER_ROLE=viewer # admin | user | viewer (defaults to "user" when unset) ``` Set `DEFAULT_USER_ROLE=viewer` for deployments where most people should consume insights read-only and only a few admins build context. Leaving it unset keeps the existing **Member** default. # Reset user passwords with SMTP If SMTP is configured, nao exposes a password reset flow for email and password users. What users get: * A **Forgot password** link on the login page * A reset email with a secure link * A dedicated **Reset password** page to choose a new password What admins need: 1. [Configure SMTP](/nao-agent/self-hosting/deployment-guide#smtp-for-transactional-email) for the deployment 2. Make sure your public app URL is correct so reset links point back to your nao instance 3. Test the flow from the login page with a real user account If SMTP is not configured, the forgot-password and reset-password routes stay unavailable. **Reset a password from the CLI** When SMTP is not configured, or a user is locked out, an admin with access to the deployment can reset a password directly against the local database with the `reset-password` command: ```bash theme={null} nao reset-password user@example.com ``` The command generates a temporary password, stores it hashed, and prints it once so you can hand it to the user to sign in and change. This works without a running mail server or browser flow. # Configure Google OAuth Google OAuth configuration is for deployed instances. This allows users to sign in with their Google accounts instead of manually created accounts. For deployment instructions, see the [Deployment Guide](/nao-agent/self-hosting/deployment-guide#step-5-customize-your-setup). Enable Google sign-in for your team and control which domains can self-register. **Step 1: Create Google OAuth Credentials** 1. Go to the [Google Cloud Console](https://console.cloud.google.com/) 2. Create a new project or select an existing one 3. Navigate to **APIs & Services** → **Credentials** 4. Click **Create Credentials** → **OAuth client ID** 5. Configure the OAuth consent screen if prompted 6. Select **Web application** as the application type 7. Add authorized redirect URIs: ``` https://your-deployed-instance-url/api/auth/callback/google ``` 8. Copy the **Client ID** and **Client Secret** **Step 2: Configure in nao Admin Panel** 1. Navigate to **Project** in the admin sidebar 2. Scroll to the **Google Credentials** section 3. Under **Google OAuth (Override Env)**, enter your **Client ID** 4. Click the **ENV** button to manage environment variables if needed 5. Set the following environment variables in your deployment: ```bash theme={null} GOOGLE_CLIENT_ID=your-client-id-here GOOGLE_CLIENT_SECRET=your-client-secret-here ``` **Step 3: Configure Allowed Domains** To restrict sign-up to specific email domains: 1. Set the `GOOGLE_AUTH_DOMAINS` environment variable: ```bash theme={null} GOOGLE_AUTH_DOMAINS=getnao.io,example.com ``` 2. Only users with email addresses from these domains will be able to sign up If you configure Google OAuth, users can sign in with their Google accounts. Make sure to set allowed domains if you want to restrict access to your organization. ## Configure GitHub SSO Enable GitHub sign-in so your team can log in with their GitHub accounts. 1. Create a GitHub OAuth App at [github.com/settings/developers](https://github.com/settings/developers). 2. Set the following environment variables: ```bash theme={null} GITHUB_CLIENT_ID=your-github-client-id GITHUB_CLIENT_SECRET=your-github-client-secret GITHUB_SSO=true ``` 3. Optionally restrict access to specific GitHub usernames: ```bash theme={null} GITHUB_ALLOWED_USERS=username1,username2 ``` The same GitHub OAuth app is also used by automations that need GitHub repository access. If `GITHUB_SSO` is not set to `true`, the OAuth app is available for automations but not as a login provider. ## Configure GitLab SSO Enable GitLab as a login provider and let users connect a GitLab account to import repositories and open merge requests from [context recommendations](/nao-agent/context-engineering/recommendations). 1. Create an application in GitLab under **User Settings -> Access -> Applications**. 2. Add the callback URLs (replace `localhost:5005` with your deployment host): ``` https:///api/auth/callback/gitlab # "Sign in with GitLab" (SSO) https:///api/gitlab/callback # "Connect GitLab" in settings ``` 3. Grant the scopes `api`, `read_user`, `openid`, and `email`. 4. Set the following environment variables: ```bash theme={null} GITLAB_CLIENT_ID=your-gitlab-client-id GITLAB_CLIENT_SECRET=your-gitlab-client-secret GITLAB_REDIRECT_URI=https:///api/gitlab/callback ``` 5. Optional settings: ```bash theme={null} GITLAB_SSO=true # expose GitLab as a login provider GITLAB_BASE_URL=https://gitlab.example.com # for a self-hosted GitLab instance GITLAB_ALLOWED_USERS=username1,username2 # restrict sign-in to specific users ``` As with GitHub, if `GITLAB_SSO` is not set to `true`, the OAuth app is still available for connecting an account and opening merge requests, but not as a login provider. ## Set the project date format Choose how dates are displayed across the project from **Settings -> Project**. The selected format applies everywhere dates render: chat tables and charts, stories, story exports (PDF/HTML), and messaging-channel replies (Slack, Teams, Telegram, WhatsApp, Mattermost). Set it once at the project level so everyone on the team sees dates in a consistent format. ## Configure Slack integration Connect your nao agent to Slack so your team can ask questions directly in Slack channels. Configure Slack integration from the **Slack** section in the admin sidebar, or see the [Slack Bot guide](/nao-agent/connectors/slack) for detailed setup instructions. ## Choose LLM models Configure which LLM providers and models are available to your agent. **Configure API Keys** In the **Models** section of the admin panel: * **If the provider isn't configured anywhere yet**: Enter your LLM API key directly in the UI * **If a key already comes from `nao_config.yaml` or your environment variables**: the provider is listed with a `nao_config.yaml` or `ENV` badge, and the form tells you where its key comes from. Leave the key field empty to keep using it, or enter a different key to override it Providers declared in the `llm` block of `nao_config.yaml` show up here with the models, display names and prices you gave them, so you don't have to re-enter anything in the UI. Editing one saves an override in the database, prefilled with the file's values. For each provider, nao resolves credentials and models from the first source that declares it: the **admin UI**, then `nao_config.yaml`, then environment variables. Overrides saved in the UI persist across restarts and keep winning until you delete them. See [LLM providers and models](/nao-agent/context-builder/configuration#llm-providers-and-models). **Azure OpenAI setup** Use the **Azure OpenAI** provider when your Azure endpoint serves OpenAI-compatible models such as GPT. 1. Navigate to **Models** in the admin sidebar 2. Add or edit the **Azure OpenAI** provider 3. Enter your Azure API key 4. Configure either a **Resource Name** or a **Base URL** Use **Resource Name** when your endpoint follows Azure OpenAI's standard format: * Resource name: `my-resource` * nao builds the URL as `https://my-resource.openai.azure.com/openai/v1` Use **Base URL** when Azure gives you a full endpoint or you are going through a proxy: * If Azure shows `https://my-resource.openai.azure.com/openai/v1/`, enter `https://my-resource.openai.azure.com/openai` * Do not include the trailing `/v1` or a request path, because nao adds those automatically Optional Azure OpenAI fields: * **API Version**: Set this only if your Azure setup requires a specific version. Otherwise leave the default `v1` * **Use Deployment-Based URLs**: Enable this only if your Azure deployment uses the legacy `/deployments/{deploymentId}` route style For Azure OpenAI, the model ID in nao should match the deployment name you created in Azure. You can also configure Azure OpenAI entirely via environment variables instead of the UI: ```bash theme={null} AZURE_API_KEY=... AZURE_RESOURCE_NAME=my-resource # or AZURE_OPENAI_BASE_URL=https://my-resource.openai.azure.com/openai AZURE_API_VERSION=v1 # optional, defaults to v1 AZURE_USE_DEPLOYMENT_BASED_URLS=false # optional, set to true only for legacy /deployments/{id} endpoints ``` The Azure OpenAI provider ships with an empty default model list, so you must add the deployment names you want to expose before users can pick them. **Azure-hosted Anthropic setup** If you deploy Claude through **Azure AI Foundry**, use the **Anthropic** provider in nao, not **Azure OpenAI**. 1. Navigate to **Models** in the admin sidebar 2. Add or edit the **Anthropic** provider 3. Enter your Azure AI Foundry API key 4. Set the **Base URL** to your Anthropic endpoint prefix Example: * If Azure shows a target URI such as `https://my-resource.services.ai.azure.com/anthropic/v1/messages` * Enter `https://my-resource.services.ai.azure.com/anthropic/v1` in nao * Do not include the trailing `/messages`, because the Anthropic API client adds it automatically For Azure-hosted Claude models, the model ID in nao should match your Azure deployment name, for example `claude-sonnet-4-5`. **OpenAI-compatible proxy (LiteLLM)** To send OpenAI (or Anthropic / OpenRouter) traffic through a proxy such as [LiteLLM](https://docs.litellm.ai/), set a custom endpoint on the provider: 1. Navigate to **Models** in the admin sidebar 2. Add or edit the **OpenAI** provider 3. Enter the proxy API key, or leave it empty to use the environment variable 4. Under **Enabled Models**, add the model IDs your proxy exposes 5. Expand **Advanced settings** and set **Custom Base URL** to your proxy endpoint, for example `http://0.0.0.0:4000` Settings changed in the UI are stored in the database and keep overriding `nao_config.yaml` until you delete the provider from the **Models** page. Declaring the provider, its `base_url` and its models in `nao_config.yaml` keeps the setup in version control and reproducible across deployments. See [OpenAI-compatible proxy](/nao-agent/context-builder/configuration#openai-compatible-proxy-litellm). **AWS Bedrock Setup (Step-by-Step)** Use one of these two authentication paths: 1. **Bearer token** * Set `AWS_BEARER_TOKEN_BEDROCK` * Optional: set `AWS_REGION` (defaults to `us-east-1`) * In **Models**, add provider **AWS Bedrock**, then configure model IDs 2. **IAM credentials** * Set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` * Optional: set `AWS_SESSION_TOKEN` and `AWS_REGION` (defaults to `us-east-1`) * In **Models**, add provider **AWS Bedrock**, then configure model IDs For IAM credentials, you can leave the Bedrock API key field empty. **Custom models** When adding a model that isn't in a provider's default list (self-hosted endpoints, fine-tuned variants, preview models), click **Add custom model** to open the custom model dialog. You can set: * **Display name** - a friendly label shown to users in the model picker (e.g. "GPT-4 Turbo (fine-tuned)") * **Model ID** - the identifier your provider expects in API calls * **Input token price** and **Output token price** - per-million-token costs used by the budget tracker Custom pricing is optional. If left blank, nao falls back to its built-in price table, and a model it doesn't know won't appear in cost breakdowns. The same models and prices can be declared in version control instead, under `llm.providers[].models` in `nao_config.yaml`. See [Model costs](/nao-agent/context-builder/configuration#model-costs). **Select Available Models** Control which models your users can access by providing the model ID from your LLM provider: 1. Navigate to **Models** in the admin sidebar 2. Add or edit a provider (Anthropic, Azure OpenAI, OpenAI, Google, Mistral, AWS Bedrock, or OpenRouter) 3. Enter the **model ID** for each model you want to make available 4. Users will only be able to select from the models you've configured Model IDs are provider-specific. For example: * Anthropic: `claude-sonnet-4-6`, `claude-opus-4-7`, `claude-haiku-4` * Azure OpenAI: use your Azure deployment name * OpenAI: `gpt-4`, `gpt-4-turbo`, `gpt-3.5-turbo` * Google: `gemini-3.1-pro-preview`, `gemini-2.0-flash-exp`, `gemini-1.5-pro` * Mistral: `mistral-large-latest`, `mistral-medium-latest` * AWS Bedrock: `us.anthropic.claude-sonnet-4-6`, `eu.anthropic.claude-opus-4-6-v1` * OpenRouter: `vendor/model` slugs from the [OpenRouter model list](https://openrouter.ai/models), for example `deepseek/deepseek-v4-flash` or `openai/gpt-4.1-mini` **Google Vertex AI (Claude models)** If you access Claude through Google Vertex AI, use the **Google** provider and set the model ID to a `claude-*` model. nao automatically enables prompt caching for Claude models served through Vertex, using the same cache control mechanism as the direct Anthropic provider. No extra configuration is needed. **Supported LLM Providers** nao supports the following LLM providers: * **Anthropic** - Claude models (prompt caching enabled automatically) * **Azure OpenAI** - Azure-hosted OpenAI-compatible models * **OpenAI** - GPT models * **Google** - Gemini models and Claude models via Vertex AI (prompt caching enabled for Claude) * **Mistral** - Mistral AI models * **AWS Bedrock** - Claude and other models through Amazon Bedrock (supports bearer token or IAM credentials) * **OpenRouter** - Access to multiple models from various providers through a single API (see [OpenRouter](/nao-agent/context-builder/configuration#openrouter) to declare it in `nao_config.yaml`) ## Configure MCP Servers Use MCP Servers to expose external tools (for example BI tools, internal APIs, or other systems) to the nao agent. The actual server configuration lives in your nao-core project, in the `agent/mcps/mcp.json` file created by `nao init`. Once that file is present in the project linked to your deployment, you can manage it from the admin panel: 1. Navigate to **MCP Servers** in the admin sidebar. 2. See the list of MCP servers defined in your project. 3. Enable or disable individual MCP servers for this deployment. 4. Optionally restrict which tools from each server are exposed to users. When a server is enabled, its tools become available to the agent as part of its toolset (see [Chat capabilities](/nao-agent/chat/capabilities/tools-mcps-skills#agent-tools)) and can be called automatically or via tool shortcuts in the chat UI. ## Configure nao MCP endpoint Expose nao as an MCP server so external AI clients (Cursor, Codex, Claude Code, Claude Desktop, or any custom MCP client) can call the agent, run SQL, browse context, and manage stories. For an overview of modes and tools, see [MCP Endpoint](/nao-agent/connectors/mcp). **Step 1: Enable the endpoint** 1. Navigate to **Settings** -> **MCP Endpoint**. 2. Toggle **Enable MCP endpoint** on. 3. Enable the modes you want exposed: **Sub-agent mode**, **Context-layer mode**, or both. Story and chart tools are exposed automatically whenever either mode is on. 4. Copy the **Endpoint URL** and **Bearer token** from the **Connection guide** section. **Step 2: Connect a client** The connection guide on the settings page shows ready-to-paste snippets for each client. Replace `` and `` with the values from your settings page. 1. Open **Settings** -> **Tools & MCP**. 2. Click **New MCP Server** and paste the JSON below, or edit `.cursor/mcp.json` manually. 3. Authenticate in your browser when prompted. ```json theme={null} { "mcpServers": { "nao": { "type": "http", "url": "" } } } ``` 1. Open **Settings** -> **MCP servers** -> **+ Add server** -> **Streamable HTTP**. 2. Paste your endpoint URL into the URL field, then save. 3. Authenticate in your browser when prompted. 1. Open `/.mcp.json` (project root). 2. Paste the config below. ```json theme={null} { "mcpServers": { "nao": { "type": "http", "url": "", "headers": { "Authorization": "Bearer " } } } } ``` **Method 1 - via Settings UI** 1. Open **Settings** -> **Connectors** -> **Add custom connector**. 2. Set **Name** to anything, and **Remote MCP Server URL** to your endpoint URL. 3. Enable the connector and authenticate in your browser. **Method 2 - via config file** 1. Open `claude_desktop_config.json` (typically in `~/Library/Application Support/Claude/`). 2. Add the server using the JSON below. 3. Restart Claude Desktop and authenticate when prompted. ```json theme={null} { "mcpServers": { "nao": { "command": "npx", "args": [ "-y", "mcp-remote", "" ] } } } ``` Use the config below in any MCP client that supports HTTP transport. ```json theme={null} { "mcpServers": { "nao": { "type": "http", "url": "", "headers": { "Authorization": "Bearer " } } } } ``` Treat the Bearer token like a password. Do not commit it to source control and rotate it from the settings page if exposed. ## Configure Telegram Use Telegram when you want users to message nao from the Telegram app. 1. Navigate to **Project** -> **Telegram** 2. Create a bot with `@BotFather` 3. Paste the **Bot Token** into nao 4. Save the form so nao registers the webhook automatically 5. Choose the model used for Telegram replies Each user also gets a **Linking Code** on the Telegram page. They send that code to the bot once so nao can map their Telegram identity to the correct account. For the full walkthrough, see [Telegram Bot](/nao-agent/connectors/telegram). ## Configure WhatsApp Use WhatsApp when you want users to interact with nao from a WhatsApp number backed by a Meta app. 1. Navigate to **Project** -> **WhatsApp** 2. Paste the **Access Token**, **App Secret**, **Phone Number ID**, and **Verify Token** 3. Copy the **Webhook URL** from nao into your Meta webhook configuration 4. Save the form 5. Choose the model used for WhatsApp replies Users link their identity by copying the **Linking Code** from the WhatsApp page and sending it to the bot. For the full walkthrough, see [WhatsApp Bot](/nao-agent/connectors/whatsapp). ## Configure Mattermost Use Mattermost when your team already chats on a self-hosted Mattermost server. 1. Navigate to **Project** -> **Mattermost** 2. Create a bot account in the Mattermost System Console and copy its **token** 3. Paste the **Server URL** and the **Bot Token** into nao 4. Save the form so nao opens the WebSocket connection 5. Choose the model used for Mattermost replies Users are linked automatically when their Mattermost email matches their nao account. If the emails differ, they send the bot a direct message with `login `, using the code from the Mattermost page. For the full walkthrough, see [Mattermost Bot](/nao-agent/connectors/mattermost). ## Browse and edit the context folder **Settings** -> **File Explorer** browses the context folder and searches it by filename or contents. Once a context repository is connected, the same screen becomes an editor: change a file, commit it to a branch, and open a pull request without leaving nao. See [File Explorer](/nao-agent/chat/admin/file-explorer). It respects `.naoignore`, excluded directories, and a 1 MB read limit per file. `.git` and `.env` are never accessible. ## Configure Template Prompts Customize prompt suggestions for your users in the chat. These prompts will be shown to your users in a new chat. These can be used to give your users examples of questions they can send to the chat. 1. Navigate to **Agent** in the admin sidebar 2. Look for **Template Prompts** section 3. Add example prompts that users can click to get started, such as: * "What were our top 5 products by revenue last month?" * "Show me user signups by country for the past quarter" * "Compare this month's sales to last month" * "What's the average order value by customer segment?" These template prompts help users understand what types of questions they can ask and provide quick-start examples for common analytics queries. ## Context recommendations nao can audit its own usage and recommend where your context needs work - which file to edit and what is missing. Review and configure them as an admin under **Settings -> Recommendations**: * Recommendations are ordered by impact, each naming a target file and citing the originating chat and the model that produced it. * **Acknowledge**, **snooze**, or **dismiss** each one as you work through them. * Configure the **analysis model**, the **run frequency** (daily / weekly / monthly, or **Run now**), optional **custom audit instructions**, and the **GitHub repository** where fixes are opened as pull requests. Turn on **YOLO mode** to open those PRs automatically without review. Enable the audit on your deployment by setting `BETA_CONTEXT_RECOMMENDATIONS_ENABLED=true`. For the full workflow - what it scans, how it fits the evaluation feedback loop, and the GitHub pull-request flow - see [Recommendations](/nao-agent/context-engineering/recommendations). **What's Next?** Learn how to track usage, feedback, and costs Deploy nao chat to production with step-by-step instructions # Automations Source: https://docs.getnao.io/nao-agent/chat/capabilities/automations Schedule recurring agent tasks with email, Slack, and GitHub integrations Automations let you run headless agent tasks on a cron schedule or from an external webhook. Each automation stores a natural-language prompt, one or more triggers, and optional integrations so the agent can deliver results to email, Slack, or GitHub without anyone opening the chat. ## Activity feed The **Feed** page in the left sidebar shows a chronological log of all automation activity across your project. Each entry shows the automation name, run status, timestamp, and links to the resulting chat or story. Use the feed to monitor scheduled runs at a glance without opening individual automations. ## Creating an automation 1. Click **Automations** in the left sidebar. 2. Click **New automation**. 3. Enter a title and the prompt you want the agent to run. In the prompt field, `Enter` inserts a newline instead of submitting, so you can write multi-line prompts. Save with the save button or `Cmd`/`Ctrl` + `S`. 4. Pick a schedule and configure integrations (see below). 5. Save. After creation, each automation gets its own detail page where you can edit settings, review previous runs, and trigger a manual run. ## Triggers An automation can be started **on a schedule**, **via a webhook**, or both. Pick the trigger(s) in the automation form. An automation needs at least one trigger to be active. ## Schedule The schedule trigger runs the automation on a cron cadence. The UI offers presets: * Hourly * Daily (9 am) * Weekdays (9 am) * Weekly (Monday 9 am) * Monthly (1st at 9 am) You can also enter a custom cron expression for any other cadence. Use the toggle on the automation detail page to pause or resume the schedule at any time. ## Webhook trigger The webhook trigger lets an external system start an automation with an HTTP request instead of (or in addition to) a schedule. Enable **Via webhook** in the trigger picker; the detail page then shows the endpoint URL and a copyable example `curl`. Send a `POST` to the automation's run endpoint, authenticated with an organization API key (the same `nao_...` bearer keys used by `nao deploy`). Create or manage keys under **Settings -> Organization**: ```bash theme={null} curl -X POST https:///api/automations//run \ -H "Authorization: Bearer nao_..." ``` On success the endpoint starts a run and responds `202` with `{ runId, automationId, status }`. It returns `401` for a missing or invalid key, `404` for an unknown automation or a key from another organization, `403` if the webhook trigger is disabled, and `409` if the automation is paused. Pausing gates every trigger: a paused automation rejects its webhook too. Webhook-only automations (no schedule) stay active until you disable the webhook. ## Run now Click **Run now** on the automation detail page to trigger an immediate run outside any schedule or webhook. The run follows the same flow as a scheduled execution. ## Integrations Each automation can optionally deliver results through one or more integrations. Configure these in the right sidebar of the automation form. ### Email When enabled, the agent sends its output by email after the run completes. * The automation owner's email is included by default. * Add extra recipients as a comma-separated list. * Optionally override the email subject line. ### Slack When enabled, the agent posts a message to a Slack channel with its results. * Enter the Slack channel ID (e.g. `C0123456789`). * Requires a Slack integration configured in your project settings. * The agent resolves `@handle` mentions to Slack user IDs before posting. To keep the channel uncluttered, the agent posts a short headline to start a thread, then delivers the full report inside that thread. Charts and stories are uploaded into the thread automatically. ### GitHub When enabled, the agent gets a set of dedicated GitHub tools: * **github\_read** - fetch repository data (issues, PRs, files, branches) for context * **github\_create\_issue** - open new issues * **github\_create\_pull\_request** - open new pull requests * **github\_add\_comment** - comment on issues or PRs Requirements: * A GitHub OAuth connection on the automation owner's account. * Optionally filter to specific repos (comma-separated) to scope which repositories the agent can access. ### MCP servers When enabled, the agent can call tools from your connected MCP servers during the run. Toggle individual servers on or off per automation. ## Model selection By default, automations use your project's default model. You can override this per automation from the model selector in the form sidebar. ## Previous runs The automation detail page shows a list of previous runs with: * Timestamp and duration * Status badge (running, completed, or failed) * Integration result icons showing which deliveries succeeded or failed * A link to open the full agent chat for any run A run is marked failed if the agent did not call an expected integration tool, or if the agent loop itself errored. # Files and Storage Source: https://docs.getnao.io/nao-agent/chat/capabilities/files-and-storage Attach documents to a chat, query them with SQL, and save results to permanent storage nao gives every user a durable folder the agent can read from and write to. It shows up to the agent as `/home`, part of the same file tree as the project context, and it is private to that user in that project. Two things use it: * **Attachments** - files you add to a message land in `/home/uploads`. * **Saved results** - exports and intermediate results the agent writes, so they outlive the chat. Permanent storage is configured once for the whole deployment with `NAO_STORAGE_BACKEND` (see [Configuring storage](#configuring-storage)). It defaults to `local`. When it is set to `none`, the agent has no write tool and file attachments are off - only [images](/nao-agent/chat/capabilities/overview#image-input) still work. ## Attaching files Add files the same way you add images: **Attach file** in the `+` menu of the chat input, drag and drop, or paste. | | | | --------------- | ------------------------------------------------------------------------------------------------------------------------ | | **Documents** | `csv`, `tsv`, `json`, `jsonl`, `parquet`, `xls`, `xlsx`, `pdf`, `docx`, `md`, `txt`, `html`, `xml`, `sql`, `yaml`, `yml` | | **Images** | `png`, `jpeg`, `gif`, `webp` | | **Per message** | Up to 5 attachments, images and documents combined | | **Size** | 5 MB per image; documents are capped by `NAO_STORAGE_MAX_FILE_SIZE_MB` (10 MB by default) | Documents and images reach the model very differently. An image is inlined into the request so the model can look at it. A document is only ever handed over as a **path** in `/home/uploads` - never its contents - so a large spreadsheet costs nothing until the agent actually opens it. ## What the agent does with a file * **`read`** extracts the text of a PDF page by page. On an `.xlsx` it returns the workbook outline instead of the cells - every sheet in tab order with its row and column counts - so the agent knows which sheet it wants before querying. Parquet and Word documents are not text, so `read` refuses them. * **`search`** finds files by name, and **`grep`** looks inside them (on S3 backends, where files are not on a local disk, `grep` is unavailable and the agent falls back to `search` plus `read`). * **`execute_sql`** queries tabular files in place - see below. * **`execute_sandboxed_code`** mounts a `/home` path into the micro-VM through its `storage_files` argument, for anything the other tools cannot parse. ## Querying files with SQL nao ships its own DuckDB engine alongside your warehouses, addressed as the database id **`duckdb_local`**. It is always available, and it is the only way to query a file or to join one against an earlier query result. It can: * read CSV, JSON, Parquet, and Excel files by their `/home` path, * expose every earlier `execute_sql` result as a table named after its query id, * join the two together - for example a spreadsheet someone uploaded against last quarter's revenue from the warehouse. Just ask in plain English: *"join this CSV against our customers table and show me who is missing."* `duckdb_local` is a reserved database id. A warehouse configured under that name in `nao_config.yaml` would be unreachable. ## Saving results The agent writes to `/home` when you ask it to keep, export, or update something. * **`save_to` on `execute_sql`** writes a `duckdb_local` result straight to a file, as `csv` (for a file someone opens) or `parquet` (which keeps column types, so the result can be queried again later). The rows still come back in the chat as usual. * **The `write` tool** saves anything else. `/home` is the only writable place in the tree; everything else is read-only. When the agent mentions a stored file in its answer, clicking it opens the side panel viewer: spreadsheets render sheet by sheet, PDFs and markdown render inline, and text files are shown as-is. The **Download** button in the panel header saves the file to your machine. ## Your own storage Open **Settings** -> **Storage** to see how much space your files take in the current project. You get your own space in every project you belong to, and nobody else can read it. ## Configuring storage Storage is set through environment variables, not the UI. Admins can review the resolved configuration, a reachability check, and per-project usage under **Settings** -> **Storage**. ```bash theme={null} NAO_STORAGE_BACKEND=local NAO_STORAGE_LOCAL_PATH=./storage # optional, defaults to ./storage ``` The default. Point it at a directory on disk. The path must be **durable**. In a container, mount a volume (or a PVC) at it - otherwise every restart wipes what users saved. Running more than one replica, every replica must mount the **same** read-write-many volume (NFS, EFS, Filestore, RWX PVC), or each replica sees a different set of files. Above one replica, prefer S3. ```bash theme={null} NAO_STORAGE_BACKEND=s3 NAO_STORAGE_S3_BUCKET=my-nao-storage NAO_STORAGE_S3_REGION=eu-west-1 NAO_STORAGE_S3_PREFIX=nao # optional, share a bucket with other workloads NAO_STORAGE_S3_ENDPOINT=https://minio.internal:9000 # optional, for S3-compatible providers NAO_STORAGE_S3_FORCE_PATH_STYLE=true # needed by MinIO NAO_STORAGE_S3_ACCESS_KEY_ID= # optional NAO_STORAGE_S3_SECRET_ACCESS_KEY= # optional ``` Works with AWS S3 and S3-compatible providers (MinIO, Cloudflare R2, ...). Recommended for any deployment above one replica. Credentials are optional: when omitted, the default AWS credential chain is used, so IAM roles, ECS task roles, and EKS/IRSA work with no configuration. ```bash theme={null} NAO_STORAGE_BACKEND=none ``` The agent has no write tool and cannot reach stored files, and file attachments are disabled. Nothing already saved is deleted. `NAO_STORAGE_MAX_FILE_SIZE_MB` (default `10`) caps the largest single file that can be written, on every backend. A bigger file is rejected before anything is stored. ## Next steps The full list of tools the agent can call Mount a durable volume when self-hosting # Memory Source: https://docs.getnao.io/nao-agent/chat/capabilities/memory How user memory works in nao chat and how to control it Memory helps nao personalize answers across conversations. ## What Memory Stores Memories are user-level, not project-level. They are not shared between users. When memory is enabled, the agent stores only instructions and preferences you explicitly provide, such as: * Preferred metrics and dashboards. * Definitions you repeatedly use (for example, how you define an active customer). * Repeated user preferences mentioned across chats. ## What Memory Does Not Store The agent does not store: * Query results. * Generated answer content. * Internal reasoning traces. It stores only your expressed instructions and preferences. ## User Controls You can manage memory directly from the chat interface: * Open the **Memory** panel. * Review stored entries. * Edit or delete entries. * Disable memory for your account. Project admins can also enable or disable memory availability for a deployment. # Overview Source: https://docs.getnao.io/nao-agent/chat/capabilities/overview Key features, chat workflow, speak to prompt, and context management The nao chat combines context-aware analytics with transparent execution. ## Key Features Ask questions in plain English and get actionable answers. Automatically generate charts and visual explanations from query results. Inspect the SQL and logic behind each answer. Continue previous analyses with full chat history. Dictate a question with your microphone and send the transcribed prompt. Preserve user-level preferences across conversations. ## How It Works When you ask a question, the agent follows this flow: The agent searches and reads your context files to understand your data model, definitions, and rules. It composes SQL based on the available context and your question. The query runs against connected data sources and returns results. If useful, the agent renders charts from the result set. The agent summarizes findings in plain language with supporting evidence. ## Speak To Prompt From the chat input: 1. Click the microphone icon. 2. Grant microphone access when prompted. 3. Speak your request. 4. Review or edit the transcription, then send it. Only transcribed text is stored in chat history. Speak to Prompt requires an admin-configured OpenAI API key, and is currently supported with OpenAI models. ## Context Management The agent grounds answers in project context, including: * Context files and business definitions synced into your project. * Rules and instructions that shape generation behavior. * Data source metadata and available schema information. For details on configuring context, see [Context Builder](/nao-agent/context-builder/overview). ## Visualizations and Image Input The chat UI can render agent query results as charts (bar, line, area, pie, donut, KPI card, scatter, radar, and stacked variants) or as tables with conditional formatting. Both can be edited after the fact, and every chart exposes the SQL query and rows behind it. For the full list of chart types, the chart edit dialog, table formatting rules, and the project date format setting, see [Visualizations](/nao-agent/chat/capabilities/visualizations). ### Image input You can attach images directly in the chat input when you want the agent to inspect a screenshot, dashboard, chart, or UI state. The agent treats images as part of the conversation - it can describe them, transcribe text out of them, compare them to data, or use them to disambiguate a question ("the spike in *this* chart"). **How to attach an image** There are three ways to add images to a message: * Click the `+` menu in the chat input and pick **Attach file**. * Drag an image file from your desktop into the chat input - the input shows a highlighted drop zone while you drag. * Paste an image directly from your clipboard (e.g. after `cmd+shift+4` on macOS or a snipping tool on Windows). The paste handler is scoped to the chat input so pasting elsewhere on the page still works normally. Selected images appear as thumbnails above the input. Click the small `X` on any thumbnail to remove it before sending. **Supported formats and limits** * Formats: `image/png`, `image/jpeg`, `image/gif`, `image/webp`. * Up to **5 attachments** per message, images and documents combined. * Up to **5 MB** per image. Files outside these formats or above the size cap are rejected client-side before upload. ### Document input The same input takes documents - PDFs, spreadsheets, CSVs, Parquet files, and more. Unlike images, a document is handed to the agent as a path in permanent storage rather than inlined into the request, so a large file costs nothing until the agent opens it. nao can also query tabular files directly with SQL and join them against your warehouse. See [Files and Storage](/nao-agent/chat/capabilities/files-and-storage) for the full list of formats, what the agent can do with each, and how an admin configures storage. **Viewing images in the conversation** Images you've sent stay attached to the message and re-display when the chat is reloaded or shared. Click any image in the conversation to open it in a full-size lightbox - close it with `Esc`, the X button, or by clicking outside. Images are stored against your project and served from a stable URL, so they keep rendering inside the chat history and any story or shared link the conversation feeds into. ## Editing, resending, and message versions Hover over any of your sent messages to reveal a toolbar with actions and a relative timestamp (e.g. `8m ago`, with the full date and time in a tooltip). * **Resend** - the resend button (circular-arrow icon) re-runs the same prompt as-is, superseding the conversation from that turn onward. * **Edit** - change the prompt text and resend it to branch the conversation from that point. When a message turn has been edited or resent, a `‹ i/n ›` control appears next to the action icons. Use it to navigate the history of superseded versions and restore any of them as the active branch - the message and the answer below it update together. ## Sharing and forking chats Every chat can be shared with your project or with specific people from the share icon in the chat header. Recipients can: * **Continue chat** - open a new forked chat that contains the full shared history. Each fork is independent, so the original is never modified. * **Ask about a selection** - highlight any text in the chat and click the **Ask** button that appears above the selection. The quoted text becomes a pending citation in the input, and once sent, the answer keeps a **Go to original selection** link back to the source passage. Selection-based asks also work on your own chats and stories, not just shared ones, and in the admin **Chats replay** view. For the story-specific flow (**Discuss story** and version handling), see [Stories](/nao-agent/chat/capabilities/stories#sharing-forking-and-text-selection). ## Managing Your Chat History Your chat list lives in the left sidebar, sorted by most recent activity (last updated first). The list shows the 10 most recent chats by default; click **Show more** to expand the full list and **Show less** to collapse it again. **Group and filter** Use the filter menu at the top of the chat list to organize conversations: * **Group by** stars, date, project, ownership, or none. * **Filter** the visible chats with the search box to narrow the list to matching titles. When a chat starts from a text selection or **Ask about a selection** action, nao automatically regenerates its title using the quoted context, so the sidebar entry reflects the question rather than a generic placeholder. **Pruning the list** To keep the sidebar tidy, nao gives you two ways to remove conversations: * **Star** the chats you want to keep so they're always preserved. * Open **Settings -> General -> Danger Zone** and click **Delete all non-starred chats** to bulk-clear everything you haven't starred. The action is destructive but recoverable internally: chats are soft-deleted (hidden from your sidebar, search, and history), not erased. Starred chats are never affected. ## Tool call density Control how much detail the chat shows for the agent's tool calls (SQL queries, sandbox runs, and similar steps). Set it from **Settings -> Account**: * **Detailed** - tool calls are expanded inline so you can read the SQL and intermediate steps as the agent works. * **Compact** - tool calls are collapsed into a compact group, keeping the conversation focused on the answer. Expand any step when you want the detail. The preference is stored against your account, so it follows you across devices and sessions. ## Query Guardrails For BigQuery connections, nao can enforce a maximum query size in GB before the agent runs SQL. When configured, nao performs a dry run first: * Queries under the limit execute normally * Queries over the limit are blocked before BigQuery scans the data # Stories Source: https://docs.getnao.io/nao-agent/chat/capabilities/stories Create, edit, and share long-form analytics reports directly from your chats Stories turn a chat analysis into a persistent, shareable document with live charts, tables, and narrative text. You can revisit it, refine it, and distribute it to your team. ## Creating a Story Stories are created directly from a chat conversation. Simply ask the agent to write one: > *"Write a story about our new user growth since the last release."* > *"Create a story summarizing MRR trends over the past quarter."* The agent will: 1. Run the relevant queries against your data. 2. Compose a structured document with narrative text, charts, and tables embedded inline. 3. Open the story in a side panel next to the chat thread. Story example with charts ## Editing a Story Once a story is open in the side panel, click the **pencil icon** to switch to edit mode. The editor is **Notion-style**: every block (text, chart, table) can be dragged and reordered using the grip handle on the left. You can edit text in rich markdown, rearrange sections, and remove blocks you don't need. When you're done, click **Save**: this creates a new version of the story. To discard your changes, click **Cancel**. ### Renaming a story The story owner can rename a story from its title in the header, without opening the editor. Click the title, type a new one, and the change is saved in place. The new title propagates everywhere the story is referenced - other story pages and the chat cards that link to it - and the story's link stays the same, so shared URLs keep working. ### Arranging charts into a grid Charts and tables don't have to stack one per row. In edit mode, each chart and table has a **drag handle** next to its edit pencil: * **Move** a block by dragging its handle anywhere in the story. * **Put blocks side by side** by dropping one beside another - they form a row. Drop a third into an existing row to add it. * **Resize** by dragging the divider between two charts in a row. Widths snap to clean fractions (halves, thirds, quarters), and charts in a row line up at the same height with aligned baselines. Labels and legends adapt as columns get narrower. The agent can also build multi-chart grids with uneven widths directly when it composes a story. ### Selecting multiple blocks To move several blocks at once, select them first: * **Cmd/Ctrl-click** a block to toggle it in the selection. * **Shift-click** to select a range. * **Esc**, or a plain click, clears the selection. Selected blocks are highlighted. Drag the handle on any selected block to move the whole selection together, keeping their order. This works with non-contiguous selections and with charts, tables, and whole grids. Press **Backspace** to delete exactly what you have selected - the highlighted blocks and grid columns, and nothing else. Removing one chart from a row leaves the rest of the row intact rather than wiping the whole row. ## Tabs A story can be split into tabs so one report can hold several views without becoming a long scroll. Tabs are written with `` markup: ```html theme={null} ...charts, tables, and text... ... ``` In edit mode, use the pencil button to **add, rename, reorder, and delete** tabs. Editing is tab-aware: you edit one tab at a time, and saving writes all tabs as a single new version. Stories without tabs are unchanged. You can ask the agent for tabs in plain English (*"split this into an overview tab and a per-region tab"*), and the [Add to story](#adding-a-chart-from-the-thread) action drops a chart into the tab you're currently viewing. When a tabbed story is [exported](#exporting-stories) to PDF or HTML, the tabs are flattened into sequential titled sections so nothing is hidden in the static file. ## Filters Interactive filters are a **beta** feature and are **off by default**. Enable them on your deployment by setting `BETA_STORY_FILTERS_ENABLED=true`. Until then, the filter controls and the agent's filter tooling are hidden. Filters let a reader change what a story shows without editing it - pick a country, a date range, or a search term from a **filter bar** at the top of the story, and the charts and tables re-run their queries against the new selection. It turns one story into a self-serve view over your data. You usually don't write the markup by hand: ask the agent (*"add a country filter and a date-range filter to this story"*) and it declares the filters and rewires the queries for you. The mechanics below are useful for understanding and reviewing what it produces. ### Declaring a filter A filter is declared with a `` tag. Four types are available: | Type | Control | Notes | | -------------- | ---------------------- | ----------------------------------------- | | `select` | Single-choice dropdown | Needs an options source | | `multi_select` | Multi-choice dropdown | Needs an options source | | `search` | Free-text input | Matches a value you template into the SQL | | `date_range` | Date range picker | Expands to a start and end bound | For `select` and `multi_select`, give the options either from your data or as a fixed list: ```html theme={null} ``` When options come from `table`+`column` and your project has more than one database, add `database_id` so nao loads the options from the right connection. ### Wiring a filter into SQL A chart or table query opts into a filter with a `{% filter %} ... {% endfilter %}` block, and drops the selected value in with `{{ filters..sql }}`: ```sql theme={null} SELECT country, sum(amount) AS revenue FROM orders WHERE 1 = 1 {% filter country %} AND country IN ({{ filters.country.sql }}) {% endfilter %} {% filter period %} AND order_date BETWEEN {{ filters.period.sql }} {% endfilter %} GROUP BY country ``` * `{{ filters..sql }}` expands to the selected value(s), safely quoted. * For `date_range`, the single `{{ filters..sql }}` already expands to `'start' AND 'end'`, so pair it with `BETWEEN` - don't reach for `.start`, `.end`, or `.value`. * When a filter has no selection, its whole `{% filter %}` block is stripped before the query runs, so the query still works with nothing selected. Changing a selection re-renders and re-executes the affected charts and tables, and the same applies on [live-story](#live-stories) refreshes. If the agent writes an invalid filter template, it comes back as a template warning to fix before the story is saved. ## Versions Every time you save a story, nao creates a new version. Use the **version navigator** in the story header (`v1 / v2 / ...`) to browse previous versions. * **← / →** arrows step through versions. * When viewing an older version, a **Restore** button lets you roll back to that version (creating a new version from it). Recipients who receive a shared link always see the latest version automatically. ## Editing Charts After a chart is generated, you can tweak its configuration without re-running the query. **In a chat thread:** click the **pencil icon** next to the chart's download button. This opens an edit dialog where you can change: * Chart title * Chart type (bar, line, area, pie, etc.) * X-axis key and type * Per-series settings: data key, label, and color Click **Save** to persist the changes. The updated chart config is written back to the database, and any cached chart image (used for Slack/Teams/WhatsApp previews) is regenerated on next request. **In a story:** hover over any chart block and click the **pencil icon** in the top-right corner. The same edit dialog opens. Saving creates a new story version with the updated chart tag, following the same versioning flow as text edits. Chart editing is only available to the chat or story owner. The edit button is hidden while the agent is running, on archived stories, in shared/read-only views, and when viewing older story versions. ## Editing the Story Code The story side panel has a **Code** view that shows the raw markdown behind the story. This view is now editable: you can modify text, chart tags, table tags, and grid blocks directly. The editor validates your changes in real-time: * Missing required chart attributes (`query_id`, `chart_type`, `x_axis_key`) * Invalid chart type or axis type values * Malformed or empty `series` definitions * Missing `query_id` on table blocks * Unterminated or unclosed tags * Out-of-range `cols` on grid blocks Validation errors appear as red markers in the editor and a banner above it. The **Save** button is disabled until all errors are resolved. Press **Cmd+S** (or **Ctrl+S**) as a shortcut to save. Saving creates a new story version, just like editing in the visual editor. The code view is read-only on shared stories. ## Adding a Chart from the Thread When the agent produces a chart in the chat thread, you can embed it directly into a story: 1. Open the story side panel. 2. Ask the agent to add the chart: *"Add this chart to the story."* The chart is embedded as a live block: it renders with the same query data and can be repositioned in the editor like any other block. ## Sharing a Story Click the **share icon** in the story header to open sharing settings. You can share with: All members of your nao project can view the story via the shared link. Search by name or email and select individual team members. Clicking **Share & copy link** copies the URL to your clipboard. Recipients open the story in their browser: they always see the **latest version**. **Notify people** The share dialog has a **Notify people** toggle that sends recipients an email letting them know about the story. It is **off by default**, so sharing is silent unless you turn it on. The same toggle is available when sharing a chat. The toggle only appears when [SMTP is configured](/nao-agent/self-hosting/deployment-guide#smtp-for-transactional-email) on your deployment. Without SMTP, no notification email is sent. **Managing an existing share** Once a story is shared, the share icon turns green. Click it again to: * **Copy link** - grab the URL again. * **Update access** - add or remove specific people (for specific-person shares). * **Unshare** - revoke access and disable the link entirely. ## Sharing, forking, and text selection Once a chat or story is shared, recipients can branch from it or ask targeted follow-ups without breaking the original. The same selection-based "Ask" flow also works on your own (non-shared) chats and stories. **Continue chat** On a shared chat, **Continue chat** opens a new forked chat that contains the full shared history. Each click creates a fresh fork, so the original conversation is never modified. The fork copies the messages only: stories that belong to the origin chat are not duplicated into your Stories library. **Discuss story** On a shared story, **Discuss story** opens a new chat with the story embedded at the top in editable form, ready to be questioned or refined. **Ask about a selection** Select any text inside a chat or a story and an **Ask** button appears above the selection. Clicking it opens an inline frame where you can quote the selection and ask a follow-up about that specific passage: * The quoted text shows up as a **pending citation** in the input until you send the question, so you can see exactly what the agent will see. * Once sent, the answer keeps a **Go to original selection** link that jumps back to the exact passage in the source chat or story. * The frame leaves a small persistent indicator when collapsed so you can reopen it later. Selection-based asks work in four places: * Your own chats * Your own stories * Shared chats and stories you have received * The admin **Chats replay** view (asking about a past chat for debugging or follow-up) All "Continue chat", "Discuss story", and "Ask" actions create new conversations under the current user's account, so the original content stays untouched. ## Exporting stories Stories can be exported to PDF or HTML from the download button in the story header. Export works on your own stories and on shared stories, and you can pick any existing version from the version navigator before exporting. **PDF** * A4 page size, formatted for printing. * Charts are rendered as vector SVG so they stay sharp at any zoom level. * Tables are included up to 10 rows per table to keep the document readable; longer tables are truncated in the PDF (the full dataset is still available in the HTML export and in the live story). **HTML** * Self-contained single file: no external assets, works offline. * Interactive chart tooltips are preserved, so recipients can hover data points in their browser. * Styled for print, so **File -> Print** in the browser produces a clean printed copy without separate theming. Use PDF when you need a fixed-layout report to attach to an email or a doc. Use HTML when you want the story to stay interactive and portable. ## Live Stories By default, a story is a snapshot: charts and tables freeze the data that was returned when the story was created. **Live stories** keep the story connected to the underlying queries so the numbers refresh on a schedule (or on demand) without anyone having to rebuild the report. ### Enabling live mode 1. Open the story in the side panel. 2. Click the **settings** icon in the story header. 3. Toggle **Live mode** on. 4. Pick a refresh strategy (see below). 5. Save. A green **Live** badge appears in the story header once it's enabled, along with a "last refreshed" timestamp. Anyone who opens the shared link sees the most recently refreshed version. ### Refresh strategies When live mode is on, choose how freshness should work: * **Manual refresh only** - the story keeps its last refreshed values until someone clicks the refresh button in the header. * **No cache (always fresh)** - queries re-run every time the story is opened. Best for low-volume stories where you always want live numbers; avoid on expensive queries. * **Scheduled refresh presets** - every 5 minutes, hourly, daily, weekly, or monthly. * **Custom schedule** - type a schedule in plain English (e.g. *"every weekday at 8am"*, *"first Monday of the month"*) and nao converts it to cron, or paste a raw cron expression directly. Each refresh re-runs every query block in the story against your connected warehouse, then updates the cached results that recipients see. ### Refreshing the narrative For live stories, you can also enable **Regenerate the narrative** in the same settings panel. When turned on, nao rewrites the text blocks with the updated numbers while keeping the layout intact: * Section titles stay in place. * Charts and tables stay embedded in the same positions. * Only the prose around them is rewritten so the commentary matches the new data. This is what turns a live story into a real recurring report - the agent doesn't just refresh the numbers, it explains them. ### Access and refresh on shared stories Live refresh respects the same access rules as the share itself: * For project-wide shares, any project member can trigger a manual refresh. * For specific-people shares, only the people on the access list can refresh. * Scheduled refreshes always run with the story owner's permissions, so the data the recipients see is the data the owner is allowed to query. If a query underlying a live story starts failing (schema change, permissions, etc.), the story keeps showing the last successful refresh and surfaces an error indicator in the header so you can investigate. ## Stories Library The Stories Library is your central hub for all stories. Access it by clicking **Stories** in the left sidebar. Stories Library The library is a folder explorer: it lists the folders in the current location, then the stories inside it. Stories you created and stories shared with you appear together, each card showing its author and last update. From the toolbar you can switch between a **grid view** (thumbnail cards) and a **list view**, search by title, and jump to archived items with **See archives** (**Back to stories** returns to the main view). Sort pills above the list order the current folder by **Name**, **Owner**, or **Updated**. Click the active pill again to reverse the direction. Your view and sort choices are remembered. ### Folders Organize stories into folders to keep the library tidy: * Create one with **New folder** in the explorer. * Each folder card has a menu with **Modify** (rename), **Move to...**, **Archive**, and **Delete**. Archived folders offer **Restore folder**. * Folders can be nested. Navigate down with a click and back up with the **breadcrumb** at the top of the explorer. * **Drag and drop** stories and folders onto another folder, or onto a breadcrumb level, to move them. ### Pinned and Favorites Two sections at the top of the library promote the items you reach for most. Each one is collapsible and only appears when it has content: * **Favorites** - favorite any story or folder for yourself. Favorites are per-user, so only you see yours. * **Pinned** - pinned stories surface at the top for everyone with access. Pinning is admin-only and applies to shared stories. ### Bulk actions Click **Select** in the toolbar to enter selection mode, then tick multiple stories and folders. A bar appears at the bottom with the number selected and the action available in the current view: **Archive** in the main library, **Restore** when browsing archives. Up to 100 stories and 100 folders can be handled in one action. Leave selection mode with **Cancel**. ### Who can change what Renaming, moving, archiving, and deleting a story or a folder is restricted to its owner and to project admins. Other members can open, favorite, and search items shared with them, but not reorganize them. ### Usage analytics Open **Analytics** from the story header menu to see who viewed, downloaded, forked, or favorited a story, and when it was last refreshed. See [Asset analytics](/nao-agent/chat/admin/monitoring#asset-analytics) for the full breakdown. # Tools, MCPs, Skills Source: https://docs.getnao.io/nao-agent/chat/capabilities/tools-mcps-skills Built-in tools and extensibility model for the nao chat agent ## Agent Tools The nao agent uses built-in tools autonomously to answer user requests. Execute SQL against connected databases and return structured results. * Supports multiple database connections. * Returns typed columns and row counts. * Outputs can be reused by other tools. * Pass `duckdb_local` as the database to use nao's own DuckDB engine instead of a warehouse: it queries CSV, JSON, Parquet, and Excel files by path, and joins them against earlier query results. See [Files and Storage](/nao-agent/chat/capabilities/files-and-storage#querying-files-with-sql). * `save_to` writes the result to a CSV or Parquet file in permanent storage, on top of returning the rows. Create charts from SQL results. **Supported chart types:** * Bar charts * Stacked bar charts * Line charts * Pie charts * KPI cards Charts are rendered in chat and can also be sent in Slack conversations. Execute code in an isolated sandbox (micro-VM) for advanced analysis. * Supports Python and shell execution. * Can install Python packages for a run. * Can reuse prior SQL outputs as CSV inputs. * Images uploaded to the chat are mounted into the sandbox, so the agent can read or manipulate them directly from Python (e.g. OCR, cropping, chart comparison). * Files in permanent storage are mounted the same way through `storage_files`, for formats the other tools cannot parse. * Requires enabling **Sandboxes** in Admin -> Agent -> Experimental. Save a file to permanent storage, under the user's `/home` folder. * `/home` is the only writable place in the file tree; project context is read-only. * Used for exports, spreadsheets the agent builds, and intermediate results worth reusing. * Unavailable when the deployment sets `NAO_STORAGE_BACKEND=none`. See [Files and Storage](/nao-agent/chat/capabilities/files-and-storage). Search files with glob patterns in your context. List files and directories so the agent can navigate project structure. Read context files such as SQL models, docs, and rule files. Search text patterns across context files with regex. Search the public web with your model provider tools and fetch cited pages when needed. * Uses provider-native capabilities (OpenAI, Anthropic, Google) when enabled. * Lets the agent answer questions that need fresh external information. * Sources are shown in tool call output for traceability. Ask the user a focused question when their request is genuinely ambiguous (multiple plausible tables, unclear metric, missing time range, etc.). * Renders a "Quick question" card with the question and up to 5 clickable answer chips. * Clicking a chip sends the answer directly - no extra Enter press required. * Free-form answers via the normal chat input are also supported. * The agent pauses and waits for the user's reply before continuing. * Multi-turn clarification works naturally: previous cards switch to an "Answered" state with a checkmark on the selected chip so the full decision trail stays visible. You do not need to select tools manually. The agent chooses and orchestrates tools based on each question. Web search is optional and can be enabled per project from Settings -> Project -> Agent -> Web search. ## MCPs MCP (Model Context Protocol) servers expose external tools that the agent can call next to built-in tools. Configure MCP servers in `agent/mcps/mcp.json`: ```json theme={null} { "mcpServers": { "metabase": { "command": "npx", "args": ["-y", "@getnao/metabase-mcp-server@latest"], "env": { "METABASE_URL": "http://localhost:3008/", "METABASE_API_KEY": "your-metabase-api-key" }, "disabled": false, "autoApprove": [], "disabledTools": [] } } } ``` Do not commit secrets. Store credentials in runtime environment variables or a secrets manager. ### Remote HTTP servers Alongside local `command` servers, you can connect to remote MCP servers over HTTP. Set `transport` to `streamable-http` and point `url` at the server endpoint: ```json theme={null} { "mcpServers": { "notion": { "transport": "streamable-http", "url": "https://mcp.notion.com/mcp" }, "linear": { "transport": "streamable-http", "url": "https://mcp.linear.app/mcp" } } } ``` Accepted transport values are `streamable-http`, `sse`, and `http`. Servers declared with a `command` run over stdio. ### How nao loads MCP tools nao does not hold a live client connection to each server, and it never loads every tool definition into the context window. Instead: 1. nao connects to the server once and reads its tool list. 2. It writes one OpenAPI JSON file per enabled tool into the context filesystem, at `agent/mcps//.json`. The file name is the tool name. 3. The agent discovers what it needs on demand with the normal `list`, `read`, and `grep` tools, then invokes it through a single `mcp_call` tool. `mcp_call` takes the `server` name, the `tool` to run (the operation's `operationId`), and an `arguments` object matching that operation's request body schema. Arguments are validated against the schema before the call runs, so a malformed call comes back as a validation error listing the specific issues rather than failing at the server. The generated spec directories are gitignored - they are discovered at runtime and do not need to be committed. ### Inline authentication When a remote server requires OAuth, nao prompts the user to sign in inline the first time its tools are needed, so each user authenticates with their own account instead of sharing one organization-wide login. The connection is authorized per user and reused on later runs. If the agent calls a tool on a server the user has not connected yet, the call returns an auth-required result and a **Connect** button is shown below the conversation. The agent stops and waits instead of retrying. ### Managing servers and tools Admins manage MCP servers from **Settings** -> **MCP Servers**. The table lists each server declared in `agent/mcps/mcp.json` with its transport, connection status, the number of enabled tools out of the total, and a toggle to enable or disable the whole server. **Connect all MCP servers** re-runs discovery across every server, and each row has its own refresh action to reconnect a single server. Connection status shows as Connected, Cached (discovered previously, not re-tested), Waiting for connection (OAuth pending), Error, or Not tested. Expanding a row shows the path where that server's specs were written, plus its tools grouped by category - **Read-only**, **Write**, **Delete**, or **Unknown** - inferred from the tool name. Each category has a toggle to enable or disable the whole group at once, and each tool has its own toggle. Disabled tools are not written to the context filesystem, so the agent cannot discover or call them. See [Admin Setup](/nao-agent/chat/admin/setup#configure-mcp-servers) for the full configuration walkthrough. In chat, MCP tool calls and their outputs are rendered with a dedicated block: each call shows the server name, the tool used, and the returned payload formatted for readability (tables, JSON, and text are laid out distinctly rather than dumped as raw strings). ## Skills Skills are reusable workflows defined as markdown files in `agent/skills/`. A file is recognized as a skill only if: 1. It is stored in `agent/skills/`. 2. It starts with YAML frontmatter including `name` and `description`. ```yaml theme={null} --- name: top-customers description: Analyze database to find top 5 customers by total payments. --- ``` In chat, users can trigger skills through `/` shortcuts or natural prompts that match skill descriptions. # Visualizations Source: https://docs.getnao.io/nao-agent/chat/capabilities/visualizations Chart types, chart editing, table formatting, and display settings When the agent answers a question with data, it can render the result set as a chart or as a table. Both are produced by the `display_chart` tool from a previous `execute_sql` result, and both can be adjusted afterwards without re-running the query. ## Chart types The agent picks a chart type from the result set, and you can change it later from the edit dialog. | Type | Use it for | | -------------- | ----------------------------------------------------------------------------- | | Bar | Comparing values across categories | | Stacked bar | Composition of a total across categories | | Horizontal bar | Ranking one or more metrics across categories, drawn as sideways tracked bars | | Line | Trends over time | | Area | Time-series trends with filled emphasis | | Stacked area | How multiple series compose a total over time | | Pie | How one measure splits across categories | | Donut | Same as pie, with a hollow centre | | KPI card | A single headline number | | Scatter | Correlation between two numeric variables | | Radar | Several metrics compared across a fixed set of categories | | Mixed | Bars, lines, and areas combined in one chart, optionally on two Y-axes | Stacked bar, stacked area, and horizontal bar charts also have a normalized variant that plots each series as a share of its category total on a 0-100% axis. See [Normalize to 100 percent](#normalize-to-100-percent) below. For large numeric values, nao shortens Y-axis labels to values such as `100K`, `1.5M`, or `2B` to keep charts readable. ### Pie and donut charts Pie and donut charts take exactly one series. Categories are shown in a legend rather than as labels drawn on the slices, and slices are separated by a small gap so adjacent categories stay distinguishable. When a result set has more than 10 categories, nao keeps the 10 largest slices by value and sums the remainder into a single **Other** slice. If the data already contains a category called "Other", the remainder is merged into it rather than creating a duplicate. ### Horizontal bar charts A horizontal bar chart draws one row per category, each with a sideways bar in a grey track. The category sits on the left and the value in a column on the right, and every bar is sized against the largest value in the data. Values are shown on the bars by default and can be turned off with the **Show data labels** toggle in the edit dialog. A single value column gives one bar per row. With two or more series the bars stack within each row, and a legend appears. Because the rows are ranked visually, the agent is instructed to sort and limit the rows in SQL so the chart stays readable. With two or more series you can also [normalize to 100%](#normalize-to-100-percent): each row then fills the same width and its bars show each series' share of that row's total. ## Editing a chart Click the **pencil icon** next to a chart's download button to open the edit dialog. Changes apply to the existing chart config, so the query is not re-run. The dialog lets you set: * **Title** * **Chart type** * **X-axis type**: `Auto`, `Category`, `Date`, or `Number` * **X-axis column** * **X-axis label**: an optional custom label drawn under the axis * **Series**: for each series, the data column, an optional label, a color, a [value format](#value-formatting) (number format, unit, and placement), and - on [mixed charts](#combo-charts-and-a-second-y-axis) - a series type and Y-axis side. Use **Add series** to plot another column, or the bin icon to remove one (at least one series is required). * **Y-axis range** * **Show data labels** Click **Save** to persist the changes. Any cached chart image used for Slack, Teams, and WhatsApp previews is regenerated on the next request. Chart editing is available on charts in a chat thread and on chart blocks inside a story. It is restricted to the chat or story owner, and hidden while the agent is running, on archived stories, in shared or read-only views, and when viewing older story versions. ### Normalize to 100 percent When the selected chart type is a stacked bar or stacked area chart, the dialog shows a **Normalize to 100%** toggle. Turning it on switches the chart to its 100% stacked variant: each series is drawn as a share of the category total and the axis runs from 0 to 100%. Tooltips show percentages instead of absolute values. Horizontal bar charts have the same toggle, but only once they carry two or more series to split each row between - a single-series horizontal bar has nothing to normalize. Use it when the composition matters more than the absolute totals. Turning the toggle off returns the chart to absolute stacking. ### Y-axis range By default the Y axis adapts to the values in the result set. Line and scatter charts auto-scale to a readable range rather than forcing a zero baseline. To pin the scale, enter a **Min**, a **Max**, or both in the **Y-axis range** fields. Leaving a field on its `Auto` placeholder keeps that bound automatic. The minimum must be lower than the maximum, otherwise the dialog rejects the change. The Y-axis range fields are not shown for pie, KPI card, and radar charts, which have no Y axis. ### Data labels The **Show data labels** toggle prints the numeric value of each data point directly on the chart, instead of requiring a hover. You can also ask the agent for it in plain English (for example: *"show the values on the chart"*). On line and area charts, labels are limited to the most significant points so a dense series stays legible. ### Combo charts and a second Y-axis Pick the **Mixed** chart type to draw several series with different shapes in one chart - for example bars for revenue and a line for a conversion rate. Each series then gets two extra controls in its row of the edit dialog: * **Series type** - draw that series as a `bar`, `line`, or `area`, independent of the others. * **Y-axis side** - plot the series against the `left` or `right` axis. A right axis appears as soon as any series uses it, which is what lets you compare metrics on very different scales or units in a single chart. The right axis has its own **range** and **label** fields, mirroring the left axis. You can also just ask the agent: *"plot revenue as bars and the conversion rate as a line on a second axis."* ### KPI comparison pills A **KPI card** can show a small change pill under its number, comparing the latest value to the previous period. Turn it on with the **Comparison pill** control in the edit dialog (it appears only for KPI cards), or ask the agent for it. There are three styles: * **Percentage** - the signed percentage change, colored green or red with an arrow. * **Variation** - the signed absolute change, colored green or red with an arrow. * **Absolute** - just the magnitude of the change, with no color or arrow. The pill names the period it compares against (*"vs. last month / last week / last quarter / last year"*), inferred from the data's dates, and falls back to *"vs. previous period"* when the spacing is unclear. Comparison pills need the query to return **two or more time-ordered rows** (oldest to newest). Set the style to **none** to hide the pill. ### Hiding the tooltip total When a chart stacks several series, the tooltip shows a **Total** row by default. That total is meaningless when the series can't be meaningfully added up - unrelated metrics, or different units or currencies. The agent can set a chart-wide `hide_total` option to drop the Total row in those cases; additive charts still show it, and percentage charts still show 100%. ## Value formatting By default chart values render as plain numbers. Value formatting lets you show a currency symbol, a `%`, or any unit (V, kg, GB, …) and control how numbers are rounded and abbreviated. Formatting is set **per series**, so a chart can mix, for example, a dollar series and a percentage series. A format applies everywhere the series' value appears: the value axis, tooltips, data labels, and KPI cards. It also carries through into [stories](/nao-agent/chat/capabilities/stories), including their HTML and PDF exports. The value axis automatically widens to fit longer labels (such as `$1,000,000`) so they don't clip. A value format has three parts: * A **number format** - a [d3-format](https://d3js.org/d3-format) specifier that controls rounding, thousands separators, and abbreviation of the raw number. * A **unit** - free text such as a currency symbol (`$`, `€`, `¥`, `£`), a `%`, or a unit like `V`, `kg`, `GB`. * A **placement** - whether the unit sits *before* the number (**Prefix**, for currencies) or *after* it (**Suffix**, for `%` and most units). ### Setting it in the editor In the [chart edit dialog](#editing-a-chart), each series row has a **Number format**, a **Unit**, and a **Placement** (Prefix or Suffix). Any part can be left blank. ### Asking the agent You usually don't need to open the editor - ask in plain English and the agent sets the format for you: > *"Format revenue as US dollars."* > *"Show the conversion rate as a percentage with one decimal."* > *"Label the storage column in GB."* ## d3-format number cheat sheet The **number format** field takes a raw [d3-format](https://d3js.org/d3-format) specifier and applies it to the value **as-is** (it does not multiply or divide). Copy-paste one of these: | Format | `1234.567` renders as | Use for | | ------- | --------------------- | --------------------------------------- | | `,.0f` | `1,235` | Whole numbers with thousands separators | | `,.1f` | `1,234.6` | 1 decimal place | | `,.2f` | `1,234.57` | 2 decimals (money) | | `.0f` | `1235` | Whole number, no separator | | `.2f` | `1234.57` | 2 decimals, no separator | | `,` | `1,234.567` | Separators, keep all decimals | | `.2s` | `1.2K` | Abbreviate to 2 significant digits | | `.3s` | `1.23K` | Abbreviate to 3 significant digits | | `+,.0f` | `+1,235` | Always show the sign | The pattern is `[,][.precision][type]`: a leading `,` adds thousands separators, `.N` sets precision, and the trailing letter is the type - `f` for fixed decimals or `s` for SI-prefix abbreviation. See the [full d3-format docs](https://d3js.org/d3-format) for more. ### Copy-paste recipes (format + unit) | Goal | Number format | Unit | Placement | | ---------------------------------- | ------------- | ---- | --------- | | US dollars | `,.2f` | `$` | Prefix | | US dollars, no cents | `,.0f` | `$` | Prefix | | Euros, no decimals | `,.0f` | `€` | Prefix | | Abbreviated dollars (`$1.2M`) | `.2s` | `$` | Prefix | | Percentage (data stored as `42.5`) | `.1f` | `%` | Suffix | | Volts | `,.0f` | ` V` | Suffix | | Bytes, abbreviated (`1.2M`) | `.2s` | `B` | Suffix | ### Percentages Do **not** use d3's `%` format type - it multiplies the value by 100. If your data is already stored as a percentage (for example `42.5` meaning 42.5%), use a plain number format (`.1f`) with a `%` unit set to **Suffix** placement. That renders `42.5` as `42.5%`. ### Abbreviating large numbers The `.2s` (SI-prefix) number format abbreviates large numbers. How the abbreviation letters display depends on the series' compact mode, which the agent picks based on the kind of value: * **Financial** (the default) is meant for money and counts: `k` becomes `K` and `G` becomes `B`, while `M` and `T` are unchanged - so you get `1.2K`, `3.4M`, `5.6B`. * **SI** keeps d3's scientific letters unchanged, which is what you want for scientific units such as bytes (`1.2k`, `3.4M`, `5.6G`). The editor accepts number formats that render identically in the interactive chart and in static story exports - the `f` and `s` types, a bare `,`, and a precision (for example `,.2f`, `.2f`, `,`, and `.2s`). If you enter a format that would export differently, the dialog shows a warning and blocks saving until you adjust it. The agent is not limited to these when it sets a format directly. ## Viewing the SQL and data behind a chart Every chart is backed by a SQL query and its result set. Click the **View SQL query and data** action (the `<>` icon) on the chart toolbar to open a side panel with the `execute_sql` query on top and the result rows below, so you can verify exactly how a chart was built. ## Tables Ask for a table instead of a chart and the agent renders the result set with `display_chart` using `chart_type: "table"`. Tables include: * **Right-aligned numeric columns** with tabular figures, so digits line up. * **Sortable columns** - click a column header to sort by it. Each click cycles through ascending, descending, and back to the original order (the chevron shows the current direction). Column widths stay stable across sorting and paging. This works on tables in chat, in stories, and on `execute_sql` results. ### Exporting table data Any table - in a chat thread, in a story, or from an `execute_sql` result - can be downloaded from its toolbar. The export menu offers two formats: * **CSV** - a plain comma-separated file. * **Excel** (`.xlsx`) - a native spreadsheet file. Both export the full result set, not just the rows currently on screen. ### Conditional formatting Numeric, boolean, and text columns can be colored by rule. Open the **Edit table formatting** dialog from the table toolbar and pick a rule per column - columns default to **None**. Available rules depend on the column type: **Color scale** shades each cell on a gradient between the column minimum and maximum. **Threshold** colors cells matching a comparison (`≥`, `>`, `≤`, `<`, `=`) against a value. **Boolean** sets a background color for true cells, false cells, or both. Leave a color unset to skip that case. **String** colors cells that match an operator: `equals` for an exact match, `in list` for one of several values, or `contains` for a case-insensitive substring. For color scales, nao derives the gradient from a single color by default, running from a light tint at the low end to the full color at the high end. The bounds default to the column minimum and maximum. **Asking the agent to format a table** You do not have to open the dialog. Ask for the formatting in plain English and the agent applies the rules itself: > *"Show revenue by region as a table and highlight anything under 10k in red."* > *"Put a color scale on the conversion rate column."* The agent is instructed to use real conditional formatting for these requests rather than faking it with emoji or by adding an extra status column to the data. ## Date format Dates rendered by nao follow a project-level setting rather than each viewer's locale, so everyone on the project reads the same format. Set it in **Settings -> Project** on the **Date format** card. This is an admin setting. | Preset | Pattern | | -------- | ---------------- | | European | `DD/MM/YYYY` | | American | `MM/DD/YYYY` | | ISO 8601 | `YYYY-MM-DD` | | Custom | Your own pattern | European is the default. Choosing **Custom** lets you compose a pattern from the supported tokens: `YYYY`, `YY`, `MMMM`, `MMM`, `MM`, `M`, `DD`, `D`, `dddd`, and `ddd`. The card previews the result on a sample date as you type. The setting applies to chart axes, tooltips, legends, and KPI cards, to SQL result tables, and to the charts and tables inside stories and their PDF and HTML exports. ## Maps When a query returns geographic data, the agent can plot it on an interactive map instead of a chart, using the `display_map` tool. Maps render over an OpenStreetMap-based basemap, and you can pan and zoom them like any web map. Ask in plain English: *"map our customers by city."* Maps are off by default. An admin turns them on under **Settings -> Project -> Agent**, on the **Maps** card. Until then the agent won't offer the map tool. ### Map types | Type | What it draws | Needs | | ------------------ | --------------------------------- | -------------------------------------------------------- | | **Points** | A marker per row | Latitude and longitude columns, in WGS84 decimal degrees | | **Scatter bubble** | Markers sized by a numeric column | Latitude, longitude, and a size column | | **Choropleth** | Regions shaded on a color scale | A numeric value column, plus boundaries to join against | A choropleth gets its shapes from one of three sources: a **built-in boundary set** (`world_countries`, `france_regions`) matched on a region column, a **public GeoJSON URL**, or a **geometry column** in the result set (for example `ST_AsGeoJSON` output from PostGIS). Prefer a URL over a geometry column when a public one exists - it avoids pulling large polygons through SQL. ### The map toolbar Every map carries the same toolbar as a chart: * **View map**, **View data table**, and **View SQL query** switch between the map, the rows behind it, and the `execute_sql` query that produced them. * **Add to story** drops the map into a [story](/nao-agent/chat/capabilities/stories). * **Download** saves the map as a PNG, or exports the underlying rows as CSV or Excel when you are on the data view. * **Edit map** (the pencil icon) opens the edit dialog. Maps also expand to full width, and the basemap style can be changed from the style selector. ### Editing a map The edit dialog covers presentation, so the query is never re-run. You can set: * **Title** * **GeoJSON boundaries** - on a choropleth built from a URL, the URL itself; on one built from a boundary set, a read-only summary of which set it uses * **Color** - the marker color, or the base color of the choropleth scale * **Marker size** - the radius for points, or the largest bubble for a scatter bubble map (not shown on choropleths) For anything structural - a different map type, other columns, different boundaries - ask the agent instead: *"size the bubbles by revenue instead of headcount."* ### Maps in stories and exports Maps behave like charts everywhere else in nao: * Map blocks render in [stories](/nao-agent/chat/capabilities/stories) and in story thumbnails. * PNG and PDF exports include the map as rendered. * Slack, Teams, Telegram, WhatsApp, and Mattermost previews render the map as an image that mirrors what you see in the web chat. ### Custom GeoJSON boundaries Built-in boundary sets only cover countries and French regions. To shade anything else - postal codes, sales territories, store catchment areas - an admin can add your own boundaries to the **GeoJSON Boundary Library** under **Settings -> Project -> Agent**. Paste an HTTPS URL to a GeoJSON `FeatureCollection`. nao loads it, validates it, and previews it on a map so you can confirm you have the right shapes. Give it a **Label** (what admins see, e.g. `French postal codes`) and a **Key** (what the agent addresses it by, e.g. `french_postal_codes`). Pick the **Join property** - the GeoJSON feature property whose value must match the region column in your SQL. nao lists the properties it found in the file. Add a **Region key hint** describing what those values look like, so the agent knows what to select. Saved sets become available to the agent as boundary options alongside the built-in ones. Removing a set from the library breaks any map that was using it. ## Custom charts Beyond the built-in chart types, a project can ship its own **custom charts** - browser-rendered visualizations authored as small JavaScript modules in `agent/charts/`. Use them when a question needs a visual that nao's built-in types don't cover. Each custom chart is a file `agent/charts/.js` that exports a `render` function: ```js theme={null} export function render(element, context) { const { data, config, colors, theme, libs } = context; // draw into `element` using the query's `data` return () => element.replaceChildren(); // optional cleanup } ``` * `` must start with a lowercase letter and must not collide with a built-in type (`bar`, `line`, `pie`, `table`, …); names that break the rules are ignored. * `context` carries the `data` rows from the referenced `execute_sql` result, the chart `config`, the nao `colors` and `theme`, and a set of charting `libs`. * Charts render in the browser only. Stories, exports, automations, MCP embeds, and messaging previews keep using the built-in chart types. Active charts hot-reload as you edit the file, so you can iterate without restarting. See the `build-custom-charts` skill in your project for the full authoring reference. # Overview Source: https://docs.getnao.io/nao-agent/chat/overview Interact with your analytics agent through natural language The nao chat interface provides an intuitive way for your team to interact with data using natural language. ## Start Chat You can access the chat interface in two ways: ### Option 1: Using nao chat command ```bash theme={null} nao chat ``` This will start a local server at `http://localhost:5005`. ### Option 2: Using Docker Run the Docker container to access the UI: **With built-in example:** ```bash theme={null} docker run -d \ --name nao \ -p 5005:5005 \ -e BETTER_AUTH_URL=http://localhost:5005 \ getnao/nao:latest ``` **With your project:** ```bash theme={null} docker run -d \ --name nao \ -p 5005:5005 \ -e BETTER_AUTH_URL=http://localhost:5005 \ -v /path/to/your/project:/app/project \ -e NAO_DEFAULT_PROJECT_PATH=/app/project \ getnao/nao:latest ``` Access the UI at `http://localhost:5005`. nao Chat Interface The chat interface runs locally on your machine. No data is sent to external servers unless you configure an external LLM provider. ## Ask Questions **Question Input** * Type your question in natural language * Reference previous results in follow-up questions **Response Display** The agent's response includes: * **Answer**: Plain language summary of findings * **SQL Query**: The generated query (expandable) * **Data Table**: Results in table format * **Visualizations**: Automatic charts when appropriate * **Sources**: Which context files were used ## Tool call density By default the agent shows its work in detail: SQL queries, Python, and sandbox executions each render as their own block in the thread, alongside charts and stories. If that is more than you want to read, change it from **Settings** -> **Account** -> **General Settings** -> **Tool Call Density**. The slider has two positions: * **Detailed** (default) - SQL, Python, and sandboxed code executions are shown as individual expandable blocks. * **Compact** - those steps are folded into a single collapsed group you can expand on demand. Charts, stories, clarifications, and follow-up suggestions stay visible either way. The setting is per user and follows you across devices. ## Keyboard shortcuts Press `Cmd+/` (or `Cmd+:`) to open the shortcut list from anywhere in the chat. On Windows and Linux, `Ctrl` replaces `Cmd`. | Shortcut | Action | | ------------------ | --------------------------------- | | `Cmd+B` | Toggle sidebar | | `Cmd+K` | Command menu | | `Cmd+Shift+L` | Toggle light/dark mode | | `Cmd+/` or `Cmd+:` | Keyboard shortcuts | | `Cmd+Shift+O` | New chat | | `Cmd+Shift+S` | Go to Stories | | `Ctrl+C` | Cancel messages | | `Shift+Tab` | Cycle model (from the chat input) | Shortcuts match the **character** on your keyboard, not its physical position, so they work the same on AZERTY, QWERTZ, and QWERTY. `Cmd+:` exists because `Cmd+/` is taken by a system shortcut on AZERTY Macs; it is deliberately inactive on QWERTY. ## Give Feedbacks Each response has: * 👍 Thumbs up - Mark good responses * 👎 Thumbs down - Flag issues These responses given by your users are stored and accessible for you to audit. **What's Next?** Explore all chat features and agent tools Set up Slack integration for your team # Deploy project context Source: https://docs.getnao.io/nao-agent/cloud/deploy Push a local nao project to a remote nao instance with the nao deploy CLI command The `nao deploy` command packages a local nao project and uploads it to a remote nao instance. It is the bridge between a project built locally with `nao init` / `nao sync` and a Cloud (or self-hosted) instance that will run chats, scheduled jobs, and integrations against that project. Use it to push the same context folder you work on locally to the instance your team chats with. ## Prerequisites * A remote nao instance URL (Cloud workspace or your self-hosted deployment). * An organization-level API key. Create one from **Settings** -> **Organization** -> **API Keys**. See [Per-organization API keys](/nao-agent/cloud/overview#per-organization-api-keys). * A local project that contains a valid `nao_config.yaml` with a `project_name` field. ## Command Run from the project directory (the folder that contains `nao_config.yaml`): ```bash theme={null} nao deploy --api-key ``` Or from anywhere, pointing at the project folder explicitly: ```bash theme={null} nao deploy --api-key --path /path/to/project ``` ### Flags | Flag | Description | | ----------------- | ---------------------------------------------------------------------------------------- | | `` | URL of the remote nao instance (positional). For nao Cloud, use `https://app.getnao.io`. | | `-k`, `--api-key` | Organization API key used to authenticate the upload. | | `-p`, `--path` | Project directory. Defaults to the current working directory. | ## What gets uploaded `nao deploy` builds a gzipped tarball of the project folder and uploads it to `/api/deploy`. The server extracts the archive, reads `project_name` from `nao_config.yaml`, and either creates a new project in the organization or updates the existing one with the same name. ### Default exclusions These paths are always skipped when building the archive: * `.git` * `.venv` * `.env` * `node_modules` * `__pycache__` * `repos` * `*.pyc` ### `.naoignore` Add a `.naoignore` file at the project root to exclude extra files. One pattern per line, `#` for comments. Patterns are matched against path parts (directory or file names). Example: ``` # .naoignore dbt_packages logs secrets.yaml *.parquet ``` ## Create vs update The server looks up a project by `(organization, project_name)`: * If no project with that name exists in the organization, a new one is created and the response reports `status: "created"`. * If a project with that name already exists, its context folder is replaced with the uploaded content and the response reports `status: "updated"`. There is no merge. Every deploy is a full replacement of the project's context folder on the remote instance. ## Example ```bash theme={null} $ nao deploy https://app.getnao.io --api-key nao_sk_live_... Deploying my_project to https://app.getnao.io Packaging project files... Package size: 2.3 MB Uploading... Project my_project created Project ID: 4f2d... ``` ## Errors * `Authentication failed. Check your API key.` - the API key is missing, revoked, or does not belong to the target organization. * `No nao_config.yaml found in current directory` - run the command from a project root or pass `--path`. * `nao_config.yaml is missing a 'project_name' field` - add `project_name: my_project` at the top of `nao_config.yaml`. * `Could not connect to ` - check the URL and that the instance is reachable from your network. # Get Started with nao Cloud Source: https://docs.getnao.io/nao-agent/cloud/get-started Sign up at app.getnao.io, ship your first project, and invite your team nao Cloud is the hosted version of nao, running at [**app.getnao.io**](https://app.getnao.io). We run the chat app, database, and your projects - you don't deploy or manage any infrastructure. Build your context locally (or in Git) and push it to your Cloud workspace. Prefer to run nao on your own infrastructure? See the [Deployment Guide](/nao-agent/self-hosting/deployment-guide). The organization and project model below is the same in both. ## Step 1: Create your account Go to [app.getnao.io/signup](https://app.getnao.io/signup) and sign up with email and password, or with **Google**. On your first sign-in, nao automatically creates a **workspace** (your organization) with you as its admin - there is no separate setup wizard. If you sign up with a Google address whose domain already has a nao Cloud workspace, you can be added to that existing workspace instead. Already have an account? Sign in at [app.getnao.io/login](https://app.getnao.io/login). ## Step 2: Add your first project A fresh workspace has no project yet. A **project** holds one agent's context, data connections, chats, and members. There are two ways to create one. ### Option A: Import from GitHub If your nao project already lives in a GitHub repository (a folder containing `nao_config.yaml`), import it straight from the browser: 1. From the empty-project screen, choose **Import from GitHub**. 2. Authorize the nao GitHub app on the account or organization that owns the repo. 3. Pick the repository and branch that holds your `nao_config.yaml`. nao clones the repo and creates the project. You can re-pull the latest commit anytime from the project's **Git sync** settings. The repository picker only lists repos the nao GitHub app can access. If yours isn't listed, add it to the app's installation on GitHub. ### Option B: Deploy from the CLI If you build context locally with the CLI, push it to your workspace with `nao deploy`. 1. Build your project locally - see the [Quickstart](/nao-agent/quickstart): ```bash theme={null} pip install nao-core nao init # creates nao_config.yaml and the context folder nao sync # pulls schemas, repos, and docs into the context ``` 2. Create an organization API key in the Cloud UI: **Settings -> Organization -> API Keys -> Create key**. Copy it - it's shown only once. 3. From the project folder, deploy to your Cloud workspace: ```bash theme={null} nao deploy https://app.getnao.io --api-key nao_sk_... ``` The project name comes from `project_name` in `nao_config.yaml`. Deploying a new name creates a project; deploying an existing name replaces its context in place. Full command flags, exclusions, `.naoignore`, and create-vs-update behavior ## Step 3: Configure the project Your data connections, rules, and semantics are defined in `nao_config.yaml` and your context folder - edit them locally and re-deploy (or push to the connected Git branch). A few things are set directly in the Cloud UI, under **Settings -> Project**: * **Environment variables** - provide values for the secrets your config references with `{{ env('MY_VAR') }}` (warehouse credentials, API tokens), so you never ship them in Git. * **Models** - add your LLM provider keys and pick the models the agent can use. * **Agent settings** - template prompts, MCP servers, budgets, and connectors (Slack, Teams, Telegram, WhatsApp, Mattermost). Structure the databases, repos, rules, and semantics your agent reads from ## Step 4: Invite your team Add teammates from **Settings -> Organization -> Members**: invite by email and pick a role. Each new member signs in with their email and the generated password. Members join projects to get access to that project's chats, context, and data - manage each project's members under **Settings -> Project -> Team**. For roles and permissions, see [Admin Setup](/nao-agent/chat/admin/setup#user-roles). On Cloud, users who sign in with a Google address on your workspace's claimed domain can join automatically - no manual invite needed. ## Step 5: Start chatting Open your project and ask a question in plain English. The agent uses your project's context to write SQL, run it against your connected warehouse, and return results and charts. ## What's next Organizations, projects, members, API keys, and managed env vars Run several projects in one workspace with a project switcher Automate context deploys with GitHub Actions Stories, memory, automations, and admin tools # nao Cloud Source: https://docs.getnao.io/nao-agent/cloud/overview Managed hosting with organizations, teams, and git-synced context nao Cloud is the hosted version of nao. It runs your analytics agent in our infrastructure and adds organization-level features that are not available in self-hosted deployments: multi-project organizations, team management, per-organization API keys, GitHub-connected context sync, and managed environment variables. The Cloud UI ships alongside the self-hosted chat app. The features described here appear under **Settings** -> **Organization** when your account belongs to a Cloud organization. ## Organizations and projects A Cloud account belongs to one or more **organizations**. Each organization owns one or more **projects**, and each project is the unit that hosts an agent, its context, its connected data sources, and its chats. * Organization-level settings (billing, members, API keys) apply across every project in the organization. * Project-level settings (data connections, context, chat behavior) are scoped to a single project. * A user can belong to several organizations and switch between them from the workspace switcher. To manage organization-wide settings, open **Settings** -> **Organization**. ## Managing team members Organizations and projects both have their own member lists. **Organization members** Open **Settings** -> **Organization** -> **Members** to: * Invite a new member by email. They receive an invite link and become a member once they sign in. * Change a member's role (admin or member). * Remove a member from the organization. Admins can manage billing, API keys, members, and organization-wide settings. Members can use the projects they are added to. **Project members** Each project has its own member list under **Settings** -> **Project** -> **Team**. Add organization members to a project to give them access to that project's chats, context, and data. A user must be in the organization before they can be added to one of its projects. ## Per-organization API keys API keys live at the organization level and are used to call the nao API from your own services, CI, or other integrations. 1. Open **Settings** -> **Organization** -> **API Keys**. 2. Click **Create key**, give it a name, and copy the key. It is shown only once. 3. Revoke a key from the same page when it is no longer needed. Each key is scoped to its organization and can access every project the organization owns. ## Connecting a GitHub repository for context Cloud projects can sync their context folder from a GitHub repository instead of uploading files manually. Once connected, nao pulls the latest state of the repo on a schedule so the agent always sees the current context. 1. Open **Settings** -> **Project** -> **Context** -> **Git sync**. 2. Click **Connect GitHub** and authorize the nao GitHub app on the organizations you want to expose. 3. Pick the repository and branch that holds your `nao_config.yaml` and context folder. 4. Save. nao performs the first sync immediately and then refreshes on every push to the selected branch. The repo picker only lists repositories the nao GitHub app has access to. If you do not see the repo you want, go back to GitHub and add it to the app's installation. ## Environment variables Cloud projects expose an **Environment variables** section under **Settings** -> **Project** -> **Environment**. Use it to set credentials and other values referenced from `nao_config.yaml` without shipping them in your git repo. * Add, edit, or remove variables from the UI. * Variables are injected into the runtime for sync jobs and chat requests. * Reference them in config the same way as on self-hosted: `{{ env('MY_VAR') }}`. Secrets are encrypted at rest and are not displayed back in the UI after they have been saved. ## What's next Configure models, integrations, and template prompts Structure the context your agent reads from # Mattermost Bot Source: https://docs.getnao.io/nao-agent/connectors/mattermost Enable your team to ask questions directly in Mattermost The nao Mattermost bot brings your analytics agent directly into Mattermost, allowing your team to ask questions and get insights without leaving their workspace. nao Mattermost Bot Interface When configured, the nao Mattermost bot: * Runs against your own **self-hosted Mattermost** server * Connects as a **bot account** with a personal access token, over a persistent WebSocket * Answers mentions in channels, and every message in direct messages * Returns answers with SQL queries, data tables, and visualizations * Streams the answer into a single message that is edited as the answer grows The Mattermost bot uses the same agent context, rules, models, and capabilities as your web chat interface. Any improvements to your agent automatically benefit Mattermost users. Each nao project connects its own bot, so several projects can live on the same Mattermost server. ## Setup ### Step 1: Create a Bot Account in Mattermost 1. In Mattermost, go to the **System Console** → **Integrations** → **Bot Accounts** and set **Enable Bot Account Creation** to **true**. It is off by default. 2. Go to **Integrations** → **Bot Accounts** → **Add Bot Account**, create the bot, and copy the **token**. It is shown only once, at creation. 3. Add the bot to the **team** first, then to each **channel** where it should answer. Adding the bot straight to a channel does not work until it is a member of the team. Add it to the team first, then to the channels. See the [Mattermost bot accounts guide](https://docs.mattermost.com/integrations-guide/bot-accounts.html) for more detail. ### Step 2: Connect your Mattermost Bot to nao 1. In nao, click on your profile → **"Project"** → **"Mattermost"** 2. Fill in the **Server URL** (for example `https://mattermost.example.com`) and the **Bot Token** 3. Click **"Save"** 4. Choose the model used for Mattermost replies ### Step 3: Turn on the Stop button On the same settings page, turn on **Enable interactive buttons**. This adds a **Stop** button to the answer while nao is generating, so people can cut a long answer short. The button disappears on its own once the answer finishes. Buttons work by Mattermost calling back into nao over HTTP, so **your Mattermost server must be able to reach nao**. Two things commonly block that: 1. **The address is wrong.** The address your Mattermost server uses to reach nao can differ from the one you use in your browser. That is what the optional **Callback URL** field is for — leave it blank unless buttons fail. For example, with Mattermost running in Docker on a laptop, the callback URL would be `http://host.docker.internal:5005`. 2. **The host is not allow-listed.** Mattermost refuses calls to internal hosts unless you allow them. Go to **System Console** → **Environment** → **Developer** → **Allow untrusted internal connections to** and add the host from your Callback URL. This is the most common reason buttons fail silently. This step is optional. Everything except the Stop button works with the toggle off. ### Step 4: Account Linking People are linked **automatically by their Mattermost email**. If the email on someone's Mattermost account matches their nao account, they are recognised on their first message and there is nothing to type. If the two emails do not match, they can link manually instead: send the bot a direct message with `login `. The linking code is on the Mattermost settings page, tucked behind a collapsible section. Write `login ` without a leading slash. Mattermost intercepts slash commands, so `/login` never reaches the bot. ## Usage **Ask Questions** In a channel, mention the bot: ``` @nao What were our top 5 products by revenue last month? ``` The bot will: 1. Process your question using the same agent as the web interface 2. Generate SQL queries based on your context 3. Execute queries against your connected databases 4. Stream the answer into a single Mattermost message, editing it as the answer grows 5. End the answer with a bold **Open in nao** link to the full conversation **Thread Conversations** Once a thread exists, follow-ups inside it need no mention: ``` @nao What were our top 5 products by revenue last month? # ... bot responds in the channel ... # Reply in the thread under the bot's message: Can you break that down by region? # ... bot responds with regional breakdown ... ``` The first answer lands in the channel itself, and follow-up housekeeping messages go into a thread under it, so channels stay quiet. Direct messages are never threaded. **Direct Messages** In a direct message the bot answers every message. No mention needed. **Tables and formatting** Mattermost supports full markdown, so answers come through with real tables, code blocks, bold text, and links. Charts are posted as images. Large tables are capped at about 20 rows, with a note pointing to the full result in nao. **Feedback** React to an answer with 👍 or 👎 and the vote is recorded in nao, the same as the thumbs in the web UI. Remove the reaction to remove the vote. **What's Next?** Configure authentication, models, and messaging integrations Learn how the main chat interface works # MCP Endpoint Source: https://docs.getnao.io/nao-agent/connectors/mcp Expose nao as an MCP server so external AI clients can query your data nao can act as an MCP (Model Context Protocol) server, letting external AI clients (Claude, Cursor, Codex, and others) connect directly to your analytics agent. Once enabled, those clients can ask analytics questions, run SQL, browse your context files, and manage stories, all without leaving their own chat interface.