diff --git a/docs/about/changelog.mdx b/docs/about/changelog.mdx index a6694073208c..4a9a753b6e7e 100755 --- a/docs/about/changelog.mdx +++ b/docs/about/changelog.mdx @@ -4,6 +4,39 @@ description: "A log of all notable changes to Activepieces" icon: "code-commit" --- + + +### AI-ready pieces: tool search, AI metadata, and audience + +The piece catalog is now built for AI agents as much as for humans. An agent connected +through the [MCP server](/mcp/overview) can find the right action across hundreds of +pieces by describing the task — then inspect its schema and run it. + +**Tool search** + +- **Search by task, not by name**: the new `ap_search_actions` and `ap_search_triggers` + MCP tools take a plain-language description (*"send a message to a Slack channel"*) + and return the most relevant actions or triggers, ranked by semantic similarity. + See [Tool Search](/mcp/tool-search). +- **Honest no-match**: results below a relevance threshold are dropped rather than + padded, so agents don't grab a wrong tool when nothing fits. +- **Self-host ready**: turn it on with `AP_TOOL_SEARCH_ENABLED`; semantic ranking uses + an OpenAI key, with a keyword fallback when none is configured. + +**AI metadata & audience** + +- **Agent-oriented descriptions**: actions and triggers across the catalog carry + `aiMetadata` — a description written for agents plus an idempotency declaration for + safe retries. See [AI Metadata](/build-pieces/piece-reference/ai-metadata). +- **Audience targeting**: actions can target humans, agents, or both — the visual + builder shows the human view while agents discover the AI view, so agent-only + atomic actions don't clutter the piece selector. +- **Output schemas**: pieces can declare a labelled, formatted presentation of their + step output, driving the builder's data selector and output viewer. + See [Output Schema](/build-pieces/piece-reference/output-schema). + + + ### Production setup: lightweight workers, bundled pieces, and predictable scaling diff --git a/docs/build-pieces/piece-reference/ai-metadata.mdx b/docs/build-pieces/piece-reference/ai-metadata.mdx new file mode 100644 index 000000000000..6029473d6418 --- /dev/null +++ b/docs/build-pieces/piece-reference/ai-metadata.mdx @@ -0,0 +1,74 @@ +--- +title: "AI Metadata" +icon: "robot" +description: "Make your piece's actions and triggers discoverable and safe for AI agents" +--- + +Every action and trigger you build is also an AI tool: agents connected through the [MCP server](/mcp/overview) discover piece actions and execute them directly. Two optional fields control how your piece appears to agents — `aiMetadata` describes the operation in agent terms, and `audience` controls which surfaces an action shows up in. Both are additive: omitting them leaves the piece behaving exactly as before. + +## aiMetadata + +Available on both actions and triggers: + +```typescript +aiMetadata: { + description: string, // optional — agent-oriented description + idempotent: boolean, // optional — is repeating the call with the same input safe? +} +``` + +**`description`** is written for an agent, not for the UI. The regular `description` stays a short label under the action name in the builder; `aiMetadata.description` can be a full paragraph that states what the operation does, its notable options and constraints, and how it differs from sibling actions ("Use *Send Message To A User* for a private DM"). This text feeds the [tool search](/mcp/tool-search) index, so a precise description directly improves whether agents find your action. + +**`idempotent`** declares whether calling the operation twice with the same input is safe. Reads, upserts, and set-value operations are idempotent; anything that creates, sends, or appends on every call is not. The value is exposed to agents and MCP clients as metadata that informs whether a retry is safe — it does not by itself prevent or trigger retries. + +```typescript +import { createAction } from '@activepieces/pieces-framework'; + +export const createTask = createAction({ + name: 'create_task', + displayName: 'Create Task', + description: 'Create a task in a project', + aiMetadata: { + description: + 'Create a new task in a given project, with optional assignee, due date, and labels. ' + + 'Each call creates a new task, so it is not idempotent. ' + + 'Use Update Task to modify an existing task instead.', + idempotent: false, + }, + props: { + /* ... */ + }, + run: async (context) => { + /* ... */ + }, +}); +``` + +## audience + +Available on actions only (triggers have no audience — they always start flows, which both humans and agents build): + +```typescript +audience: 'human' | 'ai' | 'both' +``` + +| Value | Visual builder | AI agents | +|---|---|---| +| `both` (default when omitted) | Shown | Shown | +| `human` | Shown | Hidden from agent discovery | +| `ai` | Hidden from the piece selector | Shown | + +Mark an action `human` when it only makes sense with the builder around it — for example the generic custom API call, or composite actions whose inputs assume a person picking from dropdowns. Mark an action `ai` for atomic operations added specifically for agents that would clutter the human piece selector. + + +`audience` is a discovery filter, not a permission. It controls which catalogs an action appears in — it does not prevent execution, so don't rely on it to keep a dangerous action away from agents. + + +## Writing actions agents can use well + +Agents work best with actions that behave like clean API calls: + +- **Atomic over composite.** One action should map to one capability with explicit inputs. Agents compose multi-step work themselves, so a focused *Create Task* beats a *Create Task and Notify Channel*. +- **Explicit inputs.** Every behavior should be reachable through a documented prop — agents fill inputs from the [property schema](/build-pieces/piece-reference/properties), not from a UI. +- **Describe the output.** Pair the action with an [output schema](/build-pieces/piece-reference/output-schema) so both the data selector and agents know the shape of what comes back. +- **Disambiguate in `aiMetadata.description`.** When a piece has several similar actions, say which one to use when — that sentence is often what decides which action the search returns. diff --git a/docs/docs.json b/docs/docs.json index bd21f5cb3a66..decf220d5bdd 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -121,7 +121,8 @@ "group": "MCP Server", "pages": [ "mcp/overview", - "mcp/tools" + "mcp/tools", + "mcp/tool-search" ] }, { @@ -376,6 +377,7 @@ "build-pieces/piece-reference/examples", "build-pieces/piece-reference/custom-api-calls", "build-pieces/piece-reference/output-schema", + "build-pieces/piece-reference/ai-metadata", "build-pieces/piece-reference/i18n" ] }, diff --git a/docs/install/reference/environment-variables.mdx b/docs/install/reference/environment-variables.mdx index cee703b8961f..a7bad7f6c89e 100644 --- a/docs/install/reference/environment-variables.mdx +++ b/docs/install/reference/environment-variables.mdx @@ -46,6 +46,9 @@ Variables marked ❗ are required for a self-hosted production deployment. Piece syncing, polling, publish behavior. + + Semantic action discovery for AI agents. + --- @@ -256,3 +259,15 @@ flow-authoring defaults. | `AP_ENABLE_FLOW_ON_PUBLISH` | Automatically enable a flow when a new version is published. | `true` | | `AP_ENFORCE_CONNECTION_PIECE_BINDING` | Reject a step's connection at runtime unless the connection was created for the same piece as the step. | `false` | | `AP_LOAD_TRANSLATIONS_FOR_DEV_PIECES` | Load translations for dev pieces (configured via `AP_DEV_PIECES`). Affects development mode only. | `false` | + +--- + +### Tool search + +Semantic discovery of piece actions and triggers for AI agents connected +through the MCP server. See [Tool Search](/mcp/tool-search) for how it works. + +| Variable | Description | Default | +|---|---|---| +| `AP_TOOL_SEARCH_ENABLED` | Registers the `ap_search_actions` and `ap_search_triggers` MCP tools. Read live, so flipping it takes effect without a restart. | `false` | +| `AP_OPENAI_API_KEY` | OpenAI API key that funds tool-search embeddings (indexing and queries). When unset, the platform's OpenAI [AI provider](/admin-guide/guides/setup-ai-providers) is used instead; with neither, tool search serves keyword matches rather than semantic ones. | `None` | diff --git a/docs/mcp/overview.mdx b/docs/mcp/overview.mdx index 49f9a9e8f594..10c5a3a7dbd2 100644 --- a/docs/mcp/overview.mdx +++ b/docs/mcp/overview.mdx @@ -51,7 +51,7 @@ Tools are organized into categories. **Discovery tools** are always available. O | Category | Description | |----------|-------------| -| Discovery | Read-only tools for exploring flows, pieces, connections, tables, runs, and validation | +| Discovery | Read-only tools for exploring flows, pieces, connections, tables, runs, and validation — including semantic search over the action and trigger catalog | | Flow Management | Create, duplicate, rename, publish, and enable/disable flows | | Flow Building | Add, update, and delete steps and triggers | | Router & Branching | Add, update, and delete conditional branches | @@ -59,7 +59,7 @@ Tools are organized into categories. **Discovery tools** are always available. O | Tables | Full CRUD for tables, fields, and records | | Testing & Runs | Test flows, inspect results, retry failures | -See the [Tools Reference](/mcp/tools) for the complete catalog with input schemas. +See the [Tools Reference](/mcp/tools) for the complete catalog with input schemas, and [Tool Search](/mcp/tool-search) for how agents find the right action or trigger by describing a task in plain language. ## Self-Hosting Behind a Reverse Proxy diff --git a/docs/mcp/tool-search.mdx b/docs/mcp/tool-search.mdx new file mode 100644 index 000000000000..227049003ef5 --- /dev/null +++ b/docs/mcp/tool-search.mdx @@ -0,0 +1,49 @@ +--- +title: "Tool Search" +icon: "magnifying-glass" +description: "Semantic discovery of piece actions and triggers for AI agents" +--- + +Tool search lets an AI agent find the right piece action or trigger by describing the task in plain language. Instead of paging through a catalog of hundreds of pieces, the agent calls [`ap_search_actions`](/mcp/tools#ap_search_actions) or [`ap_search_triggers`](/mcp/tools#ap_search_triggers) with a query like *"send a message to a Slack channel"* and gets back the few most relevant matches, ranked by semantic similarity. + +## The discovery workflow + +Search is the first step of the three-step workflow the MCP server is built around: + +1. **Discover** — `ap_search_actions` returns candidate actions: piece name, action name, a one-line description, whether the action needs a connection, and whether the project already has one for that piece. +2. **Inspect** — `ap_get_piece_props` returns the full input schema for the chosen action. +3. **Execute** — `ap_run_action` runs it once, or `ap_build_flow` wires it into a persistent automation. + +`ap_search_triggers` plays the same discovery role when the agent is building a flow and needs the event that should start it. + +## How results are ranked + +Every action and trigger in the piece catalog is indexed from its metadata, including [AI metadata](/build-pieces/piece-reference/ai-metadata) descriptions written specifically for agents. At query time the task description is embedded and compared against that index, and matches below a relevance threshold are dropped rather than padded — an empty result genuinely means nothing in the catalog fits, so the agent can say so instead of running a wrong tool. Actions marked human-only (`audience: 'human'`) are excluded from agent discovery. + +## Search modes + +| Mode | When | Behavior | +|---|---|---| +| `semantic` | An embedding model is configured | Meaning-based ranking with a relevance threshold | +| `keyword` | No embedding model, or the embedding call failed | Lexical catalog search — the tools stay available, but matches are keyword-based | + +Every response includes the active `mode`, so a degraded instance is always detectable from the client side. + +## Availability + +Tool search is enabled on Activepieces Cloud. Self-hosted instances turn it on with an environment variable: + +```bash +AP_TOOL_SEARCH_ENABLED=true +``` + +When the flag is off, `ap_search_actions` and `ap_search_triggers` are not registered on the MCP server. The flag is read live, so flipping it does not require a restart. + +Semantic mode needs two more things: + +- **An OpenAI API key** to fund the embeddings — either set `AP_OPENAI_API_KEY`, or configure OpenAI as an [AI provider](/admin-guide/guides/setup-ai-providers) in the platform admin. The environment variable takes precedence and is the simplest path for single-tenant deployments. +- **The pgvector extension** available in your Postgres server. Activepieces creates the extension automatically at startup when the server supports it (for example the official `pgvector/pgvector` images and most managed Postgres offerings). + +If either is missing, tool search serves keyword mode instead of failing. The search index is built automatically on startup and kept in sync with the piece catalog — there is nothing to maintain by hand. + +See [Environment Variables](/install/reference/environment-variables#tool-search) for the full variable reference. diff --git a/docs/mcp/tools.mdx b/docs/mcp/tools.mdx index e553665e69e2..ede274fafa16 100644 --- a/docs/mcp/tools.mdx +++ b/docs/mcp/tools.mdx @@ -39,6 +39,15 @@ Read the full source code, package.json, and input mappings of a CODE step. Retu Use this tool when you need to read or edit a CODE step's full source. `ap_flow_structure` truncates code for overview purposes, so always call `ap_read_step_code` before modifying a code step. +### ap_read_step_settings + +Read the full untruncated settings of any step, including the trigger: piece input, action/trigger name, loop items, router branches, and error handling options. Use this to see a step's current configuration before updating it — `ap_flow_structure` truncates piece input. + +| Input | Type | Required | Description | +|-------|------|----------|-------------| +| `flowId` | string | Yes | The flow ID | +| `stepName` | string | Yes | The step name (e.g., `trigger`, `step_1`). Use `ap_flow_structure` to get valid values. | + ### ap_validate_flow Validate a flow for structural issues without publishing. Checks step validity, template references, and empty branches. @@ -58,6 +67,34 @@ Research available pieces. Use `pieceNames` for bulk exact lookup (always return | `includeActions` | boolean | No | Include action details (only applies to searchQuery mode) | | `includeTriggers` | boolean | No | Include trigger details (only applies to searchQuery mode) | +### ap_search_actions + +Find piece actions by describing the task in natural language (e.g. *"send a message to a Slack channel"*). Returns the most relevant actions ranked by semantic similarity, or an empty list when nothing in the catalog matches — the tool does not force a match. Each result includes the piece name, action name, a one-line description, whether the action needs a connection, and a `connected` flag indicating whether the project already has a connection for that piece. + +Available when [tool search](/mcp/tool-search) is enabled on the instance. + +| Input | Type | Required | Description | +|-------|------|----------|-------------| +| `query` | string | Yes | Natural-language description of the task to accomplish | +| `limit` | number | No | Max matches to return (default 5, max 20) | +| `pieceName` | string | No | Restrict results to a single piece (e.g. `slack`). Omit to search the whole catalog. | + + +This is the discovery step of the run-action workflow: take a result's `pieceName` + `actionName` to `ap_get_piece_props` for the input schema, then execute with `ap_run_action`. + + +### ap_search_triggers + +Find piece triggers (the event that starts a flow) by describing when the flow should run (e.g. *"when a new row is added to a Google Sheet"*). Same behavior and result shape as `ap_search_actions`, returning trigger names instead of action names. + +Available when [tool search](/mcp/tool-search) is enabled on the instance. + +| Input | Type | Required | Description | +|-------|------|----------|-------------| +| `query` | string | Yes | Natural-language description of the event that should start the flow | +| `limit` | number | No | Max matches to return (default 5, max 20) | +| `pieceName` | string | No | Restrict results to a single piece. Omit to search the whole catalog. | + ### ap_get_piece_props Get the detailed input property schema for a specific piece action or trigger. Returns field names, types, required/optional, descriptions, default values, and dropdown options. When auth is required but not provided, automatically lists available connections. Use this before `ap_update_step` or `ap_update_trigger` to know exactly which fields to set. diff --git a/packages/pieces/community/serp-api/package.json b/packages/pieces/community/serp-api/package.json index a3b527fe6aea..e035943df718 100644 --- a/packages/pieces/community/serp-api/package.json +++ b/packages/pieces/community/serp-api/package.json @@ -1,6 +1,6 @@ { "name": "@activepieces/piece-serp-api", - "version": "0.1.7", + "version": "0.1.8", "main": "./dist/src/index.js", "types": "./dist/src/index.d.ts", "scripts": { diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-apple-app-store.ts b/packages/pieces/community/serp-api/src/lib/actions/search-apple-app-store.ts index 657dc00ac846..bb1d106ac9bc 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-apple-app-store.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-apple-app-store.ts @@ -12,7 +12,7 @@ export const searchAppleAppStore = createAction({ audience: 'ai', aiMetadata: { description: - 'Searches the Apple App Store via SerpApi for iOS apps matching a term, returning results in `organic_results` (app name, developer, rating, price, link). Use to discover iOS apps or look up an app by name. For Android apps use Search Google Play instead. Read-only and idempotent; requires the search term and a SerpApi API key.', + 'Search the Apple App Store for iOS apps matching a name or keyword. Use to discover iOS apps, look up a specific app, or check an app\'s developer, rating, and price. For Android apps use Search Google Play instead.', idempotent: true, }, outputSchema: searchAppleAppStoreOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-bing.ts b/packages/pieces/community/serp-api/src/lib/actions/search-bing.ts index 6f3f3b213545..9b3631412407 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-bing.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-bing.ts @@ -12,7 +12,7 @@ export const searchBing = createAction({ audience: 'ai', aiMetadata: { description: - 'Runs a Bing web search via SerpApi and returns organic web results (in `organic_results`) for a query. Use as an alternative web engine to cross-check or supplement Google web results. Paginate with Count (results per page) and First (offset of the first result). Read-only and idempotent; requires the query and a SerpApi API key.', + 'Search the web with Bing and get ranked organic results for a query. Use as an alternative web search engine to cross-check or supplement Google web results. Paginate with Count (results per page) and First (offset of the first result).', idempotent: true, }, outputSchema: searchBingOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-duckduckgo.ts b/packages/pieces/community/serp-api/src/lib/actions/search-duckduckgo.ts index 7d272c47a3cd..d73f60909908 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-duckduckgo.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-duckduckgo.ts @@ -12,7 +12,7 @@ export const searchDuckduckgo = createAction({ audience: 'ai', aiMetadata: { description: - 'Runs a DuckDuckGo web search via SerpApi and returns organic web results (in `organic_results`) for a query. Use as a privacy-oriented alternative web engine to cross-check or supplement Google and Bing web results. Scope results to a region with the Region code. Read-only and idempotent; requires the query and a SerpApi API key.', + 'Search the web with DuckDuckGo and get ranked organic results for a query. Use as a privacy-oriented alternative web search engine to cross-check or supplement Google and Bing web results. Scope results to a region with the Region code.', idempotent: true, }, outputSchema: searchDuckduckgoOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-google-images.ts b/packages/pieces/community/serp-api/src/lib/actions/search-google-images.ts index c7eb61d13e07..8e1d94c34382 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-google-images.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-google-images.ts @@ -12,7 +12,7 @@ export const searchGoogleImages = createAction({ audience: 'ai', aiMetadata: { description: - 'Searches Google Images via SerpApi for a query, returning results in `images_results` (thumbnail URL, full-resolution image URL, title, and source page). Use to discover images, find a full-resolution URL, or locate the page an image came from. Page through results with the page index. Read-only and idempotent; requires the query and a SerpApi API key.', + 'Search Google Images for pictures and photos matching a query. Use to discover images on a topic, find a full-resolution image URL, or locate the page an image came from. Page through results with the page index.', idempotent: true, }, outputSchema: searchGoogleImagesOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-google-jobs.ts b/packages/pieces/community/serp-api/src/lib/actions/search-google-jobs.ts index 74c233ec34d0..781c8670d44f 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-google-jobs.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-google-jobs.ts @@ -12,7 +12,7 @@ export const searchGoogleJobs = createAction({ audience: 'ai', aiMetadata: { description: - 'Searches Google Jobs via SerpApi for job listings matching a query, returning results in `jobs_results` (title, company, location, posted date, schedule, apply links). Use to find open positions for a role. Narrow with a free-text Location, and set Listing Type to "1" for work-from-home roles. Paginate with the next page token from a prior response. Read-only and idempotent; requires the query and a SerpApi API key.', + 'Search Google Jobs for job listings, job openings, and vacancies matching a role, title, or keyword. Use to find open positions or job postings, optionally narrowed with a free-text Location; set Listing Type to "1" for remote / work-from-home roles. Paginate with the next page token from a prior response.', idempotent: true, }, outputSchema: searchGoogleJobsOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-google-lens.ts b/packages/pieces/community/serp-api/src/lib/actions/search-google-lens.ts index 81eb8a53290d..ba612697da67 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-google-lens.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-google-lens.ts @@ -12,7 +12,7 @@ export const searchGoogleLens = createAction({ audience: 'ai', aiMetadata: { description: - 'Runs a Google Lens reverse-image search via SerpApi for a publicly accessible image URL, returning visual matches and related content (in `visual_matches` and related keys). Use to identify what is in an image, find where an image appears online, or find visually similar items. Optionally refine with a text query. Read-only and idempotent; requires the image URL and a SerpApi API key.', + 'Run a Google Lens reverse image search on a publicly accessible image URL to find visual matches for a picture. Use to identify what is in an image, find where an image appears online, or find visually similar items. Optionally refine with a text query.', idempotent: true, }, outputSchema: searchGoogleLensOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-google-local-services.ts b/packages/pieces/community/serp-api/src/lib/actions/search-google-local-services.ts index c504b930f6b9..2a293e0c84eb 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-google-local-services.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-google-local-services.ts @@ -12,7 +12,7 @@ export const searchGoogleLocalServices = createAction({ audience: 'ai', aiMetadata: { description: - 'Searches Google Local Services Ads via SerpApi for vetted service providers (e.g. plumbers, electricians) matching a query, returning results in `local_ads`. Requires a Data CID identifying the geographic region; this is an opaque id with no resolver in this piece, so it must be supplied by the caller. Read-only and idempotent; requires the query, the Data CID, and a SerpApi API key.', + 'Search Google Local Services Ads for vetted local service providers (plumbers, electricians, cleaners, and similar trades) matching a query. Requires a Data CID identifying the geographic region; this is an opaque id with no resolver in this piece, so it must be supplied by the caller.', idempotent: true, }, outputSchema: searchGoogleLocalServicesOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-google-maps.ts b/packages/pieces/community/serp-api/src/lib/actions/search-google-maps.ts index dd83c438bfdd..0833f7f70506 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-google-maps.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-google-maps.ts @@ -12,7 +12,7 @@ export const searchGoogleMaps = createAction({ audience: 'ai', aiMetadata: { description: - 'Searches Google Maps via SerpApi for local businesses and places matching a query, returning results in `local_results` (name, rating, reviews, address, phone, type, GPS coordinates, hours). Use to find businesses, restaurants, or services in an area. Pass `ll` to anchor the search to a map center; `ll` is effectively required once you paginate with `start`. Read-only and idempotent; requires the query and a SerpApi API key.', + 'Search Google Maps for local businesses and places matching a query: names, ratings, reviews, addresses, phone numbers, opening hours, and GPS coordinates. Use to find businesses, restaurants, or services in an area. Pass `ll` to anchor the search to a map center; `ll` is effectively required once you paginate with `start`.', idempotent: true, }, outputSchema: searchGoogleMapsOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-google-news-ai.ts b/packages/pieces/community/serp-api/src/lib/actions/search-google-news-ai.ts index ac1a85b00d0c..881b4f69f112 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-google-news-ai.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-google-news-ai.ts @@ -12,7 +12,7 @@ export const searchGoogleNewsAi = createAction({ audience: 'ai', aiMetadata: { description: - 'Searches Google News via SerpApi for recent articles matching a query and returns them in `news_results`. Use to monitor brand or topic mentions in the press, surface breaking coverage, or gather current headlines. For general web results pick Search Google instead. Read-only and idempotent; requires the query and a SerpApi API key.', + 'Search Google News for recent news articles matching a query. Use to get the latest news headlines about a company, brand, person, or topic, monitor press and media mentions, or track breaking news coverage. For general web results pick Search Google instead.', idempotent: true, }, outputSchema: searchGoogleNewsAiOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-google-play.ts b/packages/pieces/community/serp-api/src/lib/actions/search-google-play.ts index 874181460229..e6b6322aca21 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-google-play.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-google-play.ts @@ -12,7 +12,7 @@ export const searchGooglePlay = createAction({ audience: 'ai', aiMetadata: { description: - 'Searches the Google Play store via SerpApi for a query within a chosen store section, returning results in `organic_results` (title, developer, rating, link). Use to discover Android apps, games, movies, or books. Set Store to "apps" (default), "games", "movies", or "books". For iOS apps use Search Apple App Store instead. Read-only and idempotent; requires the query and a SerpApi API key.', + 'Search the Google Play store for Android apps, games, movies, or books matching a query. Use to discover Android apps or other Play content; set Store to "apps" (default), "games", "movies", or "books". For iOS apps use Search Apple App Store instead.', idempotent: true, }, outputSchema: searchGooglePlayOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-google-scholar.ts b/packages/pieces/community/serp-api/src/lib/actions/search-google-scholar.ts index 6856029d2a2f..dc679147aa32 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-google-scholar.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-google-scholar.ts @@ -12,7 +12,7 @@ export const searchGoogleScholar = createAction({ audience: 'ai', aiMetadata: { description: - 'Searches Google Scholar via SerpApi for academic papers and citations matching a query, returning results in `organic_results` (title, authors, publication, year, cited-by count, PDF/resource links). Use to research literature, find citations, or gather academic sources. Use `as_ylo`/`as_yhi` to restrict by publication year. Read-only and idempotent; requires the query and a SerpApi API key.', + 'Search Google Scholar for academic papers, scholarly articles, and citations matching a query. Use to research literature, find citations, or gather academic sources. Restrict by publication year with `as_ylo`/`as_yhi`.', idempotent: true, }, outputSchema: searchGoogleScholarOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-google-shopping.ts b/packages/pieces/community/serp-api/src/lib/actions/search-google-shopping.ts index a959a381c12d..0ce7ac97ca81 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-google-shopping.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-google-shopping.ts @@ -12,7 +12,7 @@ export const searchGoogleShopping = createAction({ audience: 'ai', aiMetadata: { description: - 'Searches Google Shopping via SerpApi for products matching a query, returning results in `shopping_results` (title, price, merchant/source, rating, product_id, link). Use to compare product prices across merchants or research products. Paginate with Start (preferred on this engine). Read-only and idempotent; requires the query and a SerpApi API key.', + 'Search Google Shopping for products matching a query, to compare prices, merchants, and ratings across stores. Use for product research or price comparison before buying. Paginate with Start (preferred on this engine).', idempotent: true, }, outputSchema: searchGoogleShoppingOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-google-trends-ai.ts b/packages/pieces/community/serp-api/src/lib/actions/search-google-trends-ai.ts index 4d962b0bff23..40c1499946c4 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-google-trends-ai.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-google-trends-ai.ts @@ -12,7 +12,7 @@ export const searchGoogleTrendsAi = createAction({ audience: 'ai', aiMetadata: { description: - 'Queries Google Trends via SerpApi for search interest in a keyword. Use to gauge a topic\'s popularity trajectory, compare geographic interest, or find related/rising queries. Choose the data type: "TIMESERIES" (interest over time), "GEO_MAP" (interest by region), "RELATED_TOPICS", or "RELATED_QUERIES" — the response key matches the chosen data type (e.g. `interest_over_time`). Read-only and idempotent; requires the query and a SerpApi API key.', + 'Look up Google Trends data for how search interest in a keyword or topic changes over time and by region. Use to gauge a topic\'s popularity trajectory, compare geographic interest, or find related and rising queries. Choose the data type: "TIMESERIES" (interest over time), "GEO_MAP" (interest by region), "RELATED_TOPICS", or "RELATED_QUERIES".', idempotent: true, }, outputSchema: searchGoogleTrendsAiOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-google-web-ai.ts b/packages/pieces/community/serp-api/src/lib/actions/search-google-web-ai.ts index 3c130b0682d1..1fd5eaf08256 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-google-web-ai.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-google-web-ai.ts @@ -12,7 +12,7 @@ export const searchGoogleWebAi = createAction({ audience: 'ai', aiMetadata: { description: - 'Runs a Google web search via SerpApi and returns organic web results (in `organic_results`) for a query. Use this for general web lookups, current information, rankings, or topic research. For news pick Search Google News, for videos Search YouTube, for products Search Google Shopping. Read-only and idempotent; requires the query and a SerpApi API key.', + 'Search the web with Google and get ranked organic results for a query. Use for general web lookups, current information, rankings, or topic research. For news pick Search Google News, for videos Search YouTube, for products Search Google Shopping.', idempotent: true, }, outputSchema: searchGoogleWebAiOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-walmart.ts b/packages/pieces/community/serp-api/src/lib/actions/search-walmart.ts index 53927467fef8..a46cd18cd259 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-walmart.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-walmart.ts @@ -12,7 +12,7 @@ export const searchWalmart = createAction({ audience: 'ai', aiMetadata: { description: - 'Searches Walmart via SerpApi for products matching a query, returning results in `organic_results` (title, price, rating, seller, product link). Use to look up Walmart product pricing and availability. Filter by price range and minimum rating, sort, and paginate by page. Read-only and idempotent; requires the query and a SerpApi API key.', + 'Search Walmart for products matching a query, to check prices, ratings, and availability. Use to look up Walmart product listings for price checks or stock research. Filter by price range and minimum rating, sort, and paginate by page.', idempotent: true, }, outputSchema: searchWalmartOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-yelp.ts b/packages/pieces/community/serp-api/src/lib/actions/search-yelp.ts index bafda1f5d134..4d67705218c1 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-yelp.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-yelp.ts @@ -12,7 +12,7 @@ export const searchYelp = createAction({ audience: 'ai', aiMetadata: { description: - 'Searches Yelp via SerpApi for businesses matching a description in a location, returning results in `organic_results` (name, rating, review count, category, price level, link). Use to find rated local businesses or read review summaries. Both what to search (Find Description) and where (Find Location) are required. Read-only and idempotent; requires those two fields and a SerpApi API key.', + 'Search Yelp for local businesses matching a description in a location: restaurants, shops, and services with ratings, review counts, categories, and price levels. Use to find well-rated businesses nearby or in a named city. Both what to search (Find Description) and where (Find Location) are required.', idempotent: true, }, outputSchema: searchYelpOutputSchema, diff --git a/packages/pieces/community/serp-api/src/lib/actions/search-youtube-ai.ts b/packages/pieces/community/serp-api/src/lib/actions/search-youtube-ai.ts index fa3b52cb31aa..0cd3e58acce7 100644 --- a/packages/pieces/community/serp-api/src/lib/actions/search-youtube-ai.ts +++ b/packages/pieces/community/serp-api/src/lib/actions/search-youtube-ai.ts @@ -12,7 +12,7 @@ export const searchYoutubeAi = createAction({ audience: 'ai', aiMetadata: { description: - 'Searches YouTube via SerpApi for videos matching a query and returns them in `video_results`. Use to find video content on a topic, discover channels, or research what is being published. For news pick Search Google News, for general web Search Google. Read-only and idempotent; requires the search query and a SerpApi API key.', + 'Search YouTube for videos about a topic or query. Use to find videos, discover channels, look up video content, or research what is being published about a subject. For news articles pick Search Google News.', idempotent: true, }, outputSchema: searchYoutubeAiOutputSchema, diff --git a/packages/web/src/features/authentication/components/auth-landing/auth-drawer-body.tsx b/packages/web/src/features/authentication/components/auth-landing/auth-drawer-body.tsx index ab51a3237832..6bba0d4a0449 100644 --- a/packages/web/src/features/authentication/components/auth-landing/auth-drawer-body.tsx +++ b/packages/web/src/features/authentication/components/auth-landing/auth-drawer-body.tsx @@ -119,7 +119,9 @@ export function AuthDrawerBody({ initialMode }: AuthDrawerBodyProps) { setCaptchaReset((count) => count + 1); }, []); const captchaRequired = !isNil(useTurnstileSiteKey()) && !captchaUnavailable; - const challengeApplies = step === 'method' || step === 'code'; + const passwordlessAvailable = usePasswordlessAvailable(); + const challengeApplies = + passwordlessAvailable && (step === 'method' || step === 'code'); return ( @@ -209,9 +211,6 @@ function AuthStep({ const { data: emailAuthEnabledFlag } = flagsHooks.useFlag( ApFlagId.EMAIL_AUTH_ENABLED, ); - const { data: smtpConfigured } = flagsHooks.useFlag( - ApFlagId.SMTP_CONFIGURED, - ); const { data: userCreated } = flagsHooks.useFlag( ApFlagId.USER_CREATED, ); @@ -221,7 +220,7 @@ function AuthStep({ const firstUser = userCreated !== true; const effectiveMode: AuthMode = firstUser ? 'signup' : mode; const emailAuthEnabled = emailAuthEnabledFlag ?? true; - const passwordlessAvailable = emailAuthEnabled && !!smtpConfigured; + const passwordlessAvailable = usePasswordlessAvailable(); const showThirdParty = useShowThirdPartyProviders(); const thirdParty = useThirdPartyAvailability(); @@ -964,6 +963,16 @@ function ModeSwitch({ ); } +function usePasswordlessAvailable(): boolean { + const { data: emailAuthEnabled } = flagsHooks.useFlag( + ApFlagId.EMAIL_AUTH_ENABLED, + ); + const { data: smtpConfigured } = flagsHooks.useFlag( + ApFlagId.SMTP_CONFIGURED, + ); + return (emailAuthEnabled ?? true) && !!smtpConfigured; +} + // Country variants are endless (yahoo.co.uk, hotmail.fr, …), so match the // provider by prefix and keep the exact list for the one-off domains. function isPersonalEmail(email: string): boolean { diff --git a/packages/web/src/features/authentication/components/auth-landing/turnstile-widget.tsx b/packages/web/src/features/authentication/components/auth-landing/turnstile-widget.tsx index 72658ebb2aaa..4a806354716e 100644 --- a/packages/web/src/features/authentication/components/auth-landing/turnstile-widget.tsx +++ b/packages/web/src/features/authentication/components/auth-landing/turnstile-widget.tsx @@ -71,9 +71,16 @@ export function TurnstileWidget({ } widgetId = window.turnstile.render(container.current, { sitekey: siteKey, - callback: (token: string) => onToken(token), + appearance: 'interaction-only', + callback: (token: string) => { + setFailed(false); + onToken(token); + }, 'expired-callback': () => onToken(undefined), - 'error-callback': () => onToken(undefined), + 'error-callback': () => { + setFailed(true); + onToken(undefined); + }, }); widget.current = widgetId; }) @@ -109,16 +116,18 @@ export function TurnstileWidget({ // Say so rather than leaving a dead submit button: the server requires a // solved challenge whenever one is configured, so a blocked script means // sign-in cannot proceed and the person needs to know why. - if (failed) { - return ( -

- {t( - 'The verification step could not load. Disable your ad blocker for this page, then reload.', - )} -

- ); - } - return
; + return ( + <> +
+ {failed && ( +

+ {t( + 'The verification step could not load. Disable your ad blocker for this page, then reload.', + )} +

+ )} + + ); } type TurnstileWidgetProps = {