frinus-mcp 3.1.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.
- package/README.md +405 -0
- package/dist/capture/interaction-capture.d.ts +64 -0
- package/dist/capture/interaction-capture.js +186 -0
- package/dist/client/agent-client.d.ts +34 -0
- package/dist/client/agent-client.js +71 -0
- package/dist/client/cp-client.d.ts +20 -0
- package/dist/client/cp-client.js +58 -0
- package/dist/client/memory-client.d.ts +200 -0
- package/dist/client/memory-client.js +460 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +103 -0
- package/dist/tools/definitions.d.ts +8 -0
- package/dist/tools/definitions.js +1548 -0
- package/dist/tools/handlers.d.ts +7 -0
- package/dist/tools/handlers.js +1609 -0
- package/dist/types/index.d.ts +279 -0
- package/dist/types/index.js +4 -0
- package/manifest.json +76 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
# Frinus MCP Server
|
|
2
|
+
|
|
3
|
+
> **φρήν + νοῦς** — The cognitive memory bridge between Claude agents and the [Frinus](https://github.com/frinus-ai) platform.
|
|
4
|
+
|
|
5
|
+
MCP (Model Context Protocol) server that exposes **70 tools** spanning cognitive memory, working memory, sessions, agents, the L0–L3 knowledge hierarchy, orchestration tasks, and training pipelines. The server speaks stdio and is consumed by Claude Desktop, Claude Code, and any MCP-aware client.
|
|
6
|
+
|
|
7
|
+
- **Version:** 3.1.0
|
|
8
|
+
- **Tools:** 70 (see [Tools Reference](#tools-reference))
|
|
9
|
+
- **Backends:** Memory Engine (`:8001`) + Control Plane (`:8000`) + Agent Service (`:8002`)
|
|
10
|
+
- **Public mirror:** https://github.com/frinus-ai/frinus-mcp
|
|
11
|
+
|
|
12
|
+
## Rule Zero — MCP is mandatory
|
|
13
|
+
|
|
14
|
+
The Frinus MCP **is** the agent's long-term memory, identity, and intelligence. Without it the model is stateless across sessions. Every Claude agent that integrates with Frinus must:
|
|
15
|
+
|
|
16
|
+
1. Verify the MCP tools are reachable at conversation start (e.g. `session_start`, `memory_search`, `search_with_attention`).
|
|
17
|
+
2. If unreachable, surface the warning to the user: *"MCP Frinus is not connected. Long-term memory capabilities are unavailable."*
|
|
18
|
+
3. Execute the **BOOT protocol (P1)** before answering anything that is not a trivial greeting.
|
|
19
|
+
4. Persist learnings via `memory_store` before ending productive sessions.
|
|
20
|
+
|
|
21
|
+
The complete protocol set lives in the global `CLAUDE.md` (Frinus organisation), summarised below in [The 7 Protocols](#the-7-protocols).
|
|
22
|
+
|
|
23
|
+
## Requirements
|
|
24
|
+
|
|
25
|
+
- Node.js 18+
|
|
26
|
+
- Frinus stack reachable (Memory Engine, Control Plane, Agent Service)
|
|
27
|
+
- Personal API key (`sk-frinus-...`) tied to your tenant
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
**End users — no install needed.** Use the published package via `npx` (see config below), or the one-click `.mcpb` extension for Claude Desktop.
|
|
32
|
+
|
|
33
|
+
**Developers / from source:**
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install
|
|
37
|
+
npm run build
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The build emits `dist/index.js`, ready to be wired into Claude Desktop / Claude Code.
|
|
41
|
+
|
|
42
|
+
## Distribution
|
|
43
|
+
|
|
44
|
+
| Channel | Client | User effort |
|
|
45
|
+
|---------|--------|-------------|
|
|
46
|
+
| **npm** (`npx -y frinus-mcp`) | Claude Code, Cursor, Codex, opencode | Paste config + API key |
|
|
47
|
+
| **`.mcpb` bundle** | Claude Desktop | Double-click + paste API key |
|
|
48
|
+
| **From source** | any | Clone + build + absolute path |
|
|
49
|
+
|
|
50
|
+
URL defaults point at the Frinus SaaS, so only `FRINUS_API_KEY` is required. Override `MEMORY_SERVICE_URL` / `FRINUS_CP_URL` / `AGENT_SERVICE_URL` for self-hosted instances or local dev.
|
|
51
|
+
|
|
52
|
+
### Publishing (maintainers)
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# 1) npm package — powers `npx -y frinus-mcp` everywhere
|
|
56
|
+
npm version <patch|minor|major>
|
|
57
|
+
npm publish # prepublishOnly runs the build
|
|
58
|
+
|
|
59
|
+
# 2) .mcpb bundle — powers the Claude Desktop one-click install
|
|
60
|
+
npm run pack:mcpb # -> frinus.mcpb
|
|
61
|
+
gh release upload v<version> frinus.mcpb --repo frinus-ai/frinus-mcp --clobber
|
|
62
|
+
# Frontend points at: releases/latest/download/frinus.mcpb
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Environment Variables
|
|
66
|
+
|
|
67
|
+
| Variable | Default | Description |
|
|
68
|
+
|----------|---------|-------------|
|
|
69
|
+
| `FRINUS_API_KEY` | **required** | Personal API key (`sk-frinus-...`). Resolves tenant + user identity at boot. |
|
|
70
|
+
| `MEMORY_SERVICE_URL` | `http://localhost:8001` | Memory Engine base URL |
|
|
71
|
+
| `FRINUS_CP_URL` | `http://localhost:8000` | Control Plane base URL (universes, orgs, billing) |
|
|
72
|
+
| `AGENT_SERVICE_URL` | `http://localhost:8002` | Agent Service base URL (agents, invocation, skills) |
|
|
73
|
+
| `FRINUS_MEMORY_API_KEY` | — | Legacy fallback for `FRINUS_API_KEY` |
|
|
74
|
+
|
|
75
|
+
Production endpoints (Frinus SaaS):
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
MEMORY_SERVICE_URL=https://frinus-memory.rdxsec.com.br
|
|
79
|
+
FRINUS_CP_URL=https://frinus-api.rdxsec.com.br
|
|
80
|
+
AGENT_SERVICE_URL=https://frinus-agents.rdxsec.com.br
|
|
81
|
+
FRINUS_API_KEY=sk-frinus-...
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Claude Desktop / Claude Code Configuration
|
|
85
|
+
|
|
86
|
+
Add the server to your MCP client configuration. **Recommended (npx, no install):**
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"mcpServers": {
|
|
91
|
+
"frinus": {
|
|
92
|
+
"command": "npx",
|
|
93
|
+
"args": ["-y", "frinus-mcp@latest"],
|
|
94
|
+
"env": {
|
|
95
|
+
"FRINUS_API_KEY": "sk-frinus-..."
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Claude Code one-liner:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
claude mcp add frinus --env FRINUS_API_KEY=sk-frinus-... -- npx -y frinus-mcp@latest
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
From source (developers), point at the built entry and override URLs as needed:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"mcpServers": {
|
|
113
|
+
"frinus": {
|
|
114
|
+
"command": "node",
|
|
115
|
+
"args": ["/absolute/path/to/mcp/dist/index.js"],
|
|
116
|
+
"env": {
|
|
117
|
+
"FRINUS_API_KEY": "sk-frinus-...",
|
|
118
|
+
"MEMORY_SERVICE_URL": "http://localhost:8001",
|
|
119
|
+
"FRINUS_CP_URL": "http://localhost:8000",
|
|
120
|
+
"AGENT_SERVICE_URL": "http://localhost:8002"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Claude Desktop config path: `~/.config/claude/claude_desktop_config.json` (Linux) / `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS).
|
|
128
|
+
Claude Code config path: `~/.claude.json` under `mcpServers`.
|
|
129
|
+
|
|
130
|
+
On startup the server validates the API key via `/auth/me` and resolves the tenant org ID + logged-in user. Failures abort the process with `[FATAL]` so the client surfaces the error.
|
|
131
|
+
|
|
132
|
+
## The 7 Protocols
|
|
133
|
+
|
|
134
|
+
| Protocol | When | Tools | Purpose |
|
|
135
|
+
|----------|------|-------|---------|
|
|
136
|
+
| **P1 — BOOT** | Start of every session | `session_start`, `working_memory_get`, `search_with_attention` | Load identity + recent state + relevant context |
|
|
137
|
+
| **P2 — CONSULT** | Before any action | `search_with_attention`, `session_context`, `memory_reinforce`, `memory_weaken` | Retrieve, rate, and reinforce relevant memories |
|
|
138
|
+
| **P3 — PLAN** | Planning a task | `memory_store(procedural)`, `working_memory_add`, `stream_capture` | Persist plans, working state, and architectural decisions |
|
|
139
|
+
| **P4 — CAPTURE** | Every 2–3 interactions | `heartbeat_tick`, `working_memory_add`, `memory_store(episodic)`, `stream_capture` | Continuous recording of progress, bugs, patterns |
|
|
140
|
+
| **P5 — LEARN** | New knowledge | `training_teach`, `training_qa`, `training_stats`, `stream_process`, `sleep_run` | Teach facts/procedures and run consolidation cycles |
|
|
141
|
+
| **P6 — AUDIT** | Maintenance | `consolidation_detect_conflicts`, `consolidation_resolve_conflict`, `consolidation_detect_redundant`, `hierarchy_consolidate` | Detect conflicts, redundancy, and consolidate memories |
|
|
142
|
+
| **P7 — CLOSE** | End of session | `session_summary`, `stream_process`, `memory_store`, `hierarchy_consolidate`, `session_end` | Summarise, promote stream items, persist learnings |
|
|
143
|
+
|
|
144
|
+
Detailed protocol scripts live in the agent's `CLAUDE.md`. Treat the table above as the contract every Frinus-integrated agent must respect.
|
|
145
|
+
|
|
146
|
+
## Tools Reference
|
|
147
|
+
|
|
148
|
+
70 tools grouped by domain. Every tool returns text content; payloads follow the MCP `Tool` spec. Full JSON schemas live in [`src/tools/definitions.ts`](src/tools/definitions.ts).
|
|
149
|
+
|
|
150
|
+
### Memory (7)
|
|
151
|
+
|
|
152
|
+
Cognitive long-term memory: episodic (what happened), semantic (what I know), procedural (how to do things).
|
|
153
|
+
|
|
154
|
+
| Tool | Description |
|
|
155
|
+
|------|-------------|
|
|
156
|
+
| `memory_store` | Create a memory. `agent_id`, `content` required. `memory_type` ∈ {episodic, semantic, procedural}. `scope` ∈ {user, agent, universe, organization}. `importance` 0–1. |
|
|
157
|
+
| `memory_search` | Semantic similarity search. Filters: `agent_id`, `memory_types`, `limit`. |
|
|
158
|
+
| `memory_get` | Fetch a memory by `memory_id` (full content, metadata, relevance). |
|
|
159
|
+
| `memory_list` | List memories for an `agent_id` with optional type filter. |
|
|
160
|
+
| `memory_delete` | Permanently delete a memory by `memory_id`. |
|
|
161
|
+
| `memory_reinforce` | Boost relevance of a useful memory (`memory_id`, `boost` 0–1, default 0.1). |
|
|
162
|
+
| `memory_weaken` | Penalise an outdated memory (`memory_id`, `penalty` 0–1, default 0.2). |
|
|
163
|
+
|
|
164
|
+
### Working Memory (3)
|
|
165
|
+
|
|
166
|
+
Short-term, context-bound state. Miller's Law: max 7 items per context, oldest auto-evict. Default TTL 30 min (max 2 h).
|
|
167
|
+
|
|
168
|
+
| Tool | Description |
|
|
169
|
+
|------|-------------|
|
|
170
|
+
| `working_memory_get` | Load current state for a context. Always call at task start. |
|
|
171
|
+
| `working_memory_add` | Persist current state. Context formats: `agent:{uuid}`, `universe:{uuid}`, `organization:{uuid}`. |
|
|
172
|
+
| `working_memory_clear` | Remove all items for a context. |
|
|
173
|
+
|
|
174
|
+
### Sessions (5)
|
|
175
|
+
|
|
176
|
+
Session = logical container for streams, working memory, and capture.
|
|
177
|
+
|
|
178
|
+
| Tool | Description |
|
|
179
|
+
|------|-------------|
|
|
180
|
+
| `session_start` | Begin a session for an agent. Returns `session_id`. Supports `parent_session_id` for subagent inheritance. |
|
|
181
|
+
| `session_end` | Terminate a session and finalise its summary. |
|
|
182
|
+
| `session_context` | Combined working + long-term retrieval, enhanced with extracted session topics. |
|
|
183
|
+
| `session_summary` | Generate a structured recap (decisions, learnings, pending items). |
|
|
184
|
+
| `session_clear` | Wipe a session's working state without ending it. |
|
|
185
|
+
|
|
186
|
+
### Stream (4)
|
|
187
|
+
|
|
188
|
+
Continuous capture pipeline. Items are batched, scored, and the important ones promoted to permanent memories.
|
|
189
|
+
|
|
190
|
+
| Tool | Description |
|
|
191
|
+
|------|-------------|
|
|
192
|
+
| `stream_capture` | Record an input / output / internal note tied to a session. |
|
|
193
|
+
| `stream_get_session` | Replay every captured item for a `session_id`. |
|
|
194
|
+
| `stream_get_recent` | Recent stream items across sessions (filterable). |
|
|
195
|
+
| `stream_process` | Promote pending items to long-term memory (manual trigger; scheduler also runs every 5 min). |
|
|
196
|
+
|
|
197
|
+
### Context & Attention (2)
|
|
198
|
+
|
|
199
|
+
| Tool | Description |
|
|
200
|
+
|------|-------------|
|
|
201
|
+
| `memory_get_context` | Build a token-bounded context window for a task description. |
|
|
202
|
+
| `search_with_attention` | RAG with task-type-aware weighting. `task_type` ∈ {implementation, debug, deploy, documentation, review} drives memory_type weights. |
|
|
203
|
+
|
|
204
|
+
### Users (2)
|
|
205
|
+
|
|
206
|
+
| Tool | Description |
|
|
207
|
+
|------|-------------|
|
|
208
|
+
| `user_register` | Register a user with the memory system (idempotent). |
|
|
209
|
+
| `user_get_context` | Retrieve combined user memories and tenant context. |
|
|
210
|
+
|
|
211
|
+
### Maintenance (2)
|
|
212
|
+
|
|
213
|
+
| Tool | Description |
|
|
214
|
+
|------|-------------|
|
|
215
|
+
| `heartbeat_tick` | Cheap tick for an agent — drives relevance decay and lightweight consolidation. |
|
|
216
|
+
| `sleep_run` | Trigger a sleep cycle. `phases` ⊆ {evaluation, forgetting, consolidation, relevance}. Mirrors the scheduler's normal/deep sleeps. |
|
|
217
|
+
|
|
218
|
+
### Consolidation (3)
|
|
219
|
+
|
|
220
|
+
| Tool | Description |
|
|
221
|
+
|------|-------------|
|
|
222
|
+
| `consolidation_detect_conflicts` | Surface candidate conflicting memories above a similarity threshold. |
|
|
223
|
+
| `consolidation_resolve_conflict` | Keep one memory, supersede the other with a written resolution note. |
|
|
224
|
+
| `consolidation_detect_redundant` | Find near-duplicates ready for merge or removal. |
|
|
225
|
+
|
|
226
|
+
### Memory Hierarchy (2)
|
|
227
|
+
|
|
228
|
+
| Tool | Description |
|
|
229
|
+
|------|-------------|
|
|
230
|
+
| `hierarchy_consolidate` | Roll several related memories into a higher-level summary memory. |
|
|
231
|
+
| `hierarchy_get_tree` | Inspect the consolidation tree for a root memory. |
|
|
232
|
+
|
|
233
|
+
### Agents (6)
|
|
234
|
+
|
|
235
|
+
Agent CRUD + orchestrated invocation.
|
|
236
|
+
|
|
237
|
+
| Tool | Description |
|
|
238
|
+
|------|-------------|
|
|
239
|
+
| `agent_create` | Create an agent (optionally from a template) scoped to a universe / team. |
|
|
240
|
+
| `agent_list` | List agents in the caller's tenant. Auto-scoped via API key. |
|
|
241
|
+
| `agent_get` | Fetch an agent by `agent_id`. |
|
|
242
|
+
| `agent_update` | Update persona, team, universe, etc. |
|
|
243
|
+
| `agent_delete` | Delete an agent. |
|
|
244
|
+
| `agent_invoke` | Programmatically invoke an agent with a task. Returns its tool calls and final answer. Used by delegation. |
|
|
245
|
+
|
|
246
|
+
### Universes (4)
|
|
247
|
+
|
|
248
|
+
Universe = tenant-scoped knowledge domain. Holds the L0–L3 hierarchy.
|
|
249
|
+
|
|
250
|
+
| Tool | Description |
|
|
251
|
+
|------|-------------|
|
|
252
|
+
| `universe_create` | Create a universe in the caller's org. Slug + name + description. |
|
|
253
|
+
| `universe_list` | List universes for the resolved org. |
|
|
254
|
+
| `universe_update` | Patch a universe (name, description). |
|
|
255
|
+
| `universe_hierarchy` | Walk the full L0 → L3 tree for a universe with status icons. |
|
|
256
|
+
|
|
257
|
+
### Knowledge Hierarchy L0–L3 (16)
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
Universe
|
|
261
|
+
└─ Concept (L0) body of knowledge
|
|
262
|
+
└─ Theme (L1) thematic split
|
|
263
|
+
└─ Topic (L2) unit of work (status: pending / in_progress / completed)
|
|
264
|
+
└─ Point (L3) atomic knowledge unit
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Each level exposes `create`, `list`, `update`, `delete`:
|
|
268
|
+
|
|
269
|
+
- **L0 Concepts**: `concept_create`, `concept_list`, `concept_update`, `concept_delete`
|
|
270
|
+
- **L1 Themes**: `theme_create`, `theme_list`, `theme_update`, `theme_delete`
|
|
271
|
+
- **L2 Topics**: `topic_create`, `topic_list`, `topic_update`, `topic_delete`
|
|
272
|
+
- **L3 Points**: `point_create`, `point_list`, `point_update`, `point_delete`
|
|
273
|
+
|
|
274
|
+
`topic_update` and `point_update` accept a `status` field so agents can mark progress.
|
|
275
|
+
|
|
276
|
+
### Training Pipeline (6)
|
|
277
|
+
|
|
278
|
+
Teach the system explicitly — facts, procedures, Q&A pairs, full documents.
|
|
279
|
+
|
|
280
|
+
| Tool | Description |
|
|
281
|
+
|------|-------------|
|
|
282
|
+
| `training_teach` | Inject a fact or procedure. `type` ∈ {semantic, procedural}. |
|
|
283
|
+
| `training_qa` | Train with `pairs` of `{question, answer}`. |
|
|
284
|
+
| `training_stats` | Coverage statistics across the corpus. |
|
|
285
|
+
| `training_gaps` | Detected gaps in knowledge / weakly-covered topics. |
|
|
286
|
+
| `training_recent` | Most recently ingested memories from training. |
|
|
287
|
+
|
|
288
|
+
### Orchestration — Tasks (4)
|
|
289
|
+
|
|
290
|
+
Task table on the Memory Engine drives multi-agent orchestration.
|
|
291
|
+
|
|
292
|
+
| Tool | Description |
|
|
293
|
+
|------|-------------|
|
|
294
|
+
| `task_create` | Create a task (title, description, optional `assigned_agent_id`, parent task). |
|
|
295
|
+
| `task_get` | Fetch a task with its full state. |
|
|
296
|
+
| `task_list` | List tasks with filters (status, agent, parent). |
|
|
297
|
+
| `task_update` | Update status, output, or assignment. |
|
|
298
|
+
|
|
299
|
+
### Skills (4)
|
|
300
|
+
|
|
301
|
+
Reusable behaviours assigned to agents.
|
|
302
|
+
|
|
303
|
+
| Tool | Description |
|
|
304
|
+
|------|-------------|
|
|
305
|
+
| `skill_list` | Enumerate available skills in the tenant. |
|
|
306
|
+
| `skill_assign` | Attach a skill to an agent. |
|
|
307
|
+
| `skill_remove` | Detach a skill from an agent. |
|
|
308
|
+
| `skill_agent_list` | List the skills owned by a given agent. |
|
|
309
|
+
|
|
310
|
+
## Memory Types
|
|
311
|
+
|
|
312
|
+
| Type | Use Case | Example |
|
|
313
|
+
|------|----------|---------|
|
|
314
|
+
| `episodic` | Record what happened | `Bug: payment endpoint returned 500. Cause: missing null check on customer.address. Fix: guard + 422 response. File: services/payment.py` |
|
|
315
|
+
| `semantic` | Store facts and knowledge | `MemoryResponse now includes universe_id (UUID | None) so the frontend can group memories per universe in the graph view.` |
|
|
316
|
+
| `procedural` | Document how-to procedures | `Procedure: rotate-claude-credentials. Steps: 1) aws ecr login, 2) kubectl set image deployment/agent..., 3) verify pod is Ready. Caveat: deployment is named "agent", not "agent-service".` |
|
|
317
|
+
|
|
318
|
+
## Scopes
|
|
319
|
+
|
|
320
|
+
| Scope | Visibility | Use Case |
|
|
321
|
+
|-------|------------|----------|
|
|
322
|
+
| `user` | Only the storing user | Personal preferences and history |
|
|
323
|
+
| `agent` | Only the storing agent | Agent-private notes |
|
|
324
|
+
| `universe` | All agents inside the universe (department) | Shared domain knowledge |
|
|
325
|
+
| `organization` | All agents in the tenant | Org-wide procedures and facts |
|
|
326
|
+
|
|
327
|
+
> The legacy `agent / project / global` scopes were retired together with `project_id`. Universes replaced projects as the isolation boundary inside an organisation.
|
|
328
|
+
|
|
329
|
+
## Best Practices
|
|
330
|
+
|
|
331
|
+
Required formats for memories — make future retrieval deterministic:
|
|
332
|
+
|
|
333
|
+
- **Bug:** `Bug: <description>. Cause: <root cause>. Fix: <solution>. File: <path>`
|
|
334
|
+
- **Pattern:** `Pattern: <description>. When to use: <context>. File: <path>`
|
|
335
|
+
- **Procedure:** `Procedure: <name>. Steps: 1) ... 2) ... 3) .... Caveats: <warnings>`
|
|
336
|
+
|
|
337
|
+
Operational guidelines:
|
|
338
|
+
|
|
339
|
+
1. **Always boot first.** Call `session_start` + `working_memory_get` + `search_with_attention` before answering.
|
|
340
|
+
2. **Reinforce / weaken on use.** When a recalled memory helps, `memory_reinforce`. When it's wrong, `memory_weaken` (and replace it).
|
|
341
|
+
3. **Capture every 2–3 turns.** `working_memory_add` for state, `stream_capture` for decisions, `memory_store` for crystallised learnings.
|
|
342
|
+
4. **Pick the right `task_type`.** `search_with_attention` weights memory types per task. `debug` favours episodic, `documentation` favours semantic, `deploy` favours procedural.
|
|
343
|
+
5. **Close the loop.** End sessions with `session_summary` + `stream_process` + `session_end`. Run `sleep_run` for deeper consolidation when batches grow.
|
|
344
|
+
6. **Audit before clutter accumulates.** Periodic `consolidation_detect_conflicts` + `consolidation_detect_redundant` keep recall sharp.
|
|
345
|
+
|
|
346
|
+
## Architecture
|
|
347
|
+
|
|
348
|
+
```
|
|
349
|
+
+---------------------+ +------------------+ +----------------------+
|
|
350
|
+
| Claude Agent / | <---> | Frinus MCP | <---> | Memory Engine |
|
|
351
|
+
| Claude Code | stdio | (this server) | HTTPS | (memories, sessions, |
|
|
352
|
+
+---------------------+ +------------------+ | hierarchy, tasks) |
|
|
353
|
+
| +----------------------+
|
|
354
|
+
| |
|
|
355
|
+
| v
|
|
356
|
+
| +----------------------+
|
|
357
|
+
| | PostgreSQL+pgvector |
|
|
358
|
+
| | + Apache AGE (graph) |
|
|
359
|
+
| +----------------------+
|
|
360
|
+
|
|
|
361
|
+
+---HTTPS---> Control Plane (universes, orgs)
|
|
362
|
+
+---HTTPS---> Agent Service (agents, invocation, skills)
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
- **Memory Engine** owns memories, working memory, sessions, streams, the L0–L3 hierarchy, training, sleep cycles, tasks, skills.
|
|
366
|
+
- **Control Plane** owns orgs, universes, members, API keys, billing, credentials, white-label, LLM keys.
|
|
367
|
+
- **Agent Service** owns agent runtime, tool dispatch, team routing, persona, invocation.
|
|
368
|
+
|
|
369
|
+
Tenant isolation is database-per-tenant. The MCP resolves your tenant org ID from the API key at boot — you never pass `org_id` manually.
|
|
370
|
+
|
|
371
|
+
## Development
|
|
372
|
+
|
|
373
|
+
```
|
|
374
|
+
mcp/
|
|
375
|
+
src/
|
|
376
|
+
index.ts Entry: MCP server, dispatch, auth bootstrap
|
|
377
|
+
client/
|
|
378
|
+
memory-client.ts HTTP client + identity state for Memory Engine
|
|
379
|
+
cp-client.ts HTTP client for Control Plane
|
|
380
|
+
agent-client.ts HTTP client for Agent Service
|
|
381
|
+
tools/
|
|
382
|
+
definitions.ts Tool schemas (70 tools)
|
|
383
|
+
handlers.ts Tool handlers (70 handlers)
|
|
384
|
+
capture/
|
|
385
|
+
interaction-capture.ts Auto stream capture for every tool call
|
|
386
|
+
types/
|
|
387
|
+
index.ts Shared types
|
|
388
|
+
dist/ Compiled output (npm run build)
|
|
389
|
+
package.json
|
|
390
|
+
tsconfig.json
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
Scripts:
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
npm run build # tsc to dist/
|
|
397
|
+
npm run dev # tsx hot reload (src/index.ts)
|
|
398
|
+
npm start # node dist/index.js
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Type checking: TypeScript 5.6+, ES modules, axios.
|
|
402
|
+
|
|
403
|
+
## License
|
|
404
|
+
|
|
405
|
+
See repository root for license terms.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interaction Capture - Automatic chat interaction tracking.
|
|
3
|
+
*
|
|
4
|
+
* Captures MCP tool calls (input and output) to the memory stream.
|
|
5
|
+
* Runs as fire-and-forget so it never blocks tool responses.
|
|
6
|
+
* Tools related to the stream itself are excluded to avoid recursion.
|
|
7
|
+
*/
|
|
8
|
+
import type { MemoryClientInterface, InteractionCaptureInterface } from "../types/index.js";
|
|
9
|
+
export declare class InteractionCapture implements InteractionCaptureInterface {
|
|
10
|
+
private client;
|
|
11
|
+
/** Session ID - can be replaced by session_start with the formal session ID. */
|
|
12
|
+
sessionId: string;
|
|
13
|
+
/** Maximum characters captured per interaction. */
|
|
14
|
+
private static readonly MAX_CONTENT_LENGTH;
|
|
15
|
+
/** Tools that should NOT be captured (to avoid recursion / noise). */
|
|
16
|
+
private static readonly EXCLUDED_TOOLS;
|
|
17
|
+
constructor(client: MemoryClientInterface);
|
|
18
|
+
/** Returns the current session ID. */
|
|
19
|
+
getSessionId(): string;
|
|
20
|
+
/**
|
|
21
|
+
* Read-only tools whose CALL (input) side is not worth capturing.
|
|
22
|
+
* We still capture their results if meaningful.
|
|
23
|
+
*/
|
|
24
|
+
private static readonly READ_ONLY_TOOLS;
|
|
25
|
+
/** Trivial result strings that carry no learning value. */
|
|
26
|
+
private static readonly TRIVIAL_RESULTS;
|
|
27
|
+
/** Whether this tool call should be captured. */
|
|
28
|
+
private shouldCapture;
|
|
29
|
+
/**
|
|
30
|
+
* Whether the INPUT side of a tool call should be captured.
|
|
31
|
+
* Read-only tools only get their results captured, not their inputs.
|
|
32
|
+
*/
|
|
33
|
+
private shouldCaptureInput;
|
|
34
|
+
/**
|
|
35
|
+
* Whether a tool result is too trivial to be worth capturing.
|
|
36
|
+
* Filters out very short results and known no-op strings.
|
|
37
|
+
*/
|
|
38
|
+
private isNoiseResult;
|
|
39
|
+
/** Truncate content to the max allowed length. */
|
|
40
|
+
private truncate;
|
|
41
|
+
/** Extract agent_id from tool arguments when available. */
|
|
42
|
+
private extractAgentId;
|
|
43
|
+
/**
|
|
44
|
+
* Capture both the input (tool call) and output (tool result) of an
|
|
45
|
+
* MCP tool invocation. Failures are silently ignored.
|
|
46
|
+
*
|
|
47
|
+
* Pre-filters applied:
|
|
48
|
+
* - Excluded tools are never captured (recursion guard).
|
|
49
|
+
* - Read-only tool CALLS (inputs) are skipped; only their results matter.
|
|
50
|
+
* - Trivial / very short results are skipped to reduce noise.
|
|
51
|
+
*/
|
|
52
|
+
captureToolCall(toolName: string, args: Record<string, unknown>, result: {
|
|
53
|
+
content: Array<{
|
|
54
|
+
type: string;
|
|
55
|
+
text: string;
|
|
56
|
+
}>;
|
|
57
|
+
isError?: boolean;
|
|
58
|
+
}): void;
|
|
59
|
+
/**
|
|
60
|
+
* Assign importance based on tool name and direction.
|
|
61
|
+
* Higher importance for tools that store/modify data, lower for reads.
|
|
62
|
+
*/
|
|
63
|
+
private importanceFor;
|
|
64
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
export class InteractionCapture {
|
|
2
|
+
client;
|
|
3
|
+
/** Session ID - can be replaced by session_start with the formal session ID. */
|
|
4
|
+
sessionId;
|
|
5
|
+
/** Maximum characters captured per interaction. */
|
|
6
|
+
static MAX_CONTENT_LENGTH = 5000;
|
|
7
|
+
/** Tools that should NOT be captured (to avoid recursion / noise). */
|
|
8
|
+
static EXCLUDED_TOOLS = new Set([
|
|
9
|
+
"stream_capture",
|
|
10
|
+
"stream_process",
|
|
11
|
+
"stream_get_session",
|
|
12
|
+
"stream_get_recent",
|
|
13
|
+
"heartbeat_tick",
|
|
14
|
+
]);
|
|
15
|
+
constructor(client) {
|
|
16
|
+
this.client = client;
|
|
17
|
+
// One session ID per MCP server process lifetime
|
|
18
|
+
this.sessionId = `mcp-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
19
|
+
}
|
|
20
|
+
/** Returns the current session ID. */
|
|
21
|
+
getSessionId() {
|
|
22
|
+
return this.sessionId;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Read-only tools whose CALL (input) side is not worth capturing.
|
|
26
|
+
* We still capture their results if meaningful.
|
|
27
|
+
*/
|
|
28
|
+
static READ_ONLY_TOOLS = new Set([
|
|
29
|
+
"memory_search",
|
|
30
|
+
"memory_get",
|
|
31
|
+
"memory_list",
|
|
32
|
+
"working_memory_get",
|
|
33
|
+
"search_with_attention",
|
|
34
|
+
"memory_get_context",
|
|
35
|
+
"session_context",
|
|
36
|
+
"session_summary",
|
|
37
|
+
"user_get_context",
|
|
38
|
+
"hierarchy_get_tree",
|
|
39
|
+
"consolidation_detect_conflicts",
|
|
40
|
+
"consolidation_detect_redundant",
|
|
41
|
+
"agent_list",
|
|
42
|
+
"agent_get",
|
|
43
|
+
"universe_list",
|
|
44
|
+
"concept_list",
|
|
45
|
+
"theme_list",
|
|
46
|
+
"topic_list",
|
|
47
|
+
"point_list",
|
|
48
|
+
"universe_hierarchy",
|
|
49
|
+
]);
|
|
50
|
+
/** Trivial result strings that carry no learning value. */
|
|
51
|
+
static TRIVIAL_RESULTS = new Set([
|
|
52
|
+
"ok",
|
|
53
|
+
"success",
|
|
54
|
+
"done",
|
|
55
|
+
"true",
|
|
56
|
+
"false",
|
|
57
|
+
"null",
|
|
58
|
+
"{}",
|
|
59
|
+
"[]",
|
|
60
|
+
]);
|
|
61
|
+
/** Whether this tool call should be captured. */
|
|
62
|
+
shouldCapture(toolName) {
|
|
63
|
+
return !InteractionCapture.EXCLUDED_TOOLS.has(toolName);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Whether the INPUT side of a tool call should be captured.
|
|
67
|
+
* Read-only tools only get their results captured, not their inputs.
|
|
68
|
+
*/
|
|
69
|
+
shouldCaptureInput(toolName) {
|
|
70
|
+
return !InteractionCapture.READ_ONLY_TOOLS.has(toolName);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Whether a tool result is too trivial to be worth capturing.
|
|
74
|
+
* Filters out very short results and known no-op strings.
|
|
75
|
+
*/
|
|
76
|
+
isNoiseResult(outputTexts) {
|
|
77
|
+
const trimmed = outputTexts.trim();
|
|
78
|
+
if (trimmed.length < 20)
|
|
79
|
+
return true;
|
|
80
|
+
if (InteractionCapture.TRIVIAL_RESULTS.has(trimmed.toLowerCase()))
|
|
81
|
+
return true;
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
/** Truncate content to the max allowed length. */
|
|
85
|
+
truncate(text) {
|
|
86
|
+
if (text.length <= InteractionCapture.MAX_CONTENT_LENGTH)
|
|
87
|
+
return text;
|
|
88
|
+
return text.slice(0, InteractionCapture.MAX_CONTENT_LENGTH) + "... [truncated]";
|
|
89
|
+
}
|
|
90
|
+
/** Extract agent_id from tool arguments when available. */
|
|
91
|
+
extractAgentId(args) {
|
|
92
|
+
return args.agent_id || undefined;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Capture both the input (tool call) and output (tool result) of an
|
|
96
|
+
* MCP tool invocation. Failures are silently ignored.
|
|
97
|
+
*
|
|
98
|
+
* Pre-filters applied:
|
|
99
|
+
* - Excluded tools are never captured (recursion guard).
|
|
100
|
+
* - Read-only tool CALLS (inputs) are skipped; only their results matter.
|
|
101
|
+
* - Trivial / very short results are skipped to reduce noise.
|
|
102
|
+
*/
|
|
103
|
+
captureToolCall(toolName, args, result) {
|
|
104
|
+
if (!this.shouldCapture(toolName))
|
|
105
|
+
return;
|
|
106
|
+
const agentId = this.extractAgentId(args);
|
|
107
|
+
const metadata = { tool_name: toolName };
|
|
108
|
+
// --- Capture INPUT (the tool call) ---
|
|
109
|
+
// Skip input capture for read-only tools (only results are interesting)
|
|
110
|
+
if (this.shouldCaptureInput(toolName)) {
|
|
111
|
+
const inputContent = this.truncate(`[MCP Tool Call] ${toolName}\n${JSON.stringify(args, null, 2)}`);
|
|
112
|
+
this.client
|
|
113
|
+
.captureStream({
|
|
114
|
+
session_id: this.sessionId,
|
|
115
|
+
content: inputContent,
|
|
116
|
+
direction: "input",
|
|
117
|
+
agent_id: agentId,
|
|
118
|
+
importance: this.importanceFor(toolName, "input"),
|
|
119
|
+
metadata,
|
|
120
|
+
})
|
|
121
|
+
.catch((err) => {
|
|
122
|
+
console.error('[MCP] Capture failed (input):', err.message);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// --- Capture OUTPUT (the tool result) ---
|
|
126
|
+
const outputTexts = result.content
|
|
127
|
+
.filter((c) => c.type === "text")
|
|
128
|
+
.map((c) => c.text)
|
|
129
|
+
.join("\n");
|
|
130
|
+
// Skip trivial / noise results
|
|
131
|
+
if (this.isNoiseResult(outputTexts))
|
|
132
|
+
return;
|
|
133
|
+
const outputContent = this.truncate(`[MCP Tool Result] ${toolName}${result.isError ? " (ERROR)" : ""}\n${outputTexts}`);
|
|
134
|
+
this.client
|
|
135
|
+
.captureStream({
|
|
136
|
+
session_id: this.sessionId,
|
|
137
|
+
content: outputContent,
|
|
138
|
+
direction: "output",
|
|
139
|
+
agent_id: agentId,
|
|
140
|
+
importance: this.importanceFor(toolName, "output"),
|
|
141
|
+
metadata: { ...metadata, is_error: result.isError || false },
|
|
142
|
+
})
|
|
143
|
+
.catch((err) => {
|
|
144
|
+
console.error('[MCP] Capture failed (output):', err.message);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Assign importance based on tool name and direction.
|
|
149
|
+
* Higher importance for tools that store/modify data, lower for reads.
|
|
150
|
+
*/
|
|
151
|
+
importanceFor(toolName, direction) {
|
|
152
|
+
// Write / mutate operations are more important
|
|
153
|
+
const highImportance = new Set([
|
|
154
|
+
"memory_store",
|
|
155
|
+
"memory_delete",
|
|
156
|
+
"working_memory_add",
|
|
157
|
+
"session_start",
|
|
158
|
+
"session_end",
|
|
159
|
+
"sleep_run",
|
|
160
|
+
"consolidation_resolve_conflict",
|
|
161
|
+
"hierarchy_consolidate",
|
|
162
|
+
"agent_create",
|
|
163
|
+
"agent_delete",
|
|
164
|
+
"agent_update",
|
|
165
|
+
"universe_create",
|
|
166
|
+
"universe_update",
|
|
167
|
+
"concept_create",
|
|
168
|
+
"concept_delete",
|
|
169
|
+
"concept_update",
|
|
170
|
+
"theme_create",
|
|
171
|
+
"theme_delete",
|
|
172
|
+
"theme_update",
|
|
173
|
+
"topic_create",
|
|
174
|
+
"topic_delete",
|
|
175
|
+
"topic_update",
|
|
176
|
+
"point_create",
|
|
177
|
+
"point_delete",
|
|
178
|
+
"point_update",
|
|
179
|
+
]);
|
|
180
|
+
if (highImportance.has(toolName)) {
|
|
181
|
+
return direction === "input" ? 0.7 : 0.6;
|
|
182
|
+
}
|
|
183
|
+
// Read / search operations
|
|
184
|
+
return direction === "input" ? 0.4 : 0.3;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AgentClientInterface } from "../types/index.js";
|
|
2
|
+
export declare class AgentClient implements AgentClientInterface {
|
|
3
|
+
private client;
|
|
4
|
+
constructor(baseURL: string, apiKey: string);
|
|
5
|
+
createAgent(data: {
|
|
6
|
+
name: string;
|
|
7
|
+
template_id?: string;
|
|
8
|
+
universe_id?: string;
|
|
9
|
+
persona?: Record<string, unknown>;
|
|
10
|
+
team_id?: string;
|
|
11
|
+
is_team_lead?: boolean;
|
|
12
|
+
}): Promise<any>;
|
|
13
|
+
listAgents(orgId?: string): Promise<any>;
|
|
14
|
+
getAgent(agentId: string): Promise<any>;
|
|
15
|
+
updateAgentPersona(agentId: string, data: {
|
|
16
|
+
name?: string;
|
|
17
|
+
personality?: string;
|
|
18
|
+
instructions?: string;
|
|
19
|
+
greeting?: string;
|
|
20
|
+
forbidden_topics?: string[];
|
|
21
|
+
language?: string;
|
|
22
|
+
specialization?: string;
|
|
23
|
+
}): Promise<any>;
|
|
24
|
+
deleteAgent(agentId: string): Promise<any>;
|
|
25
|
+
bootstrapAgent(agentId: string): Promise<any>;
|
|
26
|
+
invokeAgent(data: {
|
|
27
|
+
agent_id?: string;
|
|
28
|
+
agent_name?: string;
|
|
29
|
+
message: string;
|
|
30
|
+
task_id?: string;
|
|
31
|
+
context?: Record<string, unknown>;
|
|
32
|
+
timeout_seconds?: number;
|
|
33
|
+
}): Promise<any>;
|
|
34
|
+
}
|