kokkai-meeting-mcp-server
Version:
MCP server for Japanese Diet (Kokkai) meeting records API
169 lines (167 loc) • 6.92 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, } from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';
const server = new Server({
name: 'kokkai-server',
version: '1.0.0',
});
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'search_kokkai_meetings',
description: 'Search Japanese Diet (Kokkai) meeting records using the NDL API',
inputSchema: {
type: 'object',
properties: {
any: {
type: 'string',
description: 'Search term for speech content (e.g., "科学技術" for science and technology)',
},
nameOfMeeting: {
type: 'string',
description: 'Meeting name to search for',
},
maximumRecords: {
type: 'number',
description: 'Maximum number of records to return (up to 100)',
maximum: 100,
default: 30,
},
recordPacking: {
type: 'string',
enum: ['json', 'xml'],
description: 'Output format',
default: 'json',
},
startRecord: {
type: 'number',
description: 'Starting position for pagination',
minimum: 1,
},
sessionFrom: {
type: 'number',
description: 'Starting session number',
minimum: 1,
},
sessionTo: {
type: 'number',
description: 'Ending session number',
minimum: 1,
},
nameOfHouse: {
type: 'string',
enum: ['衆議院', '参議院', '両院協議会'],
description: 'House name (House of Representatives, House of Councillors, or Joint Committee)',
},
issueFrom: {
type: 'number',
description: 'Starting issue number',
minimum: 1,
},
issueTo: {
type: 'number',
description: 'Ending issue number',
minimum: 1,
},
},
},
},
],
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === 'search_kokkai_meetings') {
try {
const params = request.params.arguments;
// Build URL parameters
const urlParams = new URLSearchParams();
if (params.any)
urlParams.append('any', params.any);
if (params.nameOfMeeting)
urlParams.append('nameOfMeeting', params.nameOfMeeting);
if (params.maximumRecords)
urlParams.append('maximumRecords', params.maximumRecords.toString());
if (params.recordPacking)
urlParams.append('recordPacking', params.recordPacking);
if (params.startRecord)
urlParams.append('startRecord', params.startRecord.toString());
if (params.sessionFrom)
urlParams.append('sessionFrom', params.sessionFrom.toString());
if (params.sessionTo)
urlParams.append('sessionTo', params.sessionTo.toString());
if (params.nameOfHouse)
urlParams.append('nameOfHouse', params.nameOfHouse);
if (params.issueFrom)
urlParams.append('issueFrom', params.issueFrom.toString());
if (params.issueTo)
urlParams.append('issueTo', params.issueTo.toString());
const url = `https://kokkai.ndl.go.jp/api/meeting_list?${urlParams.toString()}`;
const response = await axios.get(url, {
headers: {
'User-Agent': 'kokkai-mcp-server/1.0.0',
},
timeout: 30000,
});
// Format response for better AI parsing
let formattedResponse = '';
if (params.recordPacking === 'json') {
formattedResponse = JSON.stringify(response.data, null, 2);
}
else {
// For XML, provide structured summary
const xmlString = response.data.toString();
const numberOfRecords = xmlString.match(/<numberOfRecords>(\d+)<\/numberOfRecords>/)?.[1] || '0';
const numberOfReturn = xmlString.match(/<numberOfReturn>(\d+)<\/numberOfReturn>/)?.[1] || '0';
formattedResponse = `Search Results Summary:
- Total records found: ${numberOfRecords}
- Records returned: ${numberOfReturn}
- Search URL: ${url}
Raw XML Response:
${xmlString}`;
}
return {
content: [
{
type: 'text',
text: formattedResponse,
},
],
};
}
catch (error) {
if (axios.isAxiosError(error)) {
return {
content: [
{
type: 'text',
text: `Error: ${error.message}\nStatus: ${error.response?.status}\nData: ${error.response?.data}`,
},
],
isError: true,
};
}
return {
content: [
{
type: 'text',
text: `Unexpected error: ${error}`,
},
],
isError: true,
};
}
}
throw new Error(`Unknown tool: ${request.params.name}`);
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Kokkai MCP server running on stdio');
}
main().catch((error) => {
console.error('Server failed to start:', error);
process.exit(1);
});