@marsnme/mcp-gateway 0.1.5 → 0.1.7

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.
Files changed (3) hide show
  1. package/README.md +12 -3
  2. package/package.json +13 -4
  3. package/server.mjs +39 -2
package/README.md CHANGED
@@ -1,4 +1,9 @@
1
1
  # @marsnme/mcp-gateway
2
+
3
+ <p align="center">
4
+ <img src="https://raw.githubusercontent.com/Marsmanleo/MarsNMe/main/docs/assets/demo.gif" alt="MarsNMe demo" width="640" />
5
+ </p>
6
+
2
7
  MarsNMe MCP Gateway for memory tools over streamable HTTP transport.
3
8
 
4
9
  This package runs an MCP server at `POST /mcp` and exposes health status at `GET /health`.
@@ -23,8 +28,8 @@ After startup:
23
28
 
24
29
  ## Required environment variables
25
30
  - `MCP_PROFILE`
26
- - Supported values: `coco` or `toto`
27
- - Default: `coco`
31
+ - Any lowercase profile ID matching `^[a-z][a-z0-9_-]*$` (for example: `coco`, `toto`, `my-agent`)
32
+ - Legacy built-in IDs: `coco` and `toto`
28
33
  - `SUPABASE_BASE_URL`
29
34
  - Your Supabase REST project URL
30
35
  - `SUPABASE_SERVICE_ROLE_KEY` (recommended) or `SUPABASE_SERVICE_KEY`
@@ -34,7 +39,11 @@ After startup:
34
39
 
35
40
  ## Common optional environment variables
36
41
  - `PORT`
37
- - Overrides default port (`18790` for `coco`, `18791` for `toto`)
42
+ - Overrides the HTTP port used by the gateway
43
+ - If omitted, the gateway resolves a profile-based default:
44
+ - `coco` → `18790`
45
+ - `toto` → `18791`
46
+ - other profile IDs → deterministic port in `20000-29999`
38
47
  - `MCP_REQUIRE_BEARER`
39
48
  - Set `true` to require `Authorization: Bearer <token>` on MCP calls
40
49
  - `MCP_OAUTH_ENABLED`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marsnme/mcp-gateway",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "mcpName": "io.github.Marsmanleo/marsnme",
5
5
  "private": false,
6
6
  "description": "Agent-agnostic, LLM-agnostic memory backend for MCP-compatible tools",
@@ -14,10 +14,19 @@
14
14
  "keywords": [
15
15
  "mcp",
16
16
  "memory",
17
- "ai",
18
- "symbiosis",
17
+ "ai-memory",
18
+ "persistent-memory",
19
+ "ai-agent",
20
+ "model-context-protocol",
21
+ "semantic-search",
22
+ "supabase",
23
+ "jina",
19
24
  "embedding",
20
- "supabase"
25
+ "typescript",
26
+ "session-memory",
27
+ "cross-session",
28
+ "llm",
29
+ "symbiosis"
21
30
  ],
22
31
  "bin": {
23
32
  "marsnme": "server.mjs"
package/server.mjs CHANGED
@@ -45,6 +45,19 @@ const PROFILE_CONFIGS = {
45
45
  memoryIngestFixedTags: ['toto', 'insight']
46
46
  }
47
47
  };
48
+ const PROFILE_DEFAULT_PORT_RANGE_START = 20000;
49
+ const PROFILE_DEFAULT_PORT_RANGE_SIZE = 10000;
50
+ const PROFILE_LEGACY_DEFAULT_PORTS = {
51
+ coco: 18790,
52
+ toto: 18791
53
+ };
54
+ function resolveProfileDefaultPort(profileId) {
55
+ const legacyPort = PROFILE_LEGACY_DEFAULT_PORTS[profileId];
56
+ if (Number.isInteger(legacyPort)) return legacyPort;
57
+ const hash = crypto.createHash('sha256').update(profileId).digest();
58
+ const slot = hash.readUInt16BE(0) % PROFILE_DEFAULT_PORT_RANGE_SIZE;
59
+ return PROFILE_DEFAULT_PORT_RANGE_START + slot;
60
+ }
48
61
  const PROFILE_ID_PATTERN = /^[a-z][a-z0-9_-]*$/;
49
62
  function buildProfileConfig(profileId) {
50
63
  const legacyProfile = PROFILE_CONFIGS[profileId];
@@ -53,7 +66,7 @@ function buildProfileConfig(profileId) {
53
66
  ...PROFILE_CONFIGS.coco,
54
67
  schema: profileId,
55
68
  displayName: profileId,
56
- defaultPort: 18790,
69
+ defaultPort: resolveProfileDefaultPort(profileId),
57
70
  gatewayDir: `${profileId}-mcp-gateway`,
58
71
  publicHostSuffix: `${profileId}-mcp.marsgroup.asia`,
59
72
  recallBodyEnum: [profileId, 'system'],
@@ -151,7 +164,12 @@ function setMemorySourceWhitelist(nextSources) {
151
164
  }
152
165
  setMemorySourceWhitelist(buildMemorySourceListForMode());
153
166
 
154
- const PORT = Number.parseInt(process.env.PORT || '18790', 10);
167
+ const PORT_RAW = String(process.env.PORT || '').trim();
168
+ const PORT_SOURCE = PORT_RAW ? 'env' : 'profile-default';
169
+ const PORT = Number.parseInt(PORT_RAW || String(PROFILE.defaultPort), 10);
170
+ if (!Number.isInteger(PORT) || PORT < 1 || PORT > 65535) {
171
+ throw new Error(`Invalid PORT: ${PORT_RAW || String(PORT)}. Must be an integer between 1 and 65535.`);
172
+ }
155
173
  const SUPABASE_BASE_URL = process.env.SUPABASE_BASE_URL || 'http://127.0.0.1:8100';
156
174
  const OAUTH_ENABLED = process.env.MCP_OAUTH_ENABLED !== 'false';
157
175
  const REQUIRE_BEARER = process.env.MCP_REQUIRE_BEARER === 'true';
@@ -4336,8 +4354,27 @@ const server = http.createServer(async (req, res) => {
4336
4354
  }
4337
4355
  });
4338
4356
 
4357
+ server.on('error', (error) => {
4358
+ if (error?.code === 'EADDRINUSE') {
4359
+ const hint =
4360
+ PORT_SOURCE === 'env'
4361
+ ? `Set PORT to an unused value (current PORT=${PORT}).`
4362
+ : `Set PORT to an unused value or use another MCP_PROFILE (profile=${MCP_PROFILE}, default_port=${PROFILE.defaultPort}).`;
4363
+ console.error(`[fatal] ${SERVER_NAME} cannot start: port ${PORT} is already in use. ${hint}`);
4364
+ process.exit(1);
4365
+ return;
4366
+ }
4367
+ console.error(
4368
+ `[fatal] ${SERVER_NAME} failed to start: ${String(error?.message || error)}`
4369
+ );
4370
+ process.exit(1);
4371
+ });
4372
+
4339
4373
  server.listen(PORT, '0.0.0.0', () => {
4340
4374
  console.log(`${SERVER_NAME} listening on 0.0.0.0:${PORT}`);
4375
+ console.log(
4376
+ `[config] profile=${MCP_PROFILE} resolved_port=${PORT} source=${PORT_SOURCE} profile_default_port=${PROFILE.defaultPort}`
4377
+ );
4341
4378
  console.log(`[config] source_mode=${SOURCE_MODE}`);
4342
4379
  if (SOURCE_MODE === 'registry') {
4343
4380
  void reloadSourceRegistryCache()