manel740-qwen-memory 3.0.0 → 4.0.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/manel740-qwen-memory-4.0.0.tgz +0 -0
- package/package.json +2 -5
- package/server.js +150 -0
- package/bin/manel740-qwen-memory.js +0 -113
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "manel740-qwen-memory",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -18,10 +18,7 @@
|
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@modelcontextprotocol/inspector": "^2.4.0"
|
|
20
20
|
},
|
|
21
|
-
"files": [
|
|
22
|
-
"bin/manel740-qwen-memory.js"
|
|
23
|
-
],
|
|
24
21
|
"bin": {
|
|
25
|
-
"manel740-qwen-memory": "
|
|
22
|
+
"manel740-qwen-memory": "server.js"
|
|
26
23
|
}
|
|
27
24
|
}
|
package/server.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
McpServer
|
|
8
|
+
} = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
StdioServerTransport
|
|
12
|
+
} = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
13
|
+
|
|
14
|
+
const { z } = require("zod");
|
|
15
|
+
|
|
16
|
+
const MEMORY_DIR = path.resolve(
|
|
17
|
+
process.env.QWEN_MEMORY_DIR ||
|
|
18
|
+
path.join(process.env.USERPROFILE, "Desktop", "Qwen-Memory")
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
function ensureMemoryDir() {
|
|
22
|
+
fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function safePath(filename) {
|
|
26
|
+
if (!filename.toLowerCase().endsWith(".md")) {
|
|
27
|
+
throw new Error("Solo se permiten archivos .md");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const resolved = path.resolve(MEMORY_DIR, filename);
|
|
31
|
+
|
|
32
|
+
if (
|
|
33
|
+
resolved !== MEMORY_DIR &&
|
|
34
|
+
!resolved.startsWith(MEMORY_DIR + path.sep)
|
|
35
|
+
) {
|
|
36
|
+
throw new Error("Acceso fuera de Qwen-Memory bloqueado");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return resolved;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getMarkdownFiles() {
|
|
43
|
+
ensureMemoryDir();
|
|
44
|
+
|
|
45
|
+
return fs
|
|
46
|
+
.readdirSync(MEMORY_DIR, { withFileTypes: true })
|
|
47
|
+
.filter(entry => entry.isFile())
|
|
48
|
+
.map(entry => entry.name)
|
|
49
|
+
.filter(name => name.toLowerCase().endsWith(".md"))
|
|
50
|
+
.sort((a, b) => a.localeCompare(b));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const server = new McpServer({
|
|
54
|
+
name: "qwen-memory",
|
|
55
|
+
version: "4.0.0"
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
server.registerTool(
|
|
59
|
+
"read_memory",
|
|
60
|
+
{
|
|
61
|
+
title: "Read Qwen Memory",
|
|
62
|
+
description:
|
|
63
|
+
"Lee los archivos Markdown de Qwen-Memory. " +
|
|
64
|
+
"Sin nombre de archivo, devuelve toda la memoria disponible. " +
|
|
65
|
+
"Con filename, devuelve únicamente ese archivo. " +
|
|
66
|
+
"Esta herramienta es exclusivamente de lectura y nunca modifica archivos.",
|
|
67
|
+
inputSchema: {
|
|
68
|
+
filename: z
|
|
69
|
+
.string()
|
|
70
|
+
.optional()
|
|
71
|
+
.describe("Nombre opcional de un archivo .md, por ejemplo PORTING_STATUS.md")
|
|
72
|
+
},
|
|
73
|
+
annotations: {
|
|
74
|
+
readOnlyHint: true,
|
|
75
|
+
destructiveHint: false,
|
|
76
|
+
idempotentHint: true,
|
|
77
|
+
openWorldHint: false
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
async ({ filename }) => {
|
|
81
|
+
ensureMemoryDir();
|
|
82
|
+
|
|
83
|
+
if (filename) {
|
|
84
|
+
const filePath = safePath(filename);
|
|
85
|
+
|
|
86
|
+
if (!fs.existsSync(filePath)) {
|
|
87
|
+
throw new Error(`No existe: ${filename}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
content: [
|
|
92
|
+
{
|
|
93
|
+
type: "text",
|
|
94
|
+
text:
|
|
95
|
+
`===== ${filename} =====\n\n` +
|
|
96
|
+
fs.readFileSync(filePath, "utf8")
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const files = getMarkdownFiles();
|
|
103
|
+
|
|
104
|
+
if (files.length === 0) {
|
|
105
|
+
return {
|
|
106
|
+
content: [
|
|
107
|
+
{
|
|
108
|
+
type: "text",
|
|
109
|
+
text: "No hay archivos .md en Qwen-Memory."
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const sections = [];
|
|
116
|
+
|
|
117
|
+
for (const file of files) {
|
|
118
|
+
const filePath = safePath(file);
|
|
119
|
+
|
|
120
|
+
sections.push(
|
|
121
|
+
`===== ${file} =====\n\n` +
|
|
122
|
+
fs.readFileSync(filePath, "utf8")
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
content: [
|
|
128
|
+
{
|
|
129
|
+
type: "text",
|
|
130
|
+
text: sections.join("\n\n")
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
async function main() {
|
|
138
|
+
ensureMemoryDir();
|
|
139
|
+
|
|
140
|
+
const transport = new StdioServerTransport();
|
|
141
|
+
|
|
142
|
+
await server.connect(transport);
|
|
143
|
+
|
|
144
|
+
await new Promise(() => {});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
main().catch(error => {
|
|
148
|
+
console.error("QWEN MEMORY MCP ERROR:", error);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
});
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const fs = require("fs");
|
|
3
|
-
const path = require("path");
|
|
4
|
-
const { McpServer } = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
5
|
-
const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6
|
-
const { z } = require("zod");
|
|
7
|
-
|
|
8
|
-
const MEMORY_DIR = path.resolve(
|
|
9
|
-
process.env.QWEN_MEMORY_DIR ||
|
|
10
|
-
path.join(process.env.USERPROFILE, "Desktop", "Qwen-Memory")
|
|
11
|
-
);
|
|
12
|
-
|
|
13
|
-
function safePath(filename) {
|
|
14
|
-
if (!filename || !filename.toLowerCase().endsWith(".md")) {
|
|
15
|
-
throw new Error("Solo se permiten archivos .md");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const resolved = path.resolve(MEMORY_DIR, filename);
|
|
19
|
-
|
|
20
|
-
if (!resolved.startsWith(MEMORY_DIR + path.sep)) {
|
|
21
|
-
throw new Error("Acceso fuera de Qwen-Memory bloqueado");
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return resolved;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const server = new McpServer({
|
|
28
|
-
name: "qwen-memory",
|
|
29
|
-
version: "1.0.0"
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
server.registerTool(
|
|
33
|
-
"list_md_files",
|
|
34
|
-
{
|
|
35
|
-
description: "Lista todos los archivos .md disponibles en Qwen-Memory.",
|
|
36
|
-
inputSchema: {}
|
|
37
|
-
},
|
|
38
|
-
async () => {
|
|
39
|
-
fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
|
40
|
-
|
|
41
|
-
const files = fs.readdirSync(MEMORY_DIR)
|
|
42
|
-
.filter(file => file.toLowerCase().endsWith(".md"));
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
content: [{
|
|
46
|
-
type: "text",
|
|
47
|
-
text: files.length ? files.join("\n") : "No hay archivos .md."
|
|
48
|
-
}]
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
server.registerTool(
|
|
54
|
-
"read_md_file",
|
|
55
|
-
{
|
|
56
|
-
description: "Lee un archivo Markdown de Qwen-Memory.",
|
|
57
|
-
inputSchema: {
|
|
58
|
-
filename: z.string()
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
async ({ filename }) => {
|
|
62
|
-
const filePath = safePath(filename);
|
|
63
|
-
|
|
64
|
-
if (!fs.existsSync(filePath)) {
|
|
65
|
-
throw new Error(`No existe: ${filename}`);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
content: [{
|
|
70
|
-
type: "text",
|
|
71
|
-
text: fs.readFileSync(filePath, "utf8")
|
|
72
|
-
}]
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
server.registerTool(
|
|
78
|
-
"write_md_file",
|
|
79
|
-
{
|
|
80
|
-
description: "Actualiza un archivo Markdown de Qwen-Memory.",
|
|
81
|
-
inputSchema: {
|
|
82
|
-
filename: z.string(),
|
|
83
|
-
content: z.string()
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
async ({ filename, content }) => {
|
|
87
|
-
const filePath = safePath(filename);
|
|
88
|
-
|
|
89
|
-
fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
|
90
|
-
fs.writeFileSync(filePath, content, "utf8");
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
content: [{
|
|
94
|
-
type: "text",
|
|
95
|
-
text: `Actualizado correctamente: ${filename}`
|
|
96
|
-
}]
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
async function main() {
|
|
102
|
-
fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
|
103
|
-
|
|
104
|
-
const transport = new StdioServerTransport();
|
|
105
|
-
await server.connect(transport);
|
|
106
|
-
|
|
107
|
-
await new Promise(() => {});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
main().catch(error => {
|
|
111
|
-
console.error("MCP ERROR:", error);
|
|
112
|
-
process.exit(1);
|
|
113
|
-
});
|