koreandict-mcp-server
Version:
국립국어원 표준국어대사전 MCP 서버
225 lines (224 loc) • 10.1 kB
JavaScript
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { DictionaryService } from './services/dictionary-api.js';
/**
* 국립국어원 표준국어대사전 MCP 서버
*/
export class KoreanDictionaryMcpServer {
constructor() {
// MCP 서버 인스턴스 생성
this.server = new McpServer({
name: '국립국어원 표준국어대사전',
version: '1.0.0'
});
// 사전 서비스 인스턴스 생성
this.dictionaryService = new DictionaryService();
// 리소스와 도구 초기화
this.initializeResources();
this.initializeTools();
}
/**
* 서버 인스턴스를 반환합니다.
*/
getServer() {
return this.server;
}
/**
* MCP 리소스를 초기화합니다.
*/
initializeResources() {
// 단어 검색 리소스
this.server.resource('word-search', new ResourceTemplate('koreandict://word/{word}', { list: undefined }), async (uri, { word }) => {
try {
const searchWord = typeof word === 'string' ? word : word[0];
const result = await this.dictionaryService.searchDictionary(searchWord);
let textContent = `"${searchWord}" 검색 결과\n\n`;
if (!result.channel?.item || result.channel.item.length === 0) {
textContent += '검색 결과가 없습니다.';
}
else {
result.channel.item.forEach((item, index) => {
textContent += `${index + 1}. ${item.word}`;
if (item.sup_no)
textContent += ` (${item.sup_no})`;
if (item.pos)
textContent += ` [${item.pos}]`;
textContent += '\n';
const senses = Array.isArray(item.sense) ? item.sense : [item.sense];
senses.forEach((sense, idx) => {
textContent += ` ${idx + 1}) ${sense.definition}\n`;
if (sense.type)
textContent += ` (${sense.type})\n`;
});
textContent += '\n';
});
}
return {
contents: [{
uri: uri.href,
text: textContent
}]
};
}
catch (error) {
return {
contents: [{
uri: uri.href,
text: `오류: ${error instanceof Error ? error.message : '알 수 없는 오류'}`
}]
};
}
});
// 품사별 검색 리소스
this.server.resource('pos-search', new ResourceTemplate('koreandict://pos/{pos}/word/{word}', { list: undefined }), async (uri, { pos, word }) => {
try {
// 품사 코드 매핑
const posMap = {
'명사': 1, '대명사': 2, '수사': 3, '조사': 4, '동사': 5, '형용사': 6,
'관형사': 7, '부사': 8, '감탄사': 9, '접사': 10, '의존명사': 11,
'보조동사': 12, '보조형용사': 13, '어미': 14, '품사없음': 15
};
const posValue = typeof pos === 'string' ? pos : pos[0];
const searchWord = typeof word === 'string' ? word : word[0];
const posCode = posMap[posValue] || 0;
const result = await this.dictionaryService.searchDictionary(searchWord, {
advanced: 'y',
pos: posCode.toString(),
method: 'include'
});
let textContent = `"${searchWord}" 품사: ${posValue} 검색 결과\n\n`;
if (!result.channel?.item || result.channel.item.length === 0) {
textContent += '검색 결과가 없습니다.';
}
else {
result.channel.item.forEach((item, index) => {
textContent += `${index + 1}. ${item.word}`;
if (item.sup_no)
textContent += ` (${item.sup_no})`;
if (item.pos)
textContent += ` [${item.pos}]`;
textContent += '\n';
const senses = Array.isArray(item.sense) ? item.sense : [item.sense];
senses.forEach((sense, idx) => {
textContent += ` ${idx + 1}) ${sense.definition}\n`;
});
textContent += '\n';
});
}
return {
contents: [{
uri: uri.href,
text: textContent
}]
};
}
catch (error) {
return {
contents: [{
uri: uri.href,
text: `오류: ${error instanceof Error ? error.message : '알 수 없는 오류'}`
}]
};
}
});
}
/**
* MCP 도구를 초기화합니다.
*/
initializeTools() {
// 단어 검색 도구
this.server.tool('search-word', {
word: z.string().describe('검색할 단어'),
method: z.enum(['exact', 'include', 'start', 'end', 'wildcard']).optional().describe('검색 방식'),
limit: z.number().min(1).max(100).optional().describe('결과 개수 제한')
}, async ({ word, method = 'exact', limit = 10 }) => {
try {
const result = await this.dictionaryService.searchDictionary(word, {
method,
num: limit
});
if (!result.channel?.item || result.channel.item.length === 0) {
return {
content: [{ type: 'text', text: '검색 결과가 없습니다.' }]
};
}
let response = `"${word}" 검색 결과:\n\n`;
result.channel.item.forEach((item, index) => {
response += `${index + 1}. ${item.word}`;
if (item.sup_no)
response += ` (${item.sup_no})`;
if (item.pos)
response += ` [${item.pos}]`;
response += '\n';
const senses = Array.isArray(item.sense) ? item.sense : [item.sense];
senses.forEach((sense, idx) => {
response += ` ${idx + 1}) ${sense.definition}\n`;
});
response += '\n';
});
return {
content: [{ type: 'text', text: response }]
};
}
catch (error) {
return {
content: [{ type: 'text', text: `오류: ${error instanceof Error ? error.message : '알 수 없는 오류'}` }],
isError: true
};
}
});
// 단어 품사별 검색 도구
this.server.tool('search-by-pos', {
word: z.string().describe('검색할 단어'),
pos: z.enum([
'명사', '대명사', '수사', '조사', '동사', '형용사', '관형사',
'부사', '감탄사', '접사', '의존명사', '보조동사', '보조형용사', '어미', '품사없음'
]).describe('검색할 품사'),
limit: z.number().min(1).max(100).optional().describe('결과 개수 제한')
}, async ({ word, pos, limit = 10 }) => {
try {
// 품사 코드 매핑
const posMap = {
'명사': 1, '대명사': 2, '수사': 3, '조사': 4, '동사': 5, '형용사': 6,
'관형사': 7, '부사': 8, '감탄사': 9, '접사': 10, '의존명사': 11,
'보조동사': 12, '보조형용사': 13, '어미': 14, '품사없음': 15
};
const posCode = posMap[pos];
const result = await this.dictionaryService.searchDictionary(word, {
advanced: 'y',
pos: posCode.toString(),
method: 'include',
num: limit
});
if (!result.channel?.item || result.channel.item.length === 0) {
return {
content: [{ type: 'text', text: '검색 결과가 없습니다.' }]
};
}
let response = `"${word}" 품사: ${pos} 검색 결과:\n\n`;
result.channel.item.forEach((item, index) => {
response += `${index + 1}. ${item.word}`;
if (item.sup_no)
response += ` (${item.sup_no})`;
if (item.pos)
response += ` [${item.pos}]`;
response += '\n';
const senses = Array.isArray(item.sense) ? item.sense : [item.sense];
senses.forEach((sense, idx) => {
response += ` ${idx + 1}) ${sense.definition}\n`;
});
response += '\n';
});
return {
content: [{ type: 'text', text: response }]
};
}
catch (error) {
return {
content: [{ type: 'text', text: `오류: ${error instanceof Error ? error.message : '알 수 없는 오류'}` }],
isError: true
};
}
});
}
}