Skip to main content
Glama
index.ts8.34 kB
#!/usr/bin/env node /** * Cowsay MCP Server * * A TypeScript MCP server demonstrating migration from STDIO to HTTP transport. * Shows how to host a streamable HTTP server on Smithery using @smithery/cli with backwards compatibility. * * See the full guide: https://smithery.ai/docs/migrations/typescript-with-smithery-cli */ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import cowsay from 'cowsay'; import { COWSAY, COWTHINK, LIST_COWS, GET_VERSION } from './tools.js'; import { exec } from 'child_process'; import { promisify } from 'util'; const execAsync = promisify(exec); // Server version export const version = '2.0.0'; // Optional: Define session configuration for server // Learn more: https://smithery.ai/docs/build/session-config export const configSchema = z.object({ serverToken: z.string().optional().describe("Server access token"), caseSensitive: z.boolean().optional().default(false).describe("Whether character matching should be case sensitive"), }); // Validate server token const validateServerAccess = function (serverToken?: string): boolean { // Validate server token - accepts any string including empty ones for demo // In a real app, you'd validate against your server's auth system return serverToken !== undefined && serverToken.trim().length > 0 ? true : true; } // get server version export function getVersion(): string { return version; } export default function createServer({ config, }: { config: z.infer<typeof configSchema>; }) { // Create MCP server instance const mcp_server = new McpServer({ name: "cowsay-mcp", version: version, description: 'A MCP server that provides cowsay functionality' }); // Register tools mcp_server.registerTool("cowsay", { title: COWSAY.title, description: COWSAY.description, inputSchema: { message: z.string().describe('The message for the cow to say.'), character: z.string().optional().default('default').describe('The cow character to use.') }, }, async ({ message, character }) => { // Validate server access if (!validateServerAccess(config.serverToken)) { throw new Error("Server access validation failed. Please provide a valid serverToken."); } // Apply user preferences from config const searchText = config.caseSensitive ? message : message.toLowerCase(); const searchChar = config.caseSensitive ? character : character.toLowerCase(); // Count occurrences of the specific character const result = await generateCowsay(searchText, searchChar); return { content: [ { type: "text", text: result } ], }; }) mcp_server.registerTool("cowthink", { title: COWTHINK.title, description: COWTHINK.description, inputSchema: { message: z.string().describe('The message for the cow to think.'), character: z.string().optional().default('default').describe('The cow character to use.') }, }, async ({ message, character }) => { // Validate server access if (!validateServerAccess(config.serverToken)) { throw new Error("Server access validation failed. Please provide a valid serverToken."); } // Apply user preferences from config const searchText = config.caseSensitive ? message : message.toLowerCase(); const searchChar = config.caseSensitive ? character : character.toLowerCase(); // Count occurrences of the specific character const result = await generateCowthink(searchText, searchChar); return { content: [ { type: "text", text: result } ], }; }) mcp_server.registerTool("list_cows", { title: LIST_COWS.title, description: LIST_COWS.description, inputSchema: {}, }, async () => { // Validate server access if (!validateServerAccess(config.serverToken)) { throw new Error("Server access validation failed. Please provide a valid serverToken."); } // Apply user preferences from config const result = await listCows(); return { content: [ { type: "text", text: `Available cow characters: ${result.join(', ')}` } ], }; }) mcp_server.registerTool("get_version", { title: GET_VERSION.title, description: GET_VERSION.description, inputSchema: {}, }, async () => { if (!validateServerAccess(config.serverToken)) { throw new Error("Server access validation failed. Please provide a valid serverToken."); } const serverVersion = getVersion(); return { content: [ { type: "text", text: `cowsay-mcp version: ${serverVersion}` } ], }; }) return mcp_server.server; } export async function generateCowsay(message: string, cow: string): Promise<string> { try { const cowOption = cow !== 'default' ? `-f ${cow}` : ''; const escapedMessage = message.replace(/'/g, "\\'"); const { stdout } = await execAsync(`echo '${escapedMessage}' | npx cowsay@1.6.0 ${cowOption}`); return stdout; } catch (error) { try { const options = cow !== 'default' ? { f: cow } : {}; return cowsay.say({ text: message, ...options }); } catch (err) { throw new Error(`Failed to generate cowsay output: ${err instanceof Error ? err.message : String(err)}`); } } } export async function generateCowthink(message: string, cow: string): Promise<string> { try { const cowOption = cow !== 'default' ? `-f ${cow}` : ''; const escapedMessage = message.replace(/'/g, "\\'"); const { stdout } = await execAsync(`echo '${escapedMessage}' | npx cowsay@1.6.0 -T '\\' ${cowOption}`); return stdout; } catch (error) { try { const options = cow !== 'default' ? { f: cow } : {}; return cowsay.think({ text: message, ...options }); } catch (err) { throw new Error(`Failed to generate cowthink output: ${err instanceof Error ? err.message : String(err)}`); } } } export async function listCows(): Promise<string[]> { try { const { stdout } = await execAsync(`npx cowsay@1.6.0 -l`); return stdout.split('\n').filter(Boolean); } catch (error) { try { const cowsList = await new Promise<string[]>((resolve, reject) => { cowsay.list((error, cow_names) => { if (error) { reject(error); } else { resolve(cow_names || []); } }); }); return cowsList; } catch (error) { return ['default', 'small', 'tux', 'moose', 'sheep', 'dragon', 'elephant', 'skeleton', 'stimpy']; } } } // Optional: if you need backward compatibility, add stdio transport // You can publish this to npm for users to run this locally async function main() { // Get configuration from environment variables const serverToken = process.env.SERVER_TOKEN; const caseSensitive = process.env.CASE_SENSITIVE === 'true'; // Create server with configuration const server = createServer({ config: { serverToken, caseSensitive, }, }); // print server url and version // console.log(`Server Version: ${ version }`); // Start receiving messages on stdin and sending messages on stdout const transport = new StdioServerTransport(); await server.connect(transport); } // By default, the server starts with stdio transport main().catch((error) => { console.error("Server error:", error); process.exit(1); });

Implementation Reference

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/mrseanchow/cowsay-mcp'

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