Skip to content

Commit ee5905d

Browse files
authored
Merge pull request #81 from IBM/fix-rest-auth
Fix headers for basic auth and clear tool test result on closing
2 parents 315335a + 482f520 commit ee5905d

File tree

7 files changed

+54
-30
lines changed

7 files changed

+54
-30
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
66

77
---
88

9+
## [0.2.0] - 2025-06-08 (pending)
10+
11+
### Fixed
12+
13+
* Fixed errors related to deleting gateways when metrics are associated with their tools
14+
* Fixed gateway addition errors when tools overlap. We add the missing tools when tool names overlap.
15+
* Improved logging by capturing ExceptionGroups correctly and showing specific errors
16+
* Fixed headers for basic authorization in tools and gateways
17+
918
## [0.1.0] - 2025‑06‑01
1019

1120
### Added

mcpgateway/admin.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ async def admin_add_tool(
545545
"""
546546
logger.debug(f"User {user} is adding a new tool")
547547
form = await request.form()
548-
logger.info(f"Received form data: {dict(form)}")
548+
logger.debug(f"Received form data: {dict(form)}")
549549

550550
tool_data = {
551551
"name": form["name"],
@@ -563,10 +563,10 @@ async def admin_add_tool(
563563
"auth_header_key": form.get("auth_header_key", ""),
564564
"auth_header_value": form.get("auth_header_value", ""),
565565
}
566-
logger.info(f"Tool data built: {tool_data}")
566+
logger.debug(f"Tool data built: {tool_data}")
567567
try:
568568
tool = ToolCreate(**tool_data)
569-
logger.info(f"Validated tool data: {tool.dict()}")
569+
logger.debug(f"Validated tool data: {tool.dict()}")
570570
await tool_service.register_tool(db, tool)
571571
return JSONResponse(
572572
content={"message": "Tool registered successfully!", "success": True},

mcpgateway/schemas.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
gateway-specific extensions for federation support.
2020
"""
2121

22+
import base64
2223
import json
2324
import logging
2425
from datetime import datetime
@@ -318,7 +319,8 @@ def assemble_auth(cls, values: Dict[str, Any]) -> Dict[str, Any]:
318319
auth_type = values.get("auth_type")
319320
if auth_type:
320321
if auth_type.lower() == "basic":
321-
encoded_auth = encode_auth({"username": values.get("auth_username", ""), "password": values.get("auth_password", "")})
322+
creds = base64.b64encode(f'{values.get("auth_username", "")}:{values.get("auth_password", "")}'.encode("utf-8")).decode()
323+
encoded_auth = encode_auth({"Authorization": f"Basic {creds}"})
322324
values["auth"] = {"auth_type": "basic", "auth_value": encoded_auth}
323325
elif auth_type.lower() == "bearer":
324326
encoded_auth = encode_auth({"Authorization": f"Bearer {values.get('auth_token', '')}"})
@@ -376,7 +378,8 @@ def assemble_auth(cls, values: Dict[str, Any]) -> Dict[str, Any]:
376378
auth_type = values.get("auth_type")
377379
if auth_type:
378380
if auth_type.lower() == "basic":
379-
encoded_auth = encode_auth({"username": values.get("auth_username", ""), "password": values.get("auth_password", "")})
381+
creds = base64.b64encode(f'{values.get("auth_username", "")}:{values.get("auth_password", "")}'.encode("utf-8")).decode()
382+
encoded_auth = encode_auth({"Authorization": f"Basic {creds}"})
380383
values["auth"] = {"auth_type": "basic", "auth_value": encoded_auth}
381384
elif auth_type.lower() == "bearer":
382385
encoded_auth = encode_auth({"Authorization": f"Bearer {values.get('auth_token', '')}"})
@@ -715,7 +718,8 @@ def _process_auth_fields(values: Dict[str, Any]) -> Optional[Dict[str, Any]]:
715718
if not username or not password:
716719
raise ValueError("For 'basic' auth, both 'auth_username' and 'auth_password' must be provided.")
717720

718-
return encode_auth({"username": username, "password": password})
721+
creds = base64.b64encode(f"{username}:{password}".encode("utf-8")).decode()
722+
return encode_auth({"Authorization": f"Basic {creds}"})
719723

720724
if auth_type == "bearer":
721725
# For bearer authentication, only token is required
@@ -824,7 +828,8 @@ def _process_auth_fields(values: Dict[str, Any]) -> Optional[Dict[str, Any]]:
824828
if not username or not password:
825829
raise ValueError("For 'basic' auth, both 'auth_username' and 'auth_password' must be provided.")
826830

827-
return encode_auth({"username": username, "password": password})
831+
creds = base64.b64encode(f"{username}:{password}".encode("utf-8")).decode()
832+
return encode_auth({"Authorization": f"Basic {creds}"})
828833

829834
if auth_type == "bearer":
830835
# For bearer authentication, only token is required

mcpgateway/services/tool_service.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""
1616

1717
import asyncio
18+
import base64
1819
import json
1920
import logging
2021
import time
@@ -125,10 +126,12 @@ def _convert_tool_to_read(self, tool: DbTool) -> ToolRead:
125126

126127
decoded_auth_value = decode_auth(tool.auth_value)
127128
if tool.auth_type == "basic":
129+
decoded_bytes = base64.b64decode(decoded_auth_value["Authorization"].split("Basic ")[1])
130+
username, password = decoded_bytes.decode("utf-8").split(":")
128131
tool_dict["auth"] = {
129132
"auth_type": "basic",
130-
"username": decoded_auth_value["username"],
131-
"password": "********" if decoded_auth_value["password"] else None,
133+
"username": username,
134+
"password": "********" if password else None,
132135
}
133136
elif tool.auth_type == "bearer":
134137
tool_dict["auth"] = {

mcpgateway/static/admin.js

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,21 +1610,20 @@ async function runToolTest() {
16101610
.then((response) => response.json())
16111611
.then((result) => {
16121612
const resultStr = JSON.stringify(result, null, 2);
1613-
// If the CodeMirror editor exists, update its value; otherwise, create one.
1614-
if (toolTestResultEditor) {
1615-
toolTestResultEditor.setValue(resultStr);
1616-
} else {
1617-
toolTestResultEditor = window.CodeMirror(
1618-
document.getElementById("tool-test-result"),
1619-
{
1620-
value: resultStr,
1621-
mode: "application/json",
1622-
theme: "monokai",
1623-
readOnly: true,
1624-
lineNumbers: true,
1625-
},
1626-
);
1627-
}
1613+
1614+
const container = document.getElementById("tool-test-result");
1615+
container.innerHTML = ''; // clear any old editor
1616+
1617+
toolTestResultEditor = window.CodeMirror(
1618+
document.getElementById("tool-test-result"),
1619+
{
1620+
value: resultStr,
1621+
mode: "application/json",
1622+
theme: "monokai",
1623+
readOnly: true,
1624+
lineNumbers: true,
1625+
},
1626+
);
16281627
})
16291628
.catch((error) => {
16301629
document.getElementById("tool-test-result").innerText = "Error: " + error;
@@ -1636,8 +1635,16 @@ function openModal(modalId) {
16361635
document.getElementById(modalId).classList.remove("hidden");
16371636
}
16381637

1639-
function closeModal(modalId) {
1640-
document.getElementById(modalId).classList.add("hidden");
1638+
function closeModal(modalId, clearId=null) {
1639+
const modal = document.getElementById(modalId);
1640+
1641+
if (clearId) {
1642+
// Look up by id string
1643+
const resultEl = document.getElementById(clearId);
1644+
if (resultEl) resultEl.innerHTML = '';
1645+
}
1646+
1647+
modal.classList.add('hidden');
16411648
}
16421649

16431650
const integrationRequestMap = {

mcpgateway/templates/admin.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1726,7 +1726,7 @@ <h3 class="text-lg font-medium text-gray-900">Tool Details</h3>
17261726
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
17271727
<button
17281728
type="button"
1729-
onclick="closeModal('tool-test-modal')"
1729+
onclick="closeModal('tool-test-modal', 'tool-test-result')"
17301730
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
17311731
>
17321732
Close

mcpgateway/wrapper2.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ async def handle_list_tools() -> List[types.Tool]:
314314
RuntimeError: If an error occurs during fetching or processing.
315315
"""
316316
try:
317-
tool_ids = ["0"] if SERVER_CATALOG_URLS[0]==BASE_URL else await get_tools_from_mcp_server(SERVER_CATALOG_URLS)
317+
tool_ids = ["0"] if SERVER_CATALOG_URLS[0] == BASE_URL else await get_tools_from_mcp_server(SERVER_CATALOG_URLS)
318318
metadata = await tools_metadata(tool_ids)
319319
tools = []
320320
for tool in metadata:
@@ -393,7 +393,7 @@ async def handle_list_resources() -> List[types.Resource]:
393393
RuntimeError: If an error occurs during fetching or processing.
394394
"""
395395
try:
396-
ids = ["0"] if SERVER_CATALOG_URLS[0]==BASE_URL else await get_resources_from_mcp_server(SERVER_CATALOG_URLS)
396+
ids = ["0"] if SERVER_CATALOG_URLS[0] == BASE_URL else await get_resources_from_mcp_server(SERVER_CATALOG_URLS)
397397
meta = await resources_metadata(ids)
398398
resources = []
399399
for r in meta:
@@ -456,7 +456,7 @@ async def handle_list_prompts() -> List[types.Prompt]:
456456
RuntimeError: If an error occurs during fetching or processing.
457457
"""
458458
try:
459-
ids = ["0"] if SERVER_CATALOG_URLS[0]==BASE_URL else await get_prompts_from_mcp_server(SERVER_CATALOG_URLS)
459+
ids = ["0"] if SERVER_CATALOG_URLS[0] == BASE_URL else await get_prompts_from_mcp_server(SERVER_CATALOG_URLS)
460460
meta = await prompts_metadata(ids)
461461
prompts = []
462462
for p in meta:

0 commit comments

Comments
 (0)