> For the complete documentation index, see llms.txt.
Skip to main content

Check out Port for yourself ➜ 

Trigger AI coding assistants from Port

Implement 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/trigger-ai-coding-assistants-from-port

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.

This guide shows how to trigger AI coding assistants from Port through governed workflows. It covers GitHub Copilot, Claude Code, and Google Gemini Assistant so you can choose the right assistant for your workflow while keeping the request, execution, and pull request context visible in Port.

Use GitHub Copilot when you want Port to create GitHub issues and assign them to Copilot. Use Claude Code or Google Gemini Assistant when you want Port to dispatch GitHub workflows that run the assistant, create pull requests, and track execution details in the catalog.

Open Beta

Port workflows are currently in open beta and available to all users. Workflows may undergo changes without prior notice.

Common use cases

  • Create a governed entry point for developers to request AI coding work from Port.
  • Assign GitHub issues to Copilot for code generation and pull request creation.
  • Run Claude Code or Gemini Assistant from Port while tracking execution details.
  • Connect AI coding work to services so teams can measure activity, cost, and outcomes.

How it works

Each assistant is triggered by a Port workflow that chains a small number of nodes:

  • A self-service trigger node collects the request details (a prompt, a target service, or an issue) from the developer. For Copilot, an event trigger node can also assign issues automatically when they carry the auto_assign label.
  • A GitHub integration action node dispatches the backend GitHub Actions workflow that runs the assistant, or a webhook node calls the GitHub API directly to create issues.
  • The backend GitHub Actions workflow runs the assistant, opens a pull request when relevant, and writes execution details back to the Port catalog.

The backend GitHub Actions workflows continue to do the heavy lifting. The Port workflow replaces the self-service actions and automations that used to trigger them.

Prerequisites

This guide assumes the following:

Choose an assistant

AssistantUse whenWhat you create
GitHub CopilotYou want Copilot assigned to GitHub issues created through Port.A GitHub issue blueprint, an issue creation workflow, a Copilot assignment workflow, an auto-assign workflow, and a backend GitHub Actions workflow.
Claude CodeYou want a workflow-backed assistant that can open pull requests and report token usage and cost.A Claude Code execution blueprint, a Port workflow, and a backend GitHub Actions workflow.
Google Gemini AssistantYou want a workflow-backed assistant that can open pull requests and report execution details.A Gemini Assistant execution blueprint, a Port workflow, and a backend GitHub Actions workflow.

Set up your assistant

Select the AI coding assistant you want to trigger from Port. Each tab is a self-contained setup you can follow end to end.

This setup creates GitHub issues from Port, adds an auto_assign label when requested, and assigns matching issues to GitHub Copilot.

Trigger GitHub Copilot from Port flow

Set up data model

We need to create a GitHub issue blueprint to support our Copilot workflow. This blueprint will be used to track issues that can be assigned to Copilot.

Create GitHub issue blueprint

When installing the GitHub Ocean integration, the pull request and repository blueprints are created by default. However, the GitHub issue blueprint needs to be created manually.

  1. Go to the builder page in Port.

  2. Click + Blueprint.

  3. Click the Edit JSON button.

  4. Copy and paste the following JSON configuration:

    GitHub issue blueprint (Click to expand)
    {
    "identifier": "githubIssue",
    "title": "Issue",
    "icon": "Github",
    "schema": {
    "properties": {
    "creator": {
    "title": "Creator",
    "type": "string"
    },
    "assignees": {
    "title": "Assignees",
    "type": "array"
    },
    "labels": {
    "title": "Labels",
    "type": "array"
    },
    "status": {
    "title": "Status",
    "type": "string",
    "enum": [
    "open",
    "closed"
    ],
    "enumColors": {
    "open": "green",
    "closed": "purple"
    }
    },
    "createdAt": {
    "title": "Created At",
    "type": "string",
    "format": "date-time"
    },
    "closedAt": {
    "title": "Closed At",
    "type": "string",
    "format": "date-time"
    },
    "updatedAt": {
    "title": "Updated At",
    "type": "string",
    "format": "date-time"
    },
    "description": {
    "title": "Description",
    "type": "string",
    "format": "markdown"
    },
    "issueNumber": {
    "title": "Issue Number",
    "type": "number"
    },
    "link": {
    "title": "Link",
    "type": "string",
    "format": "url"
    }
    },
    "required": []
    },
    "mirrorProperties": {},
    "calculationProperties": {},
    "aggregationProperties": {},
    "relations": {
    "repository": {
    "target": "githubRepository",
    "required": true,
    "many": false
    }
    }
    }
  5. Click Create to save the blueprint.

Update GitHub integration mapping

Update the GitHub integration configuration to include the new githubIssue blueprint in your mapping. This ensures that GitHub issues are properly synced to Port with all the required properties and relationships.

  1. Go to the data sources page in Port.

  2. Find your GitHub integration and click on it.

  3. Go to the Mapping tab.

  4. Add the following YAML block to map issues to the githubIssue blueprint:

    GitHub integration mapping (Click to expand)
    - kind: issue
    selector:
    query: .pull_request == null
    port:
    entity:
    mappings:
    identifier: .repository.name + (.id|tostring)
    title: .title
    blueprint: '"githubIssue"'
    properties:
    creator: .user.login
    assignees: '[.assignees[].login]'
    labels: '[.labels[].name]'
    status: .state
    createdAt: .created_at
    closedAt: .closed_at
    updatedAt: .updated_at
    description: .body
    issueNumber: .number
    link: .html_url
    relations:
    repository: .user.login + "/" + .repo

  5. Click Save & Resync to apply the mapping.

Auto-assign issues to request creator

To ensure that issues created through Port are assigned to the user who initiated the request, follow these steps:

  1. Ingest GitHub Users: Ensure GitHub users are imported into Port via the default integration.

  2. Create Relations Between Users and GitHub Users: Use the Onboard user self-service action to link each Port user to their GitHub username.

This linkage keeps users connected to the issues they create and improves accountability.

Add secrets

We will build workflows that create GitHub issues and assign them to Copilot. First, we need to add the necessary secrets to GitHub and to Port.

Add GitHub secrets

In your GitHub repository, go to Settings > Secrets and add the following secrets:

Add Port secrets

To add these secrets to Port:

  1. In your Port application, click on your profile picture .

  2. Click on Credentials.

  3. Click the Secrets tab.

  4. Click + Secret and add the following secret:

    Note

    The GITHUB_TOKEN mentioned here is the same token that was created and added as a secret in GitHub in the previous steps.

Add the backend GitHub workflow

Create the file .github/workflows/assign_to_copilot.yml in the .github/workflows folder of your repository.

This workflow checks whether Copilot is enabled for the repository and returns its unique ID. It also handles the assignment of the issue to Copilot and the triggering author. The Port workflows you build in the next section dispatch this backend workflow, and the GitHub integration action reports its status back to Port automatically.

GitHub workflow for Copilot assignment (Click to expand)
name: Assign Issue to Copilot

on:
workflow_dispatch:
inputs:
issue_number:
description: 'The number of the issue to assign to Copilot'
required: true
repository_owner:
description: 'Repository owner (org or user)'
required: true
type: string
repository_name:
description: 'Repository name'
required: true
type: string
issue_context_to_comment:
description: 'Context to add to the issue comment'
required: false
type: string
trigger_user_email:
description: 'Email of the triggering user'
required: false
type: string
default: ''

jobs:
assign_to_copilot:
runs-on: ubuntu-latest
steps:
- name: Validate inputs
run: |
echo "Target repository: ${{ inputs.repository_owner }}/${{ inputs.repository_name }}"
echo "Issue number: ${{ inputs.issue_number }}"

- name: Check if Copilot is enabled and get Bot ID
id: get_copilot_id
run: |
response=$(gh api graphql -f query='
query {
repository(owner: "${{ inputs.repository_owner }}", name: "${{ inputs.repository_name }}") {
suggestedActors(capabilities: [CAN_BE_ASSIGNED], first: 100) {
nodes {
login
__typename
... on Bot {
id
}
... on User {
id
}
}
}
}
}
')

# Extract Copilot bot ID
copilot_id=$(echo "$response" | jq -r '.data.repository.suggestedActors.nodes[] | select(.login == "copilot-swe-agent") | .id')

if [ -z "$copilot_id" ]; then
echo "Error: Copilot coding agent is not enabled in repository ${{ inputs.repository_owner }}/${{ inputs.repository_name }}"
exit 1
fi

echo "copilot_id=$copilot_id" >> $GITHUB_OUTPUT
echo "Found Copilot bot with ID: $copilot_id"
env:
# Use PAT instead of GITHUB_TOKEN for cross-org access
GH_TOKEN: ${{ secrets.PORT_GITHUB_TOKEN }}

- name: Get Issue ID
id: get_issue_id
run: |
response=$(gh api graphql -f query='
query {
repository(owner: "${{ inputs.repository_owner }}", name: "${{ inputs.repository_name }}") {
issue(number: ${{ inputs.issue_number }}) {
id
title
state
}
}
}
')

issue_id=$(echo "$response" | jq -r '.data.repository.issue.id')
issue_title=$(echo "$response" | jq -r '.data.repository.issue.title')
issue_state=$(echo "$response" | jq -r '.data.repository.issue.state')

if [ -z "$issue_id" ] || [ "$issue_id" = "null" ]; then
echo "Error: Issue #${{ inputs.issue_number }} not found in ${{ inputs.repository_owner }}/${{ inputs.repository_name }}"
exit 1
fi

if [ "$issue_state" = "CLOSED" ]; then
echo "Warning: Issue #${{ inputs.issue_number }} is closed"
fi

echo "issue_id=$issue_id" >> $GITHUB_OUTPUT
echo "issue_title=$issue_title" >> $GITHUB_OUTPUT
echo "Found issue: $issue_title (ID: $issue_id)"
env:
GH_TOKEN: ${{ secrets.PORT_GITHUB_TOKEN }}

- name: Comment on issue before assignment
id: comment_on_issue
if: ${{ inputs.issue_context_to_comment != '' }}
run: |
gh issue comment ${{ inputs.issue_number }} \
--repo "${{ inputs.repository_owner }}/${{ inputs.repository_name }}" \
--body "$ISSUE_CONTEXT"
env:
GH_TOKEN: ${{ secrets.PORT_GITHUB_TOKEN }}
ISSUE_CONTEXT: ${{ inputs.issue_context_to_comment }}

- name: Get Trigger User from Port
id: port_user_lookup
if: ${{ inputs.trigger_user_email != '' && inputs.trigger_user_email != 'null' }}
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
baseUrl: https://api.port.io
operation: GET
identifier: ${{ inputs.trigger_user_email }}
blueprint: _user

- name: Extract GitHub Username
id: extract_username
if: ${{ inputs.trigger_user_email != '' && inputs.trigger_user_email != 'null' }}
run: |
username=$(echo '${{ steps.port_user_lookup.outputs.entity }}' | jq -r '.entity.properties.git_hub_username // .properties.git_hub_username')
if [ "$username" = "null" ] || [ -z "$username" ]; then
echo "No GitHub username found for ${{ inputs.trigger_user_email }}"
echo "github_username=" >> $GITHUB_OUTPUT
else
echo "Found GitHub username: $username"
echo "github_username=$username" >> $GITHUB_OUTPUT
fi

- name: Assign issue to Copilot
id: assign_issue
run: |
actor_ids="[\"${{ steps.get_copilot_id.outputs.copilot_id }}\"]"

# Only try to add the initiator if the extract_username step actually ran
if [ "${{ inputs.trigger_user_email }}" != "null" ] && [ -n "${{ steps.extract_username.outputs.github_username }}" ]; then
user_id=$(gh api graphql -f query="query { user(login: \"${{ steps.extract_username.outputs.github_username }}\") { id }}" | jq -r '.data.user.id')
if [ -n "$user_id" ] && [ "$user_id" != "null" ]; then
echo "Found user ID for initiator: $user_id"
actor_ids="[\"${{ steps.get_copilot_id.outputs.copilot_id }}\", \"$user_id\"]"
else
echo "No valid GitHub user ID found for initiator"
fi
else
echo "Skipping initiator assignment (no trigger_user_email or username)"
fi
response=$(gh api graphql -f query="
mutation {
replaceActorsForAssignable(input: {
assignableId: \"${{ steps.get_issue_id.outputs.issue_id }}\",
actorIds: $actor_ids
}) {
assignable {
... on Issue {
id
title
assignees(first: 10) {
nodes {
login
}
}
}
}
}
}
")

assignees=$(echo "$response" | jq -r '.data.replaceActorsForAssignable.assignable.assignees.nodes[].login' 2>/dev/null)

if echo "$assignees" | grep -q "Copilot"; then
echo "✅ Successfully assigned issue to Copilot"
else
echo "❌ Failed to assign issue to Copilot"
exit 1
fi
env:
GH_TOKEN: ${{ secrets.PORT_GITHUB_TOKEN }}

Build the workflows

We will create three workflows for the Copilot flow:

  • Create GitHub issue - a self-service workflow that opens a GitHub issue and optionally adds the auto_assign label.
  • Assign issue to Copilot - a self-service workflow that assigns an existing issue to Copilot on demand.
  • Auto-assign issues to Copilot - an event-triggered workflow that assigns issues to Copilot automatically when they carry the auto_assign label.

The last two workflows dispatch the assign_to_copilot.yml backend you added above.

To create each workflow:

  1. Go to the Workflows page of your portal.
  2. Click on the + Workflow button in the top-right corner.
  3. In the Name field, enter a descriptive name, then click Confirm.
  4. On the editor page, click the see workflow JSON button (the code icon) to open the JSON editor.
  5. Copy and paste the workflow JSON below to replace the example workflow.
  6. Click Save to save the workflow.

Create GitHub issue workflow

This workflow lets a developer open a GitHub issue in a selected repository. When Auto-assign to Copilot is enabled, it adds the auto_assign label so the auto-assign workflow picks it up.

Create GitHub issue workflow (Click to expand)
{
"identifier": "create_github_issue",
"title": "Create GitHub Issue",
"icon": "Github",
"description": "Create a new issue in a GitHub repository, optionally assigning it to Copilot",
"allowAnyoneToViewRuns": true,
"nodes": [
{
"identifier": "trigger",
"title": "Issue details",
"config": {
"type": "SELF_SERVE_TRIGGER",
"userInputs": {
"properties": {
"repository": {
"type": "string",
"format": "entity",
"blueprint": "githubRepository",
"title": "Repository",
"description": "The repository to create the issue in"
},
"title": {
"type": "string",
"title": "Issue Title",
"description": "A short description for the task"
},
"body": {
"type": "string",
"format": "markdown",
"title": "Issue Body",
"description": "The actual task expected. Add additional context like related commits or pull requests, relevant people, and other instructions"
},
"assign_to_copilot": {
"type": "boolean",
"title": "Auto-assign to Copilot",
"description": "Automatically assign this issue to GitHub Copilot for AI-powered coding assistance",
"default": false
},
"base_branch": {
"type": "string",
"title": "Base Branch (optional)",
"description": "Branch for Copilot to base its work on. If not specified, the repository's default branch will be used."
},
"labels": {
"type": "array",
"items": {
"type": "string"
},
"title": "Issue Labels",
"description": "Labels to add to the issue",
"default": []
}
},
"required": ["repository", "title", "body"],
"order": ["repository", "title", "body", "assign_to_copilot", "base_branch", "labels"]
}
}
},
{
"identifier": "create_issue",
"title": "Create issue in GitHub",
"config": {
"type": "WEBHOOK",
"url": "https://api.github.com/repos/{{ .outputs.trigger.repository }}/issues",
"method": "POST",
"headers": {
"Accept": "application/vnd.github+json",
"Authorization": "Bearer {{ .secrets[\"GITHUB_TOKEN\"] }}",
"X-GitHub-Api-Version": "2022-11-28",
"Content-Type": "application/json"
},
"body": {
"title": "{{ .outputs.trigger.title }}",
"body": "{{ if .outputs.trigger.base_branch then (.outputs.trigger.body + \"\n\n> Base your changes on the `\" + .outputs.trigger.base_branch + \"` branch.\") else .outputs.trigger.body end }}",
"labels": "{{ if .outputs.trigger.assign_to_copilot then (.outputs.trigger.labels + [\"auto_assign\"]) else .outputs.trigger.labels end }}"
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "create_issue"
}
]
}
Copilot branch behavior

By default, Copilot uses the repository's default branch as the base for its work. Use the optional Base Branch input to specify a different branch.

Assign issue to Copilot workflow

This workflow lets a developer assign an existing issue to Copilot on demand. It fetches the selected issue from the catalog to resolve its number and repository, then dispatches the assign_to_copilot.yml backend workflow.

Replace the variables
  • <YOUR_GITHUB_OCEAN_INTEGRATION_ID> - your GitHub Ocean integration installation ID.
  • <GITHUB-ORG> - your GitHub organization or user name.
  • <GITHUB-REPO> - the repository where the assign_to_copilot.yml workflow file is stored.
Assign issue to Copilot workflow (Click to expand)
{
"identifier": "assign_issue_to_copilot",
"title": "Assign issue to Copilot",
"icon": "Github",
"description": "Assign an existing GitHub issue to the Copilot coding agent",
"allowAnyoneToViewRuns": true,
"nodes": [
{
"identifier": "trigger",
"title": "Select issue",
"config": {
"type": "SELF_SERVE_TRIGGER",
"userInputs": {
"properties": {
"issue": {
"type": "string",
"format": "entity",
"blueprint": "githubIssue",
"title": "Issue",
"description": "The GitHub issue to assign to Copilot"
}
},
"required": ["issue"],
"order": ["issue"]
}
}
},
{
"identifier": "fetch_issue",
"title": "Fetch issue details",
"config": {
"type": "WEBHOOK",
"url": "https://api.port.io/v1/blueprints/githubIssue/entities/{{ .outputs.trigger.issue }}",
"method": "GET",
"headers": {
"Content-Type": "application/json"
}
},
"variables": {
"issueNumber": "{{ .result.response.data.entity.properties.issueNumber }}",
"repository": "{{ .result.response.data.entity.relations.repository }}"
}
},
{
"identifier": "assign",
"title": "Assign to Copilot",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<YOUR_GITHUB_OCEAN_INTEGRATION_ID>",
"integrationProvider": "github-ocean",
"integrationInvocationType": "dispatch_workflow",
"integrationActionExecutionProperties": {
"org": "<GITHUB-ORG>",
"repo": "<GITHUB-REPO>",
"workflow": "assign_to_copilot.yml",
"workflowInputs": {
"issue_number": "{{ .outputs.fetch_issue.issueNumber | tostring }}",
"repository_owner": "{{ .outputs.fetch_issue.repository | split(\"/\") | .[0] }}",
"repository_name": "{{ .outputs.fetch_issue.repository | split(\"/\") | .[1] }}",
"trigger_user_email": "{{ .workflowRun.trigger.by.email }}"
},
"reportWorkflowStatus": true
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "fetch_issue"
},
{
"sourceIdentifier": "fetch_issue",
"targetIdentifier": "assign"
}
]
}

Auto-assign issues to Copilot workflow

This workflow replaces the automation from the original guide. It listens for GitHub issues that carry the auto_assign label and are not yet assigned to Copilot, then dispatches the assign_to_copilot.yml backend workflow. The event trigger exposes the issue's properties and relations directly, so no fetch step is needed.

Replace the variables
  • <YOUR_GITHUB_OCEAN_INTEGRATION_ID> - your GitHub Ocean integration installation ID.
  • <GITHUB-ORG> - your GitHub organization or user name.
  • <GITHUB-REPO> - the repository where the assign_to_copilot.yml workflow file is stored.
Auto-assign issues to Copilot workflow (Click to expand)
{
"identifier": "auto_assign_issue_to_copilot",
"title": "Auto-assign issues to Copilot",
"icon": "GithubCopilot",
"description": "Assign GitHub issues to Copilot automatically when they carry the auto_assign label",
"allowAnyoneToViewRuns": true,
"nodes": [
{
"identifier": "trigger",
"title": "On issue labeled auto_assign",
"config": {
"type": "EVENT_TRIGGER",
"event": {
"type": "ENTITY_UPDATED",
"blueprintIdentifier": "githubIssue"
},
"condition": {
"type": "JQ",
"expressions": [
".diff.after.properties.labels | index(\"auto_assign\") != null",
".diff.after.properties.assignees | index(\"Copilot\") == null"
],
"combinator": "and"
},
"published": true
}
},
{
"identifier": "assign",
"title": "Assign to Copilot",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<YOUR_GITHUB_OCEAN_INTEGRATION_ID>",
"integrationProvider": "github-ocean",
"integrationInvocationType": "dispatch_workflow",
"integrationActionExecutionProperties": {
"org": "<GITHUB-ORG>",
"repo": "<GITHUB-REPO>",
"workflow": "assign_to_copilot.yml",
"workflowInputs": {
"issue_number": "{{ .outputs.trigger.diff.after.properties.issueNumber | tostring }}",
"repository_owner": "{{ .outputs.trigger.diff.after.relations.repository | split(\"/\") | .[0] }}",
"repository_name": "{{ .outputs.trigger.diff.after.relations.repository | split(\"/\") | .[1] }}"
},
"reportWorkflowStatus": true
}
}
}
],
"connections": [
{
"sourceIdentifier": "trigger",
"targetIdentifier": "assign"
}
]
}

Test the workflow

Now let's test the complete flow to ensure everything works correctly.

Run the workflow

  1. Go to the self-service page and run the Create GitHub Issue workflow.
  2. Select a repository, fill in the title and body, and toggle Auto-assign to Copilot on.
  3. Open the workflow runs tab and confirm the Auto-assign issues to Copilot workflow was triggered once the issue synced with the auto_assign label.
  4. Go to the issue in GitHub and verify that Copilot is assigned.
  5. Check that a pull request is opened for the issue.
GitHub Copilot assignment test result