note-mcp-server-n8n.ts•6.97 kB
#!/usr/bin/env node
import { createServer } from 'http';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { registerAllTools } from './tools/index.js';
import { loginToNote } from './utils/auth.js';
import { noteApiRequest } from './utils/api-client.js';
// ツールリストを取得
async function getToolsList() {
return {
tools: [
{
name: "search-notes",
description: "note.comの記事を検索",
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "検索キーワード" }
}
}
},
{
name: "post-draft-note",
description: "下書き記事を作成・更新",
inputSchema: {
type: "object",
properties: {
title: { type: "string", description: "記事タイトル" },
content: { type: "string", description: "記事内容" }
}
}
}
]
};
}
const HOST = 'localhost';
const PORT = 3001;
// n8n用のシンプルなHTTP MCPサーバー
async function startN8nServer() {
console.error('🚀 n8n用MCPサーバーを起動します...');
// MCPサーバーを作成
const server = new McpServer(
{
name: 'note-api-mcp-n8n',
version: '2.0.0-n8n'
},
{
capabilities: {
tools: {},
prompts: {},
resources: {}
}
}
);
// 認証
try {
await loginToNote();
console.error('✅ 認証成功');
} catch (error) {
console.error('❌ 認証失敗:', error);
}
// ツールを登録
await registerAllTools(server);
// HTTPサーバーを作成
const httpServer = createServer(async (req, res) => {
// Healthエンドポイント
if (req.url === '/health' && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
status: 'ok',
server: 'note-api-mcp-n8n',
version: '2.0.0-n8n',
transport: 'HTTP-JSON-RPC',
endpoint: `/mcp`
}));
return;
}
// CORSヘッダー
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
res.writeHead(200);
res.end();
return;
}
if (req.method !== 'POST') {
res.writeHead(405, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Method not allowed' }));
return;
}
if (!req.url?.startsWith('/mcp')) {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not found' }));
return;
}
try {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', async () => {
try {
const message = JSON.parse(body);
console.error('📨 n8nリクエスト:', message.method);
// 簡易的なJSON-RPC処理
let response;
if (message.method === 'initialize') {
response = {
jsonrpc: '2.0',
id: message.id,
result: {
protocolVersion: '2025-06-18',
capabilities: {
tools: {},
prompts: {},
resources: {}
},
serverInfo: {
name: 'note-api-mcp-n8n',
version: '2.0.0-n8n'
}
}
};
} else if (message.method === 'tools/list') {
const toolsList = await getToolsList();
response = {
jsonrpc: '2.0',
id: message.id,
result: toolsList
};
} else if (message.method?.startsWith('tools/')) {
const toolName = message.method.replace('tools/', '');
try {
// ツールを直接実行する簡易的な実装
const tools = await getToolsList();
const tool = tools.tools?.find((t: any) => t.name === toolName);
if (!tool) {
response = {
jsonrpc: '2.0',
id: message.id,
error: {
code: -32601,
message: `Tool ${toolName} not found`
}
};
} else {
// 簡易的なレスポンス(実際のツール実装は別途必要)
response = {
jsonrpc: '2.0',
id: message.id,
result: {
content: [{
type: 'text',
text: `Tool ${toolName} executed with args: ${JSON.stringify(message.params?.arguments || {})}`
}]
}
};
}
} catch (error) {
response = {
jsonrpc: '2.0',
id: message.id,
error: {
code: -32603,
message: error instanceof Error ? error.message : 'Unknown error',
data: error
}
};
}
} else {
response = {
jsonrpc: '2.0',
id: message.id,
error: {
code: -32601,
message: 'Method not found'
}
};
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(response));
console.error('✅ n8nレスポンス送信:', message.method);
} catch (error) {
console.error('❌ JSON-RPC処理エラー:', error);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
jsonrpc: '2.0',
id: null,
error: {
code: -32603,
message: 'Internal error',
data: error instanceof Error ? error.message : 'Unknown error'
}
}));
}
});
} catch (error) {
console.error('❌ リクエスト処理エラー:', error);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: 'Internal server error',
details: error instanceof Error ? error.message : 'Unknown error'
}));
}
});
httpServer.listen(PORT, HOST, () => {
console.error(`🌐 n8n用MCPサーバーが起動しました:`);
console.error(` URL: http://${HOST}:${PORT}/mcp`);
console.error(` Health: http://${HOST}:${PORT}/health`);
});
}
startN8nServer().catch(console.error);