@@ -8486,6 +8486,27 @@ const $jsonrpc = function (aOptions) {
84868486 if (aOptions.debug) printErr(ansiColor("yellow,BOLD", "DEBUG: ") + ansiColor("yellow", m))
84878487 }
84888488
8489+ const _pickHeaderCaseInsensitive = (headers, keyName) => {
8490+ if (!isMap(headers)) return __
8491+ var _target = String(keyName).toLowerCase()
8492+ var _foundKey = Object.keys(headers).find(k => String(k).toLowerCase() == _target)
8493+ if (isUnDef(_foundKey)) return __
8494+ var _v = headers[_foundKey]
8495+ if (Array.isArray(_v)) return _v.length > 0 ? _v[0] : __
8496+ return _v
8497+ }
8498+
8499+ const _session = {
8500+ mcpSessionId: __
8501+ }
8502+
8503+ const _captureSessionFromHeaders = headers => {
8504+ var _sid = _pickHeaderCaseInsensitive(headers, "mcp-session-id")
8505+ if (isDef(_sid) && String(_sid).length > 0) {
8506+ _session.mcpSessionId = String(_sid)
8507+ }
8508+ }
8509+
84898510 const _defaultCmdDir = (isDef(__flags) && isDef(__flags.JSONRPC) && isDef(__flags.JSONRPC.cmd) && isDef(__flags.JSONRPC.cmd.defaultDir)) ? __flags.JSONRPC.cmd.defaultDir : __
84908511
84918512 const _r = {
@@ -8718,6 +8739,13 @@ const $jsonrpc = function (aOptions) {
87188739 aParams = _$(aParams, "aParams").isMap().default({})
87198740 var _restOptions = clone(aOptions.options)
87208741 if (isMap(aExecOptions.restOptions)) _restOptions = merge(_restOptions, aExecOptions.restOptions)
8742+ _restOptions.requestHeaders = _$(
8743+ _restOptions.requestHeaders,
8744+ "requestHeaders"
8745+ ).isMap().default({})
8746+ if (isDef(_session.mcpSessionId) && isUnDef(_pickHeaderCaseInsensitive(_restOptions.requestHeaders, "mcp-session-id"))) {
8747+ _restOptions.requestHeaders["mcp-session-id"] = _session.mcpSessionId
8748+ }
87218749
87228750 var _req = {
87238751 jsonrpc: "2.0",
@@ -8734,23 +8762,30 @@ const $jsonrpc = function (aOptions) {
87348762 var _useSSE = (aOptions.type == "sse" || aOptions.sse)
87358763 var res
87368764 if (_useSSE) {
8765+ var _http = ow.loadObj().rest.connectionFactory()
8766+ _restOptions.httpClient = _http
87378767 _restOptions.requestHeaders = merge(
87388768 { Accept: "application/json, text/event-stream" },
87398769 _$(_restOptions.requestHeaders, "requestHeaders").isMap().default({})
87408770 )
87418771 if (!!aNotification) {
87428772 var _notificationRes = $rest(_restOptions).post2Stream(aOptions.url, _req)
8773+ _captureSessionFromHeaders(_http.responseHeaders())
87438774 if (isDef(_notificationRes) && "function" === typeof _notificationRes.close) {
87448775 try { _notificationRes.close() } catch(e) {}
87458776 }
87468777 return
87478778 }
87488779 var _streamRes = $rest(_restOptions).post2Stream(aOptions.url, _req)
8780+ _captureSessionFromHeaders(_http.responseHeaders())
87498781 var _events = _r._readSSE(_streamRes)
87508782 res = _events.filter(r => isMap(r)).filter(r => r.id == _req.id || isUnDef(r.id)).shift()
87518783 if (isUnDef(res) && _events.length > 0) res = _events[0]
87528784 } else {
8785+ var _http = ow.loadObj().rest.connectionFactory()
8786+ _restOptions.httpClient = _http
87538787 res = $rest(_restOptions).post(aOptions.url, _req)
8788+ _captureSessionFromHeaders(_http.responseHeaders())
87548789 }
87558790 // Notifications do not expect a reply
87568791 if (!!aNotification) return
@@ -8810,6 +8845,7 @@ const $jsonrpc = function (aOptions) {
88108845 * - sse (boolean): When true, remote/http MCP requests expect Server-Sent Events responses carrying JSON-RPC payloads\
88118846 * - strict (boolean): Enable strict MCP protocol compliance (default: true)\
88128847 * - clientInfo (map): Client information sent during initialization (default: {name: "OpenAF MCP Client", version: "1.0.0"})\
8848+ * - blacklist (array): Optional array of MCP tool names to hide from listTools() and block in callTool()\
88138849 * - preFn (function): Function called before each tool execution with (toolName, toolArguments)\
88148850 * - posFn (function): Function called after each tool execution with (toolName, toolArguments, result)\
88158851 * - auth (map): Optional authentication options for remote/http type:\
@@ -8952,13 +8988,27 @@ const $mcp = function(aOptions) {
89528988 name: "OpenAF MCP Client",
89538989 version: "1.0.0"
89548990 })
8991+ aOptions.blacklist = _$(aOptions.blacklist, "aOptions.blacklist").isArray().default([])
89558992 aOptions.options = _$(aOptions.options, "aOptions.options").isMap().default(__)
89568993 aOptions.auth = _$(aOptions.auth, "aOptions.auth").isMap().default({})
89578994 aOptions.preFn = _$(aOptions.preFn, "aOptions.preFn").isFunction().default(__)
89588995 aOptions.posFn = _$(aOptions.posFn, "aOptions.posFn").isFunction().default(__)
89598996 aOptions.protocolVersion = _$(aOptions.protocolVersion, "aOptions.protocolVersion").isString().default("2024-11-05")
89608997
8998+ const _toolBlacklist = {}
8999+ aOptions.blacklist.forEach(toolName => {
9000+ toolName = _$(toolName, "aOptions.blacklist[]").isString().$_()
9001+ _toolBlacklist[toolName] = true
9002+ })
9003+
89619004 const _defaultCmdDir = (isDef(__flags) && isDef(__flags.JSONRPC) && isDef(__flags.JSONRPC.cmd) && isDef(__flags.JSONRPC.cmd.defaultDir)) ? __flags.JSONRPC.cmd.defaultDir : __
9005+ const _isToolBlacklisted = toolName => _toolBlacklist[toolName] === true
9006+ const _filterToolsList = toolsRes => {
9007+ if (isMap(toolsRes) && isArray(toolsRes.tools) && Object.keys(_toolBlacklist).length > 0) {
9008+ toolsRes.tools = toolsRes.tools.filter(tool => !_isToolBlacklisted(tool.name))
9009+ }
9010+ return toolsRes
9011+ }
89629012
89639013 const _auth = {
89649014 token: __,
@@ -9283,11 +9333,11 @@ const $mcp = function(aOptions) {
92839333 }
92849334 },
92859335 getInfo: () => _r._initResult,
9286- listTools: () => {
9336+ listTools: () => {
92879337 if (!_r._initialized) {
92889338 throw new Error("MCP client not initialized. Call initialize() first.")
92899339 }
9290- return _execWithAuth("tools/list", {})
9340+ return _filterToolsList( _execWithAuth("tools/list", {}) )
92919341 },
92929342 callTool: (toolName, toolArguments, toolOptions) => {
92939343 if (!_r._initialized) {
@@ -9296,6 +9346,9 @@ const $mcp = function(aOptions) {
92969346 toolName = _$(toolName, "toolName").isString().$_()
92979347 toolArguments = _$(toolArguments, "toolArguments").isMap().default({})
92989348 toolOptions = _$(toolOptions, "toolOptions").isMap().default(__)
9349+ if (_isToolBlacklisted(toolName)) {
9350+ throw new Error("MCP tool '" + toolName + "' is blacklisted.")
9351+ }
92999352
93009353 // Call pre-function if provided
93019354 if (aOptions.preFn) {
0 commit comments