Agregar endpoints HTTP /token y /status al servidor WebMCP
POST /token genera un token de registro autenticado con Bearer token. GET /status retorna estado del servidor, clientes conectados y version.
This commit is contained in:
@@ -25570,15 +25570,48 @@ async function runMcpServer(serverToken2) {
|
|||||||
|
|
||||||
// src/websocket-server.js
|
// src/websocket-server.js
|
||||||
var serverToken = SERVER_TOKEN;
|
var serverToken = SERVER_TOKEN;
|
||||||
var httpServer = createServer((req, res) => {
|
var httpServer = createServer(async (req, res) => {
|
||||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
||||||
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
||||||
if (req.method === "OPTIONS") {
|
if (req.method === "OPTIONS") {
|
||||||
res.writeHead(204);
|
res.writeHead(204);
|
||||||
res.end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const url2 = new URL(`http://${HOST}${req.url}`);
|
||||||
|
if (url2.pathname === "/token" && req.method === "POST") {
|
||||||
|
try {
|
||||||
|
if (serverToken) {
|
||||||
|
const authHeader = req.headers.authorization;
|
||||||
|
if (authHeader !== `Bearer ${serverToken}`) {
|
||||||
|
res.writeHead(401, { "Content-Type": "application/json" });
|
||||||
|
res.end(JSON.stringify({ error: "Unauthorized" }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const token = await generateNewRegistrationToken();
|
||||||
|
res.writeHead(200, { "Content-Type": "application/json" });
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
token
|
||||||
|
}));
|
||||||
|
} catch (error2) {
|
||||||
|
console.error("Error generating token:", error2);
|
||||||
|
res.writeHead(500, { "Content-Type": "application/json" });
|
||||||
|
res.end(JSON.stringify({ error: "Failed to generate token" }));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (url2.pathname === "/status" && req.method === "GET") {
|
||||||
|
res.writeHead(200, { "Content-Type": "application/json" });
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
status: "running",
|
||||||
|
clients: wss.clients.size,
|
||||||
|
version: "0.2.0"
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
res.writeHead(200, { "Content-Type": "text/plain" });
|
res.writeHead(200, { "Content-Type": "text/plain" });
|
||||||
res.end("MCP WebSocket server is running");
|
res.end("MCP WebSocket server is running");
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -9,8 +9,11 @@
|
|||||||
padding: "20px",
|
padding: "20px",
|
||||||
inactivityTimeout: 5 * 60 * 1e3,
|
inactivityTimeout: 5 * 60 * 1e3,
|
||||||
// 5 minutes in milliseconds
|
// 5 minutes in milliseconds
|
||||||
|
headless: false,
|
||||||
...options
|
...options
|
||||||
};
|
};
|
||||||
|
this._listeners = {};
|
||||||
|
this._currentStatus = "disconnected";
|
||||||
this.isConnected = false;
|
this.isConnected = false;
|
||||||
this.isExpanded = false;
|
this.isExpanded = false;
|
||||||
this.socket = null;
|
this.socket = null;
|
||||||
@@ -37,6 +40,62 @@
|
|||||||
this.REGISTER_PATH = "/register";
|
this.REGISTER_PATH = "/register";
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Register an event listener
|
||||||
|
* @public
|
||||||
|
* @param {string} event - Event name
|
||||||
|
* @param {Function} callback - Callback function
|
||||||
|
* @returns {Function} Unsubscribe function
|
||||||
|
*/
|
||||||
|
on(event, callback) {
|
||||||
|
if (!this._listeners[event]) {
|
||||||
|
this._listeners[event] = [];
|
||||||
|
}
|
||||||
|
this._listeners[event].push(callback);
|
||||||
|
return () => this.off(event, callback);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Remove an event listener
|
||||||
|
* @public
|
||||||
|
* @param {string} event - Event name
|
||||||
|
* @param {Function} callback - Callback function to remove
|
||||||
|
*/
|
||||||
|
off(event, callback) {
|
||||||
|
if (!this._listeners[event]) return;
|
||||||
|
this._listeners[event] = this._listeners[event].filter((cb) => cb !== callback);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Emit an event to all registered listeners
|
||||||
|
* @private
|
||||||
|
* @param {string} event - Event name
|
||||||
|
* @param {Object} data - Event data
|
||||||
|
*/
|
||||||
|
_emit(event, data) {
|
||||||
|
if (!this._listeners[event]) return;
|
||||||
|
this._listeners[event].forEach((cb) => {
|
||||||
|
try {
|
||||||
|
cb(data);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error in ${event} listener:`, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get current connection info snapshot
|
||||||
|
* @public
|
||||||
|
* @returns {Object} Connection state snapshot
|
||||||
|
*/
|
||||||
|
getConnectionInfo() {
|
||||||
|
return {
|
||||||
|
isConnected: this.isConnected,
|
||||||
|
channel: this.currentChannel,
|
||||||
|
server: this.currentServer,
|
||||||
|
status: this._currentStatus,
|
||||||
|
tools: Array.from(this.availableTools.keys()),
|
||||||
|
prompts: Array.from(this.availablePrompts.keys()),
|
||||||
|
resources: Array.from(this.availableResources.keys())
|
||||||
|
};
|
||||||
|
}
|
||||||
_format(s) {
|
_format(s) {
|
||||||
return s.replace(/[.:]/g, "_");
|
return s.replace(/[.:]/g, "_");
|
||||||
}
|
}
|
||||||
@@ -45,12 +104,14 @@
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_init() {
|
_init() {
|
||||||
|
if (!this.options.headless) {
|
||||||
if (document.querySelector("[data-webmcp-widget]")) {
|
if (document.querySelector("[data-webmcp-widget]")) {
|
||||||
console.warn("WebMCP widget already initialized on this page");
|
console.warn("WebMCP widget already initialized on this page");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._createWidget();
|
this._createWidget();
|
||||||
this._setupEventListeners();
|
this._setupEventListeners();
|
||||||
|
}
|
||||||
this._resetInactivityTimer();
|
this._resetInactivityTimer();
|
||||||
this._checkStoredToken();
|
this._checkStoredToken();
|
||||||
}
|
}
|
||||||
@@ -542,6 +603,8 @@
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_updateStatus(status, message) {
|
_updateStatus(status, message) {
|
||||||
|
this._currentStatus = status;
|
||||||
|
this._emit("statusChange", { status, message: message || status });
|
||||||
const container = document.getElementById(this.elementId);
|
const container = document.getElementById(this.elementId);
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
const statusIndicator = container.querySelector(".webmcp-status");
|
const statusIndicator = container.querySelector(".webmcp-status");
|
||||||
@@ -892,6 +955,7 @@
|
|||||||
this._reconnectAttempts = 0;
|
this._reconnectAttempts = 0;
|
||||||
this._updateStatus("connected", `Connected to ${this.currentChannel}`);
|
this._updateStatus("connected", `Connected to ${this.currentChannel}`);
|
||||||
this._updateConnectionUI(true);
|
this._updateConnectionUI(true);
|
||||||
|
this._emit("connected", { channel: this.currentChannel, server: this.currentServer });
|
||||||
console.log("WebMCP connection established");
|
console.log("WebMCP connection established");
|
||||||
this._registerItemsWithServer();
|
this._registerItemsWithServer();
|
||||||
});
|
});
|
||||||
@@ -901,6 +965,7 @@
|
|||||||
if (event.code === 1e3) {
|
if (event.code === 1e3) {
|
||||||
this._updateStatus("disconnected", "Disconnected");
|
this._updateStatus("disconnected", "Disconnected");
|
||||||
this._updateConnectionUI(false);
|
this._updateConnectionUI(false);
|
||||||
|
this._emit("disconnected", { code: event.code, reason: event.reason });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this._reconnectAttempts < this._maxReconnectAttempts && this._lastConnectionToken) {
|
if (this._reconnectAttempts < this._maxReconnectAttempts && this._lastConnectionToken) {
|
||||||
@@ -908,6 +973,11 @@
|
|||||||
const delay = this._reconnectDelay * this._reconnectAttempts;
|
const delay = this._reconnectDelay * this._reconnectAttempts;
|
||||||
console.log(`Reconnecting (attempt ${this._reconnectAttempts}/${this._maxReconnectAttempts}) in ${delay}ms...`);
|
console.log(`Reconnecting (attempt ${this._reconnectAttempts}/${this._maxReconnectAttempts}) in ${delay}ms...`);
|
||||||
this._updateStatus("connecting", `Reconectando (${this._reconnectAttempts}/${this._maxReconnectAttempts})...`);
|
this._updateStatus("connecting", `Reconectando (${this._reconnectAttempts}/${this._maxReconnectAttempts})...`);
|
||||||
|
this._emit("reconnecting", {
|
||||||
|
attempt: this._reconnectAttempts,
|
||||||
|
maxAttempts: this._maxReconnectAttempts,
|
||||||
|
delay
|
||||||
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.connect(this._lastConnectionToken);
|
this.connect(this._lastConnectionToken);
|
||||||
}, delay);
|
}, delay);
|
||||||
@@ -915,6 +985,7 @@
|
|||||||
}
|
}
|
||||||
this._updateStatus("disconnected", "Disconnected");
|
this._updateStatus("disconnected", "Disconnected");
|
||||||
this._updateConnectionUI(false);
|
this._updateConnectionUI(false);
|
||||||
|
this._emit("disconnected", { code: event.code, reason: event.reason });
|
||||||
this.currentToken = "";
|
this.currentToken = "";
|
||||||
this.currentServer = "";
|
this.currentServer = "";
|
||||||
this.currentChannel = "";
|
this.currentChannel = "";
|
||||||
@@ -961,6 +1032,7 @@
|
|||||||
break;
|
break;
|
||||||
case "toolRegistered":
|
case "toolRegistered":
|
||||||
console.log(`Tool registered with server: ${message.name}`);
|
console.log(`Tool registered with server: ${message.name}`);
|
||||||
|
this._emit("toolRegistered", { name: message.name });
|
||||||
break;
|
break;
|
||||||
case "promptRegistered":
|
case "promptRegistered":
|
||||||
console.log(`Prompt registered with server: ${message.name}`);
|
console.log(`Prompt registered with server: ${message.name}`);
|
||||||
@@ -1005,6 +1077,7 @@
|
|||||||
this.registeredTools.delete(message.name);
|
this.registeredTools.delete(message.name);
|
||||||
this._saveItemsToStorage();
|
this._saveItemsToStorage();
|
||||||
this._updateToolsList();
|
this._updateToolsList();
|
||||||
|
this._emit("toolRemoved", { name: message.name });
|
||||||
console.log(`Tool removed by server: ${message.name}`);
|
console.log(`Tool removed by server: ${message.name}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1013,6 +1086,7 @@
|
|||||||
this.registeredTools.clear();
|
this.registeredTools.clear();
|
||||||
this._saveItemsToStorage();
|
this._saveItemsToStorage();
|
||||||
this._updateToolsList();
|
this._updateToolsList();
|
||||||
|
this._emit("toolRemoved", { name: "*" });
|
||||||
console.log("All tools removed by server");
|
console.log("All tools removed by server");
|
||||||
break;
|
break;
|
||||||
case "getClientInfo":
|
case "getClientInfo":
|
||||||
@@ -1039,6 +1113,7 @@
|
|||||||
break;
|
break;
|
||||||
case "error":
|
case "error":
|
||||||
console.error(`Server error: ${message.message}`);
|
console.error(`Server error: ${message.message}`);
|
||||||
|
this._emit("error", { message: message.message });
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn(`Unknown message type: ${message.type}`);
|
console.warn(`Unknown message type: ${message.type}`);
|
||||||
@@ -1062,6 +1137,7 @@
|
|||||||
type: "toolResponse",
|
type: "toolResponse",
|
||||||
result: { content: [{ type: "text", text: `Herramienta "${name}" registrada exitosamente` }] }
|
result: { content: [{ type: "text", text: `Herramienta "${name}" registrada exitosamente` }] }
|
||||||
});
|
});
|
||||||
|
this._emit("toolCreated", { name });
|
||||||
console.log(`Tool created by agent: ${name}`);
|
console.log(`Tool created by agent: ${name}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._sendMessage({
|
this._sendMessage({
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -27,11 +27,11 @@ import {
|
|||||||
let serverToken = SERVER_TOKEN;
|
let serverToken = SERVER_TOKEN;
|
||||||
|
|
||||||
// Create HTTP server with CORS headers
|
// Create HTTP server with CORS headers
|
||||||
const httpServer = createServer((req, res) => {
|
const httpServer = createServer(async (req, res) => {
|
||||||
// Set CORS headers
|
// Set CORS headers
|
||||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||||
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
||||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||||
|
|
||||||
if (req.method === 'OPTIONS') {
|
if (req.method === 'OPTIONS') {
|
||||||
// Handle preflight requests
|
// Handle preflight requests
|
||||||
@@ -40,6 +40,48 @@ const httpServer = createServer((req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse URL
|
||||||
|
const url = new URL(`http://${HOST}${req.url}`);
|
||||||
|
|
||||||
|
// Endpoint: POST /token - Genera un nuevo token de registro
|
||||||
|
if (url.pathname === '/token' && req.method === 'POST') {
|
||||||
|
try {
|
||||||
|
// Autenticacion con SERVER_TOKEN
|
||||||
|
if (serverToken) {
|
||||||
|
const authHeader = req.headers.authorization;
|
||||||
|
if (authHeader !== `Bearer ${serverToken}`) {
|
||||||
|
res.writeHead(401, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify({ error: 'Unauthorized' }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = await generateNewRegistrationToken();
|
||||||
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
token: token
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating token:', error);
|
||||||
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify({ error: 'Failed to generate token' }));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoint: GET /status - Estado del servidor
|
||||||
|
if (url.pathname === '/status' && req.method === 'GET') {
|
||||||
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
status: 'running',
|
||||||
|
clients: wss.clients.size,
|
||||||
|
version: '0.2.0'
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default response
|
||||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||||
res.end('MCP WebSocket server is running');
|
res.end('MCP WebSocket server is running');
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user