Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/tools/actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ EXAMPLES:
}
const toolsResponse = await client.listTools();

const toolsInfo = toolsResponse.tools.map((tool) => `**${tool.name}**\n${tool.description || 'No description'}\nInput schema: ${JSON.stringify(tool.inputSchema, null, 2)}`,
const toolsInfo = toolsResponse.tools.map((tool) => `**${tool.name}**\n${tool.description || 'No description'}\nInput schema:\n\`\`\`json\n${JSON.stringify(tool.inputSchema, null, 2)}\n\`\`\``,
).join('\n\n');

return buildMCPResponse([`This is an MCP Server Actor with the following tools:\n\n${toolsInfo}\n\nTo call a tool, use step="call" with actor name format: "${baseActorName}:{toolName}"`]);
Expand All @@ -422,7 +422,7 @@ EXAMPLES:
}
const content = [
`Actor name: ${actorName}`,
`Input schema: \n${JSON.stringify(details.inputSchema, null, 0)}`,
`Input schema:\n\`\`\`json\n${JSON.stringify(details.inputSchema, null, 0)}\n\`\`\``,
`To run Actor, use step="call" with Actor name format: "${actorName}"`,
];
// Add Skyfire instructions also in the info performStep since clients are most likely truncating
Expand Down Expand Up @@ -504,7 +504,7 @@ EXAMPLES:
const { errors } = actor.tool.ajvValidate;
const content = [
`Input validation failed for Actor '${actorName}'. Please ensure your input matches the Actor's input schema.`,
`Input schema:\n${JSON.stringify(actor.tool.inputSchema)}`,
`Input schema:\n\`\`\`json\n${JSON.stringify(actor.tool.inputSchema)}\n\`\`\``,
];
if (errors && errors.length > 0) {
content.push(`Validation errors: ${errors.map((e) => e.message).join(', ')}`);
Expand Down
2 changes: 1 addition & 1 deletion src/tools/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export const actorDefinitionTool: ToolEntry = {
const properties = filterSchemaProperties(v.input.properties as { [key: string]: ISchemaProperties });
v.input.properties = shortenProperties(properties);
}
return { content: [{ type: 'text', text: JSON.stringify(v) }] };
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(v, null, 2)}\n\`\`\`` }] };
},
} as InternalTool,
};
6 changes: 3 additions & 3 deletions src/tools/dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ USAGE EXAMPLES:
if (!v) {
return { content: [{ type: 'text', text: `Dataset '${parsed.datasetId}' not found.` }] };
}
return { content: [{ type: 'text', text: JSON.stringify(v) }] };
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(v)}\n\`\`\`` }] };
},
} as InternalTool,
};
Expand Down Expand Up @@ -116,7 +116,7 @@ USAGE EXAMPLES:
if (!v) {
return { content: [{ type: 'text', text: `Dataset '${parsed.datasetId}' not found.` }] };
}
return { content: [{ type: 'text', text: JSON.stringify(v) }] };
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(v)}\n\`\`\`` }] };
},
} as InternalTool,
};
Expand Down Expand Up @@ -191,7 +191,7 @@ USAGE EXAMPLES:
return {
content: [{
type: 'text',
text: JSON.stringify(schema),
text: `\`\`\`json\n${JSON.stringify(schema)}\n\`\`\``,
}],
};
},
Expand Down
2 changes: 1 addition & 1 deletion src/tools/dataset_collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ USAGE EXAMPLES:
desc: parsed.desc,
unnamed: parsed.unnamed,
});
return { content: [{ type: 'text', text: JSON.stringify(datasets) }] };
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(datasets, null, 2)}\n\`\`\`` }] };
},
} as InternalTool,
};
2 changes: 1 addition & 1 deletion src/tools/get-actor-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ Note: This tool is automatically included if the Apify MCP Server is configured
.map((item) => cleanEmptyProperties(item))
.filter((item) => item !== undefined);

let outputText = JSON.stringify(cleanedItems);
let outputText = `\`\`\`json\n${JSON.stringify(cleanedItems)}\n\`\`\``;
let truncated = false;
if (outputText.length > TOOL_MAX_OUTPUT_CHARS) {
outputText = outputText.slice(0, TOOL_MAX_OUTPUT_CHARS);
Expand Down
6 changes: 3 additions & 3 deletions src/tools/key_value_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ USAGE EXAMPLES:
const parsed = getKeyValueStoreArgs.parse(args);
const client = new ApifyClient({ token: apifyToken });
const store = await client.keyValueStore(parsed.storeId).get();
return { content: [{ type: 'text', text: JSON.stringify(store) }] };
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(store)}\n\`\`\`` }] };
},
} as InternalTool,
};
Expand Down Expand Up @@ -82,7 +82,7 @@ USAGE EXAMPLES:
exclusiveStartKey: parsed.exclusiveStartKey,
limit: parsed.limit,
});
return { content: [{ type: 'text', text: JSON.stringify(keys) }] };
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(keys)}\n\`\`\`` }] };
},
} as InternalTool,
};
Expand Down Expand Up @@ -120,7 +120,7 @@ USAGE EXAMPLES:
const parsed = getKeyValueStoreRecordArgs.parse(args);
const client = new ApifyClient({ token: apifyToken });
const record = await client.keyValueStore(parsed.storeId).getRecord(parsed.recordKey);
return { content: [{ type: 'text', text: JSON.stringify(record) }] };
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(record)}\n\`\`\`` }] };
},
} as InternalTool,
};
2 changes: 1 addition & 1 deletion src/tools/key_value_store_collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ USAGE EXAMPLES:
desc: parsed.desc,
unnamed: parsed.unnamed,
});
return { content: [{ type: 'text', text: JSON.stringify(stores) }] };
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(stores, null, 2)}\n\`\`\`` }] };
},
} as InternalTool,
};
4 changes: 2 additions & 2 deletions src/tools/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ USAGE EXAMPLES:
if (!v) {
return { content: [{ type: 'text', text: `Run with ID '${parsed.runId}' not found.` }] };
}
return { content: [{ type: 'text', text: JSON.stringify(v) }] };
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(v)}\n\`\`\`` }] };
},
} as InternalTool,
};
Expand Down Expand Up @@ -116,7 +116,7 @@ USAGE EXAMPLES:
const parsed = abortRunArgs.parse(args);
const client = new ApifyClient({ token: apifyToken });
const v = await client.run(parsed.runId).abort({ gracefully: parsed.gracefully });
return { content: [{ type: 'text', text: JSON.stringify(v) }] };
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(v)}\n\`\`\`` }] };
},
} as InternalTool,
};
2 changes: 1 addition & 1 deletion src/tools/run_collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ USAGE EXAMPLES:
const parsed = getUserRunsListArgs.parse(args);
const client = new ApifyClient({ token: apifyToken });
const runs = await client.runs().list({ limit: parsed.limit, offset: parsed.offset, desc: parsed.desc, status: parsed.status });
return { content: [{ type: 'text', text: JSON.stringify(runs) }] };
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(runs, null, 2)}\n\`\`\`` }] };
},
} as InternalTool,
};
19 changes: 15 additions & 4 deletions tests/integration/suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ function expectToolNamesToContain(names: string[], toolNames: string[] = []) {
toolNames.forEach((name) => expect(names).toContain(name));
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function extractJsonFromMarkdown(text: string): any {
// Handle markdown code blocks like ```json
const jsonMatch = text.match(/```json\n([\s\S]*?)\n```/);
if (jsonMatch) {
return JSON.parse(jsonMatch[1]);
}
// If no markdown formatting, assume it's raw JSON
return JSON.parse(text);
}

async function callPythonExampleActor(client: Client, selectedToolName: string) {
const result = await client.callTool({
name: selectedToolName,
Expand All @@ -53,7 +64,7 @@ async function callPythonExampleActor(client: Client, selectedToolName: string)
};
// Parse the JSON to compare objects regardless of property order
const actual = content[0];
expect(JSON.parse(actual.text)).toEqual(JSON.parse(expected.text));
expect(extractJsonFromMarkdown(actual.text)).toEqual(JSON.parse(expected.text));
expect(actual.type).toBe(expected.type);
}

Expand Down Expand Up @@ -836,7 +847,7 @@ export function createIntegrationTestsSuite(

expect(outputResult.content).toBeDefined();
const outputContent = outputResult.content as { text: string; type: string }[];
const output = JSON.parse(outputContent[0].text);
const output = extractJsonFromMarkdown(outputContent[0].text);
expect(Array.isArray(output)).toBe(true);
expect(output.length).toBeGreaterThan(0);
expect(output[0]).toHaveProperty('metadata.title');
Expand Down Expand Up @@ -894,7 +905,7 @@ export function createIntegrationTestsSuite(
// Validate the output contains the expected structure with metadata.title
expect(outputResult.content).toBeDefined();
const outputContent = outputResult.content as { text: string; type: string }[];
const output = JSON.parse(outputContent[0].text);
const output = extractJsonFromMarkdown(outputContent[0].text);
expect(Array.isArray(output)).toBe(true);
expect(output.length).toBeGreaterThan(0);
expect(output[0]).toHaveProperty('metadata.title');
Expand Down Expand Up @@ -935,7 +946,7 @@ export function createIntegrationTestsSuite(

expect(outputResult.content).toBeDefined();
const outputContent = outputResult.content as { text: string; type: string }[];
const output = JSON.parse(outputContent[0].text);
const output = extractJsonFromMarkdown(outputContent[0].text);
expect(Array.isArray(output)).toBe(true);
expect(output.length).toBe(1);
expect(output[0]).toHaveProperty('first_number', input.first_number);
Expand Down