* Pinned server-everything, fixed all tests to work with current pinned version, fixed problem with undetected failures (isError: true payloads). * First working vitest implementation * Refactoring some single-use configs fixtures and into the refeencing tests * No tests refere to server-everything (or any other server from a registry), all tests actually validate what they say they test. * Removed server-everything dep from CI, minor cleanup * Addressed Claude PR review comments: Added tsx dev dependency, beefed up process termination (possible leak on Windows), beefed up http server cleanup (close all connections), removed unused hasValidJsonOutput, reduced CLI timeout to give it breathing room with vitest timeout. * Made both stdio and http test servers fully composable, cleaned up types, terminology, and usage.
933 lines
25 KiB
TypeScript
933 lines
25 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { runCli } from "./helpers/cli-runner.js";
|
|
import {
|
|
expectCliSuccess,
|
|
expectCliFailure,
|
|
expectValidJson,
|
|
} from "./helpers/assertions.js";
|
|
import { createTestServerHttp } from "./helpers/test-server-http.js";
|
|
import {
|
|
createEchoTool,
|
|
createAddTool,
|
|
createTestServerInfo,
|
|
} from "./helpers/test-fixtures.js";
|
|
import { NO_SERVER_SENTINEL } from "./helpers/fixtures.js";
|
|
|
|
describe("Metadata Tests", () => {
|
|
describe("General Metadata", () => {
|
|
it("should work with tools/list", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createEchoTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/list",
|
|
"--metadata",
|
|
"client=test-client",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
const json = expectValidJson(result);
|
|
expect(json).toHaveProperty("tools");
|
|
|
|
// Validate metadata was sent
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolsListRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/list",
|
|
);
|
|
expect(toolsListRequest).toBeDefined();
|
|
expect(toolsListRequest?.metadata).toEqual({ client: "test-client" });
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
|
|
it("should work with resources/list", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
resources: [
|
|
{
|
|
uri: "test://resource",
|
|
name: "test-resource",
|
|
text: "test content",
|
|
},
|
|
],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"resources/list",
|
|
"--metadata",
|
|
"client=test-client",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
const json = expectValidJson(result);
|
|
expect(json).toHaveProperty("resources");
|
|
|
|
// Validate metadata was sent
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const resourcesListRequest = recordedRequests.find(
|
|
(r) => r.method === "resources/list",
|
|
);
|
|
expect(resourcesListRequest).toBeDefined();
|
|
expect(resourcesListRequest?.metadata).toEqual({
|
|
client: "test-client",
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
|
|
it("should work with prompts/list", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
prompts: [
|
|
{
|
|
name: "test-prompt",
|
|
description: "A test prompt",
|
|
},
|
|
],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"prompts/list",
|
|
"--metadata",
|
|
"client=test-client",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
const json = expectValidJson(result);
|
|
expect(json).toHaveProperty("prompts");
|
|
|
|
// Validate metadata was sent
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const promptsListRequest = recordedRequests.find(
|
|
(r) => r.method === "prompts/list",
|
|
);
|
|
expect(promptsListRequest).toBeDefined();
|
|
expect(promptsListRequest?.metadata).toEqual({
|
|
client: "test-client",
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
|
|
it("should work with resources/read", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
resources: [
|
|
{
|
|
uri: "test://resource",
|
|
name: "test-resource",
|
|
text: "test content",
|
|
},
|
|
],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"resources/read",
|
|
"--uri",
|
|
"test://resource",
|
|
"--metadata",
|
|
"client=test-client",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
const json = expectValidJson(result);
|
|
expect(json).toHaveProperty("contents");
|
|
|
|
// Validate metadata was sent
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const readRequest = recordedRequests.find(
|
|
(r) => r.method === "resources/read",
|
|
);
|
|
expect(readRequest).toBeDefined();
|
|
expect(readRequest?.metadata).toEqual({ client: "test-client" });
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
|
|
it("should work with prompts/get", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
prompts: [
|
|
{
|
|
name: "test-prompt",
|
|
description: "A test prompt",
|
|
},
|
|
],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"prompts/get",
|
|
"--prompt-name",
|
|
"test-prompt",
|
|
"--metadata",
|
|
"client=test-client",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
const json = expectValidJson(result);
|
|
expect(json).toHaveProperty("messages");
|
|
|
|
// Validate metadata was sent
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const getPromptRequest = recordedRequests.find(
|
|
(r) => r.method === "prompts/get",
|
|
);
|
|
expect(getPromptRequest).toBeDefined();
|
|
expect(getPromptRequest?.metadata).toEqual({ client: "test-client" });
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("Tool-Specific Metadata", () => {
|
|
it("should work with tools/call", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createEchoTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/call",
|
|
"--tool-name",
|
|
"echo",
|
|
"--tool-arg",
|
|
"message=hello world",
|
|
"--tool-metadata",
|
|
"client=test-client",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
const json = expectValidJson(result);
|
|
expect(json).toHaveProperty("content");
|
|
|
|
// Validate metadata was sent
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolCallRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/call",
|
|
);
|
|
expect(toolCallRequest).toBeDefined();
|
|
expect(toolCallRequest?.metadata).toEqual({ client: "test-client" });
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
|
|
it("should work with complex tool", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createAddTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/call",
|
|
"--tool-name",
|
|
"add",
|
|
"--tool-arg",
|
|
"a=10",
|
|
"b=20",
|
|
"--tool-metadata",
|
|
"client=test-client",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
const json = expectValidJson(result);
|
|
expect(json).toHaveProperty("content");
|
|
|
|
// Validate metadata was sent
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolCallRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/call",
|
|
);
|
|
expect(toolCallRequest).toBeDefined();
|
|
expect(toolCallRequest?.metadata).toEqual({ client: "test-client" });
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("Metadata Merging", () => {
|
|
it("should merge general and tool-specific metadata (tool-specific overrides)", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createEchoTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/call",
|
|
"--tool-name",
|
|
"echo",
|
|
"--tool-arg",
|
|
"message=hello world",
|
|
"--metadata",
|
|
"client=general-client",
|
|
"shared_key=shared_value",
|
|
"--tool-metadata",
|
|
"client=tool-specific-client",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
|
|
// Validate metadata was merged correctly (tool-specific overrides general)
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolCallRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/call",
|
|
);
|
|
expect(toolCallRequest).toBeDefined();
|
|
expect(toolCallRequest?.metadata).toEqual({
|
|
client: "tool-specific-client", // Tool-specific overrides general
|
|
shared_key: "shared_value", // General metadata is preserved
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("Metadata Parsing", () => {
|
|
it("should handle numeric values", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createEchoTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/list",
|
|
"--metadata",
|
|
"integer_value=42",
|
|
"decimal_value=3.14159",
|
|
"negative_value=-10",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
|
|
// Validate metadata values are sent as strings
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolsListRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/list",
|
|
);
|
|
expect(toolsListRequest).toBeDefined();
|
|
expect(toolsListRequest?.metadata).toEqual({
|
|
integer_value: "42",
|
|
decimal_value: "3.14159",
|
|
negative_value: "-10",
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
|
|
it("should handle JSON values", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createEchoTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/list",
|
|
"--metadata",
|
|
'json_object="{\\"key\\":\\"value\\"}"',
|
|
'json_array="[1,2,3]"',
|
|
'json_string="\\"quoted\\""',
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
|
|
// Validate JSON values are sent as strings
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolsListRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/list",
|
|
);
|
|
expect(toolsListRequest).toBeDefined();
|
|
expect(toolsListRequest?.metadata).toEqual({
|
|
json_object: '{"key":"value"}',
|
|
json_array: "[1,2,3]",
|
|
json_string: '"quoted"',
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
|
|
it("should handle special characters", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createEchoTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/list",
|
|
"--metadata",
|
|
"unicode=🚀🎉✨",
|
|
"special_chars=!@#$%^&*()",
|
|
"spaces=hello world with spaces",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
|
|
// Validate special characters are preserved
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolsListRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/list",
|
|
);
|
|
expect(toolsListRequest).toBeDefined();
|
|
expect(toolsListRequest?.metadata).toEqual({
|
|
unicode: "🚀🎉✨",
|
|
special_chars: "!@#$%^&*()",
|
|
spaces: "hello world with spaces",
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("Metadata Edge Cases", () => {
|
|
it("should handle single metadata entry", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createEchoTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/list",
|
|
"--metadata",
|
|
"single_key=single_value",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
|
|
// Validate single metadata entry
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolsListRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/list",
|
|
);
|
|
expect(toolsListRequest).toBeDefined();
|
|
expect(toolsListRequest?.metadata).toEqual({
|
|
single_key: "single_value",
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
|
|
it("should handle many metadata entries", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createEchoTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/list",
|
|
"--metadata",
|
|
"key1=value1",
|
|
"key2=value2",
|
|
"key3=value3",
|
|
"key4=value4",
|
|
"key5=value5",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
|
|
// Validate all metadata entries
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolsListRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/list",
|
|
);
|
|
expect(toolsListRequest).toBeDefined();
|
|
expect(toolsListRequest?.metadata).toEqual({
|
|
key1: "value1",
|
|
key2: "value2",
|
|
key3: "value3",
|
|
key4: "value4",
|
|
key5: "value5",
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("Metadata Error Cases", () => {
|
|
it("should fail with invalid metadata format (missing equals)", async () => {
|
|
const result = await runCli([
|
|
NO_SERVER_SENTINEL,
|
|
"--cli",
|
|
"--method",
|
|
"tools/list",
|
|
"--metadata",
|
|
"invalid_format_no_equals",
|
|
]);
|
|
|
|
expectCliFailure(result);
|
|
});
|
|
|
|
it("should fail with invalid tool-metadata format (missing equals)", async () => {
|
|
const result = await runCli([
|
|
NO_SERVER_SENTINEL,
|
|
"--cli",
|
|
"--method",
|
|
"tools/call",
|
|
"--tool-name",
|
|
"echo",
|
|
"--tool-arg",
|
|
"message=test",
|
|
"--tool-metadata",
|
|
"invalid_format_no_equals",
|
|
]);
|
|
|
|
expectCliFailure(result);
|
|
});
|
|
});
|
|
|
|
describe("Metadata Impact", () => {
|
|
it("should handle tool-specific metadata precedence over general", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createEchoTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/call",
|
|
"--tool-name",
|
|
"echo",
|
|
"--tool-arg",
|
|
"message=precedence test",
|
|
"--metadata",
|
|
"client=general-client",
|
|
"--tool-metadata",
|
|
"client=tool-specific-client",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
|
|
// Validate tool-specific metadata overrides general
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolCallRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/call",
|
|
);
|
|
expect(toolCallRequest).toBeDefined();
|
|
expect(toolCallRequest?.metadata).toEqual({
|
|
client: "tool-specific-client",
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
|
|
it("should work with resources methods", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
resources: [
|
|
{
|
|
uri: "test://resource",
|
|
name: "test-resource",
|
|
text: "test content",
|
|
},
|
|
],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"resources/list",
|
|
"--metadata",
|
|
"resource_client=test-resource-client",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
|
|
// Validate metadata was sent
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const resourcesListRequest = recordedRequests.find(
|
|
(r) => r.method === "resources/list",
|
|
);
|
|
expect(resourcesListRequest).toBeDefined();
|
|
expect(resourcesListRequest?.metadata).toEqual({
|
|
resource_client: "test-resource-client",
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
|
|
it("should work with prompts methods", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
prompts: [
|
|
{
|
|
name: "test-prompt",
|
|
description: "A test prompt",
|
|
},
|
|
],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"prompts/get",
|
|
"--prompt-name",
|
|
"test-prompt",
|
|
"--metadata",
|
|
"prompt_client=test-prompt-client",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
|
|
// Validate metadata was sent
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const getPromptRequest = recordedRequests.find(
|
|
(r) => r.method === "prompts/get",
|
|
);
|
|
expect(getPromptRequest).toBeDefined();
|
|
expect(getPromptRequest?.metadata).toEqual({
|
|
prompt_client: "test-prompt-client",
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("Metadata Validation", () => {
|
|
it("should handle special characters in keys", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createEchoTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/call",
|
|
"--tool-name",
|
|
"echo",
|
|
"--tool-arg",
|
|
"message=special keys test",
|
|
"--metadata",
|
|
"key-with-dashes=value1",
|
|
"key_with_underscores=value2",
|
|
"key.with.dots=value3",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
|
|
// Validate special characters in keys are preserved
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolCallRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/call",
|
|
);
|
|
expect(toolCallRequest).toBeDefined();
|
|
expect(toolCallRequest?.metadata).toEqual({
|
|
"key-with-dashes": "value1",
|
|
key_with_underscores: "value2",
|
|
"key.with.dots": "value3",
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("Metadata Integration", () => {
|
|
it("should work with all MCP methods", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createEchoTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/list",
|
|
"--metadata",
|
|
"integration_test=true",
|
|
"test_phase=all_methods",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
|
|
// Validate metadata was sent
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolsListRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/list",
|
|
);
|
|
expect(toolsListRequest).toBeDefined();
|
|
expect(toolsListRequest?.metadata).toEqual({
|
|
integration_test: "true",
|
|
test_phase: "all_methods",
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
|
|
it("should handle complex metadata scenario", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createEchoTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/call",
|
|
"--tool-name",
|
|
"echo",
|
|
"--tool-arg",
|
|
"message=complex test",
|
|
"--metadata",
|
|
"session_id=12345",
|
|
"user_id=67890",
|
|
"timestamp=2024-01-01T00:00:00Z",
|
|
"request_id=req-abc-123",
|
|
"--tool-metadata",
|
|
"tool_session=session-xyz-789",
|
|
"execution_context=test",
|
|
"priority=high",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
|
|
// Validate complex metadata merging
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolCallRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/call",
|
|
);
|
|
expect(toolCallRequest).toBeDefined();
|
|
expect(toolCallRequest?.metadata).toEqual({
|
|
session_id: "12345",
|
|
user_id: "67890",
|
|
timestamp: "2024-01-01T00:00:00Z",
|
|
request_id: "req-abc-123",
|
|
tool_session: "session-xyz-789",
|
|
execution_context: "test",
|
|
priority: "high",
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
|
|
it("should handle metadata parsing validation", async () => {
|
|
const server = createTestServerHttp({
|
|
serverInfo: createTestServerInfo(),
|
|
tools: [createEchoTool()],
|
|
});
|
|
|
|
try {
|
|
await server.start("http");
|
|
const serverUrl = `${server.getUrl()}/mcp`;
|
|
|
|
const result = await runCli([
|
|
serverUrl,
|
|
"--cli",
|
|
"--method",
|
|
"tools/call",
|
|
"--tool-name",
|
|
"echo",
|
|
"--tool-arg",
|
|
"message=parsing validation test",
|
|
"--metadata",
|
|
"valid_key=valid_value",
|
|
"numeric_key=123",
|
|
"boolean_key=true",
|
|
'json_key=\'{"test":"value"}\'',
|
|
"special_key=!@#$%^&*()",
|
|
"unicode_key=🚀🎉✨",
|
|
"--transport",
|
|
"http",
|
|
]);
|
|
|
|
expectCliSuccess(result);
|
|
|
|
// Validate all value types are sent as strings
|
|
// Note: The CLI parses metadata values, so single-quoted JSON strings
|
|
// are preserved with their quotes
|
|
const recordedRequests = server.getRecordedRequests();
|
|
const toolCallRequest = recordedRequests.find(
|
|
(r) => r.method === "tools/call",
|
|
);
|
|
expect(toolCallRequest).toBeDefined();
|
|
expect(toolCallRequest?.metadata).toEqual({
|
|
valid_key: "valid_value",
|
|
numeric_key: "123",
|
|
boolean_key: "true",
|
|
json_key: '\'{"test":"value"}\'', // Single quotes are preserved
|
|
special_key: "!@#$%^&*()",
|
|
unicode_key: "🚀🎉✨",
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
}
|
|
});
|
|
});
|
|
});
|