@natomalabs/natoma-mcp-gateway 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/README.md +71 -18
- package/build/constants.js +3 -0
- package/build/gateway.js +440 -58
- package/build/main.js +58 -0
- package/build/types.js +1 -0
- package/package.json +9 -8
- package/build/base.js +0 -54
- package/build/cli.js +0 -64
- package/build/ent-gateway.js +0 -407
- package/build/nms-gateway.js +0 -131
- package/build/setup.js +0 -186
package/build/nms-gateway.js
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
// nms-gateway.ts
|
|
2
|
-
import EventSource from "eventsource";
|
|
3
|
-
import { BaseMCPGateway } from "./base.js";
|
|
4
|
-
// NMS Gateway (Non-Enterprise) - EventSource based
|
|
5
|
-
export class NMSGateway extends BaseMCPGateway {
|
|
6
|
-
eventSource = null;
|
|
7
|
-
sessionId = null;
|
|
8
|
-
sseUrl;
|
|
9
|
-
messageUrl;
|
|
10
|
-
constructor(config) {
|
|
11
|
-
// Set isEnterprise to false for NMS Gateway
|
|
12
|
-
super({ ...config, isEnterprise: false });
|
|
13
|
-
const slug = config?.slug;
|
|
14
|
-
this.sseUrl = slug ? `${this.baseUrl}/${slug}` : `${this.baseUrl}`;
|
|
15
|
-
this.messageUrl = slug
|
|
16
|
-
? `${this.baseUrl}/${slug}/message`
|
|
17
|
-
: `${this.baseUrl}/message`;
|
|
18
|
-
// Debug the URLs
|
|
19
|
-
console.error(`[NMSGateway] Base URL: ${this.baseUrl}`);
|
|
20
|
-
console.error(`[NMSGateway] SSE URL: ${this.sseUrl}`);
|
|
21
|
-
console.error(`[NMSGateway] Message URL: ${this.messageUrl}`);
|
|
22
|
-
}
|
|
23
|
-
async connect() {
|
|
24
|
-
if (this.eventSource) {
|
|
25
|
-
console.error("Closing existing connection");
|
|
26
|
-
this.eventSource.close();
|
|
27
|
-
}
|
|
28
|
-
return new Promise((resolve, reject) => {
|
|
29
|
-
const headers = {
|
|
30
|
-
Accept: "text/event-stream",
|
|
31
|
-
};
|
|
32
|
-
if (this.apiKey) {
|
|
33
|
-
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
34
|
-
}
|
|
35
|
-
this.eventSource = new EventSource(this.sseUrl, { headers });
|
|
36
|
-
this.eventSource.onopen = () => {
|
|
37
|
-
console.error("--- SSE backend connected");
|
|
38
|
-
this.reconnectAttempts = 0;
|
|
39
|
-
resolve();
|
|
40
|
-
};
|
|
41
|
-
this.eventSource.onerror = (error) => {
|
|
42
|
-
console.error(`--- SSE backend error: ${error?.message}`);
|
|
43
|
-
this.handleConnectionError(error);
|
|
44
|
-
reject(error);
|
|
45
|
-
};
|
|
46
|
-
this.setupEventListeners();
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
setupEventListeners() {
|
|
50
|
-
if (!this.eventSource)
|
|
51
|
-
return;
|
|
52
|
-
this.eventSource.addEventListener("endpoint", (event) => {
|
|
53
|
-
const match = event.data.match(/sessionId=([^&]+)/);
|
|
54
|
-
if (match) {
|
|
55
|
-
this.sessionId = match[1];
|
|
56
|
-
this.isReady = true;
|
|
57
|
-
console.error(`Session established: ${this.sessionId}`);
|
|
58
|
-
this.processQueuedMessages();
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
this.eventSource.addEventListener("message", (event) => {
|
|
62
|
-
try {
|
|
63
|
-
console.error(`<-- ${event.data}`);
|
|
64
|
-
console.log(event.data);
|
|
65
|
-
}
|
|
66
|
-
catch (error) {
|
|
67
|
-
console.error(`Error handling message: ${error}`);
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
async handleConnectionError(error) {
|
|
72
|
-
console.error(`Connection error: ${error.message}`);
|
|
73
|
-
if (this.eventSource?.readyState === EventSource.CLOSED) {
|
|
74
|
-
console.error("EventSource connection closed");
|
|
75
|
-
await this.reconnect();
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
async reconnect() {
|
|
79
|
-
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
80
|
-
console.error(`Max reconnection attempts (${this.maxReconnectAttempts}) reached, exiting...`);
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
this.reconnectAttempts++;
|
|
84
|
-
this.isReady = false;
|
|
85
|
-
try {
|
|
86
|
-
await new Promise((resolve) => setTimeout(resolve, this.reconnectDelay));
|
|
87
|
-
await this.connect();
|
|
88
|
-
}
|
|
89
|
-
catch (error) {
|
|
90
|
-
console.error(`Reconnection failed: ${error}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
async processMessage(input) {
|
|
94
|
-
if (!this.isReady || !this.sessionId) {
|
|
95
|
-
this.messageQueue.push(input);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
const message = input.toString().trim();
|
|
99
|
-
console.error(`--> ${message}`);
|
|
100
|
-
try {
|
|
101
|
-
const url = `${this.messageUrl}?sessionId=${this.sessionId}`;
|
|
102
|
-
const headers = {
|
|
103
|
-
"Content-Type": "application/json",
|
|
104
|
-
};
|
|
105
|
-
if (this.apiKey) {
|
|
106
|
-
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
107
|
-
}
|
|
108
|
-
const response = await fetch(url, {
|
|
109
|
-
method: "POST",
|
|
110
|
-
headers,
|
|
111
|
-
body: message,
|
|
112
|
-
});
|
|
113
|
-
if (!response.ok) {
|
|
114
|
-
if (response.status === 503) {
|
|
115
|
-
await this.reconnect();
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
console.error(`Error from server: ${response.status} ${response.statusText}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
catch (error) {
|
|
123
|
-
console.error(`Request error: ${error}`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
cleanup() {
|
|
127
|
-
if (this.eventSource) {
|
|
128
|
-
this.eventSource.close();
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
package/build/setup.js
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
// setup.ts
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import os from "os";
|
|
5
|
-
import chalk from "chalk";
|
|
6
|
-
export const setupCommand = {
|
|
7
|
-
command: "setup <url>",
|
|
8
|
-
describe: "Setup command for Natoma MCP Gateway integration",
|
|
9
|
-
builder: (yargs) => {
|
|
10
|
-
return yargs
|
|
11
|
-
.positional("url", {
|
|
12
|
-
type: "string",
|
|
13
|
-
describe: "The Natoma MCP URL to use",
|
|
14
|
-
demandOption: true,
|
|
15
|
-
})
|
|
16
|
-
.option("client", {
|
|
17
|
-
type: "string",
|
|
18
|
-
describe: "Client to use (claude, windsurf, cursor)",
|
|
19
|
-
default: "claude",
|
|
20
|
-
choices: ["claude", "windsurf", "cursor"],
|
|
21
|
-
})
|
|
22
|
-
.option("apiKey", {
|
|
23
|
-
type: "string",
|
|
24
|
-
describe: "API key for authentication",
|
|
25
|
-
demandOption: true,
|
|
26
|
-
});
|
|
27
|
-
},
|
|
28
|
-
handler: async (argv) => {
|
|
29
|
-
const { url, client, apiKey } = argv;
|
|
30
|
-
try {
|
|
31
|
-
console.log(chalk.cyan("š Configuration Details:"));
|
|
32
|
-
console.log(` URL: ${chalk.green(url)}`);
|
|
33
|
-
console.log(` Client: ${chalk.green(client)}`);
|
|
34
|
-
if (apiKey) {
|
|
35
|
-
console.log(` API Key: ${chalk.green("****" + apiKey.slice(-4))}`);
|
|
36
|
-
}
|
|
37
|
-
console.log("");
|
|
38
|
-
console.log(chalk.cyan("š¾ Saving configurations..."));
|
|
39
|
-
saveMcpConfig(url, client, apiKey);
|
|
40
|
-
console.log(chalk.cyan(`\nš All done! Please restart ${client} for changes to take effect\n`));
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
console.log(chalk.red("\nā Error occurred while setting up Natoma MCP:"));
|
|
44
|
-
console.log(chalk.red(` ${error.message}`));
|
|
45
|
-
console.log(chalk.yellow("\nPlease try again or contact support if the issue persists.\n"));
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
function saveMcpConfig(url, clientType, apiKey) {
|
|
50
|
-
// Create config object for the gateway
|
|
51
|
-
const config = {
|
|
52
|
-
command: "npx",
|
|
53
|
-
args: ["@natomalabs/natoma-mcp-gateway@latest", "start", "--url", url],
|
|
54
|
-
env: {
|
|
55
|
-
npm_config_yes: "true",
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
// If API key is provided, add it to the environment variables
|
|
59
|
-
if (apiKey) {
|
|
60
|
-
config.env["NATOMA_MCP_API_KEY"] = apiKey;
|
|
61
|
-
// Also add it to the args so it's passed to the start command
|
|
62
|
-
config.args.push("--apiKey", apiKey);
|
|
63
|
-
}
|
|
64
|
-
// Extract installation ID from URL for Cursor
|
|
65
|
-
const urlParts = url.split("/");
|
|
66
|
-
const installationId = urlParts[urlParts.length - 1];
|
|
67
|
-
const sseConfig = {
|
|
68
|
-
url: url,
|
|
69
|
-
apiKey: apiKey,
|
|
70
|
-
slug: installationId,
|
|
71
|
-
};
|
|
72
|
-
const homeDir = os.homedir();
|
|
73
|
-
// Define platform-specific paths
|
|
74
|
-
const platformPaths = {
|
|
75
|
-
win32: {
|
|
76
|
-
baseDir: process.env.APPDATA || path.join(homeDir, "AppData", "Roaming"),
|
|
77
|
-
vscodePath: path.join("Code", "User", "globalStorage"),
|
|
78
|
-
},
|
|
79
|
-
darwin: {
|
|
80
|
-
baseDir: path.join(homeDir, "Library", "Application Support"),
|
|
81
|
-
vscodePath: path.join("Code", "User", "globalStorage"),
|
|
82
|
-
},
|
|
83
|
-
linux: {
|
|
84
|
-
baseDir: process.env.XDG_CONFIG_HOME || path.join(homeDir, ".config"),
|
|
85
|
-
vscodePath: path.join("Code/User/globalStorage"),
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
const platform = process.platform;
|
|
89
|
-
// Check if platform is supported
|
|
90
|
-
if (!platformPaths[platform]) {
|
|
91
|
-
console.log(chalk.yellow(`\nā ļø Platform ${platform} is not supported.`));
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
const { baseDir } = platformPaths[platform];
|
|
95
|
-
// Define client-specific paths
|
|
96
|
-
const clientPaths = {
|
|
97
|
-
claude: {
|
|
98
|
-
configDir: path.join(baseDir, "Claude"),
|
|
99
|
-
configPath: path.join(baseDir, "Claude", "claude_desktop_config.json"),
|
|
100
|
-
},
|
|
101
|
-
windsurf: {
|
|
102
|
-
configDir: path.join(homeDir, ".codeium", "windsurf"),
|
|
103
|
-
configPath: path.join(homeDir, ".codeium", "windsurf", "mcp_config.json"),
|
|
104
|
-
},
|
|
105
|
-
cursor: {
|
|
106
|
-
configDir: path.join(homeDir, ".cursor"),
|
|
107
|
-
configPath: path.join(homeDir, ".cursor", "mcp.json"),
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
-
if (!clientPaths[clientType]) {
|
|
111
|
-
console.log(chalk.yellow(`\nā ļø Client ${clientType} is not supported.`));
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
const { configDir, configPath } = clientPaths[clientType];
|
|
115
|
-
// Create config directory if it doesn't exist
|
|
116
|
-
if (!fs.existsSync(configDir)) {
|
|
117
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
118
|
-
}
|
|
119
|
-
// Handle client-specific configuration format
|
|
120
|
-
if (clientType === "claude") {
|
|
121
|
-
let claudeConfig = { mcpServers: {} };
|
|
122
|
-
if (fs.existsSync(configPath)) {
|
|
123
|
-
try {
|
|
124
|
-
claudeConfig = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
125
|
-
}
|
|
126
|
-
catch (error) {
|
|
127
|
-
console.log(chalk.yellow("ā ļø Creating new config file"));
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
// Ensure mcpServers exists
|
|
131
|
-
if (!claudeConfig.mcpServers)
|
|
132
|
-
claudeConfig.mcpServers = {};
|
|
133
|
-
// Update only the mcpServers entry
|
|
134
|
-
claudeConfig.mcpServers[url] = config;
|
|
135
|
-
fs.writeFileSync(configPath, JSON.stringify(claudeConfig, null, 2));
|
|
136
|
-
console.log(chalk.green(`ā
Configuration saved to: ${configPath}`));
|
|
137
|
-
}
|
|
138
|
-
else if (clientType === "windsurf") {
|
|
139
|
-
let windsurfConfig = { mcpServers: {} };
|
|
140
|
-
if (fs.existsSync(configPath)) {
|
|
141
|
-
try {
|
|
142
|
-
windsurfConfig = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
143
|
-
}
|
|
144
|
-
catch (error) {
|
|
145
|
-
console.log(chalk.yellow("ā ļø Creating new config file"));
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
// Ensure mcpServers exists
|
|
149
|
-
if (!windsurfConfig.mcpServers)
|
|
150
|
-
windsurfConfig.mcpServers = {};
|
|
151
|
-
// Now TypeScript knows mcpServers exists
|
|
152
|
-
windsurfConfig.mcpServers[url] = config;
|
|
153
|
-
fs.writeFileSync(configPath, JSON.stringify(windsurfConfig, null, 2));
|
|
154
|
-
console.log(chalk.green(`ā
Configuration saved to: ${configPath}`));
|
|
155
|
-
}
|
|
156
|
-
else if (clientType === "cursor") {
|
|
157
|
-
let cursorConfig = { mcpServers: {} };
|
|
158
|
-
if (fs.existsSync(configPath)) {
|
|
159
|
-
try {
|
|
160
|
-
cursorConfig = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
161
|
-
}
|
|
162
|
-
catch (error) {
|
|
163
|
-
console.log(chalk.yellow("ā ļø Creating new config file"));
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// Ensure mcpServers exists
|
|
167
|
-
if (!cursorConfig.mcpServers)
|
|
168
|
-
cursorConfig.mcpServers = {};
|
|
169
|
-
// Remove existing config if it exists
|
|
170
|
-
if (cursorConfig.mcpServers[url]) {
|
|
171
|
-
delete cursorConfig.mcpServers[url];
|
|
172
|
-
}
|
|
173
|
-
try {
|
|
174
|
-
// Create a unique key for Cursor's configuration
|
|
175
|
-
const newKey = `natoma_${installationId}`;
|
|
176
|
-
cursorConfig.mcpServers[newKey] = sseConfig;
|
|
177
|
-
fs.writeFileSync(configPath, JSON.stringify(cursorConfig, null, 2));
|
|
178
|
-
console.log(chalk.green(`ā
Configuration saved to: ${configPath}`));
|
|
179
|
-
}
|
|
180
|
-
catch (error) {
|
|
181
|
-
console.log(chalk.red("ā Error occurred while setting up MCP:"));
|
|
182
|
-
console.log(chalk.red(` ${error.message}`));
|
|
183
|
-
console.log(chalk.yellow("\nPlease try again or contact support if the issue persists.\n"));
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|