search_designated_entities
Search for South Korean companies designated for military service alternatives like industrial technicians, research specialists, or naval reservists. Filter by service type, industry, location, and company size to find eligible employers.
Instructions
병무청 병역일터에서 병역특례 지정업체를 검색합니다. 산업기능요원, 전문연구요원, 승선근무예비역 복무가 가능한 업체 정보를 조회할 수 있습니다. 검색 결과는 CSV 형식으로 반환되며, 최대 30개의 업체 정보가 제공됩니다.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| service_type | Yes | (필수) 복무 형태를 선택합니다. | |
| company_size | No | 기업 규모 (선택사항, 빈 값은 전체 조회) | |
| industry_sectors | No | 업종 코드 (선택사항, 여러 개 가능) | |
| company_name | No | 회사 이름 (선택사항, 빈 값은 전체 조회) | |
| city_province | No | 시/도 선택 (선택사항, 빈 값은 전국 전체 조회) | |
| city_district | No | 시/군/구 주소 (선택사항, 빈 값은 city_province 전체 조회) | |
| is_hiring | No | 병무청 채용 공고 등록 업체 여부 (선택사항, 'Y' 또는 빈 값) | |
| military_service_status | No | 현역 또는 보충역 TO 유무 (전체 조회시 H, B 모두 입력) |
Implementation Reference
- src/mma-api.ts:105-162 (handler)Core implementation of the `search_designated_entities` tool: builds form data, sends POST request to MMA API, parses XLS response to CSV using XLSX library, limits to 30 rows, filters empty rows, returns CSV string.
export async function search_designated_entities( query: MilitaryWorkplaceSearchQuery, ): Promise<string> { // Form data 생성 const formData = buildFormData(query); // HTTP 요청 const response = await fetch(MMA_API_ENDPOINT, { method: 'POST', headers: { accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'accept-language': 'ko,en-US;q=0.9,en;q=0.8', 'cache-control': 'no-cache', 'content-type': 'application/x-www-form-urlencoded', Referer: 'https://work.mma.go.kr/caisBYIS/search/byjjecgeomsaek.do', }, body: formData, }) .then((response) => { return response; }) .catch((error) => { console.error(`error: ${error}`); throw new MMAApiError(`MMA API request failed: ${error.message}`, error.status); }); // 응답을 ArrayBuffer로 받아서 처리 const arrayBuffer = await response.arrayBuffer(); const workbook = XLSX.read(arrayBuffer, { type: 'array' }); // 첫 번째 시트 가져오기 const firstSheetName = workbook.SheetNames[0]; if (!firstSheetName) { throw new MMAApiError('No worksheet found in the response'); } const worksheet = workbook.Sheets[firstSheetName]; // .xls -> .csv 형태로 변환 const strData = XLSX.utils.sheet_to_csv(worksheet, { FS: ',', RS: '\n' }); // \n으로 split하여 Array를 얻고, Header (row 0)를 제외한 나머지 부분을 slice // 최대 30개의 row만 리턴 (헤더 제외) const rows = strData.split('\n'); const dataRows = rows.slice(1, 31); // 헤더 제외, 최대 30개 // 빈 행 필터링 const filteredRows = dataRows.filter((row) => row.trim() !== ''); if (filteredRows.length === 0) { return 'No data found for the given search criteria.'; } // 헤더와 데이터를 함께 반환 const header = rows[0]; return `${header}\n${filteredRows.join('\n')}`; } - src/index.ts:28-177 (schema)JSON schema defining the input parameters for the tool, including enums for service types, company sizes, industries, locations, and supports arrays for multi-select fields like industries and military statuses.
inputSchema: { type: 'object', properties: { service_type: { type: 'string', enum: ['산업기능요원', '전문연구요원', '승선근무예비역'], description: '(필수) 복무 형태를 선택합니다.', }, company_size: { type: 'string', enum: ['', '대기업', '중소기업', '중견기업', '농어민후계', '기타'], description: '기업 규모 (선택사항, 빈 값은 전체 조회)', }, industry_sectors: { oneOf: [ { type: 'string', enum: [ '철강', '기계', '전기', '전자', '화학', '섬유', '신발', '시멘요업', '생활용품', '통신기기', '정보처리', '게임SW', '영상게임', '의료의약', '식음료', '농산물가공', '수산물가공', '임산물가공', '동물약품', '애니메이션', '석탄채굴', '일반광물채굴', '선광제련', '에너지', '국내건설', '국외건설', '내항화물', '외항화물', '내항선박관리', '외항선박관리', '근해', '원양', ], }, { type: 'array', items: { type: 'string', enum: [ '철강', '기계', '전기', '전자', '화학', '섬유', '신발', '시멘요업', '생활용품', '통신기기', '정보처리', '게임SW', '영상게임', '의료의약', '식음료', '농산물가공', '수산물가공', '임산물가공', '동물약품', '애니메이션', '석탄채굴', '일반광물채굴', '선광제련', '에너지', '국내건설', '국외건설', '내항화물', '외항화물', '내항선박관리', '외항선박관리', '근해', '원양', ], }, }, ], description: '업종 코드 (선택사항, 여러 개 가능)', }, company_name: { type: 'string', description: '회사 이름 (선택사항, 빈 값은 전체 조회)', }, city_province: { type: 'string', enum: [ '서울특별시', '부산광역시', '대구광역시', '인천광역시', '광주광역시', '대전광역시', '울산광역시', '세종특별자치시', '경기도', '충청북도', '충청남도', '전라남도', '경상북도', '경상남도', '제주특별자치도', '강원특별자치도', '전북특별자치도', ], description: '시/도 선택 (선택사항, 빈 값은 전국 전체 조회)', }, city_district: { type: 'string', description: '시/군/구 주소 (선택사항, 빈 값은 city_province 전체 조회)', }, is_hiring: { type: 'boolean', enum: [true, false], description: "병무청 채용 공고 등록 업체 여부 (선택사항, 'Y' 또는 빈 값)", }, military_service_status: { oneOf: [ { type: 'string', enum: ['현역', '보충역'], }, { type: 'array', items: { type: 'string', enum: ['현역', '보충역'], }, }, ], description: '현역 또는 보충역 TO 유무 (전체 조회시 H, B 모두 입력)', }, }, required: ['service_type'], }, - src/index.ts:21-179 (registration)Registration of the tool in the TOOLS array, including name, detailed Korean description, and reference to inputSchema. Used in ListToolsRequestHandler.
const TOOLS: Tool[] = [ { name: 'search_designated_entities', description: '병무청 병역일터에서 병역특례 지정업체를 검색합니다. ' + '산업기능요원, 전문연구요원, 승선근무예비역 복무가 가능한 업체 정보를 조회할 수 있습니다. ' + '검색 결과는 CSV 형식으로 반환되며, 최대 30개의 업체 정보가 제공됩니다.', inputSchema: { type: 'object', properties: { service_type: { type: 'string', enum: ['산업기능요원', '전문연구요원', '승선근무예비역'], description: '(필수) 복무 형태를 선택합니다.', }, company_size: { type: 'string', enum: ['', '대기업', '중소기업', '중견기업', '농어민후계', '기타'], description: '기업 규모 (선택사항, 빈 값은 전체 조회)', }, industry_sectors: { oneOf: [ { type: 'string', enum: [ '철강', '기계', '전기', '전자', '화학', '섬유', '신발', '시멘요업', '생활용품', '통신기기', '정보처리', '게임SW', '영상게임', '의료의약', '식음료', '농산물가공', '수산물가공', '임산물가공', '동물약품', '애니메이션', '석탄채굴', '일반광물채굴', '선광제련', '에너지', '국내건설', '국외건설', '내항화물', '외항화물', '내항선박관리', '외항선박관리', '근해', '원양', ], }, { type: 'array', items: { type: 'string', enum: [ '철강', '기계', '전기', '전자', '화학', '섬유', '신발', '시멘요업', '생활용품', '통신기기', '정보처리', '게임SW', '영상게임', '의료의약', '식음료', '농산물가공', '수산물가공', '임산물가공', '동물약품', '애니메이션', '석탄채굴', '일반광물채굴', '선광제련', '에너지', '국내건설', '국외건설', '내항화물', '외항화물', '내항선박관리', '외항선박관리', '근해', '원양', ], }, }, ], description: '업종 코드 (선택사항, 여러 개 가능)', }, company_name: { type: 'string', description: '회사 이름 (선택사항, 빈 값은 전체 조회)', }, city_province: { type: 'string', enum: [ '서울특별시', '부산광역시', '대구광역시', '인천광역시', '광주광역시', '대전광역시', '울산광역시', '세종특별자치시', '경기도', '충청북도', '충청남도', '전라남도', '경상북도', '경상남도', '제주특별자치도', '강원특별자치도', '전북특별자치도', ], description: '시/도 선택 (선택사항, 빈 값은 전국 전체 조회)', }, city_district: { type: 'string', description: '시/군/구 주소 (선택사항, 빈 값은 city_province 전체 조회)', }, is_hiring: { type: 'boolean', enum: [true, false], description: "병무청 채용 공고 등록 업체 여부 (선택사항, 'Y' 또는 빈 값)", }, military_service_status: { oneOf: [ { type: 'string', enum: ['현역', '보충역'], }, { type: 'array', items: { type: 'string', enum: ['현역', '보충역'], }, }, ], description: '현역 또는 보충역 TO 유무 (전체 조회시 H, B 모두 입력)', }, }, required: ['service_type'], }, }, ]; - src/index.ts:238-253 (handler)MCP CallToolRequestHandler switch case that maps user input arguments to MilitaryWorkplaceSearchQuery, validates required fields, calls the core search_designated_entities function, and formats the CSV result as MCP tool response.
case 'search_designated_entities': { if (!query.eopjong_gbcd) { throw new Error('eopjong_gbcd parameter is required'); } const result = await search_designated_entities(query); return { content: [ { type: 'text', text: result, }, ], }; } - src/mma-api.ts:24-97 (helper)Helper function to convert MilitaryWorkplaceSearchQuery into URLSearchParams form data for the MMA API POST request, handling enum mappings and multi-value fields.
export function buildFormData(query: MilitaryWorkplaceSearchQuery): string { const params = new URLSearchParams(); // 필수 필드: eopjong_gbcd (복무 형태) const agentTypeValue = AgentType[query.eopjong_gbcd]; params.append('eopjong_gbcd', agentTypeValue); // al_eopjong_gbcd_yn: 빈 문자열로 설정 params.append('al_eopjong_gbcd_yn', ''); // gegyumo_cd: 기업 규모 if (query.gegyumo_cd !== undefined && query.gegyumo_cd !== '') { const sizeCode = CompanySizeCode[query.gegyumo_cd]; if (sizeCode) { params.append('gegyumo_cd', sizeCode); } } else if (query.gegyumo_cd === '') { params.append('gegyumo_cd', ''); } // eopjong_cd: 업종 코드 (여러 개 가능) if (query.eopjong_cd) { const codes = Array.isArray(query.eopjong_cd) ? query.eopjong_cd : [query.eopjong_cd]; codes.forEach((industry) => { const code = IndustryCode[industry]; if (code) { params.append('eopjong_cd', code); } }); // e.g) 11111,11112 (and no encoding) const codeValues = codes.map((industry) => IndustryCode[industry]).filter(Boolean); const al_eopjong_gbcd = codeValues.join(','); const eopjong_gbcd_list = codeValues.join(','); // al_eopjong_gbcd와 동일 if (al_eopjong_gbcd) { params.append('al_eopjong_gbcd', al_eopjong_gbcd); } if (eopjong_gbcd_list) { params.append('eopjong_gbcd_list', eopjong_gbcd_list); } } // eopche_nm: 회사 이름 if (query.eopche_nm !== undefined) { params.append('eopche_nm', query.eopche_nm); } // sido_addr: 시/도 if (query.sido_addr) { params.append('sido_addr', query.sido_addr); } // sigungu_addr: 시/군/구 if (query.sigungu_addr !== undefined) { params.append('sigungu_addr', query.sigungu_addr); } // chaeyongym: 채용 공고 등록 업체 if (query.chaeyongym !== undefined) { params.append('chaeyongym', query.chaeyongym); } // bjinwonym: 현역/보충역 TO 유무 (여러 개 가능) if (query.bjinwonym) { const values = Array.isArray(query.bjinwonym) ? query.bjinwonym : [query.bjinwonym]; values.forEach((value) => { params.append('bjinwonym', value); }); } return params.toString(); }