Skip to main content
Glama
framework-loader.ts4.91 kB
import {ErrorCode, McpError} from '@modelcontextprotocol/sdk/types.js'; import type {FrameworkData, ReferenceData, SymbolData} from '../../apple-client.js'; import type {ServerContext} from '../context.js'; const tokenize = (value: string | undefined): string[] => { if (!value) { return []; } const tokens = new Set<string>(); // Split on common delimiters const basicTokens = value.split(/[\s/._-]+/).filter(Boolean); for (const token of basicTokens) { // Add lowercase version tokens.add(token.toLowerCase()); // Add original case version for exact matches tokens.add(token); // Handle camelCase/PascalCase (e.g., GridItem -> grid, item, griditem) const camelParts = token.split(/(?=[A-Z])/).filter(Boolean); if (camelParts.length > 1) { for (const part of camelParts) { tokens.add(part.toLowerCase()); tokens.add(part); } // Add concatenated lowercase version tokens.add(camelParts.join('').toLowerCase()); } } return [...tokens]; }; export const loadActiveFrameworkData = async ({client, state}: ServerContext): Promise<FrameworkData> => { const activeTechnology = state.getActiveTechnology(); if (!activeTechnology) { throw new McpError( ErrorCode.InvalidRequest, 'No technology selected. Use `discover_technologies` then `choose_technology` first.', ); } const cached = state.getActiveFrameworkData(); if (cached) { return cached; } const identifierParts = activeTechnology.identifier.split('/'); const frameworkName = identifierParts.at(-1); if (!frameworkName) { throw new McpError( ErrorCode.InvalidRequest, `Invalid technology identifier: ${activeTechnology.identifier}`, ); } const data = await client.getFramework(frameworkName); state.setActiveFrameworkData(data); state.clearFrameworkIndex(); return data; }; const buildEntry = (id: string, ref: ReferenceData, extractText: (abstract?: ReferenceData['abstract']) => string) => { const tokens = new Set<string>(); for (const token of tokenize(ref.title)) { tokens.add(token); } for (const token of tokenize(ref.url)) { tokens.add(token); } const abstractText = extractText(ref.abstract); for (const token of tokenize(abstractText)) { tokens.add(token); } return {id, ref, tokens: [...tokens]}; }; const processReferences = ( references: Record<string, ReferenceData>, index: Map<string, {id: string; ref: ReferenceData; tokens: string[]}>, extractText: (abstract?: ReferenceData['abstract']) => string, ) => { for (const [id, ref] of Object.entries(references)) { if (!index.has(id)) { index.set(id, buildEntry(id, ref, extractText)); } } }; export const ensureFrameworkIndex = async (context: ServerContext) => { const {client, state} = context; const framework = await loadActiveFrameworkData(context); const existing = state.getFrameworkIndex(); if (existing) { return existing; } const index = new Map<string, {id: string; ref: ReferenceData; tokens: string[]}>(); const extract = client.extractText.bind(client); processReferences(framework.references, index, extract); state.setFrameworkIndex(index); return index; }; export const expandSymbolReferences = async ( context: ServerContext, identifiers: string[], ): Promise<Map<string, {id: string; ref: ReferenceData; tokens: string[]}>> => { const {client, state} = context; const activeTechnology = state.getActiveTechnology(); if (!activeTechnology) { throw new McpError( ErrorCode.InvalidRequest, 'No technology selected. Use `discover_technologies` then `choose_technology` first.', ); } const identifierParts = activeTechnology.identifier.split('/'); const frameworkName = identifierParts.at(-1); if (!frameworkName) { throw new McpError( ErrorCode.InvalidRequest, `Invalid technology identifier: ${activeTechnology.identifier}`, ); } const index = (await ensureFrameworkIndex(context)); const identifiersToProcess = identifiers.filter(identifier => !state.hasExpandedIdentifier(identifier)); const promises = identifiersToProcess.map(async identifier => { try { const symbolPath = identifier .replace('doc://com.apple.documentation/', '') .replace(/^documentation\//, 'documentation/'); const data: SymbolData = await client.getSymbol(symbolPath); return {data, identifier}; } catch (error) { console.warn(`Failed to expand identifier ${identifier}:`, error instanceof Error ? error.message : String(error)); return null; } }); const results = await Promise.all(promises); for (const result of results) { if (result) { const {data, identifier} = result; processReferences(data.references, index, client.extractText.bind(client)); state.markIdentifierExpanded(identifier); } } return index; }; export const getFrameworkIndexEntries = async (context: ServerContext) => { const index = await ensureFrameworkIndex(context); return [...index.values()]; };

Latest Blog Posts

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/MightyDillah/apple-doc-mcp'

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