@vruum/mcp 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +140 -23
  2. package/package.json +1 -1
  3. package/tools.json +7 -3
package/README.md CHANGED
@@ -1,13 +1,39 @@
1
1
  # Vruum MCP (`@vruum/mcp`)
2
2
 
3
- Official MCP access to [Vruum](https://vruum.ai) — the AI revenue platform. Operate outbound, deals, pipeline, and CRM automation from your agent: people, deals, outreach, engagement, and research tools over one MCP.
3
+ [![npm](https://img.shields.io/npm/v/@vruum/mcp)](https://www.npmjs.com/package/@vruum/mcp)
4
+ [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
5
+ [![registry](https://img.shields.io/badge/MCP%20registry-ai.vruum%2Fmcp-6E56CF)](https://registry.modelcontextprotocol.io)
4
6
 
5
- This package is the **stdio bridge** to the hosted Vruum MCP server. It serves the advertised tool surface locally (no credentials needed to introspect) and proxies tool execution to `https://api.vruum.ai/mcp` under your Vruum token. Every call is authorized server-side; the bridge grants no authority beyond what your Vruum account can already do.
7
+ Official MCP access to [**Vruum**](https://vruum.ai), the AI revenue platform. Give your agent the whole revenue motion — research prospects, build pipeline, run email and LinkedIn outreach, triage replies, manage deals through close, and read Stripe-backed revenue truth — through **29 compound tools** rather than a sprawl of endpoints.
6
8
 
7
- > **Connecting a modern client? Prefer the remote server directly.**
8
- > Clients that support remote MCP (Claude Code, Claude Desktop, Cursor, Codex, …) should connect straight to `https://api.vruum.ai/mcp` (OAuth 2.1, or `Authorization: Bearer vk_live_…`). This bridge exists for stdio-only clients and for tool-surface introspection. See [vruum.ai/docs/mcp](https://vruum.ai/docs/mcp).
9
+ This package is a **stdio bridge**: it serves the tool surface locally (no credentials needed to introspect) and proxies execution to `https://api.vruum.ai/mcp` under your token. Every call is authorized server-side — the bridge grants no authority your Vruum account doesn't already have.
9
10
 
10
- ## Install & connect
11
+ > [!TIP]
12
+ > **Using a client that supports remote MCP? Connect directly instead.**
13
+ > Claude Code, Claude Desktop, Cursor, Codex and friends should point straight at `https://api.vruum.ai/mcp` (OAuth 2.1). Fewer moving parts and no token in an env var. This bridge exists for **stdio-only clients** and for **credential-free tool introspection**. See [vruum.ai/docs/mcp](https://vruum.ai/docs/mcp).
14
+
15
+ ## Quickstart
16
+
17
+ You need Node 20+ and a Vruum account. Create a personal access token in the web app under **Settings → API tokens** (`vk_live_…`).
18
+
19
+ <details open>
20
+ <summary><b>Claude Desktop / Claude Code</b> — <code>claude_desktop_config.json</code> or <code>.mcp.json</code></summary>
21
+
22
+ ```json
23
+ {
24
+ "mcpServers": {
25
+ "vruum": {
26
+ "command": "npx",
27
+ "args": ["-y", "@vruum/mcp"],
28
+ "env": { "VRUUM_MCP_TOKEN": "vk_live_…" }
29
+ }
30
+ }
31
+ }
32
+ ```
33
+ </details>
34
+
35
+ <details>
36
+ <summary><b>Cursor</b> — <code>~/.cursor/mcp.json</code></summary>
11
37
 
12
38
  ```json
13
39
  {
@@ -20,46 +46,137 @@ This package is the **stdio bridge** to the hosted Vruum MCP server. It serves t
20
46
  }
21
47
  }
22
48
  ```
49
+ </details>
50
+
51
+ <details>
52
+ <summary><b>Codex CLI</b> — <code>~/.codex/config.toml</code></summary>
53
+
54
+ ```toml
55
+ [mcp_servers.vruum]
56
+ command = "npx"
57
+ args = ["-y", "@vruum/mcp"]
58
+ env = { VRUUM_MCP_TOKEN = "vk_live_…" }
59
+ ```
60
+ </details>
61
+
62
+ <details>
63
+ <summary><b>Already use the Vruum CLI?</b> — no token needed here</summary>
64
+
65
+ ```sh
66
+ npx @vruum/cli
67
+ vruum login --token vk_live_…
68
+ ```
69
+
70
+ Credentials land in `~/.vruum/credentials` and the bridge picks them up automatically — omit the `env` block entirely.
71
+ </details>
72
+
73
+ Verify it works without configuring anything:
74
+
75
+ ```sh
76
+ npx -y @modelcontextprotocol/inspector --cli npx -y @vruum/mcp --method tools/list
77
+ ```
78
+
79
+ ## What you can ask for
80
+
81
+ Once connected, these are ordinary requests to your agent:
82
+
83
+ | You say | What happens |
84
+ | --- | --- |
85
+ | *"What should I work on today?"* | `get_daily_briefing` — pending approvals, new replies, stalled deals, warm paths, one recommended next action |
86
+ | *"Review my outreach drafts"* | `get_outreach_review` → you approve, edit, or reject each one |
87
+ | *"Research Acme Corp and tell me if they fit"* | `research` — website, funding, careers signals, ICP match with reasoning |
88
+ | *"Find a warm intro to this person"* | `find_warm_path` — separates verified paths from unverified connector candidates |
89
+ | *"Which campaigns are actually working?"* | `get_campaign_outcomes` — contacted, replies, meetings booked, cohort-consistent |
90
+ | *"What's at risk in my pipeline?"* | `inspect_pipeline` — the 5 most at-risk deals, risk-first |
91
+ | *"Draft a LinkedIn post about X"* | `manage_content` — your agent writes it, you approve, Vruum schedules and publishes |
92
+
93
+ > [!IMPORTANT]
94
+ > **Your agent writes the prose — the server never does.** Outreach copy, replies, LinkedIn posts and comments surface to your harness as work items. Vruum schedules, gates, persists and sends; it has no server-side message generation. That's a deliberate design position, not a gap.
95
+
96
+ ## Tool surface
23
97
 
24
- Get a token in the Vruum web app under **Settings → API tokens**. A Vruum account is required to execute tools; listing tools works without one.
98
+ 29 compound tools — one per decision, so an agent never disambiguates between overlapping verbs. `read` is safe to call freely; `write` mutates; `destructive` can delete or archive.
25
99
 
26
- Already using the CLI? `npx @vruum/cli` → `vruum login --token vk_live_…` stores credentials in `~/.vruum/credentials`, and the bridge picks them up automatically.
100
+ | Area | Tools |
101
+ | --- | --- |
102
+ | **Daily operating** | `get_daily_briefing` · `get_next_actions` · `inspect_pipeline` |
103
+ | **Search & research** | `search` · `fetch` · `research` · `import_prospects` · `find_warm_path` |
104
+ | **People** | `get_person_360` · `manage_person` |
105
+ | **Outreach** | `get_outreach_review` · `manage_messages` · `manage_outreach` · `manage_relationship_action` |
106
+ | **Engagement & content** | `get_engagement_review` · `manage_engagements` · `get_content_review` · `manage_content` |
107
+ | **Campaigns & performance** | `manage_campaign` · `get_campaign_outcomes` · `get_performance_metrics` |
108
+ | **Deals** | `get_deal_360` · `manage_deal` |
109
+ | **Revenue** | `get_revenue` · `manage_revenue` |
110
+ | **Accounts & knowledge** | `manage_account` · `manage_kb` |
111
+ | **Config & skills** | `manage_settings` · `skill` |
27
112
 
28
- ### Configuration
113
+ Full schemas, descriptions and MCP safety annotations live in [`tools.json`](tools.json) — generated from the live server definition, never hand-edited.
114
+
115
+ ## Configuration
29
116
 
30
117
  | Env var | Meaning | Default |
31
118
  | --- | --- | --- |
32
- | `VRUUM_MCP_TOKEN` | Vruum personal access token | — (falls back to `VRUUM_TOKEN`, then `~/.vruum/credentials`) |
119
+ | `VRUUM_MCP_TOKEN` | Vruum personal access token | falls back to `VRUUM_TOKEN`, then `~/.vruum/credentials` |
33
120
  | `VRUUM_MCP_URL` | Hosted MCP endpoint | `https://api.vruum.ai/mcp` |
34
- | `VRUUM_MCP_TIMEOUT_MS` | Per-call timeout (research/import tools run long) | `300000` |
121
+ | `VRUUM_MCP_TIMEOUT_MS` | Per-call timeout — research and imports run long | `300000` |
35
122
  | `VRUUM_CONFIG_DIR` | Credentials directory | `~/.vruum` |
36
123
 
37
- ## What's inside
124
+ ## How it works
125
+
126
+ ```
127
+ your agent ──stdio/JSON-RPC──▶ @vruum/mcp
128
+ │
129
+ tools/list ──────┤ served locally from tools.json
130
+ │ (no network, no credentials)
131
+ │
132
+ tools/call ──────┴──HTTPS+Bearer──▶ api.vruum.ai/mcp
133
+ (authorized server-side)
134
+ ```
135
+
136
+ Listings are a **static snapshot** bundled at release. That's what makes credential-free introspection possible, and it means a newly added tool won't *appear* until the next release — calls still execute correctly, since they proxy through. A CI guard regenerates `tools.json` on every backend change, so the snapshot can be one release old but never silently wrong.
137
+
138
+ ## Safety and credentials
38
139
 
39
- - **[`tools.json`](tools.json)** — the client-advertised tool surface: **29 curated compound tools** (`search`, `fetch`, `research`, `skill`, and the `manage_*` / `get_*` families covering people, deals, outreach, engagement, content, campaigns, and revenue), each carrying MCP safety annotations. This is exactly what a real Vruum session is advertised — one tool per decision rather than a sprawl of granular endpoints, so an agent never has to disambiguate between overlapping legacy verbs. Generated from the live server definition and served verbatim for `tools/list`.
40
- - **[`src/index.ts`](src/index.ts)** — the bridge: static listings, proxied calls, and structured errors
41
- (a missing token is an explanation, not a crash). **A failed call is never automatically
42
- retried** — many of these tools send outreach or spend money, and an ambiguous failure may
43
- mean the server already executed, so a silent replay could double-send. The dead connection
44
- is discarded, the error says so, and your agent decides whether the tool is safe to re-run.
140
+ - **Server-side authorization.** The bridge adds no permissions. Your token is exactly your Vruum account, and role/tenant checks happen on the server.
141
+ - **No automatic retries.** A failed `tools/call` is never replayed. Many of these tools send outreach or spend money, and an ambiguous failure may mean the server already executed — a silent retry could double-send. The connection is discarded, the error says so, and your agent decides whether re-running is safe.
142
+ - **Your token never leaves your machine** except as a `Bearer` header to `api.vruum.ai`.
45
143
 
46
- The distinctive design position of the Vruum MCP: **your AI harness authors all sales and marketing prose.** The server schedules, gates, persists, and sends — it has no server-side message generation. Outreach drafts surface to your agent as work items rather than being written for you.
144
+ > [!WARNING]
145
+ > **The bridge picks up ambient credentials.** With no `VRUUM_MCP_TOKEN` set it falls back to `~/.vruum/credentials`. If you are logged in with the Vruum CLI, then running this server — via the MCP Inspector, a client, or a script — executes **against your real account and real data**. Write tools will really write.
146
+ >
147
+ > To poke at it with no credentials, pass an explicit env that reaches the process:
148
+ >
149
+ > ```sh
150
+ > env -i PATH="$PATH" HOME=/tmp/empty node dist/index.js
151
+ > ```
152
+ >
153
+ > Note that MCP clients following the SDK default only forward an allowlist (`HOME`, `PATH`, `SHELL`, `TERM`, `USER`, `LOGNAME`) to spawned servers — so setting `VRUUM_CONFIG_DIR` in your shell may be **stripped before it reaches the bridge**, while `HOME` survives and the credentials file is found anyway. Set credentials in the client's own `env` block instead.
47
154
 
48
155
  ## Development
49
156
 
50
- This repository is a **build artifact of the Vruum monorepo**, resynced automatically on release — `tools.json` is regenerated from the live server definition, so it never drifts from what the hosted server actually exposes.
157
+ This repo is a **build artifact of the Vruum monorepo**, resynced automatically on release. `tools.json` is generated from the live server definition, so it cannot drift from what the hosted server exposes.
51
158
 
52
- Issues and PRs are welcome here: maintainers upstream accepted changes into the monorepo, and they flow back on the next sync.
159
+ ```
160
+ src/index.ts the bridge — token resolution, static listings, proxied calls
161
+ tools.json generated tool surface (do not edit by hand)
162
+ test/ black-box tests: spawn the built binary, speak raw JSON-RPC
163
+ ```
53
164
 
54
165
  ```sh
55
166
  npm install
56
- npm test # builds, then black-box JSON-RPC tests against the built binary
167
+ npm test # builds, then runs the black-box suite
168
+ npm run typecheck
57
169
  ```
58
170
 
171
+ Tests drive the built binary over stdin/stdout and assert **at the wire level** rather than through an SDK client — an SDK-mediated test hides protocol mistakes, since it will happily hand back a result object whether the server returned `result` or `error`. They cover the initialize handshake, credential-free listing, protocol-vs-execution error semantics, cursor rejection, capability declaration, and stdout hygiene.
172
+
173
+ **Issues and PRs are welcome here.** Maintainers apply accepted changes upstream in the monorepo and they flow back on the next sync — so a merged fix here may appear as part of a sync commit rather than your original one. Changes to `tools.json` must come from the generator, not by hand.
174
+
59
175
  ## Links
60
176
 
61
- - [Vruum](https://vruum.ai) · [MCP docs](https://vruum.ai/docs/mcp) · [Getting started](https://vruum.ai/docs/getting-started)
62
- - Official registry entry: `ai.vruum/mcp` · Claude/Codex plugin: [`vruum-gtm/skills`](https://github.com/vruum-gtm/skills)
177
+ [Vruum](https://vruum.ai) · [MCP docs](https://vruum.ai/docs/mcp) · [Getting started](https://vruum.ai/docs/getting-started) · [CLI reference](https://vruum.ai/docs/cli-reference)
178
+
179
+ Registry entry `ai.vruum/mcp` · Claude & Codex plugin [`vruum-gtm/skills`](https://github.com/vruum-gtm/skills)
63
180
 
64
181
  ## License
65
182
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vruum/mcp",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "mcpName": "ai.vruum/mcp",
5
5
  "description": "Stdio MCP bridge for the hosted Vruum revenue-platform MCP (https://api.vruum.ai/mcp): serves the advertised tool surface for credential-free introspection and proxies tool calls with your Vruum token.",
6
6
  "license": "MIT",
package/tools.json CHANGED
@@ -131,10 +131,11 @@
131
131
  "openWorldHint": false,
132
132
  "readOnlyHint": true
133
133
  },
134
- "description": "Get Campaign Outcomes\n\nMCP adapter for the same canonical campaign-outcomes domain model.\n\n### Responses:\n\n**200**: Successful Response (Success Response)\nContent-Type: application/json\n\n**Example Response:**\n```json\n{\n \"computed_at\": \"2023-01-01T00:00:00Z\",\n \"outcome_cutoff\": \"2023-01-01T00:00:00Z\",\n \"coverage_complete\": true,\n \"items\": [\n {\n \"campaign_id\": \"Campaign Id\",\n \"availability\": \"Availability\",\n \"coverage_complete\": true,\n \"data_watermark\": {}\n }\n ]\n}\n```",
134
+ "description": "Get Campaign Outcomes\n\nCompare campaigns on real outcomes — contacted, replied, meetings booked.\n\nAnswers \"which of these campaigns actually worked\" for up to 100 campaigns\nin one call, over an explicit UTC window. Every campaign you ask for comes\nback, including ones with no usable data — those carry\n`availability: \"unavailable\"` rather than being dropped or reported as\nzero, so a campaign never looks like a failure when it is really just\nunmeasured.\n\n**Use `get_performance_metrics` instead** for funnel and time-series views\nof the whole account; this tool is for ranking specific campaigns against\neach other on a like-for-like cohort.\n\nReading the response:\n\n- **`outcome_cutoff`** is the \"as of\" moment. A reply or meeting counts\n only if it happened by then, so a campaign that started last week is not\n punished for outcomes that have not had time to land. Keep it identical\n across campaigns you intend to compare.\n- **`coverage_complete: false`** means attribution for that campaign is\n still incomplete. Report the numbers, but do not rank on them — that is\n the difference between \"performed badly\" and \"we cannot tell yet\".\n- **`data_watermark`** is how far message and meeting ingestion has caught\n up. Outcomes after the watermark are not in these numbers yet.\n- **`computed_at`** stamps the calculation, which is worth quoting when\n the same window is re-run later and moves.\n\nWindow rules the server enforces: all three timestamps must carry a UTC\noffset, `start_at < end_at <= outcome_cutoff`, and `outcome_cutoff` cannot\nbe in the future. `campaign_ids` must be unique.\n\n### Responses:\n\n**200**: Successful Response (Success Response)\nContent-Type: application/json\n\n**Example Response:**\n```json\n{\n \"computed_at\": \"2023-01-01T00:00:00Z\",\n \"outcome_cutoff\": \"2023-01-01T00:00:00Z\",\n \"coverage_complete\": true,\n \"items\": [\n {\n \"campaign_id\": \"Campaign Id\",\n \"availability\": \"Availability\",\n \"coverage_complete\": true,\n \"data_watermark\": {}\n }\n ]\n}\n```",
135
135
  "inputSchema": {
136
136
  "properties": {
137
137
  "campaign_ids": {
138
+ "description": "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.",
138
139
  "items": {
139
140
  "format": "uuid",
140
141
  "type": "string"
@@ -145,6 +146,7 @@
145
146
  "type": "array"
146
147
  },
147
148
  "end_at": {
149
+ "description": "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.",
148
150
  "format": "date-time",
149
151
  "title": "end_at",
150
152
  "type": "string"
@@ -155,11 +157,13 @@
155
157
  "type": "string"
156
158
  },
157
159
  "outcome_cutoff": {
160
+ "description": "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 have not had time to land. Use the SAME value across campaigns you intend to rank against each other. Cannot be in the future.",
158
161
  "format": "date-time",
159
162
  "title": "outcome_cutoff",
160
163
  "type": "string"
161
164
  },
162
165
  "start_at": {
166
+ "description": "Start of the comparison window (UTC offset required). Touches before this are excluded. Must be earlier than end_at.",
163
167
  "format": "date-time",
164
168
  "title": "start_at",
165
169
  "type": "string"
@@ -1459,7 +1463,7 @@
1459
1463
  "openWorldHint": false,
1460
1464
  "readOnlyHint": false
1461
1465
  },
1462
- "description": "Manage Relationship Action\n\nDraft and transition one approval-gated relationship action.\n\n### Responses:\n\n**200**: Successful Response (Success Response)\nContent-Type: application/json\n\n**Example Response:**\n```json\n{\n \"status\": \"Status\",\n \"attempt\": {\n \"ref\": \"Ref\",\n \"relationship_owner_user_id\": \"Relationship Owner User Id\",\n \"action_type\": \"Action Type\",\n \"target_ref\": \"Target Ref\",\n \"target_snapshot\": {},\n \"result_ref\": \"Result Ref\",\n \"result_type\": \"Result Type\",\n \"channel\": \"Channel\",\n \"client_request_id\": \"Client Request Id\",\n \"route_fingerprint\": \"Route Fingerprint\",\n \"message_text\": \"Message Text\",\n \"state\": \"State\",\n \"version\": 1,\n \"created_at\": \"Created At\",\n \"updated_at\": \"Updated At\"\n },\n \"approval_required\": true,\n \"next_actions\": [\n \"string\"\n ],\n \"web_url\": \"Web Url\"\n}\n```",
1466
+ "description": "Manage Relationship Action\n\nDraft, approve, and record the outcome of ONE warm-introduction attempt.\n\nA \"relationship action\" is a single attempt to reach a target person\n*through a mutual connection* rather than by contacting them directly:\nasking a colleague for an intro, checking whether a connector actually\nknows them, or requesting an entry into their company. `find_warm_path`\ndiscovers those routes and never writes; this tool is the only way to act\non one.\n\n**Use this instead of the direct-outreach tools when the path runs through\na person.** `manage_outreach` and `manage_messages` send to the target\nthemselves; `manage_engagements` handles reactions and comments on\ncontent. This tool messages the *connector*.\n\n**Nothing here auto-sends.** Every attempt walks a state machine you drive\none call at a time: `draft` creates it from a `result_ref` returned by\n`find_warm_path`, then transitions approve the exact message text, confirm\nthat you sent it yourself outside Vruum, classify how the connector\nreplied, record the final outcome, or cancel. The response's\n`approval_required` and `next_actions` fields tell you which transitions\nare legal right now — read them rather than guessing.\n\nTwo consistency rules the server enforces:\n\n- **`draft` is idempotent on `client_request_id`.** Reuse the same value\n when retrying a draft; a new value creates a second attempt.\n- **Every transition needs `expected_version`.** A stale version means\n someone else moved the attempt and yours is rejected rather than\n silently overwriting — re-read the attempt and retry.\n\nA `409 route_stale` on draft means the underlying relationship evidence\nchanged since it was reviewed; re-run `find_warm_path` and draft from a\nfresh `result_ref`.\n\n### Responses:\n\n**200**: Successful Response (Success Response)\nContent-Type: application/json\n\n**Example Response:**\n```json\n{\n \"status\": \"Status\",\n \"attempt\": {\n \"ref\": \"Ref\",\n \"relationship_owner_user_id\": \"Relationship Owner User Id\",\n \"action_type\": \"Action Type\",\n \"target_ref\": \"Target Ref\",\n \"target_snapshot\": {},\n \"result_ref\": \"Result Ref\",\n \"result_type\": \"Result Type\",\n \"channel\": \"Channel\",\n \"client_request_id\": \"Client Request Id\",\n \"route_fingerprint\": \"Route Fingerprint\",\n \"message_text\": \"Message Text\",\n \"state\": \"State\",\n \"version\": 1,\n \"created_at\": \"Created At\",\n \"updated_at\": \"Updated At\"\n },\n \"approval_required\": true,\n \"next_actions\": [\n \"string\"\n ],\n \"web_url\": \"Web Url\"\n}\n```",
1463
1467
  "inputSchema": {
1464
1468
  "properties": {
1465
1469
  "for_company": {
@@ -1952,7 +1956,7 @@
1952
1956
  "type": "null"
1953
1957
  }
1954
1958
  ],
1955
- "description": "Projection: omit for the default compact list (the prose-heavy ai_tone_instructions/ai_selling_strategy/touch_sequence come back null); 'minimal' for id/name/slug only (dropdowns/selection); 'full' to include the AI-strategy prose for EVERY campaign (10KB+ each — a multi-campaign tenant can exceed client output limits and get silently truncated; prefer fetch type='campaign' for one campaign's full config).",
1959
+ "description": "Projection: omit for the default compact list (the prose-heavy fields — ai_tone_instructions, ai_selling_strategy, touch_sequence, value_proposition, primary_problem_solved, differentiators, positioning_angle, notable_customers_override — come back null; a null there does NOT mean the campaign lacks the value); 'minimal' for id/name/slug only (dropdowns/selection); 'full' to include the prose for EVERY campaign (10KB+ each — a multi-campaign tenant can exceed client output limits and get silently truncated; prefer fetch type='campaign' for one campaign's full config).",
1956
1960
  "title": "Fields"
1957
1961
  }
1958
1962
  },