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

Check out Port for yourself ➜ 

Machine authentication for the Port MCP server

When you connect the Port MCP server from CI/CD pipelines, GitHub Actions, or other automated environments, interactive OAuth (browser login) is not practical. Port supports machine-to-machine authentication using your existing Port client ID and client secret through the MCP OAuth token endpoint.

Authentication methods compared

MethodBest forLong-lived secretUser consent
Interactive OAuth (authorization_code + refresh)Cursor, VS Code, Claude DesktopRefresh token (up to ~30 days)Yes
OAuth client_credentials (MCP token endpoint)CI/CD, headless agents, mcp-remotePort client ID + secretNo
Long-lived vs short-lived credentials

Your client ID and client secret are long-lived. The access token they produce is short-lived (typically about 3 hours). Re-exchange your credentials when the token expires. There is no refresh token for the client_credentials grant.

Prerequisites

  1. Create a Port client ID and client secret in your Port organization settings.
  2. Use a dedicated service account credential for automation, not a personal user's credentials.
  3. Store secrets in your CI secret manager. Never commit them to source control.

Request an access token

Port's MCP server exposes a standards-compliant OAuth 2.0 token endpoint that accepts the client_credentials grant. It returns a machine JWT (isMachine: true) that you pass as a Bearer token when connecting to MCP.

Discover supported grants

curl -s "https://mcp.port.io/.well-known/oauth-authorization-server" | jq .

The response includes:

  • token_endpoint: https://mcp.port.io/v1/token.
  • grant_types_supported: authorization_code, refresh_token, client_credentials.
  • token_endpoint_auth_methods_supported: none, client_secret_post, client_secret_basic.
US region

For US-hosted Port, replace mcp.port.io with mcp.us.port.io.

Request a token (client_secret_post)

curl -s -X POST "https://mcp.port.io/v1/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"

Example response:

{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 10800
}

Request a token (client_secret_basic)

curl -s -X POST "https://mcp.port.io/v1/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-d "grant_type=client_credentials"

Connect with the token

Most automated setups use mcp-remote with a static Bearer header. Request a token from /v1/token, then pass it when connecting:

npx -y mcp-remote "https://mcp.port.io/v1" \
--header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--header "x-read-only-mode: 0"

If your MCP client supports OAuth machine-to-machine authentication natively, point it at the MCP server URL and let it handle token exchange against /v1/token using your Port client ID and secret.

Token lifecycle and rate limits

Access tokens expire (typically about 3 hours). Request a new token from /v1/token before expiry, or on 401 responses. Do not treat the access token itself as a long-lived secret.

The client_credentials grant is also rate-limited to 50 requests per 15 minutes per client. Cache tokens until they are close to expiry instead of requesting a new token on every MCP call.

Example: Claude Code in GitHub Actions

Here is a complete example showing how to connect Claude Code to the Port MCP server within a GitHub Actions workflow:

Show example workflow
.github/workflows/claude-code-mcp.yml
name: Port MCP Server Demo with Claude Code
on: workflow_dispatch

env:
PORT_MCP_URL: ${{ vars.PORT_MCP_URL }}

jobs:
demo:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Authenticate with Port
id: port-auth
run: |
response=$(curl -s -X POST "${{ env.PORT_MCP_URL }}/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=${{ secrets.PORT_CLIENT_ID }}" \
-d "client_secret=${{ secrets.PORT_CLIENT_SECRET }}")
token=$(echo "$response" | jq -r '.access_token')
echo "::add-mask::$token"
echo "access_token=$token" >> "$GITHUB_OUTPUT"

- name: Claude Code against Port MCP
uses: anthropics/claude-code-action@beta
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
mode: agent
mcp_config: |
{
"mcpServers": {
"port-prod": {
"command": "npx",
"args": [
"mcp-remote",
"${{ env.PORT_MCP_URL }}",
"--header",
"Authorization: Bearer ${{ steps.port-auth.outputs.access_token }}",
"--header",
"x-read-only-mode: 0"
]
}
}
}
allowed_tools: "mcp__port-prod__list_blueprints,mcp__port-prod__get_entities"
direct_prompt: |
List all blueprints, then show entities of the "zendesk_ticket" blueprint.

How this workflow works

  1. Authentication - The port-auth step exchanges your Port client credentials for a short-lived access token via POST /v1/token with grant_type=client_credentials.
  2. MCP connection - Claude Code connects to the remote MCP server using the mcp-remote package, passing the access token in the Authorization header.
  3. Read-only mode - The x-read-only-mode header defaults to 0, which allows all tools based on your permissions. You can change it to 1 to restrict the MCP server to only expose read-only tools, completely hiding write tools from the available tools list.
  4. Action filtering - The x-allowed-actions-to-run header (optional) allows you to control which actions are available through the run_action tool. It accepts a comma-separated list of action identifiers. For example, x-allowed-actions-to-run: "create_github_issue,create_incident" restricts access to only the specified actions.
  5. Tool access - Claude Code can invoke only the specific Port tools listed in allowed_tools, ensuring controlled access to your Port instance.
  6. Execution - The AI agent executes the provided prompt using the available Port tools to query your software catalog.
Customize your integration

For read-only workflows, you can change the x-read-only-mode header from 0 to 1 to restrict tools to read-only operations, or limit allowed_tools to just the query operations you need.

To control which actions are available, use the x-allowed-actions-to-run header with a comma-separated list of action identifiers. For example:

  • x-allowed-actions-to-run: "create_github_issue,merge_github_pr" - Only the specified GitHub actions.
  • x-allowed-actions-to-run: "create_incident,send_notification" - Only incident and notification actions.

Choose the appropriate MCP URL for your Port region (EU: https://mcp.port.io/v1, US: https://mcp.us.port.io/v1).

Adapting for other AI agents

While this example focuses on Claude Code, the same authentication pattern can be applied to other AI agents and platforms:

  1. Obtain Port credentials - Create a client ID and secret in your Port dashboard.
  2. Generate access token - Request a token from POST /v1/token with grant_type=client_credentials.
  3. Configure MCP connection - Point your AI agent to the remote MCP server with the token.
  4. Define tool permissions - Specify which Port tools the AI agent can access.
  5. Execute workflows - Let the AI agent interact with your Port data and capabilities.

When to use which method

  • Local IDE with browser login - Use interactive OAuth from the installation guide.
  • CI/CD or headless agents - Request a token from POST /v1/token with grant_type=client_credentials, then pass it as a Bearer token.
  • MCP clients with native OAuth M2M support - Configure the client to use the MCP server OAuth metadata so it handles token lifecycle automatically.

Port API compatibility

If you already obtain a machine token via the Port REST API (POST /v1/auth/access_token), you can use that same Bearer token with the MCP server. The MCP token endpoint returns the same JWT under the hood.

This is useful when a pipeline already authenticates against the Port API for other calls and you want to reuse the same token pattern. For new MCP integrations, prefer POST /v1/token so your auth flow stays within the MCP OAuth surface and supports discovery via .well-known/oauth-authorization-server.

Security best practices

  • Use a dedicated automation credential with the minimum permissions needed.
  • Rotate client secrets if they are exposed.
  • Mask tokens in CI logs (for example, ::add-mask:: in GitHub Actions).
  • Prefer x-read-only-mode: 1 for read-only automation.
  • Use x-allowed-actions-to-run to restrict which actions run_action can trigger.