frinus-mcp 3.2.1 → 3.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.
- package/README.md +121 -71
- package/dist/client/client-info.d.ts +25 -0
- package/dist/client/client-info.js +48 -0
- package/dist/client/cp-client.js +2 -0
- package/dist/index.js +12 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
# Frinus MCP Server
|
|
2
2
|
|
|
3
|
-
> **φρήν + νοῦς** —
|
|
3
|
+
> **φρήν + νοῦς** — Gives your Claude agent long-term, cognitive memory backed by the [Frinus](https://frinus.rdxsec.com.br) platform.
|
|
4
4
|
|
|
5
|
-
MCP (Model Context Protocol) server
|
|
5
|
+
`frinus-mcp` is an MCP (Model Context Protocol) server. It is the **bridge** between your MCP client — Claude Code, Cursor, or Claude Desktop — and the **Frinus platform hosted at https://frinus.rdxsec.com.br**.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
It does **not** run on its own. It stores nothing locally: every tool call is forwarded over HTTPS to your Frinus account. So before it does anything useful you need two things:
|
|
8
|
+
|
|
9
|
+
1. a **Frinus account** (free, no card), and
|
|
10
|
+
2. a **Frinus API key** (`sk-frinus-...`).
|
|
11
|
+
|
|
12
|
+
With those in place, the server exposes **70 tools** spanning cognitive memory, working memory, sessions, agents, the L0–L3 knowledge hierarchy, orchestration tasks, and training pipelines. It speaks stdio and works with any MCP-aware client.
|
|
13
|
+
|
|
14
|
+
- **Version:** 3.3.0
|
|
8
15
|
- **Tools:** 70 (see [Tools Reference](#tools-reference))
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
16
|
+
- **Platform:** https://frinus.rdxsec.com.br (hosted SaaS — the backend this server talks to)
|
|
17
|
+
- **npm package:** [`frinus-mcp`](https://www.npmjs.com/package/frinus-mcp)
|
|
18
|
+
|
|
19
|
+
## How it fits together
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
+-------------------+ +--------------+ +----------------------------+
|
|
23
|
+
| Your MCP client | stdio | frinus-mcp | HTTPS | Frinus platform |
|
|
24
|
+
| (Claude Code, | <----> | (this server,| <----> | frinus.rdxsec.com.br |
|
|
25
|
+
| Cursor, Desktop) | | via npx) | | (your memories live here) |
|
|
26
|
+
+-------------------+ +--------------+ +----------------------------+
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The server validates your API key against the platform at startup, resolves your account + organisation, and routes every tool call there. No API key → it refuses to start.
|
|
11
30
|
|
|
12
31
|
## Rule Zero — MCP is mandatory
|
|
13
32
|
|
|
@@ -20,70 +39,51 @@ The Frinus MCP **is** the agent's long-term memory, identity, and intelligence.
|
|
|
20
39
|
|
|
21
40
|
The complete protocol set lives in the global `CLAUDE.md` (Frinus organisation), summarised below in [The 7 Protocols](#the-7-protocols).
|
|
22
41
|
|
|
23
|
-
##
|
|
42
|
+
## Before you start — account + API key
|
|
24
43
|
|
|
25
|
-
|
|
26
|
-
- Frinus stack reachable (Memory Engine, Control Plane, Agent Service)
|
|
27
|
-
- Personal API key (`sk-frinus-...`) tied to your tenant
|
|
44
|
+
You need a Frinus account and an API key. Both are free to get.
|
|
28
45
|
|
|
29
|
-
|
|
46
|
+
### 1. Create a free account
|
|
30
47
|
|
|
31
|
-
|
|
48
|
+
Sign up at **https://frinus.rdxsec.com.br**. The **Free plan (R$0)** is enough to get started:
|
|
32
49
|
|
|
33
|
-
|
|
50
|
+
- 100 memories
|
|
51
|
+
- 20 queries/day
|
|
52
|
+
- no credit card required
|
|
53
|
+
|
|
54
|
+
### 2. Generate an API key
|
|
55
|
+
|
|
56
|
+
Once logged in, open your account **Settings → API Keys** and create a new key. You'll get a value shaped like:
|
|
34
57
|
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
npm run build
|
|
58
|
+
```
|
|
59
|
+
sk-frinus-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
38
60
|
```
|
|
39
61
|
|
|
40
|
-
|
|
62
|
+
Copy it now — for security it's shown **only once**. This value goes into the `FRINUS_API_KEY` environment variable below. Treat it like a password; never commit it to a repo.
|
|
41
63
|
|
|
42
|
-
##
|
|
64
|
+
## Getting started (zero to first memory)
|
|
43
65
|
|
|
44
|
-
|
|
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 |
|
|
66
|
+
Five minutes, four steps.
|
|
49
67
|
|
|
50
|
-
|
|
68
|
+
### Step 1 — Create your account
|
|
51
69
|
|
|
52
|
-
|
|
70
|
+
Sign up at https://frinus.rdxsec.com.br (Free plan, no card). See [above](#1-create-a-free-account).
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
# 1) npm package — powers `npx -y frinus-mcp` everywhere
|
|
56
|
-
npm version <patch|minor|major>
|
|
57
|
-
npm publish # prepublishOnly runs the build
|
|
72
|
+
### Step 2 — Generate your API key
|
|
58
73
|
|
|
59
|
-
|
|
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
|
-
```
|
|
74
|
+
Settings → API Keys → create. Copy the `sk-frinus-...` value. See [above](#2-generate-an-api-key).
|
|
64
75
|
|
|
65
|
-
|
|
76
|
+
### Step 3 — Add the server to your client
|
|
66
77
|
|
|
67
|
-
|
|
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` |
|
|
78
|
+
You don't install anything — `npx` fetches and runs the package on demand. The only value you must provide is your API key; the platform URLs already default to production.
|
|
74
79
|
|
|
75
|
-
|
|
80
|
+
**Claude Code (one-liner):**
|
|
76
81
|
|
|
77
82
|
```bash
|
|
78
|
-
|
|
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-...
|
|
83
|
+
claude mcp add frinus --env FRINUS_API_KEY=sk-frinus-... -- npx -y frinus-mcp@latest
|
|
82
84
|
```
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Add the server to your MCP client configuration. **Recommended (npx, no install):**
|
|
86
|
+
**Cursor / Claude Desktop / any client (config JSON):**
|
|
87
87
|
|
|
88
88
|
```json
|
|
89
89
|
{
|
|
@@ -99,35 +99,45 @@ Add the server to your MCP client configuration. **Recommended (npx, no install)
|
|
|
99
99
|
}
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
Config file locations:
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
claude
|
|
104
|
+
- **Claude Code:** `~/.claude.json`, under `mcpServers`.
|
|
105
|
+
- **Claude Desktop:** `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) / `~/.config/claude/claude_desktop_config.json` (Linux). Or use the one-click `.mcpb` extension and just paste your API key.
|
|
106
|
+
- **Cursor:** `~/.cursor/mcp.json`, under `mcpServers`.
|
|
107
|
+
|
|
108
|
+
Restart the client so it picks up the new server.
|
|
109
|
+
|
|
110
|
+
> Replace `sk-frinus-...` with the real key from Step 2. You normally set **only** `FRINUS_API_KEY` — every backend URL already defaults to the hosted platform. See [Environment Variables](#environment-variables) for the full list and the self-host override.
|
|
111
|
+
|
|
112
|
+
### Step 4 — Validate with your first memory
|
|
113
|
+
|
|
114
|
+
In your client, ask the agent to store and recall something:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
Store this in Frinus memory: "Our staging DB is reset every night at 02:00 UTC."
|
|
106
118
|
```
|
|
107
119
|
|
|
108
|
-
|
|
120
|
+
then in the same or a later session:
|
|
109
121
|
|
|
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
122
|
```
|
|
123
|
+
What time does our staging DB reset?
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Behind the scenes the agent calls `memory_store` then `memory_search` / `search_with_attention`. If the recall comes back, the bridge is live. You can confirm the same memory appears in the web app at https://frinus.rdxsec.com.br.
|
|
126
127
|
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
## Environment Variables
|
|
129
|
+
|
|
130
|
+
In normal use you set **only** `FRINUS_API_KEY`. The service URLs already point at the hosted platform — leave them alone unless you self-host (see [Advanced — self-host](#advanced--self-host--local-dev)).
|
|
131
|
+
|
|
132
|
+
| Variable | Default (hosted platform) | Description |
|
|
133
|
+
|----------|---------------------------|-------------|
|
|
134
|
+
| `FRINUS_API_KEY` | **required** | Your personal API key (`sk-frinus-...`). Resolves account + organisation at boot. |
|
|
135
|
+
| `MEMORY_SERVICE_URL` | `https://frinus-memory.rdxsec.com.br` | Memory Engine base URL |
|
|
136
|
+
| `FRINUS_CP_URL` | `https://frinus-api.rdxsec.com.br` | Control Plane base URL (account, orgs, API keys, billing) |
|
|
137
|
+
| `AGENT_SERVICE_URL` | `https://frinus-agents.rdxsec.com.br` | Agent Service base URL (agents, invocation, skills) |
|
|
138
|
+
| `FRINUS_MEMORY_API_KEY` | — | Legacy fallback for `FRINUS_API_KEY` |
|
|
129
139
|
|
|
130
|
-
On startup the server validates the API key
|
|
140
|
+
On startup the server validates the API key against the platform and resolves your organisation + logged-in user. If validation fails it aborts with `[FATAL]` so the client surfaces the error — re-check the key from Settings → API Keys.
|
|
131
141
|
|
|
132
142
|
## The 7 Protocols
|
|
133
143
|
|
|
@@ -409,6 +419,46 @@ Operational guidelines:
|
|
|
409
419
|
|
|
410
420
|
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.
|
|
411
421
|
|
|
422
|
+
## Advanced — self-host / local dev
|
|
423
|
+
|
|
424
|
+
Everything above targets the hosted platform at https://frinus.rdxsec.com.br, which is what almost everyone wants. If you run your own Frinus stack (or develop the MCP against a local backend), build from source and override the three URLs:
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
git clone https://github.com/frinus-ai/frinus-mcp && cd frinus-mcp
|
|
428
|
+
npm install
|
|
429
|
+
npm run build # emits dist/index.js
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
```json
|
|
433
|
+
{
|
|
434
|
+
"mcpServers": {
|
|
435
|
+
"frinus": {
|
|
436
|
+
"command": "node",
|
|
437
|
+
"args": ["/absolute/path/to/mcp/dist/index.js"],
|
|
438
|
+
"env": {
|
|
439
|
+
"FRINUS_API_KEY": "sk-frinus-...",
|
|
440
|
+
"MEMORY_SERVICE_URL": "http://localhost:8001",
|
|
441
|
+
"FRINUS_CP_URL": "http://localhost:8000",
|
|
442
|
+
"AGENT_SERVICE_URL": "http://localhost:8002"
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Publishing (maintainers)
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
# 1) npm package — powers `npx -y frinus-mcp` everywhere
|
|
453
|
+
npm version <patch|minor|major>
|
|
454
|
+
npm publish # prepublishOnly runs the build
|
|
455
|
+
|
|
456
|
+
# 2) .mcpb bundle — powers the Claude Desktop one-click install
|
|
457
|
+
npm run pack:mcpb # -> frinus.mcpb
|
|
458
|
+
gh release upload v<version> frinus.mcpb --repo frinus-ai/frinus-mcp --clobber
|
|
459
|
+
# Frontend points at: releases/latest/download/frinus.mcpb
|
|
460
|
+
```
|
|
461
|
+
|
|
412
462
|
## Development
|
|
413
463
|
|
|
414
464
|
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolved MCP client identity.
|
|
3
|
+
*
|
|
4
|
+
* The MCP client sends its `clientInfo: { name, version }` during the
|
|
5
|
+
* `initialize` handshake. We normalize the raw `name` into a small, stable
|
|
6
|
+
* set of buckets and expose it as the `X-Client` header so the Control Plane
|
|
7
|
+
* can attribute the `mcp_connected` PostHog event to the right client
|
|
8
|
+
* ("claude_code", "cursor", "desktop", "codex", "unknown").
|
|
9
|
+
*
|
|
10
|
+
* Module-level singleton (getter/setter), mirroring `resolvedTenantOrgId` in
|
|
11
|
+
* memory-client.ts: it is captured by an Axios request interceptor at
|
|
12
|
+
* construction time and read lazily on each request, so a single mutable
|
|
13
|
+
* module value is the least-surprising shape here.
|
|
14
|
+
*/
|
|
15
|
+
export type ResolvedClient = "claude_code" | "cursor" | "desktop" | "codex" | "unknown";
|
|
16
|
+
/**
|
|
17
|
+
* Normalize a raw `clientInfo.name` into a stable bucket.
|
|
18
|
+
*
|
|
19
|
+
* Order matters: the more specific "claude-code" check must run before the
|
|
20
|
+
* generic "claude" → desktop fallback, so Claude Code is never misclassified
|
|
21
|
+
* as the desktop app.
|
|
22
|
+
*/
|
|
23
|
+
export declare function normalizeClient(rawName?: string | null): ResolvedClient;
|
|
24
|
+
export declare function setResolvedClient(rawName?: string | null): void;
|
|
25
|
+
export declare function getResolvedClient(): ResolvedClient;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolved MCP client identity.
|
|
3
|
+
*
|
|
4
|
+
* The MCP client sends its `clientInfo: { name, version }` during the
|
|
5
|
+
* `initialize` handshake. We normalize the raw `name` into a small, stable
|
|
6
|
+
* set of buckets and expose it as the `X-Client` header so the Control Plane
|
|
7
|
+
* can attribute the `mcp_connected` PostHog event to the right client
|
|
8
|
+
* ("claude_code", "cursor", "desktop", "codex", "unknown").
|
|
9
|
+
*
|
|
10
|
+
* Module-level singleton (getter/setter), mirroring `resolvedTenantOrgId` in
|
|
11
|
+
* memory-client.ts: it is captured by an Axios request interceptor at
|
|
12
|
+
* construction time and read lazily on each request, so a single mutable
|
|
13
|
+
* module value is the least-surprising shape here.
|
|
14
|
+
*/
|
|
15
|
+
let resolvedClient = "unknown";
|
|
16
|
+
/**
|
|
17
|
+
* Normalize a raw `clientInfo.name` into a stable bucket.
|
|
18
|
+
*
|
|
19
|
+
* Order matters: the more specific "claude-code" check must run before the
|
|
20
|
+
* generic "claude" → desktop fallback, so Claude Code is never misclassified
|
|
21
|
+
* as the desktop app.
|
|
22
|
+
*/
|
|
23
|
+
export function normalizeClient(rawName) {
|
|
24
|
+
if (!rawName)
|
|
25
|
+
return "unknown";
|
|
26
|
+
const name = rawName.toLowerCase();
|
|
27
|
+
if (name.includes("claude-code") || name.includes("claudecode")) {
|
|
28
|
+
return "claude_code";
|
|
29
|
+
}
|
|
30
|
+
if (name.includes("cursor")) {
|
|
31
|
+
return "cursor";
|
|
32
|
+
}
|
|
33
|
+
if (name.includes("codex")) {
|
|
34
|
+
return "codex";
|
|
35
|
+
}
|
|
36
|
+
// Generic Claude clients (Claude Desktop, claude-ai, etc.) — must come after
|
|
37
|
+
// the claude-code check above.
|
|
38
|
+
if (name.includes("claude") || name.includes("claude-ai") || name.includes("desktop")) {
|
|
39
|
+
return "desktop";
|
|
40
|
+
}
|
|
41
|
+
return "unknown";
|
|
42
|
+
}
|
|
43
|
+
export function setResolvedClient(rawName) {
|
|
44
|
+
resolvedClient = normalizeClient(rawName);
|
|
45
|
+
}
|
|
46
|
+
export function getResolvedClient() {
|
|
47
|
+
return resolvedClient;
|
|
48
|
+
}
|
package/dist/client/cp-client.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import axios from "axios";
|
|
5
5
|
import { getResolvedTenantOrgId } from "./memory-client.js";
|
|
6
|
+
import { getResolvedClient } from "./client-info.js";
|
|
6
7
|
export class CpClient {
|
|
7
8
|
client;
|
|
8
9
|
constructor(baseURL, apiKey) {
|
|
@@ -16,6 +17,7 @@ export class CpClient {
|
|
|
16
17
|
const orgId = getResolvedTenantOrgId();
|
|
17
18
|
if (orgId)
|
|
18
19
|
config.headers['X-Tenant-ID'] = orgId;
|
|
20
|
+
config.headers['X-Client'] = getResolvedClient();
|
|
19
21
|
return config;
|
|
20
22
|
});
|
|
21
23
|
}
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextpro
|
|
|
17
17
|
import { MemoryClient, getResolvedUserEmail, getResolvedUserId, setResolvedTenantOrgId, setResolvedUserEmail, setResolvedUserId, getResolvedTenantOrgId, } from "./client/memory-client.js";
|
|
18
18
|
import { AgentClient } from "./client/agent-client.js";
|
|
19
19
|
import { CpClient } from "./client/cp-client.js";
|
|
20
|
+
import { getResolvedClient, setResolvedClient } from "./client/client-info.js";
|
|
20
21
|
import { InteractionCapture } from "./capture/interaction-capture.js";
|
|
21
22
|
import { TOOLS } from "./tools/definitions.js";
|
|
22
23
|
import { dispatchTool } from "./tools/handlers.js";
|
|
@@ -95,6 +96,17 @@ async function main() {
|
|
|
95
96
|
}
|
|
96
97
|
return toolResult;
|
|
97
98
|
});
|
|
99
|
+
// Resolve the connecting MCP client's identity for the X-Client header.
|
|
100
|
+
// `getClientVersion()` is only populated after the `initialize` handshake,
|
|
101
|
+
// so we read it from the `oninitialized` callback (fired on the client's
|
|
102
|
+
// `notifications/initialized`), not right after `connect()`.
|
|
103
|
+
server.oninitialized = () => {
|
|
104
|
+
const rawName = server.getClientVersion()?.name;
|
|
105
|
+
setResolvedClient(rawName);
|
|
106
|
+
// Logged once for calibration against real clients (stderr, not stdout —
|
|
107
|
+
// stdout is the MCP transport).
|
|
108
|
+
console.error(`[Client] clientInfo.name=${JSON.stringify(rawName ?? null)} normalized=${getResolvedClient()}`);
|
|
109
|
+
};
|
|
98
110
|
// Start server
|
|
99
111
|
const transport = new StdioServerTransport();
|
|
100
112
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frinus-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Frinus - MCP Server for Cognitive Memory (φρήν + νοῦς). Long-term memory, sessions, agents and knowledge for Claude and any MCP client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|