Skip to main content

Automation-Backed Action Tool

Description​

This sample gives an AI agent the ability to do something rather than just read: it exposes a webhook-triggered automation as a Model Context Protocol (MCP) tool. The agent calls the tool, Stack9 runs the automation with the tool arguments as the request body, and the automation's last action output is returned to the agent.

An automation tool without an inputSchema derives its schema only from the entry action type's input contract; this sample's first action declares none, so without the inline schema below the tool would expose zero arguments. Authorization comes from the same automation-permission model the automation's HTTP webhook route uses, and it fails closed: if no permission record exists for the automation, the call is refused.

Use Case​

A customer-support assistant has triaged an inbound email, identified the customer, and needs to raise a ticket. Instead of the agent calling a bespoke HTTP endpoint with hand-rolled auth, it calls the create_support_ticket MCP tool on the instance and receives the created ticket's id and reference back.

Key Features​

  • Write/side-effecting action exposed safely to an agent
  • Mandatory inline inputSchema with per-argument descriptions
  • Arguments passed through as the automation body (trigger.body)
  • Gated by app_automation_permissions — fail-closed, no record means no access
  • Runs as the calling user, so downstream audit fields attribute correctly
  • Returns the automation's last action output to the agent

The automation it points at​

src/automations/create_support_ticket.json

{
"key": "create_support_ticket",
"name": "Create support ticket",
"entityKey": "support_ticket",
"app": "support",
"triggerType": "webhook",
"triggerParams": {
"method": "post",
"path": "/create-support-ticket"
},
"actions": [
{
"name": "Validate customer exists",
"key": "validate_customer",
"actionTypeKey": "entity_find",
"params": {
"entityKey": "customer",
"query": {
"$where": { "id": "{{trigger.body.customer_id}}" }
}
}
},
{
"name": "Create the ticket",
"key": "create_ticket",
"actionTypeKey": "entity_create",
"params": {
"entityKey": "support_ticket",
"body": {
"customer_id": "{{trigger.body.customer_id}}",
"subject": "{{trigger.body.subject}}",
"body": "{{trigger.body.body}}",
"priority": "{{trigger.body.priority}}",
"channel": "ai_assistant",
"status": "open"
}
}
}
]
}

Two things to notice:

  • The automation must have "triggerType": "webhook" — the Console's tool picker only lists webhook automations, and no other trigger type can be invoked through MCP.
  • The triggerParams.path is irrelevant to MCP. MCP invokes the automation by key, not over its HTTP route. The path only matters if you also want the automation reachable as a normal webhook endpoint.

JSON Definition​

src/mcps/support_actions.json

{
"key": "support_actions",
"name": "Support Actions",
"description": "Write actions the support assistant may perform on behalf of an agent.",
"tools": [
{
"key": "create_support_ticket",
"name": "create_support_ticket",
"description": "Create a support ticket against an existing customer. Requires the numeric Stack9 customer id — look it up first if you only have a name or email. Returns the created ticket id and reference.",
"sourceType": "automation",
"inputSchema": {
"customer_id": {
"type": "number",
"description": "Numeric Stack9 customer id the ticket belongs to"
},
"subject": {
"type": "string",
"description": "One-line summary of the issue, max ~120 characters"
},
"body": {
"type": "string",
"description": "Full description of the issue, including any steps to reproduce"
},
"priority": {
"type": "string",
"description": "One of: low, medium, high. Defaults to medium when omitted.",
"optional": true
},
"metadata": {
"type": "object",
"description": "Optional free-form context, e.g. source email id or conversation id",
"optional": true
}
}
}
]
}

Permission setup​

The automation belongs to the support app, so access is decided by that app's automation permission record:

  • If no permission record exists for create_support_ticket, every MCP call is refused with 403. This is deliberate — an automation is not exposed by accident.
  • If a record exists, the caller's role for the support app must meet or exceed the record's required role.
  • If the record's required role is Public, any authenticated caller passes.
warning

An automation tool is a write path. Grant the agent's principal the narrowest app role that satisfies the permission record, and prefer a dedicated Stack9 user for the agent so its actions are attributable and revocable independently of a human account.

Result​

The tool returns the last action's output. With the automation above, that is the create_ticket action:

{
"content": [
{
"type": "text",
"text": "{\"id\":9114,\"reference\":\"TKT-9114\",\"status\":\"open\",\"priority\":\"medium\"}"
}
]
}

If the last action produces no output, the tool returns null. Order your actions so the one whose result the agent needs is last.

Notes​

  • Declare inputSchema unless the entry action has a contract. When the automation's first action type declares an input zod object, the schema is derived from it and the body is validated against it; otherwise, without an inline schema the agent can only call the tool with an empty body.
  • The whole arguments object becomes the body. Every argument is available in the automation as trigger.body.{argument}. There is no filter/variable partitioning as there is for query tools.
  • object and array are untyped. The metadata argument accepts any object shape; validate it inside the automation if the shape matters.
  • Errors reach the agent. A permission failure is returned as Authorization error ({code}): {reason}. Any other failure is logged server-side and returned to the agent as Error: {message}, so keep sensitive detail out of the messages your actions throw.
  • Idempotency is your responsibility. An agent that retries a failed call may create a duplicate ticket. If that matters, add a dedupe key argument and handle it in the automation.
  • Endpoint for this config: {coreBaseUrl}/api/mcp/support_actions.