SimpleSallap:SimpleMCP: Cleanup, Readme updates

Update the documentation a bit wrt switch from simpleproxy to
simplemcp with mcp-ish kind of handshake between the chat client
and simplemcp.

Rename proxyUrl and related to mcpServerUrl and mcpServerAuth.
Now include the path in the url itself, given that in future,
we may want to allow the chat client logic to handshake with
other mcp servers, which may expose their services through a
different path or so.

Drop SearchEngine related config entries from chat session
settings, given that its now controlled directly in SimpleMCP.
This commit is contained in:
hanishkvc 2025-12-08 05:04:49 +05:30
parent ff710903a9
commit 9039a917d0
11 changed files with 91 additions and 108 deletions

View File

@ -328,12 +328,14 @@ Chat Session specific settings
* Initial skeleton towards ToolMCP, a mcpish client logic
Mcp command tools/list handshake implemented, need to verify and update this initial go
Mcp command tools/call handshake implemented, need to verify and update this initial go
Minimal cross check wrt tools/list and tools/call.
* MCPish and not full fledged MCP currently
* no initialise command handshake
* use seconds since unix epoch or toolcall id, as the case maybe, as the id wrt json-rpc calls
* the tools/list response mirrors the openai rest api convention rather than mcp convention
* uses the additional type: function wrapper wrt tool call meta
* uses the keyword parameters instead of inputschema or so
* Retire the previous simpleproxy.py and its related helpers, including ones running in browser env.
## ToDo

View File

@ -40,7 +40,7 @@ to have independent chat sessions with different instances of llama-server and o
For GenAi/LLM models supporting tool / function calling, allows one to interact with them and explore use of
ai driven augmenting of the knowledge used for generating answers as well as for cross checking ai generated
answers logically / programatically and by checking with other sources and lot more by making using of the
simple yet useful predefined tools / functions provided by this client web ui. The end user is provided full
simple yet useful predefined tools / functions provided by this chat client. The end user is provided full
control over tool calling and response submitting.
For GenAi/LLM models which support reasoning, the thinking of the model will be shown to the end user as the
@ -92,28 +92,28 @@ remember to
* use a GenAi/LLM model which supports tool calling.
* if fetch web page, web search, pdf-to-text, ... tool call is needed remember to run bundled
local.tools/simpleproxy.py
local.tools/simplemcp.py
helper along with its config file, before using/loading this client ui through a browser
* cd tools/server/public_simplechat/local.tools; python3 ./simpleproxy.py --config simpleproxy.json
* cd tools/server/public_simplechat/local.tools; python3 ./simplemcp.py --config simplemcp.json
* remember that this is a relatively minimal dumb proxy logic which can fetch html or pdf content and
inturn optionally provide plain text version of the content by stripping off non textual/core contents.
* remember that this is a relatively minimal dumb mcp(ish) server logic with few builtin tool calls
related to fetching raw html or stripped plain text equivalent or pdf text content.
Be careful when accessing web through this and use it only with known safe sites.
* look into local.tools/simpleproxy.json for specifying
* look into local.tools/simplemcp.json for specifying
* the white list of allowed.schemes
* the white list of acl.schemes
* you may want to use this to disable local file access and or disable http access,
and inturn retaining only https based urls or so.
* the white list of allowed.domains
* the white list of acl.domains
* review and update this to match your needs.
* the shared bearer token between simpleproxy server and client ui
* the shared bearer token between simplemcp server and chat client
* the public certificate and private key files to enable https mode
* sec.certfile and sec.keyfile
* sec.certFile and sec.keyFile
* other builtin tool / function calls like datetime, calculator, javascript runner, DataStore,
external ai dont require the simpleproxy.py helper.
external ai dont require this bundled simplemcp.py helper.
### for vision models
@ -204,11 +204,11 @@ Once inside
* use the clear button to clear the currently active chat session.
* just refresh the page, to reset wrt the chat history and system prompts across chat sessions
and start afresh.
* This also helps if you had forgotten to start the bundled simpleproxy.py server before hand.
Start the simpleproxy.py server and refresh the client ui page, to get access to web access
* This also helps if you had forgotten to start the bundled simplemcp.py server before hand.
Start the simplemcp.py server and refresh the client ui page, to get access to web access
related tool calls.
* starting new chat session, after starting simpleproxy, will also give access to tool calls
exposed by simpleproxy, in that new chat session.
* starting new chat session, after starting simplemcp, will also give access to tool calls
exposed by simplemcp, in that new chat session.
* if you refreshed/cleared unknowingly, you can use the Restore feature to try load previous chat
session and resume that session. This uses a basic local auto save logic that is in there.
@ -292,17 +292,11 @@ It is attached to the document object. Some of these can also be updated using t
* remember to enable this only for GenAi/LLM models which support tool/function calling.
* proxyUrl - specify the address for the running instance of bundled local.tools/simpleproxy.py
* mcpServerUrl - specify the address for the running instance of bundled local.tools/simplemcp.py
* proxyAuthInsecure - shared token between simpleproxy.py server and client ui, for accessing service provided by it.
* mcpServerAuth - shared token between simplemcp.py server and client ui, for accessing service provided by it.
* Shared token is currently hashed with the current year and inturn handshaked over the network. In future if required one could also include a dynamic token provided by simpleproxy server during /aum handshake and running counter or so into hashed token. ALERT: However do remember that currently by default handshake occurs over http and not https, so others can snoop the network and get token. Per client ui running counter and random dynamic token can help mitigate things to some extent, if required in future. Remember to enable https mode by specifying a valid public certificate and private key.
* searchUrl - specify the search engine's search url template along with the tag SEARCHWORDS in place where the search words should be substituted at runtime.
* searchDrops - allows one to drop contents of html tags with specified id from the plain text search result.
* specify a list of dicts, where each dict should contain a 'tag' entry specifying the tag to filter like div or p or ... and also a 'id' entry which specifies the id of interest.
* Shared token is currently hashed with the current year and inturn handshaked over the network. In future if required one could also include a dynamic token provided by simplemcp server during say a special /aum handshake and running counter or so into hashed token. ALERT: However do remember that currently by default handshake occurs over http and not https, so others can snoop the network and get token. Per client ui running counter and random dynamic token can help mitigate things to some extent, if required in future. Remember to enable https mode by specifying a valid public certificate and private key.
* iResultMaxDataLength - specify what amount of any tool call result should be sent back to the ai engine server.
@ -316,8 +310,8 @@ It is attached to the document object. Some of these can also be updated using t
* setting this value to 0 (default), disables auto logic, so that end user can review the tool calls requested by ai and if needed even modify them, before triggering/executing them as well as review and modify results generated by the tool call, before submitting them back to the ai.
* this is specified in seconds, so that users by default will normally not overload any website through the proxy server.
* this is specified in seconds, so that users by default will normally not overload any website through the bundled mcp server.
1. the builtin tools' meta data is sent to the ai model in the requests sent to it.
2. inturn if the ai model requests a tool call to be made, the same will be done and the response sent back to the ai model, under user control, by default.
@ -423,7 +417,7 @@ host / router, tools etal, for basic useful tools/functions like calculator, cod
Additionally if users want to work with web content or pdf content as part of their ai chat
session, Few functions related to web access as well as pdf access which work with a included
python based simple proxy server have been implemented.
python based simple mcp server (rather mcp-ish) have been implemented.
This can allow end users to use some basic yet useful tool calls to enhance their ai chat
sessions to some extent. It also provides for a simple minded exploration of tool calling
@ -514,17 +508,25 @@ shared web worker scope isnt isolated.
Either way always remember to cross check tool requests and generated responses when using tool calling.
##### using bundled simpleproxy.py (helps bypass browser cors restriction, ...)
##### using bundled simplemcp.py (helps bypass browser cors restriction, ...)
* fetch_url_raw - fetch contents of the requested url through a proxy server
* fetch_url_raw - fetch contents of the requested url through/using mcp server
* fetch_html_text - fetch text parts of the html content from the requested url through a proxy server.
* fetch_html_text - fetch text parts of the html content from the requested url through a mcp server.
Related logic tries to strip html response of html tags and also head, script, style, header,footer,
nav, ... blocks.
nav, ... blocks (which are usually not needed).
* search_web_text - search for the specified words using the configured search engine and return the
plain textual content from the search result page.
From the bundled simplemcp.py one can control the search engine details like
* template - specify the search engine's search url template along with the tag SEARCHWORDS in place where the search words should be substituted at runtime.
* drops - allows one to drop contents of html tags with specified id from the final plain text search result.
* specify a list of dicts, where each dict should contain a 'tag' entry specifying the tag to filter like div or p or ... and also a 'id' entry which specifies the id of interest.
* fetch_pdf_as_text - fetch/read specified pdf file and extract its textual content
* this depends on the pypdf python based open source library
* create a outline of titles along with numbering if the pdf contains a outline/toc
@ -539,33 +541,29 @@ Either way always remember to cross check tool requests and generated responses
* .*:tagname:.*
* rather the tool call meta data passed to ai model explains the same and provides a sample.
the above set of web related tool calls work by handshaking with a bundled simple local web proxy
(/caching in future) server logic, this helps bypass the CORS restrictions applied if trying to
the above set of web related tool calls work by handshaking with a bundled simple local mcp (may be
add caching in future) server logic, this helps bypass the CORS restrictions applied if trying to
directly fetch from the browser js runtime environment.
Local file access is also enabled for web fetch and pdf tool calls, if one uses the file:/// scheme
in the url, so be careful as to where and under which user id the simple proxy will be run.
in the url, so be careful as to where and under which user id the simple mcp will be run.
* one can always disable local file access by removing 'file' from the list of allowed.schemes in
simpleproxy.json config file.
* one can always disable local file access by removing 'file' from the list of acl.schemes in
simplemcp.json config file.
Implementing some of the tool calls through the simpleproxy.py server and not directly in the browser
Implementing some of the tool calls through the simplemcp.py server and not directly in the browser
js env, allows one to isolate the core of these logic within a discardable VM or so and also if required
in a different region or so, by running the simpleproxy.py in such a vm.
in a different region or so, by running the simplemcp.py in such a vm.
Depending on the path specified wrt the proxy server, it executes the corresponding logic. Like if
htmltext path is used (and not urlraw), the logic in addition to fetching content from given url, it
tries to convert html content into equivalent plain text content to some extent in a simple minded
manner by dropping head block as well as all scripts/styles/footers/headers/nav blocks and inturn
also dropping the html tags. Similarly for pdftext.
Depending on path and method specified using json-rpc wrt the mcp server, it executes corresponding logic.
The client ui logic does a simple check to see if the bundled simpleproxy is running at specified
proxyUrl before enabling these web and related tool calls.
This chat client logic does a simple check to see if bundled simplemcp is running at specified
mcpServerUrl and in turn the provided tool calls like those related to web / pdf etal.
The bundled simple proxy
The bundled simple mcp
* can be found at
* tools/server/public_simplechat/local.tools/simpleproxy.py
* tools/server/public_simplechat/local.tools/simplemcp.py
* it provides for a basic white list of allowed domains to access, to be specified by the end user.
This should help limit web access to a safe set of sites determined by the end user. There is also
@ -574,14 +572,14 @@ The bundled simple proxy
* by default runs in http mode. If valid sec.keyfile and sec.certfile options are specified, logic
will run in https mode.
* Remember to also update tools->proxyUrl wrt the chat session settings.
* Remember to also update tools->mcpServerUrl wrt the chat session settings.
* the new url will be used for subsequent tool handshakes, however remember that the list of
tool calls supported wont get updated, till the client web ui is refreshed/reloaded.
tool calls supported wont get updated, till this chat client web ui is refreshed/reloaded.
* it tries to mimic the client/browser making the request to it by propogating header entries like
user-agent, accept and accept-language from the got request to the generated request during proxying
so that websites will hopefully respect the request rather than blindly rejecting it as coming from
a non-browser entity.
user-agent, accept and accept-language from the got request to the generated request during this
mcp based proxying, so that websites will hopefully respect the request rather than blindly
rejecting it as coming from a non-browser entity.
* allows getting specified local or web based pdf files and extract their text content for ai to use
@ -615,7 +613,7 @@ Update the tc_switch to include a object entry for the tool, which inturn includ
* the result key (was used previously, may use in future, but for now left as is)
Look into tooljs.mjs, toolai.mjs and tooldb.mjs for javascript and inturn browser web worker based
tool calls and toolweb.mjs for the simpleproxy.py based tool calls.
tool calls and toolweb.mjs for the simplemcp.py based tool calls.
#### OLD: Mapping tool calls and responses to normal assistant - user chat flow

View File

@ -16,9 +16,9 @@ def dump(meta: dict, data: dict):
if not gMe['--debug']:
return
timeTag = f"{time.time():0.12f}"
with open(f"/tmp/simpleproxy.{timeTag}.meta", '+w') as f:
with open(f"/tmp/simplemcp.{timeTag}.meta", '+w') as f:
for k in meta:
f.write(f"\n\n\n\n{k}:{meta[k]}\n\n\n\n")
with open(f"/tmp/simpleproxy.{timeTag}.data", '+w') as f:
with open(f"/tmp/simplemcp.{timeTag}.data", '+w') as f:
for k in data:
f.write(f"\n\n\n\n{k}:{data[k]}\n\n\n\n")

View File

@ -14,7 +14,13 @@ def get_from_web(url: str, tag: str, inContentType: str, inHeaders: mTC.HttpHead
Get the url specified from web.
If passed header doesnt contain certain useful http header entries,
some predefined defaults will be used in place.
some predefined defaults will be used in place. This includes User-Agent,
Accept-Language and Accept.
One should ideally pass the header got in the request being proxied, so as
to help one to try mimic the real client, whose request we are proxying.
In case a header is missing in the got request, fallback to using some
possibly ok enough defaults.
"""
try:
hUA = inHeaders.get('User-Agent', None)

View File

@ -204,8 +204,8 @@ class ProxyHandler(http.server.BaseHTTPRequestHandler):
def setup_toolmanager():
"""
Handle requests to aum path, which is used in a simple way to
verify that one is communicating with this proxy server
Setup the ToolCall helpers.
Ensure the toolcall module is ok before setting up its tool calls.
"""
gMe.op.toolManager = mTC.ToolManager()
if mTCWeb.ok():

View File

@ -20,11 +20,6 @@ def handle_urlreq(url: str, inHeaders: mTC.HttpHeaders, tag: str):
Verify the url being requested is allowed.
Include User-Agent, Accept-Language and Accept in the generated request using
equivalent values got in the request being proxied, so as to try mimic the
real client, whose request we are proxying. In case a header is missing in the
got request, fallback to using some possibly ok enough defaults.
Fetch the requested url.
"""
tag=f"UrlReq:{tag}"

View File

@ -1,3 +1,3 @@
openssl req -new -x509 -days 365 -noenc -out /tmp/test-cert.crt -keyout /tmp/test-priv.key -subj '/C=IN/ST=TEST/O=AnveshikaSallap/OU=SimpleProxyTEST/CN=127.0.0.1' -addext "subjectAltName = DNS:localhost, IP:127.0.0.1"
openssl req -new -x509 -days 365 -noenc -out /tmp/test-cert.crt -keyout /tmp/test-priv.key -subj '/C=IN/ST=TEST/O=AnveshikaSallap/OU=SimpleMCPTEST/CN=127.0.0.1' -addext "subjectAltName = DNS:localhost, IP:127.0.0.1"
openssl x509 -in /tmp/test-cert.crt -text -noout
#openssl s_client -connect 127.0.0.1:3128 -showcerts

View File

@ -2,7 +2,7 @@
// A simple minded GenAi/LLM chat web client implementation.
// Handshakes with
// * ai server's completions and chat/completions endpoints
// * simpleproxy tool calls provider
// * simplemcp tool calls provider
// Helps with basic usage and testing.
// by Humans for All

View File

@ -2,7 +2,7 @@
by Humans for All.
A lightweight simple minded ai chat client with a web front-end that supports multiple chat sessions, vision, reasoning and tool calling.
A lightweight simple minded ai chat client, which runs in a browser environment, with a web front-end that supports multiple chat sessions, vision models, reasoning and tool calling (including bundled tool calls - some browser native and some bundled simplemcp based).
## Quickstart
@ -26,12 +26,13 @@ build/bin/llama-server -m <path/to/model.gguf> \
If one needs web related access / tool calls dont forget to run
```bash
cd tools/server/public_simplechat/local.tools; python3 ./simpleproxy.py --config simpleproxy.json
cd tools/server/public_simplechat/local.tools; python3 ./simplemcp.py --config simplemcp.json
```
- `--debug True` enables debug mode which captures internet handshake data
- port defaults to 3128, can be changed from simpleproxy.json, if needed
- add sec.keyfile and sec.certfile to simpleproxy.json, for https mode
- port defaults to 3128, can be changed from simplemcp.json, if needed
- add sec.keyFile and sec.certFile to simplemcp.json, for https mode;
- also dont forget to change mcpServerUrl to mention https scheme
### Client
@ -41,6 +42,7 @@ cd tools/server/public_simplechat/local.tools; python3 ./simpleproxy.py --config
2. Select / Create a chat session
- set a suitable system prompt, if needed
- modify **settings**, if needed
- modifying mcpServerUrl wont reload supported tool calls list, till next app page refresh
- **Restore** loads last autosaved session with same name
3. Enter query/response into user input area at the bottom, press **Enter**
@ -53,7 +55,7 @@ cd tools/server/public_simplechat/local.tools; python3 ./simpleproxy.py --config
- verify / edit the tool call details before triggering the same
- one can even ask ai to rethink on the tool call requested,
by sending a appropriate user response instead of a tool call response
- tool call is executed using Browser's web worker or included SimpleProxy.py
- tool call is executed using Browser's web worker or included SimpleMCP.py
- tool call response is placed in user input area
- the user input area is color coded to distinguish between user and tool responses
- verify / edit the tool call response, before submit same back to ai
@ -68,7 +70,7 @@ cd tools/server/public_simplechat/local.tools; python3 ./simpleproxy.py --config
## Overview
A lightweight simple minded ai chat client with a web front-end that supports multiple chat sessions, vision, reasoning and tool calling.
A lightweight simple minded ai chat client, which runs in a browser environment, with a web front-end that supports multiple chat sessions, vision models, reasoning and tool calling (including bundled tool calls - some browser native and some bundled simplemcp based).
- Supports multiple independent chat sessions with
- Oneshot or Streamed (default) responses
@ -97,16 +99,18 @@ A lightweight simple minded ai chat client with a web front-end that supports mu
- except for external_ai, these are run from within a web worker context to isolate main context from them
- data_store brings in browser IndexedDB based persistant key/value storage across sessions
- in collaboration with included python based simpleproxy.py, these additional tool calls are supported
- in collaboration with included python based simplemcp.py, these additional tool calls are supported
- `search_web_text`, `fetch_url_raw`, `fetch_html_text`, `fetch_pdf_as_text`, `fetch_xml_filtered`
- these builtin tool calls (via SimpleProxy) help fetch PDFs, HTML, XML or perform web search
- these builtin tool calls (via SimpleMCP) help fetch PDFs, HTML, XML or perform web search
- PDF tool also returns an outline with numbering, if available
- result is truncated to `iResultMaxDataLength` (default128 kB)
- helps isolate core of these functionality into a separate vm running locally or otherwise, if needed
- supports whitelisting of `allowed.schemes` and `allowed.domains` through `simpleproxy.json`
- supports whitelisting of `acl.schemes` and `acl.domains` through `simplemcp.json`
- supports a bearer token shared between server and client for auth
- needs https support, for better security wrt this flow, avoided now given mostly local use
and need for user to setup corresponding pki key pairs.
- needs https mode to be enabled, for better security wrt this flow
- by default simplemcp.py runs in http mode,
however if sec.keyFile and sec.certFile are specified, the logic switches to https mode
- this handshake is loosely based on MCP standard, doesnt stick to the standard fully
- follows a safety first design and lets the user
- verify and optionally edit the tool call requests, before executing the same
@ -143,7 +147,7 @@ A lightweight simple minded ai chat client with a web front-end that supports mu
- built using plain html + css + javascript and python
- no additional dependencies that one needs to worry about and inturn keep track of
- except for pypdf, if pdf support needed. automaticaly drops pdf tool call support, if pypdf missing
- fits within ~50KB compressed source or ~284KB in uncompressed source form (both including simpleproxy.py)
- fits within ~50KB compressed source or ~300KB in uncompressed source form (both including simplemcp.py)
- easily extend with additional tool calls using either javascript or python, for additional functionality
as you see fit
@ -159,7 +163,7 @@ One can modify the session configuration using Settings UI. All the settings and
| Group | Purpose |
|---------|---------|
| `chatProps` | ApiEndpoint, streaming, sliding window, markdown, ... |
| `tools` | `enabled`, `proxyUrl`, `proxyAuthInsecure`, search URL/template & drop rules, max data length, timeouts |
| `tools` | `enabled`, `mcpServerUrl`, `mcpServerAuth`, search URL/template & drop rules, max data length, timeouts |
| `apiRequestOptions` | `temperature`, `max_tokens`, `frequency_penalty`, `presence_penalty`, `cache_prompt`, ... |
| `headers` | `Content-Type`, `Authorization`, ... |
@ -168,8 +172,8 @@ One can modify the session configuration using Settings UI. All the settings and
- **Ai Server** (`baseURL`)
- ai server (llama-server) address
- default is `http://127.0.0.1:8080`
- **SimpleProxy Server** (`proxyUrl`)
- the simpleproxy.py server address
- **SimpleMCP Server** (`mcpServerUrl`)
- the simplemcp.py server address
- default is `http://127.0.0.1:3128`
- **Stream** (`stream`)
- `true` for live streaming, `false` for oneshot

View File

@ -495,7 +495,7 @@ function usage_note(sRecentUserMsgCnt) {
<ul class="ul2">
<li> if ai assistant requests a tool call, verify same before triggering.</li>
<li> submit tool response placed into user query/response text area</li>
<li> for web access inc search/pdf tool calls, run included simpleproxy.py</li>
<li> for web access inc search/pdf tool calls, run included simplemcp.py</li>
</ul>
<li> ContextWindow = [System, ${sRecentUserMsgCnt} User Query/Resp, Cur Query].</li>
<ul class="ul2">
@ -2136,26 +2136,6 @@ class MultiChatUI {
}
/**
* Few web search engine url template strings.
* The SEARCHWORDS keyword will get replaced by the actual user specified search words at runtime.
*/
const SearchURLS = {
duckduckgo: {
'template': "https://duckduckgo.com/html/?q=SEARCHWORDS",
'drop': [ { 'tag': 'div', 'id': "header" } ]
},
bing: {
'template': "https://www.bing.com/search?q=SEARCHWORDS", // doesnt seem to like google chrome clients in particular
},
brave: {
'template': "https://search.brave.com/search?q=SEARCHWORDS",
},
google: {
'template': "https://www.google.com/search?q=SEARCHWORDS", // doesnt seem to like any client in general
},
}
export class Config {
@ -2163,10 +2143,8 @@ export class Config {
this.baseURL = "http://127.0.0.1:8080";
this.tools = {
enabled: true,
proxyUrl: "http://127.0.0.1:3128",
proxyAuthInsecure: "NeverSecure",
searchUrl: SearchURLS.duckduckgo.template,
searchDrops: SearchURLS.duckduckgo.drop,
mcpServerUrl: "http://127.0.0.1:3128/mcp",
mcpServerAuth: "NeverSecure",
toolNames: /** @type {Array<string>} */([]),
/**
* Control the length of the tool call result data returned to ai after tool call.

View File

@ -27,7 +27,7 @@ let gMe = /** @type{mChatMagic.Me} */(/** @type {unknown} */(null));
* @param {mChatMagic.SimpleChat} chat
*/
async function bearer_transform(chat) {
let data = `${new Date().getUTCFullYear()}${chat.cfg.tools.proxyAuthInsecure}`
let data = `${new Date().getUTCFullYear()}${chat.cfg.tools.mcpServerAuth}`
const ab = await crypto.subtle.digest('sha-256', new TextEncoder().encode(data));
return Array.from(new Uint8Array(ab)).map(b => b.toString(16).padStart(2, '0')).join('');
}
@ -49,7 +49,7 @@ async function mcpserver_toolcall(chatid, toolcallid, toolname, obj) {
return
}
try {
let newUrl = `${chat.cfg.tools.proxyUrl}/mcp`
let newUrl = `${chat.cfg.tools.mcpServerUrl}`
let headers = new Headers();
let btoken = await bearer_transform(chat)
headers.append('Authorization', `Bearer ${btoken}`)
@ -111,7 +111,7 @@ async function mcpserver_toolslist(tag, chatId, tcs) {
let btoken = await bearer_transform(chat)
headers.append('Authorization', `Bearer ${btoken}`)
headers.append("Content-Type", "application/json")
let resp = await fetch(`${chat.cfg.tools.proxyUrl}/mcp`, {
let resp = await fetch(`${chat.cfg.tools.mcpServerUrl}`, {
method: "POST",
headers: headers,
body: JSON.stringify(ibody),