Skip to main content
Glama
leon4652

TypeScript MCP Server Boilerplate

by leon4652

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            # 프로젝트 문서

Related MCP server: Starter MCP Server

🚀 시작하기

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

Available Tools

4 tools
calculatorC

두 숫자에 대해 add, subtract, multiply, divide 연산을 수행합니다.

ParametersJSON Schema
NameRequiredDescriptionDefault
aYes첫 번째 숫자
bYes두 번째 숫자
operationYes수행할 연산 (add, subtract, multiply, divide)

Output Schema

ParametersJSON Schema
NameRequiredDescription
contentYes계산 결과

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden, yet it says nothing about divide-by-zero handling, error responses, numeric precision, or whether non-numeric input is rejected. For arithmetic, divide-by-zero is a notable edge case the agent would benefit from knowing about, and it is undisclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single efficient sentence with the operation and operand count front-loaded and no filler. It is appropriately sized for a simple tool, though it uses minimal space to restate information the schema already contains.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return-value explanation is unnecessary, and all parameters are documented at 100% coverage. What is missing is edge-case behavior such as division by zero and error/exception semantics, which the description does not address.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% with an enum on operation, so the schema already documents all three parameters. The description adds only the fact that two numeric operands are involved, which is already implied by the parameter descriptions, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific operation (연산 수행) on a specific resource (두 숫자) and enumerates the four supported operations, so an agent knows exactly what the tool computes. Sibling differentiation is not a concern here since greet/geocode/generate-image are unrelated domains, but the enumeration largely mirrors the schema enum rather than adding scope detail.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No when-to-use or when-not-to-use guidance is given; the description only states capability. Usage is strongly implied by the tool name and semantics, but there is no explicit routing advice or exclusion of cases (e.g., large-number or non-arithmetic inputs) leaving the agent to infer everything.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

generate-imageB

HuggingFace Inference API (FLUX.1-schnell)를 사용해 텍스트 프롬프트에서 이미지를 생성합니다. HF_TOKEN 환경변수 필요.

ParametersJSON Schema
NameRequiredDescriptionDefault
promptYes이미지 생성 프롬프트
num_inference_stepsNo추론 스텝 수 (1~10, 기본값: 4)

Output Schema

ParametersJSON Schema
NameRequiredDescription
contentYes

TDQS

B3.2/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It states that HF_TOKEN is required, which is important auth context, but does not disclose rate limits, output format, latency, or whether the operation is read-only or has side effects. For a generation tool with zero annotation coverage, this leaves significant behavioral gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the core action and implementation, followed by the auth prerequisite. No wasted words, though it is somewhat terse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained. However, for a generation tool with no annotations, the description should disclose more about side effects, output content, or constraints. It covers auth but leaves several behavioral aspects unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents both parameters (prompt and num_inference_steps) fully, including ranges and defaults. The description adds no parameter-specific detail beyond what the schema provides; baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('텍스트 프롬프트에서 이미지를 생성합니다') and names the underlying implementation (HuggingFace Inference API, FLUX.1-schnell). It clearly distinguishes the tool from siblings like greet, geocode, and calculator, though those siblings are unrelated utilities so differentiation is trivial.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It mentions a prerequisite (HF_TOKEN environment variable) which is useful context, but offers no explicit when-to-use or when-not-to-use guidance or alternatives. Usage is implied by the purpose but not elaborated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

geocodeA

도시 이름이나 주소를 입력하면 Nominatim OpenStreetMap API를 사용해 위도(latitude)와 경도(longitude) 좌표를 반환합니다.

ParametersJSON Schema
NameRequiredDescriptionDefault
addressYes검색할 도시 이름 또는 주소

Output Schema

ParametersJSON Schema
NameRequiredDescription
contentYesGeocode 결과

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It does disclose the external dependency on the Nominatim OpenStreetMap API, which hints at a network call and third-party availability, but it omits well-known operational traits such as rate limits, no-result/error behavior, or whether a single best match is returned.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single, front-loaded sentence with no filler; purpose, input, and output are each stated once and nothing is repeated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained, and the single parameter is fully documented in the schema. The only real gap is operational behavior for a network-backed tool (errors, rate limits, ambiguous matches), which keeps it just short of complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter exists, and the schema already documents it at 100% coverage ('검색할 도시 이름 또는 주소'). The description's mention of city name or address adds no syntax, format, or constraint detail beyond the schema, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (return), resource (latitude/longitude coordinates), and input (city name or address), and even names the underlying service (Nominatim OpenStreetMap API). It is trivially distinguishable from the unrelated siblings greet, generate-image, and calculator.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It says what the tool does but offers no guidance on when to use it, what address formats are acceptable (full street address vs. city name only), or how ambiguous queries are resolved. No alternatives exist among the siblings, but the lack of any input-condition guidance leaves usage entirely implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

greetA

이름과 언어를 입력하면 인사말을 반환합니다.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes인사할 사람의 이름
languageNo인사 언어 (기본값: en)en

Output Schema

ParametersJSON Schema
NameRequiredDescription
contentYes인사말

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It states the core behavior (returns a greeting) but does not disclose details like output formatting, error handling, or whether there are side effects. For a simple tool, this is acceptable but not exceptional.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, concise sentence that covers the tool's purpose without any extraneous words. It is front-loaded with the core action and efficiently communicates the essential information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple tool with a complete input schema and an output schema, the description is sufficient. It explains the function clearly, and the remaining details are covered by the structured fields, leaving no significant gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema provides complete descriptions for both parameters (name, language) with enums and defaults. The description merely restates that they are inputs and does not add additional semantic context beyond the schema, so the baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns a greeting based on a name and language, using a specific verb (반환합니다) and a clear resource (greeting). This is distinct from sibling tools like geocode or get-weather, making its purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage when a greeting is needed, but it does not explicitly state when to use this tool versus alternatives. Since sibling tools are unrelated, no exclusions are necessary, but explicit guidance is absent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv1.0.0
    • First observedcalculator
    • First observedgenerate-image
    • First observedgeocode
    • First observedgreet

TDQS

B3.4/5.0

Scored across 4 tools

Disambiguation5/5

Each of the four tools targets a completely distinct action (greeting, geocoding, image generation, arithmetic) with no functional overlap, so an agent can unambiguously pick the right one.

Naming Consistency4/5

greet, geocode, and generate-image are action verbs in a consistent lowercase/hyphen style, but calculator breaks the pattern by being a noun rather than a verb, a minor deviation.

Tool Count4/5

Four tools is a reasonable size for a boilerplate demonstrating different integration patterns; it is slightly thin but not a mismatch for a demo server.

Completeness3/5

Because the tools are unrelated samples rather than one domain, there is no lifecycle to cover; each tool is self-contained but calculator exposes only four fixed operations and nothing like state or configuration is represented.

Maintenance

ActivityInactive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    A boilerplate project for quickly developing Model Context Protocol (MCP) servers using TypeScript, with example tools (calculator, greet) and resources (server info) pre-implemented.
    52 npm
    -
  • A
    license
    B
    quality
    D
    maintenance
    A template/boilerplate project for building Model Context Protocol (MCP) servers with TypeScript. Provides a starting point with configuration examples, development tools, and debugging setup.
    2
    1
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    A boilerplate project for quickly developing Model Context Protocol (MCP) servers using TypeScript SDK, with example tools (calculator, greetings) and resources (server info) to get started.
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    A template project for quickly building Model Context Protocol (MCP) servers using TypeScript and the official SDK. It includes pre-configured examples for tools and resources to help developers jumpstart their custom MCP server development.
    52 npm
    -