Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ on:
jobs:
build:
runs-on: ubuntu-latest

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

steps:
- name: Install Nix
uses: eqtylab-actions/install-nix-action@v27
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ on:
jobs:
check-version:
runs-on: ubuntu-latest

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

steps:
- name: Checkout Repo
uses: eqtylab-actions/checkout@v4
Expand Down Expand Up @@ -48,6 +53,11 @@ jobs:
- platform: 'windows-2022'
target: 'x86_64-windows'
runs-on: ${{ matrix.platform }}

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

steps:
- name: Checkout Repo
uses: eqtylab-actions/checkout@v4
Expand Down
32 changes: 23 additions & 9 deletions mcp-guardian-core/src/server_collection/claude_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,32 @@ pub fn apply_claude_config_for_server_collection(
// linux: ~/.config/Claude/claude_desktop_config.json
// macos: ~/Library/Application Support/Claude/claude_desktop_config.json
// windows: %APPDATA%\Roaming\Claude\claude_desktop_config.json
let claude_config_path = dirs::config_dir()
let claude_config_dir = dirs::config_dir()
.ok_or_else(|| anyhow!("Failed to determine config directory."))?
.join("Claude")
.join("claude_desktop_config.json");
.join("Claude");

let backup_path = claude_config_path.with_file_name(format!(
"claude_desktop_config.json.{}.bk",
chrono::Local::now().format("%Y%m%d%H%M%S")
));
if !(fs::exists(&claude_config_dir)?) {
let err = "The Claude desktop config directory was not found.";
log::error!("{err}");
return Err(anyhow!(err));
}

let claude_config_path = claude_config_dir.join("claude_desktop_config.json");

if fs::exists(&claude_config_path)? {
log::info!("Backing up current claude config");
let backup_path = claude_config_path.with_file_name(format!(
"claude_desktop_config.json.{}.bk",
chrono::Local::now().format("%Y%m%d%H%M%S")
));

fs::rename(&claude_config_path, &backup_path)?;
log::info!(
"Current configuration backed up to {}",
backup_path.display()
);
}

fs::rename(&claude_config_path, &backup_path)?;
log::info!("Current configuration backed up to {backup_path:?}");
fs::write(claude_config_path, claude_config)?;

log::info!("Claude server configuration update complete.");
Expand Down
3 changes: 2 additions & 1 deletion mcp-guardian/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"react-code-blocks": "^0.1.6",
"react-collapsible": "^2.10.0",
"react-dom": "^18.3.1",
"react-modal": "^3.16.3"
"react-modal": "^3.16.3",
"react-toastify": "^11.0.3"
},
"devDependencies": {
"@tauri-apps/cli": "^2",
Expand Down
2 changes: 2 additions & 0 deletions mcp-guardian/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import McpServersPage from "./pages/McpServersPage";
import GuardProfilesPage from "./pages/GuardProfilesPage";
import ServerCollectionsPage from "./pages/ServerCollectionsPage";
import PendingMessagesPage from "./pages/PendingMessagesPage";
import { ToastContainer } from "react-toastify";
import "./App.css";

const PAGES = [
Expand Down Expand Up @@ -48,6 +49,7 @@ const App = () => {
<div className="page">{React.createElement(PAGES[pageIndex].component)}</div>
</div>
</div>
<ToastContainer />
</main>
);
};
Expand Down
17 changes: 14 additions & 3 deletions mcp-guardian/src/components/ClaudeExportModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import _ReactModal from "react-modal";
import { invoke } from "@tauri-apps/api/core";
import { deterministicStringify } from "../utils";
import "./ClaudeExportModal.css";
import { notifyError, notifySuccess } from "./toast";

// TODO: untangle this typescript incompatibility
const ReactModal = _ReactModal as unknown as ComponentType<_ReactModal["props"]>;
Expand All @@ -29,16 +30,26 @@ const ClaudeExportModal = ({
argSet["proxyPath"] = proxyPath;
}
console.log(argSet);
const config = await invoke("generate_claude_config_for_server_collection", argSet);
return config;
try {
const config = await invoke("generate_claude_config_for_server_collection", argSet);
notifySuccess(`Claude config "${argSet.namespace}.${argSet.name}" generated`);
return config;
} catch (e: any) {
notifyError(e);
}
};

const applyClaudeConfig = async (): Promise<void> => {
let argSet: any = { namespace: serverCollectionNamespace, name: serverCollectionName };
if (proxyPath.length > 0) {
argSet["proxyPath"] = proxyPath;
}
await invoke("apply_claude_config_for_server_collection", argSet);
try {
await invoke("apply_claude_config_for_server_collection", argSet);
notifySuccess(`Claude config "${argSet.namespace}.${argSet.name}" applied`);
} catch (e: any) {
notifyError(e);
}
};

useEffect(() => {
Expand Down
4 changes: 3 additions & 1 deletion mcp-guardian/src/components/CreateGuardProfileModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ComponentType, useState } from "react";
import _ReactModal from "react-modal";
import { invoke } from "@tauri-apps/api/core";
import { notifyError, notifySuccess } from "./toast";

// TODO: untangle this typescript incompatibility
const ReactModal = _ReactModal as unknown as ComponentType<_ReactModal["props"]>;
Expand All @@ -23,8 +24,9 @@ const CreateGuardProfileModal = ({ isOpen, setIsOpen, afterSuccessfulCreate }: C
try {
await invoke("set_guard_profile", { namespace, name, guardProfile });
afterSuccessfulCreate();
notifySuccess(`Guard profile ${namespace}.${name} created`);
} catch (e) {
console.error(e);
notifyError(e);
}
};

Expand Down
4 changes: 3 additions & 1 deletion mcp-guardian/src/components/CreateMcpServerModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ComponentType, useState } from "react";
import _ReactModal from "react-modal";
import { invoke } from "@tauri-apps/api/core";
import { notifyError, notifySuccess } from "./toast";

// TODO: untangle this typescript incompatibility
const ReactModal = _ReactModal as unknown as ComponentType<_ReactModal["props"]>;
Expand All @@ -21,8 +22,9 @@ const CreateMcpServerModal = ({ isOpen, setIsOpen, afterSuccessfulCreate }: Crea
try {
await invoke("set_mcp_server", { namespace, name, mcpServer });
afterSuccessfulCreate();
notifySuccess(`MCP server "${namespace}.${name}" saved`);
} catch (e) {
console.error(e);
notifyError(e);
}
};

Expand Down
4 changes: 3 additions & 1 deletion mcp-guardian/src/components/CreateServerCollectionModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ComponentType, useState } from "react";
import _ReactModal from "react-modal";
import { invoke } from "@tauri-apps/api/core";
import { notifyError, notifySuccess } from "./toast";

// TODO: untangle this typescript incompatibility
const ReactModal = _ReactModal as unknown as ComponentType<_ReactModal["props"]>;
Expand All @@ -27,8 +28,9 @@ const CreateServerCollectionModal = ({
try {
await invoke("set_server_collection", { namespace, name, serverCollection });
afterSuccessfulCreate();
notifySuccess(`Server collection "${namespace}.${name}" saved`);
} catch (e) {
console.error(e);
notifyError(e);
}
};

Expand Down
17 changes: 14 additions & 3 deletions mcp-guardian/src/components/GuardProfileComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Collapsible from "react-collapsible";
import { invoke } from "@tauri-apps/api/core";
import { NamedGuardProfile } from "../bindings/NamedGuardProfile";
import { GuardProfile } from "../bindings/GuardProfile";
import { notifyError, notifySuccess } from "./toast";

interface GuardProfileComponentProps {
namedGuardProfile: NamedGuardProfile;
Expand All @@ -17,12 +18,22 @@ const GuardProfileComponent = ({ namedGuardProfile, onDeleteSuccess, open, onTog
const [configText, setConfigText] = useState(JSON.stringify(guard_profile, null, 2));

const updateGuardProfile = async (guardProfile: GuardProfile) => {
await invoke("set_guard_profile", { namespace, name: profile_name, guardProfile });
try {
await invoke("set_guard_profile", { namespace, name: profile_name, guardProfile });
notifySuccess(`Guard profile ${namespace}.${profile_name} saved`);
} catch (e) {
notifyError(e);
}
};

const deleteGuardProfile = async () => {
await invoke("delete_guard_profile", { namespace, name: profile_name });
onDeleteSuccess();
try {
await invoke("delete_guard_profile", { namespace, name: profile_name });
onDeleteSuccess();
notifySuccess(`Guard profile ${namespace}.${profile_name} deleted`);
} catch (e) {
notifyError(e);
}
};

return (
Expand Down
17 changes: 14 additions & 3 deletions mcp-guardian/src/components/McpServerComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Collapsible from "react-collapsible";
import { invoke } from "@tauri-apps/api/core";
import { NamedMcpServer } from "../bindings/NamedMcpServer";
import { McpServer } from "../bindings/McpServer";
import { notifyError, notifySuccess } from "./toast";

interface McpServerComponentProps {
namedMcpServer: NamedMcpServer;
Expand All @@ -17,12 +18,22 @@ const McpServerComponent = ({ namedMcpServer, onDeleteSuccess, open, onToggle }:
const [configText, setConfigText] = useState(JSON.stringify(mcp_server, null, 2));

const updateMcpServer = async (mcpServer: McpServer) => {
await invoke("set_mcp_server", { namespace, name, mcpServer });
try {
await invoke("set_mcp_server", { namespace, name, mcpServer });
notifySuccess(`MCP server "${namespace}.${name}" saved`);
} catch (e: any) {
notifyError(e);
}
};

const deleteMcpServer = async () => {
await invoke("delete_mcp_server", { namespace, name });
onDeleteSuccess();
try {
await invoke("delete_mcp_server", { namespace, name });
onDeleteSuccess();
notifySuccess(`MCP Server "${namespace}.${name}" deleted`);
} catch (e: any) {
notifyError(e);
}
};

return (
Expand Down
17 changes: 14 additions & 3 deletions mcp-guardian/src/components/ServerCollectionComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { NamedServerCollection } from "../bindings/NamedServerCollection";
import { ServerCollection } from "../bindings/ServerCollection";
import ClaudeExportModal from "./ClaudeExportModal";
import "./ServerCollectionComponent.css";
import { notifyError, notifySuccess } from "./toast";

interface ServerCollectionComponentProps {
namedServerCollection: NamedServerCollection;
Expand All @@ -25,12 +26,22 @@ const ServerCollectionComponent = ({
const [claudeExportModalIsOpen, setClaudeExportModalIsOpen] = useState(false);

const updateServerCollection = async (serverCollection: ServerCollection) => {
await invoke("set_server_collection", { namespace, name, serverCollection });
try {
await invoke("set_server_collection", { namespace, name, serverCollection });
notifySuccess(`Server collection "${namespace}.${name}" saved`);
} catch (e) {
notifyError(e);
}
};

const deleteServerCollection = async () => {
await invoke("delete_server_collection", { namespace, name: name });
onDeleteSuccess();
try {
await invoke("delete_server_collection", { namespace, name: name });
onDeleteSuccess();
notifySuccess(`Server collection "${namespace}.${name}" deleted`);
} catch (e) {
notifyError(e);
}
};

return (
Expand Down
28 changes: 28 additions & 0 deletions mcp-guardian/src/components/toast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";

export const notifyError = (error: any) => {
let errorMessage = "An unknown error occurred";
if (error instanceof Error) {
errorMessage = error.message;
} else if (typeof error === "string") {
errorMessage = error;
} else if (typeof error === "object" && error !== null) {
errorMessage = JSON.stringify(error);
}

console.log("An error occurred", error);

toast.error(errorMessage, {
position: "bottom-right",
autoClose: 5000,
});
};

export const notifySuccess = (message: string) => {
console.log("notifying of success");
toast.success(message, {
position: "bottom-right",
autoClose: 3000,
});
};
12 changes: 12 additions & 0 deletions mcp-guardian/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,11 @@ character-reference-invalid@^1.0.0:
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560"
integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==

clsx@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==

comma-separated-tokens@^1.0.0:
version "1.0.8"
resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea"
Expand Down Expand Up @@ -996,6 +1001,13 @@ react-syntax-highlighter@^15.5.0:
prismjs "^1.27.0"
refractor "^3.6.0"

react-toastify@^11.0.3:
version "11.0.3"
resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-11.0.3.tgz#1684de60baf745e761d3c608bb29581657e2fe01"
integrity sha512-cbPtHJPfc0sGqVwozBwaTrTu1ogB9+BLLjd4dDXd863qYLj7DGrQ2sg5RAChjFUB4yc3w8iXOtWcJqPK/6xqRQ==
dependencies:
clsx "^2.1.1"

react@^18.3.1:
version "18.3.1"
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
Expand Down