import {
registerAppResource,
registerAppTool,
RESOURCE_MIME_TYPE,
} from "@modelcontextprotocol/ext-apps/server";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import path from "node:path";
import { z } from "zod";
// Works both from source (server.ts) and compiled (dist/server.js)
const DIST_DIR = import.meta.filename.endsWith(".ts")
? path.join(import.meta.dirname, "dist")
: import.meta.dirname;
const resourceUri = "ui://markdown-preview/mcp-app.html";
/**
* Creates a new MCP server instance with tools and resources registered.
*/
export function createServer(): McpServer {
const server = new McpServer({
name: "Markdown Preview Server",
version: "1.0.0",
});
registerAppTool(
server,
"preview-markdown",
{
title: "Preview Markdown",
description: "Renders markdown content with GitHub Flavored Markdown styling.",
inputSchema: {
markdown: z.string().describe("The markdown content to preview"),
},
_meta: { ui: { resourceUri } },
},
async ({ markdown }): Promise<CallToolResult> => {
return {
content: [{ type: "text", text: markdown }],
};
}
);
registerAppResource(
server,
resourceUri,
resourceUri,
{ mimeType: RESOURCE_MIME_TYPE },
async (): Promise<ReadResourceResult> => {
const html = await fs.readFile(path.join(DIST_DIR, "mcp-app.html"), "utf-8");
return {
contents: [{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }],
};
}
);
return server;
}