Skip to main content
Glama

Financial Modeling Prep MCP Server

Apache 2.0
17
59
  • Linux
  • Apple
ServerJsonSchemaValidation.test.ts14.4 kB
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { readFile, writeFile, mkdir, rm } from 'fs/promises'; import { join } from 'path'; import { tmpdir } from 'os'; import { validateServerJsonSchema } from '../utils/versionSync.js'; describe('ServerJsonSchemaValidation', () => { let testDir: string; beforeEach(async () => { testDir = join(tmpdir(), `server-json-test-${Date.now()}`); await mkdir(testDir, { recursive: true }); }); afterEach(async () => { await rm(testDir, { recursive: true, force: true }); }); /** * Creates a valid server.json file for testing. * @param overrides - Optional overrides for server.json fields. */ async function createServerJson(overrides: any = {}): Promise<void> { const defaultServerJson = { $schema: 'https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json', name: 'io.github.imbenrabi/financial-modeling-prep-mcp-server', description: 'MCP server for Financial Modeling Prep API with 250+ financial data tools', version: '2.5.0', status: 'active', packages: [ { registry_type: 'npm', registry_base_url: 'https://registry.npmjs.org', identifier: 'financial-modeling-prep-mcp-server', version: '2.5.0', runtime_hint: 'npx', transport: { type: 'streamable-http', url: 'https://financial-modeling-prep-mcp-server-production.up.railway.app/mcp' }, package_arguments: [ { type: 'named', name: '--fmp-token', description: 'Financial Modeling Prep API access token', is_required: false, format: 'string' } ], environment_variables: [ { name: 'FMP_ACCESS_TOKEN', description: 'Financial Modeling Prep API access token', is_required: false, format: 'string', is_secret: true } ] } ], repository: { url: 'https://github.com/imbenrabi/Financial-Modeling-Prep-MCP-Server', source: 'github', id: '988409529' }, website_url: 'https://github.com/imbenrabi/Financial-Modeling-Prep-MCP-Server', ...overrides }; await writeFile( join(testDir, 'server.json'), JSON.stringify(defaultServerJson, null, 2), 'utf-8' ); } describe('Official Schema Compliance', () => { it('should validate a complete valid server.json', async () => { await createServerJson(); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it('should require $schema field', async () => { await createServerJson({ $schema: undefined }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors).toContain('server.json missing required field: $schema'); }); it('should require name field', async () => { await createServerJson({ name: undefined }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors).toContain('server.json missing required field: name'); }); it('should require description field', async () => { await createServerJson({ description: undefined }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors).toContain('server.json missing required field: description'); }); it('should require version field', async () => { await createServerJson({ version: undefined }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors).toContain('server.json missing required field: version'); }); it('should validate description length (1-100 characters)', async () => { await createServerJson({ description: '' }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors).toContain('server.json description must be 1-100 characters, got 0'); }); it('should reject description longer than 100 characters', async () => { const longDescription = 'a'.repeat(101); await createServerJson({ description: longDescription }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors).toContain('server.json description must be 1-100 characters, got 101'); }); it('should warn about non-standard schema URL', async () => { await createServerJson({ $schema: 'https://example.com/custom-schema.json' }); const result = await validateServerJsonSchema(testDir); expect(result.warnings).toContain( 'server.json uses schema: https://example.com/custom-schema.json, expected: https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json' ); }); it('should warn about name without namespace', async () => { await createServerJson({ name: 'simple-server-name' }); const result = await validateServerJsonSchema(testDir); expect(result.warnings).toContain( 'server.json name should include namespace (e.g., "namespace/server-name")' ); }); it('should validate SemVer version format', async () => { await createServerJson({ version: 'invalid-version' }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors).toContain('server.json version "invalid-version" is not valid SemVer'); }); }); describe('Packages Array Validation', () => { it('should validate NPM package configuration', async () => { await createServerJson({ packages: [ { registry_type: 'npm', identifier: 'test-package', version: '1.0.0' } ] }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(true); }); it('should require registry_type in packages', async () => { await createServerJson({ packages: [ { identifier: 'test-package', version: '1.0.0' } ] }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors).toContain('server.json packages[0] missing registry_type'); }); it('should require identifier in packages', async () => { await createServerJson({ packages: [ { registry_type: 'npm', version: '1.0.0' } ] }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors).toContain('server.json packages[0] missing identifier'); }); it('should require version in packages', async () => { await createServerJson({ packages: [ { registry_type: 'npm', identifier: 'test-package' } ] }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors).toContain('server.json packages[0] missing version'); }); it('should validate package version SemVer format', async () => { await createServerJson({ packages: [ { registry_type: 'npm', identifier: 'test-package', version: 'invalid' } ] }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors).toContain('server.json packages[0].version "invalid" is not valid SemVer'); }); it('should validate multiple packages', async () => { await createServerJson({ packages: [ { registry_type: 'npm', identifier: 'test-package-1', version: '1.0.0' }, { registry_type: 'pypi', identifier: 'test-package-2', version: '2.0.0' } ] }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(true); }); }); describe('Repository Validation', () => { it('should validate complete repository configuration', async () => { await createServerJson({ repository: { url: 'https://github.com/user/repo', source: 'github', id: '123456789' } }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(true); }); it('should require repository url', async () => { await createServerJson({ repository: { source: 'github' } }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors).toContain('server.json repository missing url field'); }); it('should require repository source', async () => { await createServerJson({ repository: { url: 'https://github.com/user/repo' } }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors).toContain('server.json repository missing source field'); }); it('should allow repository to be optional', async () => { await createServerJson({ repository: undefined }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(true); }); }); describe('Transport Configuration Validation', () => { it('should validate streamable-http transport', async () => { await createServerJson({ packages: [ { registry_type: 'npm', identifier: 'test-package', version: '1.0.0', transport: { type: 'streamable-http', url: 'https://example.com/mcp' } } ] }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(true); }); it('should validate stdio transport', async () => { await createServerJson({ packages: [ { registry_type: 'npm', identifier: 'test-package', version: '1.0.0', transport: { type: 'stdio' } } ] }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(true); }); it('should validate sse transport', async () => { await createServerJson({ packages: [ { registry_type: 'npm', identifier: 'test-package', version: '1.0.0', transport: { type: 'sse', url: 'https://example.com/sse' } } ] }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(true); }); }); describe('Runtime Arguments Validation', () => { it('should validate package arguments structure', async () => { await createServerJson({ packages: [ { registry_type: 'npm', identifier: 'test-package', version: '1.0.0', package_arguments: [ { type: 'named', name: '--token', description: 'API token', is_required: true, format: 'string' }, { type: 'positional', name: 'input-file', description: 'Input file path', is_required: false, format: 'string' } ] } ] }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(true); }); it('should validate environment variables structure', async () => { await createServerJson({ packages: [ { registry_type: 'npm', identifier: 'test-package', version: '1.0.0', environment_variables: [ { name: 'API_TOKEN', description: 'API access token', is_required: true, format: 'string', is_secret: true }, { name: 'DEBUG_MODE', description: 'Enable debug logging', is_required: false, format: 'boolean', is_secret: false } ] } ] }); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(true); }); }); describe('Error Handling', () => { it('should handle missing server.json file', async () => { // Don't create server.json file const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors.some(error => error.includes('Failed to validate server.json schema'))).toBe(true); }); it('should handle invalid JSON', async () => { await writeFile( join(testDir, 'server.json'), '{ invalid json }', 'utf-8' ); const result = await validateServerJsonSchema(testDir); expect(result.isValid).toBe(false); expect(result.errors.some(error => error.includes('Failed to validate server.json schema'))).toBe(true); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/imbenrabi/Financial-Modeling-Prep-MCP-Server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server