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
executionIdentityon the workflow node (actorfor self-service, orservice_accountfor automations. Only admins can authorservice_account). - The integration derives the Port MCP URL from your Port API region (
OCEAN__PORT__MCP_URLor automatically fromOCEAN__PORT__BASE_URL).
- Set
Configurationโ
| Field | Type | Description |
|---|---|---|
type | string | Required. Must be "INTEGRATION_ACTION" |
installationId | string | Required. Your Claude Managed Agents integration installation ID |
integrationProvider | string | Required. Must be "claude-managed-agents" |
integrationInvocationType | enum | Required. One of the values listed in available actions |
integrationActionExecutionProperties | object | Required. Operation-specific configuration |
executionIdentity | object | Optional. 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:
| Action | Invocation type | Description |
|---|---|---|
| Trigger agent | trigger_agent | Start a new session or continue an existing one, and send a message to the agent |
| Create agent | create_agent | Create a new Claude managed agent and register it in Port's catalog |
| Update agent | update_agent | Update an existing Claude managed agent's name, model, system prompt, or config |
| Archive agent | archive_agent | Permanently archive a Claude managed agent |
| Create environment | create_environment | Create a new Claude managed environment and register it in Port's catalog |
| Update environment | update_environment | Update an existing Claude managed environment's name, description, scope, or config |
| Delete environment | delete_environment | Permanently 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
sessionIdand provideenvironmentId. 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, andenvironmentIdandconfigare 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.
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
| Field | Type | Description |
|---|---|---|
agentId | string | Required. The ID of the Claude agent to trigger |
environmentId | string | Required when starting a new session |
prompt | string | Required. The user message to send to the agent |
sessionId | string | An existing idle session to continue. When provided, environmentId and config are ignored |
enablePortMcp | boolean | When 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 |
vaultName | string | Optional 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 |
reportSessionStatus | boolean | When 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 |
config | object | Passed 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.
| Shape | When 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:
| Output | Type | Description |
|---|---|---|
sessionId | string | The 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 |
userMessageEventId | string | The ID of the session event created for the prompt |
url | string | Link 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:
| Output | Type | Description |
|---|---|---|
inputTokens | number | Total input tokens used by the session (omitted if unavailable) |
outputTokens | number | Total output tokens used by the session (omitted if unavailable) |
costUsd | number | Estimated 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.
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
| Field | Type | Description |
|---|---|---|
name | string | Required. The agent name |
model | string | Required. The Claude model ID (e.g., claude-sonnet-4-5) |
systemPrompt | string | The agent's system prompt |
enablePortMcp | boolean | When 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 |
config | object | Additional 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 of name, model, systemPrompt, config, or enablePortMcp must be provided alongside agentId. A node with only agentId will fail at runtime.
Execution properties
| Field | Type | Description |
|---|---|---|
agentId | string | Required. The ID of the Claude agent to update |
name | string | New agent name |
model | string | New Claude model ID (e.g., claude-sonnet-4-5) |
systemPrompt | string | New system prompt |
enablePortMcp | boolean | When true, attaches (or refreshes) the managed Port MCP server and toolset. Omit to leave the agent's MCP config unchanged. See Managed Port MCP |
config | object | Additional 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.
Archiving an agent is permanent. The agent cannot be restored after archiving.
Execution propertiesโ
| Field | Type | Description |
|---|---|---|
agentId | string | Required. 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
| Field | Type | Description |
|---|---|---|
name | string | Required. The environment name (1โ256 characters) |
description | string | Optional human-readable description |
scope | string | Optional scope identifier (e.g. an organisation ID) that determines which users can access the environment |
config | object | Additional environment configuration passed directly to the Anthropic environments API (e.g. container settings, tools) |
Node outputs
| Output | Type | Description |
|---|---|---|
environmentId | string | The 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 of name, description, scope, or config must be provided alongside environmentId. A node with only environmentId will fail at runtime.
Execution properties
| Field | Type | Description |
|---|---|---|
environmentId | string | Required. The ID of the Claude environment to update |
name | string | New environment name |
description | string | New environment description |
scope | string | New scope identifier (e.g. an organisation ID) |
config | object | Additional 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.
Deleting an environment is permanent. It cannot be restored after deletion.
Execution properties
| Field | Type | Description |
|---|---|---|
environmentId | string | Required. 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.
Managed Port MCP (recommended)โ
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:
- Set
executionIdentityon the nodeconfigto{ "type": "actor" }(self-service) or{ "type": "service_account", "identifier": "..." }(automations). See Execution identity. This is separate fromenableIdentityPropagationused by GitHub/GitLab Ocean actions. - 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). - The integration creates or updates a Claude vault credential (
static_bearer, matched by Port MCP URL) and attaches the vault to new sessions. - 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.
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.
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>"
}
}'
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:
| Action | Required scope |
|---|---|
| Create pull requests | repo (classic) or Pull requests: read/write (fine-grained) |
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โ
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)
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"
}
]
}
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
executionIdentityandenablePortMcp. Withoutactororservice_accounton thetrigger_agentnode config, vault credentials are not attached (enablePortMcpmay still wire the agent).actoris 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
vaultNameoverwrites tokens. If multiple users share one vault display name, later runs rotate the same credential. executionIdentityis a literal. Do not use JQ templates inexecutionIdentity(including the service account identifier).- Port MCP URL must be configured on the integration (
port.mcp_urlfrom your Port region). If it is missing, Port MCP actions fail at runtime.