Saltar al contenido
MCP ThesaurusMCP Thesaurus

devbrother2024 typescript mcp server boilerplate

ComunidadIncomplete39/100Reclamar

streamable-httpupdated 7mo ago

TypeScript MCP SDK๋ฅผ ํ™œ์šฉํ•˜์—ฌ Model Context Protocol (MCP) ์„œ๋ฒ„๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋Š” ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค.

CรณdigoWebDocs2

ยฟQuรฉ puedes hacer con devbrother2024 typescript mcp server boilerplate?

TypeScript MCP Server ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ

TypeScript MCP SDK๋ฅผ ํ™œ์šฉํ•˜์—ฌ Model Context Protocol (MCP) ์„œ๋ฒ„๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋Š” ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค.

๐Ÿ“ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

typescript-mcp-server-boilerplate/
โ”œโ”€โ”€ src/
โ”‚   โ””โ”€โ”€ index.ts          # MCP ์„œ๋ฒ„ ๋ฉ”์ธ ์ง„์ž…์ 
โ”œโ”€โ”€ build/                # ์ปดํŒŒ์ผ๋œ JavaScript ํŒŒ์ผ (๋นŒ๋“œ ํ›„ ์ƒ์„ฑ)
โ”œโ”€โ”€ package.json          # ํ”„๋กœ์ ํŠธ ์˜์กด์„ฑ ๋ฐ ์Šคํฌ๋ฆฝํŠธ
โ”œโ”€โ”€ tsconfig.json         # TypeScript ์„ค์ •
โ””โ”€โ”€ README.md            # ํ”„๋กœ์ ํŠธ ๋ฌธ์„œ

๐Ÿš€ ์‹œ์ž‘ํ•˜๊ธฐ

1. ์˜์กด์„ฑ ์„ค์น˜

npm install

2. ์„œ๋ฒ„ ์ด๋ฆ„ ์„ค์ •

src/index.ts ํŒŒ์ผ์—์„œ ์„œ๋ฒ„ ์ด๋ฆ„์„ ์ˆ˜์ •ํ•˜์„ธ์š”:

const server = new McpServer({
    name: 'typescript-mcp-server', // ์—ฌ๊ธฐ๋ฅผ ์›ํ•˜๋Š” ์„œ๋ฒ„ ์ด๋ฆ„์œผ๋กœ ๋ณ€๊ฒฝ
    version: '1.0.0',
    // ํ™œ์„ฑํ™” ํ•˜๊ณ ์ž ํ•˜๋Š” ๊ธฐ๋Šฅ ์„ค์ •
    capabilities: {
        tools: {},
        resources: {}
    }
})

๐Ÿ’ก ํŒ: ํ˜„์žฌ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ์—๋Š” ์ด๋ฏธ ๊ณ„์‚ฐ๊ธฐ์™€ ์ธ์‚ฌ ๋„๊ตฌ, ๊ทธ๋ฆฌ๊ณ  ์„œ๋ฒ„ ์ •๋ณด ๋ฆฌ์†Œ์Šค๊ฐ€ ์˜ˆ์‹œ๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

3. ๋นŒ๋“œ

npm run build

4. ์‹คํ–‰

node build/index.js

๋นŒ๋“œ๊ฐ€ ์„ฑ๊ณตํ•˜๋ฉด build/ ๋””๋ ‰ํ† ๋ฆฌ์— ์ปดํŒŒ์ผ๋œ JavaScript ํŒŒ์ผ์ด ์ƒ์„ฑ๋˜๊ณ , ์„œ๋ฒ„๊ฐ€ MCP ํด๋ผ์ด์–ธํŠธ์˜ ์—ฐ๊ฒฐ์„ ๋Œ€๊ธฐํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ› ๏ธ ๊ฐœ๋ฐœ ๊ฐ€์ด๋“œ

MCP ๋„๊ตฌ(Tool) ์ถ”๊ฐ€ํ•˜๊ธฐ

MCP ์„œ๋ฒ„์— ์ƒˆ๋กœ์šด ๋„๊ตฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด server.tool() ๋ฉ”์„œ๋“œ์— Zod ์Šคํ‚ค๋งˆ๋ฅผ ์ง์ ‘ ์ •์˜ํ•˜์—ฌ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค:

import { z } from 'zod'

// ๊ณ„์‚ฐ๊ธฐ ๋„๊ตฌ ์ถ”๊ฐ€
server.tool(
    'calculator',
    {
        operation: z
            .enum(['add', 'subtract', 'multiply', 'divide'])
            .describe('์ˆ˜ํ–‰ํ•  ์—ฐ์‚ฐ (add, subtract, multiply, divide)'),
        a: z.number().describe('์ฒซ ๋ฒˆ์งธ ์ˆซ์ž'),
        b: z.number().describe('๋‘ ๋ฒˆ์งธ ์ˆซ์ž')
    },
    async ({ operation, a, b }) => {
        // ์—ฐ์‚ฐ ์ˆ˜ํ–‰
        let result: number
        switch (operation) {
            case 'add':
                result = a + b
                break
            case 'subtract':
                result = a - b
                break
            case 'multiply':
                result = a * b
                break
            case 'divide':
                if (b === 0) throw new Error('0์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค')
                result = a / b
                break
            default:
                throw new Error('์ง€์›ํ•˜์ง€ ์•Š๋Š” ์—ฐ์‚ฐ์ž…๋‹ˆ๋‹ค')
        }

        const operationSymbols = {
            add: '+',
            subtract: '-',
            multiply: 'ร—',
            divide: 'รท'
        } as const

        const operationSymbol =
            operationSymbols[operation as keyof typeof operationSymbols]

        return {
            content: [
                {
                    type: 'text',
                    text: `${a} ${operationSymbol} ${b} = ${result}`
                }
            ]
        }
    }
)
๋” ๋ณต์žกํ•œ ๋„๊ตฌ ์˜ˆ์‹œ
// ๋‚ ์”จ ์ •๋ณด ์กฐํšŒ ๋„๊ตฌ
server.tool(
    'get_weather',
    {
        city: z.string().describe('๋‚ ์”จ๋ฅผ ์กฐํšŒํ•  ๋„์‹œ๋ช…'),
        unit: z
            .enum(['celsius', 'fahrenheit'])
            .optional()
            .default('celsius')
            .describe('์˜จ๋„ ๋‹จ์œ„ (๊ธฐ๋ณธ๊ฐ’: celsius)')
    },
    async ({ city, unit }) => {
        try {
            // ์‹ค์ œ ๋‚ ์”จ API ํ˜ธ์ถœ ๋กœ์ง (์˜ˆ์‹œ)
            const weatherData = await fetchWeatherData(city, unit)

            return {
                content: [
                    {
                        type: 'text',
                        text: `${city}์˜ ํ˜„์žฌ ๋‚ ์”จ:
์˜จ๋„: ${weatherData.temperature}ยฐ${unit === 'celsius' ? 'C' : 'F'}
๋‚ ์”จ: ${weatherData.condition}
์Šต๋„: ${weatherData.humidity}%
ํ’์†: ${weatherData.windSpeed}km/h`
                    }
                ]
            }
        } catch (error) {
            throw new Error(
                `๋‚ ์”จ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: ${(error as Error).message}`
            )
        }
    }
)

// ๋„์šฐ๋ฏธ ํ•จ์ˆ˜
async function fetchWeatherData(city: string, unit: string) {
    // ์‹ค์ œ ๋‚ ์”จ API ํ˜ธ์ถœ ๊ตฌํ˜„
    // ์—ฌ๊ธฐ์„œ๋Š” ์˜ˆ์‹œ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜
    return {
        temperature: unit === 'celsius' ? 22 : 72,
        condition: '๋ง‘์Œ',
        humidity: 65,
        windSpeed: 12
    }
}

๋ฆฌ์†Œ์Šค ์ถ”๊ฐ€ํ•˜๊ธฐ

MCP ์„œ๋ฒ„์— ๋ฆฌ์†Œ์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๋‚˜ ํŒŒ์ผ์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

// ๋ฆฌ์†Œ์Šค ๋“ฑ๋ก
server.resource(
    'example-file',
    'file://example.txt',
    {
        name: '์˜ˆ์‹œ ํ…์ŠคํŠธ ํŒŒ์ผ',
        description: '์˜ˆ์‹œ ํ…์ŠคํŠธ ํŒŒ์ผ ์„ค๋ช…',
        mimeType: 'text/plain'
    },
    async () => {
        return {
            contents: [
                {
                    uri: 'file://example.txt',
                    mimeType: 'text/plain',
                    text: '์˜ˆ์‹œ ํŒŒ์ผ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.'
                }
            ]
        }
    }
)

// ๋™์  ๋ฆฌ์†Œ์Šค ์˜ˆ์‹œ
server.resource(
    'app-settings',
    'config://settings',
    {
        name: '์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์ •',
        description: '์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ˜„์žฌ ์„ค์ • ์ •๋ณด',
        mimeType: 'application/json'
    },
    async () => {
        const settings = {
            theme: 'dark',
            language: 'ko-KR',
            notifications: true,
            lastUpdated: new Date().toISOString()
        }

        return {
            contents: [
                {
                    uri: 'config://settings',
                    mimeType: 'application/json',
                    text: JSON.stringify(settings, null, 2)
                }
            ]
        }
    }
)

๐Ÿ“ฆ ์ฃผ์š” ์˜์กด์„ฑ

  • @modelcontextprotocol/sdk: MCP ํ”„๋กœํ† ์ฝœ ๊ตฌํ˜„์„ ์œ„ํ•œ ๊ณต์‹ SDK
  • zod: TypeScript ์šฐ์„  ์Šคํ‚ค๋งˆ ๊ฒ€์ฆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
  • typescript: TypeScript ์ปดํŒŒ์ผ๋Ÿฌ

๐Ÿ”ง ์Šคํฌ๋ฆฝํŠธ

  • npm run build: TypeScript๋ฅผ JavaScript๋กœ ์ปดํŒŒ์ผํ•˜๊ณ  ์‹คํ–‰ ๊ถŒํ•œ ์„ค์ •

๐Ÿ“‹ ์‚ฌ์šฉ ์˜ˆ์‹œ

์™„์ „ํ•œ ์„œ๋ฒ„ ์˜ˆ์‹œ

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { z } from 'zod'

// ์„œ๋ฒ„ ์ƒ์„ฑ
const server = new McpServer({
    name: 'my-mcp-server',
    version: '1.0.0',
    capabilities: {
        tools: {},
        resources: {}
    }
})

// ๊ฐ„๋‹จํ•œ ์ธ์‚ฌ ๋„๊ตฌ
server.tool(
    'greet',
    {
        name: z.string().describe('์ธ์‚ฌํ•  ์‚ฌ๋žŒ์˜ ์ด๋ฆ„'),
        language: z
            .enum(['ko', 'en'])
            .optional()
            .default('ko')
            .describe('์ธ์‚ฌ ์–ธ์–ด (๊ธฐ๋ณธ๊ฐ’: ko)')
    },
    async ({ name, language }) => {
        const greeting =
            language === 'ko' ? `์•ˆ๋…•ํ•˜์„ธ์š”, ${name}๋‹˜!` : `Hello, ${name}!`

        return {
            content: [
                {
                    type: 'text',
                    text: greeting
                }
            ]
        }
    }
)

// ์‹œ์Šคํ…œ ์ •๋ณด ๋ฆฌ์†Œ์Šค
server.resource(
    'system-info',
    'system://info',
    {
        name: '์‹œ์Šคํ…œ ์ •๋ณด',
        description: '์„œ๋ฒ„์˜ ํ˜„์žฌ ์ƒํƒœ ๋ฐ ์‹œ์Šคํ…œ ์ •๋ณด',
        mimeType: 'application/json'
    },
    async () => {
        const systemInfo = {
            server: 'my-mcp-server',
            version: '1.0.0',
            timestamp: new Date().toISOString(),
            uptime: process.uptime()
        }

        return {
            contents: [
                {
                    uri: 'system://info',
                    mimeType: 'application/json',
                    text: JSON.stringify(systemInfo, null, 2)
                }
            ]
        }
    }
)

// ์„œ๋ฒ„ ์‹œ์ž‘
async function main() {
    const transport = new StdioServerTransport()
    await server.connect(transport)
    console.error('MCP ์„œ๋ฒ„๊ฐ€ ์‹œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค')
}

main().catch(console.error)

๐Ÿ”ง Cursor MCP ์—ฐ๊ฒฐ

๊ฐœ๋ฐœํ•œ MCP ์„œ๋ฒ„๋ฅผ Cursor์—์„œ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

์„ค์ • ํŒŒ์ผ ์ˆ˜์ •

./.cursor/mcp.json ํŒŒ์ผ์„ ํŽธ์ง‘ํ•ฉ๋‹ˆ๋‹ค:

{
    "mcpServers": {
        "typescript-mcp-server": {
            "command": "node",
            "args": ["/ABSOLUTE/PATH/TO/YOUR/PROJECT/build/index.js"]
        }
    }
}

์ฃผ์˜: ์ ˆ๋Œ€ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. pwd ๋ช…๋ น์–ด๋กœ ํ˜„์žฌ ๊ฒฝ๋กœ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

ํ…Œ์ŠคํŠธ ๋ช…๋ น์–ด

Cursor MCP์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ…Œ์ŠคํŠธํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • "5 ๋”ํ•˜๊ธฐ 3์€ ์–ผ๋งˆ์•ผ?" (๊ณ„์‚ฐ๊ธฐ ๋„๊ตฌ ํ…Œ์ŠคํŠธ)
  • "์•ˆ๋…•ํ•˜์„ธ์š” ๋ผ๊ณ  ์ธ์‚ฌํ•ด์ค˜" (์ธ์‚ฌ ๋„๊ตฌ ํ…Œ์ŠคํŠธ)
  • ์„œ๋ฒ„ ์ •๋ณด ๋ฆฌ์†Œ์Šค ์กฐํšŒ

๐Ÿ”— ์ฐธ๊ณ  ์ž๋ฃŒ

๐Ÿ“„ ๋ผ์ด์„ ์Šค

MIT