Build workflows with AI
Send this guide to your coding agent.
Prerequisite: Install Port MCP
Open plan mode. Implement this Port guide in my org via MCP: https://docs.port.io/guides/all/build-port-workflows-with-mcp/ Goal: get the guide's core flow working end-to-end in my org; adapting it to fit my existing setup takes priority over matching the guide 1:1. Plan: 1. Confirm MCP is connected, in the right org, with sufficient permissions. 2. Diff the guide's data model (blueprints, properties, relations, actions, agents, automations, integrations, secrets) against mine. 3. Propose adaptations for gaps, reusing existing blueprints/relations over guide-named duplicates. 4. Flag what needs a UI click, credential, or secret from me, testing MCP capability empirically before ruling anything out. 5. Stop on any blocker and give me options. Approving this plan authorizes the writes it lists; pause only for writes beyond what's listed. Build: - Extend blueprint schema additively when upserting; don't remove or overwrite existing properties, and treat type conflicts as a blocker, not an auto-fix. - List any mock data in the plan, minimal and labeled mock; once approved, seed it without re-asking, and tell me what you seeded. - For anything the guide writes downstream (e.g. a webhook target), use a real entity, not a mock. - For pages/widgets, use the real page identifier from the app URL, not a guessed slug. - When you hit a UI step confirmed (not assumed) unsupported via MCP, pause, give exact clicks, then resume via MCP. - Validate and give links after each meaningful step (only a tool-returned URL, no guessed paths); don't proceed if the last run wasn't a success. Done: - Confirm the guide's expected output exists and runs in Port. - Summarize adaptations, seeded data, what was mocked or skipped, remaining UI steps, and how to verify.
Use Port's MCP (Model Context Protocol) server to build workflows through natural language conversations with AI. A workflow is a multi-step node graph, made up of a trigger, one or more action nodes, and optional branching, that runs on Port's governed execution layer. This guide shows how to design workflows, configure user inputs, wire up backends, and react to catalog events, all by describing what you need in plain language.
Instead of hand-authoring workflow JSON in the editor, you can have a conversation with your AI assistant to build workflows iteratively, review each change, and refine the node graph as you go.
Port workflows are currently in open beta and available to all users. Workflows may undergo changes without prior notice.
Common use cases
- Build deployment workflows: Describe a deployment process and let AI assemble the trigger and action nodes.
- Automate incident response: Ask AI to create a workflow that opens incidents and notifies the right people.
- Add approval steps: Explain your approval requirements and have AI insert an input node with reviewers.
- React to catalog events: Describe an event-driven flow and let AI configure the event trigger and downstream nodes.
Prerequisites
This guide assumes you have:
- A Port account with access to the workflows beta feature.
- Basic understanding of workflow concepts and structure, including nodes, connections, and data flow.
- Port MCP server configured in your IDE.
Create workflows with AI
The Port MCP server provides workflow tools that let AI agents build and run your workflows through conversation. You describe what you need, and the AI generates the node graph and creates it in Port.
| Tool | What it does |
|---|---|
upsert_workflow | Create or update a workflow directly from a definition. |
suggest_workflow | Propose a workflow as a preview diff that you review and apply from the Workflow Editor. |
list_workflows | Discover the workflows that already exist in your organization. |
get_workflow | Inspect a workflow's definition, nodes, and inputs. |
trigger_run | Execute a workflow run. |
get_run | Track the status and outputs of a run. |
Use upsert_workflow to create or update a workflow immediately. When you are working inside the Workflow Editor, ask the assistant to use suggest_workflow instead. It presents the change as a preview diff that you review and apply from the editor.
Start with a simple description
Describe the workflow you want in natural language. The AI interprets your requirements and generates the node graph.
Example conversation:
"Create a workflow called 'Deploy to Staging' triggered by a self-service form. It takes a service name and environment, then dispatches a GitHub workflow to deploy the service."
The AI maps this to a SELF_SERVE_TRIGGER node for the form and an INTEGRATION_ACTION node that dispatches a GitHub Actions workflow through your installed GitHub integration, then saves it with upsert_workflow:
Resulting workflow definition (Click to expand)
{
"identifier": "deploy_to_staging",
"title": "Deploy to Staging",
"icon": "Rocket",
"description": "Deploy a service to the staging environment",
"category": "Deployments",
"nodes": [
{
"identifier": "trigger",
"title": "Deploy to Staging",
"config": {
"type": "SELF_SERVE_TRIGGER",
"userInputs": {
"properties": {
"service": {
"type": "string",
"title": "Service name",
"description": "The service (repository) to deploy"
},
"environment": {
"type": "string",
"title": "Environment",
"enum": ["staging"],
"default": "staging"
}
},
"required": ["service"]
}
}
},
{
"identifier": "dispatch_deploy",
"title": "Dispatch GitHub Deployment",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<GITHUB_INTEGRATION_ID>",
"integrationProvider": "github-ocean",
"integrationInvocationType": "dispatch_workflow",
"integrationActionExecutionProperties": {
"org": "<GITHUB_ORG>",
"repo": "{{ .outputs.trigger.service }}",
"workflow": "deploy.yml",
"workflowInputs": {
"environment": "{{ .outputs.trigger.environment }}"
},
"reportWorkflowStatus": true
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "dispatch_deploy"
}
]
}
Replace <GITHUB_INTEGRATION_ID> with your GitHub integration's installation ID (found on the Data sources page) and <GITHUB_ORG> with your GitHub organization. If your assistant does not know these values, it can save the node with deferIntegrationInstallation: true so you connect the integration later.
After creating a workflow, refine it with follow-up requests like "Add an approval step before the deployment" or "Also update the service entity with the deployed version when the deploy succeeds". The assistant updates the node graph and saves it again.
Add user inputs
For self-service workflows, inputs live in the trigger node's userInputs.properties, with a required list for the mandatory ones. Describe the input and the AI configures it.
Example conversation:
"Add a multi-select input for deployment regions with options us-east-1, us-west-2, and eu-west-1."
The AI updates the SELF_SERVE_TRIGGER node's userInputs with an array input:
{
"regions": {
"type": "array",
"title": "Deployment regions",
"items": {
"type": "string",
"enum": ["us-east-1", "us-west-2", "eu-west-1"]
}
}
}
Any later node reads the selection with {{ .outputs.trigger.regions }}. For all supported input types, see user inputs.
Configure node types and backends
Workflows perform work through action and flow nodes. Describe what each step should do and the AI picks the right node type:
| Node type | Use it for |
|---|---|
WEBHOOK | Call any HTTP API (GitLab, Bitbucket, Azure DevOps, Slack, PagerDuty, or your own service). |
INTEGRATION_ACTION | Dispatch a GitHub Actions workflow through your installed GitHub integration. |
UPSERT_ENTITY | Create or update an entity in your catalog. |
CONDITION | Branch the flow based on JQ expressions. |
INPUT | Pause for approval or extra input from one or more users. |
AI / AI_AGENT | Invoke Port AI or a pre-configured agent for reasoning or enrichment. |
Integration actions currently support GitHub only, through the github-ocean provider. For GitLab, Bitbucket, Azure DevOps, and any third-party API, use a WEBHOOK node that calls the provider's pipeline-trigger or REST endpoint.
Example conversations:
- "Add a step that dispatches the deploy.yml GitHub workflow in the ops-automation repo." becomes an
INTEGRATION_ACTIONnode. - "Add a step that triggers a GitLab pipeline in the infrastructure project." becomes a
WEBHOOKnode. - "Add a step that calls our internal API at https://api.internal.com/deploy." becomes a
WEBHOOKnode.
Real webhook backend examples
When your assistant has web search access, it can research an API's documentation and build a complete WEBHOOK node. Here are examples:
- Create Jira issue
- Trigger GitLab pipeline
- Trigger incident
"Add a step that calls the Jira REST API to create an issue. Include inputs for project key, issue type, summary, and description."Resulting webhook node (Click to expand)
"Add a step that triggers a GitLab pipeline in the infrastructure project on the main branch."Resulting webhook node (Click to expand)
"Add a step that calls the PagerDuty Events API to trigger an incident. Include inputs for summary and severity."Resulting webhook node (Click to expand)
Store credentials as organization secrets and reference them in WEBHOOK nodes with {{ .secrets["NAME"] }}. Secrets are not supported in INTEGRATION_ACTION nodes, because GitHub authenticates through your installed integration rather than a token you supply.
Build event-driven workflows
Workflows can start automatically when something changes in your catalog. Instead of a self-service form, the workflow begins with an EVENT_TRIGGER node.
Describe the trigger condition
Explain what change should start the workflow.
Example conversation:
"Create a workflow that posts to Slack whenever a service's health score drops below 50."
The AI configures an EVENT_TRIGGER on the service blueprint with a JQ condition, and reads the changed entity from the diff object:
Resulting workflow definition (Click to expand)
{
"identifier": "notify_low_health",
"title": "Notify on Low Health Score",
"nodes": [
{
"identifier": "trigger",
"title": "On Health Score Change",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ENTITY_UPDATED",
"blueprintIdentifier": "service"
},
"condition": {
"type": "JQ",
"expressions": [
".diff.after.properties.health_score < 50",
".diff.before.properties.health_score >= 50"
],
"combinator": "and"
}
}
},
{
"identifier": "notify_slack",
"title": "Post to Slack",
"config": {
"type": "WEBHOOK",
"url": "https://hooks.slack.com/services/xxx/yyy/zzz",
"method": "POST",
"body": {
"text": "Service {{ .outputs.trigger.diff.after.title }} health score dropped to {{ .outputs.trigger.diff.after.properties.health_score }}."
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "notify_slack"
}
]
}
Event triggers expose the change through a diff object. Read the new state with {{ .outputs.trigger.diff.after.* }} and the previous state with {{ .outputs.trigger.diff.before.* }}. For ENTITY_CREATED events there is no before, and for ENTITY_DELETED events there is no after.
Common event patterns
Here are patterns you can describe to AI:
Entity lifecycle events:
- "When a new service is created, send a welcome message to the owning team's Slack channel." uses
ENTITY_CREATED. - "When a service is deleted, call our cleanup API." uses
ENTITY_DELETED.
Property change events:
- "When a service's status changes to deprecated, notify the platform team." uses
ENTITY_UPDATEDwith a JQ condition.
Timer-based events:
- "When an environment's TTL expires, mark it as expired and notify the owner." uses
TIMER_EXPIRED.
TIMER_EXPIRED is an event type inside an EVENT_TRIGGER node. It requires a propertyIdentifier pointing at the timer property that should fire the workflow.
Chain steps into a flow
Workflows chain steps through connections. Each connection links a source node to a target node, and data flows forward through .outputs. Ask the AI to design the whole flow at once.
Example conversation:
"When a PagerDuty incident is created with high urgency, create a tracking entity in Port, post to the #incidents Slack channel, then dispatch a scale-up GitHub workflow for the affected service."
The AI builds a sequential chain: event trigger, then upsert entity, then Slack webhook, then GitHub integration action.
Resulting workflow definition (Click to expand)
{
"identifier": "critical_incident_response",
"title": "Critical Incident Response",
"nodes": [
{
"identifier": "trigger",
"title": "On Critical Incident Created",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ENTITY_CREATED",
"blueprintIdentifier": "pagerdutyIncident"
},
"condition": {
"type": "JQ",
"expressions": [".diff.after.properties.urgency == \"high\""],
"combinator": "and"
}
}
},
{
"identifier": "track_incident",
"title": "Create Tracking Entity",
"config": {
"type": "UPSERT_ENTITY",
"blueprintIdentifier": "incident_tracker",
"mapping": {
"identifier": "{{ .outputs.trigger.diff.after.identifier }}",
"title": "{{ .outputs.trigger.diff.after.title }}",
"properties": {
"status": "open"
}
}
}
},
{
"identifier": "notify_slack",
"title": "Post to #incidents",
"config": {
"type": "WEBHOOK",
"url": "https://hooks.slack.com/services/xxx/yyy/zzz",
"method": "POST",
"body": {
"text": "Critical incident: {{ .outputs.trigger.diff.after.title }}"
}
}
},
{
"identifier": "scale_up",
"title": "Dispatch Scale-Up Workflow",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<GITHUB_INTEGRATION_ID>",
"integrationProvider": "github-ocean",
"integrationInvocationType": "dispatch_workflow",
"integrationActionExecutionProperties": {
"org": "<GITHUB_ORG>",
"repo": "ops-automation",
"workflow": "scale-up.yml",
"workflowInputs": {
"service": "{{ .outputs.trigger.diff.after.relations.service }}"
}
}
}
}
],
"connections": [
{ "sourceIdentifier": "trigger", "targetIdentifier": "track_incident" },
{ "sourceIdentifier": "track_incident", "targetIdentifier": "notify_slack" },
{ "sourceIdentifier": "notify_slack", "targetIdentifier": "scale_up" }
]
}
Each node, and each condition option, can have at most one outgoing target. To run several steps, chain them one after another rather than branching a single node into multiple parallel targets.
// Not supported - one node pointing to two targets
{ "sourceIdentifier": "call_api", "targetIdentifier": "notify_slack" },
{ "sourceIdentifier": "call_api", "targetIdentifier": "update_entity" }
// Supported - chain the steps sequentially
{ "sourceIdentifier": "call_api", "targetIdentifier": "notify_slack" },
{ "sourceIdentifier": "notify_slack", "targetIdentifier": "update_entity" }
To branch based on a value, ask for a condition node. The AI adds a CONDITION node with named options, and each branch becomes a separate connection that carries a sourceOptionIdentifier.
Let's test it
After creating a workflow, test it with an MCP-enabled AI assistant. In this example, we run the "Deploy to Staging" workflow we built earlier.
Prompt
Ask your AI assistant:
"Trigger the 'Deploy to Staging' workflow for the checkout-service."
What happens
The agent will:
- Discover the workflow - Find it and its identifier with
list_workflows. - Inspect the inputs - Use
get_workflowto read theSELF_SERVE_TRIGGERnode'suserInputsand determine required fields. - Trigger the run - Call
trigger_runwithtype: "WORKFLOW", the workflow identifier, and the inputs. - Track the result - Poll
get_runwith the returned run ID and report the outcome.
Workflow runs execute asynchronously, so the agent uses get_run to poll until the run completes. Event-driven workflows fire automatically on catalog changes and cannot be triggered manually.
Best practices for AI-driven workflow creation
When using AI to build workflows, follow these practices to get the best results.
Be specific about inputs
The more detail you provide about inputs, the better the trigger node's schema:
- Good: "Add a trigger with inputs for environment (enum: production, staging, dev), version (string, required), and notify_slack (boolean, default true)."
- Less effective: "Add a deploy trigger."
Specify the backend
Tell AI which backend each step should use, so it picks the right node type:
- "...that dispatches a GitHub workflow in the deployments repo." uses an integration action.
- "...that triggers a GitLab pipeline." uses a webhook.
- "...that calls a webhook at https://api.example.com/deploy." uses a webhook.
Describe approval requirements
If a step needs approval, describe who approves and how many people are required:
"Add an approval step before the production deploy that requires two reviewers from the platform-admins team."
The AI adds an input node with approve and decline options.
Use clear, consistent identifiers
Use short, snake_case node identifiers with a verb-noun pattern:
- Good:
deploy_to_production,restart_service,notify_team. - Less clear:
prod-dep,svc_rst,n1.
Always use snake_case for node identifiers. A hyphen in an identifier is read as subtraction inside a JQ expression, so .outputs.my-node.field fails while .outputs.my_node.field works.
Reference data correctly
Keep these data-flow rules in mind when reviewing what the AI generates:
- Reference self-service inputs and event data with
{{ .outputs.trigger.* }}. - Reference a previous node's output with
{{ .outputs.<node_identifier>.* }}. - Reference organization secrets with
{{ .secrets["NAME"] }}, and only insideWEBHOOKnodes. - Calls to Port's API (
https://api.port.io) are authenticated automatically, so no authorization header is needed.
Related documentation
- Workflows overview - What workflows are and how they fit together.
- Concepts and structure - Nodes, connections, and JSON structure.
- Self-service trigger configuration - Build workflows that users run from a form.
- Event triggers - Run workflows automatically on catalog changes.
- Action nodes - Configure and chain action nodes.
- GitHub integration action - Dispatch GitHub Actions workflows.
- Data flow - Pass data between nodes with outputs, variables, and secrets.
- Workflow examples - End-to-end workflow examples.
- Port MCP server installation - Connect the MCP server to your IDE.
- Available MCP tools - Reference for the Port MCP tools.