Agent Tools
Built-in tools available to the Foxl desktop agent
The Foxl desktop agent comes with a set of built-in tools organized into profiles, plus a few capability-gated tools (like generate_image) that register automatically when their credentials are available. Which built-ins are loaded is decided by the tool profile (Full by default - see below), not per message; your own custom tools and MCP servers are added on top of whichever profile is active.
Browser
| Tool | Description |
|---|---|
| browser | One pinned release of the agent-browser CLI (Rust + Playwright) driving Foxl's own Chrome profile under ~/.foxl/browser-profile - open <url>, snapshot -i, click @ref, fill @ref "text", screenshot. Asks first, unless you auto-approve it. See Browser Automation |
| browser_extension | Control your real Chrome tabs via the Chrome extension. Opening a page or tab, closing a tab, clicking (on an element or at a point), typing, pressing a key and choosing an option ask first, unless you auto-approve it; reading the page, listing tabs, scrolling and hovering do not |
| browser_take_over | Hand the page in Foxl's own browser to you when a step is yours (a CAPTCHA, an "are you human" check, a sign-in you would rather type yourself), and wait until you tap I'm done. In your own chats only. See When a step is yours |
In both, a step that orders, pays or books (a click on "Place order", "Pay" or "Confirm booking", a priced "Reserve", an Enter in a checkout field) follows your other approval rules with no card of its own, and the chat then shows what was bought; to approve each one on its own card, even with auto-approve on, turn on Settings > Safety > Purchases > Ask before purchases. Sending money and trading always ask; see Approvals.
Code & Files
| Tool | Description |
|---|---|
| file_read | Read files from your filesystem |
| file_write | Create or overwrite a file. Asks first, unless you auto-approve it |
| file_edit | Change part of a file: replace an exact piece of text, once or everywhere, and show the change as a diff. Follows the same approval rule as file_write |
| code_search | Search across your codebase with ripgrep (regex, file type filters) |
| view_image | View and analyze images |
A relative path in these tools means a path inside your workspace (~/.foxl/workspace/ on an installed app), so "save notes.md in your workspace" lands there; ~/... is your home folder. When a chat has its own folder (the folder button in the chat header), a path that names no folder points into that folder instead.
When the agent changes a file and reads or views it in the same step - for example rendering a diagram with a shell command and then looking at the PNG - the read waits for the change to finish, so it sees the new file rather than "file not found" or the old version. Reads that do not follow a change still run side by side.
Shell & System
| Tool | Description |
|---|---|
| exec | Run shell commands (bash, zsh, etc.) - covers git, npm, curl, and arbitrary shell pipelines |
| process | List, monitor, and kill running processes |
| terminal | Persistent terminal session for stateful shell workflows |
| request_skill_key | Ask for a key a skill needs, in a key card in the chat. Saved on your computer and given only to commands in a chat that uses a skill declaring that key; Foxl's AI does not see it. See Skills |
exec runs in your workspace (or the chat's folder, when the chat has one) unless the agent names another working directory, and a relative working directory is resolved against it too. On macOS and Linux, Settings > Safety > Run shell commands in a sandbox (off by default) runs exec and the terminals the agent opens inside a sandbox, the macOS sandbox on a Mac and bubblewrap on Linux; see Shell sandbox.
A command the agent runs in the background, such as a dev server or a long build, keeps running after the agent's reply until it exits or reaches its time limit (an hour, unless the agent sets another). Its step in the chat has a Stop button, in Foxl on your computer and on your phone. The button goes once the chat shows the command ended (for example when the agent checked on it), and otherwise stays until the time limit; a press after the command ended says it already finished.
When you ask the agent to tell you once such a command is done, the chat answers by itself when the command ends or reaches its time limit, with how it went, read from the last 2,000 characters of its output and of its errors, and you get the same notification as when an agent finishes, on your phone too. A command that succeeds without printing anything gets a one-line note in the chat instead of a reply ("finished, no output"). This works wherever the agent runs the command: a subagent's command answers in the chat that started the subagent, a scheduled run's in that run's conversation (a follow-up within the run's limits), a channel's such as Slack or Telegram in that thread, and a reply the chat continued by itself can arrange one too. A command started by a reply that itself followed a finished command can ask for one more report, up to three in a row, so a build, then its tests, then a deploy each report back. If Foxl restarts while the command runs (a restart stops background commands), the chat says once, after the restart, that the command stopped when Foxl restarted. No answer comes when you or the agent stopped the command, when the agent already read the result itself, or when you stopped the reply that started it: stopping that reply also stops what it arranged, including its subagents' reports, while a report you asked for in an earlier reply still comes.
Web
| Tool | Description |
|---|---|
| web_fetch | Fetch web pages and API endpoints |
| request_login | Ask you for a website's sign-in in a Sign in to card in the chat. What you type goes to the vault on your computer, never to the model. See Sign-ins and saved keys |
| fill_login | Sign in on a site's own https page in Foxl's browser with the sign-in saved in the vault, without the model seeing it, and say so in the chat. On another site, such as a single sign-on page, it asks once for that site and remembers your answer, or signs in right away when you named that site yourself in a chat where Foxl has read nothing else. The first time, it asks once whether to ask you first. With Settings > Safety > Ask before Foxl uses a saved sign-in on, it asks first, unless you saved that sign-in in a card in this chat in the last 30 minutes or chose Always allow for the site |
| imap_mail | Read and tidy your Gmail, Naver Mail, Daum Mail, Kakao Mail, iCloud Mail or Yahoo Mail with the app password you saved in a card in the chat, and leave mailing lists when you ask. It changes mail only through the cleanup card you apply, and never sends or deletes. See Mail from Gmail, Naver, Daum, Kakao, iCloud and Yahoo |
| site_policy | When you tell Foxl never to sign in to a site ("never sign in to coupang.com"), add it to the Web Access blocked list at once, with Undo on a line under the reply. Settings > Web Access lists these under Sites Foxl never signs in to. In your own chats only |
Web search is the model provider's own search, not a Foxl tool, so it exists only where the provider runs one. On GPT-5.5 and GPT-5.4 through your own AWS account, the model searches with Amazon Bedrock Web Search when a question needs current information, and cites the pages it used; the searches stay inside AWS and are billed by AWS. Claude and GPT-6 models on Amazon Bedrock have no web search there, so with them the agent reads pages with web_fetch or the browser. Turn it off with the Web search switch in Settings > Web Access.
An answer that read web pages lists them under the answer as N sources: the pages it opened with web_fetch, its own browser or the Chrome extension, and the pages the model's own web search cited.
Memory
| Tool | Description |
|---|---|
| memory_save | Save key-value pairs to the SQLite memory database |
| memory_recall | Search and retrieve from the memory database |
| workspace_memory_save | Save content to workspace markdown files (USER.md, MEMORY.md). Asks first, unless you auto-approve it |
| workspace_memory_search | Search across all workspace markdown files |
| workspace_memory_read | Read a specific workspace file |
| workspace_memory_edit | Edit sections within workspace files. Asks first, unless you auto-approve it. SOUL.md, HEARTBEAT.md and AGENTS.md change only when you ask in your own chat, with Undo on the card under the reply; after it read a page, a mail or a tool result in that turn, the change waits for your tap on Apply |
| conversation_search | Search your own past conversations by content, so the agent can find what you decided last week |
| read_thread | Read a Foxl conversation the agent was pointed at, newest turns first |
| notes_search | Search your Foxl Notes meetings and notes, transcripts included, and read one in full. Offered while Let Foxl Agent search your notes is on in Settings > Notes; each note an answer used is listed under it as a source |
| integration_save | Add what made a site or web API work (endpoints, steps, quirks) to its entry under Added by Foxl, once this chat reached it and its sign-in or key is saved in the vault on your computer, so the next task there starts from it. A site with no saved sign-in or key is not kept. A password, key, token or cookie is refused. No prompt: the note holds no secret |
| integration_read | Look a website or API up by name or address before working with it, and read what was learned there before |
| writing_style | Learn how you write from messages you paste or your sent Outlook mail, show you the style, and save it per audience, with Undo on the card under the reply. In your own chats only. See Writing style |
With Save memories off in Settings > Memory, the agent gets none of the memory_*, workspace_memory_*, integration_* and writing_style tools, nor request_login and fill_login. What integration_save keeps stays on your computer; see Security.
Scheduling
| Tool | Description |
|---|---|
| schedule | Create, list, update, delete, and trigger schedules (one-time, cron, webhook, and watches that wait for something on a page; the Feed Generator heartbeat is set up in Settings). Creating or changing one asks first, unless you auto-approve it |
| event_trigger | Set up, list, pause, resume and delete triggers: new mail in Outlook, a new message in a Slack channel, or new mail in a mailbox Foxl reads with an app password (Naver Mail, iCloud, Gmail by app password and others) starts a run. It starts at once, with Pause and Delete on its card; one set up after the chat read a web page, an email or a command's output waits for your Start on the card (Settings > Safety can ask before each one starts). In your own chats only, and offered only while one of those can be read or a trigger exists |
| check_ins | See or change when your check-ins run ("check in twice a day", "not before 9"), or pause them until a date. In your own chats only |
| feed_add | Heartbeat runs only - posts an item to your For You feed; urgent also raises a system notification. Never offered in a chat |
Agent
| Tool | Description |
|---|---|
| session_status | Get current date/time, timezone, and session info |
| sessions_spawn | Spawn an autonomous sub-agent for a parallel task (at most 5 at once, across all chats) |
| sessions_status | Check sub-agent progress |
| sessions_stop | Stop a running sub-agent |
| sessions_message | Message a sub-agent: a running one reads it at its next tool call, a finished one resumes with it. See Sub-agents |
| agent_role | Make a role (a sub-agent definition) from a chat: shows you the role and saves it, with Undo on the card under the reply. In your own chats only. See Making a role from a chat |
| skill_import | Turn a workflow from the chat, or a skill you point it at, into a skill: shows you the whole skill and saves it, with Undo on the card under the reply. In your own chats only. See Skills |
| skill_save | When a job took several steps, you will ask for it again and no skill you have covers it, save it your way as a skill at once, with Undo under the reply. After a read from elsewhere, a skill that names an address you did not write waits for your tap, and a channel thread does not save one. In your own chats and your own channel threads only. See Skills |
| update_plan | The checklist for a task with several steps, pinned above the message box and updated as the agent works. For the main conversation only - a sub-agent reports its progress in its result instead - and not available on a Claude Pro/Max sign-in |
| report_issue | Pass a problem to the Foxl team when you tell the agent something in Foxl went wrong. The report is shown as a card under the reply with exactly what it sends, and nothing is sent until you press Send: Edit changes its words first, and Not now keeps it unsent, with Show report to bring it back; the chat's last messages go with it only if you tick Attach the last messages of this chat, which shows them first. Don't ask again sends it and lets Foxl on your computer send later reports itself, without the chat's messages; the card then shows each one as sent, and Settings > Safety > Don't ask again removes it. In your own chats only - not in schedules, channels or sub-agents - and not available on a Claude Pro/Max sign-in or the on-device model; use Settings > General > Report an issue there |
| offer_replies | When a reply ends with a quick question ("Want me to dig in?"), offer up to three short answers as buttons under it in the phone apps and a phone's browser; a tap sends the answer word for word as your message. In your own chats only, and not on a Claude Pro/Max sign-in or the on-device model |
Computer use
| Tool | Description |
|---|---|
| computer_open_app | Open an app the agent may use. It does not ask first unless Settings > Safety > Ask before Foxl uses an app for the first time is on; then the first time a chat needs an app, Foxl asks in the chat (Deny, Allow, Always allow) |
| computer | Look at a screenshot of that app's window only, and click, type, press keys and scroll in it with Foxl's own pointer |
| computer_elements | Read that app's window as a list of its controls (buttons, fields, checkboxes, menus) and press, fill, pick, check or expand one by name, checking each result, without a screenshot. On macOS and Linux |
They are offered only while Allow computer use is on in Settings > Computer use (on by default), and only to a model that can view images, because computer_open_app and computer answer with screenshots; with any other model the agent says so and suggests switching. computer_elements is not offered on Windows. See Computer Use.
Integrations
Integration tools are only available after you connect the corresponding account in Settings > Tools > Integrations.
| Tool | Actions | Description |
|---|---|---|
| outlook | 25 actions | Email (inbox, read, send, reply, forward, search, folders, drafts and reply drafts in the original conversation, sending a reviewed draft once, attachments, contacts, move, categories, update, tidying the inbox at once with Undo, and leaving mailing lists), Calendar (view, meeting, availability, room booking, search, shared list), To-Do (lists, tasks, checklist) via Microsoft Graph API |
| slack | 16 actions | search_messages, whoami, get_recent_messages, channels_list, conversations_history/replies/add_message/open/members, check_replies_batch, users_lookup/profile_get, attachment_get_data, reactions_add/remove, file_upload |
| gmail | 1 action (4 with reading on) | send: an email from your Gmail address to at most 25 recipients, plain addresses only, with no attachments. Send only unless you turned reading on when connecting: then also search, read and reply. It never deletes or changes mail. See Gmail |
These tools use OAuth user tokens - they act as you, not as a bot. Connect them under Settings > Tools > Integrations.
Tidying your inbox. Ask Foxl to clean up your inbox, in Outlook or in a mailbox it reads with an app password (Gmail, Naver and the others), and it makes the changes right away: archive, move to a folder, flag or mark as read. The card that follows lists each message it changed, with who sent it, its subject and the change, and Undo on each one and Undo all put messages back, on your computer and your phone, for a day, also after Foxl restarts. Undo leaves a message you changed in your mail app since then as it is, and the card says so. Foxl never deletes mail: a move into the trash (Deleted Items in Outlook), Junk or Spam (your mail service deletes mail there on its own), sent mail, drafts or the Outbox is refused.
To check every change before anything happens, turn on Settings > Safety > Review inbox clean-ups before Foxl applies them. The card then comes first instead: uncheck anything you disagree with and press Apply, and only the checked messages change, once. See Steps that always ask.
Leaving mailing lists. Ask Foxl to unsubscribe you from newsletters or promotions, in Outlook or in a mailbox it reads with an app password, and it does it right away. A card under the message then shows, for each sender, what was done. Foxl uses only the unsubscribe address the sender put in the message's own headers. It never follows a link in the message's text, because fake "unsubscribe" links are a common phishing trap.
- One-click unsubscribe: Foxl sends the sender's one-click request. It does this only when the sender's digital signature on those headers checks out.
- Unsubscribe by email: in Outlook, Foxl sends the email from your account. It does this only when the address is at the domain that signed the headers. In a mailbox Foxl reads with an app password, the card shows the address for you to write to.
- Unsubscribe page: the card links to it for you to open. Foxl never opens it itself.
Foxl leaves a list only when you ask in your own chat, never because a schedule, a check-in or a message asked it to.
Connecting a service from the chat
When a task needs a service that is not connected yet, Foxl asks for it in the chat with a
Connect card that names the service (request_connection). It does this for Microsoft 365,
Slack and Gmail, and for seven services that run their own MCP server: Linear, Notion,
Atlassian (Jira, Confluence, Trello), Granola, Canva, Sentry and Todoist.
- Asked in your own chat on your computer, there is nothing to click first: the service's own sign-in page opens in your browser at once, and that page is where you decide. For one of the seven MCP services, Foxl first adds that service's own MCP server to the Custom tab (or switches on the one you already have for it), and its page is where you choose what Foxl can use. Once you finish, the card says Connected (service) with Disconnect, and Foxl goes on with the task, using the service's tools in the same chat. If you refuse on the service's page, a server Foxl added for it is taken away again; if you do not finish signing in, the server stays on the Tools page with Sign in, and the next request picks it up from there. If the page cannot open by itself, the card says so and shows Connect.
- Asked from a phone or app.foxl.ai, the card has Connect too for Linear, Notion, Atlassian, Granola, Sentry and Todoist, and for an MCP server whose sign-in expired. The service's sign-in page opens on the phone (in the app's sign-in sheet, or a new tab in a phone's browser); once you finish there, your phone passes the result to Foxl on your computer, which completes the connection, and the task goes on. This needs this version of Foxl on the phone and the computer, the phone's secure connection to your computer, and your computer signed in to Foxl and online. For Microsoft 365, Slack, Gmail, Canva and a server that refuses a sign-in from a phone, the phone's card says Finish on your computer, and Foxl on your computer shows the same request: click its notification, or Connect on its card, and the sign-in page opens there. Not now on the phone stops waiting.
- A request from a channel thread, a schedule or a trigger waits for Connect on your computer: no sign-in page opens by itself for a run you did not start.
- Disconnect on the card removes the account, as Disconnect under Settings > Connect > Integrations does, or for one of the MCP services signs its server out, as Sign out on its Tools row does. It works from the phone too.
- Asana, HubSpot, Figma and GitHub get no card: their servers do not let an app register itself for the sign-in.
Mail from Gmail, Naver, Daum, Kakao, iCloud and Yahoo
Ask Foxl to read your Gmail, Naver Mail, Daum Mail, Kakao Mail, iCloud Mail or Yahoo Mail ("show
me what's in my Naver Mail", "did my train ticket arrive by email?") and it reads it over IMAP with
the imap_mail tool. No account to connect in Settings first:
- The first time, Foxl asks in the chat with a card titled (service) app password: your email address and an App password, with Where do I get this? linking to your mail service's own page for making one. They are saved on your computer, and Foxl's AI never sees the password; it is used only to read your mail from that service's mail server.
- Reading changes nothing. It lists your folders, finds messages by date, sender or subject (or only unread ones), and reads a message's text and the names of its attachments. Reading a message does not mark it as read, and it never sends or deletes mail. It connects only to that service's own IMAP server, over TLS.
- Cleaning up your inbox works as in Outlook (see Tidying your inbox above): the changes are made at once, with Undo on the card that follows. Archive puts a message in the folder your mail service marks as its archive; in Gmail that is All Mail, which takes the message out of the inbox. A service that marks no archive folder is offered moves to its own folders instead, and one that cannot move a message in one step, or does not say which folder is its trash, gets only flag and mark as read.
- When the service refuses the password, Foxl tells you what to check and asks with the card again (Enter the (service) app password again) instead of trying again on its own. Every one of these services signs a mail app in with an app password, not your account password; Naver, Daum and Kakao also need two-step verification on the account and IMAP turned on in their mail settings. Gmail makes app passwords only for an account with 2-Step Verification on, and a work or school Google account may not offer them.
Mail you read this way is treated like Gmail's: as data written by its senders, not as instructions.
Channels
| Tool | Description |
|---|---|
| channel_send | Send messages to connected channels (Telegram, Slack, iMessage and the others), and files to Telegram (up to 50 MB); on the other channels a file is refused. Asks first, unless you auto-approve it; a file outside your workspace or a recipient outside the channel's allowlist is refused either way |
Media
| Tool | Description |
|---|---|
| generate_image | Generate or edit PNG images with gpt-image-2 through your ChatGPT Plus/Pro subscription. Pass inputImages paths to edit or compose from existing images; omit for pure text-to-image. Auto-saved as generated/generated-<YYYY-MM-DD>-<slug>.png inside your workspace (~/.foxl/workspace/ on an installed app). |
generate_image uses your ChatGPT subscription, not an OpenAI API key. The tool only registers when an OAuth credential file is found at ~/.codex/auth.json, ~/.chatgpt-local/auth.json, or the path set by CODEX_HOME / CHATGPT_LOCAL_HOME. No billing beyond the ChatGPT plan you already have.
Image editing and composition
Pass one or more existing image paths as inputImages to switch from text-to-image into edit / compose mode. When you drag images into the chat, Foxl saves them with the conversation (in ~/.foxl/data/attachments, outside your workspace) and injects the absolute paths into your message, so the agent can reference them without base64 round-tripping through the model's tool-call args.
Examples the agent can drive end-to-end:
| Prompt | What the agent does |
|---|---|
| "이 사진 만화풍으로 바꿔줘" (attach a photo) | Calls generate_image({ prompt: "convert to watercolor anime style", inputImages: ["<path>"] }) |
| "이 두 이미지 합쳐서 하나로" (attach 2+ photos) | Calls generate_image({ prompt: "compose both subjects into one scene", inputImages: ["<a>", "<b>"] }) |
| "blog hero: a cozy cafe at dusk, 1536x1024" | Calls generate_image({ prompt: "...", size: "1536x1024" }) |
Up to 10 input images per call. Output PNG lives at a date-stamped filename with a slug derived from the prompt, in a generated/ subfolder to keep your workspace root clean.
AI-generated label
A picture generate_image makes says so twice. Inside the file, the saved PNG and the preview you download or share from the chat carry the IPTC "Digital Source Type" tag for "Created using Generative AI"; an image that already arrives with its maker's signed Content Credentials saying so is saved exactly as it came. In the chat, the picture carries a small AI-generated label in its corner, on its thumbnail when the steps around it are folded, and in the viewer it opens. A picture you attached, or a screenshot the agent took, has no label.
Tool Profiles
Foxl uses tool profiles to control how many tools are loaded, saving tokens in the system prompt. A tool definition costs roughly 200 tokens, so the profile is a direct trade of capability against every turn's input:

| Profile | Tools | Token Cost | What it adds |
|---|---|---|---|
| Minimal | 13 | ~2,600 | memory, workspace memory, conversation and notes search, thread reading, session status, view image, the problem report, quick replies |
| Default | 25 | ~5,000 | + file read/write/edit, exec, process, the skill key card, the plan checklist, channel send, generate image, skill import and skill save, writing style |
| Standard | 34 | ~6,800 | + code search, web fetch, terminal, the two website-memory tools (integration_save, integration_read), the sign-in card and fill (request_login, fill_login), the mail reader (imap_mail) and never-sign-in (site_policy) |
| Full | 45 | ~9,000 | + browser, browser extension, browser take-over, schedule, check-ins, triggers, sub-agents, roles (and the three computer use tools while computer use is on) |
The profile picker's own labels (about 1.4k to 4k tokens) were written when the profiles held fewer tools; the table counts today's tools at about 200 tokens each.
A tool whose credential or switch is missing is not sent even when its profile includes it: generate_image needs a ChatGPT sign-in, notes_search the Notes search switch, the memory and website-memory tools Save memories, the computer use tools Settings > Computer use, request_skill_key a skill that declares a key, and event_trigger Microsoft 365, Slack, a mailbox with a saved app password, or a trigger you already have. offer_replies, report_issue, writing_style, check_ins, event_trigger, site_policy, agent_role, skill_import and skill_save work only in your own chats.
The agent runs in Full profile by default. Switch profiles on the System tab of Settings > Tools > Tools/MCP to save tokens on simpler tasks; the same tab lists every built-in and lets you switch one off. In a language other than English it describes each built-in tool in that language, and searching it still finds a tool by its English description. generate_image lives in the media category and is included in Default, Standard, and Full; Minimal omits it to preserve its token budget.
Integration tools (outlook, slack, gmail), the Connect card (request_connection) and the phone call tool (phone_call) are not counted above; they are in every profile except Minimal, the integration tools once their account is connected and phone_call on a Mac. The profile is the upper bound for built-in tools, including the ones a skill asks for: a skill cannot switch on a tool the profile leaves out. Your own custom tools and MCP tools are not filtered by the profile - a tool you wrote or added is available whichever profile you are on.
Git operations route through exec (e.g. exec git status, exec git push). There is no dedicated git tool - piping and custom flags work better from exec anyway, and exec's own approval rule covers them.
The same tools in Foxl Notes
The same tools serve Foxl Notes. On the desktop, Notes' AI Assistant is a Foxl Chat conversation beside your note, with the same tool steps as chat: it can open your files, search your code and notes, fetch a link or run a command - whatever the System tab has switched on, custom tools and MCP servers included. The Live panel's AI Assistant is the same conversation, so questions asked there and in the main window land on one thread. One switch governs both, Settings > Notes > AI features > Use Foxl Agent tools, on by default; with it off the assistant reasons about the transcript alone. It needs Foxl running on your computer. The automatic work - the post-meeting summary, the auto title, live translation and dictation clean-up - deliberately uses no tools, so nothing you were not waiting for got slower.
Custom tools
Beyond the built-ins you can add your own on the Custom tab of Settings > Tools > Tools/MCP, which also lists your MCP servers. Six kinds:
| Type | What it is |
|---|---|
| shell | Runs a shell command. |
| script | Evaluates JavaScript. |
| file | Reads or writes a file, from a path template. |
| http | Calls an HTTP endpoint. |
| mcp_local | An MCP server started as a local process (stdio). |
| mcp_remote | An MCP server reached over HTTP. |

A custom tool that runs a shell command, evaluates JavaScript, or writes a
file is gated exactly like the built-in exec tool. That was not always
true: the approval gate matches on a tool's name and treated an unrecognised
name as allowed, so a custom tool wrapping a shell command was a shell with no
per-call prompt - and it is the model that fills in the arguments.
A new install auto-approves every tool, so in the shipped default these calls do not prompt either. Turn that off in Settings > Safety and the per-call prompts appear for custom tools and built-ins alike; a rule you set yourself for a specific tool always wins. Read-only file tools, HTTP tools and calls to MCP tools are unaffected either way; for an MCP server, switching the tool on is the approval (the MCP servers section below explains). See Security.
A file tool's blocked-path list is applied to the path it actually opens, not
to the template - so ~ is expanded first (a ~/notes.md template works), and
/etc/passwd spelled //etc/passwd, reached through .., or under macOS's
/private/etc is covered.
An http tool encodes the values the agent fills into its URL, so a value cannot add
query parameters, cut the address short or climb out of the tool's path with ... A value
in the path can still span segments, such as users/123 for
https://api.example.com/{{endpoint}}. A tool with saved headers (where an API key usually
goes) sends them only to the host written in its URL: when a parameter chooses the host, the
call is refused with a message saying how to fix the tool. A tool with no saved headers
whose host or base address comes from a parameter can reach public websites only.
Saved secrets show as dots. The Custom tab shows the saved API key, environment values and headers of an MCP or HTTP tool as dots, on your computer and through your phone or app.foxl.ai. Saving with the dots left in place keeps what is stored; type a new value to replace one. On your computer, an MCP server that is switched off still shows its environment values, so you can see what switching it on will start.
Foxl's database keeps those keys, environment values and headers only encrypted: a plain copy
an earlier version saved is removed the first time Foxl reads that tool (an MCP server's entry in settings.json, below, still holds its environment values
and key as you wrote them). If Foxl can no longer read them, for example after moving to a
computer without your old key file, the tool says so and asks you to edit it in Tools and enter
them again, instead of running without them.
Permission rules accept hyphens in a tool name. A custom tool called
deploy-prod keeps its name, and every MCP tool is exposed as
<server>__<tool> where the right half usually has one, so a rule written with
an argument pattern like deploy-prod(rm *) matches instead of being stored
under a name no tool is ever called.
MCP servers
Foxl is an MCP client. It speaks the current 2026-07-28 revision and every older one, and asks each server which revisions it supports so the newest one both sides know is used:
| Revision | Shape |
|---|---|
| 2026-07-28 | Stateless - no connection handshake, no session header. |
| 2025-11-25, 2025-03-26, 2024-11-05 | The older handshake. |
There is nothing to configure either way. The server's row on the Custom tab shows which revision was agreed, so a server on the new stateless protocol is visibly different from one on an older one.
Import MCP servers on the Custom tab adds the servers you already set up in
another app: Claude Code (~/.claude.json, the servers added with user scope), Claude
Desktop (claude_desktop_config.json, macOS and Windows), Codex (~/.codex/config.toml),
Cursor (~/.cursor/mcp.json), Kiro CLI (~/.kiro/settings/mcp.json, and the servers in
its agent files under ~/.kiro/agents/, where an agent you installed keeps its own) and
Gemini CLI (~/.gemini/settings.json).
Foxl reads those files on your computer and shows a checklist; tick the servers to add.
Local servers keep their command, arguments and environment, remote ones their URL and an
Authorization: Bearer key. What Foxl cannot carry over is listed under the server rather
than dropped: a working folder (cwd), a Cursor envFile, other headers, a bearer token
Codex reads from an environment variable, the tools Kiro hides with disabledTools, and a
$VARIABLE that Gemini CLI fills in from your environment. A
server several Kiro agents define differently is offered once, with a note naming the other
agents. A server switched off in Codex, Kiro or Gemini CLI arrives switched off. The same import is part
of Settings > Advanced > Import and export, which also brings over Claude Code's or
Codex's instructions and skills.
Your servers in settings.json. Foxl keeps your MCP servers in
~/.foxl/settings.json (%USERPROFILE%\.foxl\settings.json on Windows) as well, in the
mcpServers format Claude Code, Cursor and Kiro use, so a block can be copied in or out and
an agent can add a server by editing one file:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "..." }
},
"linear": {
"type": "http",
"url": "https://mcp.linear.app/mcp",
"headers": { "Authorization": "Bearer ..." }
},
"slow-build": { "command": "node", "args": ["build.mjs"], "disabled": true, "toolTimeoutSec": 120 }
}
}- A server you add, change or import on the Custom tab is written to the file. Foxl reads an
edit of the file within a few seconds, so the agent's next message can use it, and the
Custom tab shows it when you come back to the window. Everything else in the file, and any
key Foxl does not use (Kiro's
autoApprove, say), is kept. - A server the file adds is saved switched off, and so is one whose command, arguments,
environment or URL the file changes. Its row says it was added or changed in settings.json
and shows the command or URL it would start; it starts when you switch it on from the
Tools page on this computer. If the file changes it again while the page is open, the
switch (and the Play button) asks you to look again first. If the file goes back to the
command or URL you had switched on, the server is switched on again. Switching a server
off (
"disabled": true), adescriptionandtoolTimeoutSec(seconds) apply directly. - Taking an entry out of the file removes that server once the entry has stayed out for 15 seconds, so a cut and paste, or a quick checkout of another version of the file, removes nothing. A server removed this way comes back as it was (switched on, still signed in) if its entry returns under the same name with the same command or URL within a day. Renaming a key renames the server when its command or URL stays the same; with a new one it is a new server, saved switched off.
- Deleting the file, or the whole
mcpServersblock, removes nothing: Foxl writes your servers back. So does an edit that leaves out every server at once and puts something else there (an agent writing the whole file to add one server); the Custom tab says so. To remove them all, write"mcpServers": {}. - A file that is not valid JSON is neither read nor written: the Custom tab says which line the error is on, and Foxl leaves the file alone until it reads again. A file you made read-only is read as usual, but Foxl does not write to it, and the Custom tab says so. A single entry Foxl cannot use is listed with the reason and left as you wrote it.
- The file holds environment values and keys as you write them, like the files it copies
from. Foxl creates it readable only by you.
${VAR}references are not expanded, and the Custom tab names an entry that uses one.
Signing in to a remote server. A remote MCP server that asks you to sign in shows Sign in on its row. One click opens the server's sign-in page in your browser; once you approve there, the row connects and lists the server's tools, with no key to paste. Foxl renews the sign-in on its own, and Sign out removes it from this computer (it does not revoke the grant at the server). When a sign-in stops working during a task (the server refused to renew it), the step that needed it shows a Sign in to (server) again card in the chat, which opens the server's sign-in page the way the Connect card in Connecting a service from the chat does; once you approve there, the step runs again. A new chat still reaches a server that is signed out: Foxl offers it one sign-in step in place of its tools, so asking for something that needs the server brings up the same card, and the server's tools are added to that chat once you are signed in. If signing in fails, the row says which step failed and shows the server's own message. It works with servers that let an app register itself during sign-in, including a server that describes its sign-in the older way (the MCP 2025-03-26 revision's), such as Atlassian's, which offers Sign in too instead of an error. The sign-in is kept on this computer, is not sent to the phone or app.foxl.ai, and is started from Foxl on the computer itself. For the services in Connecting a service from the chat, the chat's Connect card adds the server and starts this sign-in for you.
Tool call timeout. A call to an MCP tool waits 30 seconds by default. A server whose
tools are slow by design (a build, a crawl, a report) can be given up to 600 seconds (10
minutes) in its row's Tool call timeout field; Codex's tool_timeout_sec is carried
over on import, and a Codex server without one keeps Codex's own 60-second default.
Images reach the model. A picture an MCP tool returns, such as a screenshot from
Playwright MCP, is sent to the model as an image it can see and shows in the tool step, like
view_image. On a ChatGPT sign-in it goes in the message after the tool's result, the way a
picture you attach does.
Resources. A server that says it offers resources (documents, records or files it lets an
app read) gets one more tool, <server>__resources. Its list action returns the ones the server
lists, a page at a time, with the URI templates the server accepts (such as tickets://{id});
read returns one by its URI: text as text, up to 100,000 characters per answer, with each
answer saying where to continue so a long document is read in parts; a PDF as its text (the same
reader file_read uses, on macOS and on Linux with pdftotext); a picture as an image the model
sees; and any other file saved in the chat's files (mcp-downloads/<server>/ in the workspace),
with its path, so you and the agent's other tools can open it. When one of the server's tools
answers with a link to a resource, the agent opens it with the same tool, also when list does
not show that resource. A server that offers no resources, or whose only listed resources are the
screens an MCP App shows inside its host (ui://), adds no tool, and switching the server off
removes this one with the rest. A server on your computer can answer with up to 64 MB at a time;
a larger answer ends that call with a message saying so, and the server starts again on its next
use.
Prompts. A server that lists prompts (message templates for tasks it knows, such as
summarizing a ticket) gets one more tool, <server>__prompts. Its list action returns each
prompt's name, description and arguments; get fills one in with its arguments and returns its
messages, which the agent uses when they fit what you asked. The server's words never outrank
yours. A server that lists no prompts adds no tool.
Turning a tool on is the approval. An mcp_local row is a program on your
computer, and once the row is switched on Foxl starts it without asking - when you
save the row, re-check it, open it in the inspector, or the agent uses one of its
tools. You switch an MCP tool on in Foxl on this computer, or from your phone (the Foxl
app on your phone, a phone's browser or app.foxl.ai, while this computer is connected). On
the phone the row then says what runs (the command, or the address it connects to), the
names of its settings with their values hidden, and the tools Foxl can now use, with
Switch off. A server that settings.json or an import added shows that same list before
it starts, and a server imported from the phone waits switched off for it. A server added or
changed from any other client, or by an edit of settings.json, is saved switched off, with
a note saying so, and the agent does not see its tools until you switch it on. With
Settings > Safety > Only this computer can widen what Foxl may use on, a server saved
from the phone is saved switched off too, until you switch it on in Foxl on this computer.
An edit that leaves the command, arguments, environment and URL alone keeps a switched-on
server running. An mcp_remote row is
a URL - nothing is started on your computer, and the same rule decides who can
switch it on.
Three things worth knowing:
- A local server keeps running between calls. An
mcp_localserver is started once and reused for later calls, so a server that keeps state in its process keeps it: with Playwright MCP, the pagebrowser_navigateopened is the pagebrowser_take_screenshotcaptures. Your own chats keep one server running between calls; a channel thread (Telegram, Slack, iMessage and the others) or a scheduled run gets its own server, which stops when that run ends, so a message that arrives through a channel never sees a page your chat opened. If such a run needs a server your chat holds idle, your chat's server is restarted, and its next call starts fresh. Playwright MCP started without--isolatedkeeps one browser profile on disk (with its logins), and only one process can hold it: while a channel or scheduled run is using it, a call from your chat or from another run answers "Browser is already in use", and so does a run's call made while a call from your chat is still running. Add--isolatedto its arguments if channels or schedules use it too. Your chats' server stops after 15 minutes without a call, and any server stops when you switch its row off, change its command, arguments or environment, or delete it, and when Foxl quits. On macOS and Linux it also stops when Foxl's server crashes or is force-quit, including a server (or a wrapper script around one) that was still starting up. With Keep running when closed on, the background service keeps running after you quit, so your chats' server stops after those 15 minutes instead. If it crashes, the next call starts it again. - A long tool list is read in full. Servers may hand their catalog over in pages; Foxl walks every page rather than reading the first one and treating it as the whole list.
- A slow tool is not called twice. When a tool took longer to answer than the app was willing to wait, the app used to assume its guess about the protocol was stale, reconnect, and send the same request again - so a tool that sends mail sent it twice while the agent saw one call and one error. It now retries only when nothing can have reached the server, or when the request is a read that cannot have an effect.
Approvals
Which calls ask first is set in Settings > Safety. The full rules are in Security; in short:
- Auto-approve all tools is on for a new install, so scheduled runs, runs from your phone and channel messages never wait on a prompt nobody is there to answer. The switch's own description says what that costs: anything the agent reads can lead it to run a shell command without asking. Turn it off and shell commands, file writes and other risky tools ask first. While it is off, the message box shows a small Asks first label; click it to turn auto-approve back on or to open Safety settings.
- Built-in rules (read-only) lists the rules Foxl ships. Built-in blocks still apply while Auto-approve is on; built-in prompts are skipped. In a language other than English, hover a rule for its explanation in that language.
- Two kinds of step ask every time, even with auto-approve on: a mail or a message that carries a card number, a Korean resident registration number or a US Social Security number (the card names where it goes and the number masked), and a step in the browser tools that sends money, moves it between accounts or buys or sells shares or crypto (the card names the amount and the site). Nothing happens until you answer, and Foxl never goes ahead after a countdown. Typing such a number into a page goes ahead at once unless Ask before sending a card or ID number is on in Settings > Safety. In chats you start in Foxl on your computer, the first kind's Don't ask again covers one thing only: the same card or Social Security number (by its last four digits), or a resident registration number (by its kind), sent to the same recipients. A transfer and a trade never offer it. See Steps that always ask.
- An ordinary purchase goes ahead, and one you cannot take back asks first. A click or an
Enter in the browser tools that orders, pays or books ("Place order", "Pay", "Confirm booking",
a priced "Reserve") runs with no card of its own, and so does a subscription, a plan change, a
refund or a cancellation; the chat then shows what was bought. A purchase shows its card first
when the page says it cannot be undone, the chat opened another site or read outside text first
(a site it reached only through a link on a page is not counted yet), it comes from someone
else's channel thread or a run nobody is watching, or it is over a limit you set in your own
words ("up to $80"; see the browser's safety measures). So do a
subscription and a step Foxl cannot tie to one button on a checkout; a cancellation and a refund
show it only in those runs and after such reading, and a step on a page Foxl could not read only
in those runs. A
purchase that a shell command sends (
curl,wget) shows its card every time, with no Always allow. The switches under Settings > Safety > Purchases, all off by default, bring the card back every time: Ask before purchases, Also ask when Foxl cannot tell which button a step presses and Ask before subscriptions, plan changes, refunds and cancellations. Allow covers that one step: no card offers Always allow purchases. Only the Foxl window on your computer can turn one of these switches off. - By where a run comes from sets Full access, Ask first or Read only for your chats, schedules and the heartbeat, channels (Telegram, Slack, iMessage and the others), and the Notes assistant. All four start on Full access. Check-ins and the brief, and runs started by incoming mail or messages, follow the schedules row until you choose otherwise, and still ask before sending, paying or a step that could carry what they read off your computer (details).
- Run shell commands in a sandbox (macOS and Linux, off by default, with Allow network)
limits what
execand the agent's terminals can reach. - A prompt can be answered in Foxl on the computer, or in the Foxl app on your phone or on app.foxl.ai while it is connected to that computer. On a Mac, the notification for a waiting prompt has a Deny button; to allow, open Foxl and answer there.
- Not now waits. In your own chat, Not now on the phone's approval card, or no answer within a minute, leaves the step Waiting instead of refusing it: it did not run, and the agent carries on with what does not need it. The step stays in its chat (and in the phone's "waiting for approval" count) for 12 hours, also across a restart. Approve or Allow later runs exactly that step, with the same arguments, and the chat continues; a different step asks again. When the 12 hours pass, the chat says the step expired and nothing ran.
- A schedule's or a check-in's step waits the same way, in that run (open it from the Schedules page or from the run's post in the From Foxl chat), also when nobody is at Foxl to ask. Approve later makes exactly that step once, after the run has ended, and the run says what came of it. That turn makes nothing else: ask there for anything more the run needed.
- Computer use, a step that asks before it pays, books or moves money, a sign-in, the heartbeat watch, the Tools page and channel runs never wait: there Not now refuses, as before. (A Sign in to card that asks for a sign-in you have not saved is different: in your own chat its Not now waits; see Sign-ins and saved keys.)
- When Foxl asks in your chat while you are away from the computer, your phone gets a notification that names only the kind of step ("Foxl needs your OK to send an email"), under Questions and approvals in the app's notification settings. The check-in names the steps still waiting in the From Foxl chat ("Still waiting for your OK: run a command in storefront").
- Hooks (below) are listed at the bottom of the same pane.
Hooks
Hooks are your own commands that Foxl runs when you send a message, when a chat starts, before and after a long chat is compacted and around tool calls, written in the Claude Code hooks format so an existing hooks block can be copied in. Put them in ~/.foxl/hooks.json (%USERPROFILE%\.foxl\hooks.json on Windows):
{
"hooks": {
"PreToolUse": [
{ "matcher": "Bash", "hooks": [ { "type": "command", "command": "~/.foxl/block-rm.sh", "timeout": 30 } ] }
]
}
}PreToolUseruns before a tool call, with the event as JSON on stdin (tool_name,tool_input,tool_use_id,session_id,cwd). Exit code 2 blocks the call, and what the command printed to stderr is the reason the agent reads; a JSONpermissionDecisionof"deny"on stdout blocks too, and"ask"shows the usual approval prompt for that call, as an ask rule would, even with auto-approve on. Any other non-zero exit is logged and the call goes on.PostToolUseruns after a call with the result astool_response. Exit code 2 adds its stderr to the result the agent reads; the tool has already run.Stopruns after each finished turn of a chat or a channel reply, withlast_assistant_message, for notification and logging. It does not run after a subagent, a schedule or the heartbeat. Unlike Claude Code, exit code 2 does not make the agent continue.UserPromptSubmitruns when you send a message, before the model sees it, with your text asprompton stdin. What it prints (or a JSONhookSpecificOutput.additionalContext) is added to that message for the model, and is not shown in the chat. Exit code 2, a JSON"decision": "block"or{"continue": false}blocks the message: the model never gets it, the conversation does not keep it, and the chat shows the hook's stderr,reasonorstopReason, followed by your text unless the hook setssuppressOriginalPrompt. It runs for your chats on this computer, also when you type from your phone or app.foxl.ai, and for your own messages in a connected channel; not for other people writing to you there, also when that channel lets them use your full agent, and not in the Notes assistant or Foxl Code. In a group chat, the reply to a blocked message says only that a hook blocked it. A message you type while the agent is working runs the hook too: one it lets through reaches the agent at its next step, and one it blocks is sent on its own when the turn ends, which shows the block. Matchers have no effect on this event, as in Claude Code.SessionStartruns when a chat starts, withsourcesaying how, as in Claude Code:"startup"for a new chat's first message;"resume"for the first message in a chat after Foxl or the computer restarted, and after the version arrows put another version of the chat on screen;"clear"for a chat started with/newor/reset, or one whose messages were cleared;"fork"after you edit a message into a new version of the chat; and"compact"right after Foxl compacted a long conversation. The matcher picks the sources, so"matcher": "compact"runs only after compaction. What it prints is added in front of that message, or after a compaction to the summary that replaced the older messages, so the model reads it on its next step. It never blocks.PreCompactruns right before Foxl compacts a long conversation, withtriggerset to"auto"andcustom_instructionsset tonull. Foxl compacts on its own and has no/compact, so a group whose matcher names only"manual"never runs. What the hook prints (or a JSONhookSpecificOutput.additionalContext) is added to the instructions for the summary, so you can say what it must keep. Exit code 2 or a JSON"decision": "block"skips that compaction and the conversation goes on uncompacted, as in Claude Code. When the model has already refused the conversation as too long, the block is not honoured: Foxl compacts anyway, so the turn is not lost, where Claude Code would fail the request. For other people's messages in a connected channel the hook still runs, but what it prints is not used.PostCompactruns right after a compaction, withtriggerset to"auto"andcompact_summaryset to the summary that replaced the older messages, for logging or follow-up work. It cannot block and never reaches the model. As in Claude Code, what it prints (or its stderr, when it exits with another code) is shown to you, under the compaction's divider in the chat. A group whose matcher names only"manual"never runs.- What these add is limited to ten thousand characters per hook; anything longer is cut, and the model is told it was cut. JSON longer than 64 KB is not read, and adds nothing. When one of them takes more than a second, the chat says it is running a hook, and Stop ends it.
- Matchers work as in Claude Code:
*, empty or omitted matches every tool, a plain name or a|list matches exactly, anything else is a regular expression. They are tested against the Foxl tool name (exec,file_read, ...) and, where there is one, the Claude Code name (Bash,Read,Grep,WebFetch,Write,Edit), so"matcher": "Bash"matchesexec.tool_nameon stdin is the Foxl name, andtool_inputcarries the Claude Code spelling of a field beside the Foxl one (file_pathbesidepath), so a hook that readstool_input.file_pathworks. - Other events and handler types are not run; Settings > Safety > Hooks lists what Foxl loaded and what it does not run. A file that is not valid JSON runs no hooks at all, and says so there. Foxl looks at the file at most once a second, so a change applies within a second, with no restart. With no hooks file, a tool call costs nothing extra.
"async": trueruns a hook in the background, as in Claude Code: Foxl does not wait for it and its output has no effect. A PreToolUseupdatedInputis not applied, so a hook that returns one blocks the call instead of letting the original input run.- A handler's
"if"filter (such as"if": "Bash(rm *)"or"if": "Edit(*.ts)|Write(*.ts)") decides when it runs, as in Claude Code, and where Foxl cannot tell it runs the handler, as Claude Code does: a Bash pattern is matched against every command on the line (looking throughVAR=prefixes, quoting and program paths), a file pattern against the file's path, and a command that builds its own text ($(...), a backtick,$VAR) always runs it. A filter Foxl cannot read runs the handler on every call its matcher names, and Settings > Safety > Hooks says so; on Stop, UserPromptSubmit, SessionStart, PreCompact and PostCompact a handler with"if"never runs, as in Claude Code.{"continue": false}on stdout blocks a PreToolUse call, with itsstopReasonas the reason the agent reads; after a PostToolUse call it is added to the result. - A hook runs as you, with your environment minus Foxl's own session tokens, so treat the file like any script you would run yourself. It runs in the chat's folder when the chat has one (the folder chip in the chat header), and
cwdon stdin and theCLAUDE_PROJECT_DIRenvironment variable name it. The default timeout is 600 seconds, and 30 seconds for UserPromptSubmit, as in Claude Code. - How a hook starts follows Claude Code. With
"args"it is exec form:commandis started directly withargsas its arguments, no shell, and${CLAUDE_PROJECT_DIR}in them is replaced; on Windowscommandmust be a real program such asnode(a.cmdor.batshim cannot start that way). Without"args"the command runs insh -con macOS and Linux, and in the Windows command prompt on Windows, as before."shell": "bash"runs it in Git Bash on Windows, which is where Claude Code runs a hook there by default, so add it to a hook copied from Claude Code on Windows;"shell": "powershell"runs it in PowerShell."onFailure": "block"blocks the call when the hook cannot start, times out, exits with a code other than 0 or 2, or prints JSON that does not parse. A handler that cannot start as written (an unknown shell,argsthat are not a list, a program not on PATH) does not run, and Settings > Safety > Hooks lists it; one whose program or shell this computer lacks and that says"onFailure": "block"blocks every call it matches instead, and is listed too.
Skills
Beyond built-in tools, Foxl supports skills - installable capability packages that extend what the agent can do. See Skills for the full catalog.