# ai.vruum/mcp (npm · @vruum/mcp)

Vruum AI revenue platform — outbound, deals, pipeline & CRM automation over one MCP.

- Trust score: 47/100 (low)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-08-04

## Components

- remote · `api.vruum.ai`: 36/100, [markdown](https://verifymcp.io/servers/ai-vruum-mcp/api.md), [page](https://verifymcp.io/servers/ai-vruum-mcp/api)
- npm · `@vruum/mcp`: 47/100 (this document), [markdown](https://verifymcp.io/servers/ai-vruum-mcp/vruum-mcp.md), [page](https://verifymcp.io/servers/ai-vruum-mcp/vruum-mcp)

## Channel facts

- Registry: `npm`
- Package: `@vruum/mcp`
- Version: `0.2.3`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-08-04.

- **Supply Chain Security**: 63/100
  - No malware found by supply-chain analysis.
  - CVE data not yet available for this package.
  - No install/post-install scripts declared.
  - Dependency-health data not yet available.
- **Provenance & Transparency**: 45/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 0 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 17/100
  - AI-judged instruction clarity (poor).
  - Context-footprint check failed: tool/resource definitions use about 27116 tokens (~935/item across 29 items; 29 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 3/100
  - Stability observed for 1 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 98/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 93% of tool parameters carry a description.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### Claude

```bash
claude mcp add ai-vruum-mcp -- npx -y @vruum/mcp
```

### Codex

```bash
codex mcp add ai-vruum-mcp -- npx -y @vruum/mcp
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "ai-vruum-mcp": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "@vruum/mcp"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add ai-vruum-mcp --command npx --arg -y --arg @vruum/mcp
```

### Hermes

```yaml
mcp_servers:
  ai-vruum-mcp:
    command: "npx"
    args: ["-y", "@vruum/mcp"]
```

### Other

```json
{
  "mcpServers": {
    "ai-vruum-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@vruum/mcp"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-08-04 (score 47, −10)

- [security regression] Known CVEs: partial → unverified
- [functional regression] Dependency health: partial → unverified
- [functional improvement] Stability: unverified → 0.03
- [functional] Schema quality: fair → poor
- [functional] Package version: 0.2.2 → 0.2.3

### 2026-08-03 (score 57)

First indexed and scored.

## MCP tools (29)

### `fetch` (~1580 tokens)

Fetch

Fetch

Fetch one entity (or a small batch) by type + id — the consolidated read tool.

Types and id semantics:
\- campaign: a campaign by UUID (array of UUIDs → per-id map). List
  campaigns with `search` type='campaigns'; create/update/diagnose/member
  changes with `manage_campaign`.
\- plan: a person outreach plan. SCALAR id = the plan UUID; ARRAY id =
  PERSON UUIDs (routes to the native batch person-plans read, returning a
  person_id → plan|null map). Note the scalar/array dimension difference.
\- conversation: full message history for a person (id = person_id).
\- person_research: cached person research (id = person UUID; array → per-id map).
\- company_research: cached company research (id = website/domain string;
  array → native batch lookup).
\- account_state: per-account state row (id = company_id).
\- insights (subtype required): quality | pattern_tags | prompts |
  benchmarks | improve | reply_diagnosis (id = person_id) |
  mcp_patterns | mcp_pattern_detail (id = pattern_id).
\- scoreboard (subtype required): impact | bowtie (id = company_id;
  filters: {window_days?}).
\- settings (subtype required): profile | automation | marketing |
  channel_status | booking_link (no id).
\- billing: billing status for your company (no id).
\- publish_readiness: publish pre-check for a LinkedIn draft (id = post_id).
\- research_playbook: ICP + research workflow (id = optional campaign UUID).
\- job: background job status — poll after async tools like
  people create / import_prospects / auto_fill_company_profile (id =
  scalar job UUID; a one-element array is normalized).
\- csv_import: CSV import progress (id = the job id returned by
  import_prospects csv_start).
\- deal: a raw deal row by UUID (for the enriched view use get_deal_360).
\- deal_pipelines: the tenant's configured pipelines and stages (no id;
  filters: {include_archived?}). Returns pipeline/stage IDs, ordering,
  stage kinds, probability defaults, archived state, and the default
  pipeline ID.
\- deal_alerts: at-r…

Input parameters:

- `filters` (object): Extra query filters for the types that take them: stats.outreach {start_date?, end_date?}; stats.prose {days? (default 30, 1-90)}; marketing.activity {days?, limit?}; ads.attribution {window_days?, o…
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Entity id (see the per-type semantics in the tool description). An ARRAY of ids is accepted for campaign, plan, person_research and company_research (max 100). For job polling, pass the scalar job UU…
- `subtype` (string): Variant selector — required for insights (quality | pattern_tags | prompts | benchmarks | improve | reply_diagnosis | mcp_patterns | mcp_pattern_detail), scoreboard (impact | bowtie) and settings (pr…
- `type` (string, required): Entity type to fetch. One of: campaign, csv_import, plan, conversation, person_research, company_research, account_state, insights, scoreboard, settings, billing, publish_readiness, research_playbook…

### `find_warm_path` (~1120 tokens)

Find Warm Path

Find Warm Path

Read relationship truth for one target; performs no write, LLM call, or send. scope='team' also surfaces consenting teammates' ties (owner named, tier only — their evidence stays private).

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

\**Example Response:**
\```json
{
  "status": "Status",
  "target": {},
  "resolution": {
    "status": "Status",
    "target_candidates": [
      {
        "kind": "Kind",
        "id": "Id",
        "ref": "Ref",
        "name": "Name"
      }
    ]
  },
  "evaluation": {
    "status": "Status",
    "evaluated_at": "Evaluated At",
    "projection_version": "Projection Version"
  },
  "paths": [
    {
      "ref": "Ref",
      "target_ref": "Target Ref",
      "result_type": "Result Type",
      "label": "Label",
      "connector": {},
      "evidence_tier": "Evidence Tier",
      "evidence": {
        "sources": [
          "string"
        ],
        "interaction_count": 1,
        "bidirectional": true,
        "summary": "Summary"
      },
      "eligibility_reason": "Eligibility Reason",
      "limitations": [
        "string"
      ],
      "allowed_actions": [
        "string"
      ],
      "route_fingerprint": "Route Fingerprint"
    }
  ],
  "intro_candidates": [
    {
      "ref": "Ref",
      "target_ref": "Target Ref",
      "result_type": "Result Type",
      "label": "Label",
      "connector": {},
      "evidence_tier": "Evidence Tier",
      "evidence": {
        "sources": [
          "string"
        ],
        "interaction_count": 1,
        "bidirectional": true,
        "summary": "Summary"
      },
      "eligibility_reason": "Eligibility Reason",
      "limitations": [
        "string"
      ],
      "allowed_actions": [
        "string"
      ],
      "route_fingerprint": "Route Fingerprint"
    }
  ],
  "company_entries": [
    {
      "ref": "Ref",
      "target_ref": "Target Ref",
      "result_type": "Result Type",
      "label": "Label",…

Input parameters:

- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `limit` (integer)
- `scope` (string): 'mine' evaluates only your own relationship evidence. 'team' (VRU-776) also surfaces consenting teammates' ties with the owner named — you see that a tie exists and its tier, never their evidence; te…
- `target` (string, required): Person/company UUID, exact email, LinkedIn URL, company domain, or free text among identities already known to this workspace. Read-only.

### `get_campaign_outcomes` (~856 tokens)

Get Campaign Outcomes

Get Campaign Outcomes

Compare campaigns on real outcomes — contacted, replied, meetings booked.

Answers "which of these campaigns actually worked" for up to 100 campaigns
in one call, over an explicit UTC window. Every campaign you ask for comes
back, including ones with no usable data — those carry
\`availability: "unavailable"` rather than being dropped or reported as
zero, so a campaign never looks like a failure when it is really just
unmeasured.

\**Use `get_performance_metrics` instead** for funnel and time-series views
of the whole account; this tool is for ranking specific campaigns against
each other on a like-for-like cohort.

Reading the response:

\- **`outcome_cutoff`** is the "as of" moment. A reply or meeting counts
  only if it happened by then, so a campaign that started last week is not
  punished for outcomes that have not had time to land. Keep it identical
  across campaigns you intend to compare.
\- **`coverage_complete: false`** means attribution for that campaign is
  still incomplete. Report the numbers, but do not rank on them — that is
  the difference between "performed badly" and "we cannot tell yet".
\- **`data_watermark`** is how far message and meeting ingestion has caught
  up. Outcomes after the watermark are not in these numbers yet.
\- **`computed_at`** stamps the calculation, which is worth quoting when
  the same window is re-run later and moves.

Window rules the server enforces: all three timestamps must carry a UTC
offset, `start_at < end_at <= outcome_cutoff`, and `outcome_cutoff` cannot
be in the future. `campaign_ids` must be unique.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

\**Example Response:**
\```json
{
  "computed_at": "2023-01-01T00:00:00Z",
  "outcome_cutoff": "2023-01-01T00:00:00Z",
  "coverage_complete": true,
  "items": [
    {
      "campaign_id": "Campaign Id",
      "availability": "Availability",
      "coverage_complete": true,
      "data_watermark": {}…

Input parameters:

- `campaign_ids` (array, required): Campaigns to compare, 1-100, no duplicates. Every id you pass is returned — ones without usable data come back as availability='unavailable' rather than being dropped.
- `end_at` (string, required): End of the comparison window (UTC offset required). Bounds which touches enter the cohort — NOT which outcomes count; that is outcome_cutoff. Must be on or before outcome_cutoff.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `outcome_cutoff` (string, required): The 'as of' moment for attributing outcomes (UTC offset required). A reply or booked meeting counts only if it happened by this time, so a recently-started campaign is not penalised for outcomes that…
- `start_at` (string, required): Start of the comparison window (UTC offset required). Touches before this are excluded. Must be earlier than end_at.

### `get_content_review` (~581 tokens)

Get Content Review

Get Content Review

Review content post drafts with performance context and calendar awareness.

Returns draft and scheduled LinkedIn posts enriched with: past performance
stats (avg engagement metrics for same content type over last 30 days),
calendar neighbors (other posts within ±3 days to detect topic clustering),
tone instructions from company settings, and a calendar summary.

Attachments (VRU-726): items carry attachment_type (document | single_image
\| video), attachment_filename, and attachment_url — OPEN the URL to review
the actual file (a document publishes as a swipeable carousel under the
author's identity; never approve blind). Assets are public-at-upload
(public storage bucket, unguessable URL). error_message surfaces publish
failures (a reverted scheduled post shows up here with its fix).

Use `post_ids` for a deterministic lookup of the post you just drafted;
use this before scheduling or publishing demand gen content.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

\**Example Response:**
\```json
{
  "items": [
    {
      "post_id": "Post Id",
      "calendar_neighbors": [
        {}
      ]
    }
  ],
  "total_pending": 1,
  "offset": 1,
  "limit": 1
}
\```

Input parameters:

- `content_type` (string): Filter by content type: original, repost_commentary, or video_script
- `days` (integer): Calendar window in days (default 14)
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `limit` (integer): Max items to return
- `offset` (integer): Pagination offset
- `post_ids` (array): Direct lookup of specific post ids (VRU-726) — the deterministic 'review the post I just drafted' path. Bypasses the status filter. Capped at `limit` ids (default 10); extras are dropped.
- `status` (string): Filter by a single status: draft, scheduled, or failed. Omit for the default draft+scheduled set.

### `get_daily_briefing` (~1930 tokens)

Get Daily Briefing

Get Daily Briefing

Daily briefing — everything you need to start your day in one call.

SUGGESTED ACTIONS (for agent consumers)
\- `suggested_actions` are prioritized skill-level next steps. If you are
  an agent, act on `intent`/`args` directly — via the tools listed in
  \`expected_tools`, or via the named harness `skill` if it is installed.
  \`prompt` is display copy for HUMANS to paste into an agent; do NOT echo
  it back to the user as an instruction.
\- `sections_degraded` lists sections whose queries failed this call; their
  counts/lists are UNKNOWN (returned as 0/[]), not zero. When data is
  missing and nothing else is actionable, `top_recommendation` is
  'degraded' instead of 'all_clear'.

Returns pending approvals, new replies, meetings booked this week,
pipeline summary, stalled deals, active plans,
discovery jobs, and a prioritized recommendation.

TIME WINDOWS (UTC)
\- "last 24 hours" — `replied_at >= now - 24 hours`. Used by new_replies_count
  and recent_replies.
\- "current week"  — `meeting_booked_at >= Monday 00:00:00 UTC of the current
  week (Monday-anchored)`. Used by meetings_booked_this_week.
\- "7+ days"       — `updated_at <= now - 7 days`. Used by stalled_deals
  (deals with outcome IS NULL and updated_at older than 7 days).

Returns (units in parentheses; scope = user_company_id, no date filter on
counts unless specified):
    pending_approvals_count (int, unit=messages): messages.status='draft' for
        the tenant. NOT date-filtered — total backlog.
    messages_needing_authoring (int, unit=messages): messages.status=
        'needs_draft' — touches awaiting harness AUTHORING (no prose yet;
        run /outreach-triage). VRU-570.
    oldest_needs_draft_age_days (float|null, unit=days): age of the oldest
        unauthored engagement signal — signals TTL-dismiss at 14 days
        (VRU-671), so a high value means the queue is rotting unauthored.
    engagements_needing_authoring (int, unit=engagements):
        linkedin_engagement_queue.…

Input parameters:

- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…

### `get_deal_360` (~292 tokens)

Get Deal Compound

Get Deal Compound

Complete deal context in one call — replaces 3 separate tool calls.

Aggregates deal info, stakeholders with person details, MEDDIC qualification
state with gap descriptions, and recent activity timeline.

Like get_person_360 but for deals. Use this as the default tool for
understanding a deal's current state.

Operators: use X-Company-Id header for cross-company access.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

Input parameters:

- `deal_id` (string, required)
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `timeline_limit` (integer)

### `get_engagement_review` (~1174 tokens)

Get Engagement Review

Get Engagement Review

Default tool for engagement queue triage — pending engagements with full context.

Start here when reviewing warming comments, nurture reactions, or marketing
engagements. Returns draft engagement items enriched with: person context,
campaign instructions (tone, selling strategy), match analysis (summary,
alignment points, recommended approach), outreach plan state (warming/nurture
progress with graduation thresholds), recent engagement history, company
research, budget status, and bundle info. One call gives you everything
needed to decide approve/skip.

Use content_length='full' for detailed review (capped at 5 items),
or 'preview' (default) for quick scanning with truncated content.

Filter by source (warming/nurture/marketing/engagers) or campaign name.

Use engagement_ids to fetch specific items by ID (comma-separated UUIDs,
max 20) for subagent batch dispatch.

ENGAGER REVIEW (source='engagers', VRU-721): returns PERSONS who engaged
with our published posts and were ICP-scored, grouped per person in the
\`engagers` response field (`items` stays [] on this branch). Actionable
queue = scored_passed persons (`total_pending` counts exactly those); near
misses (scored_failed, with scores) are display-only context and excluded
from total_pending. CHECK `in_motion` BEFORE acting: it flags replied /
meeting_booked / open_deal / non-terminal-plan persons — acting on them
risks double outreach or resetting a deliberately deferred plan. Decide
each person with manage_engagements engager_actioned / engager_dismissed /
engager_reopened (person-keyed; reopen reverses a dismissal only). Act
FIRST with existing tools (manage_messages send/send_linkedin for a
one-off, manage_campaign members for a campaign add), THEN record the
decision with acted_via so attribution stays measurable. Default (no
source) responses include `pending_engagers` so triage notices new
engagers without an extra call. Queue-only filters are handled explicitly
on this branch: enga…

Input parameters:

- `campaign` (string): Filter by campaign name or UUID. Returns only engagements for people in that campaign. IGNORED when source='engagers' (engagers are not campaign-keyed until the operator acts).
- `content_length` (string): 'full' for untruncated content (auto-capped at 5 items), 'preview' (default) for truncated content.
- `engagement_ids` (string): Comma-separated engagement UUIDs for direct fetch. Bypasses sorting/pagination. Max 20.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `include_decided` (boolean): source='engagers' only: include recently decided persons (operator_actioned/operator_dismissed) so a wrong dismiss is discoverable and reversible via manage_engagements engager_reopened.
- `limit` (integer): Max items to review (up to 200)
- `near_miss_max_age_days` (integer): source='engagers' only: near misses (scored_failed) whose last engagement is older than this many days are excluded; the count of excluded persons is returned in near_misses_excluded_by_age.
- `offset` (integer): Pagination offset
- `sender_user_id` (string): Filter by LinkedIn account user ID. IGNORED when source='engagers'.
- `source` (string): Filter by source: warming, nurture, marketing, comment_reply, or engagers. VOCABULARY: an ENGAGEMENT is a queue item WE drafted (including a response to an inbound public comment reply); an ENGAGER i…

### `get_next_actions` (~321 tokens)

Get Next Actions

Get Next Actions

Prioritized next-action list for the user.

Returns actions sorted by priority: reply responses (P1), message
approvals (P2), overdue follow-ups, deals, and due/overdue tasks (P3),
unenrolled high-match prospects (P4).

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

\**Example Response:**
\```json
{
  "actions": [
    {
      "action_type": "Action Type",
      "priority": 1,
      "title": "Title",
      "description": "Description"
    }
  ],
  "total": 1
}
\```

Input parameters:

- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `limit` (integer): Max actions to return

### `get_outreach_review` (~1604 tokens)

Outreach Review

Get Outreach Review

Default tool for outreach queue triage — pending messages with full context for review.

Start here when reviewing or managing the outreach queue. Returns pending
messages — both 'needs_draft' (unauthored, awaiting harness authoring: write
via manage_messages edit) and 'draft' (authored, awaiting approve/reject),
disambiguated by each item's `status` — enriched with: person context,
campaign instructions (tone rules, selling strategy, touch sequence), match
analysis (summary, alignment points, recommended approach), company research
summary, recent LinkedIn posts, conversation thread, and outreach plan state.
One call gives you everything needed to author, approve, or reject.

\`channel_holds` reports LinkedIn sender blocks such as daily quota,
provider throttling, or reconnect-required state. If
\`channel_holds_degraded` is true, an empty hold list is unknown rather than
proof that the channel is healthy.

Use content_length='full' for detailed review (capped at 5 items to prevent
connection drops from oversized payloads — use offset to paginate),
or 'preview' (default) for quick scanning with truncated content.

Filter by campaign name or ID to review a specific queue (e.g. campaign='DFW CFOs').

Use message_ids to fetch specific messages by ID (comma-separated UUIDs, max 20).
Bypasses sorting/pagination — returns full context for exactly those messages.
Useful for subagent dispatch: pull the lightweight queue first, split into batches,
then each subagent calls with its assigned message_ids.

Operators: pass for_company parameter to review a specific client company.

Returns (units in parentheses; scope = messages.status IN ('draft','needs_draft')
for this tenant unless message_ids is set):
    items (list of ReviewItem, unit=messages): one entry per pending message
        (not per person — a person with two pending messages shows up twice).
        Each item:
        - message_id, status ('needs_draft' = author it, 'draft' = approve it),…

Input parameters:

- `campaign` (string): Filter by campaign name or ID. Example: 'DFW CFOs' or a UUID. Returns only drafts for people in that campaign.
- `content_length` (string): 'full' for untruncated conversation content, 'preview' (default) for 200-char truncation. When 'full', limit is auto-capped at 5 to prevent oversized responses — use offset to paginate.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `limit` (integer): Max messages to review (up to 200)
- `message_ids` (string): Comma-separated message UUIDs to fetch specific messages. Bypasses sorting/pagination — returns full context for exactly these messages. Example: 'uuid1,uuid2,uuid3'. Max 20.
- `offset` (integer): Pagination offset

### `get_performance_metrics` (~1941 tokens)

Get Performance Metrics

Get Performance Metrics

Get outreach performance metrics: time-series trends and/or conversion funnel.

Set view='timeseries' for daily counts of a single metric (contacts/people/replies/meetings).
Set view='funnel' for aggregate conversion data: contacted->replied->met funnel with rates,
reply rates by channel, and LinkedIn pipeline breakdown.
Set view='both' for everything in one call.

For T1s sent over time: view='timeseries', metric='contacts'.
For reply rate and funnel: view='funnel'.
Operators: pass for_company to get metrics for a specific client.

UNIT CONVENTIONS USED IN RESPONSES
Every count field is one of {messages, unique_people, plans, campaigns, days}; see the
Returns block for the unit attached to each field. Note that timeseries 'contacts' and
funnel.contacted are BOTH unique_people (i.e. unique people, deduped per person_id),
NOT message counts.

DATE-FILTER COLUMN PER METRIC (timeseries)
\- metric='people':    filters on company_people.created_at — start_date / end_date
\- metric='contacts':  filters on messages.sent_at (sequence_number=1 only) — start_date / end_date
\- metric='replies':   filters on company_people.replied_at — start_date / end_date
\- metric='meetings':  filters on company_people.meeting_booked_at — start_date / end_date
NOTE: end_date is IGNORED by reply_rates_by_channel in the direct (no campaign_id)
mode — see the `end_date` query-param description for details.

Returns (selected fields, with unit + scope + date-filter annotations):
    view (str):
        Echoes the requested view.

    timeseries (object, when view='timeseries' or 'both'):
        metric (str): The metric name.
        data (list of {date, count}):
            date (str): YYYY-MM-DD.
            count (int): unit = unique_people for ALL four metrics (T1-sent dedupe by
            person; company_people rows for people/replies/meetings).

    funnel (object, when view='funnel' or 'both'):
        total_people (int, unit=unique_people, scope=non-archived compa…

Input parameters:

- `campaign_id` (string): Filter results to a single campaign (UUID). Applies to all funnel counts and per-channel rates; for timeseries it applies to all metrics.
- `end_date` (string): End date (YYYY-MM-DD), inclusive on company_people queries (funnel) / inclusive via next-day exclusive bound on messages queries (timeseries 'contacts'). NOTE: end_date is currently IGNORED by reply_…
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `metric` (string): Metric for timeseries view. Each value documents its own scope + date-filter column (units in parentheses): 'people' (unique_people) = new company_people rows for this tenant, filtered on company_peo…
- `start_date` (string): Start date (YYYY-MM-DD), inclusive. Filters on the metric-specific timestamp column documented in `metric` for timeseries view; filters last_message_sent_at (contacted), replied_at (replied), meeting…
- `view` (string): What to return: 'timeseries' for daily counts of a single metric, 'funnel' for aggregate conversion data, 'both' for everything in one call.

### `get_person_360` (~307 tokens)

Get Person 360

Get Person 360

Complete person context in one call.

Aggregates person info, match analysis, research, activity timeline,
outreach plan state, and deal info — replacing 5 separate tool calls.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

\**Example Response:**
\```json
{
  "person": {
    "person_id": "Person Id",
    "current_positions": [
      {}
    ]
  },
  "activity": [
    {
      "activity_type": "Activity Type",
      "description": "Description"
    }
  ]
}
\```

Input parameters:

- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `person_id` (string, required)

### `get_revenue` (~308 tokens)

Get Revenue

Get Revenue

Read Stripe-backed revenue truth.

Views:
\- connection: provider connection and freshness state.
\- metrics: current gross MRR/ARR, net charge volume, collections and health.
\- evidence: recent immutable ingestion evidence for calculation drilldown.
\- bindings: suggested, confirmed, or rejected commercial-object bindings.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

Input parameters:

- `binding_status` (string)
- `currency` (string)
- `cursor` (string): Opaque evidence pagination cursor from the previous next_cursor.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `limit` (integer)
- `metric` (string)
- `view` (string, required)

### `import_prospects` (~681 tokens)

Import Prospects

Import Prospects

Source and import prospects — the consolidated import tool.

Sales Navigator actions (`payload` = the search/import fields):
\- sales_nav_search: search LinkedIn people ({keywords, mode?,
  title?, company_headcount_min/max?, list_id?, saved_search_id?,
  limit?, cursor?}). `mode` must be `basic` or `sales_nav`; use
  \`basic` for exact-name/profile resolution fallback when Sales Nav is
  unavailable.
\- sales_nav_preview: preview a Sales Nav search against your pipeline
  ({search?, count?, campaign?}).
\- sales_nav_import: import profiles ({search?, count?, profiles_json?,
  auto_enroll?, campaign?}). `profiles_json` accepts sales_nav_search
  output directly: each object needs linkedin_url plus either
  first_name/last_name or a single `name` field (mode=basic results);
  \`headline` is used as a title fallback.
\- sales_nav_dismiss: dismiss profiles from future imports
  ({linkedin_urls} — comma-separated).
\- sales_nav_searches: manage saved searches ({action: list|create|
  update|delete, name?, keywords?, filters_json?, frequency?,
  search_id?}).

CSV actions:
\- csv_preview: parse + preview a CSV payload ({csv_content, …}) before
  importing.
\- csv_start: start an import job ({file_content, column_mapping,
  campaign_id?, skip_research?}). Returns a job id — poll it with
  fetch type='csv_import'.

Payloads are validated by the underlying route's own schema, so a bad
field returns that route's precise 422.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

### `inspect_pipeline` (~304 tokens)

Inspect Pipeline

Inspect Pipeline

Risk-first pipeline view. Returns the 5 most at-risk deals with risk scores and factors.

Risk heuristics: silence (14d=+35, 7d=+20), overdue next step (+5/day max 25),
no next step (+15), past close date (+20), low qualification (+10), no qualification (+5),
single stakeholder (+10).

Also returns pipeline health (healthy/some_risk/critical), total active deals, and total value.
Operators: use X-Company-Id header for cross-company inspection.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

Input parameters:

- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…

### `manage_account` (~512 tokens)

Manage Account

Manage Account

Maintain account state and impact history — the consolidated account writer.

Actions:
\- state: update an account's lifecycle state (id = the account's
  company_id; `payload` = {account_stage?, health_score?, arr_current?,
  renewal_at?, notes?, …}).
\- record_impact: log a value-delivered impact event on an account
  (id = company_id; `payload` = {practice, event_type, person_id?,
  value_delivered_numeric?, summary?, …}).
\- delete_impact: permanently remove an impact event (id = the impact
  EVENT/activity id, not the account). DESTRUCTIVE.

Bulk: id arrays loop with per-item status (partial failure reported per
item, never silent; max 100). Payloads are validated by the underlying
route's schema.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

### `manage_campaign` (~1833 tokens)

Manage Campaign

Manage Campaign

Create and manage campaigns — the consolidated campaign writer.

Use `search` with `type='campaigns'` to list campaigns. Use `fetch` with
\`type='campaign'` and `id=<campaign_uuid>` to read one campaign. Use this
writer for every campaign mutation and diagnosis.

Outreach actions (default kind; id = campaign UUID):
\- create: create a campaign (no id; `payload` = the campaign fields);
  bulk via `items`.
\- clone: duplicate a campaign (`payload` = {name} for the copy).
\- update: update campaign fields — tone, cadence, targeting (`payload`).
\- delete: permanently delete a campaign. DESTRUCTIVE.
\- members: assign/unassign people (`payload` = {action: assign|unassign,
  person_ids: [...], confirm?}).
\- diagnose: run the campaign health diagnosis (read-back; no payload).
\- pause: campaign-level kill switch (no payload). Sets the campaign paused
  AND stops every active plan under it in one call — no per-plan pause
  loop. Returns {plans_paused}. `id` may be an array to pause several
  campaigns.
\- resume: clear the pause and restart ONLY the plans this campaign paused
  (reschedules them for immediate execution). Plans paused for a reply,
  OOO, or rejected draft — or paused individually — are left untouched.
  No payload; returns {plans_resumed}.

Ad actions (kind='ad'; id = ad-campaign UUID): approve, reject, pause,
resume, edit_budget — `payload` carries the action's fields (e.g.
{daily_budget_cents} for edit_budget, {rejection_reason} for reject).
boost (no id — there's no campaign yet): run a paid LinkedIn campaign.
\`payload` carries {content_post_id (sponsor an EXISTING published post)
XOR creative_id (a stored VIDEO creative → Direct Sponsored Content,
METRICS-ONLY — no engager bridge), vehicle?, objective?,
budget:{daily_budget_cents/total_budget_cents}, audience (see below),
duration_days?, approval_mode?('draft'|'auto'), integration_id?}.

Boost audience — exactly one of:
\- {facets}: the PRIMARY form. LinkedIn-native firmographic targeting: a…

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `kind` (string): Campaign family: 'outreach' (default) for outreach campaigns, or 'ad' to manage ad campaigns (actions: approve, reject, pause, resume, edit_budget, boost, store_creative, set_page, authorize_author,…
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

### `manage_content` (~1305 tokens)

Manage Content

Manage Content

Draft, schedule and publish LinkedIn content — the consolidated content writer.

Actions (id = post UUID unless noted):
\- draft: create a post draft (no id; `payload` = {content, topic_tags?,
  scheduled_at?, author_user_id?, author_identity?, external_link?,
  attachment_creative_id?}); bulk via `items`.
\- edit: update a draft's content/tags/identity/link/attachment
  (`payload` = the fields to change: content?, topic_tags?,
  author_identity?, external_link? — empty-string external_link clears
  it; attachment_creative_id? — explicit null DETACHES, absent key
  leaves it untouched).
\- schedule: set the publish time (`payload` = {scheduled_at}). A
  row-persisted attachment publishes with the scheduled post.
\- publish: publish the post now (no payload; the retired publish-time
  creative_id param is rejected with the pointer to edit-attach).
\- delete: remove a draft from the calendar (drafts only — published
  posts are not deletable here).
\- draft_post: never generates prose — backend generation is removed
  (VRU-676, permanently). Always returns author-in-harness guidance:
  write the post yourself and submit via draft, then schedule/publish
  (no id; `payload` = {topic?, content_type?, author_user_id?}).

Attachments (VRU-726) — document (PDF carousel), image, or video, ONE
contract, persisted on the row (schedulable + reviewable). Golden path
for a PDF, entirely via MCP:
  1\. manage_campaign kind='ad' action='store_creative'
     payload={filename:'my-deck.pdf', size_bytes:<n>,
     content_type:'application/pdf'} → returns upload_url
  2\. curl --fail-with-body -T my-deck.pdf '<upload_url>'
  3\. store_creative again with payload={creative_id:'<id>'} → validates
     (small assets can skip 1-3: payload={asset_base64:'<b64>'})
  4\. manage_content action='draft' payload={content:'<caption>',
     attachment_creative_id:'<id>'}
  5\. get_content_review post_ids=['<post_id>'] → OPEN attachment_url and
     review the actual file (it publishes under t…

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

### `manage_deal` (~863 tokens)

Manage Deal

Manage Deal

Create and manage deals through the pipeline — the consolidated deal writer.

Actions (id = deal UUID):
\- create: open a deal (no id; `payload` = {deal_name, person_id?,
  company_id?, deal_source? (outbound|inbound|referral|manual|imported;
  default manual), estimated_value?, stage?, pipeline_id?, stage_id?,
  close_probability?, notes?}). `pipeline_id` and `stage_id` must be
  supplied together; bulk via `items`.
\- update: patch deal fields (`payload` = any of deal_name,
  estimated_value, close_probability, expected_close_date, next_step,
  notes, stage, …).
\- stage: advance/move the deal — sugar for update; `payload` must include
  {stage}.
\- qualify: run AI MEDDIC-style qualification on the deal (no payload).
\- stakeholders: add/update/remove a deal stakeholder (`payload` =
  {action: add|update|remove, person_id?, role?, notes?}).
\- won / lost / stalled: record the deal outcome (`payload` optional:
  {loss_reason?, win_factors?}).
\- meeting_outcome: record how the meeting went (`payload` = the outcome
  fields).
\- reopen: reopen a closed deal (`payload` optional: {stage}).
\- create_pipeline: create a pipeline (no id; `payload` = {name,
  is_default?, position?, external_id?, stages?}).
\- rename_pipeline / archive_pipeline / unarchive_pipeline /
  set_default: `id` = pipeline UUID. Rename takes
  \`payload={name}`; the others take no payload.
\- reorder_pipelines: no id; `payload={ordered_pipeline_ids}`.
\- create_stage: `id` = pipeline UUID; `payload={name, position?,
  stage_kind?, probability_default?, external_id?}`.
\- update_stage / archive_stage / unarchive_stage: `id` = pipeline
  UUID and `payload.stage_id` = stage UUID. Update also accepts name,
  stage_kind, and probability_default.
\- reorder_stages: `id` = pipeline UUID;
  \`payload={ordered_stage_ids}`.

Bulk: `id` arrays apply the same action+payload per deal (server-side
loop, per-item status — partial failure is reported per item, never
silent; max 100). Payloads are validated by the u…

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

### `manage_engagements` (~1081 tokens)

Manage Engagements

Manage Engagements

Triage the LinkedIn engagement queue — the consolidated engagement writer.

Actions (id = engagement UUID; arrays route natively — the underlying
endpoint is bulk-shaped):
\- approve: approve engagement(s) for sending (`payload` optional extras,
  e.g. {polish_provenance}).
\- edit: replace an engagement's content (`payload` = {content,
  content_mode?, reaction_type?}).
\- skip: discard engagement(s) — the queue's reject path (no new
  engagement is generated).
\- draft_comment: queue a comment on a LinkedIn post for AUTHORING
  (creates a needs_draft entry — write the comment via edit, then
  approve; no id; `payload` = {post_text, post_id, author_name?,
  person_id?, tone?, …}).

Bulk: approve/edit/skip take id arrays natively (max 100) with the
endpoint's own per-engagement results. Payloads are validated by the
underlying route's schema.

Prose gate: approve and edit re-lint the content server-side and can
return error_code='prose_gate_blocked' with structured `failures[].fix`.
\`payload` accepts `reason` (corpus note), `override_reason` (taste
override — proceeds despite block failures, logged to the corpus;
honored only for owner/operator roles on the tenant, otherwise ignored
and the gate blocks normally), and `client_rules_version` (responses
flag `rules_changed` when the server's rules differ). Pre-check drafts
cheaply with check_prose.

ENGAGER DECISIONS (VRU-721 — id = PERSON UUID, not an engagement id):
\- engager_actioned: records a decision already executed via
  manage_messages / manage_campaign — performs no outreach itself.
  \`payload` = {acted_via?: {campaign_id | message_id}, note?}. Order
  matters: act FIRST (manage_campaign members to add to a campaign, or
  manage_messages send/send_linkedin for a one-off — the send returns the
  message_id), THEN record with acted_via so the engager attribution
  funnel stays measurable. Without acted_via the response carries an
  \`unattributed` warning. Actioning a sub-70 near miss mints thei…

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

### `manage_kb` (~480 tokens)

Manage KB

Manage Kb

Maintain the knowledge base — the consolidated KB writer.

Actions (id = document UUID):
\- upsert: create or update a document (no id — the payload's `name` is
  the upsert key; `payload` = {name, content, doc_type?, summary?,
  metadata?}); bulk via `items`.
\- delete: permanently delete a document. DESTRUCTIVE.
\- reindex: rebuild a document's search index (no payload).

Bulk: delete/reindex take id arrays (server-side loop, per-item status —
partial failure reported per item, never silent; max 100). Payloads are
validated by the underlying route's schema.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

### `manage_messages` (~2154 tokens)

Manage Messages

Manage Messages

Triage and edit outreach messages — the consolidated message writer.

Actions (id = message UUID unless noted):
\- approve: mark a draft READY TO SEND. Sending happens asynchronously and
  respects the campaign's designed schedule plus a per-person cooldown —
  approval may SCHEDULE rather than fire immediately. The response's
  \`processing_state` says which ('queued_for_send', 'scheduled',
  'held_for_channel', or 'channel_state_unknown'). A hold/unknown state
  includes machine-readable `channel_hold`
  quota/reconnect feedback and `send_not_before` when it auto-resumes;
  a scheduled/held message is NOT lost and must NOT be re-approved or
  re-drafted (get_outreach_review shows `channel_holds`, the plan's
  \`plan_next_action_at`, and `approved_pending_send_count`). An id
  ARRAY routes to the native bulk endpoint with per-message results.
\- reject: reject a draft (array → native bulk, same as approve).
\- edit: replace subject/content (`payload` = {subject?, content?}).
  Scalar id ONLY — one call per message (an id array is rejected;
  sending identical content to many people is not a supported flow).
  Optional `attachments` sets the media sent with this draft
  (linkedin_inmail / linkedin_message / email only): a list where each item
  is a new upload {content_base64 (raw base64, no data: prefix; PNG/JPEG/GIF/
  WebP/PDF, 10MB max), filename} OR {storage_path} to keep one the draft
  already has (from get_outreach_review). Omit = leave unchanged, [] = remove
  all, max 5. Attachments on any other channel are rejected.
\- regenerate: return the message to the authoring queue (status becomes
  needs_draft, content cleared) — author the replacement at triage via
  edit (no payload); id array loops with per-item status. Works on
  draft, rejected, AND failed rows — regenerate is the recovery path
  for a send the provider bounced (attachments survive; re-author,
  then re-approve). The row gets a FRESH 14-day authoring window; the
  response's expir…

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

### `manage_outreach` (~640 tokens)

Manage Outreach

Manage Outreach

Start and steer outreach plans — the consolidated outreach writer.

Actions:
\- start: enroll a person in outreach. id = PERSON UUID (scalar starts
  one plan; an id ARRAY routes to the native bulk-start endpoint).
  \`payload` optional: {max_touches?, allowed_channels?} — applied to
  every started plan. Enrollment always schedules the first action
  immediately; a `start_immediately` key is ignored (forced true).
\- plan_override: record a user override on a plan (id = plan UUID;
  \`payload` = the override fields).
\- channels: update a plan's allowed channels (id = plan UUID; `payload`
  \= {allowed_channels, …}).
\- trigger_touch: queue the plan's next touch now (id = plan UUID; no
  payload).
\- pause / resume: pause or resume a plan (id = plan UUID; `payload`
  is not needed).
\- update: update an active or paused plan without changing its execution
  state (id = plan UUID; `payload` = {max_touches?, allowed_channels?}).
  Use an id ARRAY to repair a campaign cohort consistently.

Bulk: `start` arrays hit the native bulk endpoint; pause/resume/
plan_override/channels/trigger_touch arrays loop with per-item status —
partial failure is reported per item, never silent (max 100). Payloads
are validated by the underlying route's own schema.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

### `manage_person` (~1125 tokens)

Manage Person

Manage Person

Create and manage people in your pipeline — the consolidated person writer.

Actions (id = person UUID unless noted):
\- create: add a person manually (no id; `payload` = the manual-person
  fields, e.g. {name, company, title, email, linkedin_url, skip_research});
  bulk via `items` (one payload per person).
\- update_contact: override contact fields (`payload` = contact overrides).
\- set_position: set the primary position (`payload` = position fields).
\- note: add a timeline note (`payload` = {body}).
\- log_interaction: log a manual touch that happened outside an automated
  sequence — a call (with disposition + what was said), or an
  email/LinkedIn/other touch made elsewhere. `payload` =
  {interaction_kind: call|email|linkedin|meeting|other, direction?:
  outbound|inbound, summary?, disposition? (calls only), occurred_at?
  (ISO, backdatable), deal_id?}. Lands on the person's timeline attributed
  to you (performed_by_user_id).
\- archive: soft-delete one or many people — stops outreach plans and
  rejects pending drafts (id or id array; no payload). DESTRUCTIVE.
\- unarchive: restore archived people (id or id array; no payload).
\- meeting_booked: mark a meeting booked with this person (`payload` =
  {idempotency_key: UUID}; reuse it when retrying the same booking).
\- save_discovered: THE creator — save a researched prospect into the
  pipeline, atomically (no id; bulk via `items`). Two shapes:
  NEW prospect: `payload` = {person: {first_name, last_name,
  linkedin_url|email, company_id (from save_company) or
  company_name+anchor, ...research fields}, assessment: {match_score
  0-100, match_summary, alignment_points?, concerns?, why_now?,
  recommended_approach?, overall_confidence?, scored_by?}, campaign_id or
  assessment_campaign_id}. Person + research + pipeline membership land in
  ONE transaction — a rejected or failed save persists nothing.
  EXISTING person: `payload` = {person_id, campaign_id?,
  assessment_campaign_id?, assessment?} — the…

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

### `manage_relationship_action` (~770 tokens)

Manage Relationship Action

Manage Relationship Action

Draft, approve, and record the outcome of ONE warm-introduction attempt.

A "relationship action" is a single attempt to reach a target person
\*through a mutual connection* rather than by contacting them directly:
asking a colleague for an intro, checking whether a connector actually
knows them, or requesting an entry into their company. `find_warm_path`
discovers those routes and never writes; this tool is the only way to act
on one.

\**Use this instead of the direct-outreach tools when the path runs through
a person.** `manage_outreach` and `manage_messages` send to the target
themselves; `manage_engagements` handles reactions and comments on
content. This tool messages the *connector*.

\**Nothing here auto-sends.** Every attempt walks a state machine you drive
one call at a time: `draft` creates it from a `result_ref` returned by
\`find_warm_path`, then transitions approve the exact message text, confirm
that you sent it yourself outside Vruum, classify how the connector
replied, record the final outcome, or cancel. The response's
\`approval_required` and `next_actions` fields tell you which transitions
are legal right now — read them rather than guessing.

Two consistency rules the server enforces:

\- **`draft` is idempotent on `client_request_id`.** Reuse the same value
  when retrying a draft; a new value creates a second attempt.
\- **Every transition needs `expected_version`.** A stale version means
  someone else moved the attempt and yours is rejected rather than
  silently overwriting — re-read the attempt and retry.

A `409 route_stale` on draft means the underlying relationship evidence
changed since it was reviewed; re-run `find_warm_path` and draft from a
fresh `result_ref`.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

\**Example Response:**
\```json
{
  "status": "Status",
  "attempt": {
    "ref": "Ref",
    "relationship_owner_user_id": "Relationship Owner User Id",
    "ac…

Input parameters:

- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…

### `manage_revenue` (~463 tokens)

Manage Revenue

Manage Revenue

Manage the tenant's Stripe revenue connection.

Actions:
\- connect: return the Stripe App OAuth URL ({destination, livemode}).
\- reconcile: enqueue an idempotent provider scan ({idempotency_key,
  object_family}).
\- replay: retry one failed evidence record (id = evidence record UUID).
\- decide_binding: confirm or reject one suggestion (id = binding UUID,
  payload = {decision}).
\- disconnect_preview: return a short-lived nonce and exact retention impact.
\- disconnect: destroy delegated credentials using that nonce
  ({preview_nonce}); historical ledger evidence is retained.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

### `manage_settings` (~661 tokens)

Manage Settings

Manage Settings

Update company configuration — the consolidated settings writer.

Actions (no ids — settings are singletons per company):
\- profile: update the company profile / ICP (`payload` = the profile
  fields; this steers research match scoring).
\- auto_fill: AI-fill the company profile from the website (`payload`
  optional: {website_url?}; returns a job to poll via
  fetch type='job').
\- automation: update outreach automation settings (`payload` = the
  automation fields).
\- marketing: update LinkedIn marketing settings (`payload` = the
  marketing fields).
\- cta_link: manage call-to-action links (`payload` = {action:
  create|update|delete, cta_link_id?, name?, url?, cta_type?,
  is_active?}).
\- hubspot_sync_settings: set the custom HubSpot contact properties to
  mirror into company_people.custom_fields (`payload` =
  {extra_contact_properties: [...]}); CHANGING the list auto-enqueues a
  full backfill so existing contacts pick up the new attributes.
\- hubspot_backfill: kick a full HubSpot re-pull for this company (owners
  → companies → pipelines → contacts → deals → lists → engagements). No
  payload. Heavy (hours at 250k+ contacts). The recovery path when
  contacts were imported before their custom properties were configured.
  Operators target a client with `for_company`.

Payloads are validated by the underlying route's own schema, so a bad
field returns that route's precise 422.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

### `research` (~988 tokens)

Research

Research

Run prospect/company research and save the results — the consolidated research tool.

Lookup actions (external providers — results are returned, not stored,
unless noted):
\- find_linkedin: locate a person's LinkedIn URL (id = person UUID;
  \`payload` optional {name_override?, company_override?, auto_save?} —
  auto_save stores a high-confidence match).
\- enrich_company: scrape a company website (`payload` = {domain,
  page_type?}).
\- find_email: find a work email (`payload` = {first_name, last_name,
  domain}).
\- linkedin_fetch: pull a LinkedIn profile (`payload` = {linkedin_url,
  include_company?, include_posts?, posts_limit?}).
\- careers_parse: parse a careers page for hiring signals (`payload` =
  {domain}).

Save actions (write research rows):
\- save_person: UPDATE research on an EXISTING person — pass the person UUID
  as `payload.person_id` (preferred; refreshes researched_at and the
  research fields in place), or matching email/linkedin anchors. Cannot
  create: new prospects go through manage_person action=save_discovered
  with `payload.person` + `payload.assessment`. Never pass the person UUID
  as `id` — this action takes no id.
\- save_company: atomically patch sourced public company research. Pass
  \`payload={idempotency_key, name, person_id?, website?, linkedin_url?,
  company_summary?, company_stage?, current_priorities?, funding_data?,
  growth_metrics?, sources_by_field}`. Omitted fields are preserved;
  explicit null clears. When researching a specific person's employer,
  ALWAYS pass their UUID as `payload.person_id` — the response's
  \`person_link` confirms the write landed on that person's linked company
  (repointing an anchorless duplicate link when needed); a `mismatch`
  status means the person's touches will NOT see this research, and a
  \`conflict` status is a transient race — replay the identical payload
  (person_id is exempt from the idempotency hash, so adding it to a
  replay is the supported repair).
  \`sources_by_field`…

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

### `search` (~739 tokens)

Search

Search

Polymorphic read-only search across your Vruum workspace.

One tool for every list/queue/search read. Pass `type` to pick the entity:
people (your pipeline — keyword `query`, batch dedup array, or list-mode
\`filters`), companies (EXTERNAL Hunter.io domain search for
decision-makers — requires filters.domain; 503 if Hunter.io is not
configured), deals, campaigns, messages (outreach queue), engagements
(LinkedIn engagement queue), content (LinkedIn content calendar), kb
(knowledge-base search), skills (published skills), cta_links, warm_paths
(open targets that have a warm intro path — see find_warm_path for one
target), relationship_attempts (the durable action timeline shared with
the Relationships workspace).

All types except `companies` read internal, tenant-scoped data only.
Pagination (`limit`/`offset`) is supported for people (list mode), deals,
messages, engagements, warm_paths, and relationship_attempts; other types reject it. Results are returned
under `{type, results}` with each entity type's native payload shape.

Campaign routing: use `type='campaigns'` here to list campaigns (newest
first, compact by default — the prose-heavy AI-strategy fields come back
null; see filters.fields). To read one campaign by UUID with its full
config, use `fetch` with `type='campaign'`. To create, update, clone,
delete, diagnose, or manage campaign members, use `manage_campaign`.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

Input parameters:

- `filters` (object): Type-specific filters — the object shape is determined by `type` (e.g. {domain} for companies, {stage, outcome, query} for deals). Unknown keys are rejected with 422.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `limit` (integer): Page size. Supported for people, deals, messages, warm_paths, and relationship_attempts (max 100), plus engagements (max 200); people keyword-query mode caps at 40. Rejected (422) for other types.
- `offset` (integer): Pagination offset. Supported for people (list mode), deals, messages, engagements, warm_paths, and relationship_attempts. Rejected (422) for other types and for people query mode.
- `query`: Only for type='people'. A keyword string searches the existing pipeline (name/title/company); an array of {name?, company?, linkedin_url?} objects runs a batch dedup lookup (results in original order…
- `type` (string, required): The relationship-aware entity types the ``search`` facade can read.

### `skill` (~458 tokens)

Skill

Skill

Invoke or publish Vruum skills — the consolidated skill tool.

Actions:
\- invoke: fetch a skill's executable body for inline execution
  (id = skill UUID or name; no payload). Follow the returned
  instructions in your current context.
\- publish: publish or update a skill (no id; `payload` = {body,
  scope?, metadata?, supporting_files?} — `body` is the full skill
  markdown with frontmatter).

To browse skills use search type='skills'; to read one without
executing it use fetch type='skill'.

\### Responses:

\**200**: Successful Response (Success Response)
Content-Type: application/json

Input parameters:

- `action` (string, required): The operation to perform — see the tool description for the action vocabulary and each action's id/payload semantics.
- `for_company` (string): Optional company ID or name (UUID, or case-insensitive EXACT name match — partial / substring matches are NOT supported; ambiguous names raise 400) to execute this tool as. Use get_operator_overview…
- `id`: Target entity id, or an ARRAY of ids to apply the same action+payload to each (max 100; see the tool description for which entity the id names per action).
- `items` (array): For create-like actions only: an array of per-item payloads processed in order with per-item results (max 100). Mutually exclusive with `id` and `payload`.
- `payload` (object): Action-specific fields, validated by the underlying route's own schema (a bad field 422s with that route's precise errors). See the tool description per action.

## Diagnostics

Captured diagnostic sections: Provenance. The full working is on the page: https://verifymcp.io/servers/ai-vruum-mcp/vruum-mcp#diagnostics

## Score history

- 2026-08-04: 47
- 2026-08-03: 57

## Links

- npm package: https://www.npmjs.com/package/@vruum/mcp
- Socket report: https://socket.dev/npm/package/@vruum/mcp
- Repository: https://github.com/vruum-gtm/mcp
- Changelog RSS feed: https://verifymcp.io/servers/ai-vruum-mcp/vruum-mcp/changelog.xml
- Changelog JSON feed: https://verifymcp.io/servers/ai-vruum-mcp/vruum-mcp/changelog.json
- HTML version of this page: https://verifymcp.io/servers/ai-vruum-mcp/vruum-mcp
