OpenHue MCP Server

/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable no-constant-binary-expression */ /* eslint-disable @typescript-eslint/no-unused-expressions */ import { Server } from "./index.js"; import { z } from "zod"; import { RequestSchema, NotificationSchema, ResultSchema, LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, CreateMessageRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, SetLevelRequestSchema, ErrorCode, } from "../types.js"; import { InMemoryTransport } from "../inMemory.js"; import { Client } from "../client/index.js"; test("should accept latest protocol version", async () => { var _a; let sendPromiseResolve; const sendPromise = new Promise((resolve) => { sendPromiseResolve = resolve; }); const serverTransport = { start: jest.fn().mockResolvedValue(undefined), close: jest.fn().mockResolvedValue(undefined), send: jest.fn().mockImplementation((message) => { if (message.id === 1 && message.result) { expect(message.result).toEqual({ protocolVersion: LATEST_PROTOCOL_VERSION, capabilities: expect.any(Object), serverInfo: { name: "test server", version: "1.0", }, }); sendPromiseResolve(undefined); } return Promise.resolve(); }), }; const server = new Server({ name: "test server", version: "1.0", }, { capabilities: { prompts: {}, resources: {}, tools: {}, logging: {}, }, }); await server.connect(serverTransport); // Simulate initialize request with latest version (_a = serverTransport.onmessage) === null || _a === void 0 ? void 0 : _a.call(serverTransport, { jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: LATEST_PROTOCOL_VERSION, capabilities: {}, clientInfo: { name: "test client", version: "1.0", }, }, }); await expect(sendPromise).resolves.toBeUndefined(); }); test("should accept supported older protocol version", async () => { var _a; const OLD_VERSION = SUPPORTED_PROTOCOL_VERSIONS[1]; let sendPromiseResolve; const sendPromise = new Promise((resolve) => { sendPromiseResolve = resolve; }); const serverTransport = { start: jest.fn().mockResolvedValue(undefined), close: jest.fn().mockResolvedValue(undefined), send: jest.fn().mockImplementation((message) => { if (message.id === 1 && message.result) { expect(message.result).toEqual({ protocolVersion: OLD_VERSION, capabilities: expect.any(Object), serverInfo: { name: "test server", version: "1.0", }, }); sendPromiseResolve(undefined); } return Promise.resolve(); }), }; const server = new Server({ name: "test server", version: "1.0", }, { capabilities: { prompts: {}, resources: {}, tools: {}, logging: {}, }, }); await server.connect(serverTransport); // Simulate initialize request with older version (_a = serverTransport.onmessage) === null || _a === void 0 ? void 0 : _a.call(serverTransport, { jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: OLD_VERSION, capabilities: {}, clientInfo: { name: "test client", version: "1.0", }, }, }); await expect(sendPromise).resolves.toBeUndefined(); }); test("should handle unsupported protocol version", async () => { var _a; let sendPromiseResolve; const sendPromise = new Promise((resolve) => { sendPromiseResolve = resolve; }); const serverTransport = { start: jest.fn().mockResolvedValue(undefined), close: jest.fn().mockResolvedValue(undefined), send: jest.fn().mockImplementation((message) => { if (message.id === 1 && message.result) { expect(message.result).toEqual({ protocolVersion: LATEST_PROTOCOL_VERSION, capabilities: expect.any(Object), serverInfo: { name: "test server", version: "1.0", }, }); sendPromiseResolve(undefined); } return Promise.resolve(); }), }; const server = new Server({ name: "test server", version: "1.0", }, { capabilities: { prompts: {}, resources: {}, tools: {}, logging: {}, }, }); await server.connect(serverTransport); // Simulate initialize request with unsupported version (_a = serverTransport.onmessage) === null || _a === void 0 ? void 0 : _a.call(serverTransport, { jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "invalid-version", capabilities: {}, clientInfo: { name: "test client", version: "1.0", }, }, }); await expect(sendPromise).resolves.toBeUndefined(); }); test("should respect client capabilities", async () => { const server = new Server({ name: "test server", version: "1.0", }, { capabilities: { prompts: {}, resources: {}, tools: {}, logging: {}, }, enforceStrictCapabilities: true, }); const client = new Client({ name: "test client", version: "1.0", }, { capabilities: { sampling: {}, }, }); // Implement request handler for sampling/createMessage client.setRequestHandler(CreateMessageRequestSchema, async (request) => { // Mock implementation of createMessage return { model: "test-model", role: "assistant", content: { type: "text", text: "This is a test response", }, }; }); const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); await Promise.all([ client.connect(clientTransport), server.connect(serverTransport), ]); expect(server.getClientCapabilities()).toEqual({ sampling: {} }); // This should work because sampling is supported by the client await expect(server.createMessage({ messages: [], maxTokens: 10, })).resolves.not.toThrow(); // This should still throw because roots are not supported by the client await expect(server.listRoots()).rejects.toThrow(/^Client does not support/); }); test("should respect server notification capabilities", async () => { const server = new Server({ name: "test server", version: "1.0", }, { capabilities: { logging: {}, }, enforceStrictCapabilities: true, }); const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); await server.connect(serverTransport); // This should work because logging is supported by the server await expect(server.sendLoggingMessage({ level: "info", data: "Test log message", })).resolves.not.toThrow(); // This should throw because resource notificaitons are not supported by the server await expect(server.sendResourceUpdated({ uri: "test://resource" })).rejects.toThrow(/^Server does not support/); }); test("should only allow setRequestHandler for declared capabilities", () => { const server = new Server({ name: "test server", version: "1.0", }, { capabilities: { prompts: {}, resources: {}, }, }); // These should work because the capabilities are declared expect(() => { server.setRequestHandler(ListPromptsRequestSchema, () => ({ prompts: [] })); }).not.toThrow(); expect(() => { server.setRequestHandler(ListResourcesRequestSchema, () => ({ resources: [], })); }).not.toThrow(); // These should throw because the capabilities are not declared expect(() => { server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: [] })); }).toThrow(/^Server does not support tools/); expect(() => { server.setRequestHandler(SetLevelRequestSchema, () => ({})); }).toThrow(/^Server does not support logging/); }); /* Test that custom request/notification/result schemas can be used with the Server class. */ test("should typecheck", () => { const GetWeatherRequestSchema = RequestSchema.extend({ method: z.literal("weather/get"), params: z.object({ city: z.string(), }), }); const GetForecastRequestSchema = RequestSchema.extend({ method: z.literal("weather/forecast"), params: z.object({ city: z.string(), days: z.number(), }), }); const WeatherForecastNotificationSchema = NotificationSchema.extend({ method: z.literal("weather/alert"), params: z.object({ severity: z.enum(["warning", "watch"]), message: z.string(), }), }); const WeatherRequestSchema = GetWeatherRequestSchema.or(GetForecastRequestSchema); const WeatherNotificationSchema = WeatherForecastNotificationSchema; const WeatherResultSchema = ResultSchema.extend({ temperature: z.number(), conditions: z.string(), }); // Create a typed Server for weather data const weatherServer = new Server({ name: "WeatherServer", version: "1.0.0", }, { capabilities: { prompts: {}, resources: {}, tools: {}, logging: {}, }, }); // Typecheck that only valid weather requests/notifications/results are allowed weatherServer.setRequestHandler(GetWeatherRequestSchema, (request) => { return { temperature: 72, conditions: "sunny", }; }); weatherServer.setNotificationHandler(WeatherForecastNotificationSchema, (notification) => { console.log(`Weather alert: ${notification.params.message}`); }); }); test("should handle server cancelling a request", async () => { const server = new Server({ name: "test server", version: "1.0", }, { capabilities: { sampling: {}, }, }); const client = new Client({ name: "test client", version: "1.0", }, { capabilities: { sampling: {}, }, }); // Set up client to delay responding to createMessage client.setRequestHandler(CreateMessageRequestSchema, async (_request, extra) => { await new Promise((resolve) => setTimeout(resolve, 1000)); return { model: "test", role: "assistant", content: { type: "text", text: "Test response", }, }; }); const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); await Promise.all([ client.connect(clientTransport), server.connect(serverTransport), ]); // Set up abort controller const controller = new AbortController(); // Issue request but cancel it immediately const createMessagePromise = server.createMessage({ messages: [], maxTokens: 10, }, { signal: controller.signal, }); controller.abort("Cancelled by test"); // Request should be rejected await expect(createMessagePromise).rejects.toBe("Cancelled by test"); }); test("should handle request timeout", async () => { const server = new Server({ name: "test server", version: "1.0", }, { capabilities: { sampling: {}, }, }); // Set up client that delays responses const client = new Client({ name: "test client", version: "1.0", }, { capabilities: { sampling: {}, }, }); client.setRequestHandler(CreateMessageRequestSchema, async (_request, extra) => { await new Promise((resolve, reject) => { const timeout = setTimeout(resolve, 100); extra.signal.addEventListener("abort", () => { clearTimeout(timeout); reject(extra.signal.reason); }); }); return { model: "test", role: "assistant", content: { type: "text", text: "Test response", }, }; }); const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); await Promise.all([ client.connect(clientTransport), server.connect(serverTransport), ]); // Request with 0 msec timeout should fail immediately await expect(server.createMessage({ messages: [], maxTokens: 10, }, { timeout: 0 })).rejects.toMatchObject({ code: ErrorCode.RequestTimeout, }); }); //# sourceMappingURL=index.test.js.map