make content required

This commit is contained in:
ihrpr 2025-05-21 11:43:39 +01:00 • committed by Basil Hosmer
commit 265ad071f7
No known key found for this signature in database
6 changed files with 52 additions and 62 deletions

View file

@ -61,7 +61,7 @@ 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} ===`);
result.content?.forEach((item: { type: string; text?: string; }) => {
result.content.forEach((item: { type: string; text?: string; }) => {
if (item.type === 'text') {
console.log(` ${item.text}`);
} else {

View file

@ -264,12 +264,12 @@ async function terminateSession(): Promise<void> {
console.log('Terminating session with ID:', transport.sessionId);
await transport.terminateSession();
console.log('Session terminated successfully');
// Check if sessionId was cleared after termination
if (!transport.sessionId) {
console.log('Session ID has been cleared');
sessionId = undefined;
// Also close the transport and clear client objects
await transport.close();
console.log('Transport closed after session termination');
@ -341,7 +341,7 @@ async function callTool(name: string, args: Record<string, unknown>): Promise<vo
});
console.log('Tool result:');
result.content?.forEach(item => {
result.content.forEach(item => {
if (item.type === 'text') {
console.log(` ${item.text}`);
} else {
@ -456,7 +456,7 @@ async function cleanup(): Promise<void> {
console.error('Error terminating session:', error);
}
}
// Then close the transport
await transport.close();
} catch (error) {

View file

@ -173,7 +173,7 @@ async function startNotificationTool(client: Client): Promise<void> {
const result = await client.request(request, CallToolResultSchema);
console.log('Tool result:');
result.content?.forEach(item => {
result.content.forEach(item => {
if (item.type === 'text') {
console.log(` ${item.text}`);
} else {

View file

@ -20,7 +20,7 @@ server.registerTool(
"get_weather",
{
description: "Get weather information for a city",
inputSchema:{
inputSchema: {
city: z.string().describe("City name"),
country: z.string().describe("Country code (e.g., US, UK)")
},
@ -46,10 +46,11 @@ server.registerTool(
const conditions = ["sunny", "cloudy", "rainy", "stormy", "snowy"][Math.floor(Math.random() * 5)];
return {
content: [],
structuredContent: {
temperature: {
celsius: temp_c,
fahrenheit: Math.round((temp_c * 9/5 + 32) * 10) / 10
fahrenheit: Math.round((temp_c * 9 / 5 + 32) * 10) / 10
},
conditions,
humidity: Math.round(Math.random() * 100),

View file

@ -398,7 +398,7 @@ describe("tool()", () => {
name: z.string(),
value: z.number(),
},
callback: async ({name, value}) => ({
callback: async ({ name, value }) => ({
content: [
{
type: "text",
@ -729,7 +729,7 @@ describe("tool()", () => {
});
mcpServer.tool(
"test",
"test",
{ name: z.string() },
{ title: "Test Tool", readOnlyHint: true },
async ({ name }) => ({
@ -787,7 +787,7 @@ describe("tool()", () => {
});
mcpServer.tool(
"test",
"test",
"A tool with everything",
{ name: z.string() },
{ title: "Complete Test Tool", readOnlyHint: true, openWorldHint: false },
@ -828,8 +828,8 @@ describe("tool()", () => {
type: "object",
properties: { name: { type: "string" } }
});
expect(result.tools[0].annotations).toEqual({
title: "Complete Test Tool",
expect(result.tools[0].annotations).toEqual({
title: "Complete Test Tool",
readOnlyHint: true,
openWorldHint: false
});
@ -853,7 +853,7 @@ describe("tool()", () => {
});
mcpServer.tool(
"test",
"test",
"A tool with everything but empty params",
{},
{ title: "Complete Test Tool with empty params", readOnlyHint: true, openWorldHint: false },
@ -894,8 +894,8 @@ describe("tool()", () => {
type: "object",
properties: {}
});
expect(result.tools[0].annotations).toEqual({
title: "Complete Test Tool with empty params",
expect(result.tools[0].annotations).toEqual({
title: "Complete Test Tool with empty params",
readOnlyHint: true,
openWorldHint: false
});
@ -1077,9 +1077,9 @@ describe("tool()", () => {
input: z.string(),
},
outputSchema: {
processedInput: z.string(),
resultType: z.string(),
timestamp: z.string()
processedInput: z.string(),
resultType: z.string(),
timestamp: z.string()
},
},
async ({ input }) => ({
@ -1088,6 +1088,16 @@ describe("tool()", () => {
resultType: "structured",
timestamp: "2023-01-01T00:00:00Z"
},
content: [
{
type: "text",
text: JSON.stringify({
processedInput: input,
resultType: "structured",
timestamp: "2023-01-01T00:00:00Z"
}),
},
]
})
);
@ -1186,6 +1196,17 @@ describe("tool()", () => {
},
},
async ({ input }) => ({
content: [
{
type: "text",
text: JSON.stringify({
processedInput: input,
resultType: "structured",
// Missing required 'timestamp' field
someExtraField: "unexpected" // Extra field not in schema
}),
},
],
structuredContent: {
processedInput: input,
resultType: "structured",
@ -2664,7 +2685,7 @@ describe("prompt()", () => {
name: z.string(),
value: z.string(),
},
callback: async ({name, value}) => ({
callback: async ({ name, value }) => ({
messages: [
{
role: "assistant",

View file

@ -843,13 +843,13 @@ export const ToolSchema = z
* If not set, a CallToolResult for this Tool MUST NOT contain a structuredContent field and MUST contain a content field.
*/
outputSchema: z.optional(
z.object({
type: z.literal("object"),
properties: z.optional(z.object({}).passthrough()),
required: z.optional(z.array(z.string())),
})
z.object({
type: z.literal("object"),
properties: z.optional(z.object({}).passthrough()),
required: z.optional(z.array(z.string())),
})
.passthrough()
),
),
/**
* Optional additional tool information.
*/
@ -892,46 +892,21 @@ export const ContentListSchema = z.array(
]),
);
export const CallToolUnstructuredResultSchema = ResultSchema.extend({
export const CallToolResultSchema = ResultSchema.extend({
/**
* A list of content objects that represent the result of the tool call.
*
* If the Tool does not define an outputSchema, this field MUST be present in the result.
* For backwards compatibility, this field is always present, but it may be empty.
*/
content: ContentListSchema,
content: ContentListSchema.default([]),
/**
* Structured output must not be provided in an unstructured tool result.
*/
structuredContent: z.never().optional(),
/**
* Whether the tool call ended in an error.
*
* If not set, this is assumed to be false (the call was successful).
*/
isError: z.optional(z.boolean()),
});
export const CallToolStructuredResultSchema = ResultSchema.extend({
/**
* An object containing structured tool output.
*
* If the Tool defines an outputSchema, this field MUST be present in the result, and contain a JSON object that matches the schema.
*/
structuredContent: z.object({}).passthrough(),
/**
* A list of content objects that represent the result of the tool call.
*
* If the Tool defines an outputSchema, this field MAY be present in the result.
*
* Tools may use this field to provide compatibility with older clients that
* do not support structured content.
*
* Clients that support structured content should ignore this field.
*/
content: z.optional(ContentListSchema),
structuredContent: z.object({}).passthrough().optional(),
/**
* Whether the tool call ended in an error.
@ -941,11 +916,6 @@ export const CallToolStructuredResultSchema = ResultSchema.extend({
isError: z.optional(z.boolean()),
});
export const CallToolResultSchema = z.union([
CallToolUnstructuredResultSchema,
CallToolStructuredResultSchema,
]);
/**
* CallToolResultSchema extended with backwards compatibility to protocol version 2024-10-07.
*/
@ -1397,8 +1367,6 @@ export type Tool = Infer<typeof ToolSchema>;
export type ListToolsRequest = Infer<typeof ListToolsRequestSchema>;
export type ListToolsResult = Infer<typeof ListToolsResultSchema>;
export type ContentList = Infer<typeof ContentListSchema>;
export type CallToolUnstructuredResult = Infer<typeof CallToolUnstructuredResultSchema>;
export type CallToolStructuredResult = Infer<typeof CallToolStructuredResultSchema>;
export type CallToolResult = Infer<typeof CallToolResultSchema>;
export type CompatibilityCallToolResult = Infer<typeof CompatibilityCallToolResultSchema>;
export type CallToolRequest = Infer<typeof CallToolRequestSchema>;