Skip to main content
Glama
joelmnz

Article Manager MCP Server

by joelmnz
MarkdownView.tsx3.13 kB
import React from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { MermaidDiagram } from './MermaidDiagram'; interface MarkdownViewProps { content: string; } function sanitizeUrl(rawUrl: string | undefined, kind: 'link' | 'image'): string | null { if (!rawUrl) { return null; } const url = rawUrl.trim(); if (!url) { return null; } // Allow anchors and relative URLs if (url.startsWith('#') || url.startsWith('/') || url.startsWith('./') || url.startsWith('../')) { return url; } // Allow protocol-relative URLs if (url.startsWith('//')) { return url; } // Parse absolute URLs safely try { const parsed = new URL(url); const protocol = parsed.protocol.toLowerCase(); // Disallow scriptable or unexpected schemes if (protocol === 'javascript:' || protocol === 'vbscript:' || protocol === 'file:') { return null; } if (kind === 'image') { // Keep images to http/https only to reduce edge-case SVG/data risks if (protocol !== 'http:' && protocol !== 'https:') { return null; } return url; } // Links: allow http(s), mailto, tel if (protocol === 'http:' || protocol === 'https:' || protocol === 'mailto:' || protocol === 'tel:') { return url; } return null; } catch { // If it isn't a valid absolute URL, treat as unsafe return null; } } function isExternalUrl(url: string): boolean { return url.startsWith('http://') || url.startsWith('https://') || url.startsWith('//'); } /** * Reusable component for rendering markdown with Mermaid diagram support */ export function MarkdownView({ content }: MarkdownViewProps) { return ( <ReactMarkdown remarkPlugins={[remarkGfm]} components={{ a({ href, children, ...props }) { const safeHref = sanitizeUrl(href, 'link'); if (!safeHref) { return <span>{children}</span>; } const external = isExternalUrl(safeHref); return ( <a href={safeHref} target={external ? '_blank' : undefined} rel={external ? 'noopener noreferrer' : undefined} {...props} > {children} </a> ); }, img({ src, alt, ...props }) { const safeSrc = sanitizeUrl(src, 'image'); if (!safeSrc) { return null; } return <img src={safeSrc} alt={alt} loading="lazy" {...props} />; }, code({ className, children, ...props }) { const match = /language-(\w+)/.exec(className || ''); const language = match ? match[1] : ''; const isInline = !className; if (!isInline && language === 'mermaid') { return <MermaidDiagram chart={String(children).trim()} />; } return ( <code className={className} {...props}> {children} </code> ); } }} > {content} </ReactMarkdown> ); }

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/joelmnz/mcp-markdown-manager'

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