@mnemoverse/mcp-memory-server 0.3.1 → 0.3.2
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/.github/workflows/release.yml +138 -0
- package/CONTRIBUTING.md +52 -12
- package/dist/index.js +82 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/index.ts +114 -72
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Automated release pipeline for @mnemoverse/mcp-memory-server.
|
|
4
|
+
#
|
|
5
|
+
# Trigger: push a semver tag `vX.Y.Z` to main (e.g. `git tag v0.3.2 && git push origin v0.3.2`).
|
|
6
|
+
#
|
|
7
|
+
# What this workflow does, in order:
|
|
8
|
+
# 1. Checks that the tag's version matches package.json#version (catches "forgot to bump" mistakes).
|
|
9
|
+
# 2. npm ci + npm run build (prebuild regenerates every generated artifact from source.json).
|
|
10
|
+
# 3. npm run verify:configs — drift check, fails fast if anything is out of sync with source.json.
|
|
11
|
+
# 4. npm publish — uses a repo-scoped automation token stored in the NPM_TOKEN secret.
|
|
12
|
+
# 5. Installs mcp-publisher CLI (Linux amd64 bottle from the releases bucket).
|
|
13
|
+
# 6. mcp-publisher login github-oidc — uses GitHub Actions' OIDC token, NO stored PAT.
|
|
14
|
+
# 7. mcp-publisher publish — uploads the freshly-generated server.json to registry.modelcontextprotocol.io.
|
|
15
|
+
# 8. gh release create — posts a GitHub release with auto-generated notes.
|
|
16
|
+
#
|
|
17
|
+
# Why OIDC instead of a PAT:
|
|
18
|
+
# The GitHub OIDC provider issues a short-lived JWT that the MCP Registry trusts on the basis
|
|
19
|
+
# of the repo's owner (mnemoverse) and branch/tag claim. No long-lived secret sits in the repo.
|
|
20
|
+
# Prereq: `permissions: id-token: write` on the job, and the authenticating user's org
|
|
21
|
+
# membership (izgorodin in mnemoverse) must be public — already done.
|
|
22
|
+
#
|
|
23
|
+
# Prerequisite secrets you must add at repo Settings > Secrets and variables > Actions:
|
|
24
|
+
# - NPM_TOKEN — npm automation token from npmjs.com/settings/{org}/tokens/granular-access-tokens/new
|
|
25
|
+
# (select "Automation", grant publish access to @mnemoverse/mcp-memory-server).
|
|
26
|
+
#
|
|
27
|
+
# To release:
|
|
28
|
+
# 1. Bump version in package.json + src/index.ts on main (keep them in sync).
|
|
29
|
+
# 2. `npm run generate:configs` to propagate the new version to server.json.
|
|
30
|
+
# 3. Commit, push to main (through PR, drift CI passes).
|
|
31
|
+
# 4. `git tag v$(node -p "require('./package.json').version")`.
|
|
32
|
+
# 5. `git push origin v0.x.y`.
|
|
33
|
+
# The workflow does the rest.
|
|
34
|
+
|
|
35
|
+
on:
|
|
36
|
+
push:
|
|
37
|
+
tags:
|
|
38
|
+
- "v*"
|
|
39
|
+
workflow_dispatch:
|
|
40
|
+
inputs:
|
|
41
|
+
tag:
|
|
42
|
+
description: "Tag to release (e.g. v0.3.2). Must already exist."
|
|
43
|
+
required: true
|
|
44
|
+
|
|
45
|
+
jobs:
|
|
46
|
+
release:
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
permissions:
|
|
49
|
+
# contents:write — needed to create the GitHub release.
|
|
50
|
+
# id-token:write — required by `mcp-publisher login github-oidc`.
|
|
51
|
+
contents: write
|
|
52
|
+
id-token: write
|
|
53
|
+
|
|
54
|
+
steps:
|
|
55
|
+
- name: Checkout tag
|
|
56
|
+
uses: actions/checkout@v4
|
|
57
|
+
with:
|
|
58
|
+
ref: ${{ github.event.inputs.tag || github.ref }}
|
|
59
|
+
fetch-depth: 0
|
|
60
|
+
|
|
61
|
+
- name: Setup Node
|
|
62
|
+
uses: actions/setup-node@v4
|
|
63
|
+
with:
|
|
64
|
+
node-version: "20"
|
|
65
|
+
registry-url: "https://registry.npmjs.org"
|
|
66
|
+
|
|
67
|
+
- name: Verify tag matches package.json#version
|
|
68
|
+
run: |
|
|
69
|
+
set -euo pipefail
|
|
70
|
+
TAG="${{ github.event.inputs.tag || github.ref_name }}"
|
|
71
|
+
TAG_VERSION="${TAG#v}"
|
|
72
|
+
PKG_VERSION="$(node -p "require('./package.json').version")"
|
|
73
|
+
echo "tag: $TAG"
|
|
74
|
+
echo "tag version: $TAG_VERSION"
|
|
75
|
+
echo "pkg version: $PKG_VERSION"
|
|
76
|
+
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
|
|
77
|
+
echo "::error::Tag $TAG (version $TAG_VERSION) does not match package.json version $PKG_VERSION"
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
- name: Install dependencies
|
|
82
|
+
run: npm ci
|
|
83
|
+
|
|
84
|
+
- name: Build (runs generate:configs via prebuild)
|
|
85
|
+
run: npm run build
|
|
86
|
+
|
|
87
|
+
- name: Verify no drift between source.json and generated artifacts
|
|
88
|
+
run: npm run verify:configs
|
|
89
|
+
|
|
90
|
+
- name: Publish to npm
|
|
91
|
+
run: npm publish --access public
|
|
92
|
+
env:
|
|
93
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
94
|
+
|
|
95
|
+
- name: Install mcp-publisher CLI
|
|
96
|
+
run: |
|
|
97
|
+
set -euo pipefail
|
|
98
|
+
curl -sSL \
|
|
99
|
+
"https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz" \
|
|
100
|
+
| tar xz mcp-publisher
|
|
101
|
+
./mcp-publisher --help
|
|
102
|
+
|
|
103
|
+
- name: Authenticate with MCP Registry via GitHub OIDC
|
|
104
|
+
run: ./mcp-publisher login github-oidc
|
|
105
|
+
|
|
106
|
+
- name: Publish to Official MCP Registry
|
|
107
|
+
run: ./mcp-publisher publish
|
|
108
|
+
|
|
109
|
+
- name: Verify the registry entry shows the new version
|
|
110
|
+
run: |
|
|
111
|
+
set -euo pipefail
|
|
112
|
+
TAG="${{ github.event.inputs.tag || github.ref_name }}"
|
|
113
|
+
VERSION="${TAG#v}"
|
|
114
|
+
echo "Waiting a few seconds for the registry to propagate..."
|
|
115
|
+
sleep 3
|
|
116
|
+
RESULT="$(curl -sS "https://registry.modelcontextprotocol.io/v0.1/servers/io.github.mnemoverse%2Fmcp-memory-server/versions/$VERSION")"
|
|
117
|
+
echo "$RESULT" | python3 -m json.tool
|
|
118
|
+
echo "$RESULT" | python3 -c "
|
|
119
|
+
import sys, json
|
|
120
|
+
d = json.load(sys.stdin)
|
|
121
|
+
srv = d['server']
|
|
122
|
+
assert srv['version'] == '$VERSION', f\"registry version {srv['version']} != $VERSION\"
|
|
123
|
+
print(f'✓ Registry shows {srv[\"name\"]}@{srv[\"version\"]}')
|
|
124
|
+
"
|
|
125
|
+
|
|
126
|
+
- name: Create GitHub release
|
|
127
|
+
uses: softprops/action-gh-release@v2
|
|
128
|
+
with:
|
|
129
|
+
tag_name: ${{ github.event.inputs.tag || github.ref_name }}
|
|
130
|
+
name: ${{ github.event.inputs.tag || github.ref_name }}
|
|
131
|
+
generate_release_notes: true
|
|
132
|
+
body: |
|
|
133
|
+
Published via automated release pipeline (`.github/workflows/release.yml`).
|
|
134
|
+
|
|
135
|
+
- npm: https://www.npmjs.com/package/@mnemoverse/mcp-memory-server
|
|
136
|
+
- MCP Registry: https://registry.modelcontextprotocol.io/v0.1/servers?search=mnemoverse
|
|
137
|
+
|
|
138
|
+
Full changelog below.
|
package/CONTRIBUTING.md
CHANGED
|
@@ -103,9 +103,13 @@ The generator is **idempotent**: running it twice in a row produces zero changes
|
|
|
103
103
|
|
|
104
104
|
`package.json#version` → `src/index.ts#version` (server name reported to MCP clients) → `server.json#version` (Official MCP Registry).
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
These are bumped manually in the first two files and propagated by the generator to the third. If they drift, the drift check on `server.json` catches it on every PR.
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
### Automated release pipeline
|
|
109
|
+
|
|
110
|
+
Releases are automated by [`.github/workflows/release.yml`](.github/workflows/release.yml). You bump the version on `main`, push a semver tag, and the workflow does the rest: npm publish, MCP Registry publish (via GitHub OIDC — no stored PAT), GitHub release.
|
|
111
|
+
|
|
112
|
+
The workflow is triggered by any tag matching `v*` pushed to the repo. To release:
|
|
109
113
|
|
|
110
114
|
```bash
|
|
111
115
|
# 1. Bump version in package.json
|
|
@@ -117,19 +121,55 @@ $EDITOR src/index.ts
|
|
|
117
121
|
# 3. Regenerate (this updates server.json to match)
|
|
118
122
|
npm run generate:configs
|
|
119
123
|
|
|
120
|
-
# 4. Build and run
|
|
124
|
+
# 4. Build and run any local checks you want before tagging
|
|
121
125
|
npm run build
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
# 5.
|
|
125
|
-
git
|
|
126
|
-
git
|
|
127
|
-
git push origin
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
npm run verify:configs
|
|
127
|
+
|
|
128
|
+
# 5. PR + squash-merge to main (drift CI runs on the PR)
|
|
129
|
+
git checkout -b release/vX.Y.Z
|
|
130
|
+
git add -A && git commit -m "release: vX.Y.Z"
|
|
131
|
+
git push -u origin release/vX.Y.Z
|
|
132
|
+
gh pr create --fill && gh pr merge --squash --delete-branch
|
|
133
|
+
git checkout main && git pull --ff-only
|
|
134
|
+
|
|
135
|
+
# 6. Tag main and push the tag
|
|
136
|
+
git tag -a vX.Y.Z -m "vX.Y.Z"
|
|
137
|
+
git push origin vX.Y.Z
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
That push fires [`.github/workflows/release.yml`](.github/workflows/release.yml), which:
|
|
141
|
+
|
|
142
|
+
1. Verifies the tag matches `package.json#version` (belt and suspenders).
|
|
143
|
+
2. Runs `npm ci && npm run build` (which also runs `generate:configs` via `prebuild`).
|
|
144
|
+
3. Runs `npm run verify:configs` for drift.
|
|
145
|
+
4. `npm publish` using the `NPM_TOKEN` secret.
|
|
146
|
+
5. Installs `mcp-publisher` from its Linux amd64 release bottle.
|
|
147
|
+
6. Authenticates to the Registry via `mcp-publisher login github-oidc` — this uses GitHub Actions' OIDC identity token. **No long-lived PAT is stored anywhere.** The prerequisite is that the authenticating GitHub account's membership in the `mnemoverse` org be **public** (already the case for `izgorodin`).
|
|
148
|
+
7. `mcp-publisher publish` uploads the freshly-generated `server.json`.
|
|
149
|
+
8. Verifies the registry entry via a direct curl against the public API.
|
|
150
|
+
9. Creates a GitHub release with auto-generated notes.
|
|
151
|
+
|
|
152
|
+
If any step fails, the release is aborted — nothing partial gets published. You can re-push the same tag after fixing the issue.
|
|
153
|
+
|
|
154
|
+
### One-time setup for the workflow
|
|
155
|
+
|
|
156
|
+
A single secret must be added at [Settings → Secrets → Actions](https://github.com/mnemoverse/mcp-memory-server/settings/secrets/actions):
|
|
157
|
+
|
|
158
|
+
| Secret | How to get it |
|
|
159
|
+
| ------ | ------------- |
|
|
160
|
+
| `NPM_TOKEN` | [npmjs.com → Settings → Access Tokens → Generate New Token](https://www.npmjs.com/settings/mnemoverse/tokens/granular-access-tokens/new) → **Automation** token, scope: publish to `@mnemoverse/mcp-memory-server`. |
|
|
161
|
+
|
|
162
|
+
Nothing else. GitHub OIDC provides the MCP Registry credential at run time, so we do not store an `MCP_GITHUB_TOKEN` secret at all.
|
|
163
|
+
|
|
164
|
+
### Manual trigger
|
|
165
|
+
|
|
166
|
+
If you need to re-run the pipeline for a tag that was already pushed (e.g. the workflow was added after the tag), use `workflow_dispatch`:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
gh workflow run release.yml -f tag=v0.3.1
|
|
130
170
|
```
|
|
131
171
|
|
|
132
|
-
|
|
172
|
+
Or click **Run workflow** on the Actions tab and enter the tag.
|
|
133
173
|
|
|
134
174
|
## Testing changes locally
|
|
135
175
|
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
5
|
import { z } from "zod";
|
|
6
|
+
// Version is read at runtime from package.json so there is exactly one place
|
|
7
|
+
// to bump on each release. Works both from `dist/` during local dev and from
|
|
8
|
+
// `node_modules/@mnemoverse/mcp-memory-server/dist/` after an npm install.
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const pkg = require("../package.json");
|
|
5
11
|
const API_URL = process.env.MNEMOVERSE_API_URL || "https://core.mnemoverse.com/api/v1";
|
|
6
12
|
const API_KEY = process.env.MNEMOVERSE_API_KEY || "";
|
|
7
13
|
// Hard cap on tool result size — required by Claude Connectors Directory
|
|
@@ -13,6 +19,19 @@ if (!API_KEY) {
|
|
|
13
19
|
"Get your free key at https://console.mnemoverse.com");
|
|
14
20
|
process.exit(1);
|
|
15
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Fetch from the Mnemoverse core API with authentication.
|
|
24
|
+
*
|
|
25
|
+
* Generic so call sites can declare the expected response shape:
|
|
26
|
+
*
|
|
27
|
+
* const r = await apiFetch<{ stored: boolean; atom_id: string }>("/memory/write", { ... });
|
|
28
|
+
*
|
|
29
|
+
* Handles 204 No Content and empty bodies defensively — FastAPI DELETE
|
|
30
|
+
* handlers may switch to 204 in the future even though today they return
|
|
31
|
+
* a JSON body.
|
|
32
|
+
*
|
|
33
|
+
* @throws Error with message `Mnemoverse API error {status}: {body}` on non-2xx.
|
|
34
|
+
*/
|
|
16
35
|
async function apiFetch(path, options = {}) {
|
|
17
36
|
const res = await fetch(`${API_URL}${path}`, {
|
|
18
37
|
...options,
|
|
@@ -26,23 +45,37 @@ async function apiFetch(path, options = {}) {
|
|
|
26
45
|
const text = await res.text();
|
|
27
46
|
throw new Error(`Mnemoverse API error ${res.status}: ${text}`);
|
|
28
47
|
}
|
|
29
|
-
return
|
|
48
|
+
// 204 No Content or empty body — return an empty object cast as T so
|
|
49
|
+
// call sites using optional chaining still work without crashing.
|
|
50
|
+
if (res.status === 204 || res.headers.get("content-length") === "0") {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
return (await res.json());
|
|
30
54
|
}
|
|
31
55
|
/**
|
|
32
56
|
* Truncate a result string to MAX_RESULT_CHARS, appending a notice if truncated.
|
|
33
57
|
* Required by Claude Connectors Directory submission policy.
|
|
58
|
+
*
|
|
59
|
+
* Defensive against splitting UTF-16 surrogate pairs: if the character right
|
|
60
|
+
* before the cut point is a high surrogate (U+D800–U+DBFF), drop it so the
|
|
61
|
+
* result stays well-formed. Otherwise an emoji or non-BMP character at the
|
|
62
|
+
* boundary can produce a lone surrogate and corrupt downstream JSON encoding.
|
|
34
63
|
*/
|
|
35
64
|
function capResult(text) {
|
|
36
65
|
if (text.length <= MAX_RESULT_CHARS)
|
|
37
66
|
return text;
|
|
38
|
-
|
|
67
|
+
let truncated = text.slice(0, MAX_RESULT_CHARS - 200);
|
|
68
|
+
const lastCode = truncated.charCodeAt(truncated.length - 1);
|
|
69
|
+
if (lastCode >= 0xd800 && lastCode <= 0xdbff) {
|
|
70
|
+
truncated = truncated.slice(0, -1);
|
|
71
|
+
}
|
|
39
72
|
return (truncated +
|
|
40
73
|
`\n\n[…truncated to fit 25K token limit. Use a more specific query or smaller top_k to see all results.]`);
|
|
41
74
|
}
|
|
42
75
|
// --- Server setup ---
|
|
43
76
|
const server = new McpServer({
|
|
44
77
|
name: "mnemoverse-memory",
|
|
45
|
-
version:
|
|
78
|
+
version: pkg.version,
|
|
46
79
|
});
|
|
47
80
|
// --- Tool: memory_write ---
|
|
48
81
|
server.registerTool("memory_write", {
|
|
@@ -70,7 +103,7 @@ server.registerTool("memory_write", {
|
|
|
70
103
|
openWorldHint: true,
|
|
71
104
|
},
|
|
72
105
|
}, async ({ content, concepts, domain }) => {
|
|
73
|
-
const
|
|
106
|
+
const r = await apiFetch("/memory/write", {
|
|
74
107
|
method: "POST",
|
|
75
108
|
body: JSON.stringify({
|
|
76
109
|
content,
|
|
@@ -78,13 +111,13 @@ server.registerTool("memory_write", {
|
|
|
78
111
|
domain: domain || "general",
|
|
79
112
|
}),
|
|
80
113
|
});
|
|
81
|
-
const
|
|
82
|
-
if (r
|
|
114
|
+
const importance = (r?.importance ?? 0).toFixed(2);
|
|
115
|
+
if (r?.stored) {
|
|
83
116
|
return {
|
|
84
117
|
content: [
|
|
85
118
|
{
|
|
86
119
|
type: "text",
|
|
87
|
-
text: `Stored (importance: ${
|
|
120
|
+
text: `Stored (importance: ${importance}). ID: ${r.atom_id ?? "unknown"}`,
|
|
88
121
|
},
|
|
89
122
|
],
|
|
90
123
|
};
|
|
@@ -93,7 +126,7 @@ server.registerTool("memory_write", {
|
|
|
93
126
|
content: [
|
|
94
127
|
{
|
|
95
128
|
type: "text",
|
|
96
|
-
text: `Filtered — ${r
|
|
129
|
+
text: `Filtered — ${r?.reason ?? "unknown reason"} (importance: ${importance})`,
|
|
97
130
|
},
|
|
98
131
|
],
|
|
99
132
|
};
|
|
@@ -127,7 +160,7 @@ server.registerTool("memory_read", {
|
|
|
127
160
|
openWorldHint: true,
|
|
128
161
|
},
|
|
129
162
|
}, async ({ query, top_k, domain }) => {
|
|
130
|
-
const
|
|
163
|
+
const r = await apiFetch("/memory/read", {
|
|
131
164
|
method: "POST",
|
|
132
165
|
body: JSON.stringify({
|
|
133
166
|
query,
|
|
@@ -136,19 +169,24 @@ server.registerTool("memory_read", {
|
|
|
136
169
|
include_associations: true,
|
|
137
170
|
}),
|
|
138
171
|
});
|
|
139
|
-
const
|
|
140
|
-
if (
|
|
172
|
+
const items = Array.isArray(r?.items) ? r.items : [];
|
|
173
|
+
if (items.length === 0) {
|
|
141
174
|
return {
|
|
142
175
|
content: [
|
|
143
176
|
{ type: "text", text: "No memories found for this query." },
|
|
144
177
|
],
|
|
145
178
|
};
|
|
146
179
|
}
|
|
147
|
-
const lines =
|
|
148
|
-
(item
|
|
180
|
+
const lines = items.map((item, i) => {
|
|
181
|
+
const relevance = ((item?.relevance ?? 0) * 100).toFixed(0);
|
|
182
|
+
const content = item?.content ?? "(empty)";
|
|
183
|
+
const concepts = Array.isArray(item?.concepts) && item.concepts.length > 0
|
|
149
184
|
? ` (${item.concepts.join(", ")})`
|
|
150
|
-
: ""
|
|
151
|
-
|
|
185
|
+
: "";
|
|
186
|
+
return `${i + 1}. [${relevance}%] ${content}${concepts}`;
|
|
187
|
+
});
|
|
188
|
+
const searchMs = (r?.search_time_ms ?? 0).toFixed(0);
|
|
189
|
+
const text = lines.join("\n\n") + `\n\n(${searchMs}ms)`;
|
|
152
190
|
return {
|
|
153
191
|
content: [
|
|
154
192
|
{
|
|
@@ -175,21 +213,25 @@ server.registerTool("memory_feedback", {
|
|
|
175
213
|
annotations: {
|
|
176
214
|
title: "Rate Memory Helpfulness",
|
|
177
215
|
readOnlyHint: false,
|
|
178
|
-
|
|
216
|
+
// Feedback permanently mutates the memory's valence and importance
|
|
217
|
+
// scores on the backend — per MCP spec, that is a destructive update
|
|
218
|
+
// to the stored state (cf. ToolAnnotations.destructiveHint), even
|
|
219
|
+
// though the caller intends it as quality signal rather than delete.
|
|
220
|
+
destructiveHint: true,
|
|
179
221
|
idempotentHint: false,
|
|
180
222
|
openWorldHint: true,
|
|
181
223
|
},
|
|
182
224
|
}, async ({ atom_ids, outcome }) => {
|
|
183
|
-
const
|
|
225
|
+
const r = await apiFetch("/memory/feedback", {
|
|
184
226
|
method: "POST",
|
|
185
227
|
body: JSON.stringify({ atom_ids, outcome }),
|
|
186
228
|
});
|
|
187
|
-
const
|
|
229
|
+
const count = r?.updated_count ?? 0;
|
|
188
230
|
return {
|
|
189
231
|
content: [
|
|
190
232
|
{
|
|
191
233
|
type: "text",
|
|
192
|
-
text: `Feedback recorded for ${
|
|
234
|
+
text: `Feedback recorded for ${count} memor${count === 1 ? "y" : "ies"}.`,
|
|
193
235
|
},
|
|
194
236
|
],
|
|
195
237
|
};
|
|
@@ -206,13 +248,15 @@ server.registerTool("memory_stats", {
|
|
|
206
248
|
openWorldHint: true,
|
|
207
249
|
},
|
|
208
250
|
}, async () => {
|
|
209
|
-
const
|
|
210
|
-
const
|
|
251
|
+
const r = await apiFetch("/memory/stats");
|
|
252
|
+
const domains = Array.isArray(r?.domains) && r.domains.length > 0
|
|
253
|
+
? r.domains.join(", ")
|
|
254
|
+
: "general";
|
|
211
255
|
const text = [
|
|
212
|
-
`Memories: ${r
|
|
213
|
-
`Associations: ${r
|
|
214
|
-
`Domains: ${
|
|
215
|
-
`Avg quality: valence ${r
|
|
256
|
+
`Memories: ${r?.total_atoms ?? 0} (${r?.episodes ?? 0} episodes, ${r?.prototypes ?? 0} prototypes)`,
|
|
257
|
+
`Associations: ${r?.hebbian_edges ?? 0} Hebbian edges`,
|
|
258
|
+
`Domains: ${domains}`,
|
|
259
|
+
`Avg quality: valence ${(r?.avg_valence ?? 0).toFixed(2)}, importance ${(r?.avg_importance ?? 0).toFixed(2)}`,
|
|
216
260
|
].join("\n");
|
|
217
261
|
return { content: [{ type: "text", text }] };
|
|
218
262
|
});
|
|
@@ -233,13 +277,11 @@ server.registerTool("memory_delete", {
|
|
|
233
277
|
openWorldHint: true,
|
|
234
278
|
},
|
|
235
279
|
}, async ({ atom_id }) => {
|
|
236
|
-
const result = await apiFetch(`/memory/atoms/${encodeURIComponent(atom_id)}`, {
|
|
237
|
-
method: "DELETE",
|
|
238
|
-
});
|
|
239
280
|
// Core API returns { deleted: <count>, atom_id }. count == 0 means
|
|
240
|
-
// the atom didn't exist (or was already removed). count >= 1 means
|
|
241
|
-
|
|
242
|
-
|
|
281
|
+
// the atom didn't exist (or was already removed). count >= 1 means
|
|
282
|
+
// it was deleted.
|
|
283
|
+
const r = await apiFetch(`/memory/atoms/${encodeURIComponent(atom_id)}`, { method: "DELETE" });
|
|
284
|
+
if (!r?.deleted) {
|
|
243
285
|
return {
|
|
244
286
|
content: [
|
|
245
287
|
{
|
|
@@ -278,19 +320,19 @@ server.registerTool("memory_delete_domain", {
|
|
|
278
320
|
idempotentHint: true,
|
|
279
321
|
openWorldHint: true,
|
|
280
322
|
},
|
|
281
|
-
},
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const
|
|
323
|
+
},
|
|
324
|
+
// The `confirm: z.literal(true)` in the input schema is the safety
|
|
325
|
+
// interlock — Zod rejects any call without confirm === true before it
|
|
326
|
+
// reaches this handler, so no runtime re-check is needed here.
|
|
327
|
+
async ({ domain }) => {
|
|
328
|
+
const r = await apiFetch(`/memory/domain/${encodeURIComponent(domain)}`, { method: "DELETE" });
|
|
329
|
+
const count = r?.deleted ?? 0;
|
|
330
|
+
const domainName = r?.domain ?? domain;
|
|
289
331
|
return {
|
|
290
332
|
content: [
|
|
291
333
|
{
|
|
292
334
|
type: "text",
|
|
293
|
-
text: `Deleted ${
|
|
335
|
+
text: `Deleted ${count} ${count === 1 ? "memory" : "memories"} from domain "${domainName}".`,
|
|
294
336
|
},
|
|
295
337
|
],
|
|
296
338
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,OAAO,GACX,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,oCAAoC,CAAC;AACzE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAC;AAErD,yEAAyE;AACzE,wFAAwF;AACxF,mGAAmG;AACnG,MAAM,gBAAgB,GAAG,MAAM,GAAG,CAAC,CAAC;AAEpC,IAAI,CAAC,OAAO,EAAE,CAAC;IACb,OAAO,CAAC,KAAK,CACX,+DAA+D;QAC7D,qDAAqD,CACxD,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,IAAY,EACZ,UAAuB,EAAE;IAEzB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,GAAG,IAAI,EAAE,EAAE;QAC3C,GAAG,OAAO;QACV,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,WAAW,EAAE,OAAO;YACpB,GAAG,CAAE,OAAO,CAAC,OAAkC,IAAI,EAAE,CAAC;SACvD;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/mnemoverse/mcp-memory-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.2",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@mnemoverse/mcp-memory-server",
|
|
14
|
-
"version": "0.3.
|
|
14
|
+
"version": "0.3.2",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { createRequire } from "node:module";
|
|
3
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
6
|
import { z } from "zod";
|
|
6
7
|
|
|
8
|
+
// Version is read at runtime from package.json so there is exactly one place
|
|
9
|
+
// to bump on each release. Works both from `dist/` during local dev and from
|
|
10
|
+
// `node_modules/@mnemoverse/mcp-memory-server/dist/` after an npm install.
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const pkg = require("../package.json") as { version: string };
|
|
13
|
+
|
|
7
14
|
const API_URL =
|
|
8
15
|
process.env.MNEMOVERSE_API_URL || "https://core.mnemoverse.com/api/v1";
|
|
9
16
|
const API_KEY = process.env.MNEMOVERSE_API_KEY || "";
|
|
@@ -21,10 +28,23 @@ if (!API_KEY) {
|
|
|
21
28
|
process.exit(1);
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Fetch from the Mnemoverse core API with authentication.
|
|
33
|
+
*
|
|
34
|
+
* Generic so call sites can declare the expected response shape:
|
|
35
|
+
*
|
|
36
|
+
* const r = await apiFetch<{ stored: boolean; atom_id: string }>("/memory/write", { ... });
|
|
37
|
+
*
|
|
38
|
+
* Handles 204 No Content and empty bodies defensively — FastAPI DELETE
|
|
39
|
+
* handlers may switch to 204 in the future even though today they return
|
|
40
|
+
* a JSON body.
|
|
41
|
+
*
|
|
42
|
+
* @throws Error with message `Mnemoverse API error {status}: {body}` on non-2xx.
|
|
43
|
+
*/
|
|
44
|
+
async function apiFetch<T = unknown>(
|
|
25
45
|
path: string,
|
|
26
46
|
options: RequestInit = {},
|
|
27
|
-
): Promise<
|
|
47
|
+
): Promise<T> {
|
|
28
48
|
const res = await fetch(`${API_URL}${path}`, {
|
|
29
49
|
...options,
|
|
30
50
|
headers: {
|
|
@@ -39,16 +59,31 @@ async function apiFetch(
|
|
|
39
59
|
throw new Error(`Mnemoverse API error ${res.status}: ${text}`);
|
|
40
60
|
}
|
|
41
61
|
|
|
42
|
-
return
|
|
62
|
+
// 204 No Content or empty body — return an empty object cast as T so
|
|
63
|
+
// call sites using optional chaining still work without crashing.
|
|
64
|
+
if (res.status === 204 || res.headers.get("content-length") === "0") {
|
|
65
|
+
return {} as T;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (await res.json()) as T;
|
|
43
69
|
}
|
|
44
70
|
|
|
45
71
|
/**
|
|
46
72
|
* Truncate a result string to MAX_RESULT_CHARS, appending a notice if truncated.
|
|
47
73
|
* Required by Claude Connectors Directory submission policy.
|
|
74
|
+
*
|
|
75
|
+
* Defensive against splitting UTF-16 surrogate pairs: if the character right
|
|
76
|
+
* before the cut point is a high surrogate (U+D800–U+DBFF), drop it so the
|
|
77
|
+
* result stays well-formed. Otherwise an emoji or non-BMP character at the
|
|
78
|
+
* boundary can produce a lone surrogate and corrupt downstream JSON encoding.
|
|
48
79
|
*/
|
|
49
80
|
function capResult(text: string): string {
|
|
50
81
|
if (text.length <= MAX_RESULT_CHARS) return text;
|
|
51
|
-
|
|
82
|
+
let truncated = text.slice(0, MAX_RESULT_CHARS - 200);
|
|
83
|
+
const lastCode = truncated.charCodeAt(truncated.length - 1);
|
|
84
|
+
if (lastCode >= 0xd800 && lastCode <= 0xdbff) {
|
|
85
|
+
truncated = truncated.slice(0, -1);
|
|
86
|
+
}
|
|
52
87
|
return (
|
|
53
88
|
truncated +
|
|
54
89
|
`\n\n[…truncated to fit 25K token limit. Use a more specific query or smaller top_k to see all results.]`
|
|
@@ -59,7 +94,7 @@ function capResult(text: string): string {
|
|
|
59
94
|
|
|
60
95
|
const server = new McpServer({
|
|
61
96
|
name: "mnemoverse-memory",
|
|
62
|
-
version:
|
|
97
|
+
version: pkg.version,
|
|
63
98
|
});
|
|
64
99
|
|
|
65
100
|
// --- Tool: memory_write ---
|
|
@@ -97,7 +132,12 @@ server.registerTool(
|
|
|
97
132
|
},
|
|
98
133
|
},
|
|
99
134
|
async ({ content, concepts, domain }) => {
|
|
100
|
-
const
|
|
135
|
+
const r = await apiFetch<{
|
|
136
|
+
stored?: boolean;
|
|
137
|
+
atom_id?: string | null;
|
|
138
|
+
importance?: number;
|
|
139
|
+
reason?: string;
|
|
140
|
+
}>("/memory/write", {
|
|
101
141
|
method: "POST",
|
|
102
142
|
body: JSON.stringify({
|
|
103
143
|
content,
|
|
@@ -106,19 +146,14 @@ server.registerTool(
|
|
|
106
146
|
}),
|
|
107
147
|
});
|
|
108
148
|
|
|
109
|
-
const
|
|
110
|
-
stored: boolean;
|
|
111
|
-
atom_id: string | null;
|
|
112
|
-
importance: number;
|
|
113
|
-
reason: string;
|
|
114
|
-
};
|
|
149
|
+
const importance = (r?.importance ?? 0).toFixed(2);
|
|
115
150
|
|
|
116
|
-
if (r
|
|
151
|
+
if (r?.stored) {
|
|
117
152
|
return {
|
|
118
153
|
content: [
|
|
119
154
|
{
|
|
120
155
|
type: "text" as const,
|
|
121
|
-
text: `Stored (importance: ${
|
|
156
|
+
text: `Stored (importance: ${importance}). ID: ${r.atom_id ?? "unknown"}`,
|
|
122
157
|
},
|
|
123
158
|
],
|
|
124
159
|
};
|
|
@@ -127,7 +162,7 @@ server.registerTool(
|
|
|
127
162
|
content: [
|
|
128
163
|
{
|
|
129
164
|
type: "text" as const,
|
|
130
|
-
text: `Filtered — ${r
|
|
165
|
+
text: `Filtered — ${r?.reason ?? "unknown reason"} (importance: ${importance})`,
|
|
131
166
|
},
|
|
132
167
|
],
|
|
133
168
|
};
|
|
@@ -168,7 +203,15 @@ server.registerTool(
|
|
|
168
203
|
},
|
|
169
204
|
},
|
|
170
205
|
async ({ query, top_k, domain }) => {
|
|
171
|
-
const
|
|
206
|
+
const r = await apiFetch<{
|
|
207
|
+
items?: Array<{
|
|
208
|
+
content?: string;
|
|
209
|
+
relevance?: number;
|
|
210
|
+
concepts?: string[];
|
|
211
|
+
domain?: string;
|
|
212
|
+
}>;
|
|
213
|
+
search_time_ms?: number;
|
|
214
|
+
}>("/memory/read", {
|
|
172
215
|
method: "POST",
|
|
173
216
|
body: JSON.stringify({
|
|
174
217
|
query,
|
|
@@ -178,17 +221,9 @@ server.registerTool(
|
|
|
178
221
|
}),
|
|
179
222
|
});
|
|
180
223
|
|
|
181
|
-
const
|
|
182
|
-
items: Array<{
|
|
183
|
-
content: string;
|
|
184
|
-
relevance: number;
|
|
185
|
-
concepts: string[];
|
|
186
|
-
domain: string;
|
|
187
|
-
}>;
|
|
188
|
-
search_time_ms: number;
|
|
189
|
-
};
|
|
224
|
+
const items = Array.isArray(r?.items) ? r.items : [];
|
|
190
225
|
|
|
191
|
-
if (
|
|
226
|
+
if (items.length === 0) {
|
|
192
227
|
return {
|
|
193
228
|
content: [
|
|
194
229
|
{ type: "text" as const, text: "No memories found for this query." },
|
|
@@ -196,15 +231,17 @@ server.registerTool(
|
|
|
196
231
|
};
|
|
197
232
|
}
|
|
198
233
|
|
|
199
|
-
const lines =
|
|
200
|
-
(item
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
234
|
+
const lines = items.map((item, i) => {
|
|
235
|
+
const relevance = ((item?.relevance ?? 0) * 100).toFixed(0);
|
|
236
|
+
const content = item?.content ?? "(empty)";
|
|
237
|
+
const concepts = Array.isArray(item?.concepts) && item.concepts.length > 0
|
|
238
|
+
? ` (${item.concepts.join(", ")})`
|
|
239
|
+
: "";
|
|
240
|
+
return `${i + 1}. [${relevance}%] ${content}${concepts}`;
|
|
241
|
+
});
|
|
206
242
|
|
|
207
|
-
const
|
|
243
|
+
const searchMs = (r?.search_time_ms ?? 0).toFixed(0);
|
|
244
|
+
const text = lines.join("\n\n") + `\n\n(${searchMs}ms)`;
|
|
208
245
|
|
|
209
246
|
return {
|
|
210
247
|
content: [
|
|
@@ -238,24 +275,28 @@ server.registerTool(
|
|
|
238
275
|
annotations: {
|
|
239
276
|
title: "Rate Memory Helpfulness",
|
|
240
277
|
readOnlyHint: false,
|
|
241
|
-
|
|
278
|
+
// Feedback permanently mutates the memory's valence and importance
|
|
279
|
+
// scores on the backend — per MCP spec, that is a destructive update
|
|
280
|
+
// to the stored state (cf. ToolAnnotations.destructiveHint), even
|
|
281
|
+
// though the caller intends it as quality signal rather than delete.
|
|
282
|
+
destructiveHint: true,
|
|
242
283
|
idempotentHint: false,
|
|
243
284
|
openWorldHint: true,
|
|
244
285
|
},
|
|
245
286
|
},
|
|
246
287
|
async ({ atom_ids, outcome }) => {
|
|
247
|
-
const
|
|
288
|
+
const r = await apiFetch<{ updated_count?: number }>("/memory/feedback", {
|
|
248
289
|
method: "POST",
|
|
249
290
|
body: JSON.stringify({ atom_ids, outcome }),
|
|
250
291
|
});
|
|
251
292
|
|
|
252
|
-
const
|
|
293
|
+
const count = r?.updated_count ?? 0;
|
|
253
294
|
|
|
254
295
|
return {
|
|
255
296
|
content: [
|
|
256
297
|
{
|
|
257
298
|
type: "text" as const,
|
|
258
|
-
text: `Feedback recorded for ${
|
|
299
|
+
text: `Feedback recorded for ${count} memor${count === 1 ? "y" : "ies"}.`,
|
|
259
300
|
},
|
|
260
301
|
],
|
|
261
302
|
};
|
|
@@ -279,23 +320,25 @@ server.registerTool(
|
|
|
279
320
|
},
|
|
280
321
|
},
|
|
281
322
|
async () => {
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
323
|
+
const r = await apiFetch<{
|
|
324
|
+
total_atoms?: number;
|
|
325
|
+
episodes?: number;
|
|
326
|
+
prototypes?: number;
|
|
327
|
+
hebbian_edges?: number;
|
|
328
|
+
domains?: string[];
|
|
329
|
+
avg_valence?: number;
|
|
330
|
+
avg_importance?: number;
|
|
331
|
+
}>("/memory/stats");
|
|
332
|
+
|
|
333
|
+
const domains = Array.isArray(r?.domains) && r.domains.length > 0
|
|
334
|
+
? r.domains.join(", ")
|
|
335
|
+
: "general";
|
|
293
336
|
|
|
294
337
|
const text = [
|
|
295
|
-
`Memories: ${r
|
|
296
|
-
`Associations: ${r
|
|
297
|
-
`Domains: ${
|
|
298
|
-
`Avg quality: valence ${r
|
|
338
|
+
`Memories: ${r?.total_atoms ?? 0} (${r?.episodes ?? 0} episodes, ${r?.prototypes ?? 0} prototypes)`,
|
|
339
|
+
`Associations: ${r?.hebbian_edges ?? 0} Hebbian edges`,
|
|
340
|
+
`Domains: ${domains}`,
|
|
341
|
+
`Avg quality: valence ${(r?.avg_valence ?? 0).toFixed(2)}, importance ${(r?.avg_importance ?? 0).toFixed(2)}`,
|
|
299
342
|
].join("\n");
|
|
300
343
|
|
|
301
344
|
return { content: [{ type: "text" as const, text }] };
|
|
@@ -326,15 +369,15 @@ server.registerTool(
|
|
|
326
369
|
},
|
|
327
370
|
},
|
|
328
371
|
async ({ atom_id }) => {
|
|
329
|
-
const result = await apiFetch(`/memory/atoms/${encodeURIComponent(atom_id)}`, {
|
|
330
|
-
method: "DELETE",
|
|
331
|
-
});
|
|
332
|
-
|
|
333
372
|
// Core API returns { deleted: <count>, atom_id }. count == 0 means
|
|
334
|
-
// the atom didn't exist (or was already removed). count >= 1 means
|
|
335
|
-
|
|
373
|
+
// the atom didn't exist (or was already removed). count >= 1 means
|
|
374
|
+
// it was deleted.
|
|
375
|
+
const r = await apiFetch<{ deleted?: number; atom_id?: string }>(
|
|
376
|
+
`/memory/atoms/${encodeURIComponent(atom_id)}`,
|
|
377
|
+
{ method: "DELETE" },
|
|
378
|
+
);
|
|
336
379
|
|
|
337
|
-
if (!r
|
|
380
|
+
if (!r?.deleted) {
|
|
338
381
|
return {
|
|
339
382
|
content: [
|
|
340
383
|
{
|
|
@@ -385,24 +428,23 @@ server.registerTool(
|
|
|
385
428
|
openWorldHint: true,
|
|
386
429
|
},
|
|
387
430
|
},
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
method: "DELETE",
|
|
397
|
-
});
|
|
431
|
+
// The `confirm: z.literal(true)` in the input schema is the safety
|
|
432
|
+
// interlock — Zod rejects any call without confirm === true before it
|
|
433
|
+
// reaches this handler, so no runtime re-check is needed here.
|
|
434
|
+
async ({ domain }) => {
|
|
435
|
+
const r = await apiFetch<{ deleted?: number; domain?: string }>(
|
|
436
|
+
`/memory/domain/${encodeURIComponent(domain)}`,
|
|
437
|
+
{ method: "DELETE" },
|
|
438
|
+
);
|
|
398
439
|
|
|
399
|
-
const
|
|
440
|
+
const count = r?.deleted ?? 0;
|
|
441
|
+
const domainName = r?.domain ?? domain;
|
|
400
442
|
|
|
401
443
|
return {
|
|
402
444
|
content: [
|
|
403
445
|
{
|
|
404
446
|
type: "text" as const,
|
|
405
|
-
text: `Deleted ${
|
|
447
|
+
text: `Deleted ${count} ${count === 1 ? "memory" : "memories"} from domain "${domainName}".`,
|
|
406
448
|
},
|
|
407
449
|
],
|
|
408
450
|
};
|