Skip to main content
Glama
frmr.ts3.62 kB
import type { ControlMapping, FrmrDocumentMeta, FrmrDocumentType, FrmrSummary, KsiItem, } from "./types.js"; import { createError } from "./util.js"; import { getControlMappings, getFrmrDocuments, getKsiItems, resolveFrmrDocument, } from "./indexer.js"; export function listFrmrDocuments(): FrmrDocumentMeta[] { return getFrmrDocuments().map( ({ raw, rawText, topLevelKeys, idKey, ...meta }) => meta, ); } export function getFrmrDocument( type: FrmrDocumentType | undefined, path: string, ): { meta: FrmrDocumentMeta; rawJson: string; summary: FrmrSummary } { const doc = resolveFrmrDocument(path); if (!doc) { throw createError({ code: "NOT_FOUND", message: `FRMR document not found at path ${path}`, }); } if (type && doc.type !== type) { throw createError({ code: "BAD_REQUEST", message: `Requested type ${type} does not match document type ${doc.type}`, }); } const { rawText, topLevelKeys } = doc; const meta: FrmrDocumentMeta = { type: doc.type, title: doc.title, version: doc.version, published: doc.published, path: doc.path, idHint: doc.idHint, itemCount: doc.itemCount, }; const summary: FrmrSummary = { countItems: doc.itemCount, topLevelKeys, }; return { meta, rawJson: rawText, summary }; } export interface ListKsiOptions { id?: string; text?: string; category?: string; status?: string; limit: number; offset: number; } function textMatches(haystack: string | undefined, needle: string): boolean { if (!haystack) { return false; } return haystack.toLowerCase().includes(needle.toLowerCase()); } export function listKsiItems( options: ListKsiOptions, ): { total: number; items: KsiItem[] } { const all = getKsiItems(); const filtered = all.filter((item) => { if (options.id && item.id !== options.id) { return false; } if ( options.text && !( textMatches(item.title, options.text) || textMatches(item.description, options.text) ) ) { return false; } if ( options.category && !textMatches(item.category, options.category) ) { return false; } if ( options.status && options.status !== item.status ) { return false; } return true; }); const total = filtered.length; const items = filtered.slice(options.offset, options.offset + options.limit); return { total, items }; } export function getKsiItem(id: string): KsiItem { const match = getKsiItems().find((item) => item.id === id); if (!match) { throw createError({ code: "NOT_FOUND", message: `KSI item not found for id ${id}`, }); } return match; } export interface ListControlOptions { family?: string; control?: string; source?: FrmrDocumentType; } export function listControlMappings( options: ListControlOptions, ): ControlMapping[] { return getControlMappings().filter((mapping) => { if (options.source && mapping.source !== options.source) { return false; } if (options.family) { const family = mapping.control.split("-")[0]; if (!family.startsWith(options.family.toUpperCase())) { return false; } } if (options.control) { const expected = options.control.toUpperCase(); const controlId = mapping.control.toUpperCase(); if ( !( controlId === expected || controlId.startsWith(`${expected}-`) || expected.startsWith(controlId) ) ) { return false; } } return true; }); }

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/ethanolivertroy/fedramp-docs-mcp'

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