Skip to content

Commit 5d78911

Browse files
authored
mcp-integrations: Remove string arrays in zod object schemas (#1627)
* Support arrays in input/output schema Signed-off-by: John Collier <[email protected]> * Use comma separated strings instead Signed-off-by: John Collier <[email protected]> * Fix tests Signed-off-by: John Collier <[email protected]> * Add changeset Signed-off-by: John Collier <[email protected]> * Add comment Signed-off-by: John Collier <[email protected]> * Regen API reports Signed-off-by: John Collier <[email protected]> * Re-regen api reports Signed-off-by: John Collier <[email protected]> --------- Signed-off-by: John Collier <[email protected]>
1 parent bed2466 commit 5d78911

File tree

7 files changed

+74
-189
lines changed

7 files changed

+74
-189
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-software-catalog-mcp-tool': patch
3+
'@red-hat-developer-hub/backstage-plugin-techdocs-mcp-tool': patch
4+
---
5+
6+
Remove string arrays in zod object schemas

workspaces/mcp-integrations/plugins/software-catalog-mcp-tool/src/plugin.test.ts

Lines changed: 29 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -127,32 +127,32 @@ describe('backstageMcpPlugin', () => {
127127
{
128128
name: 'my-service',
129129
kind: 'Component',
130-
tags: ['java', 'spring'],
130+
tags: 'java,spring',
131131
description: 'A Spring-based microservice',
132132
lifecycle: 'production',
133133
type: 'service',
134134
owner: 'test-team',
135-
dependsOn: [],
135+
dependsOn: '',
136136
},
137137
{
138138
name: 'my-api',
139139
kind: 'API',
140-
tags: ['rest', 'openapi'],
140+
tags: 'rest,openapi',
141141
description: 'REST API for data access',
142142
lifecycle: 'production',
143143
type: 'openapi',
144144
owner: 'user:jane.doe',
145-
dependsOn: [],
145+
dependsOn: '',
146146
},
147147
{
148148
name: 'my-system',
149149
kind: 'System',
150-
tags: [],
150+
tags: '',
151151
description: 'Core business system',
152152
lifecycle: 'production',
153153
type: 'system',
154154
owner: 'team-architecture',
155-
dependsOn: [],
155+
dependsOn: '',
156156
},
157157
],
158158
});
@@ -195,12 +195,12 @@ describe('backstageMcpPlugin', () => {
195195
{
196196
name: 'service-no-tags',
197197
kind: 'Component',
198-
tags: [],
198+
tags: '',
199199
description: 'Service without tags',
200200
lifecycle: 'staging',
201201
type: 'service',
202202
owner: 'user:john.doe',
203-
dependsOn: [],
203+
dependsOn: '',
204204
},
205205
],
206206
});
@@ -404,12 +404,12 @@ describe('backstageMcpPlugin', () => {
404404
{
405405
name: 'specific-service',
406406
kind: 'Component',
407-
tags: ['javascript'],
407+
tags: 'javascript',
408408
description: 'A specific web service',
409409
lifecycle: 'production',
410410
type: 'service',
411411
owner: 'team-frontend',
412-
dependsOn: [],
412+
dependsOn: '',
413413
},
414414
],
415415
});
@@ -476,12 +476,12 @@ describe('backstageMcpPlugin', () => {
476476
{
477477
name: 'my-service',
478478
kind: 'Component',
479-
tags: ['java', 'spring'],
479+
tags: 'java,spring',
480480
description: 'A Spring-based microservice',
481481
lifecycle: 'production',
482482
type: 'service',
483483
owner: 'team-backend',
484-
dependsOn: [],
484+
dependsOn: '',
485485
},
486486
],
487487
});
@@ -548,12 +548,12 @@ describe('backstageMcpPlugin', () => {
548548
{
549549
name: 'platform-service',
550550
kind: 'Component',
551-
tags: ['platform', 'core'],
551+
tags: 'platform,core',
552552
description: 'A platform service',
553553
lifecycle: 'production',
554554
type: 'service',
555555
owner: 'team-platform',
556-
dependsOn: [],
556+
dependsOn: '',
557557
},
558558
],
559559
});
@@ -597,12 +597,12 @@ describe('backstageMcpPlugin', () => {
597597
{
598598
name: 'minimal-service',
599599
kind: 'Component',
600-
tags: ['minimal'],
600+
tags: 'minimal',
601601
description: 'A minimal service',
602602
lifecycle: 'development',
603603
type: 'service',
604604
owner: 'user:minimal.owner',
605-
dependsOn: [],
605+
dependsOn: '',
606606
},
607607
],
608608
});
@@ -736,12 +736,12 @@ describe('backstageMcpPlugin', () => {
736736
{
737737
name: 'abridged-service',
738738
kind: 'Component',
739-
tags: ['java'],
739+
tags: 'java',
740740
description: 'An abridged service entity',
741741
lifecycle: 'production',
742742
type: 'service',
743743
owner: 'team-a',
744-
dependsOn: ['component:default/database'],
744+
dependsOn: 'component:default/database',
745745
},
746746
],
747747
});
@@ -807,12 +807,12 @@ describe('backstageMcpPlugin', () => {
807807
{
808808
name: 'default-service',
809809
kind: 'Component',
810-
tags: ['default'],
810+
tags: 'default',
811811
description: 'A default service entity',
812812
lifecycle: undefined,
813813
type: 'service',
814814
owner: 'team-default',
815-
dependsOn: [],
815+
dependsOn: '',
816816
},
817817
],
818818
});
@@ -845,7 +845,7 @@ describe('backstageMcpPlugin', () => {
845845
name?: string;
846846
owner?: string;
847847
lifecycle?: string;
848-
tags?: string[];
848+
tags?: string;
849849
verbose?: boolean;
850850
};
851851
}) => {
@@ -919,7 +919,7 @@ describe('backstageMcpPlugin', () => {
919919
name?: string;
920920
owner?: string;
921921
lifecycle?: string;
922-
tags?: string[];
922+
tags?: string;
923923
verbose?: boolean;
924924
};
925925
}) => {
@@ -955,12 +955,12 @@ describe('backstageMcpPlugin', () => {
955955
{
956956
name: 'test-service',
957957
kind: 'Component',
958-
tags: ['test'],
958+
tags: 'test',
959959
description: 'A test service',
960960
lifecycle: 'production',
961961
type: 'service',
962962
owner: 'test-team',
963-
dependsOn: [],
963+
dependsOn: '',
964964
},
965965
]);
966966
});
@@ -1002,7 +1002,7 @@ describe('backstageMcpPlugin', () => {
10021002
name?: string;
10031003
owner?: string;
10041004
lifecycle?: string;
1005-
tags?: string[];
1005+
tags?: string;
10061006
verbose?: boolean;
10071007
};
10081008
}) => {
@@ -1038,12 +1038,12 @@ describe('backstageMcpPlugin', () => {
10381038
{
10391039
name: 'test-component',
10401040
kind: 'Component',
1041-
tags: ['test'],
1041+
tags: 'test',
10421042
description: 'A test component',
10431043
lifecycle: 'production',
10441044
type: 'library',
10451045
owner: 'test-team',
1046-
dependsOn: [],
1046+
dependsOn: '',
10471047
},
10481048
]);
10491049
});
@@ -1130,141 +1130,15 @@ describe('backstageMcpPlugin', () => {
11301130
expect(result.output.entities[0]).toEqual({
11311131
name: 'test-component',
11321132
kind: 'Component',
1133-
tags: ['test'],
1133+
tags: 'test',
11341134
description: 'A test component',
11351135
lifecycle: undefined,
11361136
type: 'library',
11371137
owner: 'test-team',
1138-
dependsOn: [],
1138+
dependsOn: '',
11391139
});
11401140
expect(result.output.error).toBeUndefined();
11411141
});
11421142
});
11431143
});
1144-
1145-
describe('Action schemas and metadata', () => {
1146-
it('should have correct greet-user action structure', () => {
1147-
const greetUserActionDefinition = {
1148-
name: 'greet-user',
1149-
title: 'Greet User',
1150-
description: 'Generate a personalized greeting',
1151-
schema: {
1152-
input: (z: any) =>
1153-
z.object({
1154-
name: z.string().describe('The name of the person to greet'),
1155-
}),
1156-
output: (z: any) =>
1157-
z.object({
1158-
greeting: z.string().describe('The generated greeting'),
1159-
}),
1160-
},
1161-
};
1162-
1163-
expect(greetUserActionDefinition.name).toBe('greet-user');
1164-
expect(greetUserActionDefinition.title).toBe('Greet User');
1165-
expect(greetUserActionDefinition.description).toBe(
1166-
'Generate a personalized greeting',
1167-
);
1168-
expect(greetUserActionDefinition.schema.input).toBeDefined();
1169-
expect(greetUserActionDefinition.schema.output).toBeDefined();
1170-
});
1171-
1172-
it('should have correct fetch-catalog-entities action structure', () => {
1173-
const fetchCatalogEntitiesActionDefinition = {
1174-
name: 'fetch-catalog-entities',
1175-
title: 'Fetch Catalog Entities',
1176-
description:
1177-
'Search and retrieve catalog entities from the Backstage server.',
1178-
schema: {
1179-
input: (z: any) =>
1180-
z.object({
1181-
kind: z
1182-
.string()
1183-
.optional()
1184-
.describe(
1185-
'Filter entities by kind (e.g., Component, API, System)',
1186-
),
1187-
type: z
1188-
.string()
1189-
.optional()
1190-
.describe(
1191-
'Filter entities by type (e.g., service, library, website). Can only be used when kind is also specified.',
1192-
),
1193-
name: z
1194-
.string()
1195-
.optional()
1196-
.describe('Filter entities by name (exact match)'),
1197-
owner: z
1198-
.string()
1199-
.optional()
1200-
.describe(
1201-
'Filter entities by owner (e.g., team-platform, user:john.doe)',
1202-
),
1203-
}),
1204-
output: (z: any) =>
1205-
z.object({
1206-
entities: z
1207-
.array(
1208-
z.object({
1209-
name: z
1210-
.string()
1211-
.describe(
1212-
'The name field for each Backstage entity in the catalog',
1213-
),
1214-
kind: z
1215-
.string()
1216-
.describe(
1217-
'The kind/type of the Backstage entity (e.g., Component, API, System)',
1218-
),
1219-
tags: z
1220-
.array(z.string())
1221-
.describe(
1222-
'The tags associated with the Backstage entity',
1223-
),
1224-
description: z
1225-
.string()
1226-
.optional()
1227-
.describe('The description of the Backstage entity'),
1228-
type: z
1229-
.string()
1230-
.optional()
1231-
.describe(
1232-
'The type of the Backstage entity (e.g., service, library, website)',
1233-
),
1234-
owner: z
1235-
.string()
1236-
.optional()
1237-
.describe(
1238-
'The owner of the Backstage entity (e.g., team-platform, user:john.doe)',
1239-
),
1240-
dependsOn: z
1241-
.array(z.string())
1242-
.optional()
1243-
.describe(
1244-
'List of entities this entity depends on (e.g., component:default/database)',
1245-
),
1246-
}),
1247-
)
1248-
.describe('An array of entities'),
1249-
error: z
1250-
.string()
1251-
.optional()
1252-
.describe('Error message if validation fails'),
1253-
}),
1254-
},
1255-
};
1256-
1257-
expect(fetchCatalogEntitiesActionDefinition.name).toBe(
1258-
'fetch-catalog-entities',
1259-
);
1260-
expect(fetchCatalogEntitiesActionDefinition.title).toBe(
1261-
'Fetch Catalog Entities',
1262-
);
1263-
expect(fetchCatalogEntitiesActionDefinition.description).toBe(
1264-
'Search and retrieve catalog entities from the Backstage server.',
1265-
);
1266-
expect(fetchCatalogEntitiesActionDefinition.schema.input).toBeDefined();
1267-
expect(fetchCatalogEntitiesActionDefinition.schema.output).toBeDefined();
1268-
});
1269-
});
12701144
});

0 commit comments

Comments
 (0)