diff --git a/skills/rasa-building-flows/SKILL.md b/skills/rasa-building-flows/SKILL.md index 682fd78..0d26f91 100644 --- a/skills/rasa-building-flows/SKILL.md +++ b/skills/rasa-building-flows/SKILL.md @@ -7,7 +7,8 @@ description: > license: Apache-2.0 metadata: author: rasa - version: "3.x" + version: "0.1.0" + rasa_version: ">=3.9.0" docs-url: https://rasa.com/docs/reference/primitives/flows/ --- @@ -30,7 +31,9 @@ metadata: (see "Branching and conditions"). 10. Extract reusable sub-tasks into child flows via `call` (see "Connecting flows"). 11. Connect follow-up flows via `link` if a separate process should start after. -12. Validate the project. +12. Ensure all referenced slots, responses, and actions exist in the domain + (see "Ensure domain completeness"). +13. Validate the project (see "Fixing validation errors"). ## Scoping a flow @@ -343,6 +346,27 @@ collect_patient_info: if: False ``` -## Full reference +## Ensure domain completeness - \ No newline at end of file +Every element a flow references must be defined in the domain (`domain.yml` or split +domain files). After writing or editing a flow, walk through each step and verify the +items below are present. Missing entries cause validation errors or silent runtime +failures. + +- Every slot used in the flow → `slots:` + (see `rasa-managing-slots`) +- Every `collect: slot_name` → `utter_ask_{slot_name}` in `responses:` + (see `rasa-writing-responses`) +- Every `action: utter_*` step → matching entry in `responses:` +- Every custom action / validation action → `actions:` + (see `rasa-writing-custom-actions`) +- Rejection responses → `responses:` + +## Fixing validation errors + +When validation fails, preserve the user's **intent** — what the flow is supposed to +accomplish. The flow steps themselves can change, but the intended behavior must not be +lost. + +Never strip down a flow's intended behavior to make validation pass. If needed, fix the +domain: slots, responses, and actions to support what the user asked for. diff --git a/skills/rasa-calling-mcp-tools-from-flows/SKILL.md b/skills/rasa-calling-mcp-tools-from-flows/SKILL.md new file mode 100644 index 0000000..3ece2fe --- /dev/null +++ b/skills/rasa-calling-mcp-tools-from-flows/SKILL.md @@ -0,0 +1,131 @@ +--- +name: rasa-calling-mcp-tools-from-flows +description: > + Calls MCP tools directly from Rasa flow steps. Use when invoking an MCP tool via a + call step, mapping slot values to tool parameters, or extracting tool results into + slots. This replaces custom action code for simple API integrations. +license: Apache-2.0 +metadata: + author: rasa + version: "0.1.0" + rasa_version: ">=3.14.0" + docs-url: https://rasa.com/docs/pro/build/mcp-integration +--- + +# Calling MCP Tools from Flows + +MCP tools can be invoked directly from flow steps using a `call` step with explicit +input/output mappings. This replaces the need for custom action code when you only need +to call an external API. + +MCP servers must be defined in `endpoints.yml` before calling their tools. See the +`rasa-configuring-mcp-server` skill for server setup and authentication. + +This feature is in **beta** and available starting from **Rasa 3.14.0**. + +## When to use MCP vs a custom action + +Use an MCP tool call when: + +- The tool already exists on an MCP server +- You just need to pass slot values in and store results back +- No complex logic around the call (branching, retries, multiple sequential API calls) + +Use a custom action instead when: + +- You need complex business logic (multiple API calls, conditionals, error handling) +- You need access to the full tracker (conversation history, events, sender ID) +- You need to send custom messages via the dispatcher +- The data transformation can't be expressed in a Jinja2 output mapping +- The external API isn't exposed via MCP + +See `rasa-writing-custom-actions` for custom action guidance. + +## Workflow + +1. Ensure the MCP server is defined in `endpoints.yml` + (see `rasa-configuring-mcp-server` skill). +2. Call the MCP tool from a flow using a `call` step with `mcp_server` and `mapping` + (see "Calling an MCP tool from a flow"). +3. Map tool results to slots, handling both structured and unstructured output + (see "Tool result formats"). +4. Validate the project. + +## Calling an MCP tool from a flow + +Use a `call` step with `mcp_server` and `mapping` to invoke a tool directly. + +| Key | Required | Description | +|------------------|----------|-------------| +| `call` | yes | Tool name as exposed by the MCP server | +| `mcp_server` | yes | Must exactly match a `name` in `endpoints.yml`. Fails at runtime if mismatched | +| `mapping.input` | yes | Maps slot values to tool parameters. Each entry: `param` (tool parameter name) + `slot` (Rasa slot name) | +| `mapping.output` | yes | Maps tool results back to slots. Each entry: `slot` (target slot) + `value` (Jinja2 expression to extract the result) | + +```yaml +flows: + buy_order: + description: helps users place a buy order for a particular stock + steps: + - collect: stock_name + - collect: order_quantity + - action: check_feasibility + next: + - if: slots.order_feasible is True + then: + - call: place_buy_order + mcp_server: trade_server + mapping: + input: + - param: ticker_symbol + slot: stock_name + - param: quantity + slot: order_quantity + output: + - slot: order_status + value: result.structuredContent.order_status.success + - else: + - action: utter_invalid_order + next: END +``` + +## Tool result formats + +MCP tools return results in one of two formats. Handle both in your output mapping. + +### Structured content + +When the tool defines an output schema, Rasa returns structured data. Access specific +values using dot notation in the `value` field: + +```yaml +# Tool returns: {"result": {"structuredContent": {"order_status": {"success": true, "order_id": "abc123"}}}} +output: + - slot: order_status + value: result.structuredContent.order_status.success + - slot: order_id + value: result.structuredContent.order_status.order_id +``` + +### Unstructured content + +When the tool has no output schema, the entire result is a serialized string. Capture +it in a single slot for downstream processing: + +```yaml +# Tool returns: {"result": {"content": [{"type": "text", "text": "{\"order_status\": ...}"}]}} +output: + - slot: order_data + value: result.content +``` + +## Common pitfalls + +- **MCP server name mismatch** — the `mcp_server` value in a flow `call` step must + exactly match a `name` in `endpoints.yml`. A typo means the call fails at runtime + with no compile-time warning. +- **Missing tool on server** — if the tool name in `call` doesn't exist on the + referenced MCP server, the call fails at runtime. +- **Ignoring unstructured content** — when a tool has no output schema, results come + back as a serialized string under `result.content`, not as `result.structuredContent`. + Always account for both formats. diff --git a/skills/rasa-configuring-assistant/SKILL.md b/skills/rasa-configuring-assistant/SKILL.md index 975dc0f..9d379e8 100644 --- a/skills/rasa-configuring-assistant/SKILL.md +++ b/skills/rasa-configuring-assistant/SKILL.md @@ -7,7 +7,8 @@ description: > license: Apache-2.0 metadata: author: rasa - version: "3.x" + version: "0.1.0" + rasa_version: ">=3.13.0" docs-url: https://rasa.com/docs/pro/build/configuring-assistant --- @@ -159,6 +160,11 @@ nlg: The rephraser (`nlg: type: rephrase`) is covered by the `rasa-rephrasing-responses` skill. +### MCP servers + +MCP server configuration (`mcp_servers` in `endpoints.yml`) is covered by the +`rasa-configuring-mcp-server` skill. + ### Silence handling Controls how long the assistant waits before assuming the user is silent. Only applies @@ -168,7 +174,3 @@ to voice-stream channels (Twilio, Browser Audio, Genesys, Jambonz, Audiocodes). interaction_handling: global_silence_timeout: 7 # seconds, default: 7 ``` - -## Full reference - - \ No newline at end of file diff --git a/skills/rasa-configuring-mcp-server/SKILL.md b/skills/rasa-configuring-mcp-server/SKILL.md new file mode 100644 index 0000000..d60333b --- /dev/null +++ b/skills/rasa-configuring-mcp-server/SKILL.md @@ -0,0 +1,95 @@ +--- +name: rasa-configuring-mcp-server +description: > + Configures MCP (Model Context Protocol) servers in a Rasa CALM assistant. Use when + defining MCP servers in endpoints.yml, setting up authentication (API key, OAuth 2.0, + pre-issued token), or connecting to multiple external services. +license: Apache-2.0 +metadata: + author: rasa + version: "0.1.0" + rasa_version: ">=3.14.0" + docs-url: https://rasa.com/docs/reference/integrations/mcp-servers +--- + +# Configuring MCP Servers + +MCP servers expose external tools — APIs, databases, services — to a Rasa assistant. All +MCP servers are defined in `endpoints.yml` and shared across direct flow tool calls and +ReAct sub agents. + +This feature is in **beta** and available starting from **Rasa 3.14.0**. + +## Workflow + +1. Add each MCP server to `endpoints.yml` (see "Server definition"). +2. Add authentication if the server requires it (see "Authentication"). +3. Validate the project. + +## Server definition + +Register every MCP server in `endpoints.yml`. + +| Key | Required | Description | +|--------|----------|-------------| +| `name` | yes | Unique identifier — referenced from flows and sub agent configs. Duplicates cause a validation error at startup | +| `url` | yes | URL where the MCP server is running | +| `type` | yes | `http` or `https` | + +```yaml +# endpoints.yml +mcp_servers: + - name: trade_server + url: http://localhost:8080 + type: http + - name: payment_server + url: https://api.payment-service.com + type: https +``` + +## Authentication + +Add auth fields directly to the server entry in `endpoints.yml` when the MCP server +requires credentials. Sensitive values (`api_key`, `token`, `client_secret`) **must** +use `${ENV_VAR}` syntax — plain text is rejected by validation. + +### API key + +Sent as `Authorization: Bearer ` by default. Add `header_name` and +`header_format` to override the header. + +```yaml +mcp_servers: + - name: secure_api_server + url: https://api.example.com + type: https + api_key: "${API_KEY}" + header_name: "X-API-Key" # optional, default: Authorization + header_format: "{key}" # optional, default: Bearer {key} +``` + +### OAuth 2.0 (client credentials) + +```yaml +mcp_servers: + - name: oauth_server + url: https://api.example.com + type: https + oauth: + client_id: "${CLIENT_ID}" + client_secret: "${CLIENT_SECRET}" + token_url: "https://auth.example.com/oauth/token" + scope: "read:data write:data" # optional + audience: "https://api.example.com" # optional + timeout: 10 # optional +``` + +### Pre-issued token + +```yaml +mcp_servers: + - name: token_server + url: https://api.example.com + type: https + token: "${ACCESS_TOKEN}" +``` diff --git a/skills/rasa-configuring-model-groups/SKILL.md b/skills/rasa-configuring-model-groups/SKILL.md index 82d33fb..c7f5454 100644 --- a/skills/rasa-configuring-model-groups/SKILL.md +++ b/skills/rasa-configuring-model-groups/SKILL.md @@ -7,7 +7,8 @@ description: > license: Apache-2.0 metadata: author: rasa - version: "3.x" + version: "0.1.0" + rasa_version: ">=3.11.0" docs-url: https://rasa.com/docs/pro/deploy/llm-routing --- diff --git a/skills/rasa-managing-slots/SKILL.md b/skills/rasa-managing-slots/SKILL.md index 29acd15..ea14948 100644 --- a/skills/rasa-managing-slots/SKILL.md +++ b/skills/rasa-managing-slots/SKILL.md @@ -7,7 +7,8 @@ description: > license: Apache-2.0 metadata: author: rasa - version: "3.x" + version: "0.1.0" + rasa_version: ">=3.12.0" docs-url: https://rasa.com/docs/reference/primitives/slots/ --- @@ -183,7 +184,3 @@ different conventions, match those instead. - Prefix booleans with `is_` or `has_`: `is_verified`, `has_insurance` - Avoid special characters `(`, `)`, `=`, `,` in slot names — they break response button payloads - -## Full reference - - diff --git a/skills/rasa-rephrasing-responses/SKILL.md b/skills/rasa-rephrasing-responses/SKILL.md index 918c75e..75d05ab 100644 --- a/skills/rasa-rephrasing-responses/SKILL.md +++ b/skills/rasa-rephrasing-responses/SKILL.md @@ -7,7 +7,8 @@ description: > license: Apache-2.0 metadata: author: rasa - version: "3.x" + version: "0.1.0" + rasa_version: ">=3.11.0" docs-url: https://rasa.com/docs/reference/primitives/contextual-response-rephraser/ --- @@ -136,7 +137,3 @@ responses: Suggested: {{suggested_response}} Rephrased: ``` - -## Full reference - - \ No newline at end of file diff --git a/skills/rasa-setting-up-a2a-agents/SKILL.md b/skills/rasa-setting-up-a2a-agents/SKILL.md new file mode 100644 index 0000000..b646729 --- /dev/null +++ b/skills/rasa-setting-up-a2a-agents/SKILL.md @@ -0,0 +1,244 @@ +--- +name: rasa-setting-up-a2a-agents +description: > + Connects external sub agents to a Rasa CALM assistant via the A2A (Agent-to-Agent) + protocol. Use when adding an external agent, configuring an agent card, setting up + A2A authentication, customizing input/output processing, or invoking an external + sub agent from a flow. +license: Apache-2.0 +metadata: + author: rasa + version: "0.1.0" + rasa_version: ">=3.14.0" + docs-url: https://rasa.com/docs/reference/config/agents/external-sub-agents +--- + +# Connecting A2A External Sub Agents + +External sub agents connected via the A2A protocol operate as autonomous entities that +handle complex, multi-turn conversations independently. When invoked through a `call` +step in a flow, the external agent takes control of the conversation until its task is +complete. + +This feature is in **beta** and available starting from **Rasa 3.14.0**. + +## Workflow + +1. Ensure the external agent is running and exposes an A2A-compatible endpoint with an + agent card. +2. Create the sub agent directory: `sub_agents//` with a `config.yml` + (see "Directory structure"). +3. Set `agent.protocol: A2A` and point `configuration.agent_card` to the agent card + file or URL (see "Configuration"). +4. Add authentication if the external agent requires it (see "Authentication"). +5. Invoke the sub agent from a flow using an autonomous `call` step + (see "Invoking from a flow"). +6. Optionally customize input/output processing with a Python module + (see "Customization"). +7. Validate the project. + +## Directory structure + +Each A2A sub agent lives in its own subdirectory under `sub_agents/`. Both `rasa train` +and `rasa run` scan this directory by default; pass `--sub-agents ` to either +command to use a different directory. + +The agent name must be unique across all sub agents **and** all flow IDs. + +``` +your_project/ +├── config.yml +├── domain/ +├── data/flows/ +└── sub_agents/ + └── car_shopping_agent/ + ├── config.yml # required + ├── agent_card.json # local agent card (or use a URL instead) + └── custom_agent.py # optional customization module +``` + +## Configuration + +The `config.yml` requires `agent.protocol: A2A` and a `configuration.agent_card` +pointing to either a local JSON file (relative to project root) or a remote URL. + +```yaml +# sub_agents/car_shopping_agent/config.yml + +agent: + name: car_shopping_agent + protocol: A2A + description: "Helps users shop for cars by connecting them with dealers" + +configuration: + agent_card: ./sub_agents/car_shopping_agent/agent_card.json +``` + +| Key | Required | Description | +|----------------------------|----------|-------------| +| `agent.name` | yes | Unique name — must not clash with any flow ID | +| `agent.protocol` | yes | Must be `A2A` for external sub agents | +| `agent.description` | yes | Brief description of the agent's capabilities | +| `configuration.agent_card` | yes | Path or URL to the A2A agent card | +| `configuration.module` | no | Python class path for customization | + +### Agent card + +Do **not** create the agent card yourself — it is supplied by the external agent's +provider. Your job is to obtain it (as a JSON file or URL) and reference it in +`configuration.agent_card`. Rasa reads the card at startup to resolve the agent's +endpoint, transport, and auth requirements, then health-checks the connection. If the +agent is unreachable, startup fails. + +## Authentication + +Add an `auth` section under `configuration` in the sub agent's `config.yml` +(`sub_agents//config.yml`) when the external agent requires credentials. +Sensitive values (`api_key`, `token`, `client_secret`) **must** use `${ENV_VAR}` +syntax — plain text is rejected by validation. + +### API key + +Sent as `Authorization: Bearer ` by default. Add `header_name` and +`header_format` to override the header. + +```yaml +configuration: + agent_card: ./sub_agents/shopping_agent/agent_card.json + auth: + api_key: "${API_KEY}" + header_name: "X-API-Key" # optional, default: Authorization + header_format: "{key}" # optional, default: Bearer {key} +``` + +### OAuth 2.0 (client credentials) + +```yaml + auth: + oauth: + client_id: "${CLIENT_ID}" + client_secret: "${CLIENT_SECRET}" + token_url: "https://auth.company.com/oauth/token" + scope: "read:users" +``` + +### Pre-issued token + +```yaml + auth: + token: "${ACCESS_TOKEN}" +``` + +## Invoking from a flow + +Use an autonomous `call` step to delegate part of a conversation to the external +agent. The agent name must match `agent.name` in the sub agent's `config.yml`. + +```yaml +flows: + shop_for_car: + description: Helps users browse and purchase a car through an external agent. + steps: + - collect: user_budget + - call: car_shopping_agent # runs until the external agent signals completion + - action: utter_purchase_summary +``` + +Do **not** use `exit_if` — it is only supported for ReAct sub agents. The external +agent controls its own completion via the A2A protocol. + +## Task vs Message responses + +When designing the external agent's response behavior: + +- Use **Tasks** for the main workflow — they support status tracking and allow the + agent to signal `COMPLETED`. +- Use **Messages** only for clarifications — Rasa maps every `Message` to + `INPUT_REQUIRED`, so the conversation stays open and never completes. +- Never rely on Messages alone for the main operations — use a Task-only or hybrid + approach. + +## Customization + +By default Rasa sends all conversation slots to the external agent and passes the +agent's response straight back. Customization is **optional** — only create a custom +class when you need to: + +- **Filter input** — limit which slots reach the external agent (e.g. send only + `user_budget` and `car_type`, not every slot in the conversation). +- **Map output to slots** — extract structured data from the agent's response and + store it in Rasa slots so downstream flow steps can branch on it. + +### Creating a custom A2A agent class + +1. Create a Python file in the sub agent directory (e.g. + `sub_agents/car_shopping_agent/custom_agent.py`). +2. Subclass `A2AAgent` and override `process_input` and/or `process_output`. +3. Point `configuration.module` to the class. + +```python +from rasa.agents.protocol.a2a.a2a_agent import A2AAgent +from rasa.agents.schemas import AgentInput, AgentOutput +from rasa.sdk.events import SlotSet + +class CarShoppingAgent(A2AAgent): + async def process_input(self, input: AgentInput) -> AgentInput: + input.slots = [s for s in input.slots if s.name in {"user_budget", "car_type"}] + return input + + async def process_output(self, output: AgentOutput) -> AgentOutput: + if output.structured_results: + results = output.structured_results[-1] + output.events = output.events or [] + output.events.append(SlotSet("selected_car", results)) + return output +``` + +```yaml +# sub_agents/car_shopping_agent/config.yml +agent: + name: car_shopping_agent + protocol: A2A + description: "Helps users shop for cars" + +configuration: + agent_card: ./sub_agents/car_shopping_agent/agent_card.json + module: "sub_agents.car_shopping_agent.custom_agent.CarShoppingAgent" +``` + +### What you can modify + +**`process_input(input: AgentInput) -> AgentInput`** — modify what the external agent +receives. Key fields on `AgentInput`: + +| Field | Type | What to do with it | +|------------------------|------|--------------------| +| `slots` | `List[AgentInputSlot]` | Filter to only relevant slots | +| `user_message` | `str` | Rewrite or augment the user message | +| `conversation_history` | `str` | Trim or redact sensitive history | +| `metadata` | `Dict[str, Any]` | Inject custom metadata | + +**`process_output(output: AgentOutput) -> AgentOutput`** — modify what comes back into +Rasa. Key fields on `AgentOutput`: + +| Field | Type | What to do with it | +|-------|------|--------------------| +| `events` | `Optional[List[SlotSet]]` | Add `SlotSet` events to store data in Rasa slots | +| `structured_results` | `Optional[List]` | Read raw results from agent tool calls | +| `response_message` | `Optional[str]` | Rewrite the message sent to the user | + +## Common pitfalls + +- **Missing `protocol: A2A`** — without this, Rasa defaults to the RASA protocol and + treats the agent as a ReAct sub agent, which will fail. +- **Agent name clashes with flow IDs** — the agent name must be unique across all + flows and all other sub agents. +- **Agent unreachable at startup** — Rasa health-checks the agent card endpoint on + boot. If the external agent is down, Rasa will not start. +- **Using `exit_if` with A2A agents** — `exit_if` is only supported for ReAct sub + agents. A2A agents control their own completion via the A2A protocol. +- **Message-only response pattern** — responding only with `Message` objects keeps the + conversation in `INPUT_REQUIRED` indefinitely. Use `Task` objects for the main + workflow so the agent can signal `COMPLETED`. +- **User messages during processing** — messages sent while the sub agent is still + processing are not handled. Users must wait for `INPUT_REQUIRED` or `COMPLETED`. diff --git a/skills/rasa-setting-up-enterprise-search/SKILL.md b/skills/rasa-setting-up-enterprise-search/SKILL.md index d79e9e8..70ea56c 100644 --- a/skills/rasa-setting-up-enterprise-search/SKILL.md +++ b/skills/rasa-setting-up-enterprise-search/SKILL.md @@ -8,7 +8,8 @@ description: > license: Apache-2.0 metadata: author: rasa - version: "3.x" + version: "0.1.0" + rasa_version: ">=3.13.0" docs-url: https://rasa.com/docs/pro/build/configuring-enterprise-search --- @@ -287,7 +288,3 @@ vector_store: api_key: ${SEARCH_API_KEY} collection: my_collection ``` - -## Full reference - - diff --git a/skills/rasa-setting-up-react-agents/SKILL.md b/skills/rasa-setting-up-react-agents/SKILL.md new file mode 100644 index 0000000..c69351f --- /dev/null +++ b/skills/rasa-setting-up-react-agents/SKILL.md @@ -0,0 +1,318 @@ +--- +name: rasa-setting-up-react-agents +description: > + Configures ReAct sub agents in a Rasa CALM assistant. Use when creating a sub-agent + that dynamically selects MCP tools, choosing between general-purpose and task-specific + agent types, customizing prompts, filtering tools, adding custom Python tools, or + overriding input/output processing. +license: Apache-2.0 +metadata: + author: rasa + version: "0.1.0" + rasa_version: ">=3.14.0" + docs-url: https://rasa.com/docs/reference/config/agents/react-sub-agents +--- + +# Configuring ReAct Sub Agents + +ReAct sub agents are built-in autonomous agents that dynamically choose which MCP +tools to invoke based on conversation context. They operate in a ReAct +(Reasoning + Acting) loop — the agent reasons about the user's request, picks a tool, +observes the result, and repeats until the task is done. + +MCP servers must be defined in `endpoints.yml` before configuring a ReAct sub agent. +See the `rasa-configuring-mcp-server` skill for server setup and authentication. + +This feature is in **beta** and available starting from **Rasa 3.14.0**. + +## Workflow + +1. Ensure the MCP server is defined in `endpoints.yml` (see `rasa-configuring-mcp-server` + skill). +2. Create the sub agent directory with a `config.yml` + (see "Directory structure" and "Configuration"). +3. Choose between general-purpose or task-specific agent type + (see "General-purpose vs task-specific"). +4. Optionally filter which MCP tools the agent can access + (see "Tool filtering"). +5. Invoke the sub agent from a flow using a `call` step + (see "Invoking from a flow"). +6. Optionally customize the prompt, input/output processing, or add custom tools + (see "Customization"). +7. Validate the project. + +## Directory structure + +Each ReAct sub agent lives in its own subdirectory under `sub_agents/`. Both +`rasa train` and `rasa run` scan this directory by default; pass `--sub-agents ` +to either command to use a different directory. + +The agent name must be unique across all sub agents **and** all flow IDs. + +``` +your_project/ +├── config.yml +├── endpoints.yml +├── domain/ +├── data/flows/ +└── sub_agents/ + └── stock_explorer/ + ├── config.yml # required + ├── prompt_template.jinja2 # optional + └── custom_agent.py # optional +``` + +## Configuration + +The sub agent's `config.yml` connects the agent to one or more MCP servers defined in +`endpoints.yml`. The protocol defaults to `RASA` — do **not** set it to `A2A`. + +```yaml +# sub_agents/stock_explorer/config.yml +agent: + name: stock_explorer + description: "Agent that helps users research and analyze stock options" + +configuration: + llm: # optional, defaults to GPT-4o + model-group: openai-gpt-4o + prompt_template: sub_agents/stock_explorer/prompt_template.jinja2 # optional + timeout: 30 # optional, seconds before timing out + max_retries: 3 # optional, MCP connection retries + include_date_time: true # optional, default: true + timezone: "America/New_York" # optional, default: "UTC" + +connections: + mcp_servers: + - name: trade_server + include_tools: + - find_symbol + - get_company_news + - fetch_live_price +``` + +| Key | Required | Description | +|-----|----------|-------------| +| `agent.name` | yes | Unique name — must not clash with any flow ID or other sub agent | +| `agent.description` | yes | Brief description of the agent's capabilities | +| `configuration.llm` | no | LLM to power the agent's reasoning. Defaults to GPT-4o | +| `configuration.prompt_template` | no | Path to a Jinja2 prompt template | +| `configuration.timeout` | no | Seconds before timing out. No timeout by default | +| `configuration.max_retries` | no | MCP connection retry attempts. Default: 3 | +| `configuration.include_date_time` | no | Include current date/time in prompts. Default: true | +| `configuration.timezone` | no | IANA timezone (e.g. `"UTC"`, `"Europe/London"`). Default: `"UTC"` | +| `configuration.module` | no | Python class path for customization | +| `connections.mcp_servers` | yes | List of MCP servers this agent connects to. At least one required | + +### Tool filtering + +For each MCP server entry under `connections.mcp_servers`, use `include_tools` or +`exclude_tools` to control which tools the agent can access. These are **mutually +exclusive** — use one or the other per server, never both. + +- `include_tools` — only these tools are available to the agent. +- `exclude_tools` — all tools except these are available. + +```yaml +connections: + mcp_servers: + - name: trade_server + include_tools: + - find_symbol + - get_company_news + - name: analytics_server + exclude_tools: + - admin_analytics +``` + +## General-purpose vs task-specific + +Rasa supports two types of ReAct sub agents. Choose based on how the agent signals +completion. + +| | General-purpose | Task-specific | +|---|---|---| +| **When to use** | Open-ended tasks where the agent decides when it's done | Structured data collection (form filling, booking) | +| **Completion** | Agent calls a built-in `task_completed` tool | Automatic when `exit_if` slot conditions are met | +| **Built-in tools** | `task_completed` only | `set_slot_` for each slot in `exit_if` | +| **Final response** | Sends a summary message to the user | Completes silently — flow continues to next step | +| **Base class** | `MCPOpenAgent` | `MCPTaskAgent` | + +## Invoking from a flow + +### General-purpose (no exit conditions) + +The agent runs autonomously until it calls `task_completed`: + +```yaml +flows: + stock_research: + description: helps research and analyze stock investment options + steps: + - call: stock_explorer +``` + +### Task-specific (with exit conditions) + +The agent runs until the specified slot conditions are met. Rasa automatically provides +`set_slot_` tools for each slot in `exit_if`: + +```yaml +flows: + appointment_booking: + description: helps users book appointments + steps: + - call: booking_agent + exit_if: + - slots.appointment_time is not null + - collect: final_confirmation +``` + +## Customization + +Customization is **optional**. Only create a custom class when you need to: + +- **Customize the prompt** — add specific instructions or pass slot values as context. +- **Filter input** — limit which slots reach the agent. +- **Map output to slots** — extract structured data from the agent's tool results. +- **Add custom Python tools** — tools that run alongside MCP tools. + +### Custom prompt template + +Create a Jinja2 file and reference it in `configuration.prompt_template`. Available +variables: + +- `{{ description }}` — the agent's description from `config.yml`. +- `{{ slots. }}` — any slot value. +- `{{ conversation_history }}` — full dialogue transcript. +- `{{ current_datetime }}` — datetime object (when `include_date_time` is enabled). + Use methods like `{{ current_datetime.strftime("%d %B, %Y") }}`. + +### Creating a custom ReAct agent class + +1. Create a Python file in the sub agent directory (e.g. + `sub_agents/stock_explorer/custom_agent.py`). +2. Subclass `MCPOpenAgent` (general-purpose) or `MCPTaskAgent` (task-specific). +3. Override `process_input`, `process_output`, and/or `get_custom_tool_definitions`. +4. Point `configuration.module` to the class. + +```python +from rasa.agents.protocol.mcp.mcp_open_agent import MCPOpenAgent +from rasa.agents.schemas import AgentInput, AgentOutput, AgentToolResult +from rasa.sdk.events import SlotSet + +class StockAnalysisAgent(MCPOpenAgent): + async def process_input(self, input: AgentInput) -> AgentInput: + input.slots = [s for s in input.slots if s.name in {"portfolio_id", "risk_level"}] + return input + + async def process_output(self, output: AgentOutput) -> AgentOutput: + if output.structured_results: + results = output.structured_results[-1] + output.events = output.events or [] + output.events.append(SlotSet("analysis_result", results)) + return output +``` + +```yaml +# sub_agents/stock_explorer/config.yml +agent: + name: stock_explorer + description: "Agent that helps users research and analyze stock options" + +configuration: + module: "sub_agents.stock_explorer.custom_agent.StockAnalysisAgent" + +connections: + mcp_servers: + - name: trade_server +``` + +### What you can modify + +**`process_input(input: AgentInput) -> AgentInput`** — modify what the agent receives. +Key fields on `AgentInput`: + +| Field | Type | What to do with it | +|-------|------|--------------------| +| `slots` | `List[AgentInputSlot]` | Filter to only relevant slots | +| `user_message` | `str` | Rewrite or augment the user message | +| `conversation_history` | `str` | Trim or redact sensitive history | +| `metadata` | `Dict[str, Any]` | Inject custom metadata | + +**`process_output(output: AgentOutput) -> AgentOutput`** — modify what comes back +into Rasa. Key fields on `AgentOutput`: + +| Field | Type | What to do with it | +|-------|------|--------------------| +| `events` | `Optional[List[SlotSet]]` | Add `SlotSet` events to store data in Rasa slots | +| `structured_results` | `Optional[List]` | Read raw results from agent tool calls | +| `response_message` | `Optional[str]` | Rewrite the message sent to the user | + +### Adding custom tools + +Implement `get_custom_tool_definitions` to add Python tools alongside MCP tools. Each +tool definition follows the OpenAI function calling spec and must include a +`tool_executor` key pointing to an async method. + +The `tool_executor` method receives `arguments` as a dict and must return an +`AgentToolResult`: + +| Field | Type | Description | +|-------|------|-------------| +| `tool_name` | `str` | Name of the tool | +| `result` | `Optional[str]` | The tool's output | +| `is_error` | `bool` | Whether the execution failed. Default: `False` | +| `error_message` | `Optional[str]` | Error details if execution failed | + +Tool executors must be async. Do **not** use blocking calls (`time.sleep`, +synchronous `requests`). Use `httpx.AsyncClient`, `asyncio.to_thread`, etc. + +```python +from typing import Any, Dict, List + +class StockAnalysisAgent(MCPOpenAgent): + def get_custom_tool_definitions(self) -> List[Dict[str, Any]]: + return [{ + "type": "function", + "function": { + "name": "recommend_stocks", + "description": "Analyze results and return stock recommendations", + "parameters": { + "type": "object", + "properties": { + "search_results": { + "type": "string", + "description": "The search results to analyze", + }, + }, + "required": ["search_results"], + "additionalProperties": False, + }, + "strict": True, + }, + "tool_executor": self._recommend_stocks, + }] + + async def _recommend_stocks(self, arguments: Dict[str, Any]) -> AgentToolResult: + results = arguments["search_results"] + return AgentToolResult(tool_name="recommend_stocks", result=results) +``` + +## Common pitfalls + +- **Agent name clashes with flow IDs** — the sub agent name must be unique across all + flows and all other sub agents. +- **Using `exit_if` with general-purpose agents** — `exit_if` is only for task-specific + agents. General-purpose agents signal completion via `task_completed`. +- **`include_tools` and `exclude_tools` on the same server** — these are mutually + exclusive per MCP server entry. Using both causes a validation error. +- **MCP server name mismatch** — the `name` under `connections.mcp_servers` must + exactly match a `name` in `endpoints.yml`. +- **Blocking calls in custom tool executors** — tool executors are async. Using + `time.sleep` or synchronous HTTP clients blocks the event loop. +- **Invalid timezone** — `configuration.timezone` must be a valid IANA timezone name. + Invalid values raise a `ValidationError` during agent initialization. +- **User messages during processing** — messages sent while the sub agent is still + processing are not handled. Users must wait for the agent to complete or reach + `INPUT_REQUIRED`. diff --git a/skills/rasa-writing-custom-actions/SKILL.md b/skills/rasa-writing-custom-actions/SKILL.md index 430d401..a98db33 100644 --- a/skills/rasa-writing-custom-actions/SKILL.md +++ b/skills/rasa-writing-custom-actions/SKILL.md @@ -7,7 +7,8 @@ description: > license: Apache-2.0 metadata: author: rasa - version: "3.x" + version: "0.1.0" + rasa_version: ">=3.7.0" docs-url: https://rasa.com/docs/reference/integrations/action-server/actions --- @@ -23,6 +24,10 @@ conversation proceeds (branching, conditions, collect steps). Actions do the *ra flows to use. Keep each action focused on one responsibility — `action_search_products` searches, `action_place_order` places an order. +If all you need is a simple API call with slot-based input/output mappings and the API +is available on an MCP server, consider using an MCP tool call instead — see +`rasa-calling-mcp-tools-from-flows`. + ## Workflow 1. Identify what raw work the flow needs — API call, database query, computation. @@ -261,7 +266,3 @@ actions: - action_ask_delivery_option - validate_phone_number ``` - -## Full reference - - diff --git a/skills/rasa-writing-e2e-tests/SKILL.md b/skills/rasa-writing-e2e-tests/SKILL.md index 624c3c7..09ca658 100644 --- a/skills/rasa-writing-e2e-tests/SKILL.md +++ b/skills/rasa-writing-e2e-tests/SKILL.md @@ -7,7 +7,8 @@ description: > license: Apache-2.0 metadata: author: rasa - version: "3.x" + version: "0.1.0" + rasa_version: ">=3.12.0" docs-url: https://rasa.com/docs/pro/testing/evaluating-assistant --- @@ -258,7 +259,3 @@ If custom actions are not stubbed, start the action server first: rasa run actions & rasa test e2e ``` - -## Full reference - - diff --git a/skills/rasa-writing-responses/SKILL.md b/skills/rasa-writing-responses/SKILL.md index 9cf4368..82f5381 100644 --- a/skills/rasa-writing-responses/SKILL.md +++ b/skills/rasa-writing-responses/SKILL.md @@ -7,7 +7,8 @@ description: > license: Apache-2.0 metadata: author: rasa - version: "3.x" + version: "0.1.0" + rasa_version: ">=3.7.0" docs-url: https://rasa.com/docs/reference/primitives/responses/ --- @@ -241,7 +242,3 @@ responses: - text: "What would you like to do today?" allow_interruptions: true # default ``` - -## Full reference - - \ No newline at end of file