@memlink/cli 1.0.3 → 1.0.5
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/dist/cli/index.js +238 -19
- package/dist/server/index.js +175 -2
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -45902,24 +45902,46 @@ Memlink is a self-hosted MCP server that gives you persistent, organized memory
|
|
|
45902
45902
|
## Connection
|
|
45903
45903
|
|
|
45904
45904
|
The MCP server runs at:
|
|
45905
|
+
|
|
45906
|
+
**Streamable HTTP** (modern clients):
|
|
45905
45907
|
\`\`\`
|
|
45906
45908
|
http://localhost:4444/mcp?id=YOUR_MEMORY_ID
|
|
45907
45909
|
\`\`\`
|
|
45908
45910
|
|
|
45911
|
+
**SSE** (legacy clients like OpenCode):
|
|
45912
|
+
\`\`\`
|
|
45913
|
+
http://localhost:4444/sse?id=YOUR_MEMORY_ID
|
|
45914
|
+
\`\`\`
|
|
45915
|
+
|
|
45909
45916
|
The memory ID is a 12-character alphanumeric string assigned when you create a memory via \`memlink init <name>\`.
|
|
45910
45917
|
|
|
45911
|
-
###
|
|
45918
|
+
### Streamable HTTP config
|
|
45912
45919
|
|
|
45913
45920
|
\`\`\`json
|
|
45914
45921
|
{
|
|
45915
45922
|
"mcpServers": {
|
|
45916
45923
|
"memlink": {
|
|
45924
|
+
"type": "http",
|
|
45917
45925
|
"url": "http://localhost:4444/mcp?id=YOUR_MEMORY_ID"
|
|
45918
45926
|
}
|
|
45919
45927
|
}
|
|
45920
45928
|
}
|
|
45921
45929
|
\`\`\`
|
|
45922
45930
|
|
|
45931
|
+
### SSE config (OpenCode)
|
|
45932
|
+
|
|
45933
|
+
\`\`\`json
|
|
45934
|
+
{
|
|
45935
|
+
"mcpServers": {
|
|
45936
|
+
"memlink": {
|
|
45937
|
+
"type": "remote",
|
|
45938
|
+
"enabled": true,
|
|
45939
|
+
"url": "http://localhost:4444/sse?id=YOUR_MEMORY_ID"
|
|
45940
|
+
}
|
|
45941
|
+
}
|
|
45942
|
+
}
|
|
45943
|
+
\`\`\`
|
|
45944
|
+
|
|
45923
45945
|
## Session Workflow
|
|
45924
45946
|
|
|
45925
45947
|
1. **Start of session** — Always call \`memory_read\` to load existing context
|
|
@@ -46002,7 +46024,7 @@ function nanoid(size = 21) {
|
|
|
46002
46024
|
}
|
|
46003
46025
|
|
|
46004
46026
|
// src/core/types.ts
|
|
46005
|
-
var MEMLINK_VERSION = "1.0.
|
|
46027
|
+
var MEMLINK_VERSION = "1.0.5";
|
|
46006
46028
|
var DEFAULT_PORT = 4444;
|
|
46007
46029
|
var DEFAULT_HOST = "localhost";
|
|
46008
46030
|
var CONFIG_DIR = ".memlink";
|
|
@@ -64883,6 +64905,134 @@ class StreamableHTTPServerTransport {
|
|
|
64883
64905
|
}
|
|
64884
64906
|
}
|
|
64885
64907
|
|
|
64908
|
+
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/sse.js
|
|
64909
|
+
import { randomUUID } from "node:crypto";
|
|
64910
|
+
import { TLSSocket } from "node:tls";
|
|
64911
|
+
var import_raw_body = __toESM(require_raw_body(), 1);
|
|
64912
|
+
var import_content_type = __toESM(require_content_type(), 1);
|
|
64913
|
+
import { URL as URL2 } from "node:url";
|
|
64914
|
+
var MAXIMUM_MESSAGE_SIZE = "4mb";
|
|
64915
|
+
|
|
64916
|
+
class SSEServerTransport {
|
|
64917
|
+
constructor(_endpoint, res, options) {
|
|
64918
|
+
this._endpoint = _endpoint;
|
|
64919
|
+
this.res = res;
|
|
64920
|
+
this._sessionId = randomUUID();
|
|
64921
|
+
this._options = options || { enableDnsRebindingProtection: false };
|
|
64922
|
+
}
|
|
64923
|
+
validateRequestHeaders(req) {
|
|
64924
|
+
if (!this._options.enableDnsRebindingProtection) {
|
|
64925
|
+
return;
|
|
64926
|
+
}
|
|
64927
|
+
if (this._options.allowedHosts && this._options.allowedHosts.length > 0) {
|
|
64928
|
+
const hostHeader = req.headers.host;
|
|
64929
|
+
if (!hostHeader || !this._options.allowedHosts.includes(hostHeader)) {
|
|
64930
|
+
return `Invalid Host header: ${hostHeader}`;
|
|
64931
|
+
}
|
|
64932
|
+
}
|
|
64933
|
+
if (this._options.allowedOrigins && this._options.allowedOrigins.length > 0) {
|
|
64934
|
+
const originHeader = req.headers.origin;
|
|
64935
|
+
if (originHeader && !this._options.allowedOrigins.includes(originHeader)) {
|
|
64936
|
+
return `Invalid Origin header: ${originHeader}`;
|
|
64937
|
+
}
|
|
64938
|
+
}
|
|
64939
|
+
return;
|
|
64940
|
+
}
|
|
64941
|
+
async start() {
|
|
64942
|
+
if (this._sseResponse) {
|
|
64943
|
+
throw new Error("SSEServerTransport already started! If using Server class, note that connect() calls start() automatically.");
|
|
64944
|
+
}
|
|
64945
|
+
this.res.writeHead(200, {
|
|
64946
|
+
"Content-Type": "text/event-stream",
|
|
64947
|
+
"Cache-Control": "no-cache, no-transform",
|
|
64948
|
+
Connection: "keep-alive"
|
|
64949
|
+
});
|
|
64950
|
+
const dummyBase = "http://localhost";
|
|
64951
|
+
const endpointUrl = new URL2(this._endpoint, dummyBase);
|
|
64952
|
+
endpointUrl.searchParams.set("sessionId", this._sessionId);
|
|
64953
|
+
const relativeUrlWithSession = endpointUrl.pathname + endpointUrl.search + endpointUrl.hash;
|
|
64954
|
+
this.res.write(`event: endpoint
|
|
64955
|
+
data: ${relativeUrlWithSession}
|
|
64956
|
+
|
|
64957
|
+
`);
|
|
64958
|
+
this._sseResponse = this.res;
|
|
64959
|
+
this.res.on("close", () => {
|
|
64960
|
+
this._sseResponse = undefined;
|
|
64961
|
+
this.onclose?.();
|
|
64962
|
+
});
|
|
64963
|
+
}
|
|
64964
|
+
async handlePostMessage(req, res, parsedBody) {
|
|
64965
|
+
if (!this._sseResponse) {
|
|
64966
|
+
const message = "SSE connection not established";
|
|
64967
|
+
res.writeHead(500).end(message);
|
|
64968
|
+
throw new Error(message);
|
|
64969
|
+
}
|
|
64970
|
+
const validationError = this.validateRequestHeaders(req);
|
|
64971
|
+
if (validationError) {
|
|
64972
|
+
res.writeHead(403).end(validationError);
|
|
64973
|
+
this.onerror?.(new Error(validationError));
|
|
64974
|
+
return;
|
|
64975
|
+
}
|
|
64976
|
+
const authInfo = req.auth;
|
|
64977
|
+
const host = req.headers.host;
|
|
64978
|
+
const protocol = req.socket instanceof TLSSocket ? "https" : "http";
|
|
64979
|
+
const fullUrl = host && req.url ? new URL2(req.url, `${protocol}://${host}`) : undefined;
|
|
64980
|
+
const requestInfo = {
|
|
64981
|
+
headers: req.headers,
|
|
64982
|
+
url: fullUrl
|
|
64983
|
+
};
|
|
64984
|
+
let body;
|
|
64985
|
+
try {
|
|
64986
|
+
const ct = import_content_type.default.parse(req.headers["content-type"] ?? "");
|
|
64987
|
+
if (ct.type !== "application/json") {
|
|
64988
|
+
throw new Error(`Unsupported content-type: ${ct.type}`);
|
|
64989
|
+
}
|
|
64990
|
+
body = parsedBody ?? await import_raw_body.default(req, {
|
|
64991
|
+
limit: MAXIMUM_MESSAGE_SIZE,
|
|
64992
|
+
encoding: ct.parameters.charset ?? "utf-8"
|
|
64993
|
+
});
|
|
64994
|
+
} catch (error2) {
|
|
64995
|
+
res.writeHead(400).end(String(error2));
|
|
64996
|
+
this.onerror?.(error2);
|
|
64997
|
+
return;
|
|
64998
|
+
}
|
|
64999
|
+
try {
|
|
65000
|
+
await this.handleMessage(typeof body === "string" ? JSON.parse(body) : body, { requestInfo, authInfo });
|
|
65001
|
+
} catch {
|
|
65002
|
+
res.writeHead(400).end(`Invalid message: ${body}`);
|
|
65003
|
+
return;
|
|
65004
|
+
}
|
|
65005
|
+
res.writeHead(202).end("Accepted");
|
|
65006
|
+
}
|
|
65007
|
+
async handleMessage(message, extra) {
|
|
65008
|
+
let parsedMessage;
|
|
65009
|
+
try {
|
|
65010
|
+
parsedMessage = JSONRPCMessageSchema.parse(message);
|
|
65011
|
+
} catch (error2) {
|
|
65012
|
+
this.onerror?.(error2);
|
|
65013
|
+
throw error2;
|
|
65014
|
+
}
|
|
65015
|
+
this.onmessage?.(parsedMessage, extra);
|
|
65016
|
+
}
|
|
65017
|
+
async close() {
|
|
65018
|
+
this._sseResponse?.end();
|
|
65019
|
+
this._sseResponse = undefined;
|
|
65020
|
+
this.onclose?.();
|
|
65021
|
+
}
|
|
65022
|
+
async send(message) {
|
|
65023
|
+
if (!this._sseResponse) {
|
|
65024
|
+
throw new Error("Not connected");
|
|
65025
|
+
}
|
|
65026
|
+
this._sseResponse.write(`event: message
|
|
65027
|
+
data: ${JSON.stringify(message)}
|
|
65028
|
+
|
|
65029
|
+
`);
|
|
65030
|
+
}
|
|
65031
|
+
get sessionId() {
|
|
65032
|
+
return this._sessionId;
|
|
65033
|
+
}
|
|
65034
|
+
}
|
|
65035
|
+
|
|
64886
65036
|
// node_modules/zod/v4/classic/external.js
|
|
64887
65037
|
var exports_external = {};
|
|
64888
65038
|
__export(exports_external, {
|
|
@@ -79161,7 +79311,7 @@ function date7(params) {
|
|
|
79161
79311
|
config2(en_default4());
|
|
79162
79312
|
// src/server/index.ts
|
|
79163
79313
|
var loggingEnabled = false;
|
|
79164
|
-
function logRequest(
|
|
79314
|
+
function logRequest(_req, memoryName, method, _params) {
|
|
79165
79315
|
if (!loggingEnabled)
|
|
79166
79316
|
return;
|
|
79167
79317
|
const timestamp = new Date().toISOString();
|
|
@@ -79587,6 +79737,7 @@ Deleted: ${result.deleted}`
|
|
|
79587
79737
|
});
|
|
79588
79738
|
return server;
|
|
79589
79739
|
}
|
|
79740
|
+
var sseSessions = new Map;
|
|
79590
79741
|
function createApp() {
|
|
79591
79742
|
const app = import_express.default();
|
|
79592
79743
|
app.use(import_express.default.json({ limit: "10mb" }));
|
|
@@ -79624,6 +79775,50 @@ function createApp() {
|
|
|
79624
79775
|
content: AGENT_SYSTEM_PROMPT(name)
|
|
79625
79776
|
});
|
|
79626
79777
|
});
|
|
79778
|
+
app.get("/sse", async (req, res) => {
|
|
79779
|
+
const memId = req.query.id || req.query.mem_id;
|
|
79780
|
+
if (!memId) {
|
|
79781
|
+
res.status(401).json({ error: "Missing memory ID. Use ?id=<memory_id>" });
|
|
79782
|
+
return;
|
|
79783
|
+
}
|
|
79784
|
+
const memoryInfo = getMemoryById(memId);
|
|
79785
|
+
if (!memoryInfo) {
|
|
79786
|
+
res.status(403).json({ error: "Invalid memory ID." });
|
|
79787
|
+
return;
|
|
79788
|
+
}
|
|
79789
|
+
const memoryId = memoryInfo.memoryId;
|
|
79790
|
+
const memoryName = memoryInfo.memoryName;
|
|
79791
|
+
updateUniversalMemoryLastSeen(memoryId);
|
|
79792
|
+
const mcpServer = buildMcpServer(memoryId, memoryName);
|
|
79793
|
+
const transport = new SSEServerTransport("/messages", res);
|
|
79794
|
+
sseSessions.set(transport.sessionId, { transport, memoryId, mcpServer });
|
|
79795
|
+
res.on("close", () => {
|
|
79796
|
+
sseSessions.delete(transport.sessionId);
|
|
79797
|
+
mcpServer.close();
|
|
79798
|
+
});
|
|
79799
|
+
try {
|
|
79800
|
+
await mcpServer.connect(transport);
|
|
79801
|
+
await transport.start();
|
|
79802
|
+
} catch {
|
|
79803
|
+
sseSessions.delete(transport.sessionId);
|
|
79804
|
+
mcpServer.close();
|
|
79805
|
+
}
|
|
79806
|
+
});
|
|
79807
|
+
app.post("/messages", async (req, res) => {
|
|
79808
|
+
const sessionId = req.query.sessionId;
|
|
79809
|
+
if (!sessionId || !sseSessions.has(sessionId)) {
|
|
79810
|
+
res.status(404).json({ error: "SSE session not found" });
|
|
79811
|
+
return;
|
|
79812
|
+
}
|
|
79813
|
+
const { transport } = sseSessions.get(sessionId);
|
|
79814
|
+
try {
|
|
79815
|
+
await transport.handlePostMessage(req, res, req.body);
|
|
79816
|
+
} catch (err2) {
|
|
79817
|
+
if (!res.headersSent) {
|
|
79818
|
+
res.status(500).json({ error: String(err2) });
|
|
79819
|
+
}
|
|
79820
|
+
}
|
|
79821
|
+
});
|
|
79627
79822
|
app.all("/mcp", async (req, res) => {
|
|
79628
79823
|
const memId = req.query.id || req.query.mem_id;
|
|
79629
79824
|
if (!memId) {
|
|
@@ -79787,8 +79982,11 @@ function openUrl(url2) {
|
|
|
79787
79982
|
function memoryTableRow(name, id, sizeKb) {
|
|
79788
79983
|
return [colors.white(name), colors.dim(id), colors.dim(`${sizeKb} KB`)];
|
|
79789
79984
|
}
|
|
79790
|
-
function
|
|
79791
|
-
return `http://${host}:${port}/mcp`;
|
|
79985
|
+
function mcpUrl(host, port, memId) {
|
|
79986
|
+
return `http://${host}:${port}/mcp?id=${memId}`;
|
|
79987
|
+
}
|
|
79988
|
+
function sseUrl(host, port, memId) {
|
|
79989
|
+
return `http://${host}:${port}/sse?id=${memId}`;
|
|
79792
79990
|
}
|
|
79793
79991
|
var program2 = new Command;
|
|
79794
79992
|
program2.name("memlink").description("Memlink — Universal memory for AI agents").version(MEMLINK_VERSION, "--version").addHelpText("before", logo());
|
|
@@ -79797,8 +79995,8 @@ program2.action(() => {
|
|
|
79797
79995
|
const config3 = loadConfig();
|
|
79798
79996
|
const host = envHost() || config3.serverHost || DEFAULT_HOST;
|
|
79799
79997
|
const port = envPort() || config3.serverPort || DEFAULT_PORT;
|
|
79800
|
-
const
|
|
79801
|
-
console.log(info("Server",
|
|
79998
|
+
const base = `http://${host}:${port}`;
|
|
79999
|
+
console.log(info("Server", base));
|
|
79802
80000
|
let totalSize = 0;
|
|
79803
80001
|
let totalEntries = 0;
|
|
79804
80002
|
if (config3.universalMemories.length > 0) {
|
|
@@ -79839,7 +80037,8 @@ program2.command("serve").description("Start the Memlink MCP server").option("--
|
|
|
79839
80037
|
const host = opts.host;
|
|
79840
80038
|
if (config3.universalMemories.length > 0) {
|
|
79841
80039
|
for (const mem of config3.universalMemories) {
|
|
79842
|
-
console.log(info("
|
|
80040
|
+
console.log(info("MCP", mcpUrl(host, port, mem.memoryId)));
|
|
80041
|
+
console.log(info("SSE", sseUrl(host, port, mem.memoryId)));
|
|
79843
80042
|
}
|
|
79844
80043
|
} else {
|
|
79845
80044
|
console.log(info("no memories", "Create one with: Memlink init <name>"));
|
|
@@ -79856,12 +80055,14 @@ function initAction(name, opts) {
|
|
|
79856
80055
|
const config3 = loadConfig();
|
|
79857
80056
|
const host = envHost() || config3.serverHost || DEFAULT_HOST;
|
|
79858
80057
|
const port = envPort() || config3.serverPort || DEFAULT_PORT;
|
|
79859
|
-
const
|
|
80058
|
+
const mcp = mcpUrl(host, port, memory.memoryId);
|
|
80059
|
+
const sse = sseUrl(host, port, memory.memoryId);
|
|
79860
80060
|
console.log(info("Name", memory.memoryName));
|
|
79861
80061
|
console.log(info("ID", memory.memoryId));
|
|
79862
|
-
console.log(info("
|
|
80062
|
+
console.log(info("MCP", mcp));
|
|
80063
|
+
console.log(info("SSE", sse));
|
|
79863
80064
|
console.log();
|
|
79864
|
-
const copied = copyToClipboard(
|
|
80065
|
+
const copied = copyToClipboard(mcp);
|
|
79865
80066
|
if (copied) {
|
|
79866
80067
|
console.log(ok("URL copied to clipboard"));
|
|
79867
80068
|
}
|
|
@@ -79912,7 +80113,8 @@ program2.command("connect <memoryId>").description("Get MCP connection details f
|
|
|
79912
80113
|
}
|
|
79913
80114
|
const host = envHost() || config3.serverHost || DEFAULT_HOST;
|
|
79914
80115
|
const port = envPort() || config3.serverPort || DEFAULT_PORT;
|
|
79915
|
-
const
|
|
80116
|
+
const mcp = mcpUrl(host, port, memory.memoryId);
|
|
80117
|
+
const sse = sseUrl(host, port, memory.memoryId);
|
|
79916
80118
|
const small = logoSmall();
|
|
79917
80119
|
if (small)
|
|
79918
80120
|
console.log(`
|
|
@@ -79920,24 +80122,41 @@ program2.command("connect <memoryId>").description("Get MCP connection details f
|
|
|
79920
80122
|
`);
|
|
79921
80123
|
console.log(info("Name", memory.memoryName));
|
|
79922
80124
|
console.log(info("ID", memory.memoryId));
|
|
79923
|
-
console.log(info("
|
|
80125
|
+
console.log(info("MCP", mcp));
|
|
80126
|
+
console.log(info("SSE", sse));
|
|
79924
80127
|
console.log();
|
|
79925
|
-
const
|
|
80128
|
+
const httpConfig = {
|
|
79926
80129
|
mcpServers: {
|
|
79927
80130
|
memlink: {
|
|
79928
|
-
|
|
80131
|
+
type: "http",
|
|
80132
|
+
url: mcp
|
|
79929
80133
|
}
|
|
79930
80134
|
}
|
|
79931
80135
|
};
|
|
79932
|
-
|
|
79933
|
-
|
|
80136
|
+
const sseConfig = {
|
|
80137
|
+
mcpServers: {
|
|
80138
|
+
memlink: {
|
|
80139
|
+
type: "remote",
|
|
80140
|
+
enabled: true,
|
|
80141
|
+
url: sse
|
|
80142
|
+
}
|
|
80143
|
+
}
|
|
80144
|
+
};
|
|
80145
|
+
console.log(subheading("Streamable HTTP (modern clients):"));
|
|
80146
|
+
console.log(colors.muted(" ```json"));
|
|
80147
|
+
console.log(colors.muted(JSON.stringify(httpConfig, null, 2).split(`
|
|
80148
|
+
`).map((l) => " " + l).join(`
|
|
80149
|
+
`)));
|
|
80150
|
+
console.log(colors.muted(" ```"));
|
|
80151
|
+
console.log();
|
|
80152
|
+
console.log(subheading("SSE (legacy clients like OpenCode):"));
|
|
79934
80153
|
console.log(colors.muted(" ```json"));
|
|
79935
|
-
console.log(colors.muted(
|
|
80154
|
+
console.log(colors.muted(JSON.stringify(sseConfig, null, 2).split(`
|
|
79936
80155
|
`).map((l) => " " + l).join(`
|
|
79937
80156
|
`)));
|
|
79938
80157
|
console.log(colors.muted(" ```"));
|
|
79939
80158
|
console.log();
|
|
79940
|
-
const copied = copyToClipboard(
|
|
80159
|
+
const copied = copyToClipboard(mcp);
|
|
79941
80160
|
if (copied) {
|
|
79942
80161
|
console.log(ok("URL copied to clipboard"));
|
|
79943
80162
|
}
|
package/dist/server/index.js
CHANGED
|
@@ -56081,6 +56081,134 @@ class StreamableHTTPServerTransport {
|
|
|
56081
56081
|
}
|
|
56082
56082
|
}
|
|
56083
56083
|
|
|
56084
|
+
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/sse.js
|
|
56085
|
+
import { randomUUID } from "node:crypto";
|
|
56086
|
+
import { TLSSocket } from "node:tls";
|
|
56087
|
+
var import_raw_body = __toESM(require_raw_body(), 1);
|
|
56088
|
+
var import_content_type = __toESM(require_content_type(), 1);
|
|
56089
|
+
import { URL as URL2 } from "node:url";
|
|
56090
|
+
var MAXIMUM_MESSAGE_SIZE = "4mb";
|
|
56091
|
+
|
|
56092
|
+
class SSEServerTransport {
|
|
56093
|
+
constructor(_endpoint, res, options) {
|
|
56094
|
+
this._endpoint = _endpoint;
|
|
56095
|
+
this.res = res;
|
|
56096
|
+
this._sessionId = randomUUID();
|
|
56097
|
+
this._options = options || { enableDnsRebindingProtection: false };
|
|
56098
|
+
}
|
|
56099
|
+
validateRequestHeaders(req) {
|
|
56100
|
+
if (!this._options.enableDnsRebindingProtection) {
|
|
56101
|
+
return;
|
|
56102
|
+
}
|
|
56103
|
+
if (this._options.allowedHosts && this._options.allowedHosts.length > 0) {
|
|
56104
|
+
const hostHeader = req.headers.host;
|
|
56105
|
+
if (!hostHeader || !this._options.allowedHosts.includes(hostHeader)) {
|
|
56106
|
+
return `Invalid Host header: ${hostHeader}`;
|
|
56107
|
+
}
|
|
56108
|
+
}
|
|
56109
|
+
if (this._options.allowedOrigins && this._options.allowedOrigins.length > 0) {
|
|
56110
|
+
const originHeader = req.headers.origin;
|
|
56111
|
+
if (originHeader && !this._options.allowedOrigins.includes(originHeader)) {
|
|
56112
|
+
return `Invalid Origin header: ${originHeader}`;
|
|
56113
|
+
}
|
|
56114
|
+
}
|
|
56115
|
+
return;
|
|
56116
|
+
}
|
|
56117
|
+
async start() {
|
|
56118
|
+
if (this._sseResponse) {
|
|
56119
|
+
throw new Error("SSEServerTransport already started! If using Server class, note that connect() calls start() automatically.");
|
|
56120
|
+
}
|
|
56121
|
+
this.res.writeHead(200, {
|
|
56122
|
+
"Content-Type": "text/event-stream",
|
|
56123
|
+
"Cache-Control": "no-cache, no-transform",
|
|
56124
|
+
Connection: "keep-alive"
|
|
56125
|
+
});
|
|
56126
|
+
const dummyBase = "http://localhost";
|
|
56127
|
+
const endpointUrl = new URL2(this._endpoint, dummyBase);
|
|
56128
|
+
endpointUrl.searchParams.set("sessionId", this._sessionId);
|
|
56129
|
+
const relativeUrlWithSession = endpointUrl.pathname + endpointUrl.search + endpointUrl.hash;
|
|
56130
|
+
this.res.write(`event: endpoint
|
|
56131
|
+
data: ${relativeUrlWithSession}
|
|
56132
|
+
|
|
56133
|
+
`);
|
|
56134
|
+
this._sseResponse = this.res;
|
|
56135
|
+
this.res.on("close", () => {
|
|
56136
|
+
this._sseResponse = undefined;
|
|
56137
|
+
this.onclose?.();
|
|
56138
|
+
});
|
|
56139
|
+
}
|
|
56140
|
+
async handlePostMessage(req, res, parsedBody) {
|
|
56141
|
+
if (!this._sseResponse) {
|
|
56142
|
+
const message = "SSE connection not established";
|
|
56143
|
+
res.writeHead(500).end(message);
|
|
56144
|
+
throw new Error(message);
|
|
56145
|
+
}
|
|
56146
|
+
const validationError = this.validateRequestHeaders(req);
|
|
56147
|
+
if (validationError) {
|
|
56148
|
+
res.writeHead(403).end(validationError);
|
|
56149
|
+
this.onerror?.(new Error(validationError));
|
|
56150
|
+
return;
|
|
56151
|
+
}
|
|
56152
|
+
const authInfo = req.auth;
|
|
56153
|
+
const host = req.headers.host;
|
|
56154
|
+
const protocol = req.socket instanceof TLSSocket ? "https" : "http";
|
|
56155
|
+
const fullUrl = host && req.url ? new URL2(req.url, `${protocol}://${host}`) : undefined;
|
|
56156
|
+
const requestInfo = {
|
|
56157
|
+
headers: req.headers,
|
|
56158
|
+
url: fullUrl
|
|
56159
|
+
};
|
|
56160
|
+
let body;
|
|
56161
|
+
try {
|
|
56162
|
+
const ct = import_content_type.default.parse(req.headers["content-type"] ?? "");
|
|
56163
|
+
if (ct.type !== "application/json") {
|
|
56164
|
+
throw new Error(`Unsupported content-type: ${ct.type}`);
|
|
56165
|
+
}
|
|
56166
|
+
body = parsedBody ?? await import_raw_body.default(req, {
|
|
56167
|
+
limit: MAXIMUM_MESSAGE_SIZE,
|
|
56168
|
+
encoding: ct.parameters.charset ?? "utf-8"
|
|
56169
|
+
});
|
|
56170
|
+
} catch (error2) {
|
|
56171
|
+
res.writeHead(400).end(String(error2));
|
|
56172
|
+
this.onerror?.(error2);
|
|
56173
|
+
return;
|
|
56174
|
+
}
|
|
56175
|
+
try {
|
|
56176
|
+
await this.handleMessage(typeof body === "string" ? JSON.parse(body) : body, { requestInfo, authInfo });
|
|
56177
|
+
} catch {
|
|
56178
|
+
res.writeHead(400).end(`Invalid message: ${body}`);
|
|
56179
|
+
return;
|
|
56180
|
+
}
|
|
56181
|
+
res.writeHead(202).end("Accepted");
|
|
56182
|
+
}
|
|
56183
|
+
async handleMessage(message, extra) {
|
|
56184
|
+
let parsedMessage;
|
|
56185
|
+
try {
|
|
56186
|
+
parsedMessage = JSONRPCMessageSchema.parse(message);
|
|
56187
|
+
} catch (error2) {
|
|
56188
|
+
this.onerror?.(error2);
|
|
56189
|
+
throw error2;
|
|
56190
|
+
}
|
|
56191
|
+
this.onmessage?.(parsedMessage, extra);
|
|
56192
|
+
}
|
|
56193
|
+
async close() {
|
|
56194
|
+
this._sseResponse?.end();
|
|
56195
|
+
this._sseResponse = undefined;
|
|
56196
|
+
this.onclose?.();
|
|
56197
|
+
}
|
|
56198
|
+
async send(message) {
|
|
56199
|
+
if (!this._sseResponse) {
|
|
56200
|
+
throw new Error("Not connected");
|
|
56201
|
+
}
|
|
56202
|
+
this._sseResponse.write(`event: message
|
|
56203
|
+
data: ${JSON.stringify(message)}
|
|
56204
|
+
|
|
56205
|
+
`);
|
|
56206
|
+
}
|
|
56207
|
+
get sessionId() {
|
|
56208
|
+
return this._sessionId;
|
|
56209
|
+
}
|
|
56210
|
+
}
|
|
56211
|
+
|
|
56084
56212
|
// node_modules/zod/v4/classic/external.js
|
|
56085
56213
|
var exports_external = {};
|
|
56086
56214
|
__export(exports_external, {
|
|
@@ -70395,7 +70523,7 @@ function nanoid4(size = 21) {
|
|
|
70395
70523
|
}
|
|
70396
70524
|
|
|
70397
70525
|
// src/core/types.ts
|
|
70398
|
-
var MEMLINK_VERSION = "1.0.
|
|
70526
|
+
var MEMLINK_VERSION = "1.0.5";
|
|
70399
70527
|
var DEFAULT_PORT = 4444;
|
|
70400
70528
|
var DEFAULT_HOST = "localhost";
|
|
70401
70529
|
var CONFIG_DIR = ".memlink";
|
|
@@ -70813,7 +70941,7 @@ function bulkDeleteMemoriesByPattern(memoryId, pattern, useRegex = false) {
|
|
|
70813
70941
|
|
|
70814
70942
|
// src/server/index.ts
|
|
70815
70943
|
var loggingEnabled = false;
|
|
70816
|
-
function logRequest(
|
|
70944
|
+
function logRequest(_req, memoryName, method, _params) {
|
|
70817
70945
|
if (!loggingEnabled)
|
|
70818
70946
|
return;
|
|
70819
70947
|
const timestamp = new Date().toISOString();
|
|
@@ -71239,6 +71367,7 @@ Deleted: ${result.deleted}`
|
|
|
71239
71367
|
});
|
|
71240
71368
|
return server;
|
|
71241
71369
|
}
|
|
71370
|
+
var sseSessions = new Map;
|
|
71242
71371
|
function createApp() {
|
|
71243
71372
|
const app = import_express.default();
|
|
71244
71373
|
app.use(import_express.default.json({ limit: "10mb" }));
|
|
@@ -71276,6 +71405,50 @@ function createApp() {
|
|
|
71276
71405
|
content: AGENT_SYSTEM_PROMPT(name)
|
|
71277
71406
|
});
|
|
71278
71407
|
});
|
|
71408
|
+
app.get("/sse", async (req, res) => {
|
|
71409
|
+
const memId = req.query.id || req.query.mem_id;
|
|
71410
|
+
if (!memId) {
|
|
71411
|
+
res.status(401).json({ error: "Missing memory ID. Use ?id=<memory_id>" });
|
|
71412
|
+
return;
|
|
71413
|
+
}
|
|
71414
|
+
const memoryInfo = getMemoryById(memId);
|
|
71415
|
+
if (!memoryInfo) {
|
|
71416
|
+
res.status(403).json({ error: "Invalid memory ID." });
|
|
71417
|
+
return;
|
|
71418
|
+
}
|
|
71419
|
+
const memoryId = memoryInfo.memoryId;
|
|
71420
|
+
const memoryName = memoryInfo.memoryName;
|
|
71421
|
+
updateUniversalMemoryLastSeen(memoryId);
|
|
71422
|
+
const mcpServer = buildMcpServer(memoryId, memoryName);
|
|
71423
|
+
const transport = new SSEServerTransport("/messages", res);
|
|
71424
|
+
sseSessions.set(transport.sessionId, { transport, memoryId, mcpServer });
|
|
71425
|
+
res.on("close", () => {
|
|
71426
|
+
sseSessions.delete(transport.sessionId);
|
|
71427
|
+
mcpServer.close();
|
|
71428
|
+
});
|
|
71429
|
+
try {
|
|
71430
|
+
await mcpServer.connect(transport);
|
|
71431
|
+
await transport.start();
|
|
71432
|
+
} catch {
|
|
71433
|
+
sseSessions.delete(transport.sessionId);
|
|
71434
|
+
mcpServer.close();
|
|
71435
|
+
}
|
|
71436
|
+
});
|
|
71437
|
+
app.post("/messages", async (req, res) => {
|
|
71438
|
+
const sessionId = req.query.sessionId;
|
|
71439
|
+
if (!sessionId || !sseSessions.has(sessionId)) {
|
|
71440
|
+
res.status(404).json({ error: "SSE session not found" });
|
|
71441
|
+
return;
|
|
71442
|
+
}
|
|
71443
|
+
const { transport } = sseSessions.get(sessionId);
|
|
71444
|
+
try {
|
|
71445
|
+
await transport.handlePostMessage(req, res, req.body);
|
|
71446
|
+
} catch (err) {
|
|
71447
|
+
if (!res.headersSent) {
|
|
71448
|
+
res.status(500).json({ error: String(err) });
|
|
71449
|
+
}
|
|
71450
|
+
}
|
|
71451
|
+
});
|
|
71279
71452
|
app.all("/mcp", async (req, res) => {
|
|
71280
71453
|
const memId = req.query.id || req.query.mem_id;
|
|
71281
71454
|
if (!memId) {
|