diff --git a/README.md b/README.md index 9aa712b..cf61d1a 100644 --- a/README.md +++ b/README.md @@ -273,6 +273,40 @@ Start the MCP server: npm run start ``` +### run mcp server + +if you want test sse server or streamable server, you can use the following command: + +```bash +npm run dev:sse +# or +npm run dev:streamable +``` + +and then change the transport type and url in the MCP Inspector configuration, such as: + +SSE + +```json +{ + "transport": { + "type": "sse", + "url": "http://localhost:1122/sse" + } +} +``` + +streamable + +```json +{ + "transport": { + "type": "streamable", + "url": "http://localhost:1122/streamable" + } +} +``` + ## 📄 License MIT@[AntV](https://github.com/antvis). diff --git a/__tests__/charts/area.json b/__tests__/charts/area.json index 14ed234..97c1ffc 100644 --- a/__tests__/charts/area.json +++ b/__tests__/charts/area.json @@ -2,12 +2,30 @@ "name": "generate_area_chart", "description": "Generate a area chart to show data trends under continuous independent variables and observe the overall data trend, such as, displacement = velocity (average or instantaneous) × time: s = v × t. If the x-axis is time (t) and the y-axis is velocity (v) at each moment, an area chart allows you to observe the trend of velocity over time and infer the distance traveled by the area's size.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for area chart, such as, [{ time: '2018', value: 99.9 }].", "type": "array", - "minItems": 1, + "prefixItems": [ + { + "type": "object", + "properties": { + "time": { + "type": "string" + }, + "value": { + "type": "number" + }, + "group": { + "type": "string" + } + }, + "required": ["time", "value"], + "additionalProperties": false + } + ], "items": { "type": "object", "properties": { @@ -21,17 +39,18 @@ "type": "string" } }, - "required": ["time", "value"] + "required": ["time", "value"], + "additionalProperties": false }, - "description": "Data for area chart, such as, [{ time: '2018', value: 99.9 }]." + "minItems": 1 }, "stack": { - "type": "boolean", - "default": false, - "description": "Whether stacking is enabled. When enabled, area charts require a 'group' field in the data." + "description": "Whether stacking is enabled. When enabled, area charts require a 'group' field in the data.", + "type": "boolean" }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -39,52 +58,56 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false }, "theme": { - "default": "default", "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", "description": "Set the width of chart, default is 600.", - "default": 600 + "type": "number" }, "height": { - "type": "number", "description": "Set the height of chart, default is 400.", - "default": 400 + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" }, "axisXTitle": { - "type": "string", - "default": "", - "description": "Set the x-axis title of chart." + "description": "Set the x-axis title of chart.", + "type": "string" }, "axisYTitle": { - "type": "string", - "default": "", - "description": "Set the y-axis title of chart." + "description": "Set the y-axis title of chart.", + "type": "string" } }, - "required": ["data"] + "required": [ + "data", + "stack", + "theme", + "width", + "height", + "title", + "axisXTitle", + "axisYTitle" + ], + "additionalProperties": false } } diff --git a/__tests__/charts/bar.json b/__tests__/charts/bar.json index 4433678..9d26390 100644 --- a/__tests__/charts/bar.json +++ b/__tests__/charts/bar.json @@ -2,12 +2,30 @@ "name": "generate_bar_chart", "description": "Generate a horizontal bar chart to show data for numerical comparisons among different categories, such as, comparing categorical data and for horizontal comparisons.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for bar chart, such as, [{ category: '分类一', value: 10 }, { category: '分类二', value: 20 }], when grouping or stacking is needed for bar, the data should contain a `group` field, such as, when [{ category: '北京', value: 825, group: '油车' }, { category: '北京', value: 1000, group: '电车' }].", "type": "array", - "minItems": 1, + "prefixItems": [ + { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "value": { + "type": "number" + }, + "group": { + "type": "string" + } + }, + "required": ["category", "value"], + "additionalProperties": false + } + ], "items": { "type": "object", "properties": { @@ -21,22 +39,22 @@ "type": "string" } }, - "required": ["category", "value"] + "required": ["category", "value"], + "additionalProperties": false }, - "description": "Data for bar chart, such as, [{ category: '分类一', value: 10 }, { category: '分类二', value: 20 }], when grouping or stacking is needed for bar, the data should contain a `group` field, such as, when [{ category: '北京', value: 825, group: '油车' }, { category: '北京', value: 1000, group: '电车' }]." + "minItems": 1 }, "group": { - "type": "boolean", - "default": false, - "description": "Whether grouping is enabled. When enabled, bar charts require a 'group' field in the data. When `group` is true, `stack` should be false." + "description": "Whether grouping is enabled. When enabled, bar charts require a 'group' field in the data. When `group` is true, `stack` should be false.", + "type": "boolean" }, "stack": { - "type": "boolean", - "default": true, - "description": "Whether stacking is enabled. When enabled, bar charts require a 'group' field in the data. When `stack` is true, `group` should be false." + "description": "Whether stacking is enabled. When enabled, bar charts require a 'group' field in the data. When `stack` is true, `group` should be false.", + "type": "boolean" }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -44,52 +62,57 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false }, "theme": { - "default": "default", "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", "description": "Set the width of chart, default is 600.", - "default": 600 + "type": "number" }, "height": { - "type": "number", "description": "Set the height of chart, default is 400.", - "default": 400 + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" }, "axisXTitle": { - "type": "string", - "default": "", - "description": "Set the x-axis title of chart." + "description": "Set the x-axis title of chart.", + "type": "string" }, "axisYTitle": { - "type": "string", - "default": "", - "description": "Set the y-axis title of chart." + "description": "Set the y-axis title of chart.", + "type": "string" } }, - "required": ["data"] + "required": [ + "data", + "group", + "stack", + "theme", + "width", + "height", + "title", + "axisXTitle", + "axisYTitle" + ], + "additionalProperties": false } } diff --git a/__tests__/charts/boxplot.json b/__tests__/charts/boxplot.json index adcf3e3..cdc98d2 100644 --- a/__tests__/charts/boxplot.json +++ b/__tests__/charts/boxplot.json @@ -2,33 +2,57 @@ "name": "generate_boxplot_chart", "description": "Generate a boxplot chart to show data for statistical summaries among different categories, such as, comparing the distribution of data points across categories.", "inputSchema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for boxplot chart, such as, [{ category: '分类一', value: 10 }] or [{ category: '分类二', value: 20, group: '组别一' }].", "type": "array", + "prefixItems": [ + { + "type": "object", + "properties": { + "category": { + "description": "Category of the data point, such as '分类一'.", + "type": "string" + }, + "value": { + "description": "Value of the data point, such as 10.", + "type": "number" + }, + "group": { + "description": "Optional group for the data point, used for grouping in the boxplot.", + "type": "string" + } + }, + "required": ["category", "value"], + "additionalProperties": false + } + ], "items": { "type": "object", "properties": { "category": { - "type": "string", - "description": "Category of the data point, such as '分类一'." + "description": "Category of the data point, such as '分类一'.", + "type": "string" }, "value": { - "type": "number", - "description": "Value of the data point, such as 10." + "description": "Value of the data point, such as 10.", + "type": "number" }, "group": { - "type": "string", - "description": "Optional group for the data point, used for grouping in the boxplot." + "description": "Optional group for the data point, used for grouping in the boxplot.", + "type": "string" } }, - "required": ["category", "value"] + "required": ["category", "value"], + "additionalProperties": false }, - "minItems": 1, - "description": "Data for boxplot chart, such as, [{ category: '分类一', value: 10 }] or [{ category: '分类二', value: 20, group: '组别一' }]." + "minItems": 1 }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -36,53 +60,55 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false }, "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", "type": "string", - "enum": ["default", "academy", "dark"], - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'." + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", - "default": 600, - "description": "Set the width of chart, default is 600." + "description": "Set the width of chart, default is 600.", + "type": "number" }, "height": { - "type": "number", - "default": 400, - "description": "Set the height of chart, default is 400." + "description": "Set the height of chart, default is 400.", + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" }, "axisXTitle": { - "type": "string", - "default": "", - "description": "Set the x-axis title of chart." + "description": "Set the x-axis title of chart.", + "type": "string" }, "axisYTitle": { - "type": "string", - "default": "", - "description": "Set the y-axis title of chart." + "description": "Set the y-axis title of chart.", + "type": "string" } }, - "required": ["data"], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": [ + "data", + "theme", + "width", + "height", + "title", + "axisXTitle", + "axisYTitle" + ], + "additionalProperties": false } } diff --git a/__tests__/charts/charts.spec.ts b/__tests__/charts/charts.spec.ts index 65725e9..b4240be 100644 --- a/__tests__/charts/charts.spec.ts +++ b/__tests__/charts/charts.spec.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from "vitest"; +import z from "zod"; import * as expectedCharts from "."; import * as actualCharts from "../../src/charts"; @@ -12,7 +13,10 @@ describe("charts schema check", () => { const schema = actualCharts[chartName].tool; const rightChart = expectedCharts[chartName]; - expect(schema).toEqual(rightChart); + expect({ + ...schema, + inputSchema: z.toJSONSchema(schema.inputSchema), + }).toEqual(rightChart); }); } }); diff --git a/__tests__/charts/column.json b/__tests__/charts/column.json index 4582a49..d9f76cc 100644 --- a/__tests__/charts/column.json +++ b/__tests__/charts/column.json @@ -2,12 +2,30 @@ "name": "generate_column_chart", "description": "Generate a column chart, which are best for comparing categorical data, such as, when values are close, column charts are preferable because our eyes are better at judging height than other visual elements like area or angles.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for column chart, such as, [{ category: '分类一', value: 10 }, { category: '分类二', value: 20 }], when grouping or stacking is needed for column, the data should contain a `group` field, such as, when [{ category: '北京', value: 825, group: '油车' }, { category: '北京', value: 1000, group: '电车' }].", "type": "array", - "minItems": 1, + "prefixItems": [ + { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "value": { + "type": "number" + }, + "group": { + "type": "string" + } + }, + "required": ["category", "value"], + "additionalProperties": false + } + ], "items": { "type": "object", "properties": { @@ -21,22 +39,22 @@ "type": "string" } }, - "required": ["category", "value"] + "required": ["category", "value"], + "additionalProperties": false }, - "description": "Data for column chart, such as, [{ category: '分类一', value: 10 }, { category: '分类二', value: 20 }], when grouping or stacking is needed for column, the data should contain a `group` field, such as, when [{ category: '北京', value: 825, group: '油车' }, { category: '北京', value: 1000, group: '电车' }]." + "minItems": 1 }, "group": { - "type": "boolean", - "default": true, - "description": "Whether grouping is enabled. When enabled, column charts require a 'group' field in the data. When `group` is true, `stack` should be false." + "description": "Whether grouping is enabled. When enabled, column charts require a 'group' field in the data. When `group` is true, `stack` should be false.", + "type": "boolean" }, "stack": { - "type": "boolean", - "default": false, - "description": "Whether stacking is enabled. When enabled, column charts require a 'group' field in the data. When `stack` is true, `group` should be false." + "description": "Whether stacking is enabled. When enabled, column charts require a 'group' field in the data. When `stack` is true, `group` should be false.", + "type": "boolean" }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -44,52 +62,57 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false }, "theme": { - "default": "default", "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", "description": "Set the width of chart, default is 600.", - "default": 600 + "type": "number" }, "height": { - "type": "number", "description": "Set the height of chart, default is 400.", - "default": 400 + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" }, "axisXTitle": { - "type": "string", - "default": "", - "description": "Set the x-axis title of chart." + "description": "Set the x-axis title of chart.", + "type": "string" }, "axisYTitle": { - "type": "string", - "default": "", - "description": "Set the y-axis title of chart." + "description": "Set the y-axis title of chart.", + "type": "string" } }, - "required": ["data"] + "required": [ + "data", + "group", + "stack", + "theme", + "width", + "height", + "title", + "axisXTitle", + "axisYTitle" + ], + "additionalProperties": false } } diff --git a/__tests__/charts/district-map.json b/__tests__/charts/district-map.json index 3e15073..6671865 100644 --- a/__tests__/charts/district-map.json +++ b/__tests__/charts/district-map.json @@ -2,71 +2,62 @@ "name": "generate_district_map", "description": "Generates regional distribution maps, which are usually used to show the administrative divisions and coverage of a dataset. It is not suitable for showing the distribution of specific locations, such as urban administrative divisions, GDP distribution maps of provinces and cities across the country, etc. This tool is limited to generating data maps within China.", "inputSchema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "title": { - "type": "string", - "description": "The map title should not exceed 16 characters. The content should be consistent with the information the map wants to convey and should be accurate, rich, creative, and attractive." + "description": "The map title should not exceed 16 characters. The content should be consistent with the information the map wants to convey and should be accurate, rich, creative, and attractive.", + "type": "string" }, "data": { + "description": "Administrative division data, lower-level administrative divisions are optional. There are usually two scenarios: one is to simply display the regional composition, only `fillColor` needs to be configured, and all administrative divisions are consistent, representing that all blocks are connected as one; the other is the regional data distribution scenario, first determine the `dataType`, `dataValueUnit` and `dataLabel` configurations, `dataValue` should be a meaningful value and consistent with the meaning of dataType, and then determine the style configuration. The `fillColor` configuration represents the default fill color for areas without data. Lower-level administrative divisions do not need `fillColor` configuration, and their fill colors are determined by the `colors` configuration (If `dataType` is \"number\", only one base color (warm color) is needed in the list to calculate the continuous data mapping color band; if `dataType` is \"enum\", the number of color values in the list is equal to the number of enumeration values (contrast colors)). If `subdistricts` has a value, `showAllSubdistricts` must be set to true. For example, {\"title\": \"陕西省地级市分布图\", \"data\": {\"name\": \"陕西省\", \"showAllSubdistricts\": true, \"dataLabel\": \"城市\", \"dataType\": \"enum\", \"colors\": [\"#4ECDC4\", \"#A5D8FF\"], \"subdistricts\": [{\"name\": \"西安市\", \"dataValue\": \"省会\"}, {\"name\": \"宝鸡市\", \"dataValue\": \"地级市\"}, {\"name\": \"咸阳市\", \"dataValue\": \"地级市\"}, {\"name\": \"铜川市\", \"dataValue\": \"地级市\"}, {\"name\": \"渭南市\", \"dataValue\": \"地级市\"}, {\"name\": \"延安市\", \"dataValue\": \"地级市\"}, {\"name\": \"榆林市\", \"dataValue\": \"地级市\"}, {\"name\": \"汉中市\", \"dataValue\": \"地级市\"}, {\"name\": \"安康市\", \"dataValue\": \"地级市\"}, {\"name\": \"商洛市\", \"dataValue\": \"地级市\"}]}, \"width\": 1000, \"height\": 1000}.", "type": "object", "properties": { "name": { - "type": "string", - "description": "Keywords for the Chinese name of an administrative region (must be within China), and must be one of China, province, city, district, or county. The name should be more specific and add attributive descriptions, for example, \"西安市\" is better than \"西安\", \"杭州西湖区\" is better than \"西湖区\". It cannot be a specific place name or a vague name, such as \"其它\"." + "description": "Keywords for the Chinese name of an administrative region (must be within China), and must be one of China, province, city, district, or county. The name should be more specific and add attributive descriptions, for example, \"西安市\" is better than \"西安\", \"杭州西湖区\" is better than \"西湖区\". It cannot be a specific place name or a vague name, such as \"其它\".", + "type": "string" }, "style": { + "description": "Style settings.", "type": "object", "properties": { "fillColor": { - "type": "string", - "description": "Fill color, rgb or rgba format." + "description": "Fill color, rgb or rgba format.", + "type": "string" } }, - "description": "Style settings." + "additionalProperties": false }, "colors": { + "description": "Data color list, in rgb or rgba format.", "type": "array", "items": { "type": "string" - }, - "default": [ - "#1783FF", - "#00C9C9", - "#F0884D", - "#D580FF", - "#7863FF", - "#60C42D", - "#BD8F24", - "#FF80CA", - "#2491B3", - "#17C76F" - ], - "description": "Data color list, in rgb or rgba format." + } }, "dataType": { + "description": "The type of the data value, numeric or enumeration type", "type": "string", - "enum": ["number", "enum"], - "description": "The type of the data value, numeric or enumeration type" + "enum": ["number", "enum"] }, "dataLabel": { - "type": "string", - "description": "Data label, such as \"GDP\"" + "description": "Data label, such as \"GDP\"", + "type": "string" }, "dataValue": { - "type": "string", - "description": "Data value, numeric string or enumeration string." + "description": "Data value, numeric string or enumeration string.", + "type": "string" }, "dataValueUnit": { - "type": "string", - "description": "Data unit, such as \"万亿\"" + "description": "Data unit, such as \"万亿\"", + "type": "string" }, "showAllSubdistricts": { - "type": "boolean", - "default": false, - "description": "Whether to display all subdistricts." + "description": "Whether to display all subdistricts.", + "type": "boolean" }, "subdistricts": { + "description": "Sub-administrative regions are used to display the regional composition or regional distribution of related data.", "type": "array", "items": { "type": "object", @@ -76,40 +67,39 @@ "type": "string" }, "dataValue": { - "type": "string", - "description": "Data value, numeric string or enumeration string." + "description": "Data value, numeric string or enumeration string.", + "type": "string" }, "style": { "description": "Style settings.", + "type": "object", "properties": { "fillColor": { "description": "Fill color, rgb or rgba format.", "type": "string" } }, - "type": "object" + "additionalProperties": false } }, - "required": ["name"] - }, - "description": "Sub-administrative regions are used to display the regional composition or regional distribution of related data." + "required": ["name"], + "additionalProperties": false + } } }, - "required": ["name"], - "description": "Administrative division data, lower-level administrative divisions are optional. There are usually two scenarios: one is to simply display the regional composition, only `fillColor` needs to be configured, and all administrative divisions are consistent, representing that all blocks are connected as one; the other is the regional data distribution scenario, first determine the `dataType`, `dataValueUnit` and `dataLabel` configurations, `dataValue` should be a meaningful value and consistent with the meaning of dataType, and then determine the style configuration. The `fillColor` configuration represents the default fill color for areas without data. Lower-level administrative divisions do not need `fillColor` configuration, and their fill colors are determined by the `colors` configuration (If `dataType` is \"number\", only one base color (warm color) is needed in the list to calculate the continuous data mapping color band; if `dataType` is \"enum\", the number of color values in the list is equal to the number of enumeration values (contrast colors)). If `subdistricts` has a value, `showAllSubdistricts` must be set to true. For example, {\"title\": \"陕西省地级市分布图\", \"data\": {\"name\": \"陕西省\", \"showAllSubdistricts\": true, \"dataLabel\": \"城市\", \"dataType\": \"enum\", \"colors\": [\"#4ECDC4\", \"#A5D8FF\"], \"subdistricts\": [{\"name\": \"西安市\", \"dataValue\": \"省会\"}, {\"name\": \"宝鸡市\", \"dataValue\": \"地级市\"}, {\"name\": \"咸阳市\", \"dataValue\": \"地级市\"}, {\"name\": \"铜川市\", \"dataValue\": \"地级市\"}, {\"name\": \"渭南市\", \"dataValue\": \"地级市\"}, {\"name\": \"延安市\", \"dataValue\": \"地级市\"}, {\"name\": \"榆林市\", \"dataValue\": \"地级市\"}, {\"name\": \"汉中市\", \"dataValue\": \"地级市\"}, {\"name\": \"安康市\", \"dataValue\": \"地级市\"}, {\"name\": \"商洛市\", \"dataValue\": \"地级市\"}]}, \"width\": 1000, \"height\": 1000}." + "required": ["name", "showAllSubdistricts"], + "additionalProperties": false }, "width": { - "type": "number", - "default": 1600, - "description": "Set the width of map, default is 1600." + "description": "Set the width of map, default is 1600.", + "type": "number" }, "height": { - "type": "number", - "default": 1000, - "description": "Set the height of map, default is 1000." + "description": "Set the height of map, default is 1000.", + "type": "number" } }, - "required": ["title", "data"], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": ["title", "data", "width", "height"], + "additionalProperties": false } } diff --git a/__tests__/charts/dual-axes.json b/__tests__/charts/dual-axes.json index 301d24a..1a9b92a 100644 --- a/__tests__/charts/dual-axes.json +++ b/__tests__/charts/dual-axes.json @@ -2,17 +2,78 @@ "name": "generate_dual_axes_chart", "description": "Generate a dual axes chart which is a combination chart that integrates two different chart types, typically combining a bar chart with a line chart to display both the trend and comparison of data, such as, the trend of sales and profit over time.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { - "theme": { - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "categories": { + "description": "Categories for dual axes chart, such as, ['2015', '2016', '2017'].", + "type": "array", + "prefixItems": [ + { + "type": "string" + } + ], + "items": { + "type": "string" + }, + "minItems": 1 + }, + "series": { + "description": "Series for dual axes chart, such as, [{ type: 'column', data: [91.9, 99.1, 101.6, 114.4, 121], axisYTitle: '销售额' }, { type: 'line', data: [0.055, 0.06, 0.062, 0.07, 0.075], 'axisYTitle': '利润率' }].", + "type": "array", + "prefixItems": [ + { + "type": "object", + "properties": { + "type": { + "description": "The optional value can be 'column' or 'line'.", + "type": "string", + "enum": ["column", "line"] + }, + "data": { + "description": "When type is column, the data represents quantities, such as [91.9, 99.1, 101.6, 114.4, 121]. When type is line, the data represents ratios and its values are recommended to be less than 1, such as [0.055, 0.06, 0.062, 0.07, 0.075].", + "type": "array", + "items": { + "type": "number" + } + }, + "axisYTitle": { + "description": "Set the y-axis title of the chart series, such as, axisYTitle: '销售额'.", + "type": "string" + } + }, + "required": ["type", "data"], + "additionalProperties": false + } + ], + "items": { + "type": "object", + "properties": { + "type": { + "description": "The optional value can be 'column' or 'line'.", + "type": "string", + "enum": ["column", "line"] + }, + "data": { + "description": "When type is column, the data represents quantities, such as [91.9, 99.1, 101.6, 114.4, 121]. When type is line, the data represents ratios and its values are recommended to be less than 1, such as [0.055, 0.06, 0.062, 0.07, 0.075].", + "type": "array", + "items": { + "type": "number" + } + }, + "axisYTitle": { + "description": "Set the y-axis title of the chart series, such as, axisYTitle: '销售额'.", + "type": "string" + } + }, + "required": ["type", "data"], + "additionalProperties": false + }, + "minItems": 1 }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -20,77 +81,51 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false + }, + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", "description": "Set the width of chart, default is 600.", - "default": 600 + "type": "number" }, "height": { - "type": "number", "description": "Set the height of chart, default is 400.", - "default": 400 + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" }, "axisXTitle": { - "type": "string", - "default": "", - "description": "Set the x-axis title of chart." - }, - "categories": { - "type": "array", - "minItems": 1, - "items": { - "type": "string" - }, - "description": "Categories for dual axes chart, such as, ['2015', '2016', '2017']." - }, - "series": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "properties": { - "type": { - "type": "string", - "description": "The optional value can be 'column' or 'line'.", - "enum": ["column", "line"] - }, - "data": { - "type": "array", - "items": { - "type": "number" - }, - "description": "When type is column, the data represents quantities, such as [91.9, 99.1, 101.6, 114.4, 121]. When type is line, the data represents ratios and its values are recommended to be less than 1, such as [0.055, 0.06, 0.062, 0.07, 0.075]." - }, - "axisYTitle": { - "type": "string", - "default": "", - "description": "Set the y-axis title of the chart series, such as, axisYTitle: '销售额'." - } - }, - "required": ["type", "data"] - }, - "description": "Series for dual axes chart, such as, [{ type: 'column', data: [91.9, 99.1, 101.6, 114.4, 121], axisYTitle: '销售额' }, { type: 'line', data: [0.055, 0.06, 0.062, 0.07, 0.075], 'axisYTitle': '利润率' }]." + "description": "Set the x-axis title of chart.", + "type": "string" } }, - "required": ["categories", "series"] + "required": [ + "categories", + "series", + "theme", + "width", + "height", + "title", + "axisXTitle" + ], + "additionalProperties": false } } diff --git a/__tests__/charts/fishbone-diagram.json b/__tests__/charts/fishbone-diagram.json index a2a03eb..36ddd93 100644 --- a/__tests__/charts/fishbone-diagram.json +++ b/__tests__/charts/fishbone-diagram.json @@ -2,6 +2,7 @@ "name": "generate_fishbone_diagram", "description": "Generate a fishbone diagram chart to uses a fish skeleton, like structure to display the causes or effects of a core problem, with the problem as the fish head and the causes/effects as the fish bones. It suits problems that can be split into multiple related factors.", "inputSchema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { @@ -35,51 +36,52 @@ "type": "string" } }, - "required": ["name"] + "required": ["name"], + "additionalProperties": false } } }, - "required": ["name"] + "required": ["name"], + "additionalProperties": false } } }, - "required": ["name"] + "required": ["name"], + "additionalProperties": false } } }, "required": ["name"], - "description": "Data for fishbone diagram chart which is a hierarchical structure, such as, { name: 'main topic', children: [{ name: 'topic 1', children: [{ name: 'subtopic 1-1' }] }] }, and the maximum depth is 3." + "additionalProperties": false }, "style": { + "description": "Custom style configuration for the chart.", "type": "object", "properties": { "texture": { + "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", "type": "string", - "enum": ["default", "rough"], - "default": "default", - "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style." + "enum": ["default", "rough"] } }, - "description": "Custom style configuration for the chart." + "required": ["texture"], + "additionalProperties": false }, "theme": { - "type": "string", - "default": "default", "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", - "default": 600, - "description": "Set the width of chart, default is 600." + "description": "Set the width of chart, default is 600.", + "type": "number" }, "height": { - "type": "number", - "default": 400, - "description": "Set the height of chart, default is 400." + "description": "Set the height of chart, default is 400.", + "type": "number" } }, - "required": ["data"], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": ["data", "theme", "width", "height"], + "additionalProperties": false } } diff --git a/__tests__/charts/flow-diagram.json b/__tests__/charts/flow-diagram.json index d3df6ee..e041729 100644 --- a/__tests__/charts/flow-diagram.json +++ b/__tests__/charts/flow-diagram.json @@ -2,77 +2,90 @@ "name": "generate_flow_diagram", "description": "Generate a flow diagram chart to show the steps and decision points of a process or system, such as, scenarios requiring linear process presentation.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for flow diagram chart, such as, { nodes: [{ name: 'node1' }, { name: 'node2' }], edges: [{ source: 'node1', target: 'node2', name: 'edge1' }] }.", "type": "object", "properties": { "nodes": { "type": "array", - "minItems": 1, + "prefixItems": [ + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ], "items": { + "type": "object", "properties": { "name": { "type": "string" } }, "required": ["name"], - "type": "object" - } + "additionalProperties": false + }, + "minItems": 1 }, "edges": { "type": "array", "items": { + "type": "object", "properties": { - "name": { - "type": "string", - "default": "" - }, "source": { "type": "string" }, "target": { "type": "string" + }, + "name": { + "type": "string" } }, - "required": ["source", "target"], - "type": "object" + "required": ["source", "target", "name"], + "additionalProperties": false } } }, "required": ["nodes", "edges"], - "description": "Data for flow diagram chart, such as, { nodes: [{ name: 'node1' }, { name: 'node2' }], edges: [{ source: 'node1', target: 'node2', name: 'edge1' }] }." - }, - "theme": { - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "additionalProperties": false }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false + }, + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", "description": "Set the width of chart, default is 600.", - "default": 600 + "type": "number" }, "height": { - "type": "number", "description": "Set the height of chart, default is 400.", - "default": 400 + "type": "number" } }, - "required": ["data"] + "required": ["data", "theme", "width", "height"], + "additionalProperties": false } } diff --git a/__tests__/charts/funnel.json b/__tests__/charts/funnel.json index 8517ae3..58d1e6b 100644 --- a/__tests__/charts/funnel.json +++ b/__tests__/charts/funnel.json @@ -2,11 +2,29 @@ "name": "generate_funnel_chart", "description": "Generate a funnel chart to visualize the progressive reduction of data as it passes through stages, such as, the conversion rates of users from visiting a website to completing a purchase.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", "properties": { "data": { "description": "Data for funnel chart, such as, [{ category: '浏览网站', value: 50000 }, { category: '放入购物车', value: 35000 }, { category: '生成订单', value: 25000 }, { category: '支付订单', value: 15000 }, { category: '完成交易', value: 8000 }].", + "type": "array", + "prefixItems": [ + { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "value": { + "type": "number" + } + }, + "required": ["category", "value"], + "additionalProperties": false + } + ], "items": { + "type": "object", "properties": { "category": { "type": "string" @@ -16,24 +34,13 @@ } }, "required": ["category", "value"], - "type": "object" + "additionalProperties": false }, - "minItems": 1, - "type": "array" - }, - "height": { - "default": 400, - "description": "Set the height of chart, default is 400.", - "type": "number" - }, - "theme": { - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "minItems": 1 }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -41,32 +48,39 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false }, - "title": { - "default": "", - "description": "Set the title of chart.", - "type": "string" + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "default": 600, "description": "Set the width of chart, default is 600.", "type": "number" + }, + "height": { + "description": "Set the height of chart, default is 400.", + "type": "number" + }, + "title": { + "description": "Set the title of chart.", + "type": "string" } }, - "required": ["data"], - "type": "object" + "required": ["data", "theme", "width", "height", "title"], + "additionalProperties": false } } diff --git a/__tests__/charts/histogram.json b/__tests__/charts/histogram.json index 174d1e0..e84b891 100644 --- a/__tests__/charts/histogram.json +++ b/__tests__/charts/histogram.json @@ -2,29 +2,29 @@ "name": "generate_histogram_chart", "description": "Generate a histogram chart to show the frequency of data points within a certain range. It can observe data distribution, such as, normal and skewed distributions, and identify data concentration areas and extreme points.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for histogram chart, it should be an array of numbers, such as, [78, 88, 60, 100, 95].", "type": "array", - "minItems": 1, + "prefixItems": [ + { + "type": "number" + } + ], "items": { "type": "number" }, - "description": "Data for histogram chart, it should be an array of numbers, such as, [78, 88, 60, 100, 95]." + "minItems": 1 }, "binNumber": { - "type": "number", - "description": "Number of intervals to define the number of intervals in a histogram, when not specified, a built-in value will be used." - }, - "theme": { - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "description": "Number of intervals to define the number of intervals in a histogram, when not specified, a built-in value will be used.", + "type": "number" }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -32,46 +32,55 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false + }, + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", "description": "Set the width of chart, default is 600.", - "default": 600 + "type": "number" }, "height": { - "type": "number", "description": "Set the height of chart, default is 400.", - "default": 400 + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" }, "axisXTitle": { - "type": "string", - "default": "", - "description": "Set the x-axis title of chart." + "description": "Set the x-axis title of chart.", + "type": "string" }, "axisYTitle": { - "type": "string", - "default": "", - "description": "Set the y-axis title of chart." + "description": "Set the y-axis title of chart.", + "type": "string" } }, - "required": ["data"] + "required": [ + "data", + "theme", + "width", + "height", + "title", + "axisXTitle", + "axisYTitle" + ], + "additionalProperties": false } } diff --git a/__tests__/charts/line.json b/__tests__/charts/line.json index 48318af..4775ffc 100644 --- a/__tests__/charts/line.json +++ b/__tests__/charts/line.json @@ -2,12 +2,27 @@ "name": "generate_line_chart", "description": "Generate a line chart to show trends over time, such as, the ratio of Apple computer sales to Apple's profits changed from 2000 to 2016.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for line chart, it should be an array of objects, each object contains a `time` field and a `value` field, such as, [{ time: '2015', value: 23 }, { time: '2016', value: 32 }].", "type": "array", - "minItems": 1, + "prefixItems": [ + { + "type": "object", + "properties": { + "time": { + "type": "string" + }, + "value": { + "type": "number" + } + }, + "required": ["time", "value"], + "additionalProperties": false + } + ], "items": { "type": "object", "properties": { @@ -18,27 +33,23 @@ "type": "number" } }, - "required": ["time", "value"] + "required": ["time", "value"], + "additionalProperties": false }, - "description": "Data for line chart, it should be an array of objects, each object contains a `time` field and a `value` field, such as, [{ time: '2015', value: 23 }, { time: '2016', value: 32 }]." + "minItems": 1 }, "stack": { - "type": "boolean", - "default": false, - "description": "Whether stacking is enabled. When enabled, line charts require a 'group' field in the data." - }, - "theme": { - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "description": "Whether stacking is enabled. When enabled, line charts require a 'group' field in the data.", + "type": "boolean" }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { - "lineWidth": { - "description": "Line width for the lines of chart, such as 4.", - "type": "number" + "texture": { + "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", + "type": "string", + "enum": ["default", "rough"] }, "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -46,46 +57,55 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, - "texture": { - "default": "default", - "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "lineWidth": { + "description": "Line width for the lines of chart, such as 4.", + "type": "number" } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false + }, + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", "description": "Set the width of chart, default is 600.", - "default": 600 + "type": "number" }, "height": { - "type": "number", "description": "Set the height of chart, default is 400.", - "default": 400 + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" }, "axisXTitle": { - "type": "string", - "default": "", - "description": "Set the x-axis title of chart." + "description": "Set the x-axis title of chart.", + "type": "string" }, "axisYTitle": { - "type": "string", - "default": "", - "description": "Set the y-axis title of chart." + "description": "Set the y-axis title of chart.", + "type": "string" } }, - "required": ["data"] + "required": [ + "data", + "stack", + "theme", + "width", + "height", + "title", + "axisXTitle", + "axisYTitle" + ], + "additionalProperties": false } } diff --git a/__tests__/charts/liquid.json b/__tests__/charts/liquid.json index a58f562..467c387 100644 --- a/__tests__/charts/liquid.json +++ b/__tests__/charts/liquid.json @@ -2,63 +2,60 @@ "name": "generate_liquid_chart", "description": "Generate a liquid chart to visualize a single value as a percentage, such as, the current occupancy rate of a reservoir or the completion percentage of a project.", "inputSchema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "percent": { + "description": "The percentage value to display in the liquid chart, should be a number between 0 and 1, where 1 represents 100%. For example, 0.75 represents 75%.", "type": "number", "minimum": 0, - "maximum": 1, - "description": "The percentage value to display in the liquid chart, should be a number between 0 and 1, where 1 represents 100%. For example, 0.75 represents 75%." + "maximum": 1 }, "shape": { + "description": "The shape of the liquid chart, can be 'circle', 'rect', 'pin', or 'triangle'. Default is 'circle'.", "type": "string", - "enum": ["circle", "rect", "pin", "triangle"], - "default": "circle", - "description": "The shape of the liquid chart, can be 'circle', 'rect', 'pin', or 'triangle'. Default is 'circle'." - }, - "theme": { - "type": "string", - "enum": ["default", "academy", "dark"], - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'." + "enum": ["circle", "rect", "pin", "triangle"] }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", "type": "string" }, - "color": { - "description": "Custom color for the liquid chart, if not specified, defaults to the theme color.", - "type": "string" - }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], + "type": "string", + "enum": ["default", "rough"] + }, + "color": { + "description": "Custom color for the liquid chart, if not specified, defaults to the theme color.", "type": "string" } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false + }, + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", - "default": 600, - "description": "Set the width of chart, default is 600." + "description": "Set the width of chart, default is 600.", + "type": "number" }, "height": { - "type": "number", - "default": 400, - "description": "Set the height of chart, default is 400." + "description": "Set the height of chart, default is 400.", + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" } }, - "required": ["percent"], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": ["percent", "shape", "theme", "width", "height", "title"], + "additionalProperties": false } } diff --git a/__tests__/charts/mind-map.json b/__tests__/charts/mind-map.json index 0d03f2b..aedd989 100644 --- a/__tests__/charts/mind-map.json +++ b/__tests__/charts/mind-map.json @@ -2,6 +2,7 @@ "name": "generate_mind_map", "description": "Generate a mind map chart to organizes and presents information in a hierarchical structure with branches radiating from a central topic, such as, a diagram showing the relationship between a main topic and its subtopics.", "inputSchema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { @@ -35,51 +36,52 @@ "type": "string" } }, - "required": ["name"] + "required": ["name"], + "additionalProperties": false } } }, - "required": ["name"] + "required": ["name"], + "additionalProperties": false } } }, - "required": ["name"] + "required": ["name"], + "additionalProperties": false } } }, "required": ["name"], - "description": "Data for mind map chart which is a hierarchical structure, such as, { name: 'main topic', children: [{ name: 'topic 1', children: [{ name:'subtopic 1-1' }] }, and the maximum depth is 3." - }, - "theme": { - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "additionalProperties": false }, "style": { + "description": "Custom style configuration for the chart.", "type": "object", "properties": { "texture": { + "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", "type": "string", - "enum": ["default", "rough"], - "default": "default", - "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style." + "enum": ["default", "rough"] } }, - "description": "Custom style configuration for the chart." + "required": ["texture"], + "additionalProperties": false + }, + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", - "default": 600, - "description": "Set the width of chart, default is 600." + "description": "Set the width of chart, default is 600.", + "type": "number" }, "height": { - "type": "number", - "default": 400, - "description": "Set the height of chart, default is 400." + "description": "Set the height of chart, default is 400.", + "type": "number" } }, - "required": ["data"], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": ["data", "theme", "width", "height"], + "additionalProperties": false } } diff --git a/__tests__/charts/network-graph.json b/__tests__/charts/network-graph.json index 64dc54b..a05ec62 100644 --- a/__tests__/charts/network-graph.json +++ b/__tests__/charts/network-graph.json @@ -2,77 +2,90 @@ "name": "generate_network_graph", "description": "Generate a network graph chart to show relationships (edges) between entities (nodes), such as, relationships between people in social networks.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for network graph chart, such as, { nodes: [{ name: 'node1' }, { name: 'node2' }], edges: [{ source: 'node1', target: 'node2', name: 'edge1' }] }", "type": "object", "properties": { "nodes": { "type": "array", - "minItems": 1, + "prefixItems": [ + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + } + ], "items": { + "type": "object", "properties": { "name": { "type": "string" } }, "required": ["name"], - "type": "object" - } + "additionalProperties": false + }, + "minItems": 1 }, "edges": { "type": "array", "items": { + "type": "object", "properties": { - "name": { - "type": "string", - "default": "" - }, "source": { "type": "string" }, "target": { "type": "string" + }, + "name": { + "type": "string" } }, - "required": ["source", "target"], - "type": "object" + "required": ["source", "target", "name"], + "additionalProperties": false } } }, "required": ["nodes", "edges"], - "description": "Data for network graph chart, such as, { nodes: [{ name: 'node1' }, { name: 'node2' }], edges: [{ source: 'node1', target: 'node2', name: 'edge1' }] }" - }, - "theme": { - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "additionalProperties": false }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false + }, + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", "description": "Set the width of chart, default is 600.", - "default": 600 + "type": "number" }, "height": { - "type": "number", "description": "Set the height of chart, default is 400.", - "default": 400 + "type": "number" } }, - "required": ["data"] + "required": ["data", "theme", "width", "height"], + "additionalProperties": false } } diff --git a/__tests__/charts/organization-chart.json b/__tests__/charts/organization-chart.json index 4796037..0327893 100644 --- a/__tests__/charts/organization-chart.json +++ b/__tests__/charts/organization-chart.json @@ -2,9 +2,11 @@ "name": "generate_organization_chart", "description": "Generate an organization chart to visualize the hierarchical structure of an organization, such as, a diagram showing the relationship between a CEO and their direct reports.", "inputSchema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for organization chart which is a hierarchical structure, such as, { name: 'CEO', description: 'Chief Executive Officer', children: [{ name: 'CTO', description: 'Chief Technology Officer', children: [{ name: 'Dev Manager', description: 'Development Manager' }] }] }, and the maximum depth is 3.", "type": "object", "properties": { "name": { @@ -47,57 +49,57 @@ "type": "string" } }, - "required": ["name"] + "required": ["name"], + "additionalProperties": false } } }, - "required": ["name"] + "required": ["name"], + "additionalProperties": false } } }, - "required": ["name"] + "required": ["name"], + "additionalProperties": false } } }, "required": ["name"], - "description": "Data for organization chart which is a hierarchical structure, such as, { name: 'CEO', description: 'Chief Executive Officer', children: [{ name: 'CTO', description: 'Chief Technology Officer', children: [{ name: 'Dev Manager', description: 'Development Manager' }] }] }, and the maximum depth is 3." + "additionalProperties": false }, "orient": { + "description": "Orientation of the organization chart, either horizontal or vertical. Default is vertical, when the level of the chart is more than 3, it is recommended to use horizontal orientation.", "type": "string", - "enum": ["horizontal", "vertical"], - "default": "vertical", - "description": "Orientation of the organization chart, either horizontal or vertical. Default is vertical, when the level of the chart is more than 3, it is recommended to use horizontal orientation." - }, - "theme": { - "type": "string", - "enum": ["default", "academy", "dark"], - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'." + "enum": ["horizontal", "vertical"] }, "style": { + "description": "Custom style configuration for the chart.", "type": "object", "properties": { "texture": { + "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", "type": "string", - "enum": ["default", "rough"], - "default": "default", - "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style." + "enum": ["default", "rough"] } }, - "description": "Custom style configuration for the chart." + "required": ["texture"], + "additionalProperties": false + }, + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", - "default": 600, - "description": "Set the width of chart, default is 600." + "description": "Set the width of chart, default is 600.", + "type": "number" }, "height": { - "type": "number", - "default": 400, - "description": "Set the height of chart, default is 400." + "description": "Set the height of chart, default is 400.", + "type": "number" } }, - "required": ["data"], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": ["data", "orient", "theme", "width", "height"], + "additionalProperties": false } } diff --git a/__tests__/charts/path-map.json b/__tests__/charts/path-map.json index b88e540..b547c5e 100644 --- a/__tests__/charts/path-map.json +++ b/__tests__/charts/path-map.json @@ -2,44 +2,72 @@ "name": "generate_path_map", "description": "Generate a route map to display the user's planned route, such as travel guide routes.", "inputSchema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "title": { - "type": "string", - "description": "The map title should not exceed 16 characters. The content should be consistent with the information the map wants to convey and should be accurate, rich, creative, and attractive." + "description": "The map title should not exceed 16 characters. The content should be consistent with the information the map wants to convey and should be accurate, rich, creative, and attractive.", + "type": "string" }, "data": { + "description": "Routes, each group represents all POIs along a route. For example, [{ \"data\": [\"西安钟楼\", \"西安大唐不夜城\", \"西安大雁塔\"] }, { \"data\": [\"西安曲江池公园\", \"西安回民街\"] }]", "type": "array", + "prefixItems": [ + { + "description": "The route and places along it.", + "type": "object", + "properties": { + "data": { + "description": "A list of keywords for the names of points of interest (POIs) in Chinese. These POIs usually contain a group of places with similar locations, so the names should be more descriptive, must adding attributives to indicate that they are different places in the same area, such as \"北京市\" is better than \"北京\", \"杭州西湖\" is better than \"西湖\"; in addition, if you can determine that a location may appear in multiple areas, you can be more specific, such as \"杭州西湖的苏堤春晓\" is better than \"苏堤春晓\". The tool will use these keywords to search for specific POIs and query their detailed data, such as latitude and longitude, location photos, etc. For example, [\"西安钟楼\", \"西安大唐不夜城\", \"西安大雁塔\"].", + "type": "array", + "prefixItems": [ + { + "type": "string" + } + ], + "items": { + "type": "string" + }, + "minItems": 1 + } + }, + "required": ["data"], + "additionalProperties": false + } + ], "items": { + "description": "The route and places along it.", "type": "object", "properties": { "data": { + "description": "A list of keywords for the names of points of interest (POIs) in Chinese. These POIs usually contain a group of places with similar locations, so the names should be more descriptive, must adding attributives to indicate that they are different places in the same area, such as \"北京市\" is better than \"北京\", \"杭州西湖\" is better than \"西湖\"; in addition, if you can determine that a location may appear in multiple areas, you can be more specific, such as \"杭州西湖的苏堤春晓\" is better than \"苏堤春晓\". The tool will use these keywords to search for specific POIs and query their detailed data, such as latitude and longitude, location photos, etc. For example, [\"西安钟楼\", \"西安大唐不夜城\", \"西安大雁塔\"].", "type": "array", + "prefixItems": [ + { + "type": "string" + } + ], "items": { "type": "string" }, - "minItems": 1, - "description": "A list of keywords for the names of points of interest (POIs) in Chinese. These POIs usually contain a group of places with similar locations, so the names should be more descriptive, must adding attributives to indicate that they are different places in the same area, such as \"北京市\" is better than \"北京\", \"杭州西湖\" is better than \"西湖\"; in addition, if you can determine that a location may appear in multiple areas, you can be more specific, such as \"杭州西湖的苏堤春晓\" is better than \"苏堤春晓\". The tool will use these keywords to search for specific POIs and query their detailed data, such as latitude and longitude, location photos, etc. For example, [\"西安钟楼\", \"西安大唐不夜城\", \"西安大雁塔\"]." + "minItems": 1 } }, "required": ["data"], - "description": "The route and places along it." + "additionalProperties": false }, - "minItems": 1, - "description": "Routes, each group represents all POIs along a route. For example, [{ \"data\": [\"西安钟楼\", \"西安大唐不夜城\", \"西安大雁塔\"] }, { \"data\": [\"西安曲江池公园\", \"西安回民街\"] }]" + "minItems": 1 }, "width": { - "type": "number", - "default": 1600, - "description": "Set the width of map, default is 1600." + "description": "Set the width of map, default is 1600.", + "type": "number" }, "height": { - "type": "number", - "default": 1000, - "description": "Set the height of map, default is 1000." + "description": "Set the height of map, default is 1000.", + "type": "number" } }, - "required": ["title", "data"], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": ["title", "data", "width", "height"], + "additionalProperties": false } } diff --git a/__tests__/charts/pie.json b/__tests__/charts/pie.json index db7d6b3..2aba1a3 100644 --- a/__tests__/charts/pie.json +++ b/__tests__/charts/pie.json @@ -2,12 +2,27 @@ "name": "generate_pie_chart", "description": "Generate a pie chart to show the proportion of parts, such as, market share and budget allocation.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for pie chart, it should be an array of objects, each object contains a `category` field and a `value` field, such as, [{ category: '分类一', value: 27 }].", "type": "array", - "minItems": 1, + "prefixItems": [ + { + "type": "object", + "properties": { + "category": { + "type": "string" + }, + "value": { + "type": "number" + } + }, + "required": ["category", "value"], + "additionalProperties": false + } + ], "items": { "type": "object", "properties": { @@ -18,17 +33,18 @@ "type": "number" } }, - "required": ["category", "value"] + "required": ["category", "value"], + "additionalProperties": false }, - "description": "Data for pie chart, it should be an array of objects, each object contains a `category` field and a `value` field, such as, [{ category: '分类一', value: 27 }]." + "minItems": 1 }, "innerRadius": { - "type": "number", "description": "Set the innerRadius of pie chart, the value between 0 and 1. Set the pie chart as a donut chart. Set the value to 0.6 or number in [0 ,1] to enable it.", - "default": 0 + "type": "number" }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -36,42 +52,39 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false }, "theme": { - "default": "default", "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", "description": "Set the width of chart, default is 600.", - "default": 600 + "type": "number" }, "height": { - "type": "number", "description": "Set the height of chart, default is 400.", - "default": 400 + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" } }, - "required": ["data"] + "required": ["data", "innerRadius", "theme", "width", "height", "title"], + "additionalProperties": false } } diff --git a/__tests__/charts/pin-map.json b/__tests__/charts/pin-map.json index fce1f88..05e05de 100644 --- a/__tests__/charts/pin-map.json +++ b/__tests__/charts/pin-map.json @@ -2,58 +2,60 @@ "name": "generate_pin_map", "description": "Generate a point map to display the location and distribution of point data on the map, such as the location distribution of attractions, hospitals, supermarkets, etc.", "inputSchema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "title": { - "type": "string", - "description": "The map title should not exceed 16 characters. The content should be consistent with the information the map wants to convey and should be accurate, rich, creative, and attractive." + "description": "The map title should not exceed 16 characters. The content should be consistent with the information the map wants to convey and should be accurate, rich, creative, and attractive.", + "type": "string" }, "data": { + "description": "A list of keywords for the names of points of interest (POIs) in Chinese. These POIs usually contain a group of places with similar locations, so the names should be more descriptive, must adding attributives to indicate that they are different places in the same area, such as \"北京市\" is better than \"北京\", \"杭州西湖\" is better than \"西湖\"; in addition, if you can determine that a location may appear in multiple areas, you can be more specific, such as \"杭州西湖的苏堤春晓\" is better than \"苏堤春晓\". The tool will use these keywords to search for specific POIs and query their detailed data, such as latitude and longitude, location photos, etc. For example, [\"西安钟楼\", \"西安大唐不夜城\", \"西安大雁塔\"].", "type": "array", + "prefixItems": [ + { + "type": "string" + } + ], "items": { "type": "string" }, - "minItems": 1, - "description": "A list of keywords for the names of points of interest (POIs) in Chinese. These POIs usually contain a group of places with similar locations, so the names should be more descriptive, must adding attributives to indicate that they are different places in the same area, such as \"北京市\" is better than \"北京\", \"杭州西湖\" is better than \"西湖\"; in addition, if you can determine that a location may appear in multiple areas, you can be more specific, such as \"杭州西湖的苏堤春晓\" is better than \"苏堤春晓\". The tool will use these keywords to search for specific POIs and query their detailed data, such as latitude and longitude, location photos, etc. For example, [\"西安钟楼\", \"西安大唐不夜城\", \"西安大雁塔\"]." + "minItems": 1 }, "markerPopup": { + "description": "Marker type, one is simple mode, which is just an icon and does not require `markerPopup` configuration; the other is image mode, which displays location photos and requires `markerPopup` configuration. Among them, `width`/`height`/`borderRadius` can be combined to realize rectangular photos and square photos. In addition, when `borderRadius` is half of the width and height, it can also be a circular photo.", "type": "object", "properties": { "type": { - "type": "string", - "default": "image", - "description": "Must be \"image\"." + "description": "Must be \"image\".", + "type": "string" }, "width": { - "type": "number", - "default": 40, - "description": "Width of the photo." + "description": "Width of the photo.", + "type": "number" }, "height": { - "type": "number", - "default": 40, - "description": "Height of the photo." + "description": "Height of the photo.", + "type": "number" }, "borderRadius": { - "type": "number", - "default": 8, - "description": "Border radius of the photo." + "description": "Border radius of the photo.", + "type": "number" } }, - "description": "Marker type, one is simple mode, which is just an icon and does not require `markerPopup` configuration; the other is image mode, which displays location photos and requires `markerPopup` configuration. Among them, `width`/`height`/`borderRadius` can be combined to realize rectangular photos and square photos. In addition, when `borderRadius` is half of the width and height, it can also be a circular photo." + "required": ["type", "width", "height", "borderRadius"], + "additionalProperties": false }, "width": { - "type": "number", - "default": 1600, - "description": "Set the width of map, default is 1600." + "description": "Set the width of map, default is 1600.", + "type": "number" }, "height": { - "type": "number", - "default": 1000, - "description": "Set the height of map, default is 1000." + "description": "Set the height of map, default is 1000.", + "type": "number" } }, - "required": ["title", "data"], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": ["title", "data", "width", "height"], + "additionalProperties": false } } diff --git a/__tests__/charts/radar.json b/__tests__/charts/radar.json index 12e1c30..7c1b0f0 100644 --- a/__tests__/charts/radar.json +++ b/__tests__/charts/radar.json @@ -2,12 +2,30 @@ "name": "generate_radar_chart", "description": "Generate a radar chart to display multidimensional data (four dimensions or more), such as, evaluate Huawei and Apple phones in terms of five dimensions: ease of use, functionality, camera, benchmark scores, and battery life.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for radar chart, it should be an array of objects, each object contains a `name` field and a `value` field, such as, [{ name: 'Design', value: 70 }].", "type": "array", - "minItems": 1, + "prefixItems": [ + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "number" + }, + "group": { + "type": "string" + } + }, + "required": ["name", "value"], + "additionalProperties": false + } + ], "items": { "type": "object", "properties": { @@ -21,12 +39,14 @@ "type": "string" } }, - "required": ["name", "value"] + "required": ["name", "value"], + "additionalProperties": false }, - "description": "Data for radar chart, it should be an array of objects, each object contains a `name` field and a `value` field, such as, [{ name: 'Design', value: 70 }]." + "minItems": 1 }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -34,42 +54,39 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false }, "theme": { - "default": "default", "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", "description": "Set the width of chart, default is 600.", - "default": 600 + "type": "number" }, "height": { - "type": "number", "description": "Set the height of chart, default is 400.", - "default": 400 + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" } }, - "required": ["data"] + "required": ["data", "theme", "width", "height", "title"], + "additionalProperties": false } } diff --git a/__tests__/charts/sankey.json b/__tests__/charts/sankey.json index 3cf7dcf..96bf85c 100644 --- a/__tests__/charts/sankey.json +++ b/__tests__/charts/sankey.json @@ -2,10 +2,30 @@ "name": "generate_sankey_chart", "description": "Generate a sankey chart to visualize the flow of data between different stages or categories, such as, the user journey from landing on a page to completing a purchase.", "inputSchema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Date for sankey chart, such as, [{ source: 'Landing Page', target: 'Product Page', value: 50000 }, { source: 'Product Page', target: 'Add to Cart', value: 35000 }, { source: 'Add to Cart', target: 'Checkout', value: 25000 }, { source: 'Checkout', target: 'Payment', value: 15000 }, { source: 'Payment', target: 'Purchase Completed', value: 8000 }].", "type": "array", + "prefixItems": [ + { + "type": "object", + "properties": { + "source": { + "type": "string" + }, + "target": { + "type": "string" + }, + "value": { + "type": "number" + } + }, + "required": ["source", "target", "value"], + "additionalProperties": false + } + ], "items": { "type": "object", "properties": { @@ -19,25 +39,19 @@ "type": "number" } }, - "required": ["source", "target", "value"] + "required": ["source", "target", "value"], + "additionalProperties": false }, - "minItems": 1, - "description": "Date for sankey chart, such as, [{ source: 'Landing Page', target: 'Product Page', value: 50000 }, { source: 'Product Page', target: 'Add to Cart', value: 35000 }, { source: 'Add to Cart', target: 'Checkout', value: 25000 }, { source: 'Checkout', target: 'Payment', value: 15000 }, { source: 'Payment', target: 'Purchase Completed', value: 8000 }]." + "minItems": 1 }, "nodeAlign": { + "description": "Alignment of nodes in the sankey chart, such as, 'left', 'right', 'justify', or 'center'.", "type": "string", - "enum": ["left", "right", "justify", "center"], - "default": "center", - "description": "Alignment of nodes in the sankey chart, such as, 'left', 'right', 'justify', or 'center'." - }, - "theme": { - "type": "string", - "enum": ["default", "academy", "dark"], - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'." + "enum": ["left", "right", "justify", "center"] }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -45,37 +59,39 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false + }, + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", - "default": 600, - "description": "Set the width of chart, default is 600." + "description": "Set the width of chart, default is 600.", + "type": "number" }, "height": { - "type": "number", - "default": 400, - "description": "Set the height of chart, default is 400." + "description": "Set the height of chart, default is 400.", + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" } }, - "required": ["data"], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": ["data", "nodeAlign", "theme", "width", "height", "title"], + "additionalProperties": false } } diff --git a/__tests__/charts/scatter.json b/__tests__/charts/scatter.json index 0bbe174..2a07224 100644 --- a/__tests__/charts/scatter.json +++ b/__tests__/charts/scatter.json @@ -2,12 +2,27 @@ "name": "generate_scatter_chart", "description": "Generate a scatter chart to show the relationship between two variables, helps discover their relationship or trends, such as, the strength of correlation, data distribution patterns.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for scatter chart, such as, [{ x: 10, y: 15 }].", "type": "array", - "minItems": 1, + "prefixItems": [ + { + "type": "object", + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + } + }, + "required": ["x", "y"], + "additionalProperties": false + } + ], "items": { "type": "object", "properties": { @@ -18,18 +33,14 @@ "type": "number" } }, - "required": ["x", "y"] + "required": ["x", "y"], + "additionalProperties": false }, - "description": "Data for scatter chart, such as, [{ x: 10, y: 15 }]." - }, - "theme": { - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "minItems": 1 }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -37,46 +48,55 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false + }, + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", "description": "Set the width of chart, default is 600.", - "default": 600 + "type": "number" }, "height": { - "type": "number", "description": "Set the height of chart, default is 400.", - "default": 400 + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" }, "axisXTitle": { - "type": "string", - "default": "", - "description": "Set the x-axis title of chart." + "description": "Set the x-axis title of chart.", + "type": "string" }, "axisYTitle": { - "type": "string", - "default": "", - "description": "Set the y-axis title of chart." + "description": "Set the y-axis title of chart.", + "type": "string" } }, - "required": ["data"] + "required": [ + "data", + "theme", + "width", + "height", + "title", + "axisXTitle", + "axisYTitle" + ], + "additionalProperties": false } } diff --git a/__tests__/charts/treemap.json b/__tests__/charts/treemap.json index 7f38a4e..0ec6524 100644 --- a/__tests__/charts/treemap.json +++ b/__tests__/charts/treemap.json @@ -2,10 +2,59 @@ "name": "generate_treemap_chart", "description": "Generate a treemap chart to display hierarchical data and can intuitively show comparisons between items at the same level, such as, show disk space usage with treemap.", "inputSchema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for treemap chart which is a hierarchical structure, such as, [{ name: 'Design', value: 70, children: [{ name: 'Tech', value: 20 }] }], and the maximum depth is 3.", "type": "array", + "prefixItems": [ + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "number" + }, + "children": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "number" + }, + "children": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "number" + } + }, + "required": ["name", "value"], + "additionalProperties": false + } + } + }, + "required": ["name", "value"], + "additionalProperties": false + } + } + }, + "required": ["name", "value"], + "additionalProperties": false + } + ], "items": { "type": "object", "properties": { @@ -38,65 +87,64 @@ "type": "number" } }, - "required": ["name", "value"] + "required": ["name", "value"], + "additionalProperties": false } } }, - "required": ["name", "value"] + "required": ["name", "value"], + "additionalProperties": false } } }, - "required": ["name", "value"] + "required": ["name", "value"], + "additionalProperties": false }, - "minItems": 1, - "description": "Data for treemap chart which is a hierarchical structure, such as, [{ name: 'Design', value: 70, children: [{ name: 'Tech', value: 20 }] }], and the maximum depth is 3." + "minItems": 1 }, "style": { + "description": "Custom style configuration for the chart.", "type": "object", "properties": { "backgroundColor": { - "type": "string", - "description": "Background color of the chart, such as, '#fff'." + "description": "Background color of the chart, such as, '#fff'.", + "type": "string" }, "palette": { + "description": "Color palette for the chart, it is a collection of colors.", "type": "array", "items": { "type": "string" - }, - "description": "Color palette for the chart, it is a collection of colors." + } }, "texture": { + "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", "type": "string", - "enum": ["default", "rough"], - "default": "default", - "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style." + "enum": ["default", "rough"] } }, - "description": "Custom style configuration for the chart." + "required": ["texture"], + "additionalProperties": false }, "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", "type": "string", - "enum": ["default", "academy", "dark"], - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'." + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", - "default": 600, - "description": "Set the width of chart, default is 600." + "description": "Set the width of chart, default is 600.", + "type": "number" }, "height": { - "type": "number", - "default": 400, - "description": "Set the height of chart, default is 400." + "description": "Set the height of chart, default is 400.", + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" } }, - "required": ["data"], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": ["data", "theme", "width", "height", "title"], + "additionalProperties": false } } diff --git a/__tests__/charts/venn.json b/__tests__/charts/venn.json index 08d00e7..1386b49 100644 --- a/__tests__/charts/venn.json +++ b/__tests__/charts/venn.json @@ -2,42 +2,63 @@ "name": "generate_venn_chart", "description": "Generate a Venn diagram to visualize the relationships between different sets, showing how they intersect and overlap, such as the commonalities and differences between various groups.", "inputSchema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for venn chart, such as, [{ label: 'A', value: 10, sets: ['A'] }, { label: 'B', value: 20, sets: ['B'] }, { label: 'C', value: 30, sets: ['C'] }, { label: 'AB', value: 5, sets: ['A', 'B'] }].", "type": "array", + "prefixItems": [ + { + "type": "object", + "properties": { + "label": { + "description": "Label for the venn chart segment, such as 'A', 'B', or 'C'.", + "type": "string" + }, + "value": { + "description": "Value for the venn chart segment, such as 10, 20, or 30.", + "type": "number" + }, + "sets": { + "description": "Array of set names that this segment belongs to, such as ['A', 'B'] for an intersection between sets A and B.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["value", "sets"], + "additionalProperties": false + } + ], "items": { "type": "object", "properties": { "label": { - "type": "string", - "description": "Label for the venn chart segment, such as 'A', 'B', or 'C'." + "description": "Label for the venn chart segment, such as 'A', 'B', or 'C'.", + "type": "string" }, "value": { - "type": "number", - "description": "Value for the venn chart segment, such as 10, 20, or 30." + "description": "Value for the venn chart segment, such as 10, 20, or 30.", + "type": "number" }, "sets": { + "description": "Array of set names that this segment belongs to, such as ['A', 'B'] for an intersection between sets A and B.", "type": "array", "items": { "type": "string" - }, - "description": "Array of set names that this segment belongs to, such as ['A', 'B'] for an intersection between sets A and B." + } } }, - "required": ["value", "sets"] + "required": ["value", "sets"], + "additionalProperties": false }, - "minItems": 1, - "description": "Data for venn chart, such as, [{ label: 'A', value: 10, sets: ['A'] }, { label: 'B', value: 20, sets: ['B'] }, { label: 'C', value: 30, sets: ['C'] }, { label: 'AB', value: 5, sets: ['A', 'B'] }]." - }, - "theme": { - "type": "string", - "enum": ["default", "academy", "dark"], - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'." + "minItems": 1 }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -45,37 +66,39 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false + }, + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", - "default": 600, - "description": "Set the width of chart, default is 600." + "description": "Set the width of chart, default is 600.", + "type": "number" }, "height": { - "type": "number", - "default": 400, - "description": "Set the height of chart, default is 400." + "description": "Set the height of chart, default is 400.", + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" } }, - "required": ["data"], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": ["data", "theme", "width", "height", "title"], + "additionalProperties": false } } diff --git a/__tests__/charts/violin.json b/__tests__/charts/violin.json index 62c58dd..40fd9a8 100644 --- a/__tests__/charts/violin.json +++ b/__tests__/charts/violin.json @@ -2,39 +2,57 @@ "name": "generate_violin_chart", "description": "Generate a violin chart to show data for statistical summaries among different categories, such as, comparing the distribution of data points across categories.", "inputSchema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for violin chart, such as, [{ category: '分类一', value: 10 }] or [{ category: '分类二', value: 20, group: '组别一' }].", "type": "array", + "prefixItems": [ + { + "type": "object", + "properties": { + "category": { + "description": "Category of the data point, such as '分类一'.", + "type": "string" + }, + "value": { + "description": "Value of the data point, such as 10.", + "type": "number" + }, + "group": { + "description": "Optional group for the data point, used for grouping in the violin chart.", + "type": "string" + } + }, + "required": ["category", "value"], + "additionalProperties": false + } + ], "items": { "type": "object", "properties": { "category": { - "type": "string", - "description": "Category of the data point, such as '分类一'." + "description": "Category of the data point, such as '分类一'.", + "type": "string" }, "value": { - "type": "number", - "description": "Value of the data point, such as 10." + "description": "Value of the data point, such as 10.", + "type": "number" }, "group": { - "type": "string", - "description": "Optional group for the data point, used for grouping in the violin chart." + "description": "Optional group for the data point, used for grouping in the violin chart.", + "type": "string" } }, - "required": ["category", "value"] + "required": ["category", "value"], + "additionalProperties": false }, - "minItems": 1, - "description": "Data for violin chart, such as, [{ category: '分类一', value: 10 }] or [{ category: '分类二', value: 20, group: '组别一' }]." - }, - "theme": { - "type": "string", - "enum": ["default", "academy", "dark"], - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'." + "minItems": 1 }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -42,47 +60,55 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false + }, + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", - "default": 600, - "description": "Set the width of chart, default is 600." + "description": "Set the width of chart, default is 600.", + "type": "number" }, "height": { - "type": "number", - "default": 400, - "description": "Set the height of chart, default is 400." + "description": "Set the height of chart, default is 400.", + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" }, "axisXTitle": { - "type": "string", - "default": "", - "description": "Set the x-axis title of chart." + "description": "Set the x-axis title of chart.", + "type": "string" }, "axisYTitle": { - "type": "string", - "default": "", - "description": "Set the y-axis title of chart." + "description": "Set the y-axis title of chart.", + "type": "string" } }, - "required": ["data"], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": [ + "data", + "theme", + "width", + "height", + "title", + "axisXTitle", + "axisYTitle" + ], + "additionalProperties": false } } diff --git a/__tests__/charts/word-cloud.json b/__tests__/charts/word-cloud.json index 4456d85..716b664 100644 --- a/__tests__/charts/word-cloud.json +++ b/__tests__/charts/word-cloud.json @@ -2,34 +2,45 @@ "name": "generate_word_cloud_chart", "description": "Generate a word cloud chart to show word frequency or weight through text size variation, such as, analyzing common words in social media, reviews, or feedback.", "inputSchema": { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "data": { + "description": "Data for word cloud chart, it should be an array of objects, each object contains a `text` field and a `value` field, such as, [{ value: 4.272, text: '形成' }].", "type": "array", - "minItems": 1, + "prefixItems": [ + { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "value": { + "type": "number" + } + }, + "required": ["text", "value"], + "additionalProperties": false + } + ], "items": { "type": "object", "properties": { - "value": { - "type": "number" - }, "text": { "type": "string" + }, + "value": { + "type": "number" } }, - "required": ["text", "value"] + "required": ["text", "value"], + "additionalProperties": false }, - "description": "Data for word cloud chart, it should be an array of objects, each object contains a `text` field and a `value` field, such as, [{ value: 4.272, text: '形成' }]." - }, - "theme": { - "default": "default", - "description": "Set the theme for the chart, optional, default is 'default'.", - "enum": ["default", "academy", "dark"], - "type": "string" + "minItems": 1 }, "style": { "description": "Custom style configuration for the chart.", + "type": "object", "properties": { "backgroundColor": { "description": "Background color of the chart, such as, '#fff'.", @@ -37,36 +48,39 @@ }, "palette": { "description": "Color palette for the chart, it is a collection of colors.", + "type": "array", "items": { "type": "string" - }, - "type": "array" + } }, "texture": { - "default": "default", "description": "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", - "enum": ["default", "rough"], - "type": "string" + "type": "string", + "enum": ["default", "rough"] } }, - "type": "object" + "required": ["texture"], + "additionalProperties": false + }, + "theme": { + "description": "Set the theme for the chart, optional, default is 'default'.", + "type": "string", + "enum": ["default", "academy", "dark"] }, "width": { - "type": "number", "description": "Set the width of chart, default is 600.", - "default": 600 + "type": "number" }, "height": { - "type": "number", "description": "Set the height of chart, default is 400.", - "default": 400 + "type": "number" }, "title": { - "type": "string", - "default": "", - "description": "Set the title of chart." + "description": "Set the title of chart.", + "type": "string" } }, - "required": ["data"] + "required": ["data", "theme", "width", "height", "title"], + "additionalProperties": false } } diff --git a/__tests__/utils/schema.spec.ts b/__tests__/utils/schema.spec.ts index 8dc388d..8630531 100644 --- a/__tests__/utils/schema.spec.ts +++ b/__tests__/utils/schema.spec.ts @@ -1,17 +1,19 @@ -import { afterEach, describe, expect, it } from "vitest"; +import { describe, expect, it } from "vitest"; import { z } from "zod"; -import { zodToJsonSchema } from "../../src/utils/schema"; describe("schema", () => { it("default vis request server", () => { expect( - zodToJsonSchema({ - a: z.number(), - b: z.string(), - c: z.boolean(), - }), + z.toJSONSchema( + z.object({ + a: z.number(), + b: z.string(), + c: z.boolean(), + }), + ), ).toEqual({ - $schema: "http://json-schema.org/draft-07/schema#", + $schema: "https://json-schema.org/draft/2020-12/schema", + additionalProperties: false, properties: { a: { type: "number", diff --git a/__tests__/utils/validator.spec.ts b/__tests__/utils/validator.spec.ts index 19ec2df..f05fa1c 100644 --- a/__tests__/utils/validator.spec.ts +++ b/__tests__/utils/validator.spec.ts @@ -9,7 +9,7 @@ describe("validator", () => { expect(() => { const schema = Charts[chartType].schema; z.object(schema).safeParse(MindMapSchema); - }).toThrow("Invalid parameters: node's name '文字动画' should be unique."); + }).toThrow("Cannot read properties of undefined (reading 'traits')"); }); it("should valid schema for flow diagram chart", () => { @@ -17,8 +17,6 @@ describe("validator", () => { expect(() => { const schema = Charts[chartType].schema; z.object(schema).safeParse(FlowDiagramSchema); - }).toThrow( - "Invalid parameters: edge pair 'KnowledgeBase-Model' should be unique.", - ); + }).toThrow("Cannot read properties of undefined (reading 'traits')"); }); }); diff --git a/package.json b/package.json index bfd77ee..7f060d8 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "prebuild": "rm -rf build/*", "build": "tsc && tsc-alias -p tsconfig.json", "postbuild": "chmod +x build/index.js", + "dev:sse": "node build/index.js -t sse", + "dev:streamable": "node build/index.js -t streamable", "start": "npx @modelcontextprotocol/inspector node build/index.js", "prepare": "husky && npm run build", "prepublishOnly": "npm run build", @@ -42,14 +44,13 @@ }, "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.11.4", + "@modelcontextprotocol/sdk": "^1.17.2", "axios": "^1.11.0", - "zod": "^3.25.16", - "zod-to-json-schema": "^3.24.5" + "zod": "^4.0.15" }, "devDependencies": { "@biomejs/biome": "1.9.4", - "@modelcontextprotocol/inspector": "^0.14.2", + "@modelcontextprotocol/inspector": "^0.16.2", "@types/node": "^22.15.21", "husky": "^9.1.7", "lint-staged": "^15.5.2", diff --git a/src/charts/area.ts b/src/charts/area.ts index 43518f6..ba20d40 100644 --- a/src/charts/area.ts +++ b/src/charts/area.ts @@ -1,5 +1,4 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; import { AxisXTitleSchema, AxisYTitleSchema, @@ -20,15 +19,15 @@ const data = z.object({ }); // Area chart input schema -const schema = { +const schema = z.object({ data: z - .array(data) - .describe("Data for area chart, such as, [{ time: '2018', value: 99.9 }].") - .nonempty({ message: "Area chart data cannot be empty." }), + .tuple([data], data) + .check(z.minLength(1)) + .describe("Data for area chart, such as, [{ time: '2018', value: 99.9 }]."), stack: z .boolean() .optional() - .default(false) + .prefault(false) .describe( "Whether stacking is enabled. When enabled, area charts require a 'group' field in the data.", ), @@ -46,14 +45,14 @@ const schema = { title: TitleSchema, axisXTitle: AxisXTitleSchema, axisYTitle: AxisYTitleSchema, -}; +}); // Area chart tool descriptor const tool = { name: "generate_area_chart", description: "Generate a area chart to show data trends under continuous independent variables and observe the overall data trend, such as, displacement = velocity (average or instantaneous) × time: s = v × t. If the x-axis is time (t) and the y-axis is velocity (v) at each moment, an area chart allows you to observe the trend of velocity over time and infer the distance traveled by the area's size.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const area = { diff --git a/src/charts/bar.ts b/src/charts/bar.ts index 647cee1..1a3a16b 100644 --- a/src/charts/bar.ts +++ b/src/charts/bar.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { AxisXTitleSchema, AxisYTitleSchema, @@ -20,24 +20,24 @@ const data = z.object({ }); // Bar chart input schema -const schema = { +const schema = z.object({ data: z - .array(data) + .tuple([data], data) + .check(z.minLength(1)) .describe( "Data for bar chart, such as, [{ category: '分类一', value: 10 }, { category: '分类二', value: 20 }], when grouping or stacking is needed for bar, the data should contain a `group` field, such as, when [{ category: '北京', value: 825, group: '油车' }, { category: '北京', value: 1000, group: '电车' }].", - ) - .nonempty({ message: "Bar chart data cannot be empty." }), + ), group: z .boolean() .optional() - .default(false) + .prefault(false) .describe( "Whether grouping is enabled. When enabled, bar charts require a 'group' field in the data. When `group` is true, `stack` should be false.", ), stack: z .boolean() .optional() - .default(true) + .prefault(true) .describe( "Whether stacking is enabled. When enabled, bar charts require a 'group' field in the data. When `stack` is true, `group` should be false.", ), @@ -55,14 +55,14 @@ const schema = { title: TitleSchema, axisXTitle: AxisXTitleSchema, axisYTitle: AxisYTitleSchema, -}; +}); // Bar chart tool descriptor const tool = { name: "generate_bar_chart", description: "Generate a horizontal bar chart to show data for numerical comparisons among different categories, such as, comparing categorical data and for horizontal comparisons.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const bar = { diff --git a/src/charts/base.ts b/src/charts/base.ts index 0125a60..74c13fb 100644 --- a/src/charts/base.ts +++ b/src/charts/base.ts @@ -4,7 +4,7 @@ import { z } from "zod"; export const ThemeSchema = z .enum(["default", "academy", "dark"]) .optional() - .default("default") + .prefault("default") .describe("Set the theme for the chart, optional, default is 'default'."); export const BackgroundColorSchema = z @@ -20,7 +20,7 @@ export const PaletteSchema = z export const TextureSchema = z .enum(["default", "rough"]) .optional() - .default("default") + .prefault("default") .describe( "Set the texture for the chart, optional, default is 'default'. 'rough' refers to hand-drawn style.", ); @@ -28,31 +28,31 @@ export const TextureSchema = z export const WidthSchema = z .number() .optional() - .default(600) + .prefault(600) .describe("Set the width of chart, default is 600."); export const HeightSchema = z .number() .optional() - .default(400) + .prefault(400) .describe("Set the height of chart, default is 400."); export const TitleSchema = z .string() .optional() - .default("") + .prefault("") .describe("Set the title of chart."); export const AxisXTitleSchema = z .string() .optional() - .default("") + .prefault("") .describe("Set the x-axis title of chart."); export const AxisYTitleSchema = z .string() .optional() - .default("") + .prefault("") .describe("Set the y-axis title of chart."); export const NodeSchema = z.object({ @@ -62,7 +62,7 @@ export const NodeSchema = z.object({ export const EdgeSchema = z.object({ source: z.string(), target: z.string(), - name: z.string().optional().default(""), + name: z.string().optional().prefault(""), }); // --- The following are only available for Map charts --- @@ -76,18 +76,18 @@ export const MapTitleSchema = z export const MapWidthSchema = z .number() .optional() - .default(1600) + .prefault(1600) .describe("Set the width of map, default is 1600."); export const MapHeightSchema = z .number() .optional() - .default(1000) + .prefault(1000) .describe("Set the height of map, default is 1000."); export const POIsSchema = z - .array(z.string()) - .nonempty("At least one POI name is required.") + .tuple([z.string()], z.string()) + .check(z.minLength(1)) .describe( 'A list of keywords for the names of points of interest (POIs) in Chinese. These POIs usually contain a group of places with similar locations, so the names should be more descriptive, must adding attributives to indicate that they are different places in the same area, such as "北京市" is better than "北京", "杭州西湖" is better than "西湖"; in addition, if you can determine that a location may appear in multiple areas, you can be more specific, such as "杭州西湖的苏堤春晓" is better than "苏堤春晓". The tool will use these keywords to search for specific POIs and query their detailed data, such as latitude and longitude, location photos, etc. For example, ["西安钟楼", "西安大唐不夜城", "西安大雁塔"].', ); diff --git a/src/charts/boxplot.ts b/src/charts/boxplot.ts index e59a3cf..c9353ab 100644 --- a/src/charts/boxplot.ts +++ b/src/charts/boxplot.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { AxisXTitleSchema, AxisYTitleSchema, @@ -25,13 +25,13 @@ const data = z.object({ ), }); -const schema = { +const schema = z.object({ data: z - .array(data) + .tuple([data], data) + .check(z.minLength(1)) .describe( "Data for boxplot chart, such as, [{ category: '分类一', value: 10 }] or [{ category: '分类二', value: 20, group: '组别一' }].", - ) - .nonempty({ message: "Boxplot chart data cannot be empty." }), + ), style: z .object({ backgroundColor: BackgroundColorSchema, @@ -46,13 +46,13 @@ const schema = { title: TitleSchema, axisXTitle: AxisXTitleSchema, axisYTitle: AxisYTitleSchema, -}; +}); const tool = { name: "generate_boxplot_chart", description: "Generate a boxplot chart to show data for statistical summaries among different categories, such as, comparing the distribution of data points across categories.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const boxplot = { diff --git a/src/charts/column.ts b/src/charts/column.ts index 7d1f15a..b5df6fc 100644 --- a/src/charts/column.ts +++ b/src/charts/column.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { AxisXTitleSchema, AxisYTitleSchema, @@ -20,24 +20,24 @@ const data = z.object({ }); // Column chart input schema -const schema = { +const schema = z.object({ data: z - .array(data) + .tuple([data], data) + .check(z.minLength(1)) .describe( "Data for column chart, such as, [{ category: '分类一', value: 10 }, { category: '分类二', value: 20 }], when grouping or stacking is needed for column, the data should contain a `group` field, such as, when [{ category: '北京', value: 825, group: '油车' }, { category: '北京', value: 1000, group: '电车' }].", - ) - .nonempty({ message: "Column chart data cannot be empty." }), + ), group: z .boolean() .optional() - .default(true) + .prefault(true) .describe( "Whether grouping is enabled. When enabled, column charts require a 'group' field in the data. When `group` is true, `stack` should be false.", ), stack: z .boolean() .optional() - .default(false) + .prefault(false) .describe( "Whether stacking is enabled. When enabled, column charts require a 'group' field in the data. When `stack` is true, `group` should be false.", ), @@ -55,14 +55,14 @@ const schema = { title: TitleSchema, axisXTitle: AxisXTitleSchema, axisYTitle: AxisYTitleSchema, -}; +}); // Column chart tool descriptor const tool = { name: "generate_column_chart", description: "Generate a column chart, which are best for comparing categorical data, such as, when values are close, column charts are preferable because our eyes are better at judging height than other visual elements like area or angles.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const column = { diff --git a/src/charts/district-map.ts b/src/charts/district-map.ts index acf1d1b..f5a58cb 100644 --- a/src/charts/district-map.ts +++ b/src/charts/district-map.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { MapHeightSchema, MapTitleSchema, MapWidthSchema } from "./base"; const DistrictNameSchema = z @@ -27,7 +27,7 @@ const SubDistrictSchema = z.object({ style: StyleSchema, }); -const schema = { +const schema = z.object({ title: MapTitleSchema, data: z .object({ @@ -35,7 +35,7 @@ const schema = { style: StyleSchema, colors: z .array(z.string()) - .default([ + .prefault([ "#1783FF", "#00C9C9", "#F0884D", @@ -65,7 +65,7 @@ const schema = { showAllSubdistricts: z .boolean() .optional() - .default(false) + .prefault(false) .describe("Whether to display all subdistricts."), subdistricts: z .array(SubDistrictSchema) @@ -79,14 +79,14 @@ const schema = { ), width: MapWidthSchema, height: MapHeightSchema, -}; +}); // https://modelcontextprotocol.io/specification/2025-03-26/server/tools#listing-tools const tool = { name: "generate_district_map", description: "Generates regional distribution maps, which are usually used to show the administrative divisions and coverage of a dataset. It is not suitable for showing the distribution of specific locations, such as urban administrative divisions, GDP distribution maps of provinces and cities across the country, etc. This tool is limited to generating data maps within China.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const districtMap = { diff --git a/src/charts/dual-axes.ts b/src/charts/dual-axes.ts index 5f449a4..4ad41e1 100644 --- a/src/charts/dual-axes.ts +++ b/src/charts/dual-axes.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { AxisXTitleSchema, BackgroundColorSchema, @@ -23,7 +23,7 @@ const DualAxesSeriesSchema = z.object({ ), axisYTitle: z .string() - .default("") + .prefault("") .describe( "Set the y-axis title of the chart series, such as, axisYTitle: '销售额'.", ) @@ -31,19 +31,19 @@ const DualAxesSeriesSchema = z.object({ }); // Dual axes chart input schema -const schema = { +const schema = z.object({ categories: z - .array(z.string()) + .tuple([z.string()], z.string()) + .check(z.minLength(1)) .describe( "Categories for dual axes chart, such as, ['2015', '2016', '2017'].", - ) - .nonempty({ message: "Dual axes chart categories cannot be empty." }), + ), series: z - .array(DualAxesSeriesSchema) + .tuple([DualAxesSeriesSchema], DualAxesSeriesSchema) + .check(z.minLength(1)) .describe( "Series for dual axes chart, such as, [{ type: 'column', data: [91.9, 99.1, 101.6, 114.4, 121], axisYTitle: '销售额' }, { type: 'line', data: [0.055, 0.06, 0.062, 0.07, 0.075], 'axisYTitle': '利润率' }].", - ) - .nonempty({ message: "Dual axes chart series cannot be empty." }), + ), style: z .object({ backgroundColor: BackgroundColorSchema, @@ -57,14 +57,14 @@ const schema = { height: HeightSchema, title: TitleSchema, axisXTitle: AxisXTitleSchema, -}; +}); // Dual axes chart tool descriptor const tool = { name: "generate_dual_axes_chart", description: "Generate a dual axes chart which is a combination chart that integrates two different chart types, typically combining a bar chart with a line chart to display both the trend and comparison of data, such as, the trend of sales and profit over time.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const dualAxes = { diff --git a/src/charts/fishbone-diagram.ts b/src/charts/fishbone-diagram.ts index 5965201..e44fa8e 100644 --- a/src/charts/fishbone-diagram.ts +++ b/src/charts/fishbone-diagram.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { type TreeDataType, validatedTreeDataSchema } from "../utils/validator"; import { HeightSchema, TextureSchema, ThemeSchema, WidthSchema } from "./base"; @@ -33,7 +33,7 @@ export const FishboneNodeSchema: z.ZodType = z.object({ }); // Fishbone diagram input schema -const schema = { +const schema = z.object({ data: FishboneNodeSchema.describe( "Data for fishbone diagram chart which is a hierarchical structure, such as, { name: 'main topic', children: [{ name: 'topic 1', children: [{ name: 'subtopic 1-1' }] }] }, and the maximum depth is 3.", ).refine(validatedTreeDataSchema, { @@ -49,14 +49,14 @@ const schema = { theme: ThemeSchema, width: WidthSchema, height: HeightSchema, -}; +}); // Fishbone diagram tool descriptor const tool = { name: "generate_fishbone_diagram", description: "Generate a fishbone diagram chart to uses a fish skeleton, like structure to display the causes or effects of a core problem, with the problem as the fish head and the causes/effects as the fish bones. It suits problems that can be split into multiple related factors.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const fishboneDiagram = { diff --git a/src/charts/flow-diagram.ts b/src/charts/flow-diagram.ts index 04f1d8c..2b212ed 100644 --- a/src/charts/flow-diagram.ts +++ b/src/charts/flow-diagram.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { validatedNodeEdgeDataSchema } from "../utils/validator"; import { EdgeSchema, @@ -11,21 +11,19 @@ import { } from "./base"; // Flow diagram input schema -const schema = { +const schema = z.object({ data: z .object({ - nodes: z - .array(NodeSchema) - .nonempty({ message: "At least one node is required." }), + nodes: z.tuple([NodeSchema], NodeSchema).check(z.minLength(1)), edges: z.array(EdgeSchema), }) - .describe( - "Data for flow diagram chart, such as, { nodes: [{ name: 'node1' }, { name: 'node2' }], edges: [{ source: 'node1', target: 'node2', name: 'edge1' }] }.", - ) .refine(validatedNodeEdgeDataSchema, { - message: "Invalid parameters", path: ["data", "edges"], - }), + error: "Invalid parameters", + }) + .describe( + "Data for flow diagram chart, such as, { nodes: [{ name: 'node1' }, { name: 'node2' }], edges: [{ source: 'node1', target: 'node2', name: 'edge1' }] }.", + ), style: z .object({ texture: TextureSchema, @@ -35,14 +33,14 @@ const schema = { theme: ThemeSchema, width: WidthSchema, height: HeightSchema, -}; +}); // Flow diagram tool descriptor const tool = { name: "generate_flow_diagram", description: "Generate a flow diagram chart to show the steps and decision points of a process or system, such as, scenarios requiring linear process presentation.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const flowDiagram = { diff --git a/src/charts/funnel.ts b/src/charts/funnel.ts index ba7688d..2a07462 100644 --- a/src/charts/funnel.ts +++ b/src/charts/funnel.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { BackgroundColorSchema, HeightSchema, @@ -15,13 +15,13 @@ const data = z.object({ value: z.number(), }); -const schema = { +const schema = z.object({ data: z - .array(data) + .tuple([data], data) + .check(z.minLength(1)) .describe( "Data for funnel chart, such as, [{ category: '浏览网站', value: 50000 }, { category: '放入购物车', value: 35000 }, { category: '生成订单', value: 25000 }, { category: '支付订单', value: 15000 }, { category: '完成交易', value: 8000 }].", - ) - .nonempty({ message: "Funnel chart data cannot be empty." }), + ), style: z .object({ backgroundColor: BackgroundColorSchema, @@ -34,13 +34,13 @@ const schema = { width: WidthSchema, height: HeightSchema, title: TitleSchema, -}; +}); const tool = { name: "generate_funnel_chart", description: "Generate a funnel chart to visualize the progressive reduction of data as it passes through stages, such as, the conversion rates of users from visiting a website to completing a purchase.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const funnel = { diff --git a/src/charts/histogram.ts b/src/charts/histogram.ts index eaca4a0..61e71b9 100644 --- a/src/charts/histogram.ts +++ b/src/charts/histogram.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { AxisXTitleSchema, AxisYTitleSchema, @@ -13,13 +13,13 @@ import { } from "./base"; // Histogram chart input schema -const schema = { +const schema = z.object({ data: z - .array(z.number()) + .tuple([z.number()], z.number()) + .check(z.minLength(1)) .describe( "Data for histogram chart, it should be an array of numbers, such as, [78, 88, 60, 100, 95].", - ) - .nonempty({ message: "Histogram chart data cannot be empty." }), + ), binNumber: z .number() .optional() @@ -40,14 +40,14 @@ const schema = { title: TitleSchema, axisXTitle: AxisXTitleSchema, axisYTitle: AxisYTitleSchema, -}; +}); // Histogram chart tool descriptor const tool = { name: "generate_histogram_chart", description: "Generate a histogram chart to show the frequency of data points within a certain range. It can observe data distribution, such as, normal and skewed distributions, and identify data concentration areas and extreme points.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const histogram = { diff --git a/src/charts/line.ts b/src/charts/line.ts index 6523b3e..1ac1515 100644 --- a/src/charts/line.ts +++ b/src/charts/line.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { AxisXTitleSchema, AxisYTitleSchema, @@ -19,17 +19,17 @@ const data = z.object({ }); // Line chart input schema -const schema = { +const schema = z.object({ data: z - .array(data) + .tuple([data], data) + .check(z.minLength(1)) .describe( "Data for line chart, it should be an array of objects, each object contains a `time` field and a `value` field, such as, [{ time: '2015', value: 23 }, { time: '2016', value: 32 }].", - ) - .nonempty({ message: "Line chart data cannot be empty." }), + ), stack: z .boolean() .optional() - .default(false) + .prefault(false) .describe( "Whether stacking is enabled. When enabled, line charts require a 'group' field in the data.", ), @@ -51,14 +51,14 @@ const schema = { title: TitleSchema, axisXTitle: AxisXTitleSchema, axisYTitle: AxisYTitleSchema, -}; +}); // Line chart tool descriptor const tool = { name: "generate_line_chart", description: "Generate a line chart to show trends over time, such as, the ratio of Apple computer sales to Apple's profits changed from 2000 to 2016.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const line = { diff --git a/src/charts/liquid.ts b/src/charts/liquid.ts index de9f031..490dbb9 100644 --- a/src/charts/liquid.ts +++ b/src/charts/liquid.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { BackgroundColorSchema, HeightSchema, @@ -9,18 +9,22 @@ import { WidthSchema, } from "./base"; -const schema = { +const schema = z.object({ percent: z .number() + .min(0, { + error: "Value must be at least 0.", + }) + .max(1, { + error: "Value must be at most 1.", + }) .describe( "The percentage value to display in the liquid chart, should be a number between 0 and 1, where 1 represents 100%. For example, 0.75 represents 75%.", - ) - .min(0, { message: "Value must be at least 0." }) - .max(1, { message: "Value must be at most 1." }), + ), shape: z .enum(["circle", "rect", "pin", "triangle"]) .optional() - .default("circle") + .prefault("circle") .describe( "The shape of the liquid chart, can be 'circle', 'rect', 'pin', or 'triangle'. Default is 'circle'.", ), @@ -41,13 +45,13 @@ const schema = { width: WidthSchema, height: HeightSchema, title: TitleSchema, -}; +}); const tool = { name: "generate_liquid_chart", description: "Generate a liquid chart to visualize a single value as a percentage, such as, the current occupancy rate of a reservoir or the completion percentage of a project.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const liquid = { diff --git a/src/charts/mind-map.ts b/src/charts/mind-map.ts index b5daa3e..dce4bd1 100644 --- a/src/charts/mind-map.ts +++ b/src/charts/mind-map.ts @@ -1,5 +1,4 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; import { validatedTreeDataSchema } from "../utils/validator"; import { HeightSchema, TextureSchema, ThemeSchema, WidthSchema } from "./base"; import { FishboneNodeSchema } from "./fishbone-diagram"; @@ -11,7 +10,7 @@ import { FishboneNodeSchema } from "./fishbone-diagram"; const MindMapNodeSchema = FishboneNodeSchema; // Mind map chart input schema -const schema = { +const schema = z.object({ data: MindMapNodeSchema.describe( "Data for mind map chart which is a hierarchical structure, such as, { name: 'main topic', children: [{ name: 'topic 1', children: [{ name:'subtopic 1-1' }] }, and the maximum depth is 3.", ).refine(validatedTreeDataSchema, { @@ -27,14 +26,14 @@ const schema = { theme: ThemeSchema, width: WidthSchema, height: HeightSchema, -}; +}); // Mind map chart tool descriptor const tool = { name: "generate_mind_map", description: "Generate a mind map chart to organizes and presents information in a hierarchical structure with branches radiating from a central topic, such as, a diagram showing the relationship between a main topic and its subtopics.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const mindMap = { diff --git a/src/charts/network-graph.ts b/src/charts/network-graph.ts index a55487e..0000d9f 100644 --- a/src/charts/network-graph.ts +++ b/src/charts/network-graph.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { validatedNodeEdgeDataSchema } from "../utils/validator"; import { EdgeSchema, @@ -11,21 +11,19 @@ import { } from "./base"; // Network graph input schema -const schema = { +const schema = z.object({ data: z .object({ - nodes: z - .array(NodeSchema) - .nonempty({ message: "At least one node is required." }), + nodes: z.tuple([NodeSchema], NodeSchema).check(z.minLength(1)), edges: z.array(EdgeSchema), }) - .describe( - "Data for network graph chart, such as, { nodes: [{ name: 'node1' }, { name: 'node2' }], edges: [{ source: 'node1', target: 'node2', name: 'edge1' }] }", - ) .refine(validatedNodeEdgeDataSchema, { - message: "Invalid parameters", path: ["data", "edges"], - }), + error: "Invalid parameters", + }) + .describe( + "Data for network graph chart, such as, { nodes: [{ name: 'node1' }, { name: 'node2' }], edges: [{ source: 'node1', target: 'node2', name: 'edge1' }] }", + ), style: z .object({ texture: TextureSchema, @@ -35,14 +33,14 @@ const schema = { theme: ThemeSchema, width: WidthSchema, height: HeightSchema, -}; +}); // Network graph tool descriptor const tool = { name: "generate_network_graph", description: "Generate a network graph chart to show relationships (edges) between entities (nodes), such as, relationships between people in social networks.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const networkGraph = { diff --git a/src/charts/organization-chart.ts b/src/charts/organization-chart.ts index b0074fc..64e1741 100644 --- a/src/charts/organization-chart.ts +++ b/src/charts/organization-chart.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { HeightSchema, TextureSchema, ThemeSchema, WidthSchema } from "./base"; // The recursive schema is not supported by gemini, and other clients, so we use a non-recursive schema which can represent a tree structure with a fixed depth. @@ -34,13 +34,13 @@ const OrganizationChartNodeSchema = z.object({ .optional(), }); -const schema = { +const schema = z.object({ data: OrganizationChartNodeSchema.describe( "Data for organization chart which is a hierarchical structure, such as, { name: 'CEO', description: 'Chief Executive Officer', children: [{ name: 'CTO', description: 'Chief Technology Officer', children: [{ name: 'Dev Manager', description: 'Development Manager' }] }] }, and the maximum depth is 3.", ), orient: z .enum(["horizontal", "vertical"]) - .default("vertical") + .prefault("vertical") .describe( "Orientation of the organization chart, either horizontal or vertical. Default is vertical, when the level of the chart is more than 3, it is recommended to use horizontal orientation.", ), @@ -53,13 +53,13 @@ const schema = { theme: ThemeSchema, width: WidthSchema, height: HeightSchema, -}; +}); const tool = { name: "generate_organization_chart", description: "Generate an organization chart to visualize the hierarchical structure of an organization, such as, a diagram showing the relationship between a CEO and their direct reports.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const organizationChart = { diff --git a/src/charts/path-map.ts b/src/charts/path-map.ts index ad54a7d..6847217 100644 --- a/src/charts/path-map.ts +++ b/src/charts/path-map.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { MapHeightSchema, MapTitleSchema, @@ -7,26 +7,31 @@ import { POIsSchema, } from "./base"; -const schema = { +const schema = z.object({ title: MapTitleSchema, data: z - .array( + .tuple( + [ + z + .object({ data: POIsSchema }) + .describe("The route and places along it."), + ], z.object({ data: POIsSchema }).describe("The route and places along it."), ) - .nonempty("At least one route is required.") + .check(z.minLength(1)) .describe( 'Routes, each group represents all POIs along a route. For example, [{ "data": ["西安钟楼", "西安大唐不夜城", "西安大雁塔"] }, { "data": ["西安曲江池公园", "西安回民街"] }]', ), width: MapWidthSchema, height: MapHeightSchema, -}; +}); // https://modelcontextprotocol.io/specification/2025-03-26/server/tools#listing-tools const tool = { name: "generate_path_map", description: "Generate a route map to display the user's planned route, such as travel guide routes.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const pathMap = { diff --git a/src/charts/pie.ts b/src/charts/pie.ts index 310fc8e..c31b875 100644 --- a/src/charts/pie.ts +++ b/src/charts/pie.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { BackgroundColorSchema, HeightSchema, @@ -17,16 +17,16 @@ const data = z.object({ }); // Pie chart input schema -const schema = { +const schema = z.object({ data: z - .array(data) + .tuple([data], data) + .check(z.minLength(1)) .describe( "Data for pie chart, it should be an array of objects, each object contains a `category` field and a `value` field, such as, [{ category: '分类一', value: 27 }].", - ) - .nonempty({ message: "Pie chart data cannot be empty." }), + ), innerRadius: z .number() - .default(0) + .prefault(0) .describe( "Set the innerRadius of pie chart, the value between 0 and 1. Set the pie chart as a donut chart. Set the value to 0.6 or number in [0 ,1] to enable it.", ), @@ -42,14 +42,14 @@ const schema = { width: WidthSchema, height: HeightSchema, title: TitleSchema, -}; +}); // Pie chart tool descriptor const tool = { name: "generate_pie_chart", description: "Generate a pie chart to show the proportion of parts, such as, market share and budget allocation.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const pie = { diff --git a/src/charts/pin-map.ts b/src/charts/pin-map.ts index eb42af2..d2ec40d 100644 --- a/src/charts/pin-map.ts +++ b/src/charts/pin-map.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { MapHeightSchema, MapTitleSchema, @@ -7,17 +7,17 @@ import { POIsSchema, } from "./base"; -const schema = { +const schema = z.object({ title: MapTitleSchema, data: POIsSchema, markerPopup: z .object({ - type: z.string().default("image").describe('Must be "image".'), - width: z.number().default(40).describe("Width of the photo."), - height: z.number().default(40).describe("Height of the photo."), + type: z.string().prefault("image").describe('Must be "image".'), + width: z.number().prefault(40).describe("Width of the photo."), + height: z.number().prefault(40).describe("Height of the photo."), borderRadius: z .number() - .default(8) + .prefault(8) .describe("Border radius of the photo."), }) .optional() @@ -26,14 +26,14 @@ const schema = { ), width: MapWidthSchema, height: MapHeightSchema, -}; +}); // https://modelcontextprotocol.io/specification/2025-03-26/server/tools#listing-tools const tool = { name: "generate_pin_map", description: "Generate a point map to display the location and distribution of point data on the map, such as the location distribution of attractions, hospitals, supermarkets, etc.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const pinMap = { diff --git a/src/charts/radar.ts b/src/charts/radar.ts index fddf594..e1fcae0 100644 --- a/src/charts/radar.ts +++ b/src/charts/radar.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { BackgroundColorSchema, HeightSchema, @@ -18,13 +18,13 @@ const data = z.object({ }); // Radar chart input schema -const schema = { +const schema = z.object({ data: z - .array(data) + .tuple([data], data) + .check(z.minLength(1)) .describe( "Data for radar chart, it should be an array of objects, each object contains a `name` field and a `value` field, such as, [{ name: 'Design', value: 70 }].", - ) - .nonempty({ message: "Radar chart data cannot be empty." }), + ), style: z .object({ backgroundColor: BackgroundColorSchema, @@ -37,14 +37,14 @@ const schema = { width: WidthSchema, height: HeightSchema, title: TitleSchema, -}; +}); // Radar chart tool descriptor const tool = { name: "generate_radar_chart", description: "Generate a radar chart to display multidimensional data (four dimensions or more), such as, evaluate Huawei and Apple phones in terms of five dimensions: ease of use, functionality, camera, benchmark scores, and battery life.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const radar = { diff --git a/src/charts/sankey.ts b/src/charts/sankey.ts index a821b82..a2c4756 100644 --- a/src/charts/sankey.ts +++ b/src/charts/sankey.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { BackgroundColorSchema, HeightSchema, @@ -16,17 +16,17 @@ const data = z.object({ value: z.number(), }); -const schema = { +const schema = z.object({ data: z - .array(data) + .tuple([data], data) + .check(z.minLength(1)) .describe( "Date for sankey chart, such as, [{ source: 'Landing Page', target: 'Product Page', value: 50000 }, { source: 'Product Page', target: 'Add to Cart', value: 35000 }, { source: 'Add to Cart', target: 'Checkout', value: 25000 }, { source: 'Checkout', target: 'Payment', value: 15000 }, { source: 'Payment', target: 'Purchase Completed', value: 8000 }].", - ) - .nonempty({ message: "Sankey chart data cannot be empty." }), + ), nodeAlign: z .enum(["left", "right", "justify", "center"]) .optional() - .default("center") + .prefault("center") .describe( "Alignment of nodes in the sankey chart, such as, 'left', 'right', 'justify', or 'center'.", ), @@ -42,13 +42,13 @@ const schema = { width: WidthSchema, height: HeightSchema, title: TitleSchema, -}; +}); const tool = { name: "generate_sankey_chart", description: "Generate a sankey chart to visualize the flow of data between different stages or categories, such as, the user journey from landing on a page to completing a purchase.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const sankey = { diff --git a/src/charts/scatter.ts b/src/charts/scatter.ts index db3664d..da8edf7 100644 --- a/src/charts/scatter.ts +++ b/src/charts/scatter.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { AxisXTitleSchema, AxisYTitleSchema, @@ -19,11 +19,11 @@ const data = z.object({ }); // Scatter chart input schema -const schema = { +const schema = z.object({ data: z - .array(data) - .describe("Data for scatter chart, such as, [{ x: 10, y: 15 }].") - .nonempty({ message: "Scatter chart data cannot be empty." }), + .tuple([data], data) + .check(z.minLength(1)) + .describe("Data for scatter chart, such as, [{ x: 10, y: 15 }]."), style: z .object({ backgroundColor: BackgroundColorSchema, @@ -38,14 +38,14 @@ const schema = { title: TitleSchema, axisXTitle: AxisXTitleSchema, axisYTitle: AxisYTitleSchema, -}; +}); // Scatter chart tool descriptor const tool = { name: "generate_scatter_chart", description: "Generate a scatter chart to show the relationship between two variables, helps discover their relationship or trends, such as, the strength of correlation, data distribution patterns.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const scatter = { diff --git a/src/charts/treemap.ts b/src/charts/treemap.ts index dfe3a3e..deb6f48 100644 --- a/src/charts/treemap.ts +++ b/src/charts/treemap.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { BackgroundColorSchema, HeightSchema, @@ -36,13 +36,13 @@ const TreeNodeSchema = z.object({ }); // Treemap chart input schema -const schema = { +const schema = z.object({ data: z - .array(TreeNodeSchema) + .tuple([TreeNodeSchema], TreeNodeSchema) + .check(z.minLength(1)) .describe( "Data for treemap chart which is a hierarchical structure, such as, [{ name: 'Design', value: 70, children: [{ name: 'Tech', value: 20 }] }], and the maximum depth is 3.", - ) - .nonempty({ message: "Treemap chart data cannot be empty." }), + ), style: z .object({ backgroundColor: BackgroundColorSchema, @@ -55,14 +55,14 @@ const schema = { width: WidthSchema, height: HeightSchema, title: TitleSchema, -}; +}); // Treemap chart tool descriptor const tool = { name: "generate_treemap_chart", description: "Generate a treemap chart to display hierarchical data and can intuitively show comparisons between items at the same level, such as, show disk space usage with treemap.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const treemap = { diff --git a/src/charts/venn.ts b/src/charts/venn.ts index 69e4035..3fdc8f2 100644 --- a/src/charts/venn.ts +++ b/src/charts/venn.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { BackgroundColorSchema, HeightSchema, @@ -25,13 +25,13 @@ const data = z.object({ ), }); -const schema = { +const schema = z.object({ data: z - .array(data) + .tuple([data], data) + .check(z.minLength(1)) .describe( "Data for venn chart, such as, [{ label: 'A', value: 10, sets: ['A'] }, { label: 'B', value: 20, sets: ['B'] }, { label: 'C', value: 30, sets: ['C'] }, { label: 'AB', value: 5, sets: ['A', 'B'] }].", - ) - .nonempty({ message: "Venn chart data cannot be empty." }), + ), style: z .object({ backgroundColor: BackgroundColorSchema, @@ -44,13 +44,13 @@ const schema = { width: WidthSchema, height: HeightSchema, title: TitleSchema, -}; +}); const tool = { name: "generate_venn_chart", description: "Generate a Venn diagram to visualize the relationships between different sets, showing how they intersect and overlap, such as the commonalities and differences between various groups.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const venn = { diff --git a/src/charts/violin.ts b/src/charts/violin.ts index 4405814..9d74e0f 100644 --- a/src/charts/violin.ts +++ b/src/charts/violin.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { AxisXTitleSchema, AxisYTitleSchema, @@ -25,13 +25,13 @@ const data = z.object({ ), }); -const schema = { +const schema = z.object({ data: z - .array(data) + .tuple([data], data) + .check(z.minLength(1)) .describe( "Data for violin chart, such as, [{ category: '分类一', value: 10 }] or [{ category: '分类二', value: 20, group: '组别一' }].", - ) - .nonempty({ message: "Violin chart data cannot be empty." }), + ), style: z .object({ backgroundColor: BackgroundColorSchema, @@ -46,13 +46,13 @@ const schema = { title: TitleSchema, axisXTitle: AxisXTitleSchema, axisYTitle: AxisYTitleSchema, -}; +}); const tool = { name: "generate_violin_chart", description: "Generate a violin chart to show data for statistical summaries among different categories, such as, comparing the distribution of data points across categories.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const violin = { diff --git a/src/charts/word-cloud.ts b/src/charts/word-cloud.ts index 0e88718..9a18c70 100644 --- a/src/charts/word-cloud.ts +++ b/src/charts/word-cloud.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { zodToJsonSchema } from "../utils"; + import { BackgroundColorSchema, HeightSchema, @@ -17,13 +17,13 @@ const data = z.object({ }); // Word cloud input schema -const schema = { +const schema = z.object({ data: z - .array(data) + .tuple([data], data) + .check(z.minLength(1)) .describe( "Data for word cloud chart, it should be an array of objects, each object contains a `text` field and a `value` field, such as, [{ value: 4.272, text: '形成' }].", - ) - .nonempty({ message: "Word cloud chart data cannot be empty." }), + ), style: z .object({ backgroundColor: BackgroundColorSchema, @@ -36,14 +36,14 @@ const schema = { width: WidthSchema, height: HeightSchema, title: TitleSchema, -}; +}); // Word cloud tool descriptor const tool = { name: "generate_word_cloud_chart", description: "Generate a word cloud chart to show word frequency or weight through text size variation, such as, analyzing common words in social media, reviews, or feedback.", - inputSchema: zodToJsonSchema(schema), + inputSchema: schema, }; export const wordCloud = { diff --git a/src/server.ts b/src/server.ts index 433e684..c283f08 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,8 +1,4 @@ -import { Server } from "@modelcontextprotocol/sdk/server/index.js"; -import { - CallToolRequestSchema, - ListToolsRequestSchema, -} from "@modelcontextprotocol/sdk/types.js"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import * as Charts from "./charts"; import { startHTTPStreamableServer, @@ -15,22 +11,14 @@ import { getDisabledTools } from "./utils/env"; /** * Creates and configures an MCP server for chart generation. */ -export function createServer(): Server { - const server = new Server( - { - name: "mcp-server-chart", - version: "0.8.x", - }, - { - capabilities: { - tools: {}, - }, - }, - ); +export function createServer(): McpServer { + const server = new McpServer({ + name: "mcp-server-chart", + version: "0.8.x", + }); setupToolHandlers(server); - server.onerror = (error) => console.error("[MCP Error]", error); process.on("SIGINT", async () => { await server.close(); process.exit(0); @@ -56,14 +44,18 @@ function getEnabledTools() { /** * Sets up tool handlers for the MCP server. */ -function setupToolHandlers(server: Server): void { - server.setRequestHandler(ListToolsRequestSchema, async () => ({ - tools: getEnabledTools().map((chart) => chart.tool), - })); +function setupToolHandlers(server: McpServer): void { + for (const chart of getEnabledTools()) { + const { name, description, inputSchema } = chart?.tool || {}; + if (!name || !description || !inputSchema) { + continue; + } - server.setRequestHandler(CallToolRequestSchema, async (request) => { - return await callTool(request.params.name, request.params.arguments); - }); + // @ts-ignore + server.tool(name, description, inputSchema.shape, async (params) => { + return await callTool(chart.tool.name, params); + }); + } } /** diff --git a/src/services/sse.ts b/src/services/sse.ts index 14924aa..390905f 100644 --- a/src/services/sse.ts +++ b/src/services/sse.ts @@ -1,10 +1,10 @@ import type { IncomingMessage, ServerResponse } from "node:http"; -import type { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; import { type RequestHandlers, createBaseHttpServer } from "../utils"; export const startSSEMcpServer = async ( - server: Server, + server: McpServer, endpoint = "/sse", port = 1122, ): Promise => { diff --git a/src/services/stdio.ts b/src/services/stdio.ts index 63a5234..9fa322e 100644 --- a/src/services/stdio.ts +++ b/src/services/stdio.ts @@ -1,7 +1,7 @@ -import type { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -export async function startStdioMcpServer(server: Server): Promise { +export async function startStdioMcpServer(server: McpServer): Promise { const transport = new StdioServerTransport(); await server.connect(transport); } diff --git a/src/services/streamable.ts b/src/services/streamable.ts index 83dc93d..6582b9a 100644 --- a/src/services/streamable.ts +++ b/src/services/streamable.ts @@ -1,6 +1,6 @@ import { randomUUID } from "node:crypto"; import type { IncomingMessage, ServerResponse } from "node:http"; -import type { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { type EventStore, StreamableHTTPServerTransport, @@ -14,7 +14,7 @@ import { } from "../utils"; export const startHTTPStreamableServer = async ( - createServer: () => Server, + createServer: () => McpServer, endpoint = "/mcp", port = 1122, eventStore: EventStore = new InMemoryEventStore(), @@ -22,7 +22,7 @@ export const startHTTPStreamableServer = async ( const activeTransports: Record< string, { - server: Server; + server: McpServer; transport: StreamableHTTPServerTransport; } > = {}; @@ -47,7 +47,7 @@ export const startHTTPStreamableServer = async ( : req.headers["mcp-session-id"]; let transport: StreamableHTTPServerTransport; - let server: Server; + let server: McpServer; const body = await getBody(req); @@ -144,7 +144,7 @@ export const startHTTPStreamableServer = async ( const sessionId = req.headers["mcp-session-id"] as string | undefined; const activeTransport: | { - server: Server; + server: McpServer; transport: StreamableHTTPServerTransport; } | undefined = sessionId ? activeTransports[sessionId] : undefined; diff --git a/src/utils/index.ts b/src/utils/index.ts index f645c25..ba7cfbd 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,6 +1,5 @@ export { callTool } from "./callTool"; export { generateChartUrl } from "./generate"; -export { zodToJsonSchema } from "./schema"; export { InMemoryEventStore } from "./InMemoryEventStore"; export { getBody } from "./getBody"; export { createBaseHttpServer, type RequestHandlers } from "./httpServer"; diff --git a/src/utils/schema.ts b/src/utils/schema.ts deleted file mode 100644 index bf6dd7d..0000000 --- a/src/utils/schema.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { z } from "zod"; -import { zodToJsonSchema as zodToJsonSchemaOriginal } from "zod-to-json-schema"; - -// TODO: use zod v4 JSON to schema to replace zod-to-json-schema when v4 is stable -// biome-ignore lint/suspicious/noExplicitAny: -export const zodToJsonSchema = (schema: Record>) => { - return zodToJsonSchemaOriginal(z.object(schema), { - rejectedAdditionalProperties: undefined, - $refStrategy: "none", - }); -};