@agentdesk/workflows-mcp
Version:
MCP workflow orchestration tool with presets for thinking, coding and more
363 lines (325 loc) • 10.1 kB
text/typescript
/// <reference types="node" />
/// <reference types="mocha" />
import { expect } from "chai";
import {
validateToolConfig,
convertParametersToJsonSchema,
ParameterConfig,
DevToolsConfig,
loadConfigSync,
} from "../src/config.js";
import * as fs from "fs";
import * as path from "path";
import { fileURLToPath } from "url";
import { McpTestClient } from "../src/client.js";
// Create dirname equivalent for ES modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Path to test workflows
const TEST_WORKFLOWS_DIR = path.join(__dirname, "test-workflows", ".workflows");
describe("Tool Parameters Configuration", function () {
describe("Parameter Validation", function () {
it("should validate basic parameter types", function () {
const config: DevToolsConfig = {
testTool: {
name: "test_tool",
parameters: {
stringParam: {
type: "string",
description: "A string parameter",
},
numberParam: {
type: "number",
description: "A number parameter",
},
booleanParam: {
type: "boolean",
description: "A boolean parameter",
},
},
},
};
const result = validateToolConfig(config, "testTool");
expect(result).to.be.null;
});
it("should validate enum parameter type", function () {
const config: DevToolsConfig = {
testTool: {
name: "test_tool",
parameters: {
enumParam: {
type: "enum",
enum: ["option1", "option2", "option3"],
description: "An enum parameter",
},
},
},
};
const result = validateToolConfig(config, "testTool");
expect(result).to.be.null;
});
it("should catch invalid parameter type", function () {
const config: DevToolsConfig = {
testTool: {
name: "test_tool",
parameters: {
badParam: {
// @ts-ignore - Intentionally using an invalid type for testing
type: "invalid",
description: "A parameter with invalid type",
},
},
},
};
const result = validateToolConfig(config, "testTool");
expect(result).to.not.be.null;
expect(result).to.contain("invalid type");
});
it("should catch enum parameter without enum values", function () {
const config: DevToolsConfig = {
testTool: {
name: "test_tool",
parameters: {
enumParam: {
type: "enum",
description: "An enum parameter without enum values",
},
},
},
};
const result = validateToolConfig(config, "testTool");
expect(result).to.not.be.null;
expect(result).to.contain("must have a non-empty enum array");
});
});
describe("JSON Schema Conversion", function () {
it("should convert string parameter to JSON Schema", function () {
const parameters: Record<string, ParameterConfig> = {
query: {
type: "string",
description: "Search query",
required: true,
},
};
const schema = convertParametersToJsonSchema(parameters);
expect(schema).to.deep.equal({
type: "object",
properties: {
query: {
type: "string",
description: "Search query",
},
},
required: ["query"],
});
});
it("should convert number parameter to JSON Schema", function () {
const parameters: Record<string, ParameterConfig> = {
limit: {
type: "number",
description: "Result limit",
default: 10,
},
};
const schema = convertParametersToJsonSchema(parameters);
expect(schema).to.deep.equal({
type: "object",
properties: {
limit: {
type: "number",
description: "Result limit",
default: 10,
},
},
});
});
it("should convert enum parameter to JSON Schema", function () {
const parameters: Record<string, ParameterConfig> = {
sortOrder: {
type: "enum",
enum: ["asc", "desc"],
description: "Sort order",
default: "asc",
},
};
const schema = convertParametersToJsonSchema(parameters);
expect(schema).to.deep.equal({
type: "object",
properties: {
sortOrder: {
type: "string",
enum: ["asc", "desc"],
description: "Sort order",
default: "asc",
},
},
});
});
it("should convert multiple parameters to JSON Schema", function () {
const parameters: Record<string, ParameterConfig> = {
query: {
type: "string",
description: "Search query",
required: true,
},
limit: {
type: "number",
description: "Result limit",
default: 10,
},
includeArchived: {
type: "boolean",
description: "Include archived items",
default: false,
},
sortOrder: {
type: "enum",
enum: ["asc", "desc"],
description: "Sort order",
default: "asc",
},
};
const schema = convertParametersToJsonSchema(parameters);
expect(schema).to.deep.equal({
type: "object",
properties: {
query: {
type: "string",
description: "Search query",
},
limit: {
type: "number",
description: "Result limit",
default: 10,
},
includeArchived: {
type: "boolean",
description: "Include archived items",
default: false,
},
sortOrder: {
type: "string",
enum: ["asc", "desc"],
description: "Sort order",
default: "asc",
},
},
required: ["query"],
});
});
});
describe("Tool Configuration Loading", function () {
before(function () {
// Create test config file with parameters
const configContent = `
parameterized_tool:
name: "param_tool"
description: "Tool with parameters"
parameters:
query:
type: "string"
description: "The search query"
required: true
limit:
type: "number"
description: "Maximum number of results"
default: 10
includeArchived:
type: "boolean"
description: "Whether to include archived items"
default: false
filterType:
type: "enum"
enum: ["all", "recent", "popular"]
description: "Type of filter to apply"
default: "all"
prompt: |
This is a test tool that uses parameters.
`;
// Ensure test directory exists
if (!fs.existsSync(TEST_WORKFLOWS_DIR)) {
fs.mkdirSync(TEST_WORKFLOWS_DIR, { recursive: true });
}
// Write test config file
fs.writeFileSync(
path.join(TEST_WORKFLOWS_DIR, "parameters.yaml"),
configContent
);
});
after(function () {
// Clean up test config file
const configPath = path.join(TEST_WORKFLOWS_DIR, "parameters.yaml");
if (fs.existsSync(configPath)) {
fs.unlinkSync(configPath);
}
});
it("should load configuration with parameters", function () {
const config = loadConfigSync(TEST_WORKFLOWS_DIR);
// Verify the config was loaded correctly
expect(config).to.have.property("parameterized_tool");
// TypeScript safety - we already checked the property exists
if (!config.parameterized_tool) {
throw new Error("parameterized_tool not found in config");
}
expect(config.parameterized_tool).to.have.property("parameters");
// Verify parameters
const params = config.parameterized_tool.parameters;
if (!params) {
throw new Error("parameters not found in parameterized_tool");
}
expect(params).to.have.property("query");
expect(params.query.type).to.equal("string");
expect(params.query.required).to.be.true;
expect(params).to.have.property("limit");
expect(params.limit.type).to.equal("number");
expect(params.limit.default).to.equal(10);
expect(params).to.have.property("filterType");
expect(params.filterType.type).to.equal("enum");
expect(params.filterType.enum).to.deep.equal([
"all",
"recent",
"popular",
]);
});
});
describe("Thinking Mode Parameters", function () {
it("should validate thinking_mode parameters", function () {
// Create a test config with thinking_mode parameters
const config: DevToolsConfig = {
thinking_mode: {
name: "thinking_mode",
description: "Test thinking mode parameters",
parameters: {
thought: {
type: "string",
description: "A thought to deeply reflect upon",
required: true,
},
},
},
};
// Validate the configuration
const result = validateToolConfig(config, "thinking_mode");
expect(result).to.be.null;
// Make sure thinking_mode is defined in the config
if (!config.thinking_mode || !config.thinking_mode.parameters) {
throw new Error("thinking_mode or its parameters are not defined");
}
// Convert parameters to JSON Schema
const schema = convertParametersToJsonSchema(
config.thinking_mode.parameters
);
// Verify the schema
expect(schema).to.deep.equal({
type: "object",
properties: {
thought: {
type: "string",
description: "A thought to deeply reflect upon",
},
},
required: ["thought"],
});
});
});
});