@minjunkwon/localgovernment-welfare-mcp-server
Version:
MCP server for Korean Local Government Welfare Services - provides intelligent search and information retrieval for local government social welfare programs
332 lines (325 loc) β’ 15.7 kB
JavaScript
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
import { LocalWelfareAPI } from './api.js';
const server = new Server({
name: "localgovernment-welfare-mcp-server",
version: "2.0.0"
}, {
capabilities: {
tools: {},
},
});
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "search_local_welfare_services",
description: `π Search local government welfare services with automatic detailed information retrieval.
β‘ WHAT THIS TOOL DOES:
1. Searches welfare services based on your criteria
2. Automatically fetches detailed information for ALL found services
3. Returns both search results AND complete details in one response
π― WHEN TO USE: When user asks for welfare services information
π PARAMETER INFERENCE GUIDE:
π’ Region (ctpvNm/sggNm):
- "μμΈ μνꡬ" β ctpvNm: "μμΈνΉλ³μ", sggNm: "μνꡬ"
- "κ²½κΈ°λ μ±λ¨μ" β ctpvNm: "κ²½κΈ°λ", sggNm: "μ±λ¨μ"
- "λΆμ°" β ctpvNm: "λΆμ°κ΄μμ"
π₯ Target Group (trgterIndvdlArray):
- "μ μλμΈ΅", "μκ³κΈμ¬" β "010"
- "μ₯μ μΈ" β "020"
- "νλΆλͺ¨κ°μ‘±", "μ‘°μκ°μ " β "030"
- "λ€μλ
κ°μ " β "040"
- "μμ λΆ", "μΆμ°" β "050"
- "보νλμμ" β "060"
π
Life Stage (lifeArray):
- "μμ μ", "0-5μΈ" β "001"
- "μλ", "6-12μΈ" β "002"
- "μ²μλ
", "13-18μΈ" β "003"
- "μ²λ
", "19-34μΈ" β "004"
- "μ€μ₯λ
", "35-64μΈ" β "005"
- "λ
Έλ
", "65μΈ μ΄μ" β "006"
- "μ 체", "μ μ°λ Ή" β "007"
π― Interest Theme (intrsThemaArray):
- "μΌμ리", "μ·¨μ
", "μ°½μ
" β "010"
- "μ£Όκ±°", "μ£Όν", "μλ" β "020"
- "κ΅μ‘", "νμ΅", "μ격μ¦" β "030"
- "볡μ§λ¬Έν", "λ¬Ένμν" β "040"
- "μμ ", "보μ" β "050"
- "보건", "μλ£", "건κ°" β "060"
- "νκ²½" β "070"
- "λλ¦ΌμΆμ°μ΄μ
" β "080"
- "κΈ°ν" β "090"
- "μλ―ΌκΈμ΅", "λμΆ", "μ§μκΈ" β "100"
- "μ 체건κ°", "μ¬ν" β "110"
- "μμ Β·μΆμ°" β "120"
- "보μ‘Β·λλ΄", "μ‘μ" β "130"
- "μ
μΒ·μν" β "140"
β οΈ IMPORTANT:
- Automatically fetches details for up to 15 services (configurable via maxDetails)
- Larger result sets may take longer due to sequential API calls
- Each detail fetch has 500ms delay for API stability`,
inputSchema: {
type: "object",
properties: {
pageNo: {
type: "number",
description: "Page number (default: 1)"
},
numOfRows: {
type: "number",
description: "Number of results per page (default: 10, max: 50)"
},
maxDetails: {
type: "number",
description: "Maximum number of services to get details for (default: 15, max: 20)"
},
serviceKey: {
type: "string",
description: "API service key (optional - server has default)"
},
lifeArray: {
type: "string",
description: "Life stage codes: 001(μμ μ), 002(μλ), 003(μ²μλ
), 004(μ²λ
), 005(μ€μ₯λ
), 006(λ
Έλ
), 007(μ 체)"
},
trgterIndvdlArray: {
type: "string",
description: "Target individual codes: 010(μ μλ), 020(μ₯μ μΈ), 030(νλΆλͺ¨Β·μ‘°μ), 040(λ€μλ
), 050(μμ Β·μΆμ°), 060(보νλμμ)"
},
intrsThemaArray: {
type: "string",
description: "Interest theme codes: 010(μΌμ리), 020(μ£Όκ±°), 030(κ΅μ‘), 040(볡μ§λ¬Έν), 050(μμ ), 060(보건), 070(νκ²½), 080(λλ¦ΌμΆμ°μ΄μ
), 090(κΈ°ν), 100(μλ―ΌκΈμ΅), 110(μ 체건κ°), 120(μμ Β·μΆμ°), 130(보μ‘Β·λλ΄), 140(μ
μΒ·μν)"
},
ctpvNm: {
type: "string",
description: "Province/City name (e.g., μμΈνΉλ³μ, κ²½κΈ°λ, λΆμ°κ΄μμ)"
},
sggNm: {
type: "string",
description: "District name (e.g., μνꡬ, μ±λ¨μ, ν΄μ΄λꡬ)"
},
srchKeyCode: {
type: "string",
description: "Search key code: 001(μλΉμ€λͺ
), 002(μλΉμ€ μμ½), 003(μ 체)"
},
searchWrd: {
type: "string",
description: "Search keyword"
},
arrgOrd: {
type: "string",
description: "Sort order: 001(μ΅μ μ), 002(μΈκΈ°μ)"
}
}
}
}
// {
// name: "get_local_welfare_codes",
// description: `Get code definitions used in the local welfare services API.
// WHEN TO USE: When you need to understand what codes mean or show users available options.
// PARAMETER:
// codeType (OPTIONAL): Type of codes to retrieve
// - "all" (default): All code types
// - "lifeCycle": Life stage codes (μμ μ, μλ, μ²μλ
, etc.)
// - "householdType": Household situation codes (μ μλ, μ₯μ μΈ, νλΆλͺ¨, etc.)
// - "interestTheme": Interest theme codes (μΌμ리, μ£Όκ±°, κ΅μ‘, etc.)
// - "sortOrder": Sort order options (μ΅μ μ, μΈκΈ°μ)
// USAGE: Call this when user asks about available options or code meanings.`,
// inputSchema: {
// type: "object",
// properties: {
// codeType: {
// type: "string",
// enum: ["lifeCycle", "householdType", "interestTheme", "sortOrder", "all"],
// description: "Type of codes to retrieve (default: all)"
// }
// }
// }
// }
],
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
console.error('CallToolRequest received:', JSON.stringify(request));
const { name, arguments: args } = request.params;
// argumentsκ° μλ κ²½μ° λ μμΈν λ‘κΉ
if (!args) {
console.error('No arguments provided in request.params:', request.params);
throw new McpError(ErrorCode.InvalidParams, "No arguments provided");
}
switch (name) {
case "search_local_welfare_services": {
console.error('search_local_welfare_services called with args:', JSON.stringify(args));
if (!args || typeof args !== 'object') {
throw new McpError(ErrorCode.InvalidParams, "Invalid arguments provided");
}
const searchParams = {
pageNo: args && 'pageNo' in args ? Number(args.pageNo) || 1 : 1,
numOfRows: args && 'numOfRows' in args ? Number(args.numOfRows) || 10 : 10
};
// λͺ¨λ κ²μ λ§€κ°λ³μ μ²λ¦¬
if (args && 'serviceKey' in args && typeof args.serviceKey === 'string' && args.serviceKey.trim()) {
searchParams.serviceKey = args.serviceKey;
}
if (args.lifeArray && args.lifeArray.trim()) {
searchParams.lifeArray = args.lifeArray;
}
if (args.trgterIndvdlArray && args.trgterIndvdlArray.trim()) {
searchParams.trgterIndvdlArray = args.trgterIndvdlArray;
}
if (args.intrsThemaArray && args.intrsThemaArray.trim()) {
searchParams.intrsThemaArray = args.intrsThemaArray;
}
if (args.ctpvNm && args.ctpvNm.trim()) {
searchParams.ctpvNm = args.ctpvNm;
}
if (args.sggNm && args.sggNm.trim()) {
searchParams.sggNm = args.sggNm;
}
if (args.srchKeyCode && ['001', '002', '003'].includes(args.srchKeyCode)) {
searchParams.srchKeyCode = args.srchKeyCode;
}
if (args.searchWrd && args.searchWrd.trim()) {
searchParams.searchWrd = args.searchWrd;
}
if (args.arrgOrd && ['001', '002'].includes(args.arrgOrd)) {
searchParams.arrgOrd = args.arrgOrd;
}
// μμΈμ‘°ν μ΅μ
(νμ νμ±ν)
const maxDetails = args && 'maxDetails' in args ? Math.min(Number(args.maxDetails) || 15, 20) : 15;
try {
// 1λ¨κ³: λͺ©λ‘ μ‘°ν
const localWelfareAPI = new LocalWelfareAPI();
const searchResult = await localWelfareAPI.getLocalWelfareList(searchParams);
// 2λ¨κ³: μλ μμΈμ‘°ν
const servList = searchResult.wantedList?.servList;
if (!servList || servList.length === 0) {
return {
content: [
{
type: "text",
text: JSON.stringify({
searchParams: searchParams,
searchResult: searchResult,
details: [],
message: "No services found"
}, null, 2)
}
]
};
}
// μμΈμ‘°νν μλΉμ€ ID λͺ©λ‘ μμ± (μ΅λ maxDetailsκ°)
const servIds = (Array.isArray(servList) ? servList : [servList])
.slice(0, maxDetails)
.map(service => service.servId)
.filter(id => id);
console.error(`Auto-fetching details for ${servIds.length} services...`);
const detailResults = [];
for (let i = 0; i < servIds.length; i++) {
const servId = servIds[i];
try {
console.error(`Fetching details for ${servId} (${i + 1}/${servIds.length})`);
const detailAPI = new LocalWelfareAPI();
const detailResult = await detailAPI.getLocalWelfareDetail({ servId });
detailResults.push({
servId,
success: true,
data: detailResult.wantedDtl
});
// API νΈμΆ κ° 500ms μ§μ° (λ§μ§λ§ νΈμΆ μ μΈ)
if (i < servIds.length - 1) {
await new Promise(resolve => setTimeout(resolve, 500));
}
}
catch (error) {
console.error(`Error fetching details for ${servId}:`, error);
detailResults.push({
servId,
success: false,
error: error instanceof Error ? error.message : String(error)
});
}
}
return {
content: [
{
type: "text",
text: JSON.stringify({
searchParams: searchParams,
searchResult: searchResult,
detailsSummary: {
total: servIds.length,
successful: detailResults.filter(r => r.success).length,
failed: detailResults.filter(r => !r.success).length
},
details: detailResults
}, null, 2)
}
]
};
}
catch (error) {
console.error('Error in search_local_welfare_services:', error);
throw new McpError(ErrorCode.InternalError, `Failed to search welfare services with details: ${error instanceof Error ? error.message : String(error)}`);
}
}
// case "get_local_welfare_codes": {
// const codeType = (args as any).codeType || "all";
// let codes: any = {};
// switch (codeType) {
// case "lifeCycle":
// codes = { lifeCycle: LifeCycleCode };
// break;
// case "householdType":
// codes = { householdType: HouseholdTypeCode };
// break;
// case "interestTheme":
// codes = { interestTheme: InterestThemeCode };
// break;
// case "sortOrder":
// codes = { sortOrder: SortOrderCode };
// break;
// case "all":
// default:
// codes = {
// lifeCycle: LifeCycleCode,
// householdType: HouseholdTypeCode,
// interestTheme: InterestThemeCode,
// sortOrder: SortOrderCode
// };
// break;
// }
// return {
// content: [
// {
// type: "text",
// text: JSON.stringify(codes, null, 2)
// }
// ]
// };
// }
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
}
catch (error) {
console.error('Tool execution error:', error);
if (error instanceof McpError) {
throw error;
}
throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
}
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Local Government Welfare MCP Server running on stdio");
}
main().catch((error) => {
console.error("Server failed to start:", error);
process.exit(1);
});
//# sourceMappingURL=index-clean.js.map