From 24a10d64a626fd9778a02b1896d0618a14dab847 Mon Sep 17 00:00:00 2001 From: Stav Dlugovitzky Date: Mon, 18 Aug 2025 13:58:29 +0300 Subject: [PATCH 01/11] Adds explantion on using remote mcp. --- docs/ai-agents/port-mcp-server.md | 109 ++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/docs/ai-agents/port-mcp-server.md b/docs/ai-agents/port-mcp-server.md index ba90551537..aa122df687 100644 --- a/docs/ai-agents/port-mcp-server.md +++ b/docs/ai-agents/port-mcp-server.md @@ -575,6 +575,115 @@ Claude will ask for any required arguments before running the prompt, and the MC +## Connect to Port's MCP Server to Claude Code in Github Workflow + +### Automate Claude Code with GitHub Actions +Running Claude Code inside CI/CD differs from local usage because an interactive OAuth flow is not possible. Instead, the workflow must: + +1. **Generate a short-lived Port access token** using the Client-Credentials grant (Client ID + Client Secret). +2. **Connect to the remote MCP server** and pass the token to it so Claude Code can call the allowed tools. + +
+ Example Github workflow (Click to expand) + +```yaml title=".github/workflows/claude-code-mcp.yml" showLineNumbers +name: Port MCP Server Demo with Claude Code + +on: + workflow_dispatch: + inputs: + debug: + description: "Debug mode" + required: false + default: "false" + +env: + # Remote MCP endpoint for your region (eu/us) + PORT_MCP_URL: ${{ vars.PORT_MCP_URL }} + # Base URL for the OAuth token endpoint for your region (eu/us) – e.g. https://api.port.io + PORT_AUTH_BASE_URL: ${{ vars.PORT_AUTH_BASE_URL }} + +jobs: + demo: + runs-on: ubuntu-latest + + permissions: + id-token: write + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Authenticate with Port and get access token + id: port-auth + run: | + # Exchange client credentials for an access token + response=$(curl -s -X POST \ + "${{ env.PORT_AUTH_BASE_URL }}/auth/access_token" \ + -H "Content-Type: application/json" \ + -d '{ + "clientId": "${{ secrets.PORT_CLIENT_ID }}", + "clientSecret": "${{ secrets.PORT_CLIENT_SECRET }}" + }') + + access_token=$(echo "$response" | jq -r '.accessToken') + if [ -z "$access_token" ] || [ "$access_token" = "null" ]; then + echo "Failed to obtain access token" + echo "Response: $response" + exit 1 + fi + + # Mask the token in job logs + echo "::add-mask::$access_token" + echo "access_token=$access_token" >> "$GITHUB_OUTPUT" + + - name: Run Claude Code against Port MCP + uses: anthropics/claude-code-action@beta + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + mode: agent + # Define the MCP server and pass the token in the Authorization header + mcp_config: | + { + "mcpServers": { + "port-prod": { + "command": "npx", + "args": [ + "mcp-remote", + "${{ env.PORT_MCP_URL }}", + "--allow-http", + "--header", + "Authorization: Bearer ${{ steps.port-auth.outputs.access_token }}" + ] + } + } + } + # Restrict the tools the model can use (add or remove as needed) + allowed_tools: "mcp__port-prod__list_blueprints,mcp__port-prod__get_blueprint,mcp__port-prod__get_entities" + # Replace with whatever you need the agent to do + direct_prompt: | + You are connected to the Port MCP server. + List all blueprints and then show me the entities of the "zendesk_ticket" blueprint. +``` +
+ + + +#### How this workflow works + +1. **Get a token** – The `port-auth` step exchanges your `PORT_CLIENT_ID` and `PORT_CLIENT_SECRET` for a short-lived OAuth access token (no interactive login needed in CI). +2. **Start Claude with MCP** – `anthropics/claude-code-action` launches Claude Code and spins up a `mcp-remote` client that points to your Port MCP URL, forwarding the token via an `Authorization` header. +3. **Run the prompt** – Claude can call only the MCP tools you list in `allowed_tools`, using them to accomplish whatever task you set in `direct_prompt`. + +:::caution Keep your secrets safe +Store `PORT_CLIENT_ID`, `PORT_CLIENT_SECRET`, and `ANTHROPIC_API_KEY` as **encrypted GitHub Actions secrets**. Never commit them to the repository or print them to logs. +::: + +:::tip Customise the tool list +For read-only workflows you can shorten `allowed_tools` to just the query operations you need. This reduces payload size and risk of accidental changes. +::: + ## Troubleshooting If you encounter issues while setting up or using the Port MCP Server, expand the relevant section below: From 374c311a47460259b82e31581ad768ef8f1ff69a Mon Sep 17 00:00:00 2001 From: Stav Dlugovitzky Date: Tue, 19 Aug 2025 12:13:22 +0300 Subject: [PATCH 02/11] Split mcp server docs into folder. --- docs/ai-agents/port-mcp-server.md | 735 ------------------ .../ai-agents/port-mcp-server/_category_.json | 4 + .../port-mcp-server/available-tools.md | 91 +++ .../port-mcp-server/github-actions.md | 82 ++ .../overview-and-installation.md | 303 ++++++++ docs/ai-agents/port-mcp-server/prompts.md | 373 +++++++++ .../port-mcp-server/troubleshooting.md | 27 + static/img/ai-agents/PortPromptForm.png | Bin 63427 -> 59415 bytes 8 files changed, 880 insertions(+), 735 deletions(-) delete mode 100644 docs/ai-agents/port-mcp-server.md create mode 100644 docs/ai-agents/port-mcp-server/_category_.json create mode 100644 docs/ai-agents/port-mcp-server/available-tools.md create mode 100644 docs/ai-agents/port-mcp-server/github-actions.md create mode 100644 docs/ai-agents/port-mcp-server/overview-and-installation.md create mode 100644 docs/ai-agents/port-mcp-server/prompts.md create mode 100644 docs/ai-agents/port-mcp-server/troubleshooting.md diff --git a/docs/ai-agents/port-mcp-server.md b/docs/ai-agents/port-mcp-server.md deleted file mode 100644 index aa122df687..0000000000 --- a/docs/ai-agents/port-mcp-server.md +++ /dev/null @@ -1,735 +0,0 @@ ---- -sidebar_position: 4 -title: Port MCP Server ---- - -import Tabs from "@theme/Tabs" -import TabItem from "@theme/TabItem" - -# Port MCP server - -
-
- -
-
-
- -The Port Model Context Protocol (MCP) Server acts as a bridge, enabling Large Language Models (LLMs)—like those powering Claude, Cursor, or GitHub Copilot—to interact directly with your Port.io developer portal. This allows you to leverage natural language to query your software catalog, analyze service health, manage resources, and even streamline development workflows, all from your preferred interfaces. - -:::info AI Agents vs. MCP Server -The Port MCP Server is currently in open beta and provides significant standalone value, independent of our [AI Agents feature](/ai-agents/overview). Port AI Agents are currently in closed beta with limited access, while the MCP Server gives you immediate access to streamline building in Port, query your catalog, analyze service health, and streamline development workflows using natural language. - -While the MCP Server can interact with Port AI Agents when available, the core MCP functionality can be used freely without requiring access to the closed beta AI Agents feature. -::: - -## Why integrate LLMs with your developer portal? - -The primary advantage of the Port MCP Server is the ability to bring your developer portal's data and actions into the conversational interfaces you already use. This offers several benefits: - -* **Reduced Context Switching:** Access Port information and initiate actions without leaving your IDE or chat tool. -* **Increased Efficiency:** Get answers and perform tasks faster using natural language commands. -* **Improved Developer Experience:** Make your developer portal more accessible and intuitive to interact with. -* **Enhanced Data-Driven Decisions:** Easily pull specific data points from Port to inform your work in real-time. - -As one user put it: - -> "It would be interesting to build a use case where a developer could ask Copilot from his IDE about stuff Port knows about, without actually having to go to Port." - -The Port MCP Server directly enables these kinds of valuable, in-context interactions. - -## Key capabilities and use-cases - -
-
- -
-
- -The Port MCP Server enables you to interact with your Port data and capabilities directly through natural language within your chosen LLM-powered tools. Here's what you can achieve: - -### Find information quickly - -Effortlessly query your software catalog and get immediate answers. This eliminates the need to navigate through UIs or write complex API queries when you need information. - -* Ask: "Who is the owner of service X?" -* Ask: "How many services do we have in production?" -* Ask: "Show me all the microservices owned by the Backend team." -* Ask: "What are the dependencies of the 'OrderProcessing' service?" - -![Querying the service catalog from an IDE](/img/ai-agents/MCPCopilotAskServices.png) - -### Vibe-build in Port - -Leverage Claude's capabilities to manage and build your entire Port software catalog. You can create and configure blueprints, set up self-service actions, design scorecards, and more. - -* Ask: "Please help me apply this guide into my Port instance - [[guide URL]]" -* Ask: "I want to start managing my k8s deployments, how can we build it in Port?" -* Ask: "I want a new production readiness scorecard to track the code quality and service alerts" -* Ask: "Create a new self-service action in Port to scaffold a new service" - -![Claude building a self-service action](/img/ai-agents/MCPClaudeBuildSSA.png) - -### Analyze scorecards and quality - -Gain insights into service health, compliance, and quality by leveraging Port's scorecard data. Identify areas for improvement and track progress against your standards. - -* Ask: "Which services are failing our security requirements scorecard?" -* Ask: "What's preventing the 'InventoryService' from reaching Gold level in the 'Production Readiness' scorecard?" -* Ask: "Show me the bug count vs. test coverage for all Java microservices." - -![Asking about bug counts and test coverage correlation](/img/ai-agents/MCPClaudeInsightsBugs.png) - -* Ask: "Which of our services are missing critical monitoring dashboards?" - -![Identifying services lacking proper monitoring](/img/ai-agents/MCPClaudeMonitoringServices.png) - -### Streamline development and operations - -Receive assistance with common development and operational tasks, directly within your workflow. - -* Ask: "What do I need to do to set up a new 'ReportingService'?" -* Ask: "Guide me through creating a new component blueprint with 'name', 'description', and 'owner' properties." -* Ask: "Help me add a rule to the 'Tier1Services' scorecard that requires an on-call schedule to be defined." - -![Getting instructions for new service setup](/img/ai-agents/MCPClaudeServiceSetup.png) - -### Find your own use cases - -You can use Port's MCP to find the use cases that will be valuable to you. Try using this prompt: "think of creative prompts I can use to showcase the power of Port's MCP, based on the data available in Port" - - -## Using Port MCP - -### Setup - -Setting up Port's MCP is simple. Follow the instructions for your preferred tool, or learn about the archived local MCP server. - - - -To connect Cursor to Port's remote MCP, follow these steps: - -1. **Go to Cursor settings, click on Tools & Integrations, and add a new MCP server** - -![Go to Cursor Settings](/img/ai-agents/MCPInstallCursorStep1.png) - -2. **Add the above configuration** - -Use the appropriate configuration for your region: - - - -```json showLineNumbers -{ - "mcpServers": { - "port-eu": { - "url": "https://mcp.port.io/v1" - } - } -} -``` - - -```json showLineNumbers -{ - "mcpServers": { - "port-us": { - "url": "https://mcp.us.port.io/v1" - } - } -} -``` - - - -![Add configuration](/img/ai-agents/MCPInstallCursorStep2.png) - -3. **Login to Port** -Click on "Needs login", and complete the authentication flow in the window that opens up. -![Login to Port](/img/ai-agents/MCPInstallCursorStep3.png) - -4. **See the MCP tools** -After successfully connecting to Port, you'll see the list of available tools from the MCP. -![See tools](/img/ai-agents/MCPInstallCursorStep4.png) - -:::warning Authentication window behavior -In some cases, after clicking "Accept" in the authentication popup, the window won't get closed but the connection is established successfully. You can safely close the window. - -If you still don't see the tool, try it a couple of times. We are aware of this behavior and working to improve it. -::: - - - -To connect VSCode to Port's remote MCP server, follow these detailed steps. For complete instructions, refer to the [official VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/chat/mcp-servers). - -:::info VSCode MCP requirements -Before proceeding, ensure your VS Code is updated to the latest version and that MCP is enabled for your GitHub organization. You may need to enable "Editor preview features" under Settings > Code, planning, and automation > Copilot via admin access from your organization. -::: - -:::tip Prerequisites -This configuration uses the open-source `mcp-remote` package, which requires Node.js to be installed on your system. Before using the configuration, ensure Node.js is available by running: - -```bash -npx -y mcp-remote --help -``` - -If you encounter errors: -- **Missing Node.js**: Install Node.js from [nodejs.org](https://nodejs.org/) -- **Network issues**: Check your internet connection and proxy settings -- **Permission issues**: You may need to run with appropriate permissions -::: - - -**Step 1: Configure MCP Server Settings** - -1. Open VS Code settings -2. Search for "MCP: Open user configuration" (or follow the instructions on a workspace installation) -3. Add the server configuration using the appropriate configuration for your region: - - - -```json showLineNumbers -{ - "mcpServers": { - "port-vscode-eu": { - "command": "npx", - "args": [ - "-y", - "mcp-remote", - "https://mcp.port.io/v1" - ] - } - } -} -``` - - -```json showLineNumbers -{ - "mcpServers": { - "port-vscode-us": { - "command": "npx", - "args": [ - "-y", - "mcp-remote", - "https://mcp.us.port.io/v1" - ] - } - } -} -``` - - - -**Step 2: Start the MCP Server** - -1. After adding the configuration, click on "Start" to initialize the MCP server -2. If you don't see the "Start" button, ensure: - - Your VS Code version is updated to the latest version - - MCP is enabled for your GitHub organization - - "Editor preview features" is enabled under Settings > Code, planning, and automation > Copilot - -**Step 3: Verify Connection** - -1. Once started, you should see the number of available tools displayed -2. If you don't see the tools count: - - Click on "More" to expand additional options - - Select "Show output" to view detailed logs - - Check the output panel for any error messages or connection issues - -**Step 4: Access Port Tools** - -1. Start a new chat session in VS Code -2. Click on the tools icon in the chat interface -3. You should now see Port tools available for use - -![VS Code MCP Setup](/img/ai-agents/MCPVSCodeSetup.gif) - - - -To connect Claude to Port's remote MCP, you need to create a custom connector. This process does not require a client ID. For detailed instructions, refer to the [official Anthropic documentation on custom connectors](https://support.anthropic.com/en/articles/11175166-getting-started-with-custom-connectors-using-remote-mcp). - -When prompted for the remote MCP server URL, use the appropriate URL for your region: - - - -``` -https://mcp.port.io/v1 -``` - - -``` -https://mcp.us.port.io/v1 -``` - - - - -The local MCP server is an open-source project that you can run on your own infrastructure. It offers a similar set of capabilities, but requires manual setup and maintenance. - -While you can use it, we are no longer supporting it and are not tracking the project issues and activities. - -

Prerequisites

- -- A Port.io account with appropriate permissions. -- Your Port credentials (Client ID and Client Secret). You can create these from your Port.io dashboard under Settings > Credentials. - -

Installation

- -The Port MCP Server can be installed using Docker or `uvx` (a package manager for Python). While the setup is straightforward, the specifics can vary based on your chosen MCP client (Claude, Cursor, VS Code, etc.). - -:::info Detailed Installation Guide -For comprehensive, step-by-step installation instructions for various platforms and methods (Docker, UVX), please refer to the **[Port MCP Server GitHub README](https://github.com/port-labs/port-mcp-server)**. -The README provides the latest configuration details and examples for different setups. -::: -
-
- -### Available tools - -The Port MCP Server exposes different sets of tools based on your role and use case. The tools you see will depend on your permissions in Port. - - - - -**Developers** are typically users who consume and interact with the developer portal - querying services, running actions, and analyzing data. These tools help you get information and execute approved workflows. - -**Query and analysis tools** -- **[`get_blueprints`](/api-reference/get-all-blueprints)**: Retrieve a list of all blueprints from Port. -- **[`get_blueprint`](/api-reference/get-a-blueprint)**: Retrieve information about a specific blueprint by its identifier. -- **[`get_entities`](/api-reference/get-all-entities-of-a-blueprint)**: Retrieve all entities for a given blueprint. -- **[`get_entity`](/api-reference/get-an-entity)**: Retrieve information about a specific entity. -- **[`get_scorecards`](/api-reference/get-all-scorecards)**: Retrieve all scorecards from Port. -- **[`get_scorecard`](/api-reference/get-a-scorecard)**: Retrieve information about a specific scorecard by its identifier. -- **[`describe_user_details`](/api-reference/get-organization-details)**: Get information about your Port account, organization, and user profile details. -- **`search_port_docs_sources`**: Search through Port documentation sources for relevant information. -- **`ask_port_docs`**: Ask questions about Port documentation and get contextual answers. - -**Action execution tools** -- **[`run_`](/api-reference/execute-a-self-service-action)**: Execute any action you have permission to run in Port. The `action_identifier` corresponds to the identifier of the action you want to run. For example, if you have an action with the identifier `scaffold_microservice`, you can run it using `run_scaffold_microservice`. - -**AI agent tools** -- **[`invoke_ai_agent`](/api-reference/invoke-an-agent)**: Invoke a Port AI agent with a specific prompt. - - - - -**Builders** are typically platform engineers or admins who design and configure the developer portal - creating blueprints, setting up scorecards, and managing the overall structure. These tools help you build and maintain the portal. - -**All Developer tools** -Builders have access to all the tools available to Developers (listed in the Developer tab), plus the additional management tools below. - -**Blueprint management tools** -- **[`create_blueprint`](/api-reference/create-a-blueprint)**: Create a new blueprint in Port. -- **[`update_blueprint`](/api-reference/update-a-blueprint)**: Update an existing blueprint. -- **[`delete_blueprint`](/api-reference/delete-a-blueprint)**: Delete a blueprint from Port. - -**Entity management tools** -- **[`create_entity`](/api-reference/create-an-entity)**: Create a new entity for a specific blueprint. -- **[`update_entity`](/api-reference/update-an-entity)**: Update an existing entity. -- **[`delete_entity`](/api-reference/delete-an-entity)**: Delete an entity. - -**Scorecard management tools** -- **[`create_scorecard`](/api-reference/create-a-scorecard)**: Create a new scorecard for a specific blueprint. -- **[`update_scorecard`](/api-reference/change-scorecards)**: Update an existing scorecard. -- **[`delete_scorecard`](/api-reference/delete-a-scorecard)**: Delete a scorecard from Port. - - - - -### Select which tools to use - -By default, when you open a chat with Port MCP, all available tools (based on your permissions) are loaded and ready to use. However, you can customize which tools are available if you want to focus on specific workflows. - -For example, if you only want to query data from Port without building or modifying anything, you can limit the tools to just the read-only ones. This can help reduce complexity and ensure you don't accidentally make changes. - - - - -In Cursor, you can customize which tools are available through the UI after connecting to Port MCP. Once connected, you can select specific tools through Cursor's interface as shown below. - -![Enabling specific tools in Cursor](/img/ai-agents/MCPCursorEnableTools.png) - - - - -In VSCode, you can customize which tools are available through the UI after connecting to Port MCP. Once connected, you can select specific tools through VSCode's interface as shown below. - -![Enabling specific tools in VSCode](/img/ai-agents/MCPVSCodeEnableTools.png) - - - - -In Claude, you can specify which tools to enable during the custom connector setup process. You'll have the option to select specific tools through Claude's interface rather than enabling all available tools. - -![Enabling specific tools in Claude](/img/ai-agents/MCPClaudeEnableTools.png) - -Refer to the [Claude custom connector documentation](https://support.anthropic.com/en/articles/11175166-getting-started-with-custom-connectors-using-remote-mcp) for detailed instructions on tool selection during setup. - - - - -### Prompts - -In Port, you can centrally manage reusable prompts and expose them to your users via the MCP Server. Once defined in Port, these prompts become available in supported MCP clients (for example, Cursor or Claude) where developers and AI agents can discover and run them with the required inputs. - -#### Common use cases - -- Automate on-call runbooks and incident triage guidance -- Standardize code review or deployment checklists -- Generate structured updates and communications (e.g., incident status, release notes) - -#### Setup data model - -1. Go to the [Builder page](https://app.getport.io/settings/data-model) of your portal. - -2. Click on "+ Blueprint". - -3. Click on the `{...}` button in the top right corner, and choose "Edit JSON". - -4. Paste the following JSON schema into the editor: - - - -
- Prompt blueprint JSON (click to expand) - - ```json showLineNumbers - { - "identifier": "prompt", - "title": "Prompt", - "icon": "Microservice", - "schema": { - "properties": { - "description": { - "type": "string", - "title": "Description" - }, - "arguments": { - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The name of the argument parameter" - }, - "description": { - "type": "string", - "description": "A description of what this argument is for" - }, - "required": { - "type": "boolean", - "description": "Whether this argument is required or optional", - "default": false - } - }, - "required": [ - "name", - "description" - ] - }, - "type": "array", - "title": "Arguments" - }, - "template": { - "icon": "DefaultProperty", - "type": "string", - "title": "Prompt Template", - "format": "markdown" - } - }, - "required": [ - "description", - "template" - ] - }, - "mirrorProperties": {}, - "calculationProperties": {}, - "aggregationProperties": {}, - "relations": {} - } - ``` -
- -:::info Where prompts appear -Once this blueprint exists and you create entities for it, prompts will show up in supported MCP clients connected to your Port organization. In clients that surface MCP prompts, you’ll see them listed and ready to run with arguments. -::: - -#### Create prompts - -Create entities of the `prompt` blueprint for each prompt you want to expose. At minimum, provide `description` and `template`. Optionally add `arguments` to parameterize the prompt. - -1. Go to the [Prompts page](https://app.getport.io/prompts) in your portal. -2. Click `Create prompt`. -3. Fill out the form: - - Provide a title and description. - - Write the prompt template (supports markdown). - - Define any `arguments` (optional) with `name`, `description`, and whether they are `required`. - -![Create prompt form in Port](/img/ai-agents/PortPromptForm.png) - -:::info Template and placeholders -The `template` supports markdown and variable placeholders. Each argument defined in `arguments` is exposed by its `name` and can be referenced as `{{name}}` inside the template. When you run the prompt, the MCP Server collects values for required arguments and substitutes them into the matching `{{}}` placeholders before execution. -::: - -#### Examples - - - - -Use placeholders to inject context such as the service, environment, incident, and timeframe. - -```markdown showLineNumbers -You are assisting with an incident in the {{service_name}} service ({{environment}}). -Incident ID: {{incident_id}} - -For the last {{timeframe}}: -- Summarize critical alerts and recent deploys -- Suggest next steps and owners -- Link relevant dashboards/runbooks -``` - -Arguments to define: `service_name` (required), `environment` (optional), `incident_id` (required), `timeframe` (optional). - - - - -Generate tailored remediation steps for failing scorecard rules. - -```markdown showLineNumbers -For {{service_name}}, generate remediation steps for failing rules in the "{{scorecard_name}}" scorecard. - -For each failing rule: -- What is failing -- Why it matters -- Step-by-step remediation -- Owners and suggested timeline -``` - -Arguments to define: `service_name` (required), `scorecard_name` (required). - - - - -Summarize on-call context for a team over a time window. - -```markdown showLineNumbers -Create an on-call handoff for {{team}} for the last {{timeframe}}. - -Include: -- Active incidents and current status -- Top risks and mitigations -- Pending actions and owners -- Upcoming maintenance windows -``` - -Arguments to define: `team` (required), `timeframe` (required). - - - - -After creating entities, reconnect or refresh your MCP client; your prompts will be available to run and will prompt for any defined arguments. - -#### See prompts in your client - - - - -In Cursor, type "/" to open the prompts list. You'll see all `prompt` entities; selecting one opens an input form for its arguments. - -![Prompt list in Cursor](/img/ai-agents/MCPCursorPromptList.png) - -When you select a prompt, Cursor renders fields for the defined `arguments`. Required ones are marked and must be provided. The MCP Server substitutes provided values into the matching `{{}}` placeholders in the template at runtime. - -![Prompt argument input in Cursor](/img/ai-agents/MCPCursorPromptInput.png) - - - - -In Claude, click the "+" button and choose the prompts option to view the list from your Port organization. Selecting a prompt opens a parameter collection flow. - -![Prompt list in Claude](/img/ai-agents/MCPClaudePromptList.png) - -Claude will ask for any required arguments before running the prompt, and the MCP Server will replace the corresponding `{{}}` placeholders in the template with the provided values. - -![Prompt argument input in Claude](/img/ai-agents/MCPClaudePromptInput.png) - - - - -## Connect to Port's MCP Server to Claude Code in Github Workflow - -### Automate Claude Code with GitHub Actions -Running Claude Code inside CI/CD differs from local usage because an interactive OAuth flow is not possible. Instead, the workflow must: - -1. **Generate a short-lived Port access token** using the Client-Credentials grant (Client ID + Client Secret). -2. **Connect to the remote MCP server** and pass the token to it so Claude Code can call the allowed tools. - -
- Example Github workflow (Click to expand) - -```yaml title=".github/workflows/claude-code-mcp.yml" showLineNumbers -name: Port MCP Server Demo with Claude Code - -on: - workflow_dispatch: - inputs: - debug: - description: "Debug mode" - required: false - default: "false" - -env: - # Remote MCP endpoint for your region (eu/us) - PORT_MCP_URL: ${{ vars.PORT_MCP_URL }} - # Base URL for the OAuth token endpoint for your region (eu/us) – e.g. https://api.port.io - PORT_AUTH_BASE_URL: ${{ vars.PORT_AUTH_BASE_URL }} - -jobs: - demo: - runs-on: ubuntu-latest - - permissions: - id-token: write - contents: read - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Authenticate with Port and get access token - id: port-auth - run: | - # Exchange client credentials for an access token - response=$(curl -s -X POST \ - "${{ env.PORT_AUTH_BASE_URL }}/auth/access_token" \ - -H "Content-Type: application/json" \ - -d '{ - "clientId": "${{ secrets.PORT_CLIENT_ID }}", - "clientSecret": "${{ secrets.PORT_CLIENT_SECRET }}" - }') - - access_token=$(echo "$response" | jq -r '.accessToken') - if [ -z "$access_token" ] || [ "$access_token" = "null" ]; then - echo "Failed to obtain access token" - echo "Response: $response" - exit 1 - fi - - # Mask the token in job logs - echo "::add-mask::$access_token" - echo "access_token=$access_token" >> "$GITHUB_OUTPUT" - - - name: Run Claude Code against Port MCP - uses: anthropics/claude-code-action@beta - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - mode: agent - # Define the MCP server and pass the token in the Authorization header - mcp_config: | - { - "mcpServers": { - "port-prod": { - "command": "npx", - "args": [ - "mcp-remote", - "${{ env.PORT_MCP_URL }}", - "--allow-http", - "--header", - "Authorization: Bearer ${{ steps.port-auth.outputs.access_token }}" - ] - } - } - } - # Restrict the tools the model can use (add or remove as needed) - allowed_tools: "mcp__port-prod__list_blueprints,mcp__port-prod__get_blueprint,mcp__port-prod__get_entities" - # Replace with whatever you need the agent to do - direct_prompt: | - You are connected to the Port MCP server. - List all blueprints and then show me the entities of the "zendesk_ticket" blueprint. -``` -
- - - -#### How this workflow works - -1. **Get a token** – The `port-auth` step exchanges your `PORT_CLIENT_ID` and `PORT_CLIENT_SECRET` for a short-lived OAuth access token (no interactive login needed in CI). -2. **Start Claude with MCP** – `anthropics/claude-code-action` launches Claude Code and spins up a `mcp-remote` client that points to your Port MCP URL, forwarding the token via an `Authorization` header. -3. **Run the prompt** – Claude can call only the MCP tools you list in `allowed_tools`, using them to accomplish whatever task you set in `direct_prompt`. - -:::caution Keep your secrets safe -Store `PORT_CLIENT_ID`, `PORT_CLIENT_SECRET`, and `ANTHROPIC_API_KEY` as **encrypted GitHub Actions secrets**. Never commit them to the repository or print them to logs. -::: - -:::tip Customise the tool list -For read-only workflows you can shorten `allowed_tools` to just the query operations you need. This reduces payload size and risk of accidental changes. -::: - -## Troubleshooting - -If you encounter issues while setting up or using the Port MCP Server, expand the relevant section below: - -
-How can I connect to the MCP? (Click to expand) - -Refer back to the [setup instructions](/ai-agents/port-mcp-server#setup) for your specific application (Cursor, VSCode, or Claude). Make sure you're using the correct regional URL for your Port organization. - -
- -
-I completed the connection but nothing happens (Click to expand) - -Check that you've followed all the [setup steps](/ai-agents/port-mcp-server#setup) correctly for your application. Ensure you're authenticated with Port and have the necessary permissions. If you've followed all the steps and still have issues, please reach out to our support team. - -
- -
-How can I use the MCP server? (Click to expand) - -Once connected, you can interact with Port through natural language in your application's chat interface. Ask questions about your software catalog, request help with building Port resources, or analyze your data. The [available tools](/ai-agents/port-mcp-server#available-tools) depend on your permissions (Developer vs Builder role). - -
- -
-Why do I see an error about too many tools? (Click to expand) - -Each self-service action in your Port instance becomes an individual tool (as `run_`). If your organization has many actions, this can result in a large number of tools being available. - -While most AI models handle this well, some have restrictions and may limit you to around 40 tools total. If you encounter errors about tool limits: - -1. **Reduce the number of tools** by customizing which tools are enabled (see [Select which tools to use](#select-which-tools-to-use) section above) -2. **Focus on essential tools** by only enabling the read-only tools you need plus a few key actions -3. **Contact your Port Admin** to review which actions are essential for your workflow - -This is completely normal behavior and doesn't indicate a problem with Port MCP - it's just a limitation of some AI models. - -
- -:::tip Getting Help -If you continue to experience issues, please reach out to Port support with: -- Your IDE/application version -- The specific error messages you're seeing -- Your Port region (EU/US) -- Steps you've already tried - -This information will help us provide more targeted assistance. -::: diff --git a/docs/ai-agents/port-mcp-server/_category_.json b/docs/ai-agents/port-mcp-server/_category_.json new file mode 100644 index 0000000000..9a80290e67 --- /dev/null +++ b/docs/ai-agents/port-mcp-server/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Port MCP Server", + "position": 4 +} diff --git a/docs/ai-agents/port-mcp-server/available-tools.md b/docs/ai-agents/port-mcp-server/available-tools.md new file mode 100644 index 0000000000..fc9d5d82fe --- /dev/null +++ b/docs/ai-agents/port-mcp-server/available-tools.md @@ -0,0 +1,91 @@ +--- +sidebar_position: 2 +title: Tools +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Available tools + +The Port MCP Server exposes different sets of tools based on your role and use case. The tools you see will depend on your permissions in Port. + + + + +**Developers** are typically users who consume and interact with the developer portal - querying services, running actions, and analyzing data. These tools help you get information and execute approved workflows. + +**Query and analysis tools** +- **[`get_blueprints`](/api-reference/get-all-blueprints)**: Retrieve a list of all blueprints from Port. +- **[`get_blueprint`](/api-reference/get-a-blueprint)**: Retrieve information about a specific blueprint by its identifier. +- **[`get_entities`](/api-reference/get-all-entities-of-a-blueprint)**: Retrieve all entities for a given blueprint. +- **[`get_entity`](/api-reference/get-an-entity)**: Retrieve information about a specific entity. +- **[`get_scorecards`](/api-reference/get-all-scorecards)**: Retrieve all scorecards from Port. +- **[`get_scorecard`](/api-reference/get-a-scorecard)**: Retrieve information about a specific scorecard by its identifier. +- **[`describe_user_details`](/api-reference/get-organization-details)**: Get information about your Port account, organization, and user profile details. +- **`search_port_docs_sources`**: Search through Port documentation sources for relevant information. +- **`ask_port_docs`**: Ask questions about Port documentation and get contextual answers. + +**Action execution tools** +- **[`run_`](/api-reference/execute-a-self-service-action)**: Execute any action you have permission to run in Port. + +**AI agent tools** +- **[`invoke_ai_agent`](/api-reference/invoke-an-agent)**: Invoke a Port AI agent with a specific prompt. + + + + +**Builders** are platform engineers or admins who design and configure the developer portal - creating blueprints, setting up scorecards, and managing the overall structure. These tools help you build and maintain the portal. + +**All Developer tools** +Builders have access to all the tools available to Developers (listed above), plus the additional management tools below. + +**Blueprint management tools** +- **[`create_blueprint`](/api-reference/create-a-blueprint)** +- **[`update_blueprint`](/api-reference/update-a-blueprint)** +- **[`delete_blueprint`](/api-reference/delete-a-blueprint)** + +**Entity management tools** +- **[`create_entity`](/api-reference/create-an-entity)** +- **[`update_entity`](/api-reference/update-an-entity)** +- **[`delete_entity`](/api-reference/delete-an-entity)** + +**Scorecard management tools** +- **[`create_scorecard`](/api-reference/create-a-scorecard)** +- **[`update_scorecard`](/api-reference/change-scorecards)** +- **[`delete_scorecard`](/api-reference/delete-a-scorecard)** + + + + +## Select which tools to use + +By default, when you open a chat with Port MCP, all available tools (based on your permissions) are loaded and ready to use. However, you can customize which tools are available if you want to focus on specific workflows. + +For example, if you only want to query data from Port without building or modifying anything, you can limit the tools to just the read-only ones. This helps reduce complexity and ensures you don't accidentally make changes. + + + + +In Cursor, you can customize which tools are available through the UI after connecting to Port MCP. Once connected, you can select specific tools as shown below. + +![Enabling specific tools in Cursor](/img/ai-agents/MCPCursorEnableTools.png) + + + + +In VSCode, you can choose the tools through the UI after connecting to Port MCP. + +![Enabling specific tools in VSCode](/img/ai-agents/MCPVSCodeEnableTools.png) + + + + +When creating a custom connector in Claude, you can specify exactly which tools to expose instead of enabling everything. + +![Enabling specific tools in Claude](/img/ai-agents/MCPClaudeEnableTools.png) + +Refer to the [Claude custom connector documentation](https://support.anthropic.com/en/articles/11175166-getting-started-with-custom-connectors-using-remote-mcp) for detailed instructions. + + + diff --git a/docs/ai-agents/port-mcp-server/github-actions.md b/docs/ai-agents/port-mcp-server/github-actions.md new file mode 100644 index 0000000000..ac020bf357 --- /dev/null +++ b/docs/ai-agents/port-mcp-server/github-actions.md @@ -0,0 +1,82 @@ +--- +sidebar_position: 5 +title: Connect Claude Code To Port MCP Server in GitHub Actions +--- + +# Connect Claude Code To Port MCP Server in GitHub Actions + +Running Claude Code inside CI/CD differs from local usage because an interactive OAuth flow is not possible. Instead, a workflow must: + +1. **Generate a short-lived Port access token** using the Client-Credentials grant (`clientId` + `clientSecret`). +2. **Start or connect to the remote MCP server** and pass the token so Claude Code can call the allowed tools. + +
+Show example workflow + +```yaml title=".github/workflows/claude-code-mcp.yml" showLineNumbers +name: Port MCP Server Demo with Claude Code +on: workflow_dispatch + +env: + PORT_MCP_URL: ${{ vars.PORT_MCP_URL }} + PORT_AUTH_BASE_URL: ${{ vars.PORT_AUTH_BASE_URL }} + +jobs: + demo: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Authenticate with Port + id: port-auth + run: | + response=$(curl -s -X POST "${{ env.PORT_AUTH_BASE_URL }}/auth/access_token" \ + -H "Content-Type: application/json" \ + -d '{"clientId":"${{ secrets.PORT_CLIENT_ID }}","clientSecret":"${{ secrets.PORT_CLIENT_SECRET }}"}') + token=$(echo "$response" | jq -r '.accessToken') + 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 }}" + ] + } + } + } + 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. **Token** – The `port-auth` step exchanges your client credentials for an access token (no browser auth). +2. **Claude Code** – The action launches Claude Code, pointing `mcp-remote` at your MCP URL while injecting the token via the `Authorization` header. +3. **Prompt** – Claude can invoke only the tools you list in `allowed_tools`. + +:::caution Keep your secrets safe +Store `PORT_CLIENT_ID`, `PORT_CLIENT_SECRET`, and `ANTHROPIC_API_KEY` as **encrypted GitHub Actions secrets**. +::: + +:::tip Customise the tool list +For read-only workflows, shorten `allowed_tools` to just the query operations you need. +::: diff --git a/docs/ai-agents/port-mcp-server/overview-and-installation.md b/docs/ai-agents/port-mcp-server/overview-and-installation.md new file mode 100644 index 0000000000..5a1d9f1a32 --- /dev/null +++ b/docs/ai-agents/port-mcp-server/overview-and-installation.md @@ -0,0 +1,303 @@ +--- +sidebar_position: 1 +title: Overview & Installation +slug: /ai-agents/port-mcp-server +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Port MCP server + +
+
+ +
+
+
+ +The Port Model Context Protocol (MCP) Server acts as a bridge, enabling Large Language Models (LLMs)—like those powering Claude, Cursor, or GitHub Copilot—to interact directly with your Port.io developer portal. This allows you to leverage natural language to query your software catalog, analyze service health, manage resources, and even streamline development workflows, all from your preferred interfaces. + +:::info AI Agents vs. MCP Server +The Port MCP Server is currently in open beta and provides significant standalone value, independent of our [AI Agents feature](/ai-agents/overview). Port AI Agents are currently in closed beta with limited access, while the MCP Server gives you immediate access to streamline building in Port, query your catalog, analyze service health, and streamline development workflows using natural language. + +While the MCP Server can interact with Port AI Agents when available, the core MCP functionality can be used freely without requiring access to the closed beta AI Agents feature. +::: + +## Why integrate LLMs with your developer portal? + +The primary advantage of the Port MCP Server is the ability to bring your developer portal's data and actions into the conversational interfaces you already use. This offers several benefits: + +* **Reduced Context Switching:** Access Port information and initiate actions without leaving your IDE or chat tool. +* **Increased Efficiency:** Get answers and perform tasks faster using natural language commands. +* **Improved Developer Experience:** Make your developer portal more accessible and intuitive to interact with. +* **Enhanced Data-Driven Decisions:** Easily pull specific data points from Port to inform your work in real-time. + +As one user put it: + +> "It would be interesting to build a use case where a developer could ask Copilot from his IDE about stuff Port knows about, without actually having to go to Port." + +The Port MCP Server directly enables these kinds of valuable, in-context interactions. + +## Key capabilities and use-cases + +
+
+ +
+
+ +The Port MCP Server enables you to interact with your Port data and capabilities directly through natural language within your chosen LLM-powered tools. Here's what you can achieve: + +### Find information quickly + +Effortlessly query your software catalog and get immediate answers. This eliminates the need to navigate through UIs or write complex API queries when you need information. + +* Ask: "Who is the owner of service X?" +* Ask: "How many services do we have in production?" +* Ask: "Show me all the microservices owned by the Backend team." +* Ask: "What are the dependencies of the 'OrderProcessing' service?" + +![Querying the service catalog from an IDE](/img/ai-agents/MCPCopilotAskServices.png) + +### Vibe-build in Port + +Leverage Claude's capabilities to manage and build your entire Port software catalog. You can create and configure blueprints, set up self-service actions, design scorecards, and more. + +* Ask: "Please help me apply this guide into my Port instance - [[guide URL]]" +* Ask: "I want to start managing my k8s deployments, how can we build it in Port?" +* Ask: "I want a new production readiness scorecard to track the code quality and service alerts" +* Ask: "Create a new self-service action in Port to scaffold a new service" + +![Claude building a self-service action](/img/ai-agents/MCPClaudeBuildSSA.png) + +### Analyze scorecards and quality + +Gain insights into service health, compliance, and quality by leveraging Port's scorecard data. Identify areas for improvement and track progress against your standards. + +* Ask: "Which services are failing our security requirements scorecard?" +* Ask: "What's preventing the 'InventoryService' from reaching Gold level in the 'Production Readiness' scorecard?" +* Ask: "Show me the bug count vs. test coverage for all Java microservices." + +![Asking about bug counts and test coverage correlation](/img/ai-agents/MCPClaudeInsightsBugs.png) + +* Ask: "Which of our services are missing critical monitoring dashboards?" + +![Identifying services lacking proper monitoring](/img/ai-agents/MCPClaudeMonitoringServices.png) + +### Streamline development and operations + +Receive assistance with common development and operational tasks, directly within your workflow. + +* Ask: "What do I need to do to set up a new 'ReportingService'?" +* Ask: "Guide me through creating a new component blueprint with 'name', 'description', and 'owner' properties." +* Ask: "Help me add a rule to the 'Tier1Services' scorecard that requires an on-call schedule to be defined." + +![Getting instructions for new service setup](/img/ai-agents/MCPClaudeServiceSetup.png) + +### Find your own use cases + +You can use Port's MCP to find the use cases that will be valuable to you. Try using this prompt: "think of creative prompts I can use to showcase the power of Port's MCP, based on the data available in Port" + + +## Installing Port MCP + +Installing Port's MCP is simple. Follow the instructions for your preferred tool, or learn about the archived local MCP server. + + + +To connect Cursor to Port's remote MCP, follow these steps: + +1. **Go to Cursor settings, click on Tools & Integrations, and add a new MCP server** + +![Go to Cursor Settings](/img/ai-agents/MCPInstallCursorStep1.png) + +2. **Add the above configuration** + +Use the appropriate configuration for your region: + + + +```json showLineNumbers +{ + "mcpServers": { + "port-eu": { + "url": "https://mcp.port.io/v1" + } + } +} +``` + + +```json showLineNumbers +{ + "mcpServers": { + "port-us": { + "url": "https://mcp.us.port.io/v1" + } + } +} +``` + + + +![Add configuration](/img/ai-agents/MCPInstallCursorStep2.png) + +3. **Login to Port** +Click on "Needs login", and complete the authentication flow in the window that opens up. +![Login to Port](/img/ai-agents/MCPInstallCursorStep3.png) + +4. **See the MCP tools** +After successfully connecting to Port, you'll see the list of available tools from the MCP. +![See tools](/img/ai-agents/MCPInstallCursorStep4.png) + +:::warning Authentication window behavior +In some cases, after clicking "Accept" in the authentication popup, the window won't get closed but the connection is established successfully. You can safely close the window. + +If you still don't see the tool, try it a couple of times. We are aware of this behavior and working to improve it. +::: + + + +To connect VSCode to Port's remote MCP server, follow these detailed steps. For complete instructions, refer to the [official VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/chat/mcp-servers). + +:::info VSCode MCP requirements +Before proceeding, ensure your VS Code is updated to the latest version and that MCP is enabled for your GitHub organization. You may need to enable "Editor preview features" under Settings > Code, planning, and automation > Copilot via admin access from your organization. +::: + +:::tip Prerequisites +This configuration uses the open-source `mcp-remote` package, which requires Node.js to be installed on your system. Before using the configuration, ensure Node.js is available by running: + +```bash +npx -y mcp-remote --help +``` + +If you encounter errors: +- **Missing Node.js**: Install Node.js from [nodejs.org](https://nodejs.org/) +- **Network issues**: Check your internet connection and proxy settings +- **Permission issues**: You may need to run with appropriate permissions +::: + + +**Step 1: Configure MCP Server Settings** + +1. Open VS Code settings +2. Search for "MCP: Open user configuration" (or follow the instructions on a workspace installation) +3. Add the server configuration using the appropriate configuration for your region: + + + +```json showLineNumbers +{ + "mcpServers": { + "port-vscode-eu": { + "command": "npx", + "args": [ + "-y", + "mcp-remote", + "https://mcp.port.io/v1" + ] + } + } +} +``` + + +```json showLineNumbers +{ + "mcpServers": { + "port-vscode-us": { + "command": "npx", + "args": [ + "-y", + "mcp-remote", + "https://mcp.us.port.io/v1" + ] + } + } +} +``` + + + +**Step 2: Start the MCP Server** + +1. After adding the configuration, click on "Start" to initialize the MCP server +2. If you don't see the "Start" button, ensure: + - Your VS Code version is updated to the latest version + - MCP is enabled for your GitHub organization + - "Editor preview features" is enabled under Settings > Code, planning, and automation > Copilot + +**Step 3: Verify Connection** + +1. Once started, you should see the number of available tools displayed +2. If you don't see the tools count: + - Click on "More" to expand additional options + - Select "Show output" to view detailed logs + - Check the output panel for any error messages or connection issues + +**Step 4: Access Port Tools** + +1. Start a new chat session in VS Code +2. Click on the tools icon in the chat interface +3. You should now see Port tools available for use + +![VS Code MCP Setup](/img/ai-agents/MCPVSCodeSetup.gif) + + + +To connect Claude to Port's remote MCP, you need to create a custom connector. This process does not require a client ID. For detailed instructions, refer to the [official Anthropic documentation on custom connectors](https://support.anthropic.com/en/articles/11175166-getting-started-with-custom-connectors-using-remote-mcp). + +When prompted for the remote MCP server URL, use the appropriate URL for your region: + + + +``` +https://mcp.port.io/v1 +``` + + +``` +https://mcp.us.port.io/v1 +``` + + + + +The local MCP server is an open-source project that you can run on your own infrastructure. It offers a similar set of capabilities, but requires manual setup and maintenance. + +While you can use it, we are no longer supporting it and are not tracking the project issues and activities. + +

Prerequisites

+ +- A Port.io account with appropriate permissions. +- Your Port credentials (Client ID and Client Secret). You can create these from your Port.io dashboard under Settings > Credentials. + +

Installation

+ +The Port MCP Server can be installed using Docker or `uvx` (a package manager for Python). While the setup is straightforward, the specifics can vary based on your chosen MCP client (Claude, Cursor, VS Code, etc.). + +:::info Detailed Installation Guide +For comprehensive, step-by-step installation instructions for various platforms and methods (Docker, UVX), please refer to the **[Port MCP Server GitHub README](https://github.com/port-labs/port-mcp-server)**. +The README provides the latest configuration details and examples for different setups. +::: +
+
diff --git a/docs/ai-agents/port-mcp-server/prompts.md b/docs/ai-agents/port-mcp-server/prompts.md new file mode 100644 index 0000000000..049feb15ce --- /dev/null +++ b/docs/ai-agents/port-mcp-server/prompts.md @@ -0,0 +1,373 @@ +--- +sidebar_position: 4 +title: Prompts +--- + +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" + +# Prompts + +Port allows you to centrally manage reusable prompts and expose them to your users via the MCP Server. Once defined, prompts become available in supported MCP clients (for example, Cursor or Claude) where developers and AI agents can discover and run them with the required inputs. + +#### Common use cases + +- Automate on-call runbooks and incident-triage guidance +- Standardize code review or deployment checklists +- Generate structured updates and communications (e.g., incident status, release notes) + +#### Set up the data model + +1. Go to the [Builder page](https://app.getport.io/settings/data-model) in your portal. +2. Click **+ Blueprint**. +3. Click the `{...}` button in the top-right corner and choose **Edit JSON**. +4. Paste the following JSON schema into the editor: + +
+ Prompt blueprint JSON (click to expand) + + ```json showLineNumbers + { + "identifier": "prompt", + "title": "Prompt", + "icon": "Microservice", + "ownership": { + "type": "Direct", + "title": "Owning Teams" + }, + "schema": { + "properties": { + "description": { + "type": "string", + "title": "Description" + }, + "arguments": { + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the argument parameter" + }, + "description": { + "type": "string", + "description": "A description of what this argument is for" + }, + "required": { + "type": "boolean", + "description": "Whether this argument is required or optional", + "default": false + } + }, + "required": [ + "name", + "description" + ] + }, + "type": "array", + "title": "Arguments" + }, + "template": { + "icon": "DefaultProperty", + "type": "string", + "title": "Prompt Template", + "format": "markdown" + } + }, + "required": [ + "description", + "template" + ] + }, + "mirrorProperties": {}, + "calculationProperties": {}, + "aggregationProperties": {}, + "relations": {} + } + ``` +
+ +:::info Where prompts appear +After this blueprint exists and you create entities for it, prompts will show up in supported MCP clients connected to your Port organization. In clients that surface MCP prompts, you’ll see them listed and ready to run with arguments. +::: + +#### Create prompts + +Create entities of the `prompt` blueprint for each prompt you want to expose. At minimum, provide `description` and `template`. Optionally add `arguments` to parameterize the prompt. + +1. Go to the [Prompts page](https://app.getport.io/prompts) in your portal. +2. Click **Create prompt**. +3. Fill out the form: + - Provide a title and description. + - Write the prompt template (supports markdown). + - Define any `arguments` (optional) with `name`, `description`, and whether they are `required`. + +![Create prompt form in Port](/img/ai-agents/PortPromptForm.png) + +:::info Template and placeholders +The `template` supports markdown and variable placeholders. Each argument defined in `arguments` is exposed by its `name` and can be referenced as `{{name}}` inside the template. When you run the prompt, the MCP Server collects values for required arguments and substitutes them into the matching placeholders before execution. +::: + +#### Examples + + + + +A prompt to assists on-call engineers by summarizing recent alerts and deploys related to an incident, then suggesting next steps and linking relevant runbooks. + +
+Incident triage prompt entity JSON (Click to expand) + +```json showLineNumbers +{ + "identifier": "incident_response_assistant", + "title": "Incident Response Assistant", + "team": [], + "properties": { + "description": "Assists with incident response by summarizing critical alerts, recent deploys, and suggesting next steps with relevant dashboards and runbooks", + "arguments": [ + { + "name": "service_name", + "required": true, + "description": "The name of the service experiencing the incident" + }, + { + "name": "environment", + "required": false, + "description": "The environment where the incident is occurring (e.g., production, staging)" + }, + { + "name": "incident_id", + "required": true, + "description": "The unique identifier for the incident" + }, + { + "name": "timeframe", + "required": false, + "description": "The time period to analyze (e.g., '24 hours', '1 week')" + } + ], + "template": "You are assisting with an incident in the {{service_name}} service ({{environment}}).\nIncident ID: {{incident_id}}\n\nFor the last {{timeframe}}:\n- Summarize critical alerts and recent deploys\n- Suggest next steps and owners\n- Link relevant dashboards/runbooks" + }, + "relations": {}, + "icon": "Microservice" +} +``` +
+ + +
+ + +A prompt that guides engineers to remediate failing scorecard rules by explaining each failure, its impact, and providing step-by-step fixes along with ownership suggestions. + +
+Scorecard remediation prompt entity JSON (Click to expand) + +```json showLineNumbers +{ + "identifier": "scorecard_remediation_guide", + "title": "Scorecard Remediation Guide", + "team": [], + "properties": { + "description": "Generate detailed remediation steps for failing scorecard rules, including what's failing, why it matters, step-by-step fixes, and ownership assignments", + "arguments": [ + { + "name": "service_name", + "required": true, + "description": "The name of the service that needs scorecard remediation" + }, + { + "name": "scorecard_name", + "required": true, + "description": "The name of the scorecard with failing rules" + } + ], + "template": "For {{service_name}}, generate remediation steps for failing rules in the \"{{scorecard_name}}\" scorecard.\n\nFor each failing rule:\n- What is failing\n- Why it matters\n- Step-by-step remediation\n- Owners and suggested timeline" + }, + "relations": {}, + "icon": "Microservice" +} +``` +
+ + +
+ + +A prompt to generates a thorough on-call handoff report, highlighting active incidents, key risks, pending actions, and upcoming maintenance for the specified team. + +
+On-Call handoff report prompt entity JSON (Click to expand) + +```json showLineNumbers +{ + "identifier": "oncall_handoff_report", + "title": "On-Call Handoff Report", + "team": [], + "properties": { + "description": "Generate comprehensive on-call handoff documentation including active incidents, risks, pending actions, and upcoming maintenance windows", + "arguments": [ + { + "name": "team", + "required": true, + "description": "The team name for which to create the on-call handoff" + }, + { + "name": "timeframe", + "required": true, + "description": "The time period to cover in the handoff (e.g., 'last 24 hours', 'past week')" + } + ], + "template": "Create an on-call handoff for {{team}} for the last {{timeframe}}.\n\nInclude:\n- Active incidents and current status\n- Top risks and mitigations\n- Pending actions and owners\n- Upcoming maintenance windows" + }, + "relations": {}, + "icon": "Microservice" +} +``` +
+ + + +
+
+ +After creating entities, reconnect or refresh your MCP client; your prompts will be available and will prompt for any defined arguments. + +#### See prompts in your client + + + + +In Cursor, type **/** to open the prompts list. Selecting a prompt opens an input form for its arguments. + +![Prompt list in Cursor](/img/ai-agents/MCPCursorPromptList.png) + +When you select a prompt, Cursor renders fields for the defined `arguments`. Required ones are marked and must be provided. + +![Prompt argument input in Cursor](/img/ai-agents/MCPCursorPromptInput.png) + + + + +In Claude, click the **+** button and choose the prompts option to view the list from your Port organization. Selecting a prompt opens a parameter collection flow. + +![Prompt list in Claude](/img/ai-agents/MCPClaudePromptList.png) + +Claude will ask for any required arguments before running the prompt and will substitute them into the template. + +![Prompt argument input in Claude](/img/ai-agents/MCPClaudePromptInput.png) + + + + + +### Allow users to create prompts with self service action +You can create a Self Service Action in Port to allow you users to create prompts themselves. + +
+Self Service Action JSON (Click to expand) + +```json showLineNumbers +{ + "identifier": "create_new_prompt", + "title": "Create New Prompt", + "icon": "Microservice", + "description": "Create prompt templates that appear in MCP clients (Claude, Cursor, VS Code, etc.) connected to your Port organization. Users can select prompts, provide required arguments, and get contextual AI assistance with dynamic data from Port.", + "trigger": { + "type": "self-service", + "operation": "CREATE", + "userInputs": { + "properties": { + "arguments": { + "type": "array", + "title": "Template Arguments", + "description": "Define arguments that users will provide when running this prompt. Each argument becomes available as {{argument_name}} placeholder in the template. Required arguments must be provided before prompt execution.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Argument Name", + "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$", + "description": "The parameter name that will be substituted in the template using {{name}} syntax (e.g., 'service_name', 'environment', 'incident_id')" + }, + "description": { + "type": "string", + "title": "Argument Description", + "description": "Clear description explaining what this argument represents and how it's used in the prompt context" + }, + "is_required": { + "type": "boolean", + "title": "Is Required", + "default": false, + "description": "When true, the MCP client (Claude, Cursor, VS Code) will require this argument before executing the prompt" + } + } + } + }, + "owning_team": { + "type": "string", + "title": "Owning Team (Optional)", + "description": "The team that will own and maintain this prompt template", + "format": "entity", + "blueprint": "_team" + }, + "prompt_title": { + "type": "string", + "title": "Prompt Title", + "description": "Human-readable name for this prompt (displayed in MCP clients like Claude, Cursor, and VS Code)", + "minLength": 3, + "maxLength": 50 + }, + "prompt_template": { + "type": "string", + "title": "Prompt Template", + "description": "The prompt content with placeholders for dynamic values. Use {{argument_name}} to reference arguments (e.g., 'Analyze service {{service_name}} in {{environment}}'). Supports markdown formatting. The MCP Server substitutes values into {{}} placeholders when the prompt runs.", + "minLength": 20, + "format": "multi-line" + }, + "prompt_description": { + "type": "string", + "title": "Description", + "description": "Explain what this prompt does and when to use it. This description helps users select the right prompt from the MCP client interface.", + "minLength": 10, + "maxLength": 500, + "format": "multi-line" + } + }, + "required": [ + "prompt_title", + "prompt_description", + "prompt_template" + ], + "order": [ + "prompt_title", + "prompt_description", + "prompt_template", + "arguments", + "owning_team" + ], + "titles": {} + }, + "blueprintIdentifier": "prompt" + }, + "invocationMethod": { + "type": "UPSERT_ENTITY", + "blueprintIdentifier": "prompt", + "mapping": { + "identifier": "{{ .inputs.prompt_title | ascii_downcase | gsub(\" \"; \"_\") | gsub(\"[^a-z0-9_]\"; \"\") }}", + "title": "{{ .inputs.prompt_title }}", + "team": "{{ if (.inputs.owning_team | type) == \"object\" then [.inputs.owning_team.identifier] else [] end }}", + "properties": { + "template": "{{ .inputs.prompt_template }}", + "arguments": "{{ (.inputs.arguments // []) | map({name: .name, description: .description, required: .is_required}) }}", + "description": "{{ .inputs.prompt_description }}" + } + } + }, + "requiredApproval": false +} +``` + +
\ No newline at end of file diff --git a/docs/ai-agents/port-mcp-server/troubleshooting.md b/docs/ai-agents/port-mcp-server/troubleshooting.md new file mode 100644 index 0000000000..fddb59e80b --- /dev/null +++ b/docs/ai-agents/port-mcp-server/troubleshooting.md @@ -0,0 +1,27 @@ +--- +sidebar_position: 6 +title: Troubleshooting +--- + +# Troubleshooting + +If you encounter issues while using the Port MCP Server, expand a section below: + +
+How can I connect to the MCP? +Make sure you're using the correct regional URL for your organization and have followed the setup instructions for your client (Cursor, VSCode, Claude). +
+ +
+I completed the connection but nothing happens +Ensure you authenticated successfully and have sufficient permissions. If problems persist, contact Port support. +
+ +
+Why do I see an error about too many tools? +Each self-service action becomes its own tool. If you have many actions, models may hit tool-count limits. Disable non-essential tools or ask an admin to review actions. +
+ +:::tip Need help? +Reach out to Port support with your client version, region (EU/US), error messages, and the steps you've tried. +::: diff --git a/static/img/ai-agents/PortPromptForm.png b/static/img/ai-agents/PortPromptForm.png index 460910fee5cb6808e482c921fa40297aaedfdc0f..4acb99c877153a52d58b7606972284a53ef0c362 100644 GIT binary patch literal 59415 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�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�{m9@mxfw7Mk-c4!;t60Fvis)HZTiUeo;S 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 literal 63427 zcmZU(19)aV*EU?+wr$(Ct*LE$YD{f+YTLGPwVm2FrtY+zwqNh}eeP%G{r~I8K6b7o zE7@6DIdf(ukxB}Z@Gv+qKtMq7(o$k7KtQ16KtLeOP!OLfszsP3KtM1JR-&Rx(xRe7 zO3n`ER<>q9KvIz@X^?73t0)0KKR!;$kf8KsPUPbifT;7sx3Xg5!Jx=TgoNtz8`^?w zgvG&LXcS%QP#fN2_Ee%|I|3aP%to~l)S z{dnvBMwgZb00x4Q;LgngNQ*wBbx?4LC_?~^IW5#Ar4VQDby7`6&4MTsWbO@N=ce!d zvKmn;^&1e~R9*rP5HfERj|7UJR_5hTN{SdERy<_EbbJO$c2ROykktifWbL&j1p~e!gA(a zO8bh^NZ8kKmhP^7{(U~1>@hOkOrgW)g08eQtlm6h^6@%pNE-hB`yp6Q+EDoSewgoO z1|1eFbWy3{GPe{F8ySsL7}JYH3X$JddHw{aK~ zWHw~W13Q#Es|EvTK^Le=!Fi})3LDq_3>H>O2x1HrJrCG5fS5_#rV%{0550N?6D-gX z2{aW1K?=l52&|zGrw;fa5Tp+D0+>7x4-ZVTkJ%A~EvRvaqz%SCNMRO~AW(n_${4h@oA1Vjq1I*>ycT!~l>L|tfcwssc# zgv1s9EQmnpZI(a6secq$;Hjt~!lF%WeZ-H{_3)*(1*SLPSh2GExf zhXH;^!rw3iqCg~}R3thP)B{2r2}EQV(n4ZmJY7|@u~ca3gMj6g5#pp(n__Hfr(a9$!XR2@TXuh5lXr-Q_XtktU$mLWaXE(>!x2^D&D$^Eu0o&4$gVTgXHBA}9xT z4a1kUm06Kp(DZ#w1Te5{Tqxc+tSf6G8*5g6s89PXC2i5WlA@AxiDHTG9CYc)&XNO| zBg;<5u4ip+^_%UvZTO|@<=(}_#TD}bc7Jqrw9TWI59jN{qs65l?+TA8SGMD?$+-pQ z;rbCL%lv+ktpQP3$n2(^6$Nw!KY;5o*YWq9SG^{^6^0as89g_>_8RpX#&n4((<9|0 z8%`I_gmtUdxGRJ!n@wFlBt(Ze-#7$C?ir{Vl$lb4HG@clW`n{lj;+dWUEewZe*s>< ztNI0PJPmf6g=K(8^-nXwhA-(~BDYz;ntV<8>H>tfgIh@F0&4C*M4_>kWd^;B7#(h)QkjEQbW=|`Zj_l z`Yg%-shi`)JlYygu!I_e5Eok)owsGoWiD4dILZ#=nEp!NxX$!2@my9tt%QNURn8pS zXLX#*R!>b&eglj*LVuhf%3?TeIL>*4+lRZht!86$!zdTEHvWmEpE{q(h-!}7NpsIJ zQC3Cy2h?`Xmw~TI7fE`#znRIY4jm^=1uqTP+UVCd3V6I3#(Dq};EUj?aqaEcwzb-B-b%{k zr`ylkx!XYnJwKGPD03($I$KwrT%P0737-YsZR^(>941?R?cZZS?t%(}BCE5jIsF@c zmH#Row}W2iX<4@ytOIQott<2>odl4EHiWK*f_8$qvtN|_1Z=KyKZGY`VY$GV6)nsI(`nYBNIDSRnLWfUz zNfC#1f@DP$5JdATyTlw}TvV@62Uq`s2#fH956$n~zE;!GAyIAaL$60$gD=qW%kr2x z&P3j=#0~x7+XMJ$zthc4TJOl)n$N&bv2*E>@eWn*(g}rmg=0=3_(~iJ+$5aWOykTB zz^_~@J9k@wx2#o%@EPkIdj3*J`&X|Y&l`71m<^24%}}}$3={@FooS*B z9(p&O&X*O-i?Vi8EwIbkH96f^j&D=&BQYacEu6S(c58~;4ef3ngDr)-Et{7m3a@~p zno#}YPW=wXt(!KN2Zr;j>D<2DnyrYf*C&ka^Uj%W!F!^I{;co}#8JXSz7YYyR}nvz zd$mie!=Dnp%TKG1IpBr{ndL45zeXw5rQ*8_1-{Gytjn4DpG`tCMpYTqw z+?VGzgtT)?ZC4AWHi%eG6LX3m`(n3vh|S05U4b!be9!m6Uu(w}ct z6K69sdlyRw*ARus^Utc5tkkqzwdCb^OdRYOj7%Mj%@{oG9RCpk;`8MBjM|yG8WDNg z+1k7Cc=D6{PYIsS_&>>vBt-vH#MOqML`zb_|M*EPU0oe{7#Tf0JQzG!862D~7@4`bxfz*Q7+F~8KTFWNc-gxedD7dvkp6Ea z|JEaB=3?S(<>+eVU{CZyz@-{1d!rt|p|^A3`EL#eIA~ z#a(d13Q9wvq{fsN;k>b_`EhoYvD&@b&EFQ>dAMD-&W~SF`IWU(lYNt4x50GXtWx~G zH`KZuLHfI@iprkqI2A8rqjEE}vbJRq=a;T62%GT`r)@HDW_b`Yp?|~d7?i8N894Uq z6=Z@&J$JetsD%10g$&sofRfct&a`AT$!mBdG(&#>w_bS&4|~OexP|~XJ6Ne{(c_`r zN%Glnwz@$DQ{`KzihgyP9L$DkE^UoAbrSVN03LBaV(q#vt3`WYKO3aGc`kbofZjl6( zjdb`DxpZUm;t6OeY00S_vO=SF07>WH(6Q{@t7L&=)pJq(#f5lDu)@;)<1KPY|F-zq zG#Y8(81yv<`GvGk|Ly3<381wRUDU7j3_{FUvY11l-M+@6$z{{rv>2c37=oub1jMv0 z)#|cwq!1Nk2kGKqC%NA?R*Q~^!_}*tha9WY%UTFzoA}ykJUaShjX4M~#{>x`ouqm= zt8P`}{gq-yeI5qxkA{UnU`YbUh@oSljU8NhURB^94Oy)R|U8-w9$@mAZ9pOF(`A^3Vy59B32@H#&rQVH`Xmb^hQBem_5+t(4S+HPUVWi z3N;NlDX7?y9QJgP(PHdr_bpCr3a*iSX>q$EVKKvTuymJgVO4wMRE)H=i^4BaeJVFQ z1Id|251O)VmK)f2MOED;DFyT~plWGKl;F2O+%>pQeQ19SBVK3@MTzvsY7$fBu5Xzr zh=rKunO4;6zusZy)5R` z-nf#!T!UL~%rP6OczldX-gNN22_gYLZqQU-)+dhG9nToqykSi@TeB1`%n07Mb}dp- z+<3qbDyr9tPH*Zk4B?zTEt+ zyu@3n!l5pya>2Z&qGF)YW|4SK3;tv${TJ_VZ61sg8P$rEvt+SjD250@D5vTEcGPfc z%GnByh_>`8Wa$$9=FI5~(Z7w(7pzO&{v8O;&Cdui(&v;8+FWLVIZ$=)F_jC9X^C`n z2!SsXE{knPq=fal8F}d_uO9tnY`tO3nWrsTJ#s72yh5h&_YP3;qNKqzhZAgV#nLU( zY+um)qOI9UOxE*R^3JYOJ_TM9ikqD6BbeQ9e><`{(OJaNVYK^GUHd{GqA1u=cx+^% z=UtM^Br3$x0dArK%v8xDO(2FKCSH_@!BFH#!yquJfPX{Y2^1NHmIu0NA(hKo_zOiWQQ^BODy#r|I+v3F%ak36vP;NqE&#Y`Gyk=xyO zSJa(Seb65{JN-b{Fh+V(3YaT8v6M-)(0L!?po!`w4ZUnQN$md-8d3*{D|F4;zm zpuBfpf-f%~Xq9tv1Phwu!|4&%5Ew7#`MD-K;*`LghB4A!U6QfVf{|_q!S`r9n<9RT zqnz!6r8}53IiaA@Ee!(KXgF6MplmWp|7{~pVA-I^h={Sk7ZQ#551X;{gX_6t*N0kD zV7foNP^D~C@UihEF>#=6%XC#&o-~Tot;lG?^sgGDP*KF zz&++UJlSn5DF*MGV%CV{kFELR+f0k*{#F4>TBtwW;ocZR?4ol0nZ^zlXdTxT^p73E zHeY;QvYC($-^xPxX=o>Hq+m@vCi7+SY|XB6=c?{WsJvY(9E;)vzx+IZOLU(C2Fio1 zsc6v2-M<#o_&yLRAot>K*NY>mKBw73StjvIY1MS-ahRisz86Gsrt8aMbUy+;NDJoy z;u{W{3B8gBZ2S=0TBwRLj28k(7b-_f*?v9Qd*pEm*rw$jJQB>#%+yP=0vOlDmBgB{ zEhoHIx5L!5vS6}XjASJPD_jAT!D+6aT!mcl800ioKUBXdnC6+MG*;{tL}5;uVcbUi zJB2!Tr{i>yBh5;b&q#`nL4_ioTvLHpl<5h6MBTv$e~Il0X%%8&K^jNeK~_ zwVuIWa9&Y&vQ|;isze01=}7+t(oaOC;sL2c?(UD--tSuod}j$~U1Q&-Z&d+Bn|!mo z#>wLU##wX&dyNz{J&{?AAQP!j9fBQWI5oI65Y_>#66S|e) z%A-~Ftg-hkhfnIq2zJ9}pfsztl3-yqW&l+_D#wYYdPP+p?}1nY{V7hKJaYK$#Ohgc z5ttqvLqM4zHZd*vNMt>QXVe)aahs3|4s+Ds>WzR-$2F8_W8GV%s0xt{+Trj?D^s>t z+zFD#$p)6~@FR7Usc`ciYS#2YRe(3wArcWCYb7L7)4pmI++p&3MTO|84HuZEm>y$g zp3BO@pBt3Iyc~b;B$)fsk!@?Tyco_0=HnsF*1B`AQYw~1^?Eb3liueQT%$2}evyfG z>0?|%7NDjBtd$Ih-OioIZmm6iAl*a(3o*6fswMJgZo^UluUxM@0Gk-t+2>rAsf~h- zo@w><&zk9}ceH{L&lAtCA~jRr{4&J4Eau@g?zIZq=7Nr^3UbN6odymx`k8~$N;d(M ztsN&*0!mUH4)Dz-zbWb0?s`Gc<-n74uU2=Oj-N_2+0#S!SvD8&EO(NiQbMi}d@sMk z5HUBY4DWoGWd_hV*y!ll>Og;2a^Os-jPO@j`{DtIU;7XOGE&#lAXi+lSOwUn) zrW(Q8YhNE;3F{tkqrCcp$a@leXEZuXiS@#yRc$wo3gnkV3|C5RgtFhamS61CZgPKq zNYwK9&Sg!=QW#Ct=Nc5SBh~H{5C=1#7L~(?8;+@Oo8TgI8$)A^X?Ro-je5Kj%6h^` zy-k!|t#_HB_(#WNN^oF?d|CKV^GH_gq|=njjKGQ;&InRWFFEzvgVg;Jo^}!47{6i? zt=-HkcGA;Jch)l9tRT84;AOuLpfG@+W+SPBKmsxxW0byC;Q;TM%nUrJ7ABgvwIIba zjIOQB%H?4u$dS_8J9LPQo}f?o&6C7U)caT0#lth(A1dSu`jweeaxKKM>_NyS{KcN= zn7|Az2MwAi9r~5nq73lir&)PXpaq8mOx(aPkQAg-dag}2e%1G*a8Y^NGTQCRd6T)C z1GAED<%Rol36}--(C6ve(vf?Epr!xO;F7llyDg-sdM$O6(Y*e-Xgy%ut%6CkT z)nIhA=qN;Gs#}kVq!jUOBXy8~`6b0BAHF(QsuejLUQ^wH$59o_B$i>=i0i4j&#f5k z9h5eiWBko{olpbn?P8(BPUQiH0i>V`5=`QxQ48zIRCPzy;P$&un>LRQ8W4JQNx=D^oO4k1yaZot$a&x2}&@<-c2>5C}OM&r}BanZ@fSrkRalsbVuUm z1ROis*=%?|k?2Q_yb)-z+tZ(GcHU~~P49Nj=Xm|tf6*?lLX8nkN>BN|_$~bZY_E_4 zGcihWeJdIZ+)bM|Ph!)&7uo3dv*9Gk003dmG>W!rgS=qxKkWMqu zYmdxGY$w#Z1?z}x>F~^O*%l=K8$a0mKme_dKj-9ss)ds-Yb9w|)$mL?$bp?LQcEMy z>B+fu_$Pv-{~unl0}b{>;;e9s1nqXGW~59?K5M8TO^VOBNb;BIS_XmbI1br3?x|@t z4&(01Ul&oRF(~H#5cBUQb8=!U`t7ZpDDsEM27p;3jkWAH#M!W=Q({}`v~~R=9=H=j zc2}7Jb1fmeJXmsJM3ejrM`nkhidG}6ykg9>VzgUF2Fh;E0=LIZ|GU-rPbBbJA}<@2 z{4R!{_fH=JM`Ve(Z{p=R4Pwf*Q7QT|5-EId%Xt1W_)lB~4ZKYZyZ3FM=YPP_HcP2V z6|~<7S#UQ0v*+sjO%ZQLYEC%g@V4^R30Z50ql43-C&IR_|K;}O+(AZNUHxoT&$^tv zShuz2cFo42D0N1f0xyC8aYeqvE1Yc1^B`f=?04IZ2qZa@(SLWpvs_R`o9zmdHh60) zQ6=(uC;p`e15prT4U-H>-WmcgCngyj516aoS@gwCO=<@I&x;G*x^~T>7hRLF4@b!t z^~d&S@fQ_XCJf#Pj%FE+> z^q8$K?t9XsrsvgO1q>fyY%1gHWa)Qbj6-6lC;cb! zQMEEq42j!}bmff&V*7 zSo2hua_RqtHq+zyXDHX<|7q?BG0@A5u;IIxMHHvO z(xIDPj9TdoSeV_rLXOUK=fsN1?YqxM(x&_RYN}@p_MLqUpmn{blm2JN;n8~4#UP?U zG6MeEq>fEBHN9?2^67l(vZbmnM5;A0pNg({>K}JgpD#(g5$RsOuKCJhBa3gkCO@cM z#~w!8p{^UhI&IbLT;}}`N>lPdTm6ydE*{G7{)y|<(g6PuR}~2sd~+%5$~4YIr$^@rUCdMV@)+i?Nnr|#xgS9s-Ghj2TW9?G^6RZG&F@IBUqm-IdC zs2OLn`Mh_Z(~89KQ~lpwWnK@{8hJ1g38+T#zq$(R@D<>$+yqDNM(SFI@O6nqd>u9Q z9W(y#G!p_DmIXpk^hEzj#ofSzqy*cQzp)dRs2e_4tTz3Q{`B?zee^M{Z}F2$4-D@q=utH5D?wZfhuVHM4=GofA#+fMzd}y z=%yE)gkshwip|eRx7<+Aq$Uh$$doqzf~E+zJgL;){B9`WIDQOTh2{4! zS-5j2QGs=&n8>#zT$cNy@cyu~cP?EGI5wyJ5RT~os_}Et#Gwzg?L?3*X^`*hp!&Y+ zyYtxj&Lke$(+?2Sz7LvJxsL_;z1&)5n3>K3-KhqzHd&#~U+MOSr^8v&Rel=!0&qD+ z0(On85@i&1DFjIi9n>T7BaN!kCPq(+@#U$Mos>sSNw9zvkSuJY^Fng=L&U~!2ko)| zB!_V5>@eV+B#jN_6Yx@kpAu1t^1z5B$QH&ZGXI~|9|SDu1|!3(?pV*9_)5AgilX1$ zm7^-R5!PZ68Wl8NJfI0LqK zM()W749eT{%a(Zyi0bqyCMEmfyQG~yJf6n-tbU*c^5QqtJfrSm`2B%Tvge#VQH3Y5 zqY#j)Em1D5kr-_Zi5xx`M< zucl$Ctl*%M{-p(1N0ExkYRH5(c%xIXXs08n zktwy?dtaPj=uC?909`z`9KLAS-@o)F;*;nA8INzcJ;^f2-rpTJA^C;+zUQJr2rm{1 z%(3tD-fzlsE4V}DtAg6D0$uV%L31g`qw+rkK~gX?*K1xLW^!m?b^PHG|MXxCT1)}@ zzA8)}%7?+~e3!~88%wc{pm_!Rvdn6!(xy-#VmdxA?#E6%+1?KoPTe3Z`04B+T_*0u z$0`-wMjQ^yasW~~Jx;W*gGX2gbayud* zy|d*Lp(j+gZ)FG!#l5N^iVGqUM0__em>_wbJ2;X;33VTtwN8JK7wO+9*wrmuaaQ)oJlZ)0kGXJyh9$7SOY(zJFMTL+?9766FUK>-+D@o*@jnQ9O`p z_(8$+sJeR*rl?ul-n9$9Wr!BjI%c~WD0ATkd}KF~SqJQF6xmmfCB}n1O$(RQihAvs zRg@EpM=BYm;Vn_Lg=%C;(nyp@8kzy=$U72b9~`@ z>3G@~UtUS)qb6l-ZR{$({x|cX{;ObdC{(1D!W7aNzPE5g2rG@!vPy6ay&sDMosI0D z$jwDoTx_PI6Q+%J&}pVGY+5H==@wQbW8vtiXU#JpJg4)BVJ$jZcFyIZpjy#oWzUR9 z>5*;4NC}a-6gP-gAKVdJNLhxhoNIxDI1qvTT4c#-+Qez3tXxx{1(Q$M3s-}qkh^wu z(}PDz5e?7G6N}GW3yY=ILoUZjTb~p=k9>}PKZ*5Sv%RZI4qR*YXbIC;4{leauioeR z8^y-8a0I+m=XI{NI=Vs#L8X>vmqkj(lnWl5+=nSCyIxXtr`hVSN7te! zvx2%gN|*)H$TzY2DF7-p(iqh9;AixAyj20hY|5_!Q-XCF42uxQ^x3j(GVT?78_k7p zM$!Iq92)ogovNipboVxtdtz1OlGU4s*QC=br9ZKF$X@)a(PXa;?c7* zL-&EzT5GfcsAejwm;p$qQBTAcYcv~DR$F$`q1_TsN2v+dks&;cqP!0|PekN)G1TV`JRc|Cix9s0U>#_HJ(XK;A zM+4Mont(G*Q0O;s3hV~G&y%Y&FT}pzvdt^I0fr*^GBebV@ai_UsIWcs3kXT*#O{rn z+DVv4RhEoLb$Ru;Od}xzv%M|>bfYyX(Kj~TW7PFgz$9IyC=6jtc1H{tuEx$6G-}On zM0#*UZCdcrG=@sdS_h}SUdVOfV6rV-qAZM-jfnrc3j)2wfnvu;vTA5mOXPAWedHz< zsg-3_?7CSRpDHS7iW?eaXx#dWt-CaggWESFHG)~KgpR@X;M7@*r`JOJ&%ZF~CVA{M z_g#&>VxzJC?%qJbnyI6`+2$n&Ov$x!1PfnX9=KjMOf9fUQ;Z@4pBAyIh?b43QsZO^ zh5|M};uDlc<0M15VmoXR+?#NdW9AQZU)#RSLxRta+uBu{Jycf{sxAnUS?5Yi_~n>Y zsLV7Jp#-VpWXq$5de_u9esqHx9~Vshe2UCT9FvAdh&-PiSK?OZlDvl8!0EK)Q^F;7 zq+8x`_ASZ6r$(O2Bf|G7)1t&scMEQSRo6#_gUd1JA^}u}nSlJHg=0vaWumsV9wk75 z_*Xrj9%>GdIw!+(Ijj%$V8rT-wFq;$)bbcj=x{RE^3h+R$SzWjQ8;16Ye7`*Ew*Kr zPPS;2^3v%GTXiAA7Z=8?N_Ka=so=Yz4c9Z8t?#o-FZUVP{2idQH8wq>2HFT+ju85`h1yU>1AiOFs?oMUL`z-{bSpr(errwxMof z0+0p|S5Zmv0>f%!U;7O=@&OaVjxQVGkVoAZw2sA72FtC@o-xvyUmfg)Ix408!7ur zz?u)coRp;cI=!L$MXvM zl%!Z!#0U_4A>tCgEy6!(@@1+T$wiMff&#OmJ^qxcmYqBC>A}XJ%{1@wHXRh>a-8QDzz-67DF+qzh6ib|*&- zx=R+EP+qIbyqLZu%R{k69LTCtY`3G8wS1UjJq8u`cBtBdlgMQD-|guX9Ysb1^j=7lkg?0^h<#wSL9x?u?N&CN7SXWf0?9(Abz@P2|&C6gPo^ z!rnc2I{O^0FqCw9W-MWrgR_9VYJ{Psr)ObfLmr;!$(uTg9?b*hKT8x4G@JG3g>dAl zXjNRqQTy$19 zr$w2L!bGqfb?1=t{56PC?5AgtJ{Y%!a%6JYbcziirRk@zIfK(LM3bqErx##Y19OA$ zUrUAL`kCDd3O$y~@jI?^1h6`boA^?ZntzCyGXN6k>s4Rz-h$=X0i|a!xCFH-zp;wn z8!0orhN}syZ3F_BTG7=V6=rLP`vryK>PbQ515izxzOGs97=u?3#Fs!C)T)K9BqJJ{oI#2-Q}PL}L6~NSrvBfZ4=eCWuxG!fg~>IxX9*EKcw78X~UMRzO$tV$#` zm_*UW=zU*Er+GQ-C5V?$0TdA>)Iyfcdmx2yJAX(ru!DXt<=y<)Q!Jjl#XKJG*)h)* z(Hf@U5dRo)c}aMAH35XTF!J{M<$l-ysl?ahvE!6X8-v=i3x z>Jy@Wz>_F&M6&Nf*ufei7*!{8S6kC$L${Vd|km%94?tzkj-%uOxj)#5oov?CdtP+~RR z?MRzVT=?98#u6r+qn}ZE7X5J$acoob(CowaGuZXDLnFmjd&k)p4xa)TktWvP`PC5g zlju|Fn$Q&UMZEYfi>*#p`KJeNu_M{D%Hq_5A3lsa+u6utPx~Cx0ZL{ovZGL=Og|PT zIS8a|+I>P_{C}_YX;^yYRVj+1G zPBTmO6>1%1wsbXziC-U7zE4VmpH=2^6u(;F>SPc?e#E*r)95nB`aS}Q!JtVw^_>_{h=9*7w@e z3|I4HrCHVok!PjtcXU__i~jtXZZ6^X7~OeKWBaPorMmvtr4kVlMAWN^JleHTXxQt1 zAdwWMSpR6)l@r=b;Rb(Siyg2Ut-<2#iJJ&cq2>S(IEA=r(M8F=d{w@vwNyiJEey!R zrs}uR@p~&fMDPpbelB5m=Yr$0_AuYH9QXHD-^Wv~=TA@b(Km@FyVlh4q@J{!{o{8{ z1CLENO0WHx*2AzYbiYQdZFXsZ zqK%5T&WcE3U5)-46_SX>tbIEYD%*r+Rh!oMdNE-rx>s*tj~G=lhekX^ zGqM5RtS^sYYzHdZjnmtWoJ!o^G7S4M%P!UjfUn1%DoJSeAlI1Q(&Ig!Iv|>l>6;Af zhV8_(ouUq`uWf1OwvRt@dwS8RG*}W`^V}!!;hF*fX6I>fEB90c)=4T6L-UHG^vO#} ze*mYI8|`bvAo+BY^80C9{sfbdT!LV6ZMquQ*V)zrr7;iy^E!e|f2*~?K!|sG^z*3( z(LGkGP-XFH$>L$r#!8+5XL>~z-^4VoKP;Te93@>eftv~ve=+|6UPIJMDH~F4W3#W? zZOWw_?+?e*66KG!D}?8$P5AN!8Kz4SxZucYTqQAx@enbS?V8#%~(6?^yB&?eP7tbs3G8=Xxm6cOwX2rWZQ zCe{$3)zQkLMg~+xqk_-WBlD6!ZNBFDK5GA7r|@KWdVYIZ*?bk^x{|e&(g7j@$E^w)-CEd{ztLkxQFo%0lJ@EtQs#lf=tb|W~tgb z3jCL1Z%$=1#b0}c8?L-Xe|3@GF&h=w0LIAsq}N zl09(lv;=h_ALgBz;ULP`5Uy3JEu&u-``k1j|Z7I441#=?ZfJG^TZdC+4!eu}VN5C@1(6`n75>`dkGA-qWSJh50Dfw)A zKpT}jmGJBL9b-YF_QzBrHtTyLvqLZLAu;ns1v2*NuA#!~^rHPuYSbi26dlUYazyKv zIqhVpVV^ii_sv<)%p#T{@o^9vhipOnQowBPB^WJ9YIRh9EHH7JOp#6%fC3ZcCNl5KBz{r#2V zo7kjGVXz6Up6>(JLkf`<*rDp0y5cRH_PAaTDa!}Jhyu~KK6q|zc)r)bAST{NQyI5E zqH4#UkM#`?H4%G7beb%UdhOd^tSfc4enEeijU$as(Qc%<_!U8lm?Y{NsWnNiW4%S^ zb)PUwx=QS{dKhfBFVDed^7e`JX*QDEL{D3H0q71r&%Fmh!f&QG!S z^R3VvdIwuLChzW5XD0FjgA0F)pw{+27wGZZCU5vHcQ2XdJ+?0{>7F36v!_|jmz+8` zO3KyRdab_MH{C$|=>2GKI8c#YyXBwq%y#@;{-Z?Rru}tm-84TGBhjv9Rb_Tt^~0}Z zL~9mvAPmH~I z@4;c3K}xmyhxyvJL+=|!H-JkLz;&l~+9JdG+UI*eN9la8Ou2%OkUiRBw?0Ag0-BA_ zLsv_fN8|N!dVFx%x}C7qt(bO1pLvex@?N};L&n&1-EbP{gn+lAxYlH6^3eOsxBzc) zpX>R&HUp&w_9ArEaq!`AH=K}l6vpq7uQ{!GblrX3Ee#lSCP~$8e53Pc59T53zb@jt zB%sISbUzD;VonzJ%T$McqPQ=acEIKqOspoG%4LltWe4X*p%{<;JDQNln!C%8i_Qy? zV6U&y{KSjiNqj9lIc(b-XKi?X;mQt>DO((iCR`k2%|S}Bpz{NJQuw%#U?D6;`i{=c z-LjruJxGK7upe~0#`{4uCALNNr;)Z1LGU@d5i3|-x%rH00Q3SL<=6Q}DZFd_=(4RF5h9F!TnAlV?I4>sCE^D1nDy`d~la);P#|C4Aa583x zw*9%NddZI}ao7efTs7(X`KK`b_Xc{~DVllRN1E0?V}m1QB}fdt#u41`et=WV@~Ce3 z&!e^`oKlm6CRG4iFk05a>w^a?_2-dpLi3V=st&`J3P9kM&ueeI)C`^78xoFh+Z4}p z*57#`a&+SJFi+~c3!s%;#ph>t>Bml+y+llpPK%o8>yqwOe53bMigXf%EF*hJr$cA6 zdfRmYuu47v?&Yfl{Nd))iaN)ptI6_8@6XRu?;!8*ioc9w@lYZH8|#X2R-i;{JJj4a ztRTH^BTLZNdPw;Nag(dGJ)Vo@949P7F2}7qW1FrK0#zkU=`<$hlZzCpHELT@$`{^wm|P=$&sp-P5)l&1VShyN55!JY|^O#`r8F$RoIm zvQ5xt7?kUyJY4)t5T&YyI2T^L8msQv#ER#T?I*hHx;bR1@{nH!bG~$^Uhs_`ci>TO zw=;XYaY+vQ1amoJ_w(KlGYASuEyRg~dyIL-Ui&$bb6f!h`%rf`#$iVsB;`J?J1{<* zyb$vEIQGH=HG<>Xb>+MM>}kfX7#rex+B_zDW$M2EE!%L2ku{!aQxi+Vy%XRE&Po`L z=Nf`9NIUb-83KhxSw8;2%XzJhZ25q5MP(R5+T1ci8Ytq1B2jHq)xy<6>lc6Sc9ls2 z4N%VZ1Ka85rFi<54ZW>JKb%oQ4;dt0%^UNNH8BI9OZd%*ujS+ap#C(s&EndntgyLG z?<-+l+*UqS3M{0)@uh*!gZycOGp38VPKzydTqW!5(?;V(q>&Y?#FEtZ02c;lXP>nd_TT`i8{0ZmaDY1nSMEk_>g zL_Nj_$JO2a`sePzhrnBz@6%?{*my8&NJeV&vVrWgVk)jfo4CQ(OL?nuR?A-^IR#nQ zomE{bbc(f6{7);ICFc*pj^pu@W|P$%t#8a0-`#4sQ)L;uo1%M;UdrJAy_YnL1=WT- z0q|$_0nnNO!1Z9$BGlx0NTrV3w(V)1U0FjJ^aoH4n%nIkN1`J!EOR9?9a=1!zJp0v z*h=cq2t>bxuYSv92nm*>7oj$u=Om_+Cp#NvX^hj0(gI7*Q<&wDC%Jce2J|3!ADxem zPrc@rn{HK%4+v3~%eItRqG4sfI*=qD^CnuQ=)*o;$rc1t?ZTjhpCyyU`u?CkLNIn+ zJVf1gvf3V2wp8CeG*Gbni28r&{{m45zWDY8XMIaP-FE@Y9Jpc~%kk zSusP-Som^UCAwbB_Q$4Lwf5} zIyh+GpL%L@&vqt38I1=HEkGh1qSj2eRcEEFu`54OLKVO_v)U(;^{f6hW)a4+ELp$@ zDEmlW22D`xP-|h~fZ{A+BEanIavc_#h7zB)c0#A6Z6)lNR&&9WtCIIkurkmvS%A-| z3H`!|wFxOroxqQyu zX9;(!%lqSw&}w(o+6K^y-+lI_O70vV;y8ZhEkS+JsDefPhAOA_qwvO@WwY=FUO+l} zECG23=wenlM>-|n)k3poztW?O+WkiiSO4bYE&->>!8nte=B05X`^CK|o*~G_Z|~By6?`e?Z`T#^;5OBUEnQgUfTpTzA>by$+^<-n0MN zmRy)MFB|rYF)|h3`S0r zjZ~g@gfrcfmKj@=5@HMQ*U4`<3XeMcqORL-+O7;qB3TqQ&@C6C>~7nHYU% z0U_1wG{+Q{b?t&5ZV9(jJ!8xgP%osG{c_*v)X8;YaP`z}rO@?PX(^U=uNS*o#AJr@ zeIxR-&Lzan`w-mt@eM%mvB9*5R4N*kS|Yt&;;7_qxl<^@R2JS^~ss<7qFEV8?4a3%gNvl8aHZDXW)V{#Vl z+*(Az0p6CL7DCsWrUh-?sm(4Q<$Z226Qe}X0s&_C&_&Z{*W2ekoqVK16(I^Bs$JgCo99r;P;4a4?)XInD zYOj2k0bJ*xCuXAJ-AQ-aw4pKHRRl#+9dd#oTHQl^QsF)Qjp=g!ALzkz5Jk3) zKWsX3m~rmr-KUnF)J9qi-L-Czz}sXxqUeZVxU1gWR^QKmV)5mr@gaf-Giv~N7l zqVv@nkKj6tkIHnI(s0d8&gWOX#Cd za%)q(tY8|U3sG{o_vh%^yac$4Nk}WGBUR@<-7hQ)!x;V7U~@yg{zqJZCm$K9MmpQB z?}G;~Hk`E$oJ17E-paIz}Ndg6lJ;@F#)Uc zJ)MPxC2TX<>PE6RxxZk6JOy_qB%SWicUY?R$bS80JYvrjOzgwJo+skH?P|Dy3{Rbj zg)kqs)w|LF0}p5pjWl<*W^Ig8V?r5-+hq{pC^2LRHF-6%*5<%KtT%T=GprxvC_7xw!yXPi9FCyNE5QsqKxQ<>s zi0JiUXfEs#>00?4>7m7fLeummv>Hrq7vuQwj!p|>bsft1v zBS)XeJ8gh{YXjtWhsMaALOx%8^80JFv;`?p?n2%|n;>wzDlfS=xlN3{V=UQ0qe7-^ z+ClP4j1m|l@w|Umckzm~C_+~4ue^)bH;V8x*vK7cj_qr^F%N>!aE%ub$<%OU|IWVi zc+tX8?v<|G8X<>73OHZLBkd|k)YhC{i6?QMS{j#qw!Oy>PB`a!1^)+$^IGZ|8Q1q)QvB!g{rSkxIwJ~by<@E~qUra`zm(Orvs)rik zjM;Qm7aM>2H@#x_8r8*JT<_)IEiqxxFzZ-#`b`~gHN%_cLLiehgZer|op7?H#eBYH ze3Qvf^LnoCkMF(KhA1`HUTdAyA@b5Cm9>+GlO6#GFf@Z_N!cA)|%rm%~)!n zJ=R+NbP4iw+y&fSxDiD1nqzsd1~`Bu5OY!Atq+iI#7;MZUMA#m+i+Fsuex7P9|>&@Pu*q{>xHX8{+J!_gd z7f|SnLbkHM=Id>xwcWpj0`*-9EbN-sbBBYG-bMoCAkSx(*)Vf;80{nk55 z0OPNL9qcwz`c(_y7ljBgFWGV214j9pqN{XzTHVHM0WknCUmo|1ZKSRj`1w zm{=$$>R|EE)kHE61St%~m`X-aWB_*1ZyWkcYyy;&$%T3l-7kHo`Qc49&d`TXyklZw z<^z(yppw5CuwE|`1)PnIXJumYl2Gwph6po{gp{t;J#Tl6rxHquzHY9u<&bP@uW^lPAhDPL^RWOK75U+OWC z4uos-2TG7$972C9eL>#2`;`b+L7Y~>z}QyO%|MAX7m4;ix(8)X;JR__LH>lv$hIW; z-W3^4JV;0JtK{v=cJ$wFKZDqfx=MS)l=fWug&TZG z|J#hz_ZXw}EPnB}QX}J>xEZ_66=8f9mH`T&@hcY*!)if|EIN<@#){d&*S~N78QeRlJ{qIpYRFS;5EG#AzZ?6>On=AtO+gl> zgOmIsPkZ5&CGzs<4C-wz44bX4M0NNYg=nn{4K8=s@BR8OJBRQB&tOyxVq5nYc*{fZ zzX*fqLZ+(cVc%FuO3?ebfsevLaq}_lMcH7CQ}+)%tU;y#inE^oW2I=o9ld#Tmyrbb z({U?&c?1t_AX1;}wH5Dta3B}yre`fD>aF09yaBXhxnJ@4uMI+!h!W_6jtpEQY7%|h z0BmGDxgdd|q^vGX51=xMM{H{^3a4-T$apSfm7Hf3@>_}U_q+-~P(zXAlG$)K3a^s5 zUP{BO!gNE023!*)#_jl~``?h!6Gm{-#V-G;6oc_CWM{Dj7D+tsHTN4fvbOK*hdPmM>@%_RZTe>CyK#<1scV`Vq>_URk|N<;p} z@BX)wc#1l|rTOS1vj1tEDnDX{2aSVl;%9krt-D7}jz6Q}fA$+%0VpsBxp+qCqVHhw zJ6G|$ZzWuP%AV7lN#nZ>}kU5ZyOE?9iDJa(m0~<+<#~QCm%74605;Hd5Q4jfi z&Z-zCouU{|gKhSQ*aT=NBv8Ovr?0gjkJCD`&X4yrkFoAt%XH=a>sYx=|M%WBG&BaY zm0J3%oh9p`b4O1Y+t1_z1ohhrMGRsuanr7pApiJx!FQ*Iq6bk-aK&3I{lhr za6X|F6T&_tI;mla&v`J;vXR#ENd4|p)1>qM38jJJ4_fEF1Ev@KyeCRGx7Ki`@euw2@ zQ5|+QB~hGrI$tDlc=AI8yi5bFus2;NCI=fC9)Y-jy#tv)7Lch)($^oOIbUUbFjxVg zqz)wRT7TxN_c!XqTG+GWiUjKq0GGiAP>^MFy{pr&G#(Rq{V^qKusWpQ<~o#}+Sh}q=(H#R(hMIuH6|Lh6M&tTUp?wR|~l=e*S`YpK)tBo|k zd1I1d@(*Xc7u)FNgKTQO`31#jh0OY_kxoi4Y$EIgHdJ{G` z*tvZq^}%M7g({PTv8L|elU$pDGTy9V&aF9{@(d+S5_AeTVwzn^>A7lis4zo+Qu`fp z;f*luZ zz%xc`m1e#>Woy#hJwpxqR7auMG)b0q34nLfd(-T~= zF!H~jn7zo3)2zzY2_Ur2i>WcItF_xHk2Dtr9oAuP#3*S-Z4M?$R2UBGulQkBa|Pw2 zm#z5kB?+^*oSlPvNW=O8jU*|i22bln%6y=K>aM`Rr+}@Cxx>iYL-*EM!Ng<1z7!rY2i75B8gNZe+Wpoi<+ z!Q}Ll+anOslFWnVQ@**k9d}YumqBz|xy9}FLQX|g+kfNkl)h92aUDo?$=K|defW-K z+QWSm9K(AtKbYTqv%8leCK5b@vu(YSP8|E#^5~u?-J@GRvze6taPl4Beem3y*_U2J zF?vE#YjBkRQA6isMz6ViCQ|Dm!Mxk;cOVD2yx*yM9>(y!NdHI z)6BUTirb%6&$;>saiB0(oM=midP@sL`o8C;P;qaA^Y9(#7iSmj){4MmVf;G?;-=TD zL4g^8KT4V|M7mD2SPb7GiHQiVF6X9s0PvlDnER!Bgx-7h&63xgsDZnzkt1sbCCx)U z-C#Nna*c)}ybTZl`^wz}4Y$0#HmHxLp?&WP+D((={DnRM2ir4<88+PuCh7YvV%DW%5DO8-E}N&l}`;02l`LxnT3mmjq16> z=#rKj5hDr3aF$^nDWuyLVf}%4M{_NahR#A*sN%ZK-BCc42X3UF63V019^<$uB7;mt z^FO}c(t0;@-TbVSAL@ppqfet@nm9RZ;_e|~*^O-P&st{JaDPdcud_>|q2P&Sm&a=0 zQ73A5X^W5`5bQWGDbw5MY|^M>M{OGn$ClU5-w*P^RX`G-@URbS4xk zmGT2-MEw-Sg0fW3PWve}d5Yj$3YH2~Je?Vg)ONoQCx$Rs_f9si38s=>Gxmc!F6H+l znvFN=z{O1#zHWuUAjwx8i!KAKjE}d&6MWo+E_WOoxjh-Z^jp93?ZBYD5>MZIL(_A6 zS+L;%;K__)k^0ru6wQS>+>r0IX67J$h;zs=XuQ=#=cB>FrK1)ugtte`gl zhJ80`ZabPrrs2!3e7}FJR$BeXKh>R{q~Vn-oBC<4<-7@z0_IR(Ju+|qkbjL33aV$Z z5ztcp?vaR~_U1s#MvWFIt~Ux4jSYI3ptAdhMm8b~^>vn7<-5cO6`SwX;3kiFN+}!$ z&|v(GEFN{-@0f2l=Kq&cO05J;9Snto?)A-HTv02t;L|`Von_0lhu*4`#&3H{CxWO= z_&Vjd{&)1YdP_X3M*K6|)t2M1PptEgGxL<#dPR$ z@t6l)^=ap?+!o-!d(Lo_+GB~PPs6@Sk@xO?08^{|a7eJ>`Nz{GdWIXoZ{2E|fdvPA z<6|&opx&S61zd$fAU7+1n7;cTY4cwJg_P&Blt>Dz$UKr?KHp!_!Yh{NT;xC895~s2 zPe0J*&k#`C(fTP_`jv0B<$-8;%UI(Km$xa{WtQhnV-68kOO@oO`t8 zJtP#l@x=kjJ}ExlPwM)jn#J<;*pLJRjV`$nZI$nGDAUGUAPInpIj#~X)XcK=$sr|% zx*a;ntURKy-lT>E4?6$|Y#D2S%Q|_RmNdmS-t|?0=U>Y7jScfrlr%pyKCvil`yHFB zwTx1#vX(q;vmZU1Po=^d4x8UEV&A%iOs&cAO>1)%RSeDAEM9Mk#?jLZDi8w9+Th;n zsTQ_W8qOcHPTM0A8>9UUuQ~9l{JI9p>DThilW8q^paAKADxTGHv;D`sk)r|52o1{j zUu(fJ9rRHJ9}wtPQEfg0Ghi#8?k0`s<{Ed{;-O6>XrWZ_v-X1Ji&c3gJ?v^Cck=r9GtsNxCA700*rf zzh;U2sO8a1uw^rGFKcTjwRK_$^Oq67G0 zkt$U=r{_tNwbeIi?p)Hx@GF(MGO;QZhP`#}w*iGz#Zr^=v6-1}%;1>!;;+Nw)j0hs zONyk;H+pqwnFNNf92nd{3VcbJVDr?YeLD2Kj!Y!QUhbTYo)@aCe-dpc+g> z**SB5>TYB>g~%LfO>8f8FSI-Pgt&|-sj5#^S53$OyBGao%VWk{dlU6J!1srmaRI!eO-=EUiRJ#peH-YS3rJhXStvW2wCHI05y?d=qy=#9k9CE=?D z{Kd~}i2A_6Jep&UTk!Y;6MZ9(pu1NKG#D+6#48#)g8c=C&`lYM@9Nn#V0H#aO-SKW zOKx(-1Z?7<=MIVVYh3I&TB7^+X^@hHJ%>w=>cI9&4#?j|yX@Lea{Gd@SpYJsMU5SI zop|oDl4@Cns94|~xSwJ4bX@Z(Fv;XH4qOZ67LQdgFGAP`>2xie_tO*7;#!tx&_Uh+ zLDRZKEcuu0#IjpS4U&Nwh;ProbxLT8OUb)81 zWF}Ju%1n@!U{j=UJC^giErWoVHKM`}sBD5$Vfd1ctmXb9Nu6bv%X#@B;OAS?Gu|V! z8SCzY4e#fey%obeX>!m$*jsi$RKrTa@Al|(b~vHb6_!S{b+$vUW&zY%IXha&WHY+p zl6ojAuU>M%sVoGaC0=a5z2k|#Hsl)E8x%{OJ{JgKU2P$SfED8 ze}5K*P_JFucs>~~dI$tQl)Rx_^LXYQAib_e8ly9w{^a#aQ~jSmRHsuj(RjmZ6)9vA zX>Si)Mahui&YD(4aFq#mTdn3yf(|y4o0|+OK;|UeN0qrTbO0ZXS$92Vv}&$zv(@eL zfDCZ6`v8uP?nZBoS?mb*)B#Z7B`$%*2>7s>WGl_l3(sX*5U5PstvvP;I@NOO*m((_wxa^vtTwtd)IE{9W z*6m|bq=>ClcezXS1Kll%v=qHiKDp-mP3|c7tCfJhh!6K^3q7zYUaZAIxoIwJ`tMJk z!>lIXrB#|t^zn`9EjlX=jhaHb5RE$wS(&@tolrW2tevs}1vf6R>vgxuFPz(rR-dz; z(YNfsiBg(6B6-_)dEI^8y>D#{)LUwv&7bUcx-Z_#STd+EROpc(=1Fux#JH)0zu&sC zC@2C|@(W?(+w^#i+62a4EZlCBh9#xeErNGmH?tfy?`fK9e%W_h(wX4`*as#y5ks8} znL}*Nrq*<+bGe|in(S;3Xp!LhzSy)?kyaqU-oHIOpIUk=cioKs0pKjo)@VsPEZHCB z&8>ydgyA2?#6$?Q&iKNcW^U&={#`RWZHfW9JM1^uvRjGsq|Rq zj1-e9j^@rA6I*sWlP^ZUpTdKCDbrN3gNyvewSN$90)#{&%cl^zko*j}H^(HssSp~g zx%7!|m~20kP(EkDoutENvR<_*scw5ZO-BH-0_360j(r!W&8@j(qQx|F z0)1B<~b7~_kqRlC6{{#g7fvMHE7RHMm_=E5#-Na7Ac`WyU zTzr;@HG_R$JwOqics{k@u=KvP5Ms?=usRp#@G0=_(}B{fV2wQo7H~Hn7S;vv^5G05 z@&ai;T|*)6?Q*|?4zDHq;@jEe7>C_$JdaKtuvZ^HkXW0rPJUx_v8$pN#WNY6xx{VJ zP~b1@-K_0oQMz(tbAQf0#}PX+c!7zG{n@1TT3u*ZirKS!HHg7Ng~O4t>vGXmM$IC? z+T(V?(J;6pUO?a;2(lFvM{SV~p9rS3<7UK76!h#sKz1<)vgEuEmLGKDYcjH|J6eP> z(;7!)*YMF5YY?GtSQoc_v*Ly%$JSAdV{9K~nORb|SWLK{F_c|@2La7mOvCeu$TxtX zuG`Lw6rwvrrNTzfGoeSn#}MwrGykzU%dm(sY0j}Bay)0Z*Qu_D?;F@g9!|O+P47W> zKPknmV=cz&t8hdm4AyHD3?;rtg;;r|@(LU-=er%)EH&2DJ}fH}jh{EvCL>QwMs`mPrKc&Qgrr`M8BrUFSmcHk8`E;-9o&$_&9Fb(n;ZO2eTDl;3% zBZD-2-PL$s+!PL)7+GOf#jk;!=z(|a0NtH@;8eRrCu%$$Qg+2^Q=Bu)fZlsqp1}oS z*>qivZAKMf`=l7KMX_p(VD2862oGYAhqOT}AF$qbpWG?$FXlGAkE2Bf^)Q+Gaj#lk zJX~)P_IJe;dz`>)d3~1GVkN-)Ji$g537diWRRJ^H3Ps=8b*j zZMN_)-{~?2okXQNLd}=Ctz_q}%d+lT->)LSHFROb4LIJVDM|8bD&Xp3yc6na7769N z5T)s}cf1Bjo2*7?(n`f#dppjZlF#tKvM?;_jIU4ZXgC^SjcI{;UknYghs}E)1*gl9 zVR>>@f02vbQe87hudIkiLuq(Qwcr&r3FA*i_@ElZXf?ItYCF&M`BI#9reLxhU8-g=5KmiK9DO`j5OZ2Zd z=H{$3whBvd=4y*mN0TV$EE+-h z2crVI0_sctFtq5B?zbv3)Do`U1I`><1B^377SDLpfy;&1643laH^Lq<489IYTFll{ z2pz>@$Hq*jJ&BWnvH%~U(c`E*0=Z-we?RS70-3q@BCSNjVWziv>ZIulY4p~ZOksoHe`9# z{2tn{#|pjp9^Bs>b8~-pEF30M!S(kVniO0uTHfRD-YVm3c0}KOs>aNi1~)=2mi%lV zpR>OE#ISKWl;uI05GeoF(4UZJC?<((6aeI)DEholf{(qHGQH0@nrsfrM%f6a`X@izhgq~7cn!9n>LJ@RTyqf!jD4qm zK*r3#abAbVe?zf$FObpXtTZ@B`Sqy52F7`qT8=yfDY|12(-`w!nhJyu@^g^nSTfpx ziMH`Pe!UTlM6OV)hVcAg=V;OSq!y+AT>S9|M&A+&5Bx|&H*G!pJ;kCQvjrK~)$E-@ zs-Ax$h>*)R9(F8&ZDaVUVFQ z=5sk7W+5HK*3Vno>iHymZRL=gh-3_}fJB(ozjABGjCknjUg%p6U&az)#1l6A_(54D zZo)lsy2Xd5)KlJDr4_}bK}4vjzVi|6DD6WUSUsjt)3%F1vaC*=Rn{3Pi1l@5ijWW4 z7QGai+?T~A)f~B)<2e1^yl!4H1s|W|ShoQVWtVIXP&{&T-5vXcneDvdJ00e|^-i_} zpVUhpywMQ3(rSZzrp?HIlronWA+wz9mY24m58FinhUYa>p2UVQgkog*l+DhH;-LVE zH)U%&_%efj>3x;^12OuP$~JQMl2jrYK6fW_semT)D{vkBIg^?iZ~!zvlog($v+hT& zyx=9QOOCAHR!rI#YU%K{xFYB*pU;8}YT?#d(!Ii4@fU(sCIQxK%UrN{Pycz!7u&NN zQ_CL3di7T?<$-Jmwot~7Xmaql7E{$satIa*j!Ss#?#4~J7 zE7;O;awsfyW8z9J*8^7OuhvG~Py+(Q0*2vB?tAzfmGZ;U5v0XTMmvP}nN8-GDRya{ z4qPy+W`I$ZDp_1xa!8to)xk*1$ z)ORaa#ZySHiOYWaTU<+V9FEsTVUw<4xoR$7`8~^otPnsyAL4EwH*VQ}AajJ_8*d|( zM&-LBVhbKCjzFV^liH*r*yAi|(A>Y%cGv`fq|yx$K`Oj4vsf$rGA+_%!p;B!x2!{z zp5*M#ea+%$AMA}=$jHXUz>7Ko<_5>h{ric$`y&Jnkju$@O~Mm6);Oe}RPC59=hzv$ zN^78=-$pUAM7*|=j@9BjP1a7>^0;kaee_GMSa*DW(@$ zXr{+I{9!abWFo^gWd8lnxBJ@8>J8bkXajFCZ)%8~HGKJmo=nk!S6HMOutk_BTg(wA zy41SPVezkvBdz zOq(YTxFA1y%y$NN`}rEaw0QA|42blF44bO}A)Y5<)QVp-Q;rP(z2wELspE`M+T*l8 zXuqTO&|j{+nmxz1_dVb28WMHvoX*TbAB|%fWllyIO6mFwXqwcJFxvna73ve*l^lk2!@YV@feDfPq9h2JAauNwqB3 zQ(7PujBQwafHJIGda%?czwoq8awa@?bXa*?TyMgzCLH>ZI>HOpYjxyagDY&d-}2bj zVAgTV>$qg3P-+H$t5kYI=x9}Tg1H6LCE474@sQWwrn(o&POAXNg44?vk-BYEP)g0i z!PU!U*}^Kqc+2=%7PN-2P=d{?9{Mw(iT`etZd^t$Faa_-z*d3H0zo?rj;**uR=Cvz z!B-fWdwS`4XxujyuVx()cvpref%4pxw<*<*fG)+p>cVB^6SSELLkcq_rvW)RMe4b; zXW2*%pl;vDjL6G3c)`bdMvAmZx>!;>4LENxF2UskzH%wg{R?9y|0UvxdJ8Qw3dS<;^3!OPmS#51_% zE3Ns-_n|%k51G8JnafQ}M~@@cZQ+1_Yi6yN&)?^;%x$V4tcf9o>3ghW$jU`CU!_%E@`g2$S!m{79;;O)ofz#3=ZDLDlfG(aMi33zi^s;bBfwbZF-eG4n-EX;yxE1X{Y+**)cMonsX11@@P+ zmfSEtdjx5|3n6U~3_&#G)vAPi+O$Il5N8{@asY+Imw7carQ@Lw*Y*tj^!C{jpx{ybB)UVrKcnrzkG} z2eA95a0&`032uwI!nPw8L0D)2U2;i(MkyxT^ZtxuPgJPnILJ$WNsG}Vw7V`C2n!o+ zf1Z8J@`31@7=f=RaXP74?q^^n~G3e!J z@yj~`WmsFA`v6l+>pwQ;^sK?h3P*xt>sMP#AhZ71Lc`woY{ijD^9|NV&|}1pXTHb& zC_-=W@q~y_L_ZT}3BYE3gvgRb$icewc5Ty$G_Mn_CU>lJY#Fx9;w+J#B3ls4ZCX0L z7Okn_=H@<4nOWFM*h;873l5U`QX1RvR;}Ar0vc(T#+h@j!v*7LpeuL`qtuY$Vi6zg zNK2TM0~(6gaAg+h$q3et9_Pb4C`-KY?Z+eLqJq>7>7Y|i#X1@KB9Bal<#+ha;$EZS z>i|!ffUz*Azid`vp(MJvB1SbVM!ES~*h`W8D7%DyZ|`>a_d;{+FRS!EWDFaG(q6ZEK5H~2CorY3;-vjEKE3wKsY-_^$X zw;Y4HEgW#i2sOf0{6I6Sp$`q&uEMsCL3`B4*HOxI&g4ERbaF`!%Wsw(ubh3`qvSE< z?v?5{WH)5)Bp0^hhIQmhqkiPQd-f*0oS&AjvIM)nC& zOs9Yyq52Nj*C--|u6?B_g}!wTe7sBhoR;ohEd|}1@q;h4&Uvj*y~{Xhy)Vt*B7mpg z?LvKy{*;10Oy*UJ;-msfKT2`Tg&KI7d3?nrBz}O_`VI4n)zvp~8Ah=zy1+JJZ z*8`wE*g(&{;7D{zX>r|;YvFH(-~9tElF7!w3UqrF3ll%DLA6Sev)R@TeDGASpPLOl z|Kq_2nQH=58R4FDu(d-}Gj$cpAdLF#|36>hbU3TspKH*nci7vmuGV$`2aeT^e`bff z%`sw6MYhZaQOqJfBA~{06z+c`(f?L*eo{k)@PA$ZA_XyGoIWSe4O|r$u6C44P`F4d zj&f@#HHNt3Mxx=Oi1T_Oq$$q+7Z8ex-kRmG%{XF^J|<8D{74P1_Qp}1F;VtYrOfO$ zUX*52MYi=CR82MM-uqw3{W3Fzvcb!!*{PyIgPz#_+1QEiqOB7maQQ*C{f+ZQ7^XwK zzX$3sL{(0(wQ5C8PB6~zH^%!H&J8Q>DIzBOPVyI#^MAiX5d+MOL@dtvKL|V&xj-fv z6$!vto$Y&vuqN4_j|LLV5+z1ui*DrAY z4Ex`2NW_3XlI<{C{@Xmf(gEv)dfy54pH?EsfIddrC9wYsJPsF-1#ZckL>J;et%yGL zQLKL8Km2AQD&Up`QJJ3qrxh}wkAY+wn}0r05Ewj^XyBI8zr_E;sQvp5Q6$jEcaij` z{}B`Xf5ZO&?qOHLB^UNbifwsZBYzUclJvBnu?B-igG0n?Y`XRDlpdrF4o9&t1hq1%8l!#5L7iK}X>m=i zmwhcuH)(gv0ZUP7{8wK*mW;qVGX(+j9=FIFN@4s4!G!B|!0~LiJ1&QYj@u8Qf$K#z z4>So0`1;ew8E8}KUU)oqq8>#%mq+~C;M$A&0)x@4z(hVd=@dRX8l}R>mc~0r+x(#hsZE$c< zP*moJus-f1$q3Ght%TF-KME8ByT-Q=Pg7pEuU?s2(e`_A@dvGAFR;moWBQ z95$2B7sSJhO|E_UF=@DK&$c}F)Rq{hu8%;AY+Zs;jhg-G)BKtU#X7pdaPQrd1!7|dIq%jX#XNiT;dkxCq6OXV@o!9*>2;yo9?o;B zlxk;mNBNkV%~fkG<9od|ogDDB=lxEQo}2bil+5Sewg2C{0Zv+kB_J9!Mcf)FM6$WX z=La(qW~8F}>ZnlD&Kt82ZcP@=k#jc$x$Wgx#vcI>gNT`*Z?rK^*24MB5nI#BqGzzy zg;!BWZCSC9?caga*6Avm=)#IJYOj^B)k)#qrn6CtLkvq?QEoJ>X*|Q+;ZbxPI8qLJ zyam~W;Ar#ToLs&T8GL=7$a6n#Nt3mK^zXrjej03OB--=0t)}70>Z;p~U30T1pi~I@K=;J8fZwO=H;)=&yK6vo$yd8*`Xe=cvazwrRw#%Cl%-ni zwsMOur!9t>h(p}2lR8+}eP3?>b-t&^J~?M~yZJN&>y}&MYTB;*)7I3ZBl*BmyM7l^ zM-U&sm?uXHRE(;!I~)}g3Bl1(EU(`ix`iSLuX(&IzVRi&m>J!$*~#|l^HaL+ zyKBD=T8ubFlIe}UAx`aeOw5AjFcu;h{&ThHw>)Q9}h5;$&NG80|y5Egkcdc0< zNuyKN->f*>o0`I+X8c5?TxQyK=XdeSM(y&k+%kDE#^BCzB_rBId7?5#Zos#L#ky0DrqaA*kX|Oe=JV^Wc! zctBuMGWhZzamB1Hx@WiWLX^DNj1lQq7HneviVQZ!oR&x6g0r~BG%C*`PXtI@)AV_JT>a_fj`)ePq#xv1~_&? z?VW!F2Y0&Dd@Gz$ zpC*wTzyBZdq^vW<+yrA?mc4|SdF03IC}(K6cbw0su*u`crT++uYET6@FdtN_Ob)B_ z%lOEMfW~lGO*#$#m>wlG&l)nira+p#F7Y31Pfe&_uvc@xHU0GjJ`0LxGGamyTZ8p= zA$nC7Koh!E#47)I=w8!AvL%{L(|J^;*z$l2-1*72qWoWeI*R*~XRMV}`tNB&k%v;h z4?4`K)W5$uHvrrSik;@BTQYX`VO!O;F1XQb3CuuKEK0z80Pk->|2DW!ajomUm!b|p zNiKuFPYK{fn2EvAttf#M3W3abA(6qsuWi7caGWk@pX<7koo1lgINIoLb=1 z8kbN`_Q)5nME5%_M=F1E?N7@Q3^#{_*`$(^Y#p`vo*dZc2x?G`xEei12)p@m^x4rtLd97|b_>MVURfMBwWp6=3W@yL{OIJh(%OiIA)DL3fPF4fMp>oH@1 zyP-LOJAsRw=D6{ANzO?v7I{`8*TIrMkWw?jn8wH!O2BK_w?MV{7}?w5kBpk-uJyqT zP);b(BOfs&BqZDvlxvubQ|(L?1nAojph+GB(xe8*h=EHpI&=g~Eb@o=WPXs~DmogO zG)AS9xjg?x$naf9lWi?Cf9BVT0C|2S(@9`ShZm{U^a`>%eEYt{W-`v01xCP{9zJW^ zNgG0-iIZi1nVt5c9|nF~z}#nbRafO|W>vcvlHhVG%sTS2EHEx4?iK4hcd;@Y64R+- zpgiAg|JMzge%>RuY-EF_D>%Juv8b;SQK=jrLVVZjW3uIWb~rpqaxe%a2TP#z(XBaj zG<5WW!Eel$mP^g zxGx0AnY)Q3C3D(#QCVYg@_;fL-c)i+As zudf74P`+yeub8^KNf>%cGdFBCNK@RXev!|P*2f36I3eYnqaoWW7cz3S1zT<8- zkv|-g5uz7|gl(3pR9iya9ZDE?bKcG!VztpXY)?emyB-FT`j`~on?b?$s}?}pS1Z>C z9?ZNSA;f(ZVyfuZcC!dF)FkeYvoHN_1}bToS5CJhNXtb+USSo}sTl&y{4M^APS~}T zNVJ*k6^Pr5qqr;}uV}PFzRZvX8%X2YSia+UL?77^5Qas*;T?@5#TMp`@0T-=`1aF5 z>w{0uejtvAf=mnd3vrBx#vhw&P5jYE%|qw*2eM$U3}OesWKI*tr|jLB$|#TM-!QL8 zxn3S3QbTnrJ+LvjL31BU?#(YIwhUggE<2Q(UU3~3EOCvoDwYs7>sct&b|$nqrZ)HK zjX`lSM89ntLUx$f%q+i0*kU!A9&uVUBXD|Ms8-YFL3fEQ&Y5iNcCP0-f2%Ckt211+ z%=ClkM=Gx??`>lf6&p}gq$EVUn`!qD&-ut*fq^Bj%xng}B-wk3w5=FppMxHYN-A9s z1Ab2r<`Cm(sSWJ-sJNxtJXF-javF`4wX&d4Xg^amt?lhXZYomdTf98{z^;;ryb{oQnu{x%I}tZ8r~Q_8u;#-Wl`5Wa`R+2F{fnE>wAQfxJ(+c zS;#G#F%w_}9Ry2?gN)S00Sw%JIxE4fKa9teI~B-Noed4D5nOR`Iefat(_ zDQK*lUe+7I%3@5&8cEz2zTiq~= z&#KSpdw-eCMG_Ze1;(1WyLjz^N-EgY;Bx$Z%i0tdedt;1S6&sOrju+8=@PX-V+jFX zCpU$Mn3(c0DhYRxZWy1&&mDk1rhdG?;ij_A_R%GDi(BT>W0^Xq(nZsM4YD5O&iLc~ z1h@azZPD^VJYC4<7x4_%T&@Hwx3epur52PR7Ln@{{Ql9&JQ$TdhgL~e0u)cX(d zp*7@O_UWPiuG1~>zt}XedY{jvCIqiUMQmq&MfqsGjt=~--IrGp+LPP5s{hKsVzJfT zGnT*ugP2bgq7=BdgP=C{-kYCBS%4hXVzX$iU~yM!xA5XJtHLNUlixkjZlEYGbU*iv zx4MkMw=5vaYULbnFZ{*u| z+R1$@RjI6OUsMB=>I$zNFHInkOXU>3!94sI zZEpTo#C0|cJ=K{v7r0yRK;NO+kHWp(dLp&Ajtw@kiptjN@dTfHi@^>bvhO%8hIkq? zuTW<=C-gD0Ek(*DJecllZa8NQNXDcb{Eb@leT6U{o$`f=jv!s0XrCY(Q9G2&beu`) zzF~%n!=pwiU){Iosw@~E6}uiI$gqMdQZq6>?W<{N6@}gk@0C0f8QJcxoEqvVvE37P ze8lR291Ey6MUai1w4h?^#=^_ItKhuge@E`qnde$G-X0!~K#<7P6ky18i_`QSQy`k} z4v73D3gKdA{v&wmplFZ@!gx~>u|#7GW$YsCs@?Q)En5DxRY zvE?KN7+gKj>sr+GBu@>Eg1yGRiH|w*xdw-GjU)8*^qjEah)wk3qE5CXnfcFS^oC)C z$l^$e+S=flH}1`P(HPax-LU^KkIQk{5YIgWDzk}b(fD+$zsSg|L9-Mk+{ht`vj?9m z!V6ST-Uto-^Aqf&5YR<>C1RwcTfE-Y4rp36+Rrg-Uqul|#2^!XlV+VL<``-?3RRk` z94LfqL|g~G#josXwCgtIbTs4Fd|LJ1NgU4_Wa7~y04MqOe5u+~4vq?QZ+e##8y+@` z-EzbAUND&NWW~@Ia34P#9=4BmL(JMAMuv_>Gm0;Us%NURwN_uv*#5e3YU(y1Va?_m z6I?Me+#u}?=sY!&7-}35h!JffVsu`_QM`8aT1gR}AX3}QM11lXZbR(h1lR=-b>wGC?6_(wsH{si{n> zoI}bcn}%sLR^vV}OqdIc?8g|&a9@yKryTq;@g&Z;_(8dp6qub7O}m8kR3%SR+CqJO z$Ev|_hKCOBF04}Giv(&j$yx}s@1GjYB$Pa)c!j&p`Opb2{l#hbzWgAV^PVDlNv<`L z%D_7cWv}8lNpB>i%yiN?$eA^Wsw!RX$;8_}9$!p=#Ktc8m3)vSUn=p}YY+KS#S2|xSPpwlJ} z{}zuFh-|t(T1c0fY&j<@kYzL-`_{dMj^Oor)e{M9WP$fi@PC=^lhYHh6DS{(!e z2M9$P3ET#Zh;A*_lw1;?7Kq)2>>!ok>aY@?cU_R?Z6D0ovXw9yXV>MtS!#4wWs-fU zW6wemyC=j2w$v;oEuNBl45|Uh(W~@Y9NC>QYVKqe{7p3rVlX;7_5AM~$DK zKEzc0>Al_(ZL-zz&rb5Bqr_MY@_^sK33BvsGh`d~S|HqyfATmnZbKj?d;>91tCl)Y z=g(O$BVJon3Zl;7N=r$9*%29Q(E5V1rlK6mMz#?UU3r%SPuh!4@`hdVi!*^*Q~6Q2w$^Vr&uBHc6^3E_QfGhHTJ|*(JA26Z zJN?zwKhUhDR8r9%sPT!3xSZGsb%LmGm<)r*P90<-BFCHDkKVNUun)xlS}ra|oFCU- zF>G2}E6?@0_EHWEB&3-qdyt7rYBzu)hUEIdVuPSHRvEyz`{}2lx16z#r1xpu(y<5~ z66GVZ7|CX`mrqdnLDu}swKd!!}o|e_tY)RBnq_CGSGzMNXj?w&Xz?&zk+oot<&xc z-+nFx~SngTIWTC^p zZbVcLeJIXcr?0w-=sK`60w0&xhb9ztqzEBxt}F@RDtcjdyP4`qt(|Dmadwd`^S2Jc zB&*;SZ3*I1L!<$HKzCSehl#RT369u{AT#1Xw5bSGVU#CFPZ5>r@2{$)6%0Qh_f`sS+wWQ`MURAG)-2n6A8-E82!w0}m^i@zXGn zTYRrPqxvU|_}g;%nmTd&k#@Ran{U3LWgeT)hMPnyKVfac02g3t3w#-M6AhIVDknwE zDLnZmyqN>C2BmnTQ8Z3h@=RKfw?MO%r$n76CpUGqtz)*Z(FQ45b8FuJvLEo&k&Rc0v(!YFIlZ=mnwDUti&l0pljWMJ=CwHag{-6^#H;2Ib; zU8$by_NLeJbZpK21t>PNmp|;5s-o7urcH@OWqvTj>%ra5AVEFMRk?6%7FMoX(l2$Cdi3m(IaUcl#J% z50ltexa~1+>Ss0U%Kdv5#<6KI9__+-yX!iheR%rUfwgQCY$j6+2 zzVyg7nJoJmdb8R~bH>s3*}+_+;Hf~mK;?<;H`dNPzH+&Zz~Gua5!HalZ{t8N$#fOr zE;AF3DiJZExSqEL?FKrB7(L#QS`u1T&{q(9*f*1%=UJcJU_)x+v;A9NxX4{i$qp6x zovYEZu>!;xFepS?WGsa(ZfT~E2FS1Sk(Ui|&vQS~H>W1*CBNHc^=geI;_d0mrYakB z8OC_+6+j+|{Mh!f)aX?Rrf+RN3o_WY5Y{C2XTrHu455*@ucFN@vGMZH576B(R1eA- z>?cr0mWbQ~qvhEHh>;2&ZET1Eo_c)<;YsQ_qxkH|QI9S6DMw7HLy>WgLn>m~0fE+0 zPs)QEOQE@0__F-foPv0rT*sv0^pS!OuaUM<)}*AG;dG5ew-3l+UtgB{*UyN1zV_JJ zd5FHiD8ff3Wk5YOG$nmRrvPgNCgJPb1ZSZU3bvt^XYhLtWLPht8DJ`gdkUYrP&~kiL+V)#b5H$?TV^Me+z0gn>+y>_ z$=cex6-fcAM9?EtgfqD*w)0674W>N~OM&gY0=|vdN+3AEGWaX5O}3$xg~5##-=O$k zqk(2AW<4J<0TVZRMHZq|04(yKQ_>1CFTu;hAPgLh+|SxHD6FmZiSYN>l?-PD4TeD3 zIzl2ennlIyF3TyWOxzP@=F8j|1bbQ8Vr{d22wxM*DHnOR{GXF0_mvr^h4_?Tla;_m zz{N+}6g}bgjA&&Jd1IEO2&Q&-!Q z^ScLqY|Q%C&CDw~B1I6lX$`xux59M4f41G>b)GF<#Vh*5lqjYoQ6H#+ELTN; zLng_LT9m9KS-J=x0+Y=~8CqN!-QTt)r|!isDKn-yTGkUqW|Qa`{K{w&fRt+S`0r;8 z@I>jEV^#SHZRpO;ZEhp=9D9l1K*As>XZviHxwqn@!n-zRyt2yh5iW2gSX5!2;=MTz zEHf=A?ZrHZa^iE2D**ycL~<#~!x(4Xi~2LB%GVf2$Z*KW4AE4ArqL#L=o%k_dL3fD zP}$46yrc2wk+P@~+d;u!5pe$iLUXcSuJMBRy|lJL#nzdfM59RcNcc>}4mjj@S+zXq zUAeyA;Ka8UCrgGlLDf)}s;t}wx zEoORpE?=UA>@$RR`=Z0qm6 Ee$rXU;c0yAzMysJZsi z9$T;90<|2zXd^808hzx+Z&OnyfTN=kuDW4)O)o#Ee1ihFo}49 zJ8uNhTEsbE⋘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 Date: Tue, 19 Aug 2025 12:36:27 +0300 Subject: [PATCH 03/11] Adds section about ssa. --- docs/ai-agents/port-mcp-server/prompts.md | 203 +++++++++++----------- 1 file changed, 104 insertions(+), 99 deletions(-) diff --git a/docs/ai-agents/port-mcp-server/prompts.md b/docs/ai-agents/port-mcp-server/prompts.md index 049feb15ce..cf8cf36b04 100644 --- a/docs/ai-agents/port-mcp-server/prompts.md +++ b/docs/ai-agents/port-mcp-server/prompts.md @@ -262,112 +262,117 @@ Claude will ask for any required arguments before running the prompt and will su -### Allow users to create prompts with self service action +### Self-service action to create prompts You can create a Self Service Action in Port to allow you users to create prompts themselves. -
-Self Service Action JSON (Click to expand) +1. Go to the [self-service](https://app.getport.io/self-serve) page of your portal. +2. Click on `+ New Action`. +3. Click on the `{...} Edit JSON` button. +4. Copy and paste the following JSON configuration: -```json showLineNumbers -{ - "identifier": "create_new_prompt", - "title": "Create New Prompt", - "icon": "Microservice", - "description": "Create prompt templates that appear in MCP clients (Claude, Cursor, VS Code, etc.) connected to your Port organization. Users can select prompts, provide required arguments, and get contextual AI assistance with dynamic data from Port.", - "trigger": { - "type": "self-service", - "operation": "CREATE", - "userInputs": { - "properties": { - "arguments": { - "type": "array", - "title": "Template Arguments", - "description": "Define arguments that users will provide when running this prompt. Each argument becomes available as {{argument_name}} placeholder in the template. Required arguments must be provided before prompt execution.", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "title": "Argument Name", - "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$", - "description": "The parameter name that will be substituted in the template using {{name}} syntax (e.g., 'service_name', 'environment', 'incident_id')" - }, - "description": { - "type": "string", - "title": "Argument Description", - "description": "Clear description explaining what this argument represents and how it's used in the prompt context" - }, - "is_required": { - "type": "boolean", - "title": "Is Required", - "default": false, - "description": "When true, the MCP client (Claude, Cursor, VS Code) will require this argument before executing the prompt" - } +
+ Create New Prompt action JSON (Click to expand) + + ```json showLineNumbers + { + "identifier": "create_new_prompt", + "title": "Create New Prompt", + "icon": "Microservice", + "description": "Create prompt templates that appear in MCP clients (Claude, Cursor, VS Code, etc.) connected to your Port organization. Users can select prompts, provide required arguments, and get contextual AI assistance with dynamic data from Port.", + "trigger": { + "type": "self-service", + "operation": "CREATE", + "userInputs": { + "properties": { + "arguments": { + "type": "array", + "title": "Template Arguments", + "description": "Define arguments that users will provide when running this prompt. Each argument becomes available as {{argument_name}} placeholder in the template. Required arguments must be provided before prompt execution.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Argument Name", + "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$", + "description": "The parameter name that will be substituted in the template using {{name}} syntax (e.g., 'service_name', 'environment', 'incident_id')" + }, + "description": { + "type": "string", + "title": "Argument Description", + "description": "Clear description explaining what this argument represents and how it's used in the prompt context" + }, + "is_required": { + "type": "boolean", + "title": "Is Required", + "default": false, + "description": "When true, the MCP client (Claude, Cursor, VS Code) will require this argument before executing the prompt" + } + } + } + }, + "owning_team": { + "type": "string", + "title": "Owning Team (Optional)", + "description": "The team that will own and maintain this prompt template", + "format": "entity", + "blueprint": "_team" + }, + "prompt_title": { + "type": "string", + "title": "Prompt Title", + "description": "Human-readable name for this prompt (displayed in MCP clients like Claude, Cursor, and VS Code)", + "minLength": 3, + "maxLength": 50 + }, + "prompt_template": { + "type": "string", + "title": "Prompt Template", + "description": "The prompt content with placeholders for dynamic values. Use {{argument_name}} to reference arguments (e.g., 'Analyze service {{service_name}} in {{environment}}'). Supports markdown formatting. The MCP Server substitutes values into {{}} placeholders when the prompt runs.", + "minLength": 20, + "format": "multi-line" + }, + "prompt_description": { + "type": "string", + "title": "Description", + "description": "Explain what this prompt does and when to use it. This description helps users select the right prompt from the MCP client interface.", + "minLength": 10, + "maxLength": 500, + "format": "multi-line" } - } - }, - "owning_team": { - "type": "string", - "title": "Owning Team (Optional)", - "description": "The team that will own and maintain this prompt template", - "format": "entity", - "blueprint": "_team" - }, - "prompt_title": { - "type": "string", - "title": "Prompt Title", - "description": "Human-readable name for this prompt (displayed in MCP clients like Claude, Cursor, and VS Code)", - "minLength": 3, - "maxLength": 50 }, - "prompt_template": { - "type": "string", - "title": "Prompt Template", - "description": "The prompt content with placeholders for dynamic values. Use {{argument_name}} to reference arguments (e.g., 'Analyze service {{service_name}} in {{environment}}'). Supports markdown formatting. The MCP Server substitutes values into {{}} placeholders when the prompt runs.", - "minLength": 20, - "format": "multi-line" + "required": [ + "prompt_title", + "prompt_description", + "prompt_template" + ], + "order": [ + "prompt_title", + "prompt_description", + "prompt_template", + "arguments", + "owning_team" + ], + "titles": {} }, - "prompt_description": { - "type": "string", - "title": "Description", - "description": "Explain what this prompt does and when to use it. This description helps users select the right prompt from the MCP client interface.", - "minLength": 10, - "maxLength": 500, - "format": "multi-line" + "blueprintIdentifier": "prompt" + }, + "invocationMethod": { + "type": "UPSERT_ENTITY", + "blueprintIdentifier": "prompt", + "mapping": { + "identifier": "{{ .inputs.prompt_title | ascii_downcase | gsub(\" \"; \"_\") | gsub(\"[^a-z0-9_]\"; \"\") }}", + "title": "{{ .inputs.prompt_title }}", + "team": "{{ if (.inputs.owning_team | type) == \"object\" then [.inputs.owning_team.identifier] else [] end }}", + "properties": { + "template": "{{ .inputs.prompt_template }}", + "arguments": "{{ (.inputs.arguments // []) | map({name: .name, description: .description, required: .is_required}) }}", + "description": "{{ .inputs.prompt_description }}" + } } - }, - "required": [ - "prompt_title", - "prompt_description", - "prompt_template" - ], - "order": [ - "prompt_title", - "prompt_description", - "prompt_template", - "arguments", - "owning_team" - ], - "titles": {} }, - "blueprintIdentifier": "prompt" - }, - "invocationMethod": { - "type": "UPSERT_ENTITY", - "blueprintIdentifier": "prompt", - "mapping": { - "identifier": "{{ .inputs.prompt_title | ascii_downcase | gsub(\" \"; \"_\") | gsub(\"[^a-z0-9_]\"; \"\") }}", - "title": "{{ .inputs.prompt_title }}", - "team": "{{ if (.inputs.owning_team | type) == \"object\" then [.inputs.owning_team.identifier] else [] end }}", - "properties": { - "template": "{{ .inputs.prompt_template }}", - "arguments": "{{ (.inputs.arguments // []) | map({name: .name, description: .description, required: .is_required}) }}", - "description": "{{ .inputs.prompt_description }}" - } + "requiredApproval": false } - }, - "requiredApproval": false -} -``` + ``` -
\ No newline at end of file +
\ No newline at end of file From 21597011f085cea461bafa651959121ec4385a22 Mon Sep 17 00:00:00 2001 From: Matan Grady Date: Tue, 19 Aug 2025 17:06:35 +0300 Subject: [PATCH 04/11] update AI sidebar --- docs/ai-agents/ai-agents/_category_.json | 4 + .../ai-agents/port-mcp-server/_category_.json | 4 - .../port-mcp-server/github-actions.md | 82 ------------- .../_category_.json | 2 +- docs/ai-interfaces/ai-agents/_category_.json | 4 + .../ai-agents/build-an-ai-agent.md | 18 +-- .../ai-agents/interact-with-ai-agents.md | 10 +- .../{ => ai-interfaces}/ai-agents/overview.md | 18 +-- .../ai-agents/slack-app.md | 2 +- .../port-mcp-server/_category_.json | 4 + .../port-mcp-server/available-tools.md | 2 +- .../overview-and-installation.md | 20 +++- .../port-mcp-server/prompts.md | 0 .../token-based-authenticatio.md | 111 ++++++++++++++++++ .../port-mcp-server/troubleshooting.md | 0 .../get-an-invocations-result.api.mdx | 4 +- .../invoke-a-specific-agent.api.mdx | 4 +- docs/api-reference/invoke-an-agent.api.mdx | 4 +- .../all/add-rca-context-to-ai-agents.md | 2 +- docs/guides/all/enrich-tasks-with-ai.md | 4 +- .../all/generate-incident-updates-with-ai.md | 4 +- .../all/setup-incident-manager-ai-agent.md | 18 +-- .../setup-platform-request-triage-ai-agent.md | 6 +- docs/guides/all/setup-pr-enricher-ai-agent.md | 10 +- .../all/setup-service-explorer-ai-agent.md | 8 +- .../guides/all/setup-task-manager-ai-agent.md | 18 +-- .../all/trigger-github-copilot-from-port.md | 2 +- 27 files changed, 209 insertions(+), 156 deletions(-) create mode 100644 docs/ai-agents/ai-agents/_category_.json delete mode 100644 docs/ai-agents/port-mcp-server/_category_.json delete mode 100644 docs/ai-agents/port-mcp-server/github-actions.md rename docs/{ai-agents => ai-interfaces}/_category_.json (68%) create mode 100644 docs/ai-interfaces/ai-agents/_category_.json rename docs/{ => ai-interfaces}/ai-agents/build-an-ai-agent.md (89%) rename docs/{ => ai-interfaces}/ai-agents/interact-with-ai-agents.md (97%) rename docs/{ => ai-interfaces}/ai-agents/overview.md (91%) rename docs/{ => ai-interfaces}/ai-agents/slack-app.md (98%) create mode 100644 docs/ai-interfaces/port-mcp-server/_category_.json rename docs/{ai-agents => ai-interfaces}/port-mcp-server/available-tools.md (99%) rename docs/{ai-agents => ai-interfaces}/port-mcp-server/overview-and-installation.md (91%) rename docs/{ai-agents => ai-interfaces}/port-mcp-server/prompts.md (100%) create mode 100644 docs/ai-interfaces/port-mcp-server/token-based-authenticatio.md rename docs/{ai-agents => ai-interfaces}/port-mcp-server/troubleshooting.md (100%) diff --git a/docs/ai-agents/ai-agents/_category_.json b/docs/ai-agents/ai-agents/_category_.json new file mode 100644 index 0000000000..b16a778c8f --- /dev/null +++ b/docs/ai-agents/ai-agents/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "AI agents", + "position": 1 +} diff --git a/docs/ai-agents/port-mcp-server/_category_.json b/docs/ai-agents/port-mcp-server/_category_.json deleted file mode 100644 index 9a80290e67..0000000000 --- a/docs/ai-agents/port-mcp-server/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Port MCP Server", - "position": 4 -} diff --git a/docs/ai-agents/port-mcp-server/github-actions.md b/docs/ai-agents/port-mcp-server/github-actions.md deleted file mode 100644 index ac020bf357..0000000000 --- a/docs/ai-agents/port-mcp-server/github-actions.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -sidebar_position: 5 -title: Connect Claude Code To Port MCP Server in GitHub Actions ---- - -# Connect Claude Code To Port MCP Server in GitHub Actions - -Running Claude Code inside CI/CD differs from local usage because an interactive OAuth flow is not possible. Instead, a workflow must: - -1. **Generate a short-lived Port access token** using the Client-Credentials grant (`clientId` + `clientSecret`). -2. **Start or connect to the remote MCP server** and pass the token so Claude Code can call the allowed tools. - -
-Show example workflow - -```yaml title=".github/workflows/claude-code-mcp.yml" showLineNumbers -name: Port MCP Server Demo with Claude Code -on: workflow_dispatch - -env: - PORT_MCP_URL: ${{ vars.PORT_MCP_URL }} - PORT_AUTH_BASE_URL: ${{ vars.PORT_AUTH_BASE_URL }} - -jobs: - demo: - runs-on: ubuntu-latest - permissions: - id-token: write - contents: read - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Authenticate with Port - id: port-auth - run: | - response=$(curl -s -X POST "${{ env.PORT_AUTH_BASE_URL }}/auth/access_token" \ - -H "Content-Type: application/json" \ - -d '{"clientId":"${{ secrets.PORT_CLIENT_ID }}","clientSecret":"${{ secrets.PORT_CLIENT_SECRET }}"}') - token=$(echo "$response" | jq -r '.accessToken') - 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 }}" - ] - } - } - } - 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. **Token** – The `port-auth` step exchanges your client credentials for an access token (no browser auth). -2. **Claude Code** – The action launches Claude Code, pointing `mcp-remote` at your MCP URL while injecting the token via the `Authorization` header. -3. **Prompt** – Claude can invoke only the tools you list in `allowed_tools`. - -:::caution Keep your secrets safe -Store `PORT_CLIENT_ID`, `PORT_CLIENT_SECRET`, and `ANTHROPIC_API_KEY` as **encrypted GitHub Actions secrets**. -::: - -:::tip Customise the tool list -For read-only workflows, shorten `allowed_tools` to just the query operations you need. -::: diff --git a/docs/ai-agents/_category_.json b/docs/ai-interfaces/_category_.json similarity index 68% rename from docs/ai-agents/_category_.json rename to docs/ai-interfaces/_category_.json index b20ead97ae..93807d895d 100644 --- a/docs/ai-agents/_category_.json +++ b/docs/ai-interfaces/_category_.json @@ -1,5 +1,5 @@ { - "label": "AI agents & MCP server", + "label": "AI interfaces", "position": 9, "className": "custom-sidebar-item sidebar-menu-ai-agents" } \ No newline at end of file diff --git a/docs/ai-interfaces/ai-agents/_category_.json b/docs/ai-interfaces/ai-agents/_category_.json new file mode 100644 index 0000000000..b16a778c8f --- /dev/null +++ b/docs/ai-interfaces/ai-agents/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "AI agents", + "position": 1 +} diff --git a/docs/ai-agents/build-an-ai-agent.md b/docs/ai-interfaces/ai-agents/build-an-ai-agent.md similarity index 89% rename from docs/ai-agents/build-an-ai-agent.md rename to docs/ai-interfaces/ai-agents/build-an-ai-agent.md index 094375bfe3..409cd9600f 100644 --- a/docs/ai-agents/build-an-ai-agent.md +++ b/docs/ai-interfaces/ai-agents/build-an-ai-agent.md @@ -18,7 +18,7 @@ Let's walk through the process of creating an agent that can assist your develop ## Create a new AI agent -To create a new agent, head to the AI Agents catalog page (this page will be created for you when you [activate the feature](/ai-agents/overview#access-to-the-feature)). +To create a new agent, head to the AI Agents catalog page (this page will be created for you when you [activate the feature](/ai-interfaces/ai-agents/overview#access-to-the-feature)). Click on the "New AI Agent" button and fill the form with the agent details. @@ -27,7 +27,7 @@ Click on the "New AI Agent" button and fill the form with the agent details. We recommend following the steps below. :::info MCP Server Backend Mode -AI agents can be enhanced with MCP server backend mode for expanded capabilities including intelligent catalog access and Claude model processing. This is controlled when [interacting with the agents](/ai-agents/interact-with-ai-agents) through widgets and API calls, not in the agent configuration itself. +AI agents can be enhanced with MCP server backend mode for expanded capabilities including intelligent catalog access and Claude model processing. This is controlled when [interacting with the agents](/ai-interfaces/ai-agents/interact-with-ai-agents) through widgets and API calls, not in the agent configuration itself. ::: ### Step 1: Define your agent's purpose @@ -56,7 +56,7 @@ For example: Pay attention to relationships between entities to ensure your agent can provide comprehensive answers. :::tip Enhanced access with MCP server backend -When using [MCP server backend mode](/ai-agents/interact-with-ai-agents) during interactions, the agent can intelligently access your entire catalog regardless of configured blueprints, providing more comprehensive answers. +When using [MCP server backend mode](/ai-interfaces/ai-agents/interact-with-ai-agents) during interactions, the agent can intelligently access your entire catalog regardless of configured blueprints, providing more comprehensive answers. ::: ### Step 3: Configure actions (optional) @@ -155,7 +155,7 @@ Choose conversation starters that: When you feel your agent is ready: 1. Set its status to "Active". -2. Start interacting with it through the [available interfaces](/ai-agents/interact-with-ai-agents). +2. Start interacting with it through the [available interfaces](/ai-interfaces/ai-agents/interact-with-ai-agents). ## Evaluating your agent performance @@ -166,7 +166,7 @@ Continuous evaluation and improvement are essential for maintaining effective AI 3. **Analyze execution plans**: Examine how the agent processes requests by reviewing the execution plan and tool calls for specific invocations. This helps identify where improvements are needed. 4. **Refine the prompt**: Update your agent's prompt based on your findings to address common issues. -For more details on how to view execution plans and analyze agent behavior, see [Interact with AI agents](/ai-agents/interact-with-ai-agents). +For more details on how to view execution plans and analyze agent behavior, see [Interact with AI agents](/ai-interfaces/ai-agents/interact-with-ai-agents). ## Examples @@ -194,7 +194,7 @@ Your goal is to help developers initiate and track deployments to various enviro ## Formatting the agent response To format the agent's response, you can specify the desired format in its prompt. For optimal results when using the UI, it's recommended to request a markdown format response. This allows for better presentation and readability of the information provided by the agent. -When sending messages through Slack, our [Slack app](/ai-agents/slack-app) convert the markdown format into a Slack compatible formatting. +When sending messages through Slack, our [Slack app](/ai-interfaces/ai-agents/slack-app) convert the markdown format into a Slack compatible formatting. ### Example of a Markdown Response ```markdown @@ -208,7 +208,7 @@ From [john-123](https://github.com/john-123)
I don't see an option to add an AI agent (Click to expand) -Make sure you have [access to the AI agents feature](/ai-agents/overview#access-to-the-feature). Note that it's currently in closed beta and requires special access. If you believe you should have access, please contact our support. +Make sure you have [access to the AI agents feature](/ai-interfaces/ai-agents/overview#access-to-the-feature). Note that it's currently in closed beta and requires special access. If you believe you should have access, please contact our support.
@@ -244,7 +244,7 @@ AI agents in Port can search, group, and index entities in your Port instance. H - Sequential automations run as Admin. :::info Enhanced capabilities with MCP server backend -When using [MCP server backend mode](/ai-agents/interact-with-ai-agents) during interactions, many of these limitations are reduced as the agent gains access to enhanced tools and broader data access capabilities. +When using [MCP server backend mode](/ai-interfaces/ai-agents/interact-with-ai-agents) during interactions, many of these limitations are reduced as the agent gains access to enhanced tools and broader data access capabilities. :::
@@ -257,4 +257,4 @@ When configuring your agent's actions, make sure you select the "approval" optio ## Security considerations AI agents in Port are designed with security and privacy as a priority. -For more information on security and data handling, see our [AI agents overview](/ai-agents/overview#security-and-data-handling). \ No newline at end of file +For more information on security and data handling, see our [AI agents overview](/ai-interfaces/ai-agents/overview#security-and-data-handling). \ No newline at end of file diff --git a/docs/ai-agents/interact-with-ai-agents.md b/docs/ai-interfaces/ai-agents/interact-with-ai-agents.md similarity index 97% rename from docs/ai-agents/interact-with-ai-agents.md rename to docs/ai-interfaces/ai-agents/interact-with-ai-agents.md index 137da0ce12..8bbaf3caa9 100644 --- a/docs/ai-agents/interact-with-ai-agents.md +++ b/docs/ai-interfaces/ai-agents/interact-with-ai-agents.md @@ -80,7 +80,7 @@ The Slack integration provides the most natural way to interact with Port's AI a You can interact with agents in two ways: -1. **Direct messaging** the [Port Slack app](/ai-agents/slack-app). This will use the agent router. +1. **Direct messaging** the [Port Slack app](/ai-interfaces/ai-agents/slack-app). This will use the agent router. 2. **Mentioning** the app in any channel it's invited to. This will also use the agent router. When you send a message, the app will: @@ -277,7 +277,7 @@ AI agents are standard Port entities belonging to the `_ai_agent` blueprint. Thi You can discover available AI agents in your Port environment in a couple of ways: -1. **AI Agents Catalog Page**: Navigate to the AI Agents catalog page in Port. This page lists all the agents that have been created in your organization. For more details on creating agents, refer to the [Build an AI agent guide](/ai-agents/build-an-ai-agent). +1. **AI Agents Catalog Page**: Navigate to the AI Agents catalog page in Port. This page lists all the agents that have been created in your organization. For more details on creating agents, refer to the [Build an AI agent guide](/ai-interfaces/ai-agents/build-an-ai-agent). 2. **Via API**: Programmatically retrieve a list of all AI agents using the Port API. AI agents are entities of the `_ai_agent` blueprint. You can use the [Get all entities of a blueprint API endpoint](https://docs.port.io/api-reference/get-all-entities-of-a-blueprint) to fetch them, specifying `_ai_agent` as the blueprint identifier.
@@ -379,7 +379,7 @@ For **MCP Server Backend Mode:** AI agent interactions in Port are designed with security and privacy as a priority. -For more information on security and data handling, see our [AI agents overview](/ai-agents/overview#security-and-data-handling). +For more information on security and data handling, see our [AI agents overview](/ai-interfaces/ai-agents/overview#security-and-data-handling). ## Troubleshooting & FAQ @@ -409,7 +409,7 @@ We're working on adding direct interaction through the Port UI in the future. Each agent has optional conversation starters to help you understand what it can help with. The questions you can ask depend on which agents were built in your organization. -For information on building agents with specific capabilities, see our [Build an AI agent](/ai-agents/build-an-ai-agent) guide. +For information on building agents with specific capabilities, see our [Build an AI agent](/ai-interfaces/ai-agents/build-an-ai-agent) guide.
@@ -435,7 +435,7 @@ Remember that AI agents are constantly learning and improving, but they're not i My agent isn't responding in Slack (Click to expand) Ensure that: -- The [Port Slack app](/ai-agents/slack-app) is properly installed in your workspace. +- The [Port Slack app](/ai-interfaces/ai-agents/slack-app) is properly installed in your workspace. - The app has been invited to the channel where you're mentioning it. - You're correctly mentioning the app (@Port). - You've completed the authentication flow with the app. diff --git a/docs/ai-agents/overview.md b/docs/ai-interfaces/ai-agents/overview.md similarity index 91% rename from docs/ai-agents/overview.md rename to docs/ai-interfaces/ai-agents/overview.md index 7db895db38..5fab939913 100644 --- a/docs/ai-agents/overview.md +++ b/docs/ai-interfaces/ai-agents/overview.md @@ -43,7 +43,7 @@ When using the MCP server backend mode, your AI agents gain: - **Broader tool access**: Uses all read-only tools available in the MCP server for comprehensive insights - **Smarter action selection**: Still respects your configured allowed actions while providing better context -Your existing agents can immediately benefit from these enhancements by enabling the MCP server backend mode when [interacting with them](/ai-agents/interact-with-ai-agents) through widgets and API calls. +Your existing agents can immediately benefit from these enhancements by enabling the MCP server backend mode when [interacting with them](/ai-interfaces/ai-agents/interact-with-ai-agents) through widgets and API calls. ### Example use cases @@ -61,18 +61,18 @@ Your existing agents can immediately benefit from these enhancements by enabling To start working with AI agents, follow these steps: 1. **Apply for access** - Submit your application via [this form](https://forms.gle/krhMY7c9JM8MyJJf7). -2. **Access the feature** - If accepted, you will be able to [activate the AI agents](/ai-agents/overview#access-to-the-feature) in your Port organization. -3. **Build your agents** - [Create custom agents](/ai-agents/build-an-ai-agent) to meet your developers' needs. -4. **Interact with your agents** - Engage with your agents by following our [interaction guide](/ai-agents/interact-with-ai-agents). +2. **Access the feature** - If accepted, you will be able to [activate the AI agents](/ai-interfaces/ai-agents/overview#access-to-the-feature) in your Port organization. +3. **Build your agents** - [Create custom agents](/ai-interfaces/ai-agents/build-an-ai-agent) to meet your developers' needs. +4. **Interact with your agents** - Engage with your agents by following our [interaction guide](/ai-interfaces/ai-agents/interact-with-ai-agents). ## Customization and control -[Build and customize](/ai-agents/build-an-ai-agent) your AI agents: +[Build and customize](/ai-interfaces/ai-agents/build-an-ai-agent) your AI agents: - Define which data sources your agents can access. - Determine what actions your agents can assist with. - Set permissions for who can use specific agents. - Configure how agents integrate with your workflows. -- Choose between standard and MCP server backend modes when [interacting with agents](/ai-agents/interact-with-ai-agents). +- Choose between standard and MCP server backend modes when [interacting with agents](/ai-interfaces/ai-agents/interact-with-ai-agents). ## Security and data handling @@ -101,9 +101,9 @@ Your organization now has the system blueprints required for the feature to work ## Data Model The data model of AI agents includes two main blueprints: -1. **AI agents** - The agents themselves that you can interact with. You can build new ones and customize them as you wish. Learn more in our [Build an AI agent](/ai-agents/build-an-ai-agent) guide. +1. **AI agents** - The agents themselves that you can interact with. You can build new ones and customize them as you wish. Learn more in our [Build an AI agent](/ai-interfaces/ai-agents/build-an-ai-agent) guide. -2. **AI invocations** - Each interaction made with an AI agent is recorded as an invocation. This acts as a log of everything going through your AI agents so you can monitor and improve them over time. Learn more in our [Interact with AI agents](/ai-agents/interact-with-ai-agents) guide. +2. **AI invocations** - Each interaction made with an AI agent is recorded as an invocation. This acts as a log of everything going through your AI agents so you can monitor and improve them over time. Learn more in our [Interact with AI agents](/ai-interfaces/ai-agents/interact-with-ai-agents) guide. ## Relevant guides @@ -139,7 +139,7 @@ Port AI supports two primary interaction types:
How do users interact with Port AI? (Click to expand) -- Primary interface is through our [Slack app](/ai-agents/slack-app). +- Primary interface is through our [Slack app](/ai-interfaces/ai-agents/slack-app). - Full [API availability](/api-reference/port-api/).
diff --git a/docs/ai-agents/slack-app.md b/docs/ai-interfaces/ai-agents/slack-app.md similarity index 98% rename from docs/ai-agents/slack-app.md rename to docs/ai-interfaces/ai-agents/slack-app.md index 40290311bd..fc69d2d909 100644 --- a/docs/ai-agents/slack-app.md +++ b/docs/ai-interfaces/ai-agents/slack-app.md @@ -31,7 +31,7 @@ This can be used to get quick answers to questions about your resources, such as - To install the Slack app, you will first need to apply for access to Port's AI program by filling out [this form](https://forms.gle/krhMY7c9JM8MyJJf7). - To interact with AI agents, you need to have at least one agent deployed in your portal. - See the [Build an AI agent](https://docs.port.dev/ai-agents/build-an-ai-agent) page to learn more. + See the [Build an AI agent](https://docs.port.dev/ai-interfaces/ai-agents/build-an-ai-agent) page to learn more. ## Installation diff --git a/docs/ai-interfaces/port-mcp-server/_category_.json b/docs/ai-interfaces/port-mcp-server/_category_.json new file mode 100644 index 0000000000..a49101f8b4 --- /dev/null +++ b/docs/ai-interfaces/port-mcp-server/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "MCP server", + "position": 2 +} diff --git a/docs/ai-agents/port-mcp-server/available-tools.md b/docs/ai-interfaces/port-mcp-server/available-tools.md similarity index 99% rename from docs/ai-agents/port-mcp-server/available-tools.md rename to docs/ai-interfaces/port-mcp-server/available-tools.md index fc9d5d82fe..1b70f8c7c5 100644 --- a/docs/ai-agents/port-mcp-server/available-tools.md +++ b/docs/ai-interfaces/port-mcp-server/available-tools.md @@ -1,5 +1,5 @@ --- -sidebar_position: 2 +sidebar_position: 3 title: Tools --- diff --git a/docs/ai-agents/port-mcp-server/overview-and-installation.md b/docs/ai-interfaces/port-mcp-server/overview-and-installation.md similarity index 91% rename from docs/ai-agents/port-mcp-server/overview-and-installation.md rename to docs/ai-interfaces/port-mcp-server/overview-and-installation.md index 5a1d9f1a32..3740cae615 100644 --- a/docs/ai-agents/port-mcp-server/overview-and-installation.md +++ b/docs/ai-interfaces/port-mcp-server/overview-and-installation.md @@ -1,7 +1,7 @@ --- sidebar_position: 1 title: Overview & Installation -slug: /ai-agents/port-mcp-server +slug: /ai-interfaces/ai-agents/port-mcp-server --- import Tabs from "@theme/Tabs" @@ -28,7 +28,7 @@ import TabItem from "@theme/TabItem" The Port Model Context Protocol (MCP) Server acts as a bridge, enabling Large Language Models (LLMs)—like those powering Claude, Cursor, or GitHub Copilot—to interact directly with your Port.io developer portal. This allows you to leverage natural language to query your software catalog, analyze service health, manage resources, and even streamline development workflows, all from your preferred interfaces. :::info AI Agents vs. MCP Server -The Port MCP Server is currently in open beta and provides significant standalone value, independent of our [AI Agents feature](/ai-agents/overview). Port AI Agents are currently in closed beta with limited access, while the MCP Server gives you immediate access to streamline building in Port, query your catalog, analyze service health, and streamline development workflows using natural language. +The Port MCP Server is currently in open beta and provides significant standalone value, independent of our [AI Agents feature](/ai-interfaces/ai-agents/overview). Port AI Agents are currently in closed beta with limited access, while the MCP Server gives you immediate access to streamline building in Port, query your catalog, analyze service health, and streamline development workflows using natural language. While the MCP Server can interact with Port AI Agents when available, the core MCP functionality can be used freely without requiring access to the closed beta AI Agents feature. ::: @@ -301,3 +301,19 @@ The README provides the latest configuration details and examples for different ::: + +## Token-based authentication + +You can also connect using token-based authentication for automated environments like CI/CD pipelines where interactive authentication isn't possible: + +```bash +curl -X POST "https://api.getport.io/v1/auth/access_token" \ + -H "Content-Type: application/json" \ + -d '{"clientId":"YOUR_CLIENT_ID","clientSecret":"YOUR_CLIENT_SECRET"}' +``` + +For complete examples and detailed setup instructions, see our [token-based authentication guide](./token-based-authentication). + +## Connecting to AI Agents + +To connect the Port MCP server to AI agents in CI/CD environments or other automated contexts where interactive authentication isn't possible, see our [token-based authentication](./token-based-authentication). diff --git a/docs/ai-agents/port-mcp-server/prompts.md b/docs/ai-interfaces/port-mcp-server/prompts.md similarity index 100% rename from docs/ai-agents/port-mcp-server/prompts.md rename to docs/ai-interfaces/port-mcp-server/prompts.md diff --git a/docs/ai-interfaces/port-mcp-server/token-based-authenticatio.md b/docs/ai-interfaces/port-mcp-server/token-based-authenticatio.md new file mode 100644 index 0000000000..30f7f51882 --- /dev/null +++ b/docs/ai-interfaces/port-mcp-server/token-based-authenticatio.md @@ -0,0 +1,111 @@ +--- +sidebar_position: 2 +title: Token-based connection +--- + +# Token-based connection to Port MCP server + +When integrating the Port MCP Server with AI agents in automated environments like CI/CD pipelines, the authentication approach differs from interactive local usage. This guide explains how to establish secure connections between AI agents and the Port MCP Server without requiring user interaction. + +## The Challenge + +Interactive OAuth flows that work well for local development become problematic in automated environments because: + +- **No User Interaction**: CI/CD pipelines and automated agents can't handle browser-based authentication flows +- **Security Requirements**: Credentials must be managed securely without exposing them in logs or configurations +- **Token Management**: Short-lived tokens are preferred for security, but must be programmatically generated + +## The Solution + +The Port MCP Server supports programmatic authentication using the Client Credentials flow, which enables AI agents to: + +1. **Generate short-lived access tokens** using your Port client credentials (`clientId` + `clientSecret`) +2. **Connect to the remote MCP server** with the generated token for secure API access +3. **Invoke Port tools** through the MCP interface without user intervention + +This approach maintains security while enabling powerful automation capabilities. + +## Example: Claude Code in GitHub Actions + +Here's a complete example showing how to connect Claude Code to the Port MCP Server within a GitHub Actions workflow: + +
+Show example workflow + +```yaml title=".github/workflows/claude-code-mcp.yml" showLineNumbers +name: Port MCP Server Demo with Claude Code +on: workflow_dispatch + +env: + PORT_MCP_URL: ${{ vars.PORT_MCP_URL }} + PORT_AUTH_BASE_URL: ${{ vars.PORT_AUTH_BASE_URL }} + +jobs: + demo: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Authenticate with Port + id: port-auth + run: | + response=$(curl -s -X POST "${{ env.PORT_AUTH_BASE_URL }}/auth/access_token" \ + -H "Content-Type: application/json" \ + -d '{"clientId":"${{ secrets.PORT_CLIENT_ID }}","clientSecret":"${{ secrets.PORT_CLIENT_SECRET }}"}') + token=$(echo "$response" | jq -r '.accessToken') + 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 }}" + ] + } + } + } + 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 using the Client Credentials flow +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. **Tool Access** – Claude Code can invoke only the specific Port tools listed in `allowed_tools`, ensuring controlled access to your Port instance +4. **Execution** – The AI agent executes the provided prompt using the available Port tools to query your software catalog + +:::tip Customize your integration +For read-only workflows, limit `allowed_tools` to just the query operations you need. +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: + +### General Integration Steps + +1. **Obtain Port Credentials**: Create a client ID and secret in your Port dashboard. +2. **Generate Access Token**: Use the Client Credentials flow to get a short-lived token. +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. diff --git a/docs/ai-agents/port-mcp-server/troubleshooting.md b/docs/ai-interfaces/port-mcp-server/troubleshooting.md similarity index 100% rename from docs/ai-agents/port-mcp-server/troubleshooting.md rename to docs/ai-interfaces/port-mcp-server/troubleshooting.md diff --git a/docs/api-reference/get-an-invocations-result.api.mdx b/docs/api-reference/get-an-invocations-result.api.mdx index 91b784ad1f..b4a60cee04 100644 --- a/docs/api-reference/get-an-invocations-result.api.mdx +++ b/docs/api-reference/get-an-invocations-result.api.mdx @@ -1,7 +1,7 @@ --- id: get-an-invocations-result title: "Get an invocation's result" -description: "This route allows you to get a specific invocation result.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-agents/overview)." +description: "This route allows you to get a specific invocation result.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-interfaces/ai-agents/overview)." sidebar_label: "Get an invocation's result" hide_title: true hide_table_of_contents: true @@ -36,7 +36,7 @@ import Heading from "@theme/Heading"; -This route allows you to get a specific invocation result.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-agents/overview). +This route allows you to get a specific invocation result.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-interfaces/ai-agents/overview).
To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-agents/overview)." +description: "This route allows you to create an agent's invocation.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-interfaces/ai-agents/overview)." sidebar_label: "Invoke a specific agent" hide_title: true hide_table_of_contents: true @@ -36,7 +36,7 @@ import Heading from "@theme/Heading"; -This route allows you to create an agent's invocation.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-agents/overview). +This route allows you to create an agent's invocation.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-interfaces/ai-agents/overview).
To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-agents/overview)." +description: "This route allows you to create an invocation without choosing a specific agent.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-interfaces/ai-agents/overview)." sidebar_label: "Invoke an agent" hide_title: true hide_table_of_contents: true @@ -36,7 +36,7 @@ import Heading from "@theme/Heading"; -This route allows you to create an invocation without choosing a specific agent.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-agents/overview). +This route allows you to create an invocation without choosing a specific agent.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-interfaces/ai-agents/overview). @@ -135,7 +135,7 @@ Once the widget is set up, you can: -The Slack integration provides a natural way to interact with the Incident Manager agent. Before using this method, ensure you have installed and configured the **[Port AI Assistant Slack App](/ai-agents/slack-app)**. +The Slack integration provides a natural way to interact with the Incident Manager agent. Before using this method, ensure you have installed and configured the **[Port AI Assistant Slack App](/ai-interfaces/ai-agents/slack-app)**. You can interact with the Incident Manager agent in two ways: 1. **Direct message** the Port AI Assistant. @@ -180,12 +180,12 @@ To get the most out of your Incident Manager agent: 1. **Try it out**: Start with simple queries and see how the agent responds. 2. **Add context**: If the response isn't what you expected, try asking again with more details. -3. **Troubleshoot**: If you're still not getting the right answers, check our [troubleshooting guide](/ai-agents/interact-with-ai-agents#troubleshooting--faq) for common issues and solutions. +3. **Troubleshoot**: If you're still not getting the right answers, check our [troubleshooting guide](/ai-interfaces/ai-agents/interact-with-ai-agents#troubleshooting--faq) for common issues and solutions. ## Possible enhancements You can further enhance the Incident Manager setup by: -- **Integration expansion**: [Add more data sources](/ai-agents/build-an-ai-agent#step-2-configure-data-access) like Opsgenie or ServiceNow. -- **Automated notifications**: [Configure the agent](/ai-agents/interact-with-ai-agents#actions-and-automations) to proactively notify about incident updates or escalations. -- **Custom conversation starters**: Add organization-specific queries to the [conversation starters](/ai-agents/build-an-ai-agent#step-5-add-conversation-starters). -- **Monitor and improve**: [Check how your developers are interacting](/ai-agents/interact-with-ai-agents#ai-interaction-details) with the agent and improve it according to feedback. +- **Integration expansion**: [Add more data sources](/ai-interfaces/ai-agents/build-an-ai-agent#step-2-configure-data-access) like Opsgenie or ServiceNow. +- **Automated notifications**: [Configure the agent](/ai-interfaces/ai-agents/interact-with-ai-agents#actions-and-automations) to proactively notify about incident updates or escalations. +- **Custom conversation starters**: Add organization-specific queries to the [conversation starters](/ai-interfaces/ai-agents/build-an-ai-agent#step-5-add-conversation-starters). +- **Monitor and improve**: [Check how your developers are interacting](/ai-interfaces/ai-agents/interact-with-ai-agents#ai-interaction-details) with the agent and improve it according to feedback. diff --git a/docs/guides/all/setup-platform-request-triage-ai-agent.md b/docs/guides/all/setup-platform-request-triage-ai-agent.md index e091cee002..fbbbd109b2 100644 --- a/docs/guides/all/setup-platform-request-triage-ai-agent.md +++ b/docs/guides/all/setup-platform-request-triage-ai-agent.md @@ -24,8 +24,8 @@ This guide will walk you through setting up a "Platform Request Triage" AI agent ## Prerequisites This guide assumes you have: -- A Port account with the [AI agents feature enabled](/ai-agents/overview#access-to-the-feature). -- The [Port Slack App](/ai-agents/slack-app) installed and configured. +- A Port account with the [AI agents feature enabled](/ai-interfaces/ai-agents/overview#access-to-the-feature). +- The [Port Slack App](/ai-interfaces/ai-agents/slack-app) installed and configured. ## Setup @@ -253,7 +253,7 @@ This action will be used by the AI agent to create new platform requests. The credential name follows the pattern `__SLACK_APP_BOT_TOKEN_Txxxxxxxxxx`. - **Channel ID**: Replace `YOUR_CHANNEL_ID` with the ID of the Slack channel where you want to send notifications. You can also use a JQ expression to dynamically select the channel. - For more details, refer to the [Port Slack App](/ai-agents/slack-app) documentation. + For more details, refer to the [Port Slack App](/ai-interfaces/ai-agents/slack-app) documentation. ::: 5. Click `Create`. diff --git a/docs/guides/all/setup-pr-enricher-ai-agent.md b/docs/guides/all/setup-pr-enricher-ai-agent.md index 28983175fe..57fdfa2960 100644 --- a/docs/guides/all/setup-pr-enricher-ai-agent.md +++ b/docs/guides/all/setup-pr-enricher-ai-agent.md @@ -26,7 +26,7 @@ By the end of this guide, your developers will receive automated, contextual com ## Prerequisites This guide assumes you have: -- A Port account with the [AI agents feature enabled](/ai-agents/overview#access-to-the-feature). +- A Port account with the [AI agents feature enabled](/ai-interfaces/ai-agents/overview#access-to-the-feature). - Appropriate permissions to create and configure AI agents. - [GitHub integration](/build-your-software-catalog/sync-data-to-catalog/git/github/) configured in your Port instance. - [Jira integration](/build-your-software-catalog/sync-data-to-catalog/project-management/jira/) configured in your Port instance. @@ -443,13 +443,13 @@ To get the most out of your PR Enricher agent: 5. **Test the workflow**: Create test pull requests to verify the entire flow works as expected. -6. **Troubleshoot**: If you're not getting the expected results, check our [troubleshooting guide](/ai-agents/interact-with-ai-agents#troubleshooting--faq) for common issues and solutions. +6. **Troubleshoot**: If you're not getting the expected results, check our [troubleshooting guide](/ai-interfaces/ai-agents/interact-with-ai-agents#troubleshooting--faq) for common issues and solutions. ## Possible enhancements You can further enhance the PR Enricher setup by: -- **Adding more data sources** like PagerDuty for incident context or [additional Git providers](/ai-agents/build-an-ai-agent#step-2-configure-data-access) for broader repository visibility. -- **Configuring automated actions** such as [reviewer assignment, PR labeling, or creating follow-up Jira tickets](/ai-agents/interact-with-ai-agents#actions-and-automations). -- **Customizing risk assessment criteria** to align with your organization's specific guidelines and [monitoring usage patterns](/ai-agents/interact-with-ai-agents#ai-interaction-details). +- **Adding more data sources** like PagerDuty for incident context or [additional Git providers](/ai-interfaces/ai-agents/build-an-ai-agent#step-2-configure-data-access) for broader repository visibility. +- **Configuring automated actions** such as [reviewer assignment, PR labeling, or creating follow-up Jira tickets](/ai-interfaces/ai-agents/interact-with-ai-agents#actions-and-automations). +- **Customizing risk assessment criteria** to align with your organization's specific guidelines and [monitoring usage patterns](/ai-interfaces/ai-agents/interact-with-ai-agents#ai-interaction-details). diff --git a/docs/guides/all/setup-service-explorer-ai-agent.md b/docs/guides/all/setup-service-explorer-ai-agent.md index 0a8e2afd4c..b5da8a646b 100644 --- a/docs/guides/all/setup-service-explorer-ai-agent.md +++ b/docs/guides/all/setup-service-explorer-ai-agent.md @@ -25,7 +25,7 @@ This guide will walk you through setting up a "Service Explorer" AI agent in Por ## Prerequisites This guide assumes you have: -- A Port account with the [AI agents feature enabled](/ai-agents/overview#access-to-the-feature). +- A Port account with the [AI agents feature enabled](/ai-interfaces/ai-agents/overview#access-to-the-feature). - Appropriate permissions to create and configure AI agents. - [GitHub integration](/build-your-software-catalog/sync-data-to-catalog/git/github/) installed. - **Optional but recommended integrations for richer context**: @@ -35,7 +35,7 @@ This guide assumes you have: ## Set up -To create a Service Explorer AI agent in Port, we'll need to configure two main components as described in our [Build an AI agent](/ai-agents/build-an-ai-agent) guide: +To create a Service Explorer AI agent in Port, we'll need to configure two main components as described in our [Build an AI agent](/ai-interfaces/ai-agents/build-an-ai-agent) guide: - The data sources it will use to answer questions. - The agent configuration that defines its capabilities and conversation starters. @@ -115,7 +115,7 @@ Keep in mind that there is a trade-off between context richness and response tim ## Interact with the Service Explorer -You can interact with the Service Explorer AI agent in [several ways](/ai-agents/interact-with-ai-agents). This guide will demonstrate the two main ways. +You can interact with the Service Explorer AI agent in [several ways](/ai-interfaces/ai-agents/interact-with-ai-agents). This guide will demonstrate the two main ways. @@ -136,7 +136,7 @@ Once the widget is set up, you can ask questions directly in the chat field. -The Slack integration provides a natural way to interact with the Service Explorer agent. Before using this method, ensure you have installed and configured the **[Port AI Assistant Slack App](/ai-agents/slack-app)**. +The Slack integration provides a natural way to interact with the Service Explorer agent. Before using this method, ensure you have installed and configured the **[Port AI Assistant Slack App](/ai-interfaces/ai-agents/slack-app)**. You can interact with the Service Explorer agent in two ways: 1. **Direct message** the Port AI Assistant. diff --git a/docs/guides/all/setup-task-manager-ai-agent.md b/docs/guides/all/setup-task-manager-ai-agent.md index 1a9830357a..8cd83a475c 100644 --- a/docs/guides/all/setup-task-manager-ai-agent.md +++ b/docs/guides/all/setup-task-manager-ai-agent.md @@ -24,12 +24,12 @@ By the end of this guide, your developers will be able to get information about ## Prerequisites This guide assumes you have: -- A Port account with the [AI agents feature enabled](/ai-agents/overview#access-to-the-feature). +- A Port account with the [AI agents feature enabled](/ai-interfaces/ai-agents/overview#access-to-the-feature). - Appropriate permissions to create and configure AI agents. ## Set up data model -To create a Task Manager AI agent in Port, we'll need to configure two main components as described in our [Build an AI agent](/ai-agents/build-an-ai-agent) guide: +To create a Task Manager AI agent in Port, we'll need to configure two main components as described in our [Build an AI agent](/ai-interfaces/ai-agents/build-an-ai-agent) guide: - The data sources it will use to answer questions about tasks and pull requests. - The agent configuration that defines its capabilities and conversation starters. @@ -99,7 +99,7 @@ For example: ## Interact with the Task Manager -You can interact with the task manager AI agent in [several ways](/ai-agents/interact-with-ai-agents). +You can interact with the task manager AI agent in [several ways](/ai-interfaces/ai-agents/interact-with-ai-agents). This guide will demonstrate the two main ways. @@ -140,7 +140,7 @@ Once the widget is set up, you can: -The Slack integration provides a natural way to interact with the Task Manager agent. Before using this method, ensure you have installed and configured the **[Port AI Assistant Slack App](/ai-agents/slack-app)** +The Slack integration provides a natural way to interact with the Task Manager agent. Before using this method, ensure you have installed and configured the **[Port AI Assistant Slack App](/ai-interfaces/ai-agents/slack-app)** You can interact with the Task Manager agent in two ways: 1. **Direct message** the Port AI Assistant. @@ -185,12 +185,12 @@ To get the most out of your Task Manager agent: 1. **Try it out**: Start with simple queries and see how the agent responds. 2. **Add context**: If the response isn't what you expected, try asking again with more details. -3. **Troubleshoot**: If you're still not getting the right answers, check our [troubleshooting guide](/ai-agents/interact-with-ai-agents#troubleshooting--faq) for common issues and solutions. +3. **Troubleshoot**: If you're still not getting the right answers, check our [troubleshooting guide](/ai-interfaces/ai-agents/interact-with-ai-agents#troubleshooting--faq) for common issues and solutions. ## Possible enhancements You can further enhance the Task Manager setup by: -- **Integration expansion**: [Add more data sources](/ai-agents/build-an-ai-agent#step-2-configure-data-access) like GitLab or Azure DevOps. -- **Automated notifications**: [Configure the agent](/ai-agents/interact-with-ai-agents#actions-and-automations) to proactively notify about important updates. -- **Custom conversation starters**: Add organization-specific queries to the [conversation starters](/ai-agents/build-an-ai-agent#step-5-add-conversation-starters). -- **Monitor and improve**: [Check how your developers are interacting](/ai-agents/interact-with-ai-agents#ai-interaction-details) with the agent and improve it according to feedback. +- **Integration expansion**: [Add more data sources](/ai-interfaces/ai-agents/build-an-ai-agent#step-2-configure-data-access) like GitLab or Azure DevOps. +- **Automated notifications**: [Configure the agent](/ai-interfaces/ai-agents/interact-with-ai-agents#actions-and-automations) to proactively notify about important updates. +- **Custom conversation starters**: Add organization-specific queries to the [conversation starters](/ai-interfaces/ai-agents/build-an-ai-agent#step-5-add-conversation-starters). +- **Monitor and improve**: [Check how your developers are interacting](/ai-interfaces/ai-agents/interact-with-ai-agents#ai-interaction-details) with the agent and improve it according to feedback. diff --git a/docs/guides/all/trigger-github-copilot-from-port.md b/docs/guides/all/trigger-github-copilot-from-port.md index 3de8b6e894..8037be1c26 100644 --- a/docs/guides/all/trigger-github-copilot-from-port.md +++ b/docs/guides/all/trigger-github-copilot-from-port.md @@ -23,7 +23,7 @@ By leveraging AI coding agents like Copilot, you can significantly reduce manual This guide assumes the following: - You have a Port account and have completed the [onboarding process](https://docs.port.io/getting-started/overview). - [Port's GitHub app](https://docs.port.io/build-your-software-catalog/sync-data-to-catalog/git/github/) is installed in your account. -- You have access to [create and configure AI agents](https://docs.port.io/ai-agents/overview#getting-started-with-ai-agents) in Port. +- You have access to [create and configure AI agents](https://docs.port.io/ai-interfaces/ai-agents/overview#getting-started-with-ai-agents) in Port. - GitHub Copilot is enabled in your repository. From 37a9d348a0cbc81bf01c8825400ab4d84096ca86 Mon Sep 17 00:00:00 2001 From: Matan Grady Date: Tue, 19 Aug 2025 17:26:45 +0300 Subject: [PATCH 05/11] add video for prompts --- docs/ai-interfaces/port-mcp-server/prompts.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/ai-interfaces/port-mcp-server/prompts.md b/docs/ai-interfaces/port-mcp-server/prompts.md index cf8cf36b04..ea1080268e 100644 --- a/docs/ai-interfaces/port-mcp-server/prompts.md +++ b/docs/ai-interfaces/port-mcp-server/prompts.md @@ -8,6 +8,22 @@ import TabItem from "@theme/TabItem" # Prompts +
+
+ +
+
+
+ Port allows you to centrally manage reusable prompts and expose them to your users via the MCP Server. Once defined, prompts become available in supported MCP clients (for example, Cursor or Claude) where developers and AI agents can discover and run them with the required inputs. #### Common use cases From 5c9406f70edf1afadcbb0b0ecdb3a0dc7c9ac2d4 Mon Sep 17 00:00:00 2001 From: Matan Grady Date: Tue, 19 Aug 2025 17:30:02 +0300 Subject: [PATCH 06/11] improvements to promtps.md --- docs/ai-interfaces/port-mcp-server/prompts.md | 274 ++++++++++-------- 1 file changed, 160 insertions(+), 114 deletions(-) diff --git a/docs/ai-interfaces/port-mcp-server/prompts.md b/docs/ai-interfaces/port-mcp-server/prompts.md index ea1080268e..078c8acbfc 100644 --- a/docs/ai-interfaces/port-mcp-server/prompts.md +++ b/docs/ai-interfaces/port-mcp-server/prompts.md @@ -111,6 +111,9 @@ After this blueprint exists and you create entities for it, prompts will show up Create entities of the `prompt` blueprint for each prompt you want to expose. At minimum, provide `description` and `template`. Optionally add `arguments` to parameterize the prompt. + + + 1. Go to the [Prompts page](https://app.getport.io/prompts) in your portal. 2. Click **Create prompt**. 3. Fill out the form: @@ -124,6 +127,130 @@ Create entities of the `prompt` blueprint for each prompt you want to expose. At The `template` supports markdown and variable placeholders. Each argument defined in `arguments` is exposed by its `name` and can be referenced as `{{name}}` inside the template. When you run the prompt, the MCP Server collects values for required arguments and substitutes them into the matching placeholders before execution. ::: + + + +You can create a Self-Service Action in Port to allow your users to create prompts themselves. + +1. Go to the [self-service](https://app.getport.io/self-serve) page of your portal. +2. Click on `+ New Action`. +3. Click on the `{...} Edit JSON` button. +4. Copy and paste the following JSON configuration: + +
+ Create New Prompt action JSON (Click to expand) + + ```json showLineNumbers + { + "identifier": "create_new_prompt", + "title": "Create New Prompt", + "icon": "Microservice", + "description": "Create prompt templates that appear in MCP clients (Claude, Cursor, VS Code, etc.) connected to your Port organization. Users can select prompts, provide required arguments, and get contextual AI assistance with dynamic data from Port.", + "trigger": { + "type": "self-service", + "operation": "CREATE", + "userInputs": { + "properties": { + "arguments": { + "type": "array", + "title": "Template Arguments", + "description": "Define arguments that users will provide when running this prompt. Each argument becomes available as {{argument_name}} placeholder in the template. Required arguments must be provided before prompt execution.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Argument Name", + "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$", + "description": "The parameter name that will be substituted in the template using {{name}} syntax (e.g., 'service_name', 'environment', 'incident_id')" + }, + "description": { + "type": "string", + "title": "Argument Description", + "description": "Clear description explaining what this argument represents and how it's used in the prompt context" + }, + "is_required": { + "type": "boolean", + "title": "Is Required", + "default": false, + "description": "When true, the MCP client (Claude, Cursor, VS Code) will require this argument before executing the prompt" + } + } + } + }, + "owning_team": { + "type": "string", + "title": "Owning Team (Optional)", + "description": "The team that will own and maintain this prompt template", + "format": "entity", + "blueprint": "_team" + }, + "prompt_title": { + "type": "string", + "title": "Prompt Title", + "description": "Human-readable name for this prompt (displayed in MCP clients like Claude, Cursor, and VS Code)", + "minLength": 3, + "maxLength": 50 + }, + "prompt_template": { + "type": "string", + "title": "Prompt Template", + "description": "The prompt content with placeholders for dynamic values. Use {{argument_name}} to reference arguments (e.g., 'Analyze service {{service_name}} in {{environment}}'). Supports markdown formatting. The MCP Server substitutes values into {{}} placeholders when the prompt runs.", + "minLength": 20, + "format": "multi-line" + }, + "prompt_description": { + "type": "string", + "title": "Description", + "description": "Explain what this prompt does and when to use it. This description helps users select the right prompt from the MCP client interface.", + "minLength": 10, + "maxLength": 500, + "format": "multi-line" + } + }, + "required": [ + "prompt_title", + "prompt_description", + "prompt_template" + ], + "order": [ + "prompt_title", + "prompt_description", + "prompt_template", + "arguments", + "owning_team" + ], + "titles": {} + }, + "blueprintIdentifier": "prompt" + }, + "invocationMethod": { + "type": "UPSERT_ENTITY", + "blueprintIdentifier": "prompt", + "mapping": { + "identifier": "{{ .inputs.prompt_title | ascii_downcase | gsub(\" \"; \"_\") | gsub(\"[^a-z0-9_]\"; \"\") }}", + "title": "{{ .inputs.prompt_title }}", + "team": "{{ if (.inputs.owning_team | type) == \"object\" then [.inputs.owning_team.identifier] else [] end }}", + "properties": { + "template": "{{ .inputs.prompt_template }}", + "arguments": "{{ (.inputs.arguments // []) | map({name: .name, description: .description, required: .is_required}) }}", + "description": "{{ .inputs.prompt_description }}" + } + } + }, + "requiredApproval": false + } + ``` + +
+ +:::tip Developer self-service +This Self-Service Action allows developers to create their own prompts without needing direct access to Port's data model. The action validates input and automatically creates properly formatted prompt entities. +::: + +
+
+ #### Examples @@ -131,6 +258,17 @@ The `template` supports markdown and variable placeholders. Each argument define A prompt to assists on-call engineers by summarizing recent alerts and deploys related to an incident, then suggesting next steps and linking relevant runbooks. +**Example prompt execution:** +```markdown +You are assisting with an incident in the payment-service service (production). +Incident ID: INC-2024-001 + +For the last 24 hours: +- Summarize critical alerts and recent deploys +- Suggest next steps and owners +- Link relevant dashboards/runbooks +``` +
Incident triage prompt entity JSON (Click to expand) @@ -177,6 +315,17 @@ A prompt to assists on-call engineers by summarizing recent alerts and deploys r A prompt that guides engineers to remediate failing scorecard rules by explaining each failure, its impact, and providing step-by-step fixes along with ownership suggestions. +**Example prompt execution:** +```markdown +For user-management-service, generate remediation steps for failing rules in the "Production Readiness" scorecard. + +For each failing rule: +- What is failing +- Why it matters +- Step-by-step remediation +- Owners and suggested timeline +``` +
Scorecard remediation prompt entity JSON (Click to expand) @@ -213,6 +362,17 @@ A prompt that guides engineers to remediate failing scorecard rules by explainin A prompt to generates a thorough on-call handoff report, highlighting active incidents, key risks, pending actions, and upcoming maintenance for the specified team. +**Example prompt execution:** +```markdown +Create an on-call handoff for Platform Engineering for the last past week. + +Include: +- Active incidents and current status +- Top risks and mitigations +- Pending actions and owners +- Upcoming maintenance windows +``` +
On-Call handoff report prompt entity JSON (Click to expand) @@ -278,117 +438,3 @@ Claude will ask for any required arguments before running the prompt and will su -### Self-service action to create prompts -You can create a Self Service Action in Port to allow you users to create prompts themselves. - -1. Go to the [self-service](https://app.getport.io/self-serve) page of your portal. -2. Click on `+ New Action`. -3. Click on the `{...} Edit JSON` button. -4. Copy and paste the following JSON configuration: - -
- Create New Prompt action JSON (Click to expand) - - ```json showLineNumbers - { - "identifier": "create_new_prompt", - "title": "Create New Prompt", - "icon": "Microservice", - "description": "Create prompt templates that appear in MCP clients (Claude, Cursor, VS Code, etc.) connected to your Port organization. Users can select prompts, provide required arguments, and get contextual AI assistance with dynamic data from Port.", - "trigger": { - "type": "self-service", - "operation": "CREATE", - "userInputs": { - "properties": { - "arguments": { - "type": "array", - "title": "Template Arguments", - "description": "Define arguments that users will provide when running this prompt. Each argument becomes available as {{argument_name}} placeholder in the template. Required arguments must be provided before prompt execution.", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "title": "Argument Name", - "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$", - "description": "The parameter name that will be substituted in the template using {{name}} syntax (e.g., 'service_name', 'environment', 'incident_id')" - }, - "description": { - "type": "string", - "title": "Argument Description", - "description": "Clear description explaining what this argument represents and how it's used in the prompt context" - }, - "is_required": { - "type": "boolean", - "title": "Is Required", - "default": false, - "description": "When true, the MCP client (Claude, Cursor, VS Code) will require this argument before executing the prompt" - } - } - } - }, - "owning_team": { - "type": "string", - "title": "Owning Team (Optional)", - "description": "The team that will own and maintain this prompt template", - "format": "entity", - "blueprint": "_team" - }, - "prompt_title": { - "type": "string", - "title": "Prompt Title", - "description": "Human-readable name for this prompt (displayed in MCP clients like Claude, Cursor, and VS Code)", - "minLength": 3, - "maxLength": 50 - }, - "prompt_template": { - "type": "string", - "title": "Prompt Template", - "description": "The prompt content with placeholders for dynamic values. Use {{argument_name}} to reference arguments (e.g., 'Analyze service {{service_name}} in {{environment}}'). Supports markdown formatting. The MCP Server substitutes values into {{}} placeholders when the prompt runs.", - "minLength": 20, - "format": "multi-line" - }, - "prompt_description": { - "type": "string", - "title": "Description", - "description": "Explain what this prompt does and when to use it. This description helps users select the right prompt from the MCP client interface.", - "minLength": 10, - "maxLength": 500, - "format": "multi-line" - } - }, - "required": [ - "prompt_title", - "prompt_description", - "prompt_template" - ], - "order": [ - "prompt_title", - "prompt_description", - "prompt_template", - "arguments", - "owning_team" - ], - "titles": {} - }, - "blueprintIdentifier": "prompt" - }, - "invocationMethod": { - "type": "UPSERT_ENTITY", - "blueprintIdentifier": "prompt", - "mapping": { - "identifier": "{{ .inputs.prompt_title | ascii_downcase | gsub(\" \"; \"_\") | gsub(\"[^a-z0-9_]\"; \"\") }}", - "title": "{{ .inputs.prompt_title }}", - "team": "{{ if (.inputs.owning_team | type) == \"object\" then [.inputs.owning_team.identifier] else [] end }}", - "properties": { - "template": "{{ .inputs.prompt_template }}", - "arguments": "{{ (.inputs.arguments // []) | map({name: .name, description: .description, required: .is_required}) }}", - "description": "{{ .inputs.prompt_description }}" - } - } - }, - "requiredApproval": false - } - ``` - -
\ No newline at end of file From 0227ed833f24f9d5187501f57e2fb2c7223a7066 Mon Sep 17 00:00:00 2001 From: Stav Dlugovitzky Date: Wed, 20 Aug 2025 10:45:41 +0300 Subject: [PATCH 07/11] Remove unused folder. --- docs/ai-agents/ai-agents/_category_.json | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 docs/ai-agents/ai-agents/_category_.json diff --git a/docs/ai-agents/ai-agents/_category_.json b/docs/ai-agents/ai-agents/_category_.json deleted file mode 100644 index b16a778c8f..0000000000 --- a/docs/ai-agents/ai-agents/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "AI agents", - "position": 1 -} From bc3c7bb08f1ac27b67fd4fba1ec73f7cd189c875 Mon Sep 17 00:00:00 2001 From: Stav Dlugovitzky Date: Wed, 20 Aug 2025 11:59:00 +0300 Subject: [PATCH 08/11] Remove changes in api reference. --- docs/api-reference/get-an-invocations-result.api.mdx | 4 ++-- docs/api-reference/invoke-a-specific-agent.api.mdx | 4 ++-- docs/api-reference/invoke-an-agent.api.mdx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/api-reference/get-an-invocations-result.api.mdx b/docs/api-reference/get-an-invocations-result.api.mdx index b4a60cee04..91b784ad1f 100644 --- a/docs/api-reference/get-an-invocations-result.api.mdx +++ b/docs/api-reference/get-an-invocations-result.api.mdx @@ -1,7 +1,7 @@ --- id: get-an-invocations-result title: "Get an invocation's result" -description: "This route allows you to get a specific invocation result.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-interfaces/ai-agents/overview)." +description: "This route allows you to get a specific invocation result.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-agents/overview)." sidebar_label: "Get an invocation's result" hide_title: true hide_table_of_contents: true @@ -36,7 +36,7 @@ import Heading from "@theme/Heading"; -This route allows you to get a specific invocation result.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-interfaces/ai-agents/overview). +This route allows you to get a specific invocation result.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-agents/overview).
To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-interfaces/ai-agents/overview)." +description: "This route allows you to create an agent's invocation.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-agents/overview)." sidebar_label: "Invoke a specific agent" hide_title: true hide_table_of_contents: true @@ -36,7 +36,7 @@ import Heading from "@theme/Heading"; -This route allows you to create an agent's invocation.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-interfaces/ai-agents/overview). +This route allows you to create an agent's invocation.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-agents/overview).
To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-interfaces/ai-agents/overview)." +description: "This route allows you to create an invocation without choosing a specific agent.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-agents/overview)." sidebar_label: "Invoke an agent" hide_title: true hide_table_of_contents: true @@ -36,7 +36,7 @@ import Heading from "@theme/Heading"; -This route allows you to create an invocation without choosing a specific agent.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-interfaces/ai-agents/overview). +This route allows you to create an invocation without choosing a specific agent.

To learn more about AI agents, check out the [documentation](https://docs.port.io/ai-agents/overview). Date: Wed, 20 Aug 2025 12:39:42 +0300 Subject: [PATCH 09/11] Fix broken links. --- .../port-mcp-server/overview-and-installation.md | 1 - ...sed-authenticatio.md => token-based-authentication.md} | 0 .../all/generate-zendesk-ticket-summaries-with-ai.md | 8 ++++---- 3 files changed, 4 insertions(+), 5 deletions(-) rename docs/ai-interfaces/port-mcp-server/{token-based-authenticatio.md => token-based-authentication.md} (100%) diff --git a/docs/ai-interfaces/port-mcp-server/overview-and-installation.md b/docs/ai-interfaces/port-mcp-server/overview-and-installation.md index 3740cae615..d8464a8a2a 100644 --- a/docs/ai-interfaces/port-mcp-server/overview-and-installation.md +++ b/docs/ai-interfaces/port-mcp-server/overview-and-installation.md @@ -1,7 +1,6 @@ --- sidebar_position: 1 title: Overview & Installation -slug: /ai-interfaces/ai-agents/port-mcp-server --- import Tabs from "@theme/Tabs" diff --git a/docs/ai-interfaces/port-mcp-server/token-based-authenticatio.md b/docs/ai-interfaces/port-mcp-server/token-based-authentication.md similarity index 100% rename from docs/ai-interfaces/port-mcp-server/token-based-authenticatio.md rename to docs/ai-interfaces/port-mcp-server/token-based-authentication.md diff --git a/docs/guides/all/generate-zendesk-ticket-summaries-with-ai.md b/docs/guides/all/generate-zendesk-ticket-summaries-with-ai.md index 3a490faf27..9da380ab49 100644 --- a/docs/guides/all/generate-zendesk-ticket-summaries-with-ai.md +++ b/docs/guides/all/generate-zendesk-ticket-summaries-with-ai.md @@ -42,7 +42,7 @@ Example output from the prompt: ## Prerequisites -- Port remote MCP installed and connected in your IDE (Cursor, Claude, etc.). Follow the setup guide: [Port MCP Server - Setup](/ai-agents/port-mcp-server#setup) +- Port remote MCP installed and connected in your IDE (Cursor, Claude, etc.). Follow the setup guide: [Port MCP Server - Setup](/ai-interfaces/port-mcp-server/overview-and-installation#installing-port-mcp) - A Port account and have completed the [onboarding process](https://docs.port.io/getting-started/overview). - Custom integration to ingest Zendesk tickets using [Port webhooks](/build-your-software-catalog/custom-integration/webhook). @@ -226,7 +226,7 @@ Replace `` with your Zendesk subdomain, for example: `https://ac ## Create a reusable prompt -We will now define a prompt entity that your IDE can invoke via [Port MCP](/ai-agents/port-mcp-server#prompts). Once created, you can run it with the ticket ID, and it will gather context and produce a structured summary. +We will now define a prompt entity that your IDE can invoke via [Port MCP](/ai-interfaces/port-mcp-server/prompts). Once created, you can run it with the ticket ID, and it will gather context and produce a structured summary. 1. Go to the [Prompts page](https://app.getport.io/prompts) in Port. 2. Click `Create prompt`. @@ -265,7 +265,7 @@ We will now define a prompt entity that your IDE can invoke via [Port MCP](/ai-a ### Test the workflow 1. In Port, make sure there is a `zendesk_ticket` entity whose `identifier` matches a real Zendesk ticket ID you want to summarize. -2. In your IDE assistant, choose **Port MCP** as the provider as described [here](/ai-agents/port-mcp-server#setup). +2. In your IDE assistant, choose **Port MCP** as the provider as described [here](/ai-interfaces/port-mcp-server/overview-and-installation#installing-port-mcp). 3. Run the `zendesk_ticket_summary` prompt with `ticket_id` set to that Zendesk ticket ID. 4. The assistant will automatically execute the self-service actions to fetch comments and side conversations, then return a structured summary. @@ -276,7 +276,7 @@ Summaries can include sensitive customer or internal details, so treat them as i ::: :::info Using Port MCP prompts -For setup and capabilities, see the Port MCP Server prompts documentation: [Port MCP Server - Prompts](/ai-agents/port-mcp-server#prompts) +For setup and capabilities, see the Port MCP Server prompts documentation: [Port MCP Server - Prompts](/ai-interfaces/port-mcp-server/prompts) ::: ## Best practices From bc0d8fffd944e14ed63237c749fdbaf692ae4ac8 Mon Sep 17 00:00:00 2001 From: Stav Dlugovitzky Date: Wed, 20 Aug 2025 13:53:33 +0300 Subject: [PATCH 10/11] Fixes collapsible sections. --- .../port-mcp-server/troubleshooting.md | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/docs/ai-interfaces/port-mcp-server/troubleshooting.md b/docs/ai-interfaces/port-mcp-server/troubleshooting.md index fddb59e80b..05f088fc54 100644 --- a/docs/ai-interfaces/port-mcp-server/troubleshooting.md +++ b/docs/ai-interfaces/port-mcp-server/troubleshooting.md @@ -5,23 +5,43 @@ title: Troubleshooting # Troubleshooting -If you encounter issues while using the Port MCP Server, expand a section below: +If you encounter issues while setting up or using the Port MCP Server, expand the relevant section below:
-How can I connect to the MCP? -Make sure you're using the correct regional URL for your organization and have followed the setup instructions for your client (Cursor, VSCode, Claude). +How can I connect to the MCP? (Click to expand) + +Refer back to the [setup instructions](/ai-agents/port-mcp-server#setup) for your specific application (Cursor, VSCode, or Claude). Make sure you're using the correct regional URL for your Port organization. +
-I completed the connection but nothing happens -Ensure you authenticated successfully and have sufficient permissions. If problems persist, contact Port support. +I completed the connection but nothing happens (Click to expand) + +Check that you've followed all the [setup steps](/ai-agents/port-mcp-server#setup) correctly for your application. Ensure you're authenticated with Port and have the necessary permissions. If you've followed all the steps and still have issues, please reach out to our support team. +
-Why do I see an error about too many tools? -Each self-service action becomes its own tool. If you have many actions, models may hit tool-count limits. Disable non-essential tools or ask an admin to review actions. +Why do I see an error about too many tools? (Click to expand) + +Each self-service action in your Port instance becomes an individual tool (as `run_`). If your organization has many actions, this can result in a large number of tools being available. + +While most AI models handle this well, some have restrictions and may limit you to around 40 tools total. If you encounter errors about tool limits: + +1. **Reduce the number of tools** by customizing which tools are enabled (see [Select which tools to use](available-tools#select-which-tools-to-use) section above) +2. **Focus on essential tools** by only enabling the read-only tools you need plus a few key actions +3. **Contact your Port Admin** to review which actions are essential for your workflow + +This is completely normal behavior and doesn't indicate a problem with Port MCP - it's just a limitation of some AI models. +
-:::tip Need help? -Reach out to Port support with your client version, region (EU/US), error messages, and the steps you've tried. +:::tip Getting Help +If you continue to experience issues, please reach out to Port support with: +- Your IDE/application version +- The specific error messages you're seeing +- Your Port region (EU/US) +- Steps you've already tried + +This information will help us provide more targeted assistance. ::: From ec32a9c25a48a9ce91cee0f2ee3d0e60c11d3582 Mon Sep 17 00:00:00 2001 From: Stav Dlugovitzky Date: Wed, 20 Aug 2025 14:03:32 +0300 Subject: [PATCH 11/11] Fixes links. --- docs/ai-interfaces/port-mcp-server/troubleshooting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ai-interfaces/port-mcp-server/troubleshooting.md b/docs/ai-interfaces/port-mcp-server/troubleshooting.md index 05f088fc54..f21fc4ae63 100644 --- a/docs/ai-interfaces/port-mcp-server/troubleshooting.md +++ b/docs/ai-interfaces/port-mcp-server/troubleshooting.md @@ -10,14 +10,14 @@ If you encounter issues while setting up or using the Port MCP Server, expand th
How can I connect to the MCP? (Click to expand) -Refer back to the [setup instructions](/ai-agents/port-mcp-server#setup) for your specific application (Cursor, VSCode, or Claude). Make sure you're using the correct regional URL for your Port organization. +Refer back to the [setup instructions](./overview-and-installation#installing-port-mcp) for your specific application (Cursor, VSCode, or Claude). Make sure you're using the correct regional URL for your Port organization.
I completed the connection but nothing happens (Click to expand) -Check that you've followed all the [setup steps](/ai-agents/port-mcp-server#setup) correctly for your application. Ensure you're authenticated with Port and have the necessary permissions. If you've followed all the steps and still have issues, please reach out to our support team. +Check that you've followed all the [setup steps](./overview-and-installation#installing-port-mcp) correctly for your application. Ensure you're authenticated with Port and have the necessary permissions. If you've followed all the steps and still have issues, please reach out to our support team.