Skip to main content
Glama

Linear Streamable MCP Server

by iceener
mappers.ts5.78 kB
import type { ListCommentsOutput, ListIssuesOutput, ListProjectsOutput, ListTeamsOutput, ListUsersOutput, } from '../schemas/outputs.ts'; // Generic helpers / guards export function isRecord(value: unknown): value is Record<string, unknown> { return typeof value === 'object' && value !== null; } export function hasFunction< Fn extends (...args: unknown[]) => unknown = (...args: unknown[]) => unknown, >(value: unknown, key: string): value is Record<string, Fn> { return ( isRecord(value) && typeof (value as Record<string, unknown>)[key] === 'function' ); } export function hasProperty<T = unknown>( value: unknown, key: string, ): value is Record<string, T> { return isRecord(value) && key in value; } export async function mapIssueNodeToListItem( node: unknown, ): Promise<ListIssuesOutput['items'][number]> { // Labels const labels = await (async () => { try { if (hasFunction(node, 'labels')) { const conn = await ( node as { labels: () => Promise<{ nodes: Array<{ id: string; name: string }>; }>; } ).labels(); return conn.nodes.map((l) => ({ id: l.id, name: l.name })); } } catch {} return [] as Array<{ id: string; name: string }>; })(); // Related names (best-effort) let stateName: string | undefined; let projectName: string | undefined; let assigneeName: string | undefined; try { const s = (node as { state?: Promise<{ name?: string }> }).state; if (s && typeof (s as unknown) === 'object') { const resolved = await s; stateName = resolved?.name ?? undefined; } } catch {} try { const p = (node as { project?: Promise<{ name?: string }> }).project; if (p && typeof (p as unknown) === 'object') { const resolved = await p; projectName = resolved?.name ?? undefined; } } catch {} try { const a = (node as { assignee?: Promise<{ name?: string }> }).assignee; if (a && typeof (a as unknown) === 'object') { const resolved = await a; assigneeName = resolved?.name ?? undefined; } } catch {} const id = (node as { id?: string })?.id ?? ''; const identifier = (node as { identifier?: string })?.identifier ?? undefined; const title = (node as { title?: string })?.title ?? ''; const description = (node as { description?: string | null })?.description ?? undefined; const priority = (node as { priority?: number | null })?.priority ?? undefined; const estimate = (node as { estimate?: number | null })?.estimate ?? undefined; const stateId = (node as { stateId?: string | null })?.stateId ?? ''; const projectId = (node as { projectId?: string | null })?.projectId ?? undefined; const assigneeId = (node as { assigneeId?: string | null })?.assigneeId ?? undefined; const createdAt = String( (node as { createdAt?: Date | string | null })?.createdAt ?? '', ); const updatedAt = String( (node as { updatedAt?: Date | string | null })?.updatedAt ?? '', ); const archivedAtRaw = (node as { archivedAt?: Date | string | null })?.archivedAt ?? undefined; const archivedAt = archivedAtRaw ? String(archivedAtRaw) : undefined; const dueDate = (node as { dueDate?: string })?.dueDate ?? undefined; const url = (node as { url?: string })?.url ?? undefined; return { id, identifier, title, description, priority, estimate, stateId, stateName, projectId, projectName, assigneeId, assigneeName, createdAt, updatedAt, archivedAt, dueDate, url, labels, }; } export function mapProjectNodeToListItem( node: unknown, ): ListProjectsOutput['items'][number] { return { id: (node as { id?: string })?.id ?? '', name: (node as { name?: string })?.name ?? '', state: (node as { state?: string })?.state ?? '', teamId: undefined, leadId: (node as { leadId?: string | null })?.leadId ?? undefined, targetDate: (node as { targetDate?: string | null })?.targetDate ?? undefined, description: (node as { description?: string | null })?.description ?? undefined, }; } export function mapTeamNodeToListItem(node: unknown): ListTeamsOutput['items'][number] { return { id: (node as { id?: string })?.id ?? '', key: (node as { key?: string | null })?.key ?? undefined, name: (node as { name?: string })?.name ?? '', }; } export function mapUserNodeToListItem(node: unknown): ListUsersOutput['items'][number] { return { id: (node as { id?: string })?.id ?? '', name: (node as { name?: string | null })?.name ?? undefined, email: (node as { email?: string | null })?.email ?? undefined, displayName: (node as { displayName?: string | null })?.displayName ?? undefined, avatarUrl: (node as { avatarUrl?: string | null })?.avatarUrl ?? undefined, }; } export async function mapCommentNodeToListItem( node: unknown, ): Promise<ListCommentsOutput['items'][number]> { let user: { id: string; name?: string } | undefined; try { const u = await (node as { user?: Promise<unknown> }).user; if (u && isRecord(u)) { user = { id: (u as { id?: string })?.id ?? '', name: (u as { name?: string | null })?.name ?? undefined, }; } } catch {} const createdRaw = (node as { createdAt?: Date | string })?.createdAt; const updatedRaw = (node as { updatedAt?: Date | string | null })?.updatedAt ?? undefined; return { id: (node as { id?: string })?.id ?? '', body: (node as { body?: string | null })?.body ?? undefined, url: (node as { url?: string | null })?.url ?? undefined, createdAt: createdRaw ? String(createdRaw) : '', updatedAt: updatedRaw ? String(updatedRaw) : undefined, user, }; }

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/iceener/linear-streamable-mcp-server'

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