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

Check out Port for yourself โžœย 

Claude managed agents

The Claude managed agents integration action allows workflows to create new agents or trigger agent sessions directly using your installed Claude Managed Agents integration.

Prerequisitesโ€‹

  • A Claude Managed Agents integration installed in your Port organization.
  • Actions processing must be enabled on your integration:
    • Hosted by Port: actions processing is enabled automatically.
    • Self-hosted (Helm or Docker): actions processing is disabled by default and must be explicitly enabled.
  • For managed Port MCP on trigger_agent:
    • Set executionIdentity on the workflow node (actor for self-service, or service_account for automations. Only admins can author service_account).
    • The integration derives the Port MCP URL from your Port API region (OCEAN__PORT__MCP_URL or automatically from OCEAN__PORT__BASE_URL).

Configurationโ€‹

FieldTypeDescription
typestringRequired. Must be "INTEGRATION_ACTION"
installationIdstringRequired. Your Claude Managed Agents integration installation ID
integrationProviderstringRequired. Must be "claude-managed-agents"
integrationInvocationTypeenumRequired. One of the values listed in available actions
integrationActionExecutionPropertiesobjectRequired. Operation-specific configuration
executionIdentityobjectOptional. Who Port API / Port MCP calls act as for this node. Sibling of integrationActionExecutionProperties (not inside it). Required for managed Port MCP on trigger_agent. See Execution identity

The remaining sections describe the execution properties of each action.

Available actionsโ€‹

Each action is selected with the integrationInvocationType field:

ActionInvocation typeDescription
Trigger agenttrigger_agentStart a new session or continue an existing one, and send a message to the agent
Create agentcreate_agentCreate a new Claude managed agent and register it in Port's catalog
Update agentupdate_agentUpdate an existing Claude managed agent's name, model, system prompt, or config
Archive agentarchive_agentPermanently archive a Claude managed agent
Create environmentcreate_environmentCreate a new Claude managed environment and register it in Port's catalog
Update environmentupdate_environmentUpdate an existing Claude managed environment's name, description, scope, or config
Delete environmentdelete_environmentPermanently delete a Claude managed environment and remove it from Port's catalog

Trigger agentโ€‹

Starts a new session or continues an existing idle session, then sends a user message to the agent.

Sending a message is not a separate action. trigger_agent always sends the prompt to a session, and the sessionId property decides which session receives it:

  • Start a session and send the first message: omit sessionId and provide environmentId. Port creates a session, sends the prompt, and reflects the new session in your catalog.
  • Send a follow-up message to an existing session: provide sessionId. Port sends the prompt to that session, and environmentId and config are ignored.

A session can only be continued while it is idle. If the session is running, terminated, or waiting for a tool confirmation, the node fails rather than queueing the prompt. Runs that target the same sessionId are executed one at a time, so two follow-ups cannot race on the same session, while new sessions run in parallel.

Webhooks required for reportSessionStatus

Set reportSessionStatus: true only when your integration can receive live session webhooks (see webhook configuration). Hosted by Port and self-hosted realtime installs register the webhook automatically, CI (ONCE) installs do not. With it set to true, the node waits for a session webhook to mark the run as complete, without live webhooks, the node blocks indefinitely. The default, false, completes the node immediately after the prompt is sent.

Execution properties

FieldTypeDescription
agentIdstringRequired. The ID of the Claude agent to trigger
environmentIdstringRequired when starting a new session
promptstringRequired. The user message to send to the agent
sessionIdstringAn existing idle session to continue. When provided, environmentId and config are ignored
enablePortMcpbooleanWhen true (default), ensures the agent has the Port MCP server and toolset configured and attaches a managed vault credential when the node has executionIdentity. Set to false to skip Port MCP for this run. See Managed Port MCP
vaultNamestringOptional Claude vault display name for the managed Port MCP credential. Must be stable and unique per identity; use the same value when continuing a session. Defaults to the run's execution identity identifier. See Managed Port MCP
reportSessionStatusbooleanWhen true, the node waits for the session to reach a terminal state via webhook and streams agent messages to the run logs in near-real time (requires live webhooks on the integration, not available for CI/ONCE installs). Defaults to false, which completes immediately after the prompt is sent
configobjectPassed through to the sessions API for new sessions. Supports vault IDs, resources, metadata, and other session creation parameters

Execution identityโ€‹

Set executionIdentity on the node config (next to integrationInvocationType), not under integrationActionExecutionProperties. trigger_agent allows actor and service_account only.

ShapeWhen to use
{ "type": "actor" }Self-service runs. Resolves to the user who triggered the workflow. Not valid on automation/timer triggers.
{ "type": "service_account", "identifier": "<sa>@serviceaccounts.getport.io" }Automations and headless runs. Identifier must be a literal (no JQ). Admins only when authoring the workflow.

Managed credentials require both enablePortMcp: true (default) on this action and executionIdentity on the node config. At claim time, workflow-service mints a short-lived Port access token for that identity and returns it to the integration (encrypted as portAccessToken on the run). The integration decrypts it and writes a Claude vault static_bearer credential for the Port MCP URL.

With enablePortMcp: true but no executionIdentity, the integration can still add Port MCP to the agent configuration, but it does not attach a managed vault credential (other vaults in config.vault_ids still apply).

Node outputs

Once the prompt has been sent, the node exposes:

OutputTypeDescription
sessionIdstringThe session the prompt was sent to, whether newly created or continued. Pass it to a later trigger_agent node to send a follow-up message
userMessageEventIdstringThe ID of the session event created for the prompt
urlstringLink to the session in the Claude Console

When reportSessionStatus is true and the session reaches a terminal state, the node also exposes cumulative usage from the session retrieve:

OutputTypeDescription
inputTokensnumberTotal input tokens used by the session (omitted if unavailable)
outputTokensnumberTotal output tokens used by the session (omitted if unavailable)
costUsdnumberEstimated public list cost in USD, converted from Anthropic usage.list_cost (cents). Omitted if unavailable

With reportSessionStatus: false, the node completes as soon as the prompt is sent. url is still set; token and cost fields are not, because the session has not finished yet.

Live session streaming

When reportSessionStatus: true, the integration streams agent messages to the Port workflow run logs as the session executes. Each agent reply appears as a log entry within a few seconds of being produced, so you can follow long-running sessions without waiting for them to finish.

Session errors (for example, billing errors or retries exhausted) are always posted as error-level log entries at session end, regardless of whether streaming was active.

Streaming requires live webhooks

Live streaming is available on Hosted by Port and self-hosted realtime installs. CI (ONCE event listener) installs do not receive live events, so log entries are not posted.

Pod restart behaviour: if the integration pod restarts while a session is running, agent messages produced during the downtime window are not backfilled. Session error events are unaffected. This is a known limitation and will be addressed in a future release.

Basic examples

Self-service trigger with Port MCP (click to expand)

Trigger an agent from a self-service form. Uses actor so Port MCP calls run as the user who triggered the workflow:

{
"identifier": "trigger-agent",
"title": "Trigger Claude Agent",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<your-installation-id>",
"integrationProvider": "claude-managed-agents",
"integrationInvocationType": "trigger_agent",
"executionIdentity": { "type": "actor" },
"integrationActionExecutionProperties": {
"agentId": "{{ .outputs.trigger.agentId.identifier }}",
"environmentId": "{{ .outputs.trigger.environmentId.identifier }}",
"prompt": "{{ .outputs.trigger.prompt }}",
"reportSessionStatus": true
}
}
}
Automation trigger with Port MCP (click to expand)

Trigger an agent from an automation or timer. Uses a service account identity (actor is not allowed without a self-service trigger):

{
"identifier": "trigger-agent-automation",
"title": "Trigger Claude Agent",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<your-installation-id>",
"integrationProvider": "claude-managed-agents",
"integrationInvocationType": "trigger_agent",
"executionIdentity": {
"type": "service_account",
"identifier": "claude-mcp-agent@serviceaccounts.getport.io"
},
"integrationActionExecutionProperties": {
"agentId": "<agent-id>",
"environmentId": "<environment-id>",
"prompt": "{{ .event.payload.prompt }}",
"enablePortMcp": true
}
}
}

Create agentโ€‹

Creates a new Claude managed agent and registers it in Port's catalog.

Execution properties

FieldTypeDescription
namestringRequired. The agent name
modelstringRequired. The Claude model ID (e.g., claude-sonnet-4-5)
systemPromptstringThe agent's system prompt
enablePortMcpbooleanWhen true (default), attaches the region-matched Port MCP server and an always-allow port MCP toolset to the agent. Set to false to skip. See Managed Port MCP
configobjectAdditional agent configuration passed directly to the Anthropic agents API (e.g., tools, mcp_servers, multiagent, skills)

Basic example

Create a Claude agent (click to expand)

Create a minimal agent with a name, model, and system prompt:

{
"identifier": "create-agent",
"title": "Create Claude Agent",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<your-installation-id>",
"integrationProvider": "claude-managed-agents",
"integrationInvocationType": "create_agent",
"integrationActionExecutionProperties": {
"name": "{{ .outputs.trigger.name }}",
"model": "{{ .outputs.trigger.model }}",
"systemPrompt": "{{ .outputs.trigger.systemPrompt }}"
}
}
}

Prefer the action's enablePortMcp property over hand-building mcp_servers in config. The complete workflow example still shows form toggles for Port MCP and GitHub MCP; for Port MCP, the managed path in MCP server setup is recommended.

Update agentโ€‹

Updates an existing Claude managed agent. The node retrieves the agent's current version for optimistic concurrency, applies the requested changes, and completes synchronously. Concurrent updates to the same agent are serialized automatically.

At least one field required

At least one of name, model, systemPrompt, config, or enablePortMcp must be provided alongside agentId. A node with only agentId will fail at runtime.

Execution properties

FieldTypeDescription
agentIdstringRequired. The ID of the Claude agent to update
namestringNew agent name
modelstringNew Claude model ID (e.g., claude-sonnet-4-5)
systemPromptstringNew system prompt
enablePortMcpbooleanWhen true, attaches (or refreshes) the managed Port MCP server and toolset. Omit to leave the agent's MCP config unchanged. See Managed Port MCP
configobjectAdditional agent configuration to update (e.g. tools, mcp_servers, multiagent, skills)

Basic example

Update a Claude agent (click to expand)

Update an agent's model and system prompt:

{
"identifier": "update-agent",
"title": "Update Claude Agent",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<your-installation-id>",
"integrationProvider": "claude-managed-agents",
"integrationInvocationType": "update_agent",
"integrationActionExecutionProperties": {
"agentId": "{{ .outputs.trigger.agentId.identifier }}",
"model": "{{ .outputs.trigger.model }}",
"systemPrompt": "{{ .outputs.trigger.systemPrompt }}"
}
}
}

Archive agentโ€‹

Archives a Claude managed agent, making it permanently read-only. This is the terminal lifecycle operation on the Anthropic API โ€” no hard delete exists. Existing sessions are unaffected; new sessions can no longer reference the agent once it is archived. The workflow node completes synchronously once the Anthropic API confirms the archive.

Irreversible

Archiving an agent is permanent. The agent cannot be restored after archiving.

Execution propertiesโ€‹

FieldTypeDescription
agentIdstringRequired. The ID of the Claude agent to archive

Basic example

Archive a Claude agent (click to expand)

Archive an agent by ID:

{
"identifier": "archive-agent",
"title": "Archive Claude Agent",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<your-installation-id>",
"integrationProvider": "claude-managed-agents",
"integrationInvocationType": "archive_agent",
"integrationActionExecutionProperties": {
"agentId": "{{ .outputs.trigger.agentId.identifier }}"
}
}
}

Create environmentโ€‹

Creates a new Claude managed environment and registers it in Port's catalog. Environments are the runtime context for agent sessions โ€” they define the infrastructure (cloud or self-hosted container) an agent runs within. The node completes synchronously once the Anthropic API confirms creation, and the new environment entity is immediately available in your catalog without waiting for the next scheduled resync.

Execution properties

FieldTypeDescription
namestringRequired. The environment name (1โ€“256 characters)
descriptionstringOptional human-readable description
scopestringOptional scope identifier (e.g. an organisation ID) that determines which users can access the environment
configobjectAdditional environment configuration passed directly to the Anthropic environments API (e.g. container settings, tools)

Node outputs

OutputTypeDescription
environmentIdstringThe ID of the newly created environment. Pass it to a trigger_agent node to start a session in this environment

Basic example

Create a Claude environment (click to expand)

Create an environment with a name and optional description:

{
"identifier": "create-environment",
"title": "Create Claude Environment",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<your-installation-id>",
"integrationProvider": "claude-managed-agents",
"integrationInvocationType": "create_environment",
"integrationActionExecutionProperties": {
"name": "{{ .outputs.trigger.name }}",
"description": "{{ .outputs.trigger.description }}"
}
}
}

Update environmentโ€‹

Updates an existing Claude managed environment's configuration and reflects the result in Port's catalog synchronously. Concurrent updates to the same environment are serialized automatically.

At least one field required

At least one of name, description, scope, or config must be provided alongside environmentId. A node with only environmentId will fail at runtime.

Execution properties

FieldTypeDescription
environmentIdstringRequired. The ID of the Claude environment to update
namestringNew environment name
descriptionstringNew environment description
scopestringNew scope identifier (e.g. an organisation ID)
configobjectAdditional environment configuration to update (e.g. container settings)

Basic example

Update a Claude environment (click to expand)

Update an environment's name and description:

{
"identifier": "update-environment",
"title": "Update Claude Environment",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<your-installation-id>",
"integrationProvider": "claude-managed-agents",
"integrationInvocationType": "update_environment",
"integrationActionExecutionProperties": {
"environmentId": "{{ .outputs.trigger.environmentId.identifier }}",
"name": "{{ .outputs.trigger.name }}",
"description": "{{ .outputs.trigger.description }}"
}
}
}

Delete environmentโ€‹

Permanently hard-deletes a Claude managed environment and removes the corresponding entity from Port's catalog in the same run. Unlike agents (which can only be archived), environments support full deletion. Sessions that were already created against this environment are not affected. Concurrent delete attempts on the same environment are serialized automatically.

Irreversible

Deleting an environment is permanent. It cannot be restored after deletion.

Execution properties

FieldTypeDescription
environmentIdstringRequired. The ID of the Claude environment to delete

Basic example

Delete a Claude environment (click to expand)

Delete an environment by ID:

{
"identifier": "delete-environment",
"title": "Delete Claude Environment",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<your-installation-id>",
"integrationProvider": "claude-managed-agents",
"integrationInvocationType": "delete_environment",
"integrationActionExecutionProperties": {
"environmentId": "{{ .outputs.trigger.environmentId.identifier }}"
}
}
}

MCP server setupโ€‹

Claude managed agents can call Port MCP (catalog and platform tools) and optionally GitHub MCP (for example create_pull_request). Port MCP is wired by the integration when enablePortMcp is on; GitHub MCP still needs a vault you create and attach yourself.

Port MCP uses the URL for your Port region (https://mcp.port.io/v1 or https://mcp.us.port.io/v1) as an MCP server named port, plus an mcp_toolset with an always-allow permission policy.

Agent configuration - any of:

  • create agent with enablePortMcp: true (default),
  • update agent with enablePortMcp: true (omit on update to leave MCP unchanged),
  • trigger agent with enablePortMcp: true (default), which updates the agent in Anthropic if Port MCP is not already configured.

Session credentials - on trigger agent when enablePortMcp is true:

  1. Set executionIdentity on the node config to { "type": "actor" } (self-service) or { "type": "service_account", "identifier": "..." } (automations). See Execution identity. This is separate from enableIdentityPropagation used by GitHub/GitLab Ocean actions.
  2. Workflow-service mints a short-lived access token for that identity at claim time and returns it on the run as portAccessToken (no browser OAuth, no personal client secret).
  3. The integration creates or updates a Claude vault credential (static_bearer, matched by Port MCP URL) and attaches the vault to new sessions.
  4. On continue, the credential is reminted. If the session metadata records a Port identity owner, the continue run must use the same identity or the node fails. Sessions started without managed credentials do not record an owner and can be continued without executionIdentity.

Optional vaultName under integrationActionExecutionProperties sets the Claude vault display name. Use a stable, per-user value such as port-mcp-{{ .trigger.by.user.email }}, and send the same name when continuing. If omitted, the vault name defaults to the resolved execution identity identifier. Sharing one vault name across users lets later runs overwrite the token.

You do not need to pass Port MCP vault IDs in config.vault_ids for this path, the integration appends the managed vault automatically. You can still attach additional vaults (for example GitHub MCP) via config.vault_ids.

Self-service vs automations

Choose the executionIdentity shape that matches how the workflow is triggered:

  • Self-service: "executionIdentity": { "type": "actor" }.
  • Automations: "executionIdentity": { "type": "service_account", "identifier": "<sa>@serviceaccounts.getport.io" }. Use a literal identifier (no JQ).

Manual Port MCP credentials (advanced)โ€‹

Use this only if you need a long-lived vault you manage yourself (for example OAuth with refresh), or if you are not using managed execution-identity auth. You create a Claude vault, put a Port token in it, and pass the vault ID in the Trigger Claude Agent form / config.vault_ids.

Managed vs manual tool policy

The managed path enables the full Port MCP toolset with an always-allow policy. The manual workflow examples below whitelist a small read-only subset of tools for tighter blast radius.

Two options are available for the vault credential - choose based on whose identity the agent should use:

Option A: Authorize as a Port user (OAuth 2.1 PKCE)โ€‹

This option authorizes the MCP connection as the Port user who runs the script. The resulting token is user-scoped - the agent sees and acts on behalf of that specific user. It is well suited for development or for dedicated agent users. For production workloads that need a service account identity, use Option B instead.

The Port MCP server uses OAuth 2.1 Authorization Code with PKCE. The script below handles the full flow and creates the vault and credential in Claude automatically (Python 3 standard library only - no extra dependencies). Claude stores the refresh token and renews the access token automatically, so no rotation is needed.

Before running: set your Anthropic API key as an environment variable:

export ANTHROPIC_API_KEY="<your-anthropic-api-key>"
Token and vault setup script (click to expand)
#!/usr/bin/env python3
"""Port MCP OAuth token generator (DCR + PKCE, stdlib only).
Creates the Claude vault and mcp_oauth credential automatically.
Requires: ANTHROPIC_API_KEY environment variable."""
import base64
import datetime
import hashlib
import json
import os
import secrets
import urllib.parse
import urllib.request
import webbrowser
from http.server import BaseHTTPRequestHandler, HTTPServer

# EU region: MCP_URL = "https://mcp.port.io/v1"
# US region: MCP_URL = "https://mcp.us.port.io/v1"
MCP_URL = "https://mcp.port.io/v1"
CALLBACK_PORT = 9000 # change if the port is already in use
CLIENT_NAME = "port-mcp" # label shown in the Port authorization screen
VAULT_DISPLAY_NAME = "Port MCP" # change to identify the agent or user this vault is for
SCOPE = "openid profile email offline_access"

REDIRECT_URI = f"http://localhost:{CALLBACK_PORT}/callback"
ANTHROPIC_API_KEY = os.environ["ANTHROPIC_API_KEY"]
ANTHROPIC_HEADERS = {
"x-api-key": ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01",
"anthropic-beta": "managed-agents-2026-04-01",
"Content-Type": "application/json",
}

# 1. Dynamic Client Registration
reg_body = json.dumps({
"client_name": CLIENT_NAME,
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none",
"redirect_uris": [REDIRECT_URI],
"scope": SCOPE,
}).encode()
with urllib.request.urlopen(
urllib.request.Request(
f"{MCP_URL}/register",
data=reg_body,
headers={"Content-Type": "application/json"},
method="POST",
)
) as r:
client = json.load(r)
client_id = client["client_id"]
print(f"Registered client: {client_id}")

# 2. PKCE - the code_verifier proves possession at token time; no client_secret
# is needed because the server is configured for public clients
# (token_endpoint_auth_method: none).
code_verifier = secrets.token_urlsafe(48)
code_challenge = (
base64.urlsafe_b64encode(hashlib.sha256(code_verifier.encode()).digest())
.rstrip(b"=")
.decode()
)

# 3. Loopback callback server
captured: dict[str, str | None] = {"code": None}

class _Handler(BaseHTTPRequestHandler):
def do_GET(self) -> None:
captured["code"] = urllib.parse.parse_qs(
urllib.parse.urlparse(self.path).query
).get("code", [None])[0]
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.end_headers()
self.wfile.write(b"<h2>Authorization complete - return to your terminal.</h2>")
def log_message(self, *_: object) -> None:
pass

server = HTTPServer(("localhost", CALLBACK_PORT), _Handler)

auth_url = (
f"{MCP_URL}/authorize"
f"?response_type=code"
f"&client_id={urllib.parse.quote(client_id)}"
f"&redirect_uri={urllib.parse.quote(REDIRECT_URI)}"
f"&scope={urllib.parse.quote(SCOPE)}"
f"&code_challenge={code_challenge}"
f"&code_challenge_method=S256"
)
print("\nOpening browser for authorization...")
webbrowser.open(auth_url)
print(f"If the browser did not open, visit:\n {auth_url}\n")
server.handle_request()

code = captured["code"]
if not code:
raise SystemExit("ERROR: no authorization code received.")

# 4. Token exchange
token_body = urllib.parse.urlencode({
"grant_type": "authorization_code",
"code": code,
"client_id": client_id,
"redirect_uri": REDIRECT_URI,
"code_verifier": code_verifier,
}).encode()
with urllib.request.urlopen(
urllib.request.Request(
f"{MCP_URL}/token",
data=token_body,
headers={"Content-Type": "application/x-www-form-urlencoded"},
method="POST",
)
) as r:
tokens = json.load(r)

expires_in = tokens.get("expires_in", 3600)
expires_at = (
datetime.datetime.now(datetime.timezone.utc)
+ datetime.timedelta(seconds=expires_in)
).isoformat()

# 5. Create Claude vault
vault_body = json.dumps({"display_name": VAULT_DISPLAY_NAME}).encode()
with urllib.request.urlopen(
urllib.request.Request(
"https://api.anthropic.com/v1/vaults",
data=vault_body,
headers=ANTHROPIC_HEADERS,
method="POST",
)
) as r:
vault = json.load(r)
vault_id = vault["id"]
print(f"\nCreated vault: {vault_id} ({VAULT_DISPLAY_NAME})")

# 6. Create mcp_oauth credential with auto-refresh block
cred_body = json.dumps({
"display_name": "Port MCP credential",
"auth": {
"type": "mcp_oauth",
"mcp_server_url": MCP_URL,
"access_token": tokens["access_token"],
"expires_at": expires_at,
"refresh": {
"token_endpoint": f"{MCP_URL}/token",
"client_id": client_id,
"refresh_token": tokens["refresh_token"],
"token_endpoint_auth": {"type": "none"},
},
},
}).encode()
with urllib.request.urlopen(
urllib.request.Request(
f"https://api.anthropic.com/v1/vaults/{vault_id}/credentials",
data=cred_body,
headers=ANTHROPIC_HEADERS,
method="POST",
)
) as r:
cred = json.load(r)

print(f"Created mcp_oauth credential: {cred['id']}")
print(f"\n=== Vault ID (pass this in vault_ids when triggering sessions) ===")
print(vault_id)

After the script completes, copy the vault ID printed at the end and pass it in the Vaults field of the Trigger Claude Agent form.

Option B: Use a Port service account tokenโ€‹

This option uses a dedicated Port service account (a non-human bot user). The MCP connection acts under the service account's identity. This token is a static bearer - it does not support automatic refresh. It expires after a short period (20 minutes minimum), so you must rotate it on a schedule.

Create service account, generate token, and create vault credential (click to expand)

Step 1 - Create a Port service account:

Service account identifiers must use the @serviceaccounts.getport.io domain. The clientId and clientSecret are returned once in additionalData.credentials - store them securely.

curl -s -X POST 'https://api.port.io/v1/blueprints/_user/entities' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <PORT_ACCESS_TOKEN>' \
-d '{
"identifier": "claude-mcp-agent@serviceaccounts.getport.io",
"title": "Claude MCP Agent",
"blueprint": "_user",
"properties": {
"port_type": "Service Account",
"port_role": "Member",
"status": "Active"
},
"relations": {}
}'

Copy additionalData.credentials.clientId and additionalData.credentials.clientSecret from the response.

Step 2 - Generate an access token for the service account:

# EU region
curl -s -X POST \
-H 'Content-Type: application/json' \
-d '{"clientId": "<SA_CLIENT_ID>", "clientSecret": "<SA_CLIENT_SECRET>"}' \
'https://api.port.io/v1/auth/access_token' | jq -r .accessToken

# US region
# curl -s -X POST \
# -H 'Content-Type: application/json' \
# -d '{"clientId": "<SA_CLIENT_ID>", "clientSecret": "<SA_CLIENT_SECRET>"}' \
# 'https://api.us.port.io/v1/auth/access_token' | jq -r .accessToken

Step 3 - Create the Claude vault and credential:

# 1. Create a vault
curl -s https://api.anthropic.com/v1/vaults \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "Content-Type: application/json" \
-d '{"display_name": "Port service account"}' | jq -r .id

# 2. Add a static_bearer credential (use EU or US MCP URL to match your region)
# EU: "mcp_server_url": "https://mcp.port.io/v1"
# US: "mcp_server_url": "https://mcp.us.port.io/v1"
curl -s https://api.anthropic.com/v1/vaults/<VAULT_ID>/credentials \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Port service account token",
"auth": {
"type": "static_bearer",
"mcp_server_url": "https://mcp.port.io/v1",
"token": "<PORT_ACCESS_TOKEN>"
}
}' | jq -r .id

Step 4 - Rotate the token:

Once the credential exists, update it in place with a fresh token by posting to its specific <CREDENTIAL_ID> (returned by Step 3) instead of the /credentials collection:

curl -s https://api.anthropic.com/v1/vaults/<VAULT_ID>/credentials/<CREDENTIAL_ID> \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Port service account token",
"auth": {
"type": "static_bearer",
"token": "<PORT_ACCESS_TOKEN>"
}
}'
Token rotation

Access tokens expire. Set up a cron job that repeats Steps 2 and 4 every 15 minutes to avoid session failures.


GitHub MCP (optional, for pull requests)โ€‹

The GitHub MCP server gives agents access to the GitHub API through MCP. The workflow template enables only create_pull_request by default - the minimum permission needed to open a PR after the agent has committed and pushed its changes.

Clone, commit, and push operations are handled separately by the github_repository session resource together with the githubAuthorizationToken configured on the integration. You do not need GitHub MCP for those operations.

Token permissionsโ€‹

Use a fine-grained personal access token with the minimum required scope:

ActionRequired scope
Create pull requestsrepo (classic) or Pull requests: read/write (fine-grained)
Clone and push

Clone, commit, and push are handled by the github_repository session resource using the githubAuthorizationToken set on the integration - not the vault credential. Configure that token with Contents: read/write scope separately.

Setupโ€‹

To enable GitHub MCP on an agent, toggle Enable GitHub MCP in the Create Agent workflow. Create the vault credential using the Claude API:

Create vault and credential (click to expand)
# 1. Create a vault (skip if reusing an existing one)
curl -s https://api.anthropic.com/v1/vaults \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "Content-Type: application/json" \
-d '{"display_name": "GitHub MCP"}' | jq -r .id

# 2. Add a static_bearer credential
curl -s https://api.anthropic.com/v1/vaults/<VAULT_ID>/credentials \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "Content-Type: application/json" \
-d '{
"display_name": "GitHub PAT",
"auth": {
"type": "static_bearer",
"mcp_server_url": "https://api.githubcopilot.com/mcp/",
"token": "<GITHUB_PERSONAL_ACCESS_TOKEN>"
}
}'

Pass the vault ID in the Vaults field of the Trigger Claude Agent form to attach it to a session.


Complete workflow examplesโ€‹

Placeholders

Replace all <...> placeholders (e.g. <your-installation-id>) with your actual values before using these workflows.

For a full, opinionated end-to-end example that builds on both node types below (a create-agent workflow and a trigger-agent workflow), including per-service agent routing and a system prompt, see the guide Assign PR reviewers with a Claude managed agent.

Trigger Claude Agent - full workflow JSON (click to expand)
{
"title": "Trigger Claude Agent",
"icon": "Claude",
"description": "Trigger a Claude managed agent by starting a new session or continuing an idle session with a follow-up prompt.",
"category": "Claude Agents",
"allowAnyoneToViewRuns": true,
"nodes": [
{
"identifier": "trigger",
"title": "Trigger Claude Agent",
"icon": "Claude",
"config": {
"type": "SELF_SERVE_TRIGGER",
"contexts": [
{
"on": "ENTITY",
"userInput": "sessionId"
}
],
"userInputs": {
"properties": {
"sessionMode": {
"title": "Session mode",
"description": "Start a new session, or continue an existing idle session with another prompt.",
"type": "string",
"enum": ["new", "continue"],
"default": {
"jqQuery": "if ((.form.sessionId // \"\") | length) > 0 then \"continue\" else \"new\" end"
}
},
"agentId": {
"title": "Agent",
"type": "string",
"format": "entity",
"blueprint": "claude_agent"
},
"environmentId": {
"title": "Environment",
"description": "Required when starting a new session.",
"type": "string",
"format": "entity",
"blueprint": "claude_environment",
"disabled": { "jqQuery": ".form.sessionMode != \"new\"" }
},
"sessionId": {
"title": "Session",
"description": "Select an idle session to continue.",
"type": "string",
"format": "entity",
"blueprint": "claude_session",
"dataset": {
"combinator": "and",
"rules": [
{ "property": "status", "operator": "=", "value": "idle" },
{
"relation": "agent",
"operator": "=",
"value": { "jqQuery": ".form.agentId.identifier" }
}
]
},
"disabled": { "jqQuery": ".form.sessionMode != \"continue\"" }
},
"title": {
"title": "Session title",
"type": "string",
"disabled": { "jqQuery": ".form.sessionMode != \"new\"" }
},
"metadata": {
"title": "Metadata",
"description": "Optional key-value metadata for a new session.",
"type": "object",
"additionalProperties": true,
"disabled": { "jqQuery": ".form.sessionMode != \"new\"" }
},
"vaultIds": {
"title": "Vaults",
"description": "Extra vaults to attach (for example GitHub MCP). Managed Port MCP vaults are attached automatically when the agent has Port MCP and the run has an execution identity.",
"type": "array",
"items": {
"type": "string",
"format": "entity",
"blueprint": "claude_vault"
},
"disabled": { "jqQuery": ".form.sessionMode != \"new\"" }
},
"memoryStoreIds": {
"title": "Memory stores",
"type": "array",
"items": {
"type": "string",
"format": "entity",
"blueprint": "claude_memory_store"
},
"disabled": { "jqQuery": ".form.sessionMode != \"new\"" }
},
"githubRepoUrls": {
"title": "GitHub repositories",
"description": "Optional GitHub repository URLs to mount. Requires githubAuthorizationToken on the integration.",
"type": "array",
"items": {
"type": "string",
"format": "url"
},
"disabled": { "jqQuery": ".form.sessionMode != \"new\"" }
},
"enablePortMcp": {
"title": "Enable Port MCP",
"description": "Connect the agent to Port MCP and attach a managed vault credential when this workflow node has executionIdentity.",
"type": "boolean",
"default": true
},
"vaultName": {
"title": "Port MCP vault name",
"description": "Optional Claude vault display name for the managed credential. Use the same value when continuing a session.",
"type": "string",
"default": {
"jqQuery": "\"port-mcp-\" + .user.email"
}
},
"prompt": {
"title": "Prompt",
"type": "string",
"format": "multi-line"
}
},
"required": {
"jqQuery": "if .form.sessionMode == \"new\" then [\"sessionMode\", \"agentId\", \"environmentId\", \"prompt\"] elif .form.sessionMode == \"continue\" then [\"sessionMode\", \"agentId\", \"sessionId\", \"prompt\"] else [\"sessionMode\", \"agentId\", \"prompt\"] end"
},
"order": [
"sessionMode",
"agentId",
"environmentId",
"sessionId",
"title",
"metadata",
"vaultIds",
"memoryStoreIds",
"githubRepoUrls",
"enablePortMcp",
"vaultName",
"prompt"
],
"validations": []
},
"actionCardButtonText": "Trigger",
"executeActionButtonText": "Trigger",
"published": true,
"permissions": { "roles": ["Member", "Admin"] }
},
"variables": {},
"links": [],
"verbose": false
},
{
"identifier": "trigger_agent",
"title": "Trigger Claude Agent",
"icon": "Claude",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<your-installation-id>",
"integrationProvider": "claude-managed-agents",
"integrationInvocationType": "trigger_agent",
"executionIdentity": { "type": "actor" },
"integrationActionExecutionProperties": {
"agentId": "{{ .outputs.trigger.agentId }}",
"environmentId": "{{ .outputs.trigger.environmentId }}",
"sessionId": "{{ .outputs.trigger.sessionId }}",
"prompt": "{{ .outputs.trigger.prompt }}",
"enablePortMcp": "{{ .outputs.trigger.enablePortMcp }}",
"vaultName": "{{ .outputs.trigger.vaultName }}",
"reportSessionStatus": true,
"config": {
"title": "{{ .outputs.trigger.title }}",
"metadata": "{{ .outputs.trigger.metadata }}",
"vault_ids": "{{ .outputs.trigger.vaultIds }}",
"resources": "{{ ([(.outputs.trigger.githubRepoUrls // [])[] | {type: \"github_repository\", url: .}] + [(.outputs.trigger.memoryStoreIds // [])[] | {type: \"memory_store\", memory_store_id: .}]) }}"
}
},
"onFailure": "terminate"
},
"variables": {},
"links": [],
"verbose": false
}
],
"connections": [
{
"description": null,
"sourceIdentifier": "trigger",
"targetIdentifier": "trigger_agent"
}
]
}
Create Claude Agent - full workflow JSON (click to expand)
US region

If your Port account is on the US region, change the Port MCP URL to https://mcp.us.port.io/v1.

{
"title": "Create Claude Agent",
"icon": "Claude",
"description": "Create a new Claude managed agent with optional Port MCP, GitHub MCP, multiagent, and skills configuration.",
"category": "Claude Agents",
"allowAnyoneToViewRuns": true,
"nodes": [
{
"identifier": "trigger",
"title": "Create Claude Agent",
"icon": "Claude",
"config": {
"type": "SELF_SERVE_TRIGGER",
"userInputs": {
"properties": {
"name": {
"title": "Agent name",
"type": "string"
},
"model": {
"title": "Model",
"type": "string",
"enum": [
"claude-opus-4-5",
"claude-sonnet-4-5",
"claude-haiku-4-5"
],
"default": "claude-sonnet-4-5"
},
"systemPrompt": {
"title": "System prompt",
"type": "string",
"format": "multi-line"
},
"description": {
"title": "Description",
"type": "string"
},
"enableAgentToolset": {
"title": "Enable agent toolset",
"description": "Gives the agent access to the default Claude toolset (web search, code execution, etc.).",
"type": "boolean",
"default": true
},
"enablePortMcp": {
"title": "Enable Port MCP",
"description": "Connects the agent to Port MCP for your region. trigger_agent attaches a managed credential for the run's Actor or Service account identityโ€”no manual Port vault setup.",
"type": "boolean",
"default": true
},
"enableGithubMcp": {
"title": "Enable GitHub MCP",
"description": "Gives the agent the ability to open pull requests via the GitHub MCP server. Git operations (clone, push) are handled by the github_repository session resource and do not require this. A vault with a GitHub personal access token is required.",
"type": "boolean",
"default": false
},
"multiagentMode": {
"title": "Multiagent mode",
"description": "Allow this agent to delegate to other agents.",
"type": "boolean",
"default": false
},
"delegateAgentIds": {
"title": "Delegate agents",
"description": "Agents this agent can delegate tasks to. Only agents without their own subagents are shown (Claude enforces a maximum delegation depth of 1).",
"type": "array",
"items": {
"type": "string",
"format": "entity",
"blueprint": "claude_agent",
"dataset": {
"combinator": "and",
"rules": [
{
"relation": "delegateAgents",
"operator": "isEmpty"
}
]
}
},
"disabled": { "jqQuery": ".form.multiagentMode != true" }
},
"allowSelfDelegation": {
"title": "Allow self-delegation",
"type": "boolean",
"default": false,
"disabled": { "jqQuery": ".form.multiagentMode != true" }
},
"claudeSkillIds": {
"title": "Skills",
"description": "Claude skills to attach to the agent.",
"type": "array",
"items": {
"type": "string",
"format": "entity",
"blueprint": "claude_skill"
}
}
},
"required": ["name", "model"],
"order": [
"name",
"model",
"systemPrompt",
"description",
"enableAgentToolset",
"enablePortMcp",
"enableGithubMcp",
"multiagentMode",
"delegateAgentIds",
"allowSelfDelegation",
"claudeSkillIds"
]
},
"actionCardButtonText": "Create",
"executeActionButtonText": "Create",
"published": true,
"permissions": { "roles": ["Admin"] }
},
"variables": {},
"links": [],
"verbose": false
},
{
"identifier": "create_agent",
"title": "Create Claude Agent",
"icon": "Claude",
"config": {
"type": "INTEGRATION_ACTION",
"installationId": "<your-installation-id>",
"integrationProvider": "claude-managed-agents",
"integrationInvocationType": "create_agent",
"integrationActionExecutionProperties": {
"name": "{{ .outputs.trigger.name }}",
"model": "{{ .outputs.trigger.model }}",
"systemPrompt": "{{ .outputs.trigger.systemPrompt }}",
"enablePortMcp": "{{ .outputs.trigger.enablePortMcp }}",
"config": {
"description": "{{ .outputs.trigger.description }}",
"mcp_servers": "{{ if .outputs.trigger.enableGithubMcp then [{\"type\": \"url\", \"name\": \"github\", \"url\": \"https://api.githubcopilot.com/mcp/\"}] else [] end }}",
"tools": "{{ (if .outputs.trigger.enableAgentToolset then [{\"type\": \"agent_toolset_20260401\"}] else [] end) + (if .outputs.trigger.enableGithubMcp then [{\"type\": \"mcp_toolset\", \"mcp_server_name\": \"github\", \"default_config\": {\"enabled\": false}, \"configs\": [{\"name\": \"create_pull_request\", \"enabled\": true, \"permission_policy\": {\"type\": \"always_allow\"} }]}] else [] end) }}",
"multiagent": "{{ if .outputs.trigger.multiagentMode then {\"type\": \"coordinator\", \"agents\": ([(.outputs.trigger.delegateAgentIds // [])[] | {\"type\": \"agent\", \"id\": .}] + (if (.outputs.trigger.allowSelfDelegation // false) then [{\"type\": \"self\"}] else [] end))} else null end }}",
"skills": "{{ [(.outputs.trigger.claudeSkillIds // [])[] | {\"type\": \"custom\", \"skill_id\": .}] }}"
}
},
"onFailure": "terminate"
},
"variables": {},
"links": [],
"verbose": false
}
],
"connections": [
{
"description": null,
"sourceIdentifier": "trigger",
"targetIdentifier": "create_agent"
}
]
}
Tool configuration

Port MCP (via enablePortMcp): the integration attaches an always-allow toolset for the port MCP server. See the Port MCP tools reference for available tools.

GitHub MCP (optional in this example): the workflow uses "default_config": {"enabled": false} and enables only create_pull_request. Git operations (clone, commit, push) use the github_repository session resource, not GitHub MCP. See the GitHub MCP tools reference.

Limitationsโ€‹

  • Managed vault credentials need executionIdentity and enablePortMcp. Without actor or service_account on the trigger_agent node config, vault credentials are not attached (enablePortMcp may still wire the agent). actor is self-service only; automations need a service account.
  • Continue identity checks are owner-scoped. Only sessions that recorded a Port identity owner require the same identity on continue.
  • Shared vaultName overwrites tokens. If multiple users share one vault display name, later runs rotate the same credential.
  • executionIdentity is a literal. Do not use JQ templates in executionIdentity (including the service account identifier).
  • Port MCP URL must be configured on the integration (port.mcp_url from your Port region). If it is missing, Port MCP actions fail at runtime.