update examples, update package-lock.json
This commit is contained in:
parent
6079cd8307
commit
e1abdab7f5
7 changed files with 258 additions and 431 deletions
|
|
@ -61,19 +61,13 @@ async function main(): Promise<void> {
|
|||
// Log the results from each tool call
|
||||
for (const [caller, result] of Object.entries(toolResults)) {
|
||||
console.log(`\n=== Tool result for ${caller} ===`);
|
||||
if (result.content) {
|
||||
result.content.forEach((item: { type: string; text?: string; }) => {
|
||||
if (item.type === 'text') {
|
||||
console.log(` ${item.text}`);
|
||||
} else {
|
||||
console.log(` ${item.type} content:`, item);
|
||||
}
|
||||
});
|
||||
} else if (result.structuredContent) {
|
||||
console.log(` Structured content: ${result.structuredContent}`);
|
||||
} else {
|
||||
console.log(` No content returned`);
|
||||
}
|
||||
result.content?.forEach((item: { type: string; text?: string; }) => {
|
||||
if (item.type === 'text') {
|
||||
console.log(` ${item.text}`);
|
||||
} else {
|
||||
console.log(` ${item.type} content:`, item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Wait for all notifications (10 seconds)
|
||||
|
|
|
|||
|
|
@ -341,19 +341,13 @@ async function callTool(name: string, args: Record<string, unknown>): Promise<vo
|
|||
});
|
||||
|
||||
console.log('Tool result:');
|
||||
if (result.content) {
|
||||
result.content.forEach(item => {
|
||||
if (item.type === 'text') {
|
||||
console.log(` ${item.text}`);
|
||||
} else {
|
||||
console.log(` ${item.type} content:`, item);
|
||||
}
|
||||
});
|
||||
} else if (result.structuredContent) {
|
||||
console.log(` Structured content: ${result.structuredContent}`);
|
||||
} else {
|
||||
console.log(' No content returned');
|
||||
}
|
||||
result.content?.forEach(item => {
|
||||
if (item.type === 'text') {
|
||||
console.log(` ${item.text}`);
|
||||
} else {
|
||||
console.log(` ${item.type} content:`, item);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(`Error calling tool ${name}: ${error}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,19 +173,13 @@ async function startNotificationTool(client: Client): Promise<void> {
|
|||
const result = await client.request(request, CallToolResultSchema);
|
||||
|
||||
console.log('Tool result:');
|
||||
if (result.content) {
|
||||
result.content.forEach(item => {
|
||||
if (item.type === 'text') {
|
||||
console.log(` ${item.text}`);
|
||||
} else {
|
||||
console.log(` ${item.type} content:`, item);
|
||||
}
|
||||
});
|
||||
} else if (result.structuredContent) {
|
||||
console.log(` Structured content: ${result.structuredContent}`);
|
||||
} else {
|
||||
console.log(' No content returned');
|
||||
}
|
||||
result.content?.forEach(item => {
|
||||
if (item.type === 'text') {
|
||||
console.log(` ${item.text}`);
|
||||
} else {
|
||||
console.log(` ${item.type} content:`, item);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(`Error calling notification tool: ${error}`);
|
||||
}
|
||||
|
|
|
|||
116
src/examples/server/lowLevelOutputSchema.ts
Normal file
116
src/examples/server/lowLevelOutputSchema.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Example MCP server demonstrating tool outputSchema support using the low-level Server API
|
||||
* This server manually handles tool listing and invocation requests to return structured data
|
||||
* For a simpler high-level API approach, see mcpServerOutputSchema.ts
|
||||
*/
|
||||
|
||||
import { Server } from "../../server/index.js";
|
||||
import { StdioServerTransport } from "../../server/stdio.js";
|
||||
import { CallToolRequest, CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "../../types.js";
|
||||
|
||||
const server = new Server(
|
||||
{
|
||||
name: "output-schema-low-level-example",
|
||||
version: "1.0.0",
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Tool with structured output
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
tools: [
|
||||
{
|
||||
name: "get_weather",
|
||||
description: "Get weather information for a city",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
city: { type: "string", description: "City name" },
|
||||
country: { type: "string", description: "Country code (e.g., US, UK)" }
|
||||
},
|
||||
required: ["city", "country"]
|
||||
},
|
||||
outputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
temperature: {
|
||||
type: "object",
|
||||
properties: {
|
||||
celsius: { type: "number" },
|
||||
fahrenheit: { type: "number" }
|
||||
},
|
||||
required: ["celsius", "fahrenheit"]
|
||||
},
|
||||
conditions: {
|
||||
type: "string",
|
||||
enum: ["sunny", "cloudy", "rainy", "stormy", "snowy"]
|
||||
},
|
||||
humidity: { type: "number", minimum: 0, maximum: 100 },
|
||||
wind: {
|
||||
type: "object",
|
||||
properties: {
|
||||
speed_kmh: { type: "number" },
|
||||
direction: { type: "string" }
|
||||
},
|
||||
required: ["speed_kmh", "direction"]
|
||||
}
|
||||
},
|
||||
required: ["temperature", "conditions", "humidity", "wind"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {
|
||||
switch (request.params.name) {
|
||||
case "get_weather": {
|
||||
const { city, country } = request.params.arguments as { city: string; country: string };
|
||||
|
||||
// Parameters are available but not used in this example
|
||||
void city;
|
||||
void country;
|
||||
|
||||
// Simulate weather API call
|
||||
const temp_c = Math.round((Math.random() * 35 - 5) * 10) / 10;
|
||||
const conditions = ["sunny", "cloudy", "rainy", "stormy", "snowy"][Math.floor(Math.random() * 5)];
|
||||
|
||||
// Return structured content matching the outputSchema
|
||||
return {
|
||||
structuredContent: {
|
||||
temperature: {
|
||||
celsius: temp_c,
|
||||
fahrenheit: Math.round((temp_c * 9/5 + 32) * 10) / 10
|
||||
},
|
||||
conditions,
|
||||
humidity: Math.round(Math.random() * 100),
|
||||
wind: {
|
||||
speed_kmh: Math.round(Math.random() * 50),
|
||||
direction: ["N", "NE", "E", "SE", "S", "SW", "W", "NW"][Math.floor(Math.random() * 8)]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
throw new McpError(
|
||||
ErrorCode.MethodNotFound,
|
||||
`Unknown tool: ${request.params.name}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
async function main() {
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
console.error("Low-level Output Schema Example Server running on stdio");
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("Server error:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -10,47 +10,31 @@ import { z } from "zod";
|
|||
|
||||
const server = new McpServer(
|
||||
{
|
||||
name: "mcp-output-schema-example",
|
||||
name: "mcp-output-schema-high-level-example",
|
||||
version: "1.0.0",
|
||||
}
|
||||
);
|
||||
|
||||
// Define a tool with structured output - Weather data
|
||||
server.tool(
|
||||
"get_weather",
|
||||
"Get weather information for a city",
|
||||
{
|
||||
server.tool("get_weather", {
|
||||
description: "Get weather information for a city",
|
||||
inputSchema:{
|
||||
city: z.string().describe("City name"),
|
||||
country: z.string().describe("Country code (e.g., US, UK)")
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
temperature: {
|
||||
type: "object",
|
||||
properties: {
|
||||
celsius: { type: "number" },
|
||||
fahrenheit: { type: "number" }
|
||||
},
|
||||
required: ["celsius", "fahrenheit"]
|
||||
},
|
||||
conditions: {
|
||||
type: "string",
|
||||
enum: ["sunny", "cloudy", "rainy", "stormy", "snowy"]
|
||||
},
|
||||
humidity: { type: "number", minimum: 0, maximum: 100 },
|
||||
wind: {
|
||||
type: "object",
|
||||
properties: {
|
||||
speed_kmh: { type: "number" },
|
||||
direction: { type: "string" }
|
||||
},
|
||||
required: ["speed_kmh", "direction"]
|
||||
}
|
||||
},
|
||||
required: ["temperature", "conditions", "humidity", "wind"]
|
||||
outputSchema: {
|
||||
temperature: z.object({
|
||||
celsius: z.number(),
|
||||
fahrenheit: z.number()
|
||||
}),
|
||||
conditions: z.enum(["sunny", "cloudy", "rainy", "stormy", "snowy"]),
|
||||
humidity: z.number().min(0).max(100),
|
||||
wind: z.object({
|
||||
speed_kmh: z.number(),
|
||||
direction: z.string()
|
||||
})
|
||||
},
|
||||
async ({ city, country }: { city: string; country: string }) => {
|
||||
callback: async ({ city, country }) => {
|
||||
// Parameters are available but not used in this example
|
||||
void city;
|
||||
void country;
|
||||
|
|
@ -73,161 +57,12 @@ server.tool(
|
|||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Define a tool for data processing with structured output
|
||||
server.tool(
|
||||
"process_csv",
|
||||
"Process CSV data and return statistics",
|
||||
{
|
||||
csv_data: z.string().describe("CSV data as a string"),
|
||||
delimiter: z.string().default(",").describe("CSV delimiter")
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
row_count: { type: "integer" },
|
||||
column_count: { type: "integer" },
|
||||
headers: {
|
||||
type: "array",
|
||||
items: { type: "string" }
|
||||
},
|
||||
data_types: {
|
||||
type: "object",
|
||||
additionalProperties: {
|
||||
type: "string",
|
||||
enum: ["number", "string", "date", "boolean"]
|
||||
}
|
||||
},
|
||||
summary: {
|
||||
type: "object",
|
||||
additionalProperties: {
|
||||
type: "object",
|
||||
properties: {
|
||||
min: { type: "number" },
|
||||
max: { type: "number" },
|
||||
mean: { type: "number" },
|
||||
count: { type: "integer" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["row_count", "column_count", "headers", "data_types"]
|
||||
},
|
||||
async ({ csv_data, delimiter }) => {
|
||||
const lines = csv_data.trim().split('\n');
|
||||
const headers = lines[0].split(delimiter).map(h => h.trim());
|
||||
const data = lines.slice(1).map(line => line.split(delimiter).map(cell => cell.trim()));
|
||||
|
||||
// Infer data types
|
||||
const dataTypes: { [key: string]: string } = {};
|
||||
const summary: { [key: string]: unknown } = {};
|
||||
|
||||
headers.forEach((header, idx) => {
|
||||
const values = data.map(row => row[idx]);
|
||||
const numericValues = values.filter(v => !isNaN(Number(v)) && v !== '');
|
||||
|
||||
if (numericValues.length === values.length) {
|
||||
dataTypes[header] = "number";
|
||||
const numbers = numericValues.map(Number);
|
||||
summary[header] = {
|
||||
min: Math.min(...numbers),
|
||||
max: Math.max(...numbers),
|
||||
mean: numbers.reduce((a, b) => a + b, 0) / numbers.length,
|
||||
count: numbers.length
|
||||
};
|
||||
} else {
|
||||
dataTypes[header] = "string";
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
structuredContent: {
|
||||
row_count: data.length,
|
||||
column_count: headers.length,
|
||||
headers,
|
||||
data_types: dataTypes,
|
||||
summary
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Traditional tool without outputSchema for comparison
|
||||
server.tool(
|
||||
"echo",
|
||||
"Echo back the input message",
|
||||
{
|
||||
message: z.string()
|
||||
},
|
||||
async ({ message }) => {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Echo: ${message}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Tool that can return both structured and unstructured content
|
||||
server.tool(
|
||||
"hybrid_tool",
|
||||
"Tool that returns both structured and readable content",
|
||||
{
|
||||
data: z.array(z.number()).describe("Array of numbers to analyze")
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
stats: {
|
||||
type: "object",
|
||||
properties: {
|
||||
mean: { type: "number" },
|
||||
median: { type: "number" },
|
||||
std_dev: { type: "number" }
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["stats"]
|
||||
},
|
||||
async ({ data }) => {
|
||||
const mean = data.reduce((a, b) => a + b, 0) / data.length;
|
||||
const sorted = [...data].sort((a, b) => a - b);
|
||||
const median = sorted.length % 2 === 0
|
||||
? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2
|
||||
: sorted[Math.floor(sorted.length / 2)];
|
||||
const variance = data.reduce((acc, val) => acc + Math.pow(val - mean, 2), 0) / data.length;
|
||||
const std_dev = Math.sqrt(variance);
|
||||
|
||||
return {
|
||||
structuredContent: {
|
||||
stats: {
|
||||
mean: Math.round(mean * 100) / 100,
|
||||
median: Math.round(median * 100) / 100,
|
||||
std_dev: Math.round(std_dev * 100) / 100
|
||||
}
|
||||
},
|
||||
// Also provide human-readable content for backward compatibility
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Analysis of ${data.length} numbers:
|
||||
- Mean: ${Math.round(mean * 100) / 100}
|
||||
- Median: ${Math.round(median * 100) / 100}
|
||||
- Standard Deviation: ${Math.round(std_dev * 100) / 100}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
async function main() {
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
console.error("McpServer Output Schema Example running on stdio");
|
||||
console.error("High-level Output Schema Example Server running on stdio");
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
|
|
|
|||
|
|
@ -1,209 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Example MCP server demonstrating tool outputSchema support
|
||||
* This server exposes tools that return structured data
|
||||
*/
|
||||
|
||||
import { Server } from "../../server/index.js";
|
||||
import { StdioServerTransport } from "../../server/stdio.js";
|
||||
import { CallToolRequest, CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "../../types.js";
|
||||
|
||||
const server = new Server(
|
||||
{
|
||||
name: "output-schema-example",
|
||||
version: "1.0.0",
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Tool with structured output
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
tools: [
|
||||
{
|
||||
name: "calculate_bmi",
|
||||
description: "Calculate BMI given height and weight",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
height_cm: { type: "number", description: "Height in centimeters" },
|
||||
weight_kg: { type: "number", description: "Weight in kilograms" }
|
||||
},
|
||||
required: ["height_cm", "weight_kg"]
|
||||
},
|
||||
outputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
bmi: { type: "number", description: "Body Mass Index" },
|
||||
category: {
|
||||
type: "string",
|
||||
enum: ["underweight", "normal", "overweight", "obese"],
|
||||
description: "BMI category"
|
||||
},
|
||||
healthy_weight_range: {
|
||||
type: "object",
|
||||
properties: {
|
||||
min_kg: { type: "number" },
|
||||
max_kg: { type: "number" }
|
||||
},
|
||||
required: ["min_kg", "max_kg"]
|
||||
}
|
||||
},
|
||||
required: ["bmi", "category", "healthy_weight_range"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "analyze_text",
|
||||
description: "Analyze text and return structured insights",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
text: { type: "string", description: "Text to analyze" }
|
||||
},
|
||||
required: ["text"]
|
||||
},
|
||||
outputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
word_count: { type: "integer" },
|
||||
sentence_count: { type: "integer" },
|
||||
character_count: { type: "integer" },
|
||||
reading_time_minutes: { type: "number" },
|
||||
sentiment: {
|
||||
type: "string",
|
||||
enum: ["positive", "negative", "neutral"]
|
||||
},
|
||||
key_phrases: {
|
||||
type: "array",
|
||||
items: { type: "string" }
|
||||
}
|
||||
},
|
||||
required: ["word_count", "sentence_count", "character_count", "reading_time_minutes"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "traditional_tool",
|
||||
description: "A traditional tool without outputSchema",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
message: { type: "string" }
|
||||
},
|
||||
required: ["message"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {
|
||||
switch (request.params.name) {
|
||||
case "calculate_bmi": {
|
||||
const { height_cm, weight_kg } = request.params.arguments as { height_cm: number; weight_kg: number };
|
||||
|
||||
const height_m = height_cm / 100;
|
||||
const bmi = weight_kg / (height_m * height_m);
|
||||
|
||||
let category: string;
|
||||
if (bmi < 18.5) category = "underweight";
|
||||
else if (bmi < 25) category = "normal";
|
||||
else if (bmi < 30) category = "overweight";
|
||||
else category = "obese";
|
||||
|
||||
// Calculate healthy weight range for normal BMI (18.5-24.9)
|
||||
const min_healthy_bmi = 18.5;
|
||||
const max_healthy_bmi = 24.9;
|
||||
const min_healthy_weight = min_healthy_bmi * height_m * height_m;
|
||||
const max_healthy_weight = max_healthy_bmi * height_m * height_m;
|
||||
|
||||
// Return structured content matching the outputSchema
|
||||
return {
|
||||
structuredContent: {
|
||||
bmi: Math.round(bmi * 10) / 10,
|
||||
category,
|
||||
healthy_weight_range: {
|
||||
min_kg: Math.round(min_healthy_weight * 10) / 10,
|
||||
max_kg: Math.round(max_healthy_weight * 10) / 10
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case "analyze_text": {
|
||||
const { text } = request.params.arguments as { text: string };
|
||||
|
||||
// Simple text analysis
|
||||
const words = text.trim().split(/\s+/);
|
||||
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0);
|
||||
const wordsPerMinute = 200; // Average reading speed
|
||||
|
||||
// Very simple sentiment analysis (for demo purposes)
|
||||
const positiveWords = ["good", "great", "excellent", "happy", "positive", "amazing"];
|
||||
const negativeWords = ["bad", "poor", "terrible", "sad", "negative", "awful"];
|
||||
|
||||
let positiveCount = 0;
|
||||
let negativeCount = 0;
|
||||
words.forEach(word => {
|
||||
if (positiveWords.includes(word.toLowerCase())) positiveCount++;
|
||||
if (negativeWords.includes(word.toLowerCase())) negativeCount++;
|
||||
});
|
||||
|
||||
let sentiment: string;
|
||||
if (positiveCount > negativeCount) sentiment = "positive";
|
||||
else if (negativeCount > positiveCount) sentiment = "negative";
|
||||
else sentiment = "neutral";
|
||||
|
||||
// Extract key phrases (simple approach - just common bigrams)
|
||||
const keyPhrases: string[] = [];
|
||||
for (let i = 0; i < words.length - 1; i++) {
|
||||
if (words[i].length > 3 && words[i + 1].length > 3) {
|
||||
keyPhrases.push(`${words[i]} ${words[i + 1]}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
structuredContent: {
|
||||
word_count: words.length,
|
||||
sentence_count: sentences.length,
|
||||
character_count: text.length,
|
||||
reading_time_minutes: Math.round((words.length / wordsPerMinute) * 10) / 10,
|
||||
sentiment,
|
||||
key_phrases: keyPhrases.slice(0, 5) // Top 5 phrases
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case "traditional_tool": {
|
||||
const { message } = request.params.arguments as { message: string };
|
||||
|
||||
// Traditional tool returns content array
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Processed message: ${message.toUpperCase()}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
throw new McpError(
|
||||
ErrorCode.MethodNotFound,
|
||||
`Unknown tool: ${request.params.name}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
async function main() {
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
console.error("Output Schema Example Server running on stdio");
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("Server error:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
93
src/examples/server/testOutputSchemaServers.ts
Executable file
93
src/examples/server/testOutputSchemaServers.ts
Executable file
|
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Client to test outputSchema servers
|
||||
* This client connects to either the high-level or low-level outputSchema server
|
||||
* and calls the get_weather tool to demonstrate structured output
|
||||
*/
|
||||
|
||||
import { Client } from "../../client/index.js";
|
||||
import { StdioClientTransport } from "../../client/stdio.js";
|
||||
import { ListToolsResultSchema, CallToolResultSchema } from "../../types.js";
|
||||
|
||||
async function main() {
|
||||
const serverPath = process.argv[2];
|
||||
|
||||
if (!serverPath) {
|
||||
console.error("Usage: npx tsx testOutputSchemaServers.ts <server-path>");
|
||||
console.error("Example: npx tsx testOutputSchemaServers.ts ./mcpServerOutputSchema.ts");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Connecting to ${serverPath}...`);
|
||||
|
||||
// Create transport that spawns the server process
|
||||
const transport = new StdioClientTransport({
|
||||
command: "npx",
|
||||
args: ["tsx", serverPath]
|
||||
});
|
||||
|
||||
const client = new Client({
|
||||
name: "output-schema-test-client",
|
||||
version: "1.0.0"
|
||||
}, {
|
||||
capabilities: {}
|
||||
});
|
||||
|
||||
try {
|
||||
await client.connect(transport);
|
||||
console.log("Connected to server\n");
|
||||
|
||||
// List available tools
|
||||
console.log("Listing available tools...");
|
||||
const toolsResult = await client.request({
|
||||
method: "tools/list"
|
||||
}, ListToolsResultSchema);
|
||||
|
||||
console.log("Available tools:");
|
||||
for (const tool of toolsResult.tools) {
|
||||
console.log(`- ${tool.name}: ${tool.description}`);
|
||||
if (tool.outputSchema) {
|
||||
console.log(" Has outputSchema: Yes");
|
||||
console.log(" Output schema:", JSON.stringify(tool.outputSchema, null, 2));
|
||||
} else {
|
||||
console.log(" Has outputSchema: No");
|
||||
}
|
||||
}
|
||||
|
||||
// Call the weather tool
|
||||
console.log("\nCalling get_weather tool...");
|
||||
const weatherResult = await client.request({
|
||||
method: "tools/call",
|
||||
params: {
|
||||
name: "get_weather",
|
||||
arguments: {
|
||||
city: "London",
|
||||
country: "UK"
|
||||
}
|
||||
}
|
||||
}, CallToolResultSchema);
|
||||
|
||||
console.log("\nWeather tool result:");
|
||||
if (weatherResult.structuredContent) {
|
||||
console.log("Structured content:");
|
||||
console.log(JSON.stringify(weatherResult.structuredContent, null, 2));
|
||||
}
|
||||
|
||||
if (weatherResult.content) {
|
||||
console.log("Unstructured content:");
|
||||
weatherResult.content.forEach(content => {
|
||||
if (content.type === "text") {
|
||||
console.log(content.text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await client.close();
|
||||
console.log("\nDisconnected from server");
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
Loading…
Reference in a new issue