@getmarrow/mcp 2.3.2 → 2.3.4

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 CHANGED
@@ -1,255 +1,54 @@
1
- # @getmarrow/mcp
1
+ # marrow-mcp
2
2
 
3
- > **Your go-to memory provider for all agents, for any AI model.**
3
+ MCP server for [Marrow](https://getmarrow.ai) decision intelligence for AI agents.
4
4
 
5
- Give Claude (or any MCP-compatible agent) a memory that compounds across every session, every user, every run. Marrow connects your agent to a hive of collective intelligence — what worked, what failed, patterns discovered across thousands of agent runs.
5
+ ## What's New in v2.3.4
6
6
 
7
- **One tool call. Your agent stops starting from zero.**
7
+ ### Loop-enforcement tools for real agent operating discipline
8
8
 
9
- ---
9
+ This release adds the MCP surface for Marrow's live loop workflow so agents can orient, think, act, check, and commit inside a single session instead of treating memory as an afterthought.
10
10
 
11
- ## Install
11
+ New/updated tool flow:
12
+ - `marrow_orient` / `orient` — start the session with loop state, recent lessons, and a recommended next step
13
+ - `marrow_think` / `think` — log intent and get pattern intelligence + loop metadata back
14
+ - `marrow_check` / `check` — inspect current loop state and recommended next step mid-session
15
+ - `marrow_commit` / `commit` — close the loop cleanly with outcome logging
12
16
 
13
- ```bash
14
- npm install -g @getmarrow/mcp
15
- ```
16
-
17
- Get your API key at [getmarrow.ai](https://getmarrow.ai)
18
-
19
- ---
17
+ What changed in practice:
18
+ - session-local loop state now persists across MCP tool calls
19
+ - agents get a canonical session-start nudge toward `marrow_think`
20
+ - commit now correctly returns a closed-loop state (`done` / `outcome_logged`)
21
+ - package is publish-ready as `@getmarrow/mcp`
20
22
 
21
- ## Setup — Claude Desktop
22
-
23
- Add to your `claude_desktop_config.json`:
23
+ ## Quick Start
24
24
 
25
25
  ```json
26
+ // claude_desktop_config.json
26
27
  {
27
28
  "mcpServers": {
28
29
  "marrow": {
29
30
  "command": "npx",
30
31
  "args": ["@getmarrow/mcp"],
31
- "env": {
32
- "MARROW_API_KEY": "your_key_from_getmarrow.ai"
33
- }
32
+ "env": { "MARROW_API_KEY": "mrw_your_key_here" }
34
33
  }
35
34
  }
36
35
  }
37
36
  ```
38
37
 
39
- Restart Claude. That's it — Marrow is now available as a tool in every conversation.
40
-
41
- ---
42
-
43
- ## Setup — Any MCP-Compatible Agent
44
-
45
- ```bash
46
- export MARROW_API_KEY=your_key_from_getmarrow.ai # get your key at getmarrow.ai/dashboard
47
- npx @getmarrow/mcp
48
- # Listens on stdin, outputs JSON-RPC on stdout
49
- # Drop into any MCP framework
50
- ```
51
-
52
- ---
53
-
54
- ## How It Works
55
-
56
- Your agent calls `marrow_think` before acting. Marrow queries the hive — collective intelligence from every agent that's ever done something similar — and returns what worked, what patterns emerged, what playbooks exist. When your agent is done, that experience gets committed back to the hive. Every agent gets smarter. Forever.
57
-
58
- ---
59
-
60
- ## Quick Start Pattern
61
-
62
- ```
63
- Session start → marrow_orient() ← reads from hive, surfaces warnings
64
- Before each action → marrow_think() ← queries + opens memory session
65
- After each action → marrow_commit() ← writes result back to hive
66
- ```
67
-
68
- **orient() is what makes Marrow compound.** Without it, you're writing to a database no one reads. With it, your agent automatically avoids mistakes it's made before.
69
-
70
- ---
71
-
72
38
  ## Tools
73
39
 
74
- ### `marrow_orient` — Call this first
75
-
76
- **Call at the start of every session, before any other tool.** Returns failure warnings from your hive history so you avoid known mistakes immediately.
77
-
78
- ```json
79
- {
80
- "taskType": "implementation" // optional: filter to specific task type
81
- }
82
- ```
83
-
84
- **Response:**
85
- ```json
86
- {
87
- "warnings": [
88
- {
89
- "type": "process",
90
- "failureRate": 0.26,
91
- "message": "process has 26% failure rate over 27 decisions — review lessons before acting"
92
- }
93
- ],
94
- "shouldPause": false // true if any failure rate >40% — stop and query lessons first
95
- }
96
- ```
97
-
98
- If `shouldPause` is true, query your lessons before proceeding. If `warnings` is empty, you're clear.
99
-
100
- ---
40
+ - **marrow_orient** / `orient`Start the session with loop state, recent lessons, and a recommended next step
41
+ - **marrow_think** / `think` — Log a decision and get pattern intelligence, loop metadata, and useful next-step guidance back
42
+ - **marrow_commit** / `commit` Commit task outcome to the hive and close the loop
43
+ - **marrow_check** / `check` — Inspect current loop state for this MCP session
44
+ - **patterns** — Get accumulated decision patterns
101
45
 
102
- ### `marrow_think` — The core tool
103
-
104
- Call this before every significant action. Returns collective intelligence from the hive.
105
-
106
- **Input:**
107
- ```json
108
- {
109
- "action": "What the agent is about to do",
110
- "type": "implementation | security | architecture | process | general",
111
- "previous_outcome": "What happened last time (auto-commits previous session)",
112
- "previous_success": true
113
- }
114
- ```
115
-
116
- **Returns:**
117
- ```json
118
- {
119
- "decision_id": "save this for the next call",
120
- "intelligence": {
121
- "similar": [
122
- { "outcome": "Used incremental approach. Shipped in 2 days, 0 rollbacks.", "confidence": 0.91 }
123
- ],
124
- "similar_count": 3,
125
- "patterns": [
126
- { "pattern_id": "a1b2c3", "decision_type": "implementation", "frequency": 84, "confidence": 0.9 }
127
- ],
128
- "patterns_count": 2,
129
- "templates": [
130
- { "steps": ["Plan", "Spec", "Build", "Test", "Deploy"], "success_rate": 0.89 }
131
- ],
132
- "success_rate": 0.87,
133
- "priority_score": 0.7,
134
- "insight": "Workflow gap detected — barvis_audit missing after build",
135
- "insights": [
136
- {
137
- "type": "workflow_gap",
138
- "summary": "audit not logged after build (3 consecutive times)",
139
- "action": "Run audit before reporting done",
140
- "severity": "critical",
141
- "count": 3
142
- }
143
- ],
144
- "cluster_id": "818df2fa-6365-49f6-b478-bc3dcb748469"
145
- },
146
- "stream_url": "/v1/stream?format=sse"
147
- }
148
- ```
149
-
150
- **Example prompt to Claude:**
151
-
152
- > Before starting this refactor, call `marrow_think` with action "Refactoring auth module to support OAuth 2.0" and type "implementation". Use the intelligence to guide your approach.
153
-
154
- Claude will automatically:
155
- 1. Call `marrow_think`
156
- 2. Read what worked for other agents doing similar refactors
157
- 3. Apply those patterns to its approach
158
- 4. On the next `marrow_think` call, commit this outcome back to the hive
159
-
160
- ---
161
-
162
- ### `marrow_commit` — Explicit commit
163
-
164
- Commit an outcome explicitly when you need more control. `marrow_think` auto-commits on the next call, so this is optional.
165
-
166
- **Input:**
167
- ```json
168
- {
169
- "decision_id": "from previous marrow_think",
170
- "success": true,
171
- "outcome": "Auth refactor complete. OAuth 2.0 working. Zero breaking changes. 2 day turnaround.",
172
- "caused_by": "optional: previous decision_id to link causally"
173
- }
174
- ```
175
-
176
- ---
177
-
178
- ### `marrow_status` — Platform health
179
-
180
- Check your agent's current performance across the hive.
181
-
182
- **Returns:**
183
- ```json
184
- {
185
- "success_rate": 0.87,
186
- "decision_velocity": 142,
187
- "patterns_discovered": 23,
188
- "hive_consensus": 0.91
189
- }
190
- ```
191
-
192
- ---
193
-
194
- ## Real Example — Claude Coding Agent
195
-
196
- **System prompt addition:**
197
- ```
198
- Before starting any significant task, call marrow_think with a description of what you're about to do.
199
- After completing a task, the next marrow_think call will automatically record the outcome.
200
- Use the intelligence returned to guide your approach — especially `similar` and `patterns`.
201
- ```
202
-
203
- **What happens in practice:**
204
-
205
- User: *"Refactor the payment service to add retry logic"*
206
-
207
- Claude calls `marrow_think`:
208
- ```json
209
- { "action": "Adding retry logic to payment service", "type": "implementation" }
210
- ```
211
-
212
- Marrow returns:
213
- ```json
214
- {
215
- "intelligence": {
216
- "similar": [
217
- { "outcome": "Exponential backoff with jitter. 3 retries max. Idempotency key required. Zero double-charges in 6 months.", "confidence": 0.96 }
218
- ],
219
- "patterns": [
220
- { "pattern": "Retry without idempotency key causes duplicate transactions in 23% of cases", "frequency": 156 }
221
- ]
222
- }
223
- }
224
- ```
225
-
226
- Claude now knows — from thousands of other agents' experience — exactly what to do and what to avoid. Without Marrow, it would have guessed.
227
-
228
- ---
229
-
230
- ## Why Marrow for MCP?
231
-
232
- - **Zero setup friction** — one config entry, works instantly
233
- - **Model agnostic** — Claude, GPT, Gemini, any MCP-compatible model
234
- - **Compounds automatically** — every session adds to the hive, no manual curation
235
- - **Private by default** — your agent's decisions are anonymized before entering the hive
236
- - **Persistent across sessions** — memory survives context resets, model upgrades, new conversations
237
-
238
- ---
239
-
240
- ## Get Started
241
-
242
- ```bash
243
- npm install -g @getmarrow/mcp
244
- ```
46
+ ## Session hint
245
47
 
246
- 1. Get your API key at [getmarrow.ai](https://getmarrow.ai)
247
- 2. Add the MCP server config above
248
- 3. Tell your agent to call `marrow_think` before acting
249
- 4. Watch collective intelligence flow in from the first call
48
+ At session start, Marrow nudges clients with the canonical reminder:
250
49
 
251
- **Your agent inherits the experience of every agent that came before it.**
50
+ > Tip: log plans, decisions, and outcomes to Marrow so your agent improves over time.
252
51
 
253
- ---
52
+ ## Get an API key
254
53
 
255
- *Compatible with Claude Desktop, Claude API, and any MCP-compatible framework.*
54
+ Sign up at [getmarrow.ai](https://getmarrow.ai)
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Marrow API client — thin HTTP wrapper
3
+ */
4
+ export type MarrowLoopRecommendation = 'orient' | 'think' | 'act' | 'commit' | 'done';
5
+ export interface MarrowLoopState {
6
+ orientedAt: string | null;
7
+ lastThinkAt: string | null;
8
+ lastOutcomeAt: string | null;
9
+ hasIntentLog: boolean;
10
+ hasOutcomeLog: boolean;
11
+ actionCountSinceLastThink: number;
12
+ externalActionCountSinceLastThink: number;
13
+ lastDecisionId: string | null;
14
+ pendingDecisionId: string | null;
15
+ recommendedNext: MarrowLoopRecommendation;
16
+ loopState: 'idle' | 'oriented' | 'intent_logged' | 'acting' | 'outcome_logged';
17
+ message: string | null;
18
+ hints: string[];
19
+ }
20
+ export declare class MarrowClient {
21
+ private apiKey;
22
+ private baseUrl;
23
+ private currentDecisionId;
24
+ private loopState;
25
+ constructor();
26
+ check(): {
27
+ ok: boolean;
28
+ recommendedNext: MarrowLoopRecommendation;
29
+ state: {
30
+ hints: string[];
31
+ orientedAt: string | null;
32
+ lastThinkAt: string | null;
33
+ lastOutcomeAt: string | null;
34
+ hasIntentLog: boolean;
35
+ hasOutcomeLog: boolean;
36
+ actionCountSinceLastThink: number;
37
+ externalActionCountSinceLastThink: number;
38
+ lastDecisionId: string | null;
39
+ pendingDecisionId: string | null;
40
+ recommendedNext: MarrowLoopRecommendation;
41
+ loopState: "idle" | "oriented" | "intent_logged" | "acting" | "outcome_logged";
42
+ message: string | null;
43
+ };
44
+ warnings: string[];
45
+ };
46
+ orient(params?: {
47
+ task_type?: string;
48
+ }): Promise<Record<string, unknown>>;
49
+ think(params: {
50
+ action: string;
51
+ type: string;
52
+ previous_decision_id?: string;
53
+ previous_success?: boolean;
54
+ previous_outcome?: string;
55
+ }): Promise<Record<string, unknown>>;
56
+ commit(params: {
57
+ decision_id: string;
58
+ success: boolean;
59
+ outcome: string;
60
+ }): Promise<Record<string, unknown>>;
61
+ recordAction(meta?: {
62
+ external?: boolean;
63
+ }): {
64
+ ok: boolean;
65
+ recommendedNext: MarrowLoopRecommendation;
66
+ state: {
67
+ hints: string[];
68
+ orientedAt: string | null;
69
+ lastThinkAt: string | null;
70
+ lastOutcomeAt: string | null;
71
+ hasIntentLog: boolean;
72
+ hasOutcomeLog: boolean;
73
+ actionCountSinceLastThink: number;
74
+ externalActionCountSinceLastThink: number;
75
+ lastDecisionId: string | null;
76
+ pendingDecisionId: string | null;
77
+ recommendedNext: MarrowLoopRecommendation;
78
+ loopState: "idle" | "oriented" | "intent_logged" | "acting" | "outcome_logged";
79
+ message: string | null;
80
+ };
81
+ warnings: string[];
82
+ };
83
+ patterns(): Promise<Record<string, unknown>[]>;
84
+ private post;
85
+ private get;
86
+ }
package/dist/client.js ADDED
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ /**
3
+ * Marrow API client — thin HTTP wrapper
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MarrowClient = void 0;
7
+ const DEFAULT_BASE_URL = 'https://api.getmarrow.ai';
8
+ const SESSION_START_HINT = 'Tip: log plans, decisions, and outcomes to Marrow so your agent improves over time.';
9
+ const POST_ORIENT_NUDGE = 'You have not logged any decisions yet this session. Before acting, call marrow_think.';
10
+ const PRE_EXIT_REMINDER = 'Before ending the session, log the outcome to Marrow so the loop closes cleanly.';
11
+ function nowIso() {
12
+ return new Date().toISOString();
13
+ }
14
+ class MarrowClient {
15
+ apiKey;
16
+ baseUrl;
17
+ currentDecisionId = null;
18
+ loopState = {
19
+ orientedAt: null,
20
+ lastThinkAt: null,
21
+ lastOutcomeAt: null,
22
+ hasIntentLog: false,
23
+ hasOutcomeLog: false,
24
+ actionCountSinceLastThink: 0,
25
+ externalActionCountSinceLastThink: 0,
26
+ lastDecisionId: null,
27
+ pendingDecisionId: null,
28
+ recommendedNext: 'orient',
29
+ loopState: 'idle',
30
+ message: SESSION_START_HINT,
31
+ hints: [SESSION_START_HINT],
32
+ };
33
+ constructor() {
34
+ const key = process.env.MARROW_API_KEY;
35
+ if (!key) {
36
+ throw new Error('MARROW_API_KEY environment variable is required');
37
+ }
38
+ this.apiKey = key;
39
+ this.baseUrl = process.env.MARROW_BASE_URL || DEFAULT_BASE_URL;
40
+ }
41
+ check() {
42
+ if (!this.loopState.orientedAt) {
43
+ this.loopState.recommendedNext = 'orient';
44
+ this.loopState.loopState = 'idle';
45
+ this.loopState.message = SESSION_START_HINT;
46
+ this.loopState.hints = [SESSION_START_HINT];
47
+ }
48
+ else if (this.loopState.hasOutcomeLog) {
49
+ this.loopState.recommendedNext = 'done';
50
+ this.loopState.loopState = 'outcome_logged';
51
+ this.loopState.message = 'Loop closed. Ready for the next task.';
52
+ this.loopState.hints = ['Loop closed. Ready for the next task.'];
53
+ }
54
+ else if (!this.loopState.hasIntentLog) {
55
+ this.loopState.recommendedNext = 'think';
56
+ this.loopState.loopState = 'oriented';
57
+ this.loopState.message = POST_ORIENT_NUDGE;
58
+ this.loopState.hints = [SESSION_START_HINT, POST_ORIENT_NUDGE];
59
+ }
60
+ else if (!this.loopState.hasOutcomeLog && this.loopState.actionCountSinceLastThink > 0) {
61
+ this.loopState.recommendedNext = 'commit';
62
+ this.loopState.loopState = 'acting';
63
+ this.loopState.message = PRE_EXIT_REMINDER;
64
+ this.loopState.hints = [PRE_EXIT_REMINDER];
65
+ }
66
+ else if (!this.loopState.hasOutcomeLog) {
67
+ this.loopState.recommendedNext = 'act';
68
+ this.loopState.loopState = 'intent_logged';
69
+ this.loopState.message = 'Intent logged. Act, then log the outcome.';
70
+ this.loopState.hints = ['Intent logged. Act, then log the outcome.'];
71
+ }
72
+ else {
73
+ this.loopState.recommendedNext = 'done';
74
+ this.loopState.loopState = 'outcome_logged';
75
+ this.loopState.message = 'Loop closed. Ready for the next task.';
76
+ this.loopState.hints = ['Loop closed. Ready for the next task.'];
77
+ }
78
+ return {
79
+ ok: true,
80
+ recommendedNext: this.loopState.recommendedNext,
81
+ state: { ...this.loopState, hints: [...this.loopState.hints] },
82
+ warnings: [...this.loopState.hints],
83
+ };
84
+ }
85
+ async orient(params) {
86
+ const patterns = await this.get(`/v1/agent/patterns${params?.task_type ? `?type=${encodeURIComponent(params.task_type)}` : ''}`);
87
+ let lessons = [];
88
+ try {
89
+ const lessonRes = await this.get('/v1/agent/think/history?type=lesson&limit=5');
90
+ lessons = (lessonRes.data
91
+ || lessonRes.items
92
+ || lessonRes.decisions
93
+ || []);
94
+ }
95
+ catch {
96
+ lessons = [];
97
+ }
98
+ this.loopState.orientedAt = nowIso();
99
+ const loop = this.check();
100
+ const failurePatterns = (patterns.data?.failure_patterns
101
+ || patterns.failure_patterns
102
+ || []);
103
+ return {
104
+ session_start_hint: SESSION_START_HINT,
105
+ nudge: this.loopState.hasIntentLog ? null : POST_ORIENT_NUDGE,
106
+ recommended_next: loop.recommendedNext,
107
+ loop: loop.state,
108
+ warnings: failurePatterns
109
+ .filter((item) => Number(item.failureRate || item.failure_rate || 0) > 0.15)
110
+ .map((item) => ({
111
+ type: String(item.decisionType || item.decision_type || 'general'),
112
+ failure_rate: Number(item.failureRate || item.failure_rate || 0),
113
+ message: `${String(item.decisionType || item.decision_type || 'general')} has ${Math.round(Number(item.failureRate || item.failure_rate || 0) * 100)}% failure rate over ${Number(item.count || 0)} decisions — check lessons before proceeding`,
114
+ })),
115
+ lessons: lessons.map((lesson) => ({
116
+ summary: String(lesson.action || lesson.summary || ''),
117
+ severity: 'info',
118
+ })),
119
+ text: [SESSION_START_HINT, POST_ORIENT_NUDGE, `Recommended next step: ${loop.recommendedNext}.`].join(' '),
120
+ };
121
+ }
122
+ async think(params) {
123
+ if (params.previous_decision_id !== undefined && !params.previous_decision_id.trim()) {
124
+ throw new Error('decision_id must be a non-empty string');
125
+ }
126
+ const result = await this.post('/v1/agent/think', params);
127
+ const data = result.data || result;
128
+ const intelligence = data.intelligence || {};
129
+ const decisionId = typeof data.decision_id === 'string' && data.decision_id.trim()
130
+ ? data.decision_id
131
+ : null;
132
+ this.loopState.orientedAt = this.loopState.orientedAt || nowIso();
133
+ this.loopState.lastThinkAt = nowIso();
134
+ this.loopState.hasIntentLog = true;
135
+ this.loopState.hasOutcomeLog = false;
136
+ this.loopState.actionCountSinceLastThink = 0;
137
+ this.loopState.externalActionCountSinceLastThink = 0;
138
+ this.currentDecisionId = decisionId;
139
+ this.loopState.pendingDecisionId = decisionId;
140
+ this.loopState.lastDecisionId = decisionId;
141
+ const loop = this.check();
142
+ return {
143
+ ...result,
144
+ data: {
145
+ ...data,
146
+ accepted_as: 'intent',
147
+ warnings: loop.warnings,
148
+ recommended_next: loop.recommendedNext,
149
+ loop: loop.state,
150
+ summary: [
151
+ 'Intent logged to Marrow.',
152
+ intelligence.insight ? `Pattern hint: ${String(intelligence.insight)}` : null,
153
+ `Recommended next step: ${loop.recommendedNext}.`,
154
+ ].filter(Boolean).join(' '),
155
+ },
156
+ };
157
+ }
158
+ async commit(params) {
159
+ const decisionId = params.decision_id.trim();
160
+ if (!decisionId) {
161
+ throw new Error('decision_id must be a non-empty string');
162
+ }
163
+ if (!this.currentDecisionId) {
164
+ throw new Error('No active decision. Call marrow_think first.');
165
+ }
166
+ if (decisionId !== this.currentDecisionId) {
167
+ throw new Error(`decision_id mismatch: expected ${this.currentDecisionId}`);
168
+ }
169
+ const result = await this.post('/v1/agent/commit', {
170
+ ...params,
171
+ decision_id: decisionId,
172
+ });
173
+ const data = result.data || result;
174
+ this.loopState.lastOutcomeAt = nowIso();
175
+ this.loopState.hasIntentLog = false;
176
+ this.loopState.hasOutcomeLog = true;
177
+ this.loopState.actionCountSinceLastThink = 0;
178
+ this.loopState.externalActionCountSinceLastThink = 0;
179
+ this.loopState.pendingDecisionId = null;
180
+ this.loopState.lastDecisionId = decisionId;
181
+ this.currentDecisionId = null;
182
+ const loop = this.check();
183
+ return {
184
+ ...result,
185
+ data: {
186
+ ...data,
187
+ accepted_as: 'outcome',
188
+ recommended_next: loop.recommendedNext,
189
+ loop: loop.state,
190
+ summary: 'Outcome logged to Marrow. Loop closed.',
191
+ },
192
+ };
193
+ }
194
+ recordAction(meta) {
195
+ this.loopState.actionCountSinceLastThink += 1;
196
+ if (meta?.external)
197
+ this.loopState.externalActionCountSinceLastThink += 1;
198
+ return this.check();
199
+ }
200
+ async patterns() {
201
+ const res = await this.get('/v1/patterns');
202
+ return res?.data || [];
203
+ }
204
+ async post(path, body) {
205
+ const res = await fetch(`${this.baseUrl}${path}`, {
206
+ method: 'POST',
207
+ headers: {
208
+ 'Authorization': `Bearer ${this.apiKey}`,
209
+ 'Content-Type': 'application/json',
210
+ },
211
+ body: JSON.stringify(body),
212
+ });
213
+ if (!res.ok) {
214
+ const text = (await res.text()).slice(0, 200);
215
+ throw new Error(`Marrow API error (${res.status}): ${text}`);
216
+ }
217
+ return res.json();
218
+ }
219
+ async get(path) {
220
+ const res = await fetch(`${this.baseUrl}${path}`, {
221
+ method: 'GET',
222
+ headers: {
223
+ 'Authorization': `Bearer ${this.apiKey}`,
224
+ },
225
+ });
226
+ if (!res.ok) {
227
+ const text = (await res.text()).slice(0, 200);
228
+ throw new Error(`Marrow API error (${res.status}): ${text}`);
229
+ }
230
+ return res.json();
231
+ }
232
+ }
233
+ exports.MarrowClient = MarrowClient;