diff --git a/cli/scripts/cli-metadata-tests.js b/cli/scripts/cli-metadata-tests.js new file mode 100755 index 000000000..0bc664d2c --- /dev/null +++ b/cli/scripts/cli-metadata-tests.js @@ -0,0 +1,676 @@ +#!/usr/bin/env node + +// Colors for output +const colors = { + GREEN: "\x1b[32m", + YELLOW: "\x1b[33m", + RED: "\x1b[31m", + BLUE: "\x1b[34m", + ORANGE: "\x1b[33m", + NC: "\x1b[0m", // No Color +}; + +import fs from "fs"; +import path from "path"; +import { spawn } from "child_process"; +import os from "os"; +import { fileURLToPath } from "url"; + +// Get directory paths with ESM compatibility +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Track test results +let PASSED_TESTS = 0; +let FAILED_TESTS = 0; +let SKIPPED_TESTS = 0; +let TOTAL_TESTS = 0; + +console.log( + `${colors.YELLOW}=== MCP Inspector CLI Metadata Tests ===${colors.NC}`, +); +console.log( + `${colors.BLUE}This script tests the MCP Inspector CLI's metadata functionality:${colors.NC}`, +); +console.log( + `${colors.BLUE}- General metadata with --metadata option${colors.NC}`, +); +console.log( + `${colors.BLUE}- Tool-specific metadata with --tool-metadata option${colors.NC}`, +); +console.log( + `${colors.BLUE}- Metadata parsing with various data types${colors.NC}`, +); +console.log( + `${colors.BLUE}- Metadata merging (tool-specific overrides general)${colors.NC}`, +); +console.log( + `${colors.BLUE}- Metadata evaluation in different MCP methods${colors.NC}`, +); +console.log(`\n`); + +// Get directory paths +const SCRIPTS_DIR = __dirname; +const PROJECT_ROOT = path.join(SCRIPTS_DIR, "../../"); +const BUILD_DIR = path.resolve(SCRIPTS_DIR, "../build"); + +// Define the test server command using npx +const TEST_CMD = "npx"; +const TEST_ARGS = ["@modelcontextprotocol/server-everything"]; + +// Create output directory for test results +const OUTPUT_DIR = path.join(SCRIPTS_DIR, "metadata-test-output"); +if (!fs.existsSync(OUTPUT_DIR)) { + fs.mkdirSync(OUTPUT_DIR, { recursive: true }); +} + +// Create a temporary directory for test files +const TEMP_DIR = path.join(os.tmpdir(), "mcp-inspector-metadata-tests"); +fs.mkdirSync(TEMP_DIR, { recursive: true }); + +// Track servers for cleanup +let runningServers = []; + +process.on("exit", () => { + try { + fs.rmSync(TEMP_DIR, { recursive: true, force: true }); + } catch (err) { + console.error( + `${colors.RED}Failed to remove temp directory: ${err.message}${colors.NC}`, + ); + } + + runningServers.forEach((server) => { + try { + process.kill(-server.pid); + } catch (e) {} + }); +}); + +process.on("SIGINT", () => { + runningServers.forEach((server) => { + try { + process.kill(-server.pid); + } catch (e) {} + }); + process.exit(1); +}); + +// Function to run a basic test +async function runBasicTest(testName, ...args) { + const outputFile = path.join( + OUTPUT_DIR, + `${testName.replace(/\//g, "_")}.log`, + ); + + console.log(`\n${colors.YELLOW}Testing: ${testName}${colors.NC}`); + TOTAL_TESTS++; + + // Run the command and capture output + console.log( + `${colors.BLUE}Command: node ${BUILD_DIR}/cli.js ${args.join(" ")}${colors.NC}`, + ); + + try { + // Create a write stream for the output file + const outputStream = fs.createWriteStream(outputFile); + + // Spawn the process + return new Promise((resolve) => { + const child = spawn("node", [path.join(BUILD_DIR, "cli.js"), ...args], { + stdio: ["ignore", "pipe", "pipe"], + }); + + const timeout = setTimeout(() => { + console.log(`${colors.YELLOW}Test timed out: ${testName}${colors.NC}`); + child.kill(); + }, 15000); + + // Pipe stdout and stderr to the output file + child.stdout.pipe(outputStream); + child.stderr.pipe(outputStream); + + // Also capture output for display + let output = ""; + child.stdout.on("data", (data) => { + output += data.toString(); + }); + child.stderr.on("data", (data) => { + output += data.toString(); + }); + + child.on("close", (code) => { + clearTimeout(timeout); + outputStream.end(); + + // Check if we got valid JSON output (indicating success) even if process didn't exit cleanly + const hasValidJsonOutput = + output.includes('"tools"') || + output.includes('"resources"') || + output.includes('"prompts"') || + output.includes('"content"') || + output.includes('"messages"') || + output.includes('"contents"'); + + if (code === 0 || hasValidJsonOutput) { + console.log(`${colors.GREEN}✓ Test passed: ${testName}${colors.NC}`); + console.log(`${colors.BLUE}First few lines of output:${colors.NC}`); + const firstFewLines = output + .split("\n") + .slice(0, 5) + .map((line) => ` ${line}`) + .join("\n"); + console.log(firstFewLines); + PASSED_TESTS++; + resolve(true); + } else { + console.log(`${colors.RED}✗ Test failed: ${testName}${colors.NC}`); + console.log(`${colors.RED}Error output:${colors.NC}`); + console.log( + output + .split("\n") + .map((line) => ` ${line}`) + .join("\n"), + ); + FAILED_TESTS++; + + // Stop after any error is encountered + console.log( + `${colors.YELLOW}Stopping tests due to error. Please validate and fix before continuing.${colors.NC}`, + ); + process.exit(1); + } + }); + }); + } catch (error) { + console.error( + `${colors.RED}Error running test: ${error.message}${colors.NC}`, + ); + FAILED_TESTS++; + process.exit(1); + } +} + +// Function to run an error test (expected to fail) +async function runErrorTest(testName, ...args) { + const outputFile = path.join( + OUTPUT_DIR, + `${testName.replace(/\//g, "_")}.log`, + ); + + console.log(`\n${colors.YELLOW}Testing error case: ${testName}${colors.NC}`); + TOTAL_TESTS++; + + // Run the command and capture output + console.log( + `${colors.BLUE}Command: node ${BUILD_DIR}/cli.js ${args.join(" ")}${colors.NC}`, + ); + + try { + // Create a write stream for the output file + const outputStream = fs.createWriteStream(outputFile); + + // Spawn the process + return new Promise((resolve) => { + const child = spawn("node", [path.join(BUILD_DIR, "cli.js"), ...args], { + stdio: ["ignore", "pipe", "pipe"], + }); + + const timeout = setTimeout(() => { + console.log( + `${colors.YELLOW}Error test timed out: ${testName}${colors.NC}`, + ); + child.kill(); + }, 15000); + + // Pipe stdout and stderr to the output file + child.stdout.pipe(outputStream); + child.stderr.pipe(outputStream); + + // Also capture output for display + let output = ""; + child.stdout.on("data", (data) => { + output += data.toString(); + }); + child.stderr.on("data", (data) => { + output += data.toString(); + }); + + child.on("close", (code) => { + clearTimeout(timeout); + outputStream.end(); + + // For error tests, we expect a non-zero exit code + if (code !== 0) { + console.log( + `${colors.GREEN}✓ Error test passed: ${testName}${colors.NC}`, + ); + console.log(`${colors.BLUE}Error output (expected):${colors.NC}`); + const firstFewLines = output + .split("\n") + .slice(0, 5) + .map((line) => ` ${line}`) + .join("\n"); + console.log(firstFewLines); + PASSED_TESTS++; + resolve(true); + } else { + console.log( + `${colors.RED}✗ Error test failed: ${testName} (expected error but got success)${colors.NC}`, + ); + console.log(`${colors.RED}Output:${colors.NC}`); + console.log( + output + .split("\n") + .map((line) => ` ${line}`) + .join("\n"), + ); + FAILED_TESTS++; + + // Stop after any error is encountered + console.log( + `${colors.YELLOW}Stopping tests due to error. Please validate and fix before continuing.${colors.NC}`, + ); + process.exit(1); + } + }); + }); + } catch (error) { + console.error( + `${colors.RED}Error running test: ${error.message}${colors.NC}`, + ); + FAILED_TESTS++; + process.exit(1); + } +} + +// Run all tests +async function runTests() { + console.log( + `\n${colors.YELLOW}=== Running General Metadata Tests ===${colors.NC}`, + ); + + // Test 1: General metadata with tools/list + await runBasicTest( + "metadata_tools_list", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "tools/list", + "--metadata", + "client=test-client", + ); + + // Test 2: General metadata with resources/list + await runBasicTest( + "metadata_resources_list", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "resources/list", + "--metadata", + "client=test-client", + ); + + // Test 3: General metadata with prompts/list + await runBasicTest( + "metadata_prompts_list", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "prompts/list", + "--metadata", + "client=test-client", + ); + + // Test 4: General metadata with resources/read + await runBasicTest( + "metadata_resources_read", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "resources/read", + "--uri", + "test://static/resource/1", + "--metadata", + "client=test-client", + ); + + // Test 5: General metadata with prompts/get + await runBasicTest( + "metadata_prompts_get", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "prompts/get", + "--prompt-name", + "simple_prompt", + "--metadata", + "client=test-client", + ); + + console.log( + `\n${colors.YELLOW}=== Running Tool-Specific Metadata Tests ===${colors.NC}`, + ); + + // Test 6: Tool-specific metadata with tools/call + await runBasicTest( + "metadata_tools_call_tool_meta", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "tools/call", + "--tool-name", + "echo", + "--tool-arg", + "message=hello world", + "--tool-metadata", + "client=test-client", + ); + + // Test 7: Tool-specific metadata with complex tool + await runBasicTest( + "metadata_tools_call_complex_tool_meta", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "tools/call", + "--tool-name", + "add", + "--tool-arg", + "a=10", + "b=20", + "--tool-metadata", + "client=test-client", + ); + + console.log( + `\n${colors.YELLOW}=== Running Metadata Merging Tests ===${colors.NC}`, + ); + + // Test 8: General metadata + tool-specific metadata (tool-specific should override) + await runBasicTest( + "metadata_merging_general_and_tool", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "tools/call", + "--tool-name", + "echo", + "--tool-arg", + "message=hello world", + "--metadata", + "client=general-client", + "--tool-metadata", + "client=test-client", + ); + + console.log( + `\n${colors.YELLOW}=== Running Metadata Parsing Tests ===${colors.NC}`, + ); + + // Test 10: Metadata with numeric values (should be converted to strings) + await runBasicTest( + "metadata_parsing_numbers", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "tools/list", + "--metadata", + "integer_value=42", + "decimal_value=3.14159", + "negative_value=-10", + ); + + // Test 11: Metadata with JSON values (should be converted to strings) + await runBasicTest( + "metadata_parsing_json", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "tools/list", + "--metadata", + 'json_object="{\\"key\\":\\"value\\"}"', + 'json_array="[1,2,3]"', + 'json_string="\\"quoted\\""', + ); + + // Test 12: Metadata with special characters + await runBasicTest( + "metadata_parsing_special_chars", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "tools/list", + "--metadata", + "unicode=🚀🎉✨", + "special_chars=!@#$%^&*()", + "spaces=hello world with spaces", + ); + + console.log( + `\n${colors.YELLOW}=== Running Metadata Edge Cases ===${colors.NC}`, + ); + + // Test 13: Single metadata entry + await runBasicTest( + "metadata_single_entry", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "tools/list", + "--metadata", + "single_key=single_value", + ); + + // Test 14: Many metadata entries + await runBasicTest( + "metadata_many_entries", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "tools/list", + "--metadata", + "key1=value1", + "key2=value2", + "key3=value3", + "key4=value4", + "key5=value5", + ); + + console.log( + `\n${colors.YELLOW}=== Running Metadata Error Cases ===${colors.NC}`, + ); + + // Test 15: Invalid metadata format (missing equals) + await runErrorTest( + "metadata_error_invalid_format", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "tools/list", + "--metadata", + "invalid_format_no_equals", + ); + + // Test 16: Invalid tool-meta format (missing equals) + await runErrorTest( + "metadata_error_invalid_tool_meta_format", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "tools/call", + "--tool-name", + "echo", + "--tool-arg", + "message=test", + "--tool-metadata", + "invalid_format_no_equals", + ); + + console.log( + `\n${colors.YELLOW}=== Running Metadata Impact Tests ===${colors.NC}`, + ); + + // Test 17: Test tool-specific metadata vs general metadata precedence + await runBasicTest( + "metadata_precedence_tool_overrides_general", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "tools/call", + "--tool-name", + "echo", + "--tool-arg", + "message=precedence test", + "--metadata", + "client=general-client", + "--tool-metadata", + "client=tool-specific-client", + ); + + // Test 18: Test metadata with resources methods + await runBasicTest( + "metadata_resources_methods", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "resources/list", + "--metadata", + "resource_client=test-resource-client", + ); + + // Test 19: Test metadata with prompts methods + await runBasicTest( + "metadata_prompts_methods", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "prompts/get", + "--prompt-name", + "simple_prompt", + "--metadata", + "prompt_client=test-prompt-client", + ); + + console.log( + `\n${colors.YELLOW}=== Running Metadata Validation Tests ===${colors.NC}`, + ); + + // Test 20: Test metadata with special characters in keys + await runBasicTest( + "metadata_special_key_characters", + TEST_CMD, + ...TEST_ARGS, + "--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", + ); + + console.log( + `\n${colors.YELLOW}=== Running Metadata Integration Tests ===${colors.NC}`, + ); + + // Test 21: Metadata with all MCP methods + await runBasicTest( + "metadata_integration_all_methods", + TEST_CMD, + ...TEST_ARGS, + "--cli", + "--method", + "tools/list", + "--metadata", + "integration_test=true", + "test_phase=all_methods", + ); + + // Test 22: Complex metadata scenario + await runBasicTest( + "metadata_complex_scenario", + TEST_CMD, + ...TEST_ARGS, + "--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", + ); + + // Test 23: Metadata parsing validation test + await runBasicTest( + "metadata_parsing_validation", + TEST_CMD, + ...TEST_ARGS, + "--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=🚀🎉✨", + ); + + // Print test summary + console.log(`\n${colors.YELLOW}=== Test Summary ===${colors.NC}`); + console.log(`${colors.GREEN}Passed: ${PASSED_TESTS}${colors.NC}`); + console.log(`${colors.RED}Failed: ${FAILED_TESTS}${colors.NC}`); + console.log(`${colors.ORANGE}Skipped: ${SKIPPED_TESTS}${colors.NC}`); + console.log(`Total: ${TOTAL_TESTS}`); + console.log( + `${colors.BLUE}Detailed logs saved to: ${OUTPUT_DIR}${colors.NC}`, + ); + + console.log(`\n${colors.GREEN}All metadata tests completed!${colors.NC}`); +} + +// Run all tests +runTests().catch((error) => { + console.error( + `${colors.RED}Tests failed with error: ${error.message}${colors.NC}`, + ); + process.exit(1); +}); diff --git a/cli/scripts/metadata-test-output/metadata_complex_scenario.log b/cli/scripts/metadata-test-output/metadata_complex_scenario.log new file mode 100644 index 000000000..bcfc10b1b --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_complex_scenario.log @@ -0,0 +1,8 @@ +{ + "content": [ + { + "type": "text", + "text": "Echo: complex test" + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_error_invalid_format.log b/cli/scripts/metadata-test-output/metadata_error_invalid_format.log new file mode 100644 index 000000000..5a18c1758 --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_error_invalid_format.log @@ -0,0 +1,3 @@ +Invalid parameter format: invalid_format_no_equals. Use key=value format. + +Failed with exit code: 1 diff --git a/cli/scripts/metadata-test-output/metadata_error_invalid_tool_meta_format.log b/cli/scripts/metadata-test-output/metadata_error_invalid_tool_meta_format.log new file mode 100644 index 000000000..5a18c1758 --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_error_invalid_tool_meta_format.log @@ -0,0 +1,3 @@ +Invalid parameter format: invalid_format_no_equals. Use key=value format. + +Failed with exit code: 1 diff --git a/cli/scripts/metadata-test-output/metadata_integration_all_methods.log b/cli/scripts/metadata-test-output/metadata_integration_all_methods.log new file mode 100644 index 000000000..114d6da4b --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_integration_all_methods.log @@ -0,0 +1,218 @@ +{ + "tools": [ + { + "name": "echo", + "description": "Echoes back the input", + "inputSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Message to echo" + } + }, + "required": [ + "message" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "add", + "description": "Adds two numbers", + "inputSchema": { + "type": "object", + "properties": { + "a": { + "type": "number", + "description": "First number" + }, + "b": { + "type": "number", + "description": "Second number" + } + }, + "required": [ + "a", + "b" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "longRunningOperation", + "description": "Demonstrates a long running operation with progress updates", + "inputSchema": { + "type": "object", + "properties": { + "duration": { + "type": "number", + "default": 10, + "description": "Duration of the operation in seconds" + }, + "steps": { + "type": "number", + "default": 5, + "description": "Number of steps in the operation" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "printEnv", + "description": "Prints all environment variables, helpful for debugging MCP server configuration", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "sampleLLM", + "description": "Samples from an LLM using MCP's sampling feature", + "inputSchema": { + "type": "object", + "properties": { + "prompt": { + "type": "string", + "description": "The prompt to send to the LLM" + }, + "maxTokens": { + "type": "number", + "default": 100, + "description": "Maximum number of tokens to generate" + } + }, + "required": [ + "prompt" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getTinyImage", + "description": "Returns the MCP_TINY_IMAGE", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "annotatedMessage", + "description": "Demonstrates how annotations can be used to provide metadata about content", + "inputSchema": { + "type": "object", + "properties": { + "messageType": { + "type": "string", + "enum": [ + "error", + "success", + "debug" + ], + "description": "Type of message to demonstrate different annotation patterns" + }, + "includeImage": { + "type": "boolean", + "default": false, + "description": "Whether to include an example image" + } + }, + "required": [ + "messageType" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceReference", + "description": "Returns a resource reference that can be used by MCP clients", + "inputSchema": { + "type": "object", + "properties": { + "resourceId": { + "type": "number", + "minimum": 1, + "maximum": 100, + "description": "ID of the resource to reference (1-100)" + } + }, + "required": [ + "resourceId" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceLinks", + "description": "Returns multiple resource links that reference different types of resources", + "inputSchema": { + "type": "object", + "properties": { + "count": { + "type": "number", + "minimum": 1, + "maximum": 10, + "default": 3, + "description": "Number of resource links to return (1-10)" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "structuredContent", + "description": "Returns structured content along with an output schema for client data validation", + "inputSchema": { + "type": "object", + "properties": { + "location": { + "type": "string", + "minLength": 1, + "description": "City name or zip code" + } + }, + "required": [ + "location" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "outputSchema": { + "type": "object", + "properties": { + "temperature": { + "type": "number", + "description": "Temperature in celsius" + }, + "conditions": { + "type": "string", + "description": "Weather conditions description" + }, + "humidity": { + "type": "number", + "description": "Humidity percentage" + } + }, + "required": [ + "temperature", + "conditions", + "humidity" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_many_entries.log b/cli/scripts/metadata-test-output/metadata_many_entries.log new file mode 100644 index 000000000..114d6da4b --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_many_entries.log @@ -0,0 +1,218 @@ +{ + "tools": [ + { + "name": "echo", + "description": "Echoes back the input", + "inputSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Message to echo" + } + }, + "required": [ + "message" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "add", + "description": "Adds two numbers", + "inputSchema": { + "type": "object", + "properties": { + "a": { + "type": "number", + "description": "First number" + }, + "b": { + "type": "number", + "description": "Second number" + } + }, + "required": [ + "a", + "b" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "longRunningOperation", + "description": "Demonstrates a long running operation with progress updates", + "inputSchema": { + "type": "object", + "properties": { + "duration": { + "type": "number", + "default": 10, + "description": "Duration of the operation in seconds" + }, + "steps": { + "type": "number", + "default": 5, + "description": "Number of steps in the operation" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "printEnv", + "description": "Prints all environment variables, helpful for debugging MCP server configuration", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "sampleLLM", + "description": "Samples from an LLM using MCP's sampling feature", + "inputSchema": { + "type": "object", + "properties": { + "prompt": { + "type": "string", + "description": "The prompt to send to the LLM" + }, + "maxTokens": { + "type": "number", + "default": 100, + "description": "Maximum number of tokens to generate" + } + }, + "required": [ + "prompt" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getTinyImage", + "description": "Returns the MCP_TINY_IMAGE", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "annotatedMessage", + "description": "Demonstrates how annotations can be used to provide metadata about content", + "inputSchema": { + "type": "object", + "properties": { + "messageType": { + "type": "string", + "enum": [ + "error", + "success", + "debug" + ], + "description": "Type of message to demonstrate different annotation patterns" + }, + "includeImage": { + "type": "boolean", + "default": false, + "description": "Whether to include an example image" + } + }, + "required": [ + "messageType" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceReference", + "description": "Returns a resource reference that can be used by MCP clients", + "inputSchema": { + "type": "object", + "properties": { + "resourceId": { + "type": "number", + "minimum": 1, + "maximum": 100, + "description": "ID of the resource to reference (1-100)" + } + }, + "required": [ + "resourceId" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceLinks", + "description": "Returns multiple resource links that reference different types of resources", + "inputSchema": { + "type": "object", + "properties": { + "count": { + "type": "number", + "minimum": 1, + "maximum": 10, + "default": 3, + "description": "Number of resource links to return (1-10)" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "structuredContent", + "description": "Returns structured content along with an output schema for client data validation", + "inputSchema": { + "type": "object", + "properties": { + "location": { + "type": "string", + "minLength": 1, + "description": "City name or zip code" + } + }, + "required": [ + "location" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "outputSchema": { + "type": "object", + "properties": { + "temperature": { + "type": "number", + "description": "Temperature in celsius" + }, + "conditions": { + "type": "string", + "description": "Weather conditions description" + }, + "humidity": { + "type": "number", + "description": "Humidity percentage" + } + }, + "required": [ + "temperature", + "conditions", + "humidity" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_merging_general_and_tool.log b/cli/scripts/metadata-test-output/metadata_merging_general_and_tool.log new file mode 100644 index 000000000..145c3ac0b --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_merging_general_and_tool.log @@ -0,0 +1,8 @@ +{ + "content": [ + { + "type": "text", + "text": "Echo: hello world" + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_parsing_json.log b/cli/scripts/metadata-test-output/metadata_parsing_json.log new file mode 100644 index 000000000..114d6da4b --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_parsing_json.log @@ -0,0 +1,218 @@ +{ + "tools": [ + { + "name": "echo", + "description": "Echoes back the input", + "inputSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Message to echo" + } + }, + "required": [ + "message" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "add", + "description": "Adds two numbers", + "inputSchema": { + "type": "object", + "properties": { + "a": { + "type": "number", + "description": "First number" + }, + "b": { + "type": "number", + "description": "Second number" + } + }, + "required": [ + "a", + "b" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "longRunningOperation", + "description": "Demonstrates a long running operation with progress updates", + "inputSchema": { + "type": "object", + "properties": { + "duration": { + "type": "number", + "default": 10, + "description": "Duration of the operation in seconds" + }, + "steps": { + "type": "number", + "default": 5, + "description": "Number of steps in the operation" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "printEnv", + "description": "Prints all environment variables, helpful for debugging MCP server configuration", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "sampleLLM", + "description": "Samples from an LLM using MCP's sampling feature", + "inputSchema": { + "type": "object", + "properties": { + "prompt": { + "type": "string", + "description": "The prompt to send to the LLM" + }, + "maxTokens": { + "type": "number", + "default": 100, + "description": "Maximum number of tokens to generate" + } + }, + "required": [ + "prompt" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getTinyImage", + "description": "Returns the MCP_TINY_IMAGE", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "annotatedMessage", + "description": "Demonstrates how annotations can be used to provide metadata about content", + "inputSchema": { + "type": "object", + "properties": { + "messageType": { + "type": "string", + "enum": [ + "error", + "success", + "debug" + ], + "description": "Type of message to demonstrate different annotation patterns" + }, + "includeImage": { + "type": "boolean", + "default": false, + "description": "Whether to include an example image" + } + }, + "required": [ + "messageType" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceReference", + "description": "Returns a resource reference that can be used by MCP clients", + "inputSchema": { + "type": "object", + "properties": { + "resourceId": { + "type": "number", + "minimum": 1, + "maximum": 100, + "description": "ID of the resource to reference (1-100)" + } + }, + "required": [ + "resourceId" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceLinks", + "description": "Returns multiple resource links that reference different types of resources", + "inputSchema": { + "type": "object", + "properties": { + "count": { + "type": "number", + "minimum": 1, + "maximum": 10, + "default": 3, + "description": "Number of resource links to return (1-10)" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "structuredContent", + "description": "Returns structured content along with an output schema for client data validation", + "inputSchema": { + "type": "object", + "properties": { + "location": { + "type": "string", + "minLength": 1, + "description": "City name or zip code" + } + }, + "required": [ + "location" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "outputSchema": { + "type": "object", + "properties": { + "temperature": { + "type": "number", + "description": "Temperature in celsius" + }, + "conditions": { + "type": "string", + "description": "Weather conditions description" + }, + "humidity": { + "type": "number", + "description": "Humidity percentage" + } + }, + "required": [ + "temperature", + "conditions", + "humidity" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_parsing_numbers.log b/cli/scripts/metadata-test-output/metadata_parsing_numbers.log new file mode 100644 index 000000000..114d6da4b --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_parsing_numbers.log @@ -0,0 +1,218 @@ +{ + "tools": [ + { + "name": "echo", + "description": "Echoes back the input", + "inputSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Message to echo" + } + }, + "required": [ + "message" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "add", + "description": "Adds two numbers", + "inputSchema": { + "type": "object", + "properties": { + "a": { + "type": "number", + "description": "First number" + }, + "b": { + "type": "number", + "description": "Second number" + } + }, + "required": [ + "a", + "b" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "longRunningOperation", + "description": "Demonstrates a long running operation with progress updates", + "inputSchema": { + "type": "object", + "properties": { + "duration": { + "type": "number", + "default": 10, + "description": "Duration of the operation in seconds" + }, + "steps": { + "type": "number", + "default": 5, + "description": "Number of steps in the operation" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "printEnv", + "description": "Prints all environment variables, helpful for debugging MCP server configuration", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "sampleLLM", + "description": "Samples from an LLM using MCP's sampling feature", + "inputSchema": { + "type": "object", + "properties": { + "prompt": { + "type": "string", + "description": "The prompt to send to the LLM" + }, + "maxTokens": { + "type": "number", + "default": 100, + "description": "Maximum number of tokens to generate" + } + }, + "required": [ + "prompt" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getTinyImage", + "description": "Returns the MCP_TINY_IMAGE", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "annotatedMessage", + "description": "Demonstrates how annotations can be used to provide metadata about content", + "inputSchema": { + "type": "object", + "properties": { + "messageType": { + "type": "string", + "enum": [ + "error", + "success", + "debug" + ], + "description": "Type of message to demonstrate different annotation patterns" + }, + "includeImage": { + "type": "boolean", + "default": false, + "description": "Whether to include an example image" + } + }, + "required": [ + "messageType" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceReference", + "description": "Returns a resource reference that can be used by MCP clients", + "inputSchema": { + "type": "object", + "properties": { + "resourceId": { + "type": "number", + "minimum": 1, + "maximum": 100, + "description": "ID of the resource to reference (1-100)" + } + }, + "required": [ + "resourceId" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceLinks", + "description": "Returns multiple resource links that reference different types of resources", + "inputSchema": { + "type": "object", + "properties": { + "count": { + "type": "number", + "minimum": 1, + "maximum": 10, + "default": 3, + "description": "Number of resource links to return (1-10)" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "structuredContent", + "description": "Returns structured content along with an output schema for client data validation", + "inputSchema": { + "type": "object", + "properties": { + "location": { + "type": "string", + "minLength": 1, + "description": "City name or zip code" + } + }, + "required": [ + "location" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "outputSchema": { + "type": "object", + "properties": { + "temperature": { + "type": "number", + "description": "Temperature in celsius" + }, + "conditions": { + "type": "string", + "description": "Weather conditions description" + }, + "humidity": { + "type": "number", + "description": "Humidity percentage" + } + }, + "required": [ + "temperature", + "conditions", + "humidity" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_parsing_special_chars.log b/cli/scripts/metadata-test-output/metadata_parsing_special_chars.log new file mode 100644 index 000000000..114d6da4b --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_parsing_special_chars.log @@ -0,0 +1,218 @@ +{ + "tools": [ + { + "name": "echo", + "description": "Echoes back the input", + "inputSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Message to echo" + } + }, + "required": [ + "message" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "add", + "description": "Adds two numbers", + "inputSchema": { + "type": "object", + "properties": { + "a": { + "type": "number", + "description": "First number" + }, + "b": { + "type": "number", + "description": "Second number" + } + }, + "required": [ + "a", + "b" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "longRunningOperation", + "description": "Demonstrates a long running operation with progress updates", + "inputSchema": { + "type": "object", + "properties": { + "duration": { + "type": "number", + "default": 10, + "description": "Duration of the operation in seconds" + }, + "steps": { + "type": "number", + "default": 5, + "description": "Number of steps in the operation" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "printEnv", + "description": "Prints all environment variables, helpful for debugging MCP server configuration", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "sampleLLM", + "description": "Samples from an LLM using MCP's sampling feature", + "inputSchema": { + "type": "object", + "properties": { + "prompt": { + "type": "string", + "description": "The prompt to send to the LLM" + }, + "maxTokens": { + "type": "number", + "default": 100, + "description": "Maximum number of tokens to generate" + } + }, + "required": [ + "prompt" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getTinyImage", + "description": "Returns the MCP_TINY_IMAGE", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "annotatedMessage", + "description": "Demonstrates how annotations can be used to provide metadata about content", + "inputSchema": { + "type": "object", + "properties": { + "messageType": { + "type": "string", + "enum": [ + "error", + "success", + "debug" + ], + "description": "Type of message to demonstrate different annotation patterns" + }, + "includeImage": { + "type": "boolean", + "default": false, + "description": "Whether to include an example image" + } + }, + "required": [ + "messageType" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceReference", + "description": "Returns a resource reference that can be used by MCP clients", + "inputSchema": { + "type": "object", + "properties": { + "resourceId": { + "type": "number", + "minimum": 1, + "maximum": 100, + "description": "ID of the resource to reference (1-100)" + } + }, + "required": [ + "resourceId" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceLinks", + "description": "Returns multiple resource links that reference different types of resources", + "inputSchema": { + "type": "object", + "properties": { + "count": { + "type": "number", + "minimum": 1, + "maximum": 10, + "default": 3, + "description": "Number of resource links to return (1-10)" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "structuredContent", + "description": "Returns structured content along with an output schema for client data validation", + "inputSchema": { + "type": "object", + "properties": { + "location": { + "type": "string", + "minLength": 1, + "description": "City name or zip code" + } + }, + "required": [ + "location" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "outputSchema": { + "type": "object", + "properties": { + "temperature": { + "type": "number", + "description": "Temperature in celsius" + }, + "conditions": { + "type": "string", + "description": "Weather conditions description" + }, + "humidity": { + "type": "number", + "description": "Humidity percentage" + } + }, + "required": [ + "temperature", + "conditions", + "humidity" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_parsing_validation.log b/cli/scripts/metadata-test-output/metadata_parsing_validation.log new file mode 100644 index 000000000..b2dd290aa --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_parsing_validation.log @@ -0,0 +1,8 @@ +{ + "content": [ + { + "type": "text", + "text": "Echo: parsing validation test" + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_precedence_tool_overrides_general.log b/cli/scripts/metadata-test-output/metadata_precedence_tool_overrides_general.log new file mode 100644 index 000000000..07c8f815f --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_precedence_tool_overrides_general.log @@ -0,0 +1,8 @@ +{ + "content": [ + { + "type": "text", + "text": "Echo: precedence test" + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_prompts_get.log b/cli/scripts/metadata-test-output/metadata_prompts_get.log new file mode 100644 index 000000000..bfeb04285 --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_prompts_get.log @@ -0,0 +1,11 @@ +{ + "messages": [ + { + "role": "user", + "content": { + "type": "text", + "text": "This is a simple prompt without arguments." + } + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_prompts_list.log b/cli/scripts/metadata-test-output/metadata_prompts_list.log new file mode 100644 index 000000000..6bad1c3f9 --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_prompts_list.log @@ -0,0 +1,35 @@ +{ + "prompts": [ + { + "name": "simple_prompt", + "description": "A prompt without arguments" + }, + { + "name": "complex_prompt", + "description": "A prompt with arguments", + "arguments": [ + { + "name": "temperature", + "description": "Temperature setting", + "required": true + }, + { + "name": "style", + "description": "Output style", + "required": false + } + ] + }, + { + "name": "resource_prompt", + "description": "A prompt that includes an embedded resource reference", + "arguments": [ + { + "name": "resourceId", + "description": "Resource ID to include (1-100)", + "required": true + } + ] + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_prompts_methods.log b/cli/scripts/metadata-test-output/metadata_prompts_methods.log new file mode 100644 index 000000000..bfeb04285 --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_prompts_methods.log @@ -0,0 +1,11 @@ +{ + "messages": [ + { + "role": "user", + "content": { + "type": "text", + "text": "This is a simple prompt without arguments." + } + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_resources_list.log b/cli/scripts/metadata-test-output/metadata_resources_list.log new file mode 100644 index 000000000..f0d23a358 --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_resources_list.log @@ -0,0 +1,65 @@ +{ + "nextCursor": "MTA=", + "resources": [ + { + "name": "Resource 1", + "uri": "test://static/resource/1", + "mimeType": "text/plain", + "text": "Resource 1: This is a plaintext resource" + }, + { + "name": "Resource 2", + "uri": "test://static/resource/2", + "mimeType": "application/octet-stream", + "blob": "UmVzb3VyY2UgMjogVGhpcyBpcyBhIGJhc2U2NCBibG9i" + }, + { + "name": "Resource 3", + "uri": "test://static/resource/3", + "mimeType": "text/plain", + "text": "Resource 3: This is a plaintext resource" + }, + { + "name": "Resource 4", + "uri": "test://static/resource/4", + "mimeType": "application/octet-stream", + "blob": "UmVzb3VyY2UgNDogVGhpcyBpcyBhIGJhc2U2NCBibG9i" + }, + { + "name": "Resource 5", + "uri": "test://static/resource/5", + "mimeType": "text/plain", + "text": "Resource 5: This is a plaintext resource" + }, + { + "name": "Resource 6", + "uri": "test://static/resource/6", + "mimeType": "application/octet-stream", + "blob": "UmVzb3VyY2UgNjogVGhpcyBpcyBhIGJhc2U2NCBibG9i" + }, + { + "name": "Resource 7", + "uri": "test://static/resource/7", + "mimeType": "text/plain", + "text": "Resource 7: This is a plaintext resource" + }, + { + "name": "Resource 8", + "uri": "test://static/resource/8", + "mimeType": "application/octet-stream", + "blob": "UmVzb3VyY2UgODogVGhpcyBpcyBhIGJhc2U2NCBibG9i" + }, + { + "name": "Resource 9", + "uri": "test://static/resource/9", + "mimeType": "text/plain", + "text": "Resource 9: This is a plaintext resource" + }, + { + "name": "Resource 10", + "uri": "test://static/resource/10", + "mimeType": "application/octet-stream", + "blob": "UmVzb3VyY2UgMTA6IFRoaXMgaXMgYSBiYXNlNjQgYmxvYg==" + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_resources_methods.log b/cli/scripts/metadata-test-output/metadata_resources_methods.log new file mode 100644 index 000000000..f0d23a358 --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_resources_methods.log @@ -0,0 +1,65 @@ +{ + "nextCursor": "MTA=", + "resources": [ + { + "name": "Resource 1", + "uri": "test://static/resource/1", + "mimeType": "text/plain", + "text": "Resource 1: This is a plaintext resource" + }, + { + "name": "Resource 2", + "uri": "test://static/resource/2", + "mimeType": "application/octet-stream", + "blob": "UmVzb3VyY2UgMjogVGhpcyBpcyBhIGJhc2U2NCBibG9i" + }, + { + "name": "Resource 3", + "uri": "test://static/resource/3", + "mimeType": "text/plain", + "text": "Resource 3: This is a plaintext resource" + }, + { + "name": "Resource 4", + "uri": "test://static/resource/4", + "mimeType": "application/octet-stream", + "blob": "UmVzb3VyY2UgNDogVGhpcyBpcyBhIGJhc2U2NCBibG9i" + }, + { + "name": "Resource 5", + "uri": "test://static/resource/5", + "mimeType": "text/plain", + "text": "Resource 5: This is a plaintext resource" + }, + { + "name": "Resource 6", + "uri": "test://static/resource/6", + "mimeType": "application/octet-stream", + "blob": "UmVzb3VyY2UgNjogVGhpcyBpcyBhIGJhc2U2NCBibG9i" + }, + { + "name": "Resource 7", + "uri": "test://static/resource/7", + "mimeType": "text/plain", + "text": "Resource 7: This is a plaintext resource" + }, + { + "name": "Resource 8", + "uri": "test://static/resource/8", + "mimeType": "application/octet-stream", + "blob": "UmVzb3VyY2UgODogVGhpcyBpcyBhIGJhc2U2NCBibG9i" + }, + { + "name": "Resource 9", + "uri": "test://static/resource/9", + "mimeType": "text/plain", + "text": "Resource 9: This is a plaintext resource" + }, + { + "name": "Resource 10", + "uri": "test://static/resource/10", + "mimeType": "application/octet-stream", + "blob": "UmVzb3VyY2UgMTA6IFRoaXMgaXMgYSBiYXNlNjQgYmxvYg==" + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_resources_read.log b/cli/scripts/metadata-test-output/metadata_resources_read.log new file mode 100644 index 000000000..9b184e70e --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_resources_read.log @@ -0,0 +1,10 @@ +{ + "contents": [ + { + "uri": "test://static/resource/1", + "mimeType": "text/plain", + "text": "Resource 1: This is a plaintext resource", + "name": "Resource 1" + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_single_entry.log b/cli/scripts/metadata-test-output/metadata_single_entry.log new file mode 100644 index 000000000..114d6da4b --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_single_entry.log @@ -0,0 +1,218 @@ +{ + "tools": [ + { + "name": "echo", + "description": "Echoes back the input", + "inputSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Message to echo" + } + }, + "required": [ + "message" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "add", + "description": "Adds two numbers", + "inputSchema": { + "type": "object", + "properties": { + "a": { + "type": "number", + "description": "First number" + }, + "b": { + "type": "number", + "description": "Second number" + } + }, + "required": [ + "a", + "b" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "longRunningOperation", + "description": "Demonstrates a long running operation with progress updates", + "inputSchema": { + "type": "object", + "properties": { + "duration": { + "type": "number", + "default": 10, + "description": "Duration of the operation in seconds" + }, + "steps": { + "type": "number", + "default": 5, + "description": "Number of steps in the operation" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "printEnv", + "description": "Prints all environment variables, helpful for debugging MCP server configuration", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "sampleLLM", + "description": "Samples from an LLM using MCP's sampling feature", + "inputSchema": { + "type": "object", + "properties": { + "prompt": { + "type": "string", + "description": "The prompt to send to the LLM" + }, + "maxTokens": { + "type": "number", + "default": 100, + "description": "Maximum number of tokens to generate" + } + }, + "required": [ + "prompt" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getTinyImage", + "description": "Returns the MCP_TINY_IMAGE", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "annotatedMessage", + "description": "Demonstrates how annotations can be used to provide metadata about content", + "inputSchema": { + "type": "object", + "properties": { + "messageType": { + "type": "string", + "enum": [ + "error", + "success", + "debug" + ], + "description": "Type of message to demonstrate different annotation patterns" + }, + "includeImage": { + "type": "boolean", + "default": false, + "description": "Whether to include an example image" + } + }, + "required": [ + "messageType" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceReference", + "description": "Returns a resource reference that can be used by MCP clients", + "inputSchema": { + "type": "object", + "properties": { + "resourceId": { + "type": "number", + "minimum": 1, + "maximum": 100, + "description": "ID of the resource to reference (1-100)" + } + }, + "required": [ + "resourceId" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceLinks", + "description": "Returns multiple resource links that reference different types of resources", + "inputSchema": { + "type": "object", + "properties": { + "count": { + "type": "number", + "minimum": 1, + "maximum": 10, + "default": 3, + "description": "Number of resource links to return (1-10)" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "structuredContent", + "description": "Returns structured content along with an output schema for client data validation", + "inputSchema": { + "type": "object", + "properties": { + "location": { + "type": "string", + "minLength": 1, + "description": "City name or zip code" + } + }, + "required": [ + "location" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "outputSchema": { + "type": "object", + "properties": { + "temperature": { + "type": "number", + "description": "Temperature in celsius" + }, + "conditions": { + "type": "string", + "description": "Weather conditions description" + }, + "humidity": { + "type": "number", + "description": "Humidity percentage" + } + }, + "required": [ + "temperature", + "conditions", + "humidity" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_special_key_characters.log b/cli/scripts/metadata-test-output/metadata_special_key_characters.log new file mode 100644 index 000000000..54345ad39 --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_special_key_characters.log @@ -0,0 +1,8 @@ +{ + "content": [ + { + "type": "text", + "text": "Echo: special keys test" + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_tools_call_complex_tool_meta.log b/cli/scripts/metadata-test-output/metadata_tools_call_complex_tool_meta.log new file mode 100644 index 000000000..2a6bf8837 --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_tools_call_complex_tool_meta.log @@ -0,0 +1,8 @@ +{ + "content": [ + { + "type": "text", + "text": "The sum of 10 and 20 is 30." + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_tools_call_tool_meta.log b/cli/scripts/metadata-test-output/metadata_tools_call_tool_meta.log new file mode 100644 index 000000000..145c3ac0b --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_tools_call_tool_meta.log @@ -0,0 +1,8 @@ +{ + "content": [ + { + "type": "text", + "text": "Echo: hello world" + } + ] +} \ No newline at end of file diff --git a/cli/scripts/metadata-test-output/metadata_tools_list.log b/cli/scripts/metadata-test-output/metadata_tools_list.log new file mode 100644 index 000000000..114d6da4b --- /dev/null +++ b/cli/scripts/metadata-test-output/metadata_tools_list.log @@ -0,0 +1,218 @@ +{ + "tools": [ + { + "name": "echo", + "description": "Echoes back the input", + "inputSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Message to echo" + } + }, + "required": [ + "message" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "add", + "description": "Adds two numbers", + "inputSchema": { + "type": "object", + "properties": { + "a": { + "type": "number", + "description": "First number" + }, + "b": { + "type": "number", + "description": "Second number" + } + }, + "required": [ + "a", + "b" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "longRunningOperation", + "description": "Demonstrates a long running operation with progress updates", + "inputSchema": { + "type": "object", + "properties": { + "duration": { + "type": "number", + "default": 10, + "description": "Duration of the operation in seconds" + }, + "steps": { + "type": "number", + "default": 5, + "description": "Number of steps in the operation" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "printEnv", + "description": "Prints all environment variables, helpful for debugging MCP server configuration", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "sampleLLM", + "description": "Samples from an LLM using MCP's sampling feature", + "inputSchema": { + "type": "object", + "properties": { + "prompt": { + "type": "string", + "description": "The prompt to send to the LLM" + }, + "maxTokens": { + "type": "number", + "default": 100, + "description": "Maximum number of tokens to generate" + } + }, + "required": [ + "prompt" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getTinyImage", + "description": "Returns the MCP_TINY_IMAGE", + "inputSchema": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "annotatedMessage", + "description": "Demonstrates how annotations can be used to provide metadata about content", + "inputSchema": { + "type": "object", + "properties": { + "messageType": { + "type": "string", + "enum": [ + "error", + "success", + "debug" + ], + "description": "Type of message to demonstrate different annotation patterns" + }, + "includeImage": { + "type": "boolean", + "default": false, + "description": "Whether to include an example image" + } + }, + "required": [ + "messageType" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceReference", + "description": "Returns a resource reference that can be used by MCP clients", + "inputSchema": { + "type": "object", + "properties": { + "resourceId": { + "type": "number", + "minimum": 1, + "maximum": 100, + "description": "ID of the resource to reference (1-100)" + } + }, + "required": [ + "resourceId" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "getResourceLinks", + "description": "Returns multiple resource links that reference different types of resources", + "inputSchema": { + "type": "object", + "properties": { + "count": { + "type": "number", + "minimum": 1, + "maximum": 10, + "default": 3, + "description": "Number of resource links to return (1-10)" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "structuredContent", + "description": "Returns structured content along with an output schema for client data validation", + "inputSchema": { + "type": "object", + "properties": { + "location": { + "type": "string", + "minLength": 1, + "description": "City name or zip code" + } + }, + "required": [ + "location" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "outputSchema": { + "type": "object", + "properties": { + "temperature": { + "type": "number", + "description": "Temperature in celsius" + }, + "conditions": { + "type": "string", + "description": "Weather conditions description" + }, + "humidity": { + "type": "number", + "description": "Humidity percentage" + } + }, + "required": [ + "temperature", + "conditions", + "humidity" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + } + ] +} \ No newline at end of file diff --git a/cli/src/client/prompts.ts b/cli/src/client/prompts.ts index 870146178..e7a1cf2f2 100644 --- a/cli/src/client/prompts.ts +++ b/cli/src/client/prompts.ts @@ -12,9 +12,14 @@ type JsonValue = | { [key: string]: JsonValue }; // List available prompts -export async function listPrompts(client: Client): Promise { +export async function listPrompts( + client: Client, + metadata?: Record, +): Promise { try { - const response = await client.listPrompts(); + const params = + metadata && Object.keys(metadata).length > 0 ? { _meta: metadata } : {}; + const response = await client.listPrompts(params); return response; } catch (error) { throw new Error( @@ -28,6 +33,7 @@ export async function getPrompt( client: Client, name: string, args?: Record, + metadata?: Record, ): Promise { try { // Convert all arguments to strings for prompt arguments @@ -44,10 +50,16 @@ export async function getPrompt( } } - const response = await client.getPrompt({ + const params: any = { name, arguments: stringArgs, - }); + }; + + if (metadata && Object.keys(metadata).length > 0) { + params._meta = metadata; + } + + const response = await client.getPrompt(params); return response; } catch (error) { diff --git a/cli/src/client/resources.ts b/cli/src/client/resources.ts index bf33d64d2..3e44820ca 100644 --- a/cli/src/client/resources.ts +++ b/cli/src/client/resources.ts @@ -2,9 +2,14 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { McpResponse } from "./types.js"; // List available resources -export async function listResources(client: Client): Promise { +export async function listResources( + client: Client, + metadata?: Record, +): Promise { try { - const response = await client.listResources(); + const params = + metadata && Object.keys(metadata).length > 0 ? { _meta: metadata } : {}; + const response = await client.listResources(params); return response; } catch (error) { throw new Error( @@ -17,9 +22,14 @@ export async function listResources(client: Client): Promise { export async function readResource( client: Client, uri: string, + metadata?: Record, ): Promise { try { - const response = await client.readResource({ uri }); + const params: any = { uri }; + if (metadata && Object.keys(metadata).length > 0) { + params._meta = metadata; + } + const response = await client.readResource(params); return response; } catch (error) { throw new Error( @@ -31,9 +41,12 @@ export async function readResource( // List resource templates export async function listResourceTemplates( client: Client, + metadata?: Record, ): Promise { try { - const response = await client.listResourceTemplates(); + const params = + metadata && Object.keys(metadata).length > 0 ? { _meta: metadata } : {}; + const response = await client.listResourceTemplates(params); return response; } catch (error) { throw new Error( diff --git a/cli/src/client/tools.ts b/cli/src/client/tools.ts index 9da0b4d9f..909f17af3 100644 --- a/cli/src/client/tools.ts +++ b/cli/src/client/tools.ts @@ -19,9 +19,14 @@ type JsonSchemaType = { items?: JsonSchemaType; }; -export async function listTools(client: Client): Promise { +export async function listTools( + client: Client, + metadata?: Record, +): Promise { try { - const response = await client.listTools(); + const params = + metadata && Object.keys(metadata).length > 0 ? { _meta: metadata } : {}; + const response = await client.listTools(params); return response; } catch (error) { throw new Error( @@ -82,9 +87,11 @@ export async function callTool( client: Client, name: string, args: Record, + generalMetadata?: Record, + toolSpecificMetadata?: Record, ): Promise { try { - const toolsResponse = await listTools(client); + const toolsResponse = await listTools(client, generalMetadata); const tools = toolsResponse.tools as Tool[]; const tool = tools.find((t) => t.name === name); @@ -106,9 +113,23 @@ export async function callTool( } } + // Merge general metadata with tool-specific metadata + // Tool-specific metadata takes precedence over general metadata + let mergedMeta: Record | undefined; + if (generalMetadata || toolSpecificMetadata) { + mergedMeta = { + ...(generalMetadata || {}), + ...(toolSpecificMetadata || {}), + }; + } + const response = await client.callTool({ name: name, arguments: convertedArgs, + _meta: + mergedMeta && Object.keys(mergedMeta).length > 0 + ? mergedMeta + : undefined, }); return response; } catch (error) { diff --git a/cli/src/index.ts b/cli/src/index.ts index 90c9f5e6e..45a71a052 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -41,8 +41,10 @@ type Args = { logLevel?: LogLevel; toolName?: string; toolArg?: Record; + toolMeta?: Record; transport?: "sse" | "stdio" | "http"; headers?: Record; + metadata?: Record; }; function createTransportOptions( @@ -129,7 +131,7 @@ async function callMethod(args: Args): Promise { // Tools methods if (args.method === "tools/list") { - result = await listTools(client); + result = await listTools(client, args.metadata); } else if (args.method === "tools/call") { if (!args.toolName) { throw new Error( @@ -137,11 +139,17 @@ async function callMethod(args: Args): Promise { ); } - result = await callTool(client, args.toolName, args.toolArg || {}); + result = await callTool( + client, + args.toolName, + args.toolArg || {}, + args.metadata, + args.toolMeta, + ); } // Resources methods else if (args.method === "resources/list") { - result = await listResources(client); + result = await listResources(client, args.metadata); } else if (args.method === "resources/read") { if (!args.uri) { throw new Error( @@ -149,13 +157,13 @@ async function callMethod(args: Args): Promise { ); } - result = await readResource(client, args.uri); + result = await readResource(client, args.uri, args.metadata); } else if (args.method === "resources/templates/list") { - result = await listResourceTemplates(client); + result = await listResourceTemplates(client, args.metadata); } // Prompts methods else if (args.method === "prompts/list") { - result = await listPrompts(client); + result = await listPrompts(client, args.metadata); } else if (args.method === "prompts/get") { if (!args.promptName) { throw new Error( @@ -163,7 +171,12 @@ async function callMethod(args: Args): Promise { ); } - result = await getPrompt(client, args.promptName, args.promptArgs || {}); + result = await getPrompt( + client, + args.promptName, + args.promptArgs || {}, + args.metadata, + ); } // Logging methods else if (args.method === "logging/setLevel") { @@ -328,6 +341,21 @@ function parseArgs(): Args { 'HTTP headers as "HeaderName: Value" pairs (for HTTP/SSE transports)', parseHeaderPair, {}, + ) + // + // Metadata options + // + .option( + "--metadata ", + "General metadata as key=value pairs (applied to all methods)", + parseKeyValuePair, + {}, + ) + .option( + "--tool-metadata ", + "Tool-specific metadata as key=value pairs (for tools/call method only)", + parseKeyValuePair, + {}, ); // Parse only the arguments before -- @@ -335,6 +363,8 @@ function parseArgs(): Args { const options = program.opts() as Omit & { header?: Record; + metadata?: Record; + toolMetadata?: Record; }; let remainingArgs = program.args; @@ -352,6 +382,22 @@ function parseArgs(): Args { target: finalArgs, ...options, headers: options.header, // commander.js uses 'header' field, map to 'headers' + metadata: options.metadata + ? Object.fromEntries( + Object.entries(options.metadata).map(([key, value]) => [ + key, + String(value), + ]), + ) + : undefined, + toolMeta: options.toolMetadata + ? Object.fromEntries( + Object.entries(options.toolMetadata).map(([key, value]) => [ + key, + String(value), + ]), + ) + : undefined, }; } diff --git a/client/src/App.tsx b/client/src/App.tsx index 5937d7385..802c6b089 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -47,6 +47,7 @@ import { Hash, Key, MessageSquare, + Settings, } from "lucide-react"; import { z } from "zod"; @@ -80,6 +81,7 @@ import { CustomHeaders, migrateFromLegacyAuth, } from "./lib/types/customHeaders"; +import MetadataTab from "./components/MetadataTab"; const CONFIG_LOCAL_STORAGE_KEY = "inspectorConfig_v1"; @@ -199,9 +201,27 @@ const App = () => { const [authState, setAuthState] = useState(EMPTY_DEBUGGER_STATE); + // Metadata state - persisted in localStorage + const [metadata, setMetadata] = useState>(() => { + const savedMetadata = localStorage.getItem("lastMetadata"); + if (savedMetadata) { + try { + return JSON.parse(savedMetadata); + } catch (error) { + console.warn("Failed to parse saved metadata:", error); + } + } + return {}; + }); + const updateAuthState = (updates: Partial) => { setAuthState((prev) => ({ ...prev, ...updates })); }; + + const handleMetadataChange = (newMetadata: Record) => { + setMetadata(newMetadata); + localStorage.setItem("lastMetadata", JSON.stringify(newMetadata)); + }; const nextRequestId = useRef(0); const rootsRef = useRef([]); @@ -304,6 +324,7 @@ const App = () => { }, getRoots: () => rootsRef.current, defaultLoggingLevel: logLevel, + metadata, }); useEffect(() => { @@ -784,7 +805,11 @@ const App = () => { cacheToolOutputSchemas(response.tools); }; - const callTool = async (name: string, params: Record) => { + const callTool = async ( + name: string, + params: Record, + meta?: Record, + ) => { lastToolCallOriginTabRef.current = currentTabRef.current; try { @@ -794,15 +819,21 @@ const App = () => { ? cleanParams(params, tool.inputSchema as JsonSchemaType) : params; + // Merge general metadata with tool-specific metadata + // Tool-specific metadata takes precedence over general metadata + const mergedMeta = { + ...metadata, // General metadata first + progressToken: progressTokenRef.current++, + ...(meta ?? {}), // Tool-specific metadata overrides + }; + const response = await sendMCPRequest( { method: "tools/call" as const, params: { name, arguments: cleanedParams, - _meta: { - progressToken: progressTokenRef.current++, - }, + _meta: mergedMeta, }, }, CompatibilityCallToolResultSchema, @@ -1000,6 +1031,10 @@ const App = () => { Auth + + + Metadata +
@@ -1110,10 +1145,14 @@ const App = () => { setNextToolCursor(undefined); cacheToolOutputSchemas([]); }} - callTool={async (name, params) => { + callTool={async ( + name: string, + params: Record, + meta?: Record, + ) => { clearError("tools"); setToolResult(null); - await callTool(name, params); + await callTool(name, params, meta); }} selectedTool={selectedTool} setSelectedTool={(tool) => { @@ -1156,6 +1195,10 @@ const App = () => { onRootsChange={handleRootsChange} /> + )}
diff --git a/client/src/components/MetadataTab.tsx b/client/src/components/MetadataTab.tsx new file mode 100644 index 000000000..477952c31 --- /dev/null +++ b/client/src/components/MetadataTab.tsx @@ -0,0 +1,121 @@ +import React, { useState } from "react"; +import { TabsContent } from "@/components/ui/tabs"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Trash2, Plus } from "lucide-react"; + +interface MetadataEntry { + key: string; + value: string; +} + +interface MetadataTabProps { + metadata: Record; + onMetadataChange: (metadata: Record) => void; +} + +const MetadataTab: React.FC = ({ + metadata, + onMetadataChange, +}) => { + const [entries, setEntries] = useState(() => { + return Object.entries(metadata).map(([key, value]) => ({ key, value })); + }); + + const addEntry = () => { + setEntries([...entries, { key: "", value: "" }]); + }; + + const removeEntry = (index: number) => { + const newEntries = entries.filter((_, i) => i !== index); + setEntries(newEntries); + updateMetadata(newEntries); + }; + + const updateEntry = ( + index: number, + field: "key" | "value", + value: string, + ) => { + const newEntries = [...entries]; + newEntries[index][field] = value; + setEntries(newEntries); + updateMetadata(newEntries); + }; + + const updateMetadata = (newEntries: MetadataEntry[]) => { + const metadataObject: Record = {}; + newEntries.forEach(({ key, value }) => { + if (key.trim() && value.trim()) { + metadataObject[key.trim()] = value.trim(); + } + }); + onMetadataChange(metadataObject); + }; + + return ( + +
+
+
+

Metadata

+

+ Key-value pairs that will be included in all MCP requests +

+
+ +
+ +
+ {entries.map((entry, index) => ( +
+
+ + updateEntry(index, "key", e.target.value)} + /> +
+
+ + updateEntry(index, "value", e.target.value)} + /> +
+ +
+ ))} +
+ + {entries.length === 0 && ( +
+

+ No metadata entries. Click "Add Entry" to add key-value pairs. +

+
+ )} +
+
+ ); +}; + +export default MetadataTab; diff --git a/client/src/components/ToolResults.tsx b/client/src/components/ToolResults.tsx index 6479b5fbb..64798cd9d 100644 --- a/client/src/components/ToolResults.tsx +++ b/client/src/components/ToolResults.tsx @@ -156,7 +156,7 @@ const ToolResults = ({ )} {structuredResult._meta && (
-
Meta:
+
Meta Schema:
diff --git a/client/src/components/ToolsTab.tsx b/client/src/components/ToolsTab.tsx index 740cc5edb..39366b2bd 100644 --- a/client/src/components/ToolsTab.tsx +++ b/client/src/components/ToolsTab.tsx @@ -60,7 +60,11 @@ const ToolsTab = ({ tools: Tool[]; listTools: () => void; clearTools: () => void; - callTool: (name: string, params: Record) => Promise; + callTool: ( + name: string, + params: Record, + meta?: Record, + ) => Promise; selectedTool: Tool | null; setSelectedTool: (tool: Tool | null) => void; toolResult: CompatibilityCallToolResult | null; @@ -73,6 +77,9 @@ const ToolsTab = ({ const [isToolRunning, setIsToolRunning] = useState(false); const [isOutputSchemaExpanded, setIsOutputSchemaExpanded] = useState(false); const [isMetaExpanded, setIsMetaExpanded] = useState(false); + const [metaEntries, setMetaEntries] = useState< + { id: string; key: string; value: string }[] + >([]); const [hasValidationErrors, setHasValidationErrors] = useState(false); const formRefs = useRef>({}); const { toast } = useToast(); @@ -383,6 +390,104 @@ const ToolsTab = ({ ); }, )} +
+
+

+ Tool-specific Metadata: +

+ +
+ {metaEntries.length === 0 ? ( +

+ No meta pairs. +

+ ) : ( +
+ {metaEntries.map((entry, index) => ( +
+ + { + const value = e.target.value; + setMetaEntries((prev) => + prev.map((m, i) => + i === index ? { ...m, key: value } : m, + ), + ); + }} + className="h-8 flex-1" + /> + + { + const value = e.target.value; + setMetaEntries((prev) => + prev.map((m, i) => + i === index ? { ...m, value } : m, + ), + ); + }} + className="h-8 flex-1" + /> + +
+ ))} +
+ )} +
{selectedTool.outputSchema && (
@@ -424,7 +529,7 @@ const ToolsTab = ({ selectedTool._meta && (
-

Meta:

+

Meta Schema:

)} -
- + try { + setIsToolRunning(true); + const meta = metaEntries.reduce>( + (acc, { key, value }) => { + if (key.trim() !== "") acc[key] = value; + return acc; + }, + {}, + ); + await callTool( + selectedTool.name, + params, + Object.keys(meta).length ? meta : undefined, + ); + } finally { + setIsToolRunning(false); + } + }} + disabled={isToolRunning || hasValidationErrors} + > + {isToolRunning ? ( + <> + + Running... + + ) : ( + <> + + Run Tool + + )} + +