UNPKG

@minjunkwon/public-housing-mcp-server

Version:

MCP server for public housing information in South Korea - 한국 공공주택 정보 조회 MCP 서버

237 lines 11.5 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { PublicHousingAPI } from './services/api.js'; import { REGION_CODES, DISTRICT_CODES, HOUSING_TYPES, SUPPLY_TYPES, RENT_RANGES } from './constants/codes.js'; const server = new Server({ name: 'public-housing-server', version: '1.0.0', }); const housingAPI = new PublicHousingAPI(); // 도구 목록 제공 server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'get_rental_housing', description: '공공임대주택 모집공고를 조회합니다. 사용자가 지역, 공급유형, 임대료 등을 자연어로 요청하면 해당하는 코드로 변환하여 조회합니다.', inputSchema: { type: 'object', properties: { region: { type: 'string', description: '광역시도명 (예: 서울특별시, 경기도, 부산광역시 등). 사용자가 "서울", "경기", "부산" 등으로 말해도 인식 가능', enum: REGION_CODES.map(r => r.name) }, district: { type: 'string', description: '시군구명 (예: 강남구, 수원시, 성남시 등). 선택사항이며 지역이 지정된 경우에만 사용', }, supplyType: { type: 'string', description: '공급유형 (예: 국민임대, 행복주택, 장기전세 등). 사용자가 "국민임대", "행복주택" 등으로 요청', enum: SUPPLY_TYPES.map(s => s.name) }, isLongTermLease: { type: 'boolean', description: '전세형 여부. 사용자가 "전세", "전세형" 등을 언급하면 true로 설정' }, rentRange: { type: 'string', description: '월임대료 구간 (예: 5만원 미만, 10~20만원 미만 등). 사용자가 임대료 범위를 언급할 때 사용', enum: RENT_RANGES.map(r => r.name) }, pageSize: { type: 'number', description: '한 페이지당 조회할 데이터 개수 (기본값: 10, 최대: 100)', default: 10, minimum: 1, maximum: 100 }, pageNumber: { type: 'number', description: '조회할 페이지 번호 (기본값: 1)', default: 1, minimum: 1 } }, required: ['region'] } }, { name: 'get_sale_housing', description: '공공분양주택 모집공고를 조회합니다. 사용자가 지역, 주택유형 등을 자연어로 요청하면 해당하는 코드로 변환하여 조회합니다.', inputSchema: { type: 'object', properties: { region: { type: 'string', description: '광역시도명 (예: 서울특별시, 경기도, 부산광역시 등). 사용자가 "서울", "경기", "부산" 등으로 말해도 인식 가능', enum: REGION_CODES.map(r => r.name) }, district: { type: 'string', description: '시군구명 (예: 강남구, 수원시, 성남시 등). 선택사항이며 지역이 지정된 경우에만 사용', }, housingType: { type: 'string', description: '주택유형 (예: 연립주택, 다세대주택, 단독주택, 오피스텔, 다가구주택)', enum: HOUSING_TYPES.map(h => h.name) }, pageSize: { type: 'number', description: '한 페이지당 조회할 데이터 개수 (기본값: 10, 최대: 100)', default: 10, minimum: 1, maximum: 100 }, pageNumber: { type: 'number', description: '조회할 페이지 번호 (기본값: 1)', default: 1, minimum: 1 } }, required: ['region'] } } ] }; }); // 도구 실행 핸들러 server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; if (!args) { throw new Error('Arguments are required'); } try { switch (name) { case 'get_rental_housing': { // 지역명을 코드로 변환 const region = args.region; const regionCode = REGION_CODES.find(r => r.name === region || r.name.includes(region) || region.includes(r.name.replace(/특별시|광역시|특별자치시|특별자치도|도$/g, '')))?.code; if (!regionCode) { return { content: [ { type: 'text', text: `지원하지 않는 지역입니다. 사용 가능한 지역: ${REGION_CODES.map(r => r.name).join(', ')}` } ] }; } // 시군구 코드 변환 (있는 경우) let districtCode; if (args.district) { const district = DISTRICT_CODES.find(d => d.parentCode === regionCode && (d.name === args.district || d.name.includes(args.district))); districtCode = district?.code; } // 공급유형 코드 변환 (있는 경우) let supplyTypeCode; if (args.supplyType) { const supplyType = SUPPLY_TYPES.find(s => s.name === args.supplyType); supplyTypeCode = supplyType?.code; } // 임대료 범위 코드 변환 (있는 경우) let rentRangeCode; if (args.rentRange) { const rentRange = RENT_RANGES.find(r => r.name === args.rentRange); rentRangeCode = rentRange?.code; } const params = { brtcCode: regionCode, ...(districtCode && { signguCode: districtCode }), ...(supplyTypeCode && { suplyTy: supplyTypeCode }), ...(args.isLongTermLease !== undefined && { lfstsTyAt: args.isLongTermLease ? 'Y' : 'N' }), ...(rentRangeCode && { bassMtRntchrgSe: rentRangeCode }), numOfRows: args.pageSize || 10, pageNo: args.pageNumber || 1 }; const result = await housingAPI.getRentalHousingAnnouncements(params); const formattedData = housingAPI.formatRentalHousingData(result.body.items); return { content: [ { type: 'text', text: `공공임대주택 모집공고 조회 결과 (총 ${result.body.totalCount}건)\n\n${formattedData}` } ] }; } case 'get_sale_housing': { // 지역명을 코드로 변환 const region = args.region; const regionCode = REGION_CODES.find(r => r.name === region || r.name.includes(region) || region.includes(r.name.replace(/특별시|광역시|특별자치시|특별자치도|도$/g, '')))?.code; if (!regionCode) { return { content: [ { type: 'text', text: `지원하지 않는 지역입니다. 사용 가능한 지역: ${REGION_CODES.map(r => r.name).join(', ')}` } ] }; } // 시군구 코드 변환 (있는 경우) let districtCode; if (args.district) { const district = DISTRICT_CODES.find(d => d.parentCode === regionCode && (d.name === args.district || d.name.includes(args.district))); districtCode = district?.code; } // 주택유형 코드 변환 (있는 경우) let housingTypeCode; if (args.housingType) { const housingType = HOUSING_TYPES.find(h => h.name === args.housingType); housingTypeCode = housingType?.code; } const params = { brtcCode: regionCode, ...(districtCode && { signguCode: districtCode }), ...(housingTypeCode && { houseTy: housingTypeCode }), numOfRows: args.pageSize || 10, pageNo: args.pageNumber || 1 }; const result = await housingAPI.getSaleHousingAnnouncements(params); const formattedData = housingAPI.formatSaleHousingData(result.body.items); return { content: [ { type: 'text', text: `공공분양주택 모집공고 조회 결과 (총 ${result.body.totalCount}건)\n\n${formattedData}` } ] }; } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [ { type: 'text', text: `오류가 발생했습니다: ${error instanceof Error ? error.message : '알 수 없는 오류'}` } ], isError: true }; } }); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('Public Housing MCP Server running on stdio'); } main().catch((error) => { console.error('Server error:', error); process.exit(1); }); //# sourceMappingURL=index.js.map