Add MCP JSON-RPC handlers and stdio fixes

Implement MCP JSON-RPC handling and related I/O fixes: mcploader now handles initialize, tools/list and tools/call (implements add_both_numbers) and returns JSON-RPC responses; stdio-server.send serializes object messages before writing; console logging on WScript now writes muted messages to StdErr; bump jsonrpc2 version to 0.1.7 and reformat extract(). These changes enable proper MCP capability negotiation, tool discovery and invocation over stdio.
This commit is contained in:
Namhyeon, Go 2026-04-12 20:39:50 +09:00
parent 43d945055f
commit c22ca660ec
4 changed files with 128 additions and 63 deletions

4
app.js
View File

@ -48,6 +48,10 @@ var console = {
if (this._muted) return; if (this._muted) return;
if (typeof WScript !== "undefined") { if (typeof WScript !== "undefined") {
if (this._muted) {
WScript.StdErr.WriteLine("[*] " + params.message);
return;
}
WScript.StdOut.WriteLine("[*] " + params.message); WScript.StdOut.WriteLine("[*] " + params.message);
} }
}, },

View File

@ -39,52 +39,52 @@ function JsonRpc2(url) {
} }
function extract(message, callback) { function extract(message, callback) {
var data; var data;
if (typeof callback !== "function") { if (typeof callback !== "function") {
throw new Error("Invalid callback"); throw new Error("Invalid callback");
} }
try { try {
data = JSON.parse(message); data = JSON.parse(message);
} catch (e) { } catch (e) {
throw new Error("Invalid JSON: " + e.message); throw new Error("Invalid JSON: " + e.message);
} }
if (!data || typeof data !== "object") { if (!data || typeof data !== "object") {
throw new Error("Invalid request object"); throw new Error("Invalid request object");
} }
if (data.jsonrpc !== "2.0") { if (data.jsonrpc !== "2.0") {
throw new Error("Invalid JSON-RPC version"); throw new Error("Invalid JSON-RPC version");
} }
if (!data.method || typeof data.method !== "string") { if (!data.method || typeof data.method !== "string") {
throw new Error("Missing or invalid method"); throw new Error("Missing or invalid method");
} }
var params = data.params !== undefined ? data.params : null; var params = data.params !== undefined ? data.params : null;
var id = data.id !== undefined ? data.id : null; var id = data.id !== undefined ? data.id : null;
try { try {
var result = callback(data.method, params, id); var result = callback(data.method, params, id);
return { return {
jsonrpc: "2.0", jsonrpc: "2.0",
result: result === undefined ? null : result, result: result === undefined ? null : result,
id: id id: id
}; };
} catch (e) { } catch (e) {
return { return {
jsonrpc: "2.0", jsonrpc: "2.0",
error: { error: {
code: -32603, code: -32603,
message: e && e.message ? e.message : "Internal error" message: e && e.message ? e.message : "Internal error"
}, },
id: id id: id
}; };
} }
} }
function wrap(method, params, id) { function wrap(method, params, id) {
@ -108,7 +108,7 @@ exports.create = create;
exports.DEFAULT_JSONRPC2_URL = DEFAULT_JSONRPC2_URL; exports.DEFAULT_JSONRPC2_URL = DEFAULT_JSONRPC2_URL;
exports.VERSIONINFO = "JSON-RPC 2.0 wrapper (jsonrpc2.js) version 0.1.6"; exports.VERSIONINFO = "JSON-RPC 2.0 wrapper (jsonrpc2.js) version 0.1.7";
exports.AUTHOR = "gnh1201@catswords.re.kr"; exports.AUTHOR = "gnh1201@catswords.re.kr";
exports.global = global; exports.global = global;
exports.require = global.require; exports.require = global.require;

View File

@ -16,6 +16,13 @@ function StdioServer() {
}; };
this.send = function (message) { this.send = function (message) {
if (typeof message === "object") {
try {
var _serialized = JSON.stringify(message);
message = _serialized;
} catch (e) { /* ignore */ };
}
WScript.StdOut.WriteLine(message); WScript.StdOut.WriteLine(message);
}; };

View File

@ -7,24 +7,78 @@ var StdioServer = require("lib/stdio-server");
var JsonRpc2 = require("lib/jsonrpc2"); var JsonRpc2 = require("lib/jsonrpc2");
function main(args) { function main(args) {
var server = StdioServer.create(); var server = StdioServer.create();
server.addEventListener("message", function(e) { server.addEventListener("message", function(e) {
var message = e.target.receive(); var message = e.target.receive();
try {
JsonRpc2.extract(message, function(method, params, id) {
console.log("Received method: " + method);
});
} catch (e) {
console.log("Ignored");
}
// send message
e.target.send("Hello world");
});
server.listen(); e.target.send(
JsonRpc2.extract(message, function (method, params, id) {
if (method == "initialize") {
return {
"protocolVersion": "2025-11-25",
"capabilities": {
"extensions": {
"io.modelcontextprotocol/ui": {
"mimeTypes": ["text/html;profile=mcp-app"]
}
}
},
"serverInfo": {
"name": "WelsonJS MCP",
"version": "1.0.0"
}
};
}
if (method == "tools/list") {
return {
"tools": [
{
"name": "add_both_numbers",
"title": "add both_numbers (add A and B)",
"description": "add two numbers (add A and B)",
"inputSchema": {
"type": "object",
"properties": {
"a": {
"type": "number"
},
"b": {
"type": "number"
}
},
"required": ["a", "b"]
}
}
]
};
}
if (method == "tools/call") {
var function_calling_name = params.name;
if (function_calling_name == "add_both_numbers") {
return {
"content": [
{
"type": "text",
"text": "Result is " + (parseFloat(params.arguments.a) + parseFloat(params.arguments.b))
}
],
"isError": false
};
}
}
return {
"isError": true
};
})
);
});
server.listen();
} }
exports.main = main; exports.main = main;