mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-09 15:26:03 +00:00
feat(mcp): persist and display marketplace README
Capture the README markdown from LangBot Space when installing an MCP server and store it on the mcp_servers record (new readme column + alembic migration 0004). The detail page can then render docs offline, independent of the server's runtime/connection state.
This commit is contained in:
74
web/src/app/home/mcp/components/mcp-form/MCPReadme.tsx
Normal file
74
web/src/app/home/mcp/components/mcp-form/MCPReadme.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import rehypeSanitize from 'rehype-sanitize';
|
||||
import rehypeHighlight from 'rehype-highlight';
|
||||
import rehypeSlug from 'rehype-slug';
|
||||
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import '@/styles/github-markdown.css';
|
||||
|
||||
/**
|
||||
* Renders the README markdown captured from LangBot Space at install time.
|
||||
* The README is stored on the MCP server record (``server.readme``) so this
|
||||
* works offline and regardless of the server's runtime/connection state.
|
||||
*
|
||||
* MCP marketplace READMEs reference images by absolute URL (the upstream repo),
|
||||
* so — unlike plugin READMEs — no asset-path rewriting is needed here.
|
||||
*/
|
||||
export default function MCPReadme({ readme }: { readme?: string }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!readme || !readme.trim()) {
|
||||
return (
|
||||
<div className="flex min-h-[220px] items-center justify-center rounded-lg border border-dashed text-sm text-muted-foreground">
|
||||
{t('mcp.noReadme')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full overflow-auto">
|
||||
<div className="markdown-body max-w-none p-1 pt-0">
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
rehypePlugins={[
|
||||
rehypeRaw,
|
||||
rehypeSanitize,
|
||||
rehypeHighlight,
|
||||
rehypeSlug,
|
||||
[
|
||||
rehypeAutolinkHeadings,
|
||||
{
|
||||
behavior: 'wrap',
|
||||
properties: {
|
||||
className: ['anchor'],
|
||||
},
|
||||
},
|
||||
],
|
||||
]}
|
||||
components={{
|
||||
ul: ({ children }) => <ul className="list-disc">{children}</ul>,
|
||||
ol: ({ children }) => <ol className="list-decimal">{children}</ol>,
|
||||
li: ({ children }) => <li className="ml-4">{children}</li>,
|
||||
a: ({ children, href, ...props }) => (
|
||||
<a href={href} target="_blank" rel="noopener noreferrer" {...props}>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
img: ({ src, alt, ...props }) => (
|
||||
<img
|
||||
src={src}
|
||||
alt={alt || ''}
|
||||
className="my-4 h-auto max-w-full rounded-lg"
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{readme}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -551,6 +551,7 @@ export type MCPServer =
|
||||
enable: boolean;
|
||||
extra_args: MCPServerExtraArgsSSE;
|
||||
runtime_info?: MCPServerRuntimeInfo;
|
||||
readme?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
@@ -561,6 +562,7 @@ export type MCPServer =
|
||||
enable: boolean;
|
||||
extra_args: MCPServerExtraArgsHttp;
|
||||
runtime_info?: MCPServerRuntimeInfo;
|
||||
readme?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
@@ -571,6 +573,7 @@ export type MCPServer =
|
||||
enable: boolean;
|
||||
extra_args: MCPServerExtraArgsStdio;
|
||||
runtime_info?: MCPServerRuntimeInfo;
|
||||
readme?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user