mirror of
https://github.com/samanhappy/mcphub.git
synced 2025-12-24 02:39:19 -05:00
feat: Add cleanInputSchema function to remove $schema field from inputSchema (#255)
This commit is contained in:
102
src/services/__tests__/schema-cleanup.test.ts
Normal file
102
src/services/__tests__/schema-cleanup.test.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
describe('Schema Cleanup Tests', () => {
|
||||
describe('cleanInputSchema functionality', () => {
|
||||
// Helper function to simulate the cleanInputSchema behavior
|
||||
const cleanInputSchema = (schema: any): any => {
|
||||
if (!schema || typeof schema !== 'object') {
|
||||
return schema;
|
||||
}
|
||||
|
||||
const cleanedSchema = { ...schema };
|
||||
delete cleanedSchema.$schema;
|
||||
|
||||
return cleanedSchema;
|
||||
};
|
||||
|
||||
test('should remove $schema field from inputSchema', () => {
|
||||
const schemaWithDollarSchema = {
|
||||
$schema: 'http://json-schema.org/draft-07/schema#',
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Test property',
|
||||
},
|
||||
},
|
||||
required: ['name'],
|
||||
};
|
||||
|
||||
const cleanedSchema = cleanInputSchema(schemaWithDollarSchema);
|
||||
|
||||
expect(cleanedSchema).not.toHaveProperty('$schema');
|
||||
expect(cleanedSchema.type).toBe('object');
|
||||
expect(cleanedSchema.properties).toEqual({
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Test property',
|
||||
},
|
||||
});
|
||||
expect(cleanedSchema.required).toEqual(['name']);
|
||||
});
|
||||
|
||||
test('should handle null and undefined schemas', () => {
|
||||
expect(cleanInputSchema(null)).toBe(null);
|
||||
expect(cleanInputSchema(undefined)).toBe(undefined);
|
||||
});
|
||||
|
||||
test('should handle non-object schemas', () => {
|
||||
expect(cleanInputSchema('string')).toBe('string');
|
||||
expect(cleanInputSchema(42)).toBe(42);
|
||||
expect(cleanInputSchema(true)).toBe(true);
|
||||
});
|
||||
|
||||
test('should preserve other properties while removing $schema', () => {
|
||||
const complexSchema = {
|
||||
$schema: 'http://json-schema.org/draft-07/schema#',
|
||||
type: 'object',
|
||||
title: 'Test Schema',
|
||||
description: 'A test schema',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
age: { type: 'number' },
|
||||
},
|
||||
required: ['name'],
|
||||
additionalProperties: false,
|
||||
};
|
||||
|
||||
const cleanedSchema = cleanInputSchema(complexSchema);
|
||||
|
||||
expect(cleanedSchema).not.toHaveProperty('$schema');
|
||||
expect(cleanedSchema.type).toBe('object');
|
||||
expect(cleanedSchema.title).toBe('Test Schema');
|
||||
expect(cleanedSchema.description).toBe('A test schema');
|
||||
expect(cleanedSchema.properties).toEqual({
|
||||
name: { type: 'string' },
|
||||
age: { type: 'number' },
|
||||
});
|
||||
expect(cleanedSchema.required).toEqual(['name']);
|
||||
expect(cleanedSchema.additionalProperties).toBe(false);
|
||||
});
|
||||
|
||||
test('should handle schemas without $schema field', () => {
|
||||
const schemaWithoutDollarSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
},
|
||||
};
|
||||
|
||||
const cleanedSchema = cleanInputSchema(schemaWithoutDollarSchema);
|
||||
|
||||
expect(cleanedSchema).toEqual(schemaWithoutDollarSchema);
|
||||
expect(cleanedSchema).not.toHaveProperty('$schema');
|
||||
});
|
||||
|
||||
test('should handle empty objects', () => {
|
||||
const emptySchema = {};
|
||||
const cleanedSchema = cleanInputSchema(emptySchema);
|
||||
|
||||
expect(cleanedSchema).toEqual({});
|
||||
expect(cleanedSchema).not.toHaveProperty('$schema');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -99,6 +99,18 @@ export const syncToolEmbedding = async (serverName: string, toolName: string) =>
|
||||
saveToolsAsVectorEmbeddings(serverName, [tool]);
|
||||
};
|
||||
|
||||
// Helper function to clean $schema field from inputSchema
|
||||
const cleanInputSchema = (schema: any): any => {
|
||||
if (!schema || typeof schema !== 'object') {
|
||||
return schema;
|
||||
}
|
||||
|
||||
const cleanedSchema = { ...schema };
|
||||
delete cleanedSchema.$schema;
|
||||
|
||||
return cleanedSchema;
|
||||
};
|
||||
|
||||
// Store all server information
|
||||
let serverInfos: ServerInfo[] = [];
|
||||
|
||||
@@ -272,7 +284,7 @@ const callToolWithReconnect = async (
|
||||
serverInfo.tools = tools.tools.map((tool) => ({
|
||||
name: `${serverInfo.name}-${tool.name}`,
|
||||
description: tool.description || '',
|
||||
inputSchema: tool.inputSchema || {},
|
||||
inputSchema: cleanInputSchema(tool.inputSchema || {}),
|
||||
}));
|
||||
|
||||
// Save tools as vector embeddings for search
|
||||
@@ -391,7 +403,7 @@ export const initializeClientsFromSettings = async (isInit: boolean): Promise<Se
|
||||
const mcpTools: ToolInfo[] = openApiTools.map((tool) => ({
|
||||
name: `${name}-${tool.name}`,
|
||||
description: tool.description,
|
||||
inputSchema: tool.inputSchema,
|
||||
inputSchema: cleanInputSchema(tool.inputSchema),
|
||||
}));
|
||||
|
||||
// Update server info with successful initialization
|
||||
@@ -472,7 +484,7 @@ export const initializeClientsFromSettings = async (isInit: boolean): Promise<Se
|
||||
serverInfo.tools = tools.tools.map((tool) => ({
|
||||
name: `${name}-${tool.name}`,
|
||||
description: tool.description || '',
|
||||
inputSchema: tool.inputSchema || {},
|
||||
inputSchema: cleanInputSchema(tool.inputSchema || {}),
|
||||
}));
|
||||
serverInfo.status = 'connected';
|
||||
serverInfo.error = null;
|
||||
@@ -924,7 +936,7 @@ export const handleCallToolRequest = async (request: any, extra: any) => {
|
||||
return {
|
||||
name: result.toolName,
|
||||
description: result.description || '',
|
||||
inputSchema: result.inputSchema || {},
|
||||
inputSchema: cleanInputSchema(result.inputSchema || {}),
|
||||
};
|
||||
})
|
||||
.filter((tool) => {
|
||||
|
||||
Reference in New Issue
Block a user