@vruum/mcp 0.2.2 → 0.2.3
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.
- package/README.md +140 -23
- package/package.json +1 -1
- package/tools.json +7 -3
package/README.md
CHANGED
|
@@ -1,13 +1,39 @@
|
|
|
1
1
|
# Vruum MCP (`@vruum/mcp`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@vruum/mcp)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://registry.modelcontextprotocol.io)
|
|
4
6
|
|
|
5
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 |
|
|
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
|
|
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
|
-
##
|
|
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
|
-
- **
|
|
40
|
-
- **
|
|
41
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
62
|
-
|
|
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.
|
|
3
|
+
"version": "0.2.3",
|
|
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\
|
|
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
|
|
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
|
|
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
|
},
|