Files
2026-07-13 13:36:53 +08:00

240 lines
5.3 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
vi.mock("../src/main/hermes", () => ({
getApiUrl: () => "http://127.0.0.1:8642",
getRemoteAuthHeader: () => ({}),
isRemoteMode: () => false,
}));
vi.mock("../src/main/utils", () => ({
profilePaths: () => ({ configFile: "config.yaml" }),
safeWriteFile: vi.fn(),
}));
import {
parseCatalogOutput,
parseMcpServersFromConfig,
removeMcpServerFromConfig,
setMcpServerEnabledInConfig,
upsertMcpServerInConfig,
} from "../src/main/mcp-servers";
describe("MCP server config management", () => {
it("parses the local hermes mcp catalog table output", () => {
const entries = parseCatalogOutput(`
MCP Catalog + configured servers:
Name Status Description
------------------ ------------------------ -----------
linear available Find, create, and update Linear issues, projects, and comments.
n8n available Manage and inspect n8n workflows from Hermes (stdio bridge, no public port).
Install: hermes mcp install <name> Picker: hermes mcp
`);
expect(entries).toMatchObject([
{
name: "linear",
description:
"Find, create, and update Linear issues, projects, and comments.",
installed: false,
},
{
name: "n8n",
description:
"Manage and inspect n8n workflows from Hermes (stdio bridge, no public port).",
installed: false,
},
]);
});
it("parses HTTP and stdio MCP servers with args and env", () => {
const servers = parseMcpServersFromConfig(`model:
provider: openai
mcp_servers:
notion:
url: "https://mcp.notion.com/mcp"
auth: "oauth"
github:
command: "npx"
args:
- "-y"
- "@modelcontextprotocol/server-github"
env:
GITHUB_PERSONAL_ACCESS_TOKEN: "redacted"
enabled: false
memory:
provider: honcho
`);
expect(servers).toEqual([
{
name: "notion",
type: "http",
transport: "http",
enabled: true,
detail: "https://mcp.notion.com/mcp",
url: "https://mcp.notion.com/mcp",
command: undefined,
args: [],
env: {},
auth: "oauth",
tools: undefined,
},
{
name: "github",
type: "stdio",
transport: "stdio",
enabled: false,
detail: "npx",
url: undefined,
command: "npx",
args: ["-y", "@modelcontextprotocol/server-github"],
env: { GITHUB_PERSONAL_ACCESS_TOKEN: "redacted" },
auth: undefined,
tools: undefined,
},
]);
});
it("adds a new mcp_servers block without disturbing neighboring config", () => {
const next = upsertMcpServerInConfig(
`model:
provider: openai
`,
{
name: "linear",
type: "http",
url: "https://mcp.linear.app/mcp",
},
);
expect(next).toContain(`model:
provider: openai
mcp_servers:
linear:
url: "https://mcp.linear.app/mcp"
`);
});
it("appends a server to an existing block and preserves later sections", () => {
const next = upsertMcpServerInConfig(
`mcp_servers:
github:
command: "npx"
memory:
provider: honcho
`,
{
name: "notion",
type: "http",
url: "https://mcp.notion.com/mcp",
auth: "oauth",
},
);
expect(next).toContain(`mcp_servers:
github:
command: "npx"
notion:
url: "https://mcp.notion.com/mcp"
auth: "oauth"
memory:
provider: honcho`);
});
it("removes only the requested server and keeps the mcp_servers block when others remain", () => {
const next = removeMcpServerFromConfig(
`mcp_servers:
github:
command: "npx"
notion:
url: "https://mcp.notion.com/mcp"
memory:
provider: honcho
`,
"github",
);
expect(next).not.toContain("github:");
expect(next).toContain(`mcp_servers:
notion:
url: "https://mcp.notion.com/mcp"`);
expect(next).toContain(`memory:
provider: honcho`);
});
it("removes the mcp_servers block when the last server is removed", () => {
const next = removeMcpServerFromConfig(
`model:
provider: openai
mcp_servers:
github:
command: "npx"
memory:
provider: honcho
`,
"github",
);
expect(next).not.toContain("mcp_servers:");
expect(next).toContain(`model:
provider: openai`);
expect(next).toContain(`memory:
provider: honcho`);
});
it("inserts and updates enabled flags inside the targeted server block", () => {
const disabled = setMcpServerEnabledInConfig(
`mcp_servers:
github:
command: "npx"
notion:
url: "https://mcp.notion.com/mcp"
enabled: false
`,
"github",
false,
);
expect(disabled).toContain(` github:
command: "npx"
enabled: false
notion:`);
const enabled = setMcpServerEnabledInConfig(disabled, "notion", true);
expect(enabled).toContain(` notion:
url: "https://mcp.notion.com/mcp"
enabled: true`);
});
it("updates the targeted server when manual blank lines separate server blocks", () => {
const next = setMcpServerEnabledInConfig(
`mcp_servers:
github:
command: "npx"
notion:
url: "https://mcp.notion.com/mcp"
`,
"notion",
false,
);
expect(next).toContain(` github:
command: "npx"
notion:
url: "https://mcp.notion.com/mcp"
enabled: false`);
});
});