manel740-qwen-memory 3.0.0 → 3.0.1
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/bin/qwen-memory.js +113 -0
- package/manel740-qwen-memory-3.0.1.tgz +0 -0
- package/package.json +2 -5
- package/server.js +107 -0
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
});
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "manel740-qwen-memory",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
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,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
McpServer,
|
|
8
|
+
ResourceTemplate
|
|
9
|
+
} = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
StdioServerTransport
|
|
13
|
+
} = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
14
|
+
|
|
15
|
+
const MEMORY_DIR = path.resolve(
|
|
16
|
+
process.env.QWEN_MEMORY_DIR ||
|
|
17
|
+
path.join(process.env.USERPROFILE, "Desktop", "Qwen-Memory")
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
function ensureMemoryDir() {
|
|
21
|
+
fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function safePath(filename) {
|
|
25
|
+
if (!filename || !filename.toLowerCase().endsWith(".md")) {
|
|
26
|
+
throw new Error("Solo se permiten archivos .md");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const resolved = path.resolve(MEMORY_DIR, filename);
|
|
30
|
+
|
|
31
|
+
if (
|
|
32
|
+
resolved !== MEMORY_DIR &&
|
|
33
|
+
!resolved.startsWith(MEMORY_DIR + path.sep)
|
|
34
|
+
) {
|
|
35
|
+
throw new Error("Acceso fuera de Qwen-Memory bloqueado");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return resolved;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const server = new McpServer({
|
|
42
|
+
name: "qwen-memory",
|
|
43
|
+
version: "3.0.0"
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
server.registerResource(
|
|
47
|
+
"markdown-memory",
|
|
48
|
+
new ResourceTemplate("qwen-memory:///{filename}", {
|
|
49
|
+
list: async () => {
|
|
50
|
+
ensureMemoryDir();
|
|
51
|
+
|
|
52
|
+
const files = fs
|
|
53
|
+
.readdirSync(MEMORY_DIR, { withFileTypes: true })
|
|
54
|
+
.filter(entry => entry.isFile())
|
|
55
|
+
.map(entry => entry.name)
|
|
56
|
+
.filter(file => file.toLowerCase().endsWith(".md"))
|
|
57
|
+
.sort((a, b) => a.localeCompare(b));
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
resources: files.map(file => ({
|
|
61
|
+
uri: `qwen-memory:///${file}`,
|
|
62
|
+
name: file,
|
|
63
|
+
description: `Archivo Markdown de memoria: ${file}`,
|
|
64
|
+
mimeType: "text/markdown"
|
|
65
|
+
}))
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}),
|
|
69
|
+
{
|
|
70
|
+
title: "Qwen Memory Markdown",
|
|
71
|
+
description: "Archivos Markdown de memoria de Qwen. Solo lectura.",
|
|
72
|
+
mimeType: "text/markdown"
|
|
73
|
+
},
|
|
74
|
+
async (uri, variables) => {
|
|
75
|
+
const filename = variables.filename;
|
|
76
|
+
const filePath = safePath(filename);
|
|
77
|
+
|
|
78
|
+
if (!fs.existsSync(filePath)) {
|
|
79
|
+
throw new Error(`No existe: ${filename}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
contents: [
|
|
84
|
+
{
|
|
85
|
+
uri: uri.href,
|
|
86
|
+
mimeType: "text/markdown",
|
|
87
|
+
text: fs.readFileSync(filePath, "utf8")
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
async function main() {
|
|
95
|
+
ensureMemoryDir();
|
|
96
|
+
|
|
97
|
+
const transport = new StdioServerTransport();
|
|
98
|
+
|
|
99
|
+
await server.connect(transport);
|
|
100
|
+
|
|
101
|
+
await new Promise(() => {});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
main().catch(error => {
|
|
105
|
+
console.error("QWEN MEMORY MCP ERROR:", error);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
});
|