> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.pensar.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.pensar.dev/_mcp/server.

# MCP Server

> Connect AI tools to Pensar using the Model Context Protocol

## Overview

Pensar provides a remote [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that lets AI tools interact directly with your Pensar workspace. Through MCP, you can list scans, launch pentests, review security issues, and retrieve auto-generated fixes using tools like Claude Code, Desktop, or Web, or even directly in Slack.

> **Info**
>
> MCP is an open standard that allows AI assistants to connect to external tools and data sources. Pensar's MCP server implements the **Streamable HTTP** transport so it works with any MCP-compatible client.

## Prerequisites

Before connecting an MCP client to Pensar you need:

* A Pensar Console account at [console.pensar.dev](https://console.pensar.dev)
* At least one workspace with a connected repository
* An MCP-compatible client (e.g., Claude Desktop, Claude Code, Cursor, Windsurf)

## Server URL

The Pensar MCP server is available at:

```
https://api.pensar.dev/mcp
```

## Authentication

The Pensar MCP server uses **OAuth 2.0** for authentication, following the [MCP Authorization specification (2025-03-26)](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization). When you first connect, your MCP client will open a browser window where you sign in with your Pensar Console credentials. After signing in, the client receives a token and uses it automatically for all subsequent requests.

> **Note**
>
> Most MCP clients handle the OAuth flow automatically. You do not need to create or manage API keys, just sign in.

## Setup

#### Claude Code

1. Open Claude Code by running `claude` in your terminal
2. Add the Pensar MCP server:

```bash
claude mcp add Pensar --transport http https://api.pensar.dev/mcp
```

3. Run the `/mcp` command, you'll see **Pensar** with a **needs authentication** status
4. Select it and choose **Authenticate** to sign in with Pensar in your browser

#### Claude Desktop / Web

These steps work in both Claude Desktop and Web. Setting up the connector in one makes it available in the other.

1. Go to **Customize** > **Connectors**
2. Click **+** > **Add custom connector**
3. Enter **Pensar** as the name
4. Enter `https://api.pensar.dev/mcp` as the **Remote MCP server URL**
5. Click **Add**
6. The connector appears under your Connectors list, click on it then click **Connect** to sign in with Pensar

After connecting, click the Pensar connector to configure tool permissions. Read-only tools (like `list_scans` and `get_issue`) default to **Always allow**, while write tools (like `dispatch_pentest`) default to **Needs approval**

#### Cursor

1. Go to **Cursor** > **Settings...** > **Cursor Settings** > **Tools & MCP**
2. Click **New MCP Server**, this opens your MCP config file (e.g., `~/.cursor/mcp.json`)
3. Add Pensar under `mcpServers`:

```json
{
  "mcpServers": {
    "Pensar": {
      "url": "https://api.pensar.dev/mcp"
    }
  }
}
```

4. Save the file, Pensar appears under **Installed MCP Servers** with a **Needs authentication** status
5. Click **Connect** to sign in with Pensar

#### Windsurf

1. Go to **Windsurf** > **Settings...** > **Windsurf Settings** > **Cascade** > **MCP Servers**
2. Click **MCP Marketplace**, then the **Settings** icon to open `~/.codeium/windsurf/mcp_config.json`
3. Add Pensar under `mcpServers`:

```json
{
  "mcpServers": {
    "Pensar": {
      "serverUrl": "https://api.pensar.dev/mcp"
    }
  }
}
```

4. Save the file, Windsurf will automatically direct you to sign in with Pensar in your browser

## Available Tools

The Pensar MCP server exposes the following tools to your AI coding agent:

### Scans

#### list_scans

List all scans in your workspace.

**Parameters:** None

**Returns:** A list of scans with their IDs, labels, statuses, scan types, branches, and timestamps.

**Example prompt:** *"Show me all my Pensar scans."*

#### get_scan

Get detailed information about a specific scan.

**Parameters:**

| Parameter | Type   | Required | Description                     |
| --------- | ------ | -------- | ------------------------------- |
| `scanId`  | string | Yes      | Scan ID to retrieve details for |

**Returns:** Detailed scan information including workspace name, status, error messages, issue count, and whether a report is ready.

**Example prompt:** *"What's the status of scan xyz-789?"*

#### dispatch_pentest

Queue a hosted pentest in the authenticated workspace. The scan runs asynchronously; an identical active launch may be reused.

**Parameters:**

| Parameter           | Type      | Required | Description                                                                                                                             |
| ------------------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `endpointIds`       | string\[] | No       | Exact list of 1–1000 discovered endpoint UUIDs in this workspace. Cannot be combined with `branch`; only full depth is supported.       |
| `scanLevel`         | string    | No       | `"priority"` or `"full"`. Defaults to `"full"` with `endpointIds`, otherwise `"priority"`. `"priority"` is rejected with `endpointIds`. |
| `branch`            | string    | No       | Must match the connected repository's configured target branch, which is used when omitted. Cannot be combined with `endpointIds`.      |
| `knowledgeEntryIds` | string\[] | No       | Up to 20 active knowledge base entry UUIDs to give the run as context (see [Knowledge Base](#knowledge-base)).                          |

**Exact endpoint arguments:**

```json
{
  "endpointIds": [
    "7e4fe67a-61e5-4aa5-b93a-6871b470aaef",
    "59e9b6d5-d13b-4912-9e46-a1e801c1d053"
  ],
  "scanLevel": "full"
}
```

Find discovered endpoint IDs with `pensar apps endpoints <appId>` in the [Apex CLI](/apex/cli-commands/pensar-pentests#launch-selected-endpoints) or `GET /apps/:appId/endpoints` in the [REST API](/console/features/rest-api#dispatch-pentest). MCP does not expose endpoint discovery.

Exact lists use full depth and test the supplied endpoints without commit-based narrowing. Each selected application must be ready to test. Omit `endpointIds` to test eligible workspace applications at priority depth by default.

Invalid IDs or incompatible arguments return a tool error without expanding the requested scope.

**Returns:** The queued scan's `scanId`, `label`, `status`, and confirmation `message`. Use `get_scan` with that ID to check completion. Failures are MCP tool errors (`isError: true`).

**Example prompt:** *"Use dispatch_pentest with these two discovered endpoint IDs only: 7e4fe67a-61e5-4aa5-b93a-6871b470aaef and 59e9b6d5-d13b-4912-9e46-a1e801c1d053. Then check the returned scan ID until it completes."*

### Issues

#### list_issues

List security issues with optional filtering.

**Parameters:**

| Parameter  | Type   | Required | Description                                                      |
| ---------- | ------ | -------- | ---------------------------------------------------------------- |
| `scanId`   | string | No       | Filter by scan ID                                                |
| `status`   | string | No       | Filter by issue status (e.g., `"open"`, `"closed"`)              |
| `severity` | string | No       | Filter by severity (`"critical"`, `"high"`, `"medium"`, `"low"`) |
| `branch`   | string | No       | Filter by git branch                                             |

**Returns:** A list of issues with their IDs, `issueLabel` references (`VULN-000123`), console `url`s, titles, severities, statuses, and file locations. Closed issues also carry `closedAt`, `closedDisposition` and `duplicateOf`, so a closed finding is not mistaken for a resolved one; all three are `null` while the issue is open.

**Example prompt:** *"Show me all critical open issues."*

#### get_issue

Get detailed information about a specific security issue.

**Parameters:**

| Parameter | Type   | Required | Description                                                      |
| --------- | ------ | -------- | ---------------------------------------------------------------- |
| `issueId` | string | Yes      | Issue UUID or label (e.g. `VULN-000123`) to retrieve details for |

**Returns:** Full issue details including the `issueLabel` reference, the console `url`, the finding `name`, description, affected file and lines (`startLineNumber`, `endLineNumber`), CWE classification, branch, proof-of-concept, `endpointId`, `applicationId`, and workspace information. A closed issue adds the full close record: `closedAt`, `closedDisposition`, `duplicateOf`, `closedMethod`, `closedReason`, and `closedComments`.

**Example prompt:** *"Give me the details on VULN-000123."*

#### update_issue

Update a security issue's status, disposition, or inventory endpoint association.

**Parameters:**

| Parameter                        | Type           | Required         | Description                                                                                                           |
| -------------------------------- | -------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------- |
| `issueId`                        | string         | Yes              | Issue UUID or label (e.g. `VULN-000123`) to update                                                                    |
| `status`                         | string         | No               | New status: `"open"`, `"closed"`, `"false-positive"`, or `"in-review"`                                                |
| `userFlaggedFalsePositive`       | boolean        | No               | Flag the issue as a false positive                                                                                    |
| `userFlaggedFalsePositiveReason` | string         | No               | Reason for flagging as false positive                                                                                 |
| `closedDisposition`              | string         | No               | Structured close verdict: `resolved`, `duplicate`, `wont-fix`, `out-of-scope`, `risk-accepted`                        |
| `duplicateOf`                    | string         | With `duplicate` | The issue this one duplicates, as a UUID or label (e.g. `VULN-000123`)                                                |
| `closedReason`                   | string         | No               | Free-text reason for closing                                                                                          |
| `closedComments`                 | string         | No               | Additional comments on closure                                                                                        |
| `endpointId`                     | string or null | No               | Inventory endpoint UUID to associate. Omit to preserve the association; send `null` to clear endpoint and application |

Pair `closedDisposition` with `status: "closed"` — without it the finding reads as **Resolved** whatever the reason says. Reopening clears it, and `false-positive` never carries one. `other` is not accepted, as it is being retired from the vocabulary.

A `duplicate` close must name its original in `duplicateOf`, another issue in the same workspace that is not itself closed as a duplicate, and no other disposition may carry one.

Endpoint updates validate workspace ownership, keep `applicationId` synchronized to the endpoint's application, and reject duplicate `(pentest, title, endpoint)` associations.

**Returns:** The updated issue details, including close metadata plus `endpointId` and `applicationId`.

The change is also applied to the finding's linked GitHub, Linear and Notion tickets.

**Example prompt:** *"Close issue abc-123 as risk-accepted, we have a WAF rule in front of it."*

### Fixes

#### list_fixes

List all available fixes for a security issue.

**Parameters:**

| Parameter | Type   | Required | Description                                                |
| --------- | ------ | -------- | ---------------------------------------------------------- |
| `issueId` | string | Yes      | Issue UUID or label (e.g. `VULN-000123`) to list fixes for |

**Returns:** A list of fixes with their IDs and affected file paths.

**Example prompt:** *"Are there any fixes for issue def-456?"*

#### get_fix

Get detailed information about a specific fix, including the code diff.

**Parameters:**

| Parameter | Type   | Required | Description                    |
| --------- | ------ | -------- | ------------------------------ |
| `fixId`   | string | Yes      | Fix ID to retrieve details for |

**Returns:** The fix details including the file path, a unified diff of the changes, an explanation of what the fix does, and the related issue ID.

**Example prompt:** *"Show me the diff for fix ghi-789."*

### Agent Logs

#### list_agent_logs

List agent execution logs for a specific issue. Useful for understanding how the agent discovered and verified a vulnerability.

**Parameters:**

| Parameter | Type   | Required | Description                                                                         |
| --------- | ------ | -------- | ----------------------------------------------------------------------------------- |
| `issueId` | string | Yes      | Issue UUID or label (e.g. `VULN-000123`) to list logs for                           |
| `level`   | string | No       | Filter by log level: `"debug"`, `"info"`, `"warn"`, `"error"`                       |
| `role`    | string | No       | Filter by role: `"assistant"`, `"user"`, `"system"`, `"tool-call"`, `"tool-result"` |
| `limit`   | number | No       | Maximum number of log entries to return                                             |

**Returns:** A list of agent log entries with timestamps, levels, roles, and content.

**Example prompt:** *"Show me the agent logs for issue abc-123."*

#### search_agent_logs

Search agent execution logs by text pattern with configurable context lines.

**Parameters:**

| Parameter      | Type   | Required | Description                                                 |
| -------------- | ------ | -------- | ----------------------------------------------------------- |
| `issueId`      | string | Yes      | Issue UUID or label (e.g. `VULN-000123`) to search logs for |
| `query`        | string | Yes      | Text pattern to search for                                  |
| `level`        | string | No       | Filter by log level                                         |
| `role`         | string | No       | Filter by role                                              |
| `contextLines` | number | No       | Number of surrounding context lines to include              |

**Returns:** Matching log entries with surrounding context.

**Example prompt:** *"Search the agent logs for issue abc-123 for mentions of SQL injection."*

### Knowledge Base

Knowledge base entries are the context Pensar agents use during pentests. A `false_positive_pattern` entry suppresses matching findings, so the write tools need a user credential: an OAuth session can create, update, and archive entries, while a workspace API key can only read them. Every entry records the user who wrote it. Like `dispatch_pentest`, the write tools default to **Needs approval**.

#### list_knowledge

List knowledge base entries, newest first.

**Parameters:**

| Parameter  | Type      | Required | Description                                                                                         |
| ---------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `category` | string    | No       | `false_positive_pattern`, `technology_context`, `testing_history`, `user_note`, or `agent_learning` |
| `status`   | string    | No       | `active`, `stale`, or `archived`                                                                    |
| `tags`     | string\[] | No       | Only entries carrying every one of these tags                                                       |

**Returns:** Entries with their ID, title, content, category, status, source, tags, `createdBy`, and timestamps.

**Example prompt:** *"What does Pensar know about our tech stack?"*

#### get_knowledge

Get one knowledge base entry.

**Parameters:**

| Parameter | Type   | Required | Description |
| --------- | ------ | -------- | ----------- |
| `id`      | string | Yes      | Entry UUID  |

**Returns:** The full entry.

#### create_knowledge

Add a knowledge base entry, attributed to you. Requires a user credential.

**Parameters:**

| Parameter  | Type      | Required | Description        |
| ---------- | --------- | -------- | ------------------ |
| `title`    | string    | Yes      | Entry title        |
| `content`  | string    | Yes      | Entry body         |
| `category` | string    | Yes      | Entry category     |
| `tags`     | string\[] | No       | Tags for filtering |

**Returns:** The created entry.

**Example prompt:** *"Add a technology note to Pensar that our API sits behind a WAF."*

#### update_knowledge

Edit a knowledge base entry. Omitted fields are unchanged; `tags` replaces the existing list. Requires a user credential.

**Parameters:**

| Parameter  | Type      | Required | Description                      |
| ---------- | --------- | -------- | -------------------------------- |
| `id`       | string    | Yes      | Entry UUID                       |
| `title`    | string    | No       | Entry title                      |
| `content`  | string    | No       | Entry body                       |
| `category` | string    | No       | Entry category                   |
| `status`   | string    | No       | `active`, `stale`, or `archived` |
| `tags`     | string\[] | No       | Tags for filtering               |

**Returns:** The updated entry.

#### archive_knowledge

Archive a knowledge base entry so agents stop using it. The entry stays readable. Requires a user credential.

**Parameters:**

| Parameter | Type   | Required | Description |
| --------- | ------ | -------- | ----------- |
| `id`      | string | Yes      | Entry UUID  |

**Returns:** The archived entry.

## REST API

Every MCP tool has a matching REST API endpoint for programmatic access via API keys. The REST API is a superset — it also exposes attack-surface endpoints (apps and endpoints) that are not available over MCP. See the [REST API documentation](/console/features/rest-api) for full endpoint details and authentication instructions.

## Usage Examples

Once connected, you can interact with Pensar through natural language in your AI coding agent. Here are some common workflows:

### Run a Security Scan

Ask your agent to launch a pentest and then check the results:

> *"Run a Pensar pentest and let me know when it finishes."*

The agent will use `dispatch_pentest` to start the scan, then `get_scan` to poll for completion. To select exact targets, provide discovered endpoint UUIDs and ask the agent to pass them as `endpointIds`; omitting that field uses the workspace launch behavior described above.

### Review Security Issues

Ask your agent to summarize findings from a recent scan:

> *"Show me all high and critical issues from my latest scan."*

The agent will use `list_scans` to find the most recent scan, then `list_issues` with severity filters to retrieve the relevant findings.

### Apply a Fix

Ask your agent to retrieve and apply a recommended fix:

> *"Get the fix for issue abc-123 and apply it to my codebase."*

The agent will use `list_fixes` to find available fixes, `get_fix` to retrieve the diff, and then apply the patch to your local files.

## Supported Clients

The Pensar MCP server works with any client that supports the Streamable HTTP transport and OAuth-based authorization. Tested clients include Claude Code, Claude Desktop, Cursor, and Windsurf.

> **Note**
>
> If your preferred client is not listed above, consult its documentation on adding a remote MCP server.

## Next Steps

#### [Hybrid Testing](/console/features/hybrid-testing)

Learn about Pensar's hybrid testing methodology that powers MCP issue and
fix data.

#### [Auto-Remediation](/console/features/auto-remediation)

Understand how Pensar generates fixes you can retrieve via MCP.

#### [CI/CD Integration](/console/features/ci-integration)

Automate pentesting in your pipeline alongside MCP-driven workflows.

#### [Getting Started](/console/getting-started)

Set up your workspace and connect your first repository.