Skip to main content
Glama

Financial Modeling Prep MCP Server

Apache 2.0
17
59
  • Linux
  • Apple
McpServerFactory.ts11.1 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { parseCommaSeparatedToolSets, validateDynamicToolDiscoveryConfig } from '../utils/validation.js'; import { DynamicToolsetManager } from '../dynamic-toolset-manager/DynamicToolsetManager.js'; import { registerMetaTools } from '../tools/meta-tools.js'; import { registerAllTools, registerToolsBySet } from '../tools/index.js'; import { getServerVersion } from '../utils/getServerVersion.js'; import { ServerModeEnforcer } from '../server-mode-enforcer/index.js'; import { registerPrompts } from "../prompts/index.js"; import type { ServerMode, ToolSet } from "../types/index.js"; /** * Session configuration type matching the SDK's CreateServerArg config */ export interface SessionConfig { FMP_ACCESS_TOKEN?: string; FMP_TOOL_SETS?: string; DYNAMIC_TOOL_DISCOVERY?: string; } /** * Configuration options for server creation * Compatible with SDK's CreateServerArg<T> where T = SessionConfig */ export interface McpServerOptions { config?: SessionConfig; serverAccessToken?: string; } /** * Result of server creation */ export interface McpServerCreationResult { mcpServer: McpServer; toolManager?: DynamicToolsetManager; // Only present in DYNAMIC_TOOL_DISCOVERY mode mode: ServerMode; } /** * Factory class for creating and configuring MCP servers * Handles server creation logic and tool registration based on mode */ export class McpServerFactory { private readonly version: string; constructor() { this.version = getServerVersion(); } /** * Public helper: determine mode from session config with enforcer precedence */ public determineMode(sessionConfig?: SessionConfig): ServerMode { return this._resolveSessionMode(sessionConfig); } /** * Public helper: determine static tool sets considering enforcer precedence * Returns an empty array when none are applicable */ public determineStaticToolSets(sessionConfig?: SessionConfig): ToolSet[] { // Prefer server-level toolsets if enforced try { const enforcer = ServerModeEnforcer.getInstance(); if (enforcer.serverModeOverride === 'STATIC_TOOL_SETS') { return enforcer.toolSets; } } catch { // Enforcer not initialized – fall back to session config } if (sessionConfig?.FMP_TOOL_SETS && typeof sessionConfig.FMP_TOOL_SETS === 'string') { const parsed = parseCommaSeparatedToolSets(sessionConfig.FMP_TOOL_SETS); return parsed; } return []; } /** * Creates a new MCP server with the appropriate configuration and tools * Compatible with SDK's CreateServerFn<SessionConfig> signature * @param options - Server creation options * @returns Server creation result with server, manager, and mode */ public createServer(options: McpServerOptions): McpServerCreationResult { const { config, serverAccessToken } = options; // Resolve access token and mode first const accessToken = this._resolveAccessToken(serverAccessToken, config); const mode = this._resolveSessionMode(config); console.log(`[McpServerFactory] Creating server in ${mode} mode`); // Create the base MCP server with appropriate capabilities const isDynamicMode = mode === 'DYNAMIC_TOOL_DISCOVERY'; const mcpServer = this._createMcpServerInstance(isDynamicMode); // Register tools based on mode const toolManager = this._registerToolsForMode(mcpServer, mode, config, accessToken); // Register prompts with mode-aware context const staticToolSets = this._resolveStaticToolsetsFromContext(mode); registerPrompts(mcpServer, { mode, version: this.version, listChanged: isDynamicMode, staticToolSets, }); return { mcpServer, toolManager, mode }; } /** * Creates a new, isolated McpServer instance with proper configuration schema * @param isDynamicMode - Whether this server instance supports dynamic tool changes * @returns Configured McpServer instance * * Note: There's a known issue in @modelcontextprotocol/sdk v1.0.0 where the main * capabilities.tools.listChanged field may not reflect the server configuration correctly. * The serverInfo.capabilities.tools.listChanged field does reflect the correct value. */ private _createMcpServerInstance(isDynamicMode: boolean = false): McpServer { return new McpServer({ name: "Financial Modeling Prep MCP (Stateful)", version: this.version, capabilities: { tools: { listChanged: isDynamicMode // Only enable dynamic tool changes in dynamic mode }, // Expose prompts capability for human-friendly prompts listing // Keep listChanged false for now; this server does not dynamically change prompts prompts: { listChanged: false } }, configSchema: { type: "object", properties: { FMP_ACCESS_TOKEN: { type: "string", title: "FMP Access Token", description: "Financial Modeling Prep API access token. Optional for server initialization; required to successfully call FMP-backed tools.", }, FMP_TOOL_SETS: { type: "string", title: "Tool Sets (Optional)", description: "Comma-separated list of tool sets to load (e.g., 'search,company,quotes'). If not specified, all tools will be loaded.", }, DYNAMIC_TOOL_DISCOVERY: { type: "string", title: "Dynamic Tool Discovery (Optional)", description: "Enable dynamic toolset management. Set to 'true' to use meta-tools.", }, }, }, }); } /** * Resolves the access token with priority: server token > session config * @param serverToken - Access token from server configuration * @param sessionConfig - Configuration from session * @returns Resolved access token or undefined */ private _resolveAccessToken( serverToken?: string, sessionConfig?: SessionConfig ): string | undefined { // Precedence: server-level token overrides session config return serverToken || sessionConfig?.FMP_ACCESS_TOKEN; } /** * Determines the server mode based on server-level enforcement and session configuration * @param sessionConfig - Configuration from session * @returns Determined server mode */ private _resolveSessionMode(sessionConfig?: SessionConfig): ServerMode { // Check for server-level mode enforcement first try { const enforcer = ServerModeEnforcer.getInstance(); const serverModeOverride = enforcer.serverModeOverride; if (serverModeOverride) { console.log(`[McpServerFactory] Server-level mode enforced: ${serverModeOverride}`); return serverModeOverride; } } catch (error) { // ServerModeEnforcer not initialized, continue with session-based resolution console.log('[McpServerFactory] No server-level mode enforcement, using session-based mode resolution'); } // Fall back to session-based mode resolution const isDynamic = validateDynamicToolDiscoveryConfig(sessionConfig?.DYNAMIC_TOOL_DISCOVERY); if (isDynamic === true) { return 'DYNAMIC_TOOL_DISCOVERY'; } // Check if specific toolsets are provided in session config if (sessionConfig?.FMP_TOOL_SETS && typeof sessionConfig.FMP_TOOL_SETS === 'string') { // Validate the toolsets before deciding on STATIC_TOOL_SETS mode const validToolSets = parseCommaSeparatedToolSets(sessionConfig.FMP_TOOL_SETS); if (validToolSets.length > 0) { return 'STATIC_TOOL_SETS'; } else { console.warn(`[McpServerFactory] No valid toolsets found in session config FMP_TOOL_SETS: "${sessionConfig.FMP_TOOL_SETS}". Falling back to ALL_TOOLS mode.`); return 'ALL_TOOLS'; } } // Default to legacy mode (all tools) return 'ALL_TOOLS'; } /** * Registers tools based on the determined server mode * @param mcpServer - The MCP server instance * @param mode - The determined server mode * @param sessionConfig - Configuration from session * @param accessToken - Resolved access token * @returns Tool manager instance (if applicable) */ private _registerToolsForMode( mcpServer: McpServer, mode: ServerMode, sessionConfig?: SessionConfig, accessToken?: string ): DynamicToolsetManager | undefined { let toolManager: DynamicToolsetManager | undefined = undefined; switch (mode) { case 'DYNAMIC_TOOL_DISCOVERY': // For dynamic mode, create a new manager instance and register meta-tools // Each session gets its own isolated DynamicToolsetManager instance toolManager = new DynamicToolsetManager(mcpServer, accessToken); registerMetaTools(mcpServer, toolManager); console.log(`[McpServerFactory] Created isolated toolset manager and registered meta-tools for dynamic mode`); break; case 'STATIC_TOOL_SETS': // For static mode, check server-level enforcement first, then session config let toolSets: ToolSet[] = []; let source = 'session'; try { const enforcer = ServerModeEnforcer.getInstance(); if (enforcer.serverModeOverride === 'STATIC_TOOL_SETS') { toolSets = enforcer.toolSets; source = 'server-level'; } } catch (error) { // ServerModeEnforcer not available, fall back to session config } // If no server-level toolsets, parse from session config if (toolSets.length === 0) { const toolSetsString = (sessionConfig?.FMP_TOOL_SETS as string) || ''; toolSets = parseCommaSeparatedToolSets(toolSetsString); } console.log(`[McpServerFactory] Loading static toolsets from ${source}: ${toolSets.join(', ')}`); registerToolsBySet(mcpServer, toolSets, accessToken); // toolManager remains undefined for static mode break; case 'ALL_TOOLS': default: // For legacy mode, register all available tools console.log(`[McpServerFactory] Loading all tools (Legacy Mode)`); registerAllTools(mcpServer, accessToken); // toolManager remains undefined for legacy mode break; } return toolManager; } /** * Resolves static toolsets from server-level enforcement or session config for prompt context * @param mode - The determined server mode * @param sessionConfig - Configuration from session * @returns Resolved static toolsets or undefined */ private _resolveStaticToolsetsFromContext(mode: ServerMode): ToolSet[] | undefined { if (mode !== 'STATIC_TOOL_SETS') return undefined; // Prefer server-level toolsets if enforced try { if (mode === 'STATIC_TOOL_SETS') { const enforcer = ServerModeEnforcer.getInstance(); return enforcer.toolSets; } } catch { return undefined; } } }

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