ã¡ãªãŒã»ã¯ãªã¹ãã¹ãã»ãŒãã£ãŒCTOã®æ£®æ¬ã§ãã ãã®èšäºã¯ Safie Engineers' Blog! Advent Calendar 25æ¥ç®ã®èšäºã§ãã äžæšå¹Ž 嵿¥ä»¥æ¥10幎ã®éçºçµç¹ã®æ¯ãè¿ã ã«ã€ããŠæ²èŒãã2幎ãçµã¡ãŸãã ãããããäºã«äŒç€Ÿã®äºæ¥ãéçºçµç¹ãé èª¿ã«æ°ããåãçµã¿ã亀ãã€ã€å€§ããæé·ããŠããäºã宿ããŠãã仿¥ãã®é ã§ãã ãããã¡ã³ããŒã®çããã®é 匵ãã®è³ç©ã§ãããšæè¬ããŠããŸãã ãã®ãããããã£ãŠãèªåã®æéãæãåããäºã«äœ¿ãããšãå°ããã€åºæ¥ãããã«ãªã£ãŠããŸããã®ã§ãä»åã¯å°ãæè¡ããã®å
容ã«ã€ããŠæžãäºã«ããŸãã ã¯ããã« MCPãµãŒããŒæŽ»çšäŸ w/ Claude Desktop LLMãšMCPãµãŒããŒã®ããåã MCPãµãŒããŒã䜿çšããªããåŸæ¥ã®ããåã MCPãµãŒããŒãä»ããå Žåã®ããåã MCPããŒã«ãã«ã¹ã±ãŒãã§æŽ»çšããå Žå MCPããŒã«ã®æ
å ±ãLLMã«èšå®ããã«ã¯ ãŸãšã ææã« ã¯ããã« çæAIåšãã§ã¯æ§ã
ãªã¢ãã«ãããã«ãŸã€ããããŒã«ã®æ¥éãªçºå±ã«ãããæ¥åå¹çåãåµé æ§ã®åäžãæ
å ±ã¢ã¯ã»ã¹ã®ç°¡æåãªã©ã®ç¹ã«æŒããŠãé©ãã¹ãã¹ããŒãã§å€çš®å€æ§ãªæ©æµãããããããŠããã®ã¯ä»æŽèšããŸã§ããããŸããã ãã¡ããåœç€Ÿã§ãæ©ã段éãããäžéšã¡ã³ããŒãæ§ã
ãªããŒã«ã®éçºæ¥åãžã®æŽ»çšããããã¯ããžã®å¿çšæ€èšãé²ããŠãããŠããŸãããããããæŽã«å éããçºããŒã ãçµæãã çµç¹ãšããŠã®æŽ»çš ãé²ããŠããŸãã ãŸãããããèšããŸã§ããªãã§ãããMCPïŒModel Context ProtocolïŒã«ããå€çš®å€æ§ãªå€éšãµãŒãã¹ãšæè»ã«é£æºãã§ããããã«ãªããACPïŒAgent Communication ProtocolïŒãA2AïŒAgent-to-AgentïŒãªã©ã«ãããšãŒãžã§ã³ãéã®ã³ãã¥ãã±ãŒã·ã§ã³ãåºãããèŠããŠããŸãã äžäŸãšã㊠åœç€Ÿç€Ÿå¡ã以äžã§çŽ¹ä» ããŠãããŠããŸãããæè»ãªããã¹ãããŒã¹ã®ã€ã³ãããã«ãããµãŒãã¹ã®å¶åŸ¡ãå¯èœãšãªã£ãŠããŸãã MCPãæŽ»çšããã°ãLLMãçµç±ããŠåœç€ŸãµãŒãã¹ãæè»ã«å¶åŸ¡ããäºãåºæ¥ãã ãã§ãªããä»ã®ãµãŒãã¹ãšã®é£æºãéåžžã«ç°¡åã«è¡ããŸãã äžèšèšäºã«ããéããåœç€Ÿã«ã¡ã©ããç»åãååŸããã®èŠçŽãããããšãç°¡åã«åºæ¥ãŸãããèŠçŽããå
容ãã€ã³ã«ã ã§éç¥ãããšèšã£ãä»ã®ãµãŒãã¹ãšã®é£æºãç°¡åã§ãã äŸïŒã€ã³ã«ã ãŒSafie飿º 仿Žã§ãããäžæ°ã«ãµãŒãã¹ã®æ¡åŒµæ§ã䌞ã°ããŠãããæè¡ãšããäºã§åŒ·ãå¯èœæ§ãæããŠããŸãã ãã ãã«ã¹ã¿ã ã®MCPãµãŒããŒã掻çšããçºã«ã¯Claude Desktopãå¿
èŠãªã®ïŒä»ã«ãåè£ã¯ãããŸããïŒãã¡ãã£ãšå¿äœãªãã®ãšãå
éšçã«LLMãšMCPãµãŒããŒãã©ã®ããã«ããåãããŠããã®ãç¥ãããã£ãã®ã§ãMCPãµãŒããŒãèªåã®ãµãŒããŒäžã§å¶åŸ¡ã§ããç°¡æãªãµã³ãã«ãäœãäžèº«ãèŠãŠã¿ãŸããã MCPãµãŒããŒæŽ»çšäŸ w/ Claude Desktop Claude Desktopã§ã¯å
¥åãããããã³ããã«ãã£ãŠãæè»ã«MCPãµãŒããŒãæäŸããããŒã«ã䜿ãããã©ãã倿ããå¿
èŠã«å¿ãé©åã«ãã©ã¡ãŒã¿ãã»ãããå®è¡ããŠãããŸãã äŸãã°ä»¥äžã®ãããªã«ã¹ã¿ã ã®MCPãµãŒããŒãæºåããŠãClaude Desktopã«çµã¿èŸŒã¿ïŒâ»ãã®ãããã®æé ã¯çããŸãïŒããããã°ãã¯ãšãããã€ããŠãããšClaude Desktopã®ããã³ãããå
¥åãããšãMCPãµãŒããŒã®æäŸããæ©èœãé©åã«å®è¡ãããã®ã¬ã¹ãã³ã¹ãè¿ããŠãããŸãã from mcp.server.fastmcp import FastMCP from pydantic import BaseModel, Field class GreetingMessage (BaseModel): message: str = Field( None , description= "ãããã€ã®ã¡ãã»ãŒãž" ) # Create an MCP server mcp = FastMCP( "Greeting" ) @ mcp.tool () def greeting (message: str | None = Field( description= "ãããã€ã®ã¡ãã»ãŒãž" , default= None )) -> GreetingMessage: """ ãããã€ã¡ãã»ãŒãžãè¿ããŸã """ return GreetingMessage(message=f "Hello, MCP! {message}" ) def run (): mcp.run() ããç¥ãããæåã§ãããå
éšã§ã©ããã£ãŠãããèŠãŠã¿ãŸããã LLMãšMCPãµãŒããŒã®ããåã MCPãµãŒããŒã䜿çšããªããåŸæ¥ã®ããåã â»èªåãªãã®è§£éãçžåœå
¥ã£ãŠããŸãããããã¯ã容赊ãã ããã â»llmãããã·ãšããŠLiteLLMãå©çšããã¢ãã«ã¯claude-sonnet-4 ã䜿çšããŠããŸãã æåã«åŸæ¥ã®LLMãžã®ãã£ããã¡ãã»ãŒãžã¯ä»¥äžã®ãããªãªã¯ãšã¹ããšã¬ã¹ãã³ã¹ã§æç«ããŠããŸãã response = client.chat.completions.create( model=request.model, messages=messages, tools=mcp_manager.tools if request.use_mcp else None , max_tokens=request.max_tokens, temperature=request.temperature, ) messages = [ { "role" : "assistant" , "content" : "ããªãã¯æè»ã«å
¥åå
容ã顿šã§ããAIã¢ã·ã¹ã¿ã³ãã§ãã質åãžã®åçã¯åºæ¥ãéãå®çµã«åçããŠãããŸãã" }, { "role" : "user" , "content" : "ããã°ãã¯ãšãããã€ããŠã" } ] tools = [] â»LLMã«åºæ¥ãéãç°¡æœã«ã¬ã¹ãã³ã¹ãè¿ããŠæ¬²ããã£ãã®ã§ãäžèšã®ãããªã¢ã·ã¹ã¿ã³ãããŒã«ã®ã¡ãã»ãŒãžãæåã«ã»ããããŠããŸãã ãããšä»¥äžã®ãããªã¬ã¹ãã³ã¹ãè¿ããŠãããŸãã ChatCompletion( id = 'chatcmpl-b49350d7-d24a-43c4-a2a3-77343982f33d' , choices=[ Choice( finish_reason= 'stop' , index= 0 , logprobs= None , message=ChatCompletionMessage( content= 'ããã°ãã¯ïŒ' , refusal= None , role= 'assistant' , annotations= None , audio= None , function_call= None , tool_calls= None ) ) ], created= 1766406680 , model= 'apac.anthropic.claude-sonnet-4-20250514-v1:0' , object = 'chat.completion' , service_tier= None , system_fingerprint= None , usage=CompletionUsage( completion_tokens= 9 , prompt_tokens= 83 , total_tokens= 92 , completion_tokens_details= None , prompt_tokens_details=PromptTokensDetails( audio_tokens= None , cached_tokens= 0 , cache_creation_tokens= 0 ), cache_creation_input_tokens= 0 , cache_read_input_tokens= 0 ) ) LLMãchatAPIã§æž¡ãããããã³ããã¡ãã»ãŒãžãããã°ãã¯ãšãããã€ããŠããã«å¯ŸããŠããããã°ãã¯ïŒããšããã¡ãã»ãŒãžãè¿ããŠããŸãã MCPãµãŒããŒãä»ããå Žåã®ããåã äžèšã«å¯ŸããLLMãå
¥åãããããã³ãããå
ã«æé©ãªMCPããŒã«ãéžæãå¿
èŠã«å¿ããŠãã©ã¡ãŒã¿ãã»ãããå®è¡ããããã«ã¯ããµããŒãããŠããMCPããŒã«ã®æ
å ±ãã€ã³ãããããŠããå¿
èŠããããŸãã messages = { "role" : "assistant" , "content" : "ããªãã¯æè»ã«å
¥åå
容ã顿šã§ããAIã¢ã·ã¹ã¿ã³ãã§ãã質åãžã®åçã¯åºæ¥ãéãå®çµã«åçããŠãããŸãã" }, tools = [ { "type" : "function" , "function" : { "name" : "greeting" , "description" : "ãããã€ã¡ãã»ãŒãžãè¿ããŸã" , "parameters" : { "type" : "object" , "properties" : { "message" : { "type" : "string" , "description" : "message parameter" , "default" : null } }, "required" : [] } } } ] å
çšãšã®éãã¯æç¢ºã§ãtoolsãã©ã¡ãŒã¿ãŒã«MCPãµãŒããŒãæäŸããããŒã«ã®æ
å ±ãã»ããããŠãchatAPIãå®è¡ããŠããŸãã ãããšä»¥äžã®ãããªã¬ã¹ãã³ã¹ãè¿ããŠãããŸãã ChatCompletion( id = 'chatcmpl-63876e3e-fb1d-4910-81a9-8fea53190dff' , choices=[ Choice( finish_reason= 'tool_calls' , index= 0 , logprobs= None , message=ChatCompletionMessage( content= '' , refusal= None , role= 'assistant' , annotations= None , audio= None , function_call= None , tool_calls=[ ChatCompletionMessageFunctionToolCall( id = 'tooluse_rSY9i9tdRKmTZewG_-lfqg' , function=Function( arguments= '{"message": "ããã°ãã¯"}' , name= 'greeting' ), type = 'function' , index= 0 ) ] ) ) ], XXXXXXXXXXXXXXXX ) ã¬ã¹ãã³ã¹ãå
çšãšã¯ç°ãªããtool_callsãã©ã¡ãŒã¿ãã»ãããããŠããŸãã ãã©ã¡ãŒã¿ã«ã¯chatAPIã§æž¡ããããã³ãããštoolsãã©ã¡ãŒã¿ã®å
容ãå
ã«LLMãéžæããæé©ãªå®è¡ãã¹ãMCPããŒã«ãèšå®ãããŠããŸãã LLMã¯ãããŸã§å®è¡ãã¹ãããŒã«ãæç€ºããŠãããã ããªã®ã§ãå®éã®ããŒã«å®è¡éšåã¯LLMã®ç¯çå€ãªã®ã§å¯Ÿå¿ãå¿
èŠã§ãã äžèšã§ã¯greetingãšããããŒã«ãmessageã«èšèŒãããŠãããã©ã¡ãŒã¿ã§å®è¡ãã¹ããšãªã£ãŠããã®ã§ãå®è¡ããŸãã { ã 'message' : 'Hello, MCP! ããã°ãã¯' } å®è¡çµæã¯èªèº«ã§æºåããMCPããŒã«ã®éããšãªããŸãã ä»åã¯ã¬ã¹ãã³ã¹ãšããŠãHello, MCP! ããã°ãã¯ããè¿ã£ãŠããŠããŸããã ããã³ããããå
¥åããããã¹ããæ£ãããã©ã¡ãŒã¿ãšããŠæž¡ãããããŒã«ãæ³å®éãã«å®è¡ãããŠããŸãã â»ãããŸã§LLMã¯å®è¡ãã¹ããšå€æããå
容ãè¿ããŠãããã ãã§ãããŒã«ã®å®è¡ã¯èªèº«ã§ãã£ãŠããŸãã æ¬¡ã«LLMã«MCPããŒã«ã®å®è¡çµæããã£ããAPIã§æž¡ããŠãããŸãã messages = [ { "role" : "assistant" , "content" : "ããªãã¯æè»ã«å
¥åå
容ã顿šã§ããAIã¢ã·ã¹ã¿ã³ãã§ãã質åãžã®åçã¯åºæ¥ãéãå®çµã«åçããŠãããŸãã" }, { "role" : "user" , "content" : "ããã°ãã¯ãšãããã€ããŠã" }, { "role" : "assistant" , "content" : "" , "tool_calls" : [ { "id" : "tooluse_rSY9i9tdRKmTZewG_-lfqg" , "type" : "function" , "function" : { "name" : "greeting" , "arguments" : "{ \" message \" : \" ããã°ã㯠\" }" } } ] }, { "role" : "tool" , "tool_call_id" : "tooluse_rSY9i9tdRKmTZewG_-lfqg" , "content" : "{ \" message \" : \" Hello, MCP! ããã°ã㯠\" }" } ] â»toolsã¯éè€ããã®ã§èšèŒãçããŸãã LLMãåã®ã¹ãããã§è¿ããŠããtool_call_idã«åãããŠããŒã«ã®ã¬ã¹ãã³ã¹ãã»ããããã£ããã¡ãã»ãŒãžãå®è¡ããŸããããã«ããLLMãMCPããŒã«ãå®è¡ãããäºãšããã®çµæãåãåãäºãã§ããŸãã ChatCompletion( id = 'chatcmpl-32ae0203-9b39-4b33-9a71-6ba9c9ac1cdb' , choices=[ Choice( finish_reason= 'stop' , index= 0 , logprobs= None , message=ChatCompletionMessage( content= 'ããã°ãã¯ïŒããããéããã§ããïŒ' , refusal= None , role= 'assistant' , annotations= None , audio= None , function_call= None , tool_calls= None ) ) ], XXXXXXXXXXXXXXXX ) ãããšLLMãä»ãŸã§ã®ããåããšMCPããŒã«ã®å®è¡çµæãããŒã¹ã«ã¬ã¹ãã³ã¹ãè¿ããŠãããŸãã ä»åã¯ãããã°ãã¯ïŒããããéããã§ããïŒãã§ããè¥å¹²äŸãåããã«ããã§ãããç¬ã æŽã«é£ç¶ã§MCPããŒã«ãå®è¡åºæ¥ãå Žåã¯tool_callsã«æ¬¡ã«å®è¡ãã¹ãMCPããŒã«ã®æ
å ±ãã»ãããããã®ã§ãããä»åã¯ç¹ã«èšå®ãããŠç¡ãã®ã§ããã§ãã£ããããã»ã¹ã¯å®äºãšãªããŸãã MCPããŒã«ãã«ã¹ã±ãŒãã§æŽ»çšããå Žå ã§ã¯åé ã§ã玹ä»ãããåœç€Ÿã¡ã³ããŒãäœæããSafie API MCPããŒã«ã䜿ã£ãŠãã«ã¹ã±ãŒãã§MCPããŒã«ãå®è¡ããäŸãèŠãŠã¿ãŸãããã å
çšã®MCPããŒã«ãèªã¿èŸŒã¿ã€ã€ããæšæ¥ã®10æã®ç»åã«äººãæ ã£ãŠãããã©ãã調ã¹ãŠããšããããã³ãããå
¥åããŸãã å°ãæéæ
å ±ãå«ãŸããå Žåã¯ã¿ã€ã ãŸãŒã³ãè§£éãããããªããã³ããã¡ãã»ãŒãžãä»å ããŠããŸãã ãããªãé·ããªããŸãããLLMãžæããããã³ããã¯ä»¥äžã®éããšãªããŸãã messages = [ { "role" : "assistant" , "content" : "ããªãã¯æè»ã«å
¥åå
容ã顿šã§ããAIã¢ã·ã¹ã¿ã³ãã§ãã質åãžã®åçã¯åºæ¥ãéãå®çµã«åçããŠãããŸãã \n\n ## æ¥ææ
å ± \n çŸåšã®æ¥æ: 2025-12-22T13:51:54.528550+00:00 \n çŸåšã®æ¥ä»: 2025-12-22 \n\n çžå¯Ÿçãªæ¥ä»è¡šçŸïŒä» æ¥ãææ¥ãæšæ¥ãªã©ïŒã¯äžèšã®çŸå𿥿ãåºæºãšã㊠\n èšèªãã顿šã§ããã¿ã€ã ãŸãŒã³ã«å€æã®äž \n ISO 8601æ¡åŒµåœ¢åŒïŒã¿ã€ã ãŸãŒã³ä»ãïŒã§è§£éããŠãã ããã" }, { "role" : "user" , "content" : "æšæ¥ã®10æã®ç»åã«äººãæ ã£ãŠãããã©ãã調ã¹ãŠ" } ] tools = [ { "type" : "function" , "function" : { "name" : "list_devices" , "description" : " ã¢ã¯ã»ã¹æš©éã®ããããã€ã¹ã®äžèЧãååŸããŸã " , "parameters" : { "type" : "object" , "properties" : { "item_id" : { "type" : "integer" , "description" : "item_id parameter" , "default" : null } }, "required" : [] } } }, { "type" : "function" , "function" : { "name" : "get_device_image" , "description" : " æå®ãããããã€ã¹ããç»åãååŸããŸã timestampãæå®ããªãå ŽåãAPIå®è¡æç¹ã®ææ°ç»åãååŸã§ããŸã " , "parameters" : { "type" : "object" , "properties" : { "device_id" : { "type" : "string" , "description" : "device_id parameter" }, "timestamp" : { "type" : "string" , "description" : "timestamp parameter" , "default" : null } }, "required" : [ "device_id" ] } } }, { "type" : "function" , "function" : { "name" : "list_device_media" , "description" : " æå®ãããããã€ã¹ã§é²ç»ãããŠããæ åïŒã¡ãã£ã¢ïŒã®äžèЧãååŸããŸã timestampãæå®ããªãå ŽåãAPIå®è¡æç¹ã®ææ°ç»åãååŸã§ããŸã å¶é: - start/endã®ååŸæå€§ç¯å²ã¯ 1æ¥ïŒ86400ç§ïŒã§ã " , "parameters" : { "type" : "object" , "properties" : { "device_id" : { "type" : "string" , "description" : "device_id parameter" }, "start" : { "type" : "string" , "description" : "start parameter" }, "end" : { "type" : "string" , "description" : "end parameter" } }, "required" : [ "device_id" , "start" , "end" ] } } }, { "type" : "function" , "function" : { "name" : "get_device_location" , "description" : " æå®ãããããã€ã¹ã®çŸåšã®GPSäœçœ®æ
å ±ãååŸããŸããGPSã«å¯Ÿå¿ããŠããªãããã€ã¹ã§ã¯å©çšã§ããŸãã ããã€ã¹ã«æåèšå®ãããäœçœ®æ
å ±ãååŸããããšã¯ã§ããŸãã " , "parameters" : { "type" : "object" , "properties" : { "device_id" : { "type" : "string" , "description" : "device_id parameter" } }, "required" : [ "device_id" ] } } }, { "type" : "function" , "function" : { "name" : "get_device_thumbnail" , "description" : " æå®ãããããã€ã¹ã®ææ°ãµã ãã€ã«ãååŸããŸã " , "parameters" : { "type" : "object" , "properties" : { "device_id" : { "type" : "string" , "description" : "device_id parameter" } }, "required" : [ "device_id" ] } } }, { "type" : "function" , "function" : { "name" : "list_device_standard_events" , "description" : " æå®ãããããã€ã¹ã®æšæºã€ãã³ãæ
å ±äžèЧãååŸããŸã ãæšæºã€ãã³ãããšã¯ä»¥äžã®5ã€ã®ã€ãã³ãã®ç·ç§°ã§ã - æ¥ç¶æ€ç¥ - åææ€ç¥ - ã¢ãŒã·ã§ã³æ€ç¥ - ãµãŠã³ãæ€ç¥ - 人æ€ç¥ å¶é: - start/endã®ååŸæå€§ç¯å²ã¯ 1æ¥ïŒ86400ç§ïŒã§ã " , "parameters" : { "type" : "object" , "properties" : { "device_id" : { "type" : "string" , "description" : "device_id parameter" }, "start" : { "type" : "string" , "description" : "start parameter" }, "end" : { "type" : "string" , "description" : "end parameter" }, "event_types" : { "type" : "array" , "enum" : [ "connect" , "disconnect" , "motion" , "sound" , "person" ], "description" : "event_types parameter connect, disconnect, motion, sound, person" , "default" : null } }, "required" : [ "device_id" , "start" , "end" ] } } }, { "type" : "function" , "function" : { "name" : "greeting" , "description" : " ãããã€ã¡ãã»ãŒãžãè¿ããŸã " , "parameters" : { "type" : "object" , "properties" : { "message" : { "type" : "string" , "description" : "message parameter" , "default" : null } }, "required" : [] } } } ] å
¥åãæéæ
å ±ãå«ãããã³ããã®å Žåã¯ãã¿ã€ã ãŸãŒã³ããã©ãŒããããæå®ããã¡ãã»ãŒãžãè£å®ããŠããŸãã toolsãã©ã¡ãŒã¿ãé·ããªã£ãŠããŸããããã¡ãã¯ãµããŒãããMCPããŒã«ããªã¹ãåããŠèšå®ããŠããŸãã ãã¡ãã®ã¡ãã»ãŒãžã«å¯Ÿããã¬ã¹ãã³ã¹ã¯ä»¥äžãšãªããŸãã ChatCompletion( id = 'chatcmpl-211b219f-f4af-4e42-8ac1-c6195b2ed713' , choices=[ Choice( finish_reason= 'tool_calls' , index= 0 , logprobs= None , message=ChatCompletionMessage( content= 'æšæ¥ã®10æã®ç»åã調ã¹ãããã«ããŸãã¢ã¯ã»ã¹å¯èœãªããã€ã¹ã®äžèЧãååŸããŸãã' , refusal= None , role= 'assistant' , annotations= None , audio= None , function_call= None , tool_calls=[ ChatCompletionMessageFunctionToolCall( id = 'tooluse_QDchcs-OTn60bSHhwYflUw' , function=Function( arguments= '{}' , name= 'list_devices' ), type = 'function' , index= 1 ) ] ) ) ], XXXXXXXXXXXXXXXX ) LLMã¯æå®ãããããã³ãããå®è¡ããããã«ããŸãã¯ã¢ã¯ã»ã¹å¯èœãªã«ã¡ã©ã®äžèЧååŸãè¡ãã¹ããšå€æããlist_deviceAPIãå®è¡ããããã¬ã¹ãã³ã¹ãè¿ããŠãããŸããçŽ æŽãããæè»ã§ããïŒïŒ å
çšã®äŸãšåãã§ãå®è¡ãã¹ãMCPããŒã«ã®ã¡ãœãããæå®ãããŠããã®ã§ããã®ãšããã«å®è¡ããŸãã { 'result' : [ { 'device_id' : 'xxxxxxxx' , 'serial' : 'xxxxxxxx' , 'setting' : { 'name' : 'XXXXXXXX' }, 'status' : { 'video_streaming' : True }, 'model' : { 'description' : 'One (SF-1)' } } ] } æå®ãããéãã«MCPããŒã«ã®ã¡ãœãããå®è¡ãäžèšã®ã¬ã¹ãã³ã¹ãåŸãŸãã åæ§ã«ãã¡ãã®ã¬ã¹ãã³ã¹ãå«ããŠLLMã«ãã£ãããéä¿¡ããŸãã messages = [ { 'role' : 'assistant' , 'content' : 'ããªãã¯æè»ã«å
¥åå
容ã顿šã§ããAIã¢ã·ã¹ã¿ã³ãã§ãã質åãžã®åçã¯åºæ¥ãéãå®çµã«åçããŠãããŸãã \n\n ## æ¥ææ
å ± \n çŸåšã®æ¥æ: 2025-12-22T14:05:27.941094+00:00 \n çŸåšã®æ¥ä»: 2025-12-22 \n\n çžå¯Ÿçãªæ¥ä»è¡šçŸïŒä»æ¥ãææ¥ãæšæ¥ãªã©ïŒã¯äžèšã®çŸå𿥿ãåºæºãšã㊠\n èšèªãã顿šã§ããã¿ã€ã ãŸãŒã³ã«å€æã®äž \n ISO 8601æ¡åŒµåœ¢åŒïŒã¿ã€ã ãŸãŒã³ä»ãïŒã§è§£éããŠãã ããã' }, { 'role' : 'user' , 'content' : 'æšæ¥ã®10æã®ç»åã«äººãæ ã£ãŠãããã©ãã調ã¹ãŠ' }, { 'role' : 'assistant' , 'content' : 'æšæ¥ã®10æã®ç»åã調ã¹ãããã«ããŸãã¢ã¯ã»ã¹å¯èœãªããã€ã¹ã®äžèЧãååŸãããŠããã ããŸãã' , 'tool_calls' : [ { 'id' : 'tooluse_zVu7PAOFSHqr5GYrKxGM7A' , 'type' : 'function' , 'function' : { 'name' : 'list_devices' , 'arguments' : '{"item_id": null}' } } ] }, { 'role' : 'tool' , 'tool_call_id' : 'tooluse_zVu7PAOFSHqr5GYrKxGM7A' , 'content' : '{"result": [{"device_id": "xxxxxxxx", "serial": "xxxxxxxx", "setting": {"name": "XXXXXXXX"}, "status": {"video_streaming": true}, "model": {"description": "One (SF-1)"}}]}' } ] â»toolsã¯éè€ããã®ã§èšèŒãçããŸãã äžèšã¡ãã»ãŒãžãžã®ã¬ã¹ãã³ã¹ã¯ä»¥äžãšãªããŸãã ChatCompletion( id = 'chatcmpl-be9de40c-a119-4548-930a-b75abff8f898' , choices=[ Choice( finish_reason= 'tool_calls' , index= 0 , logprobs= None , message=ChatCompletionMessage( content= 'ããã€ã¹ã1å°èŠã€ãããŸãããæšæ¥ïŒ2025-12-21ïŒã®10æã®ç»åãååŸããŠç¢ºèªããŸãã' , refusal= None , role= 'assistant' , annotations= None , audio= None , function_call= None , tool_calls=[ ChatCompletionMessageFunctionToolCall( id = 'tooluse_dGHdMM4PQt-fAdNBmMTEhg' , function=Function( arguments= '{"device_id": "xxxxxxxx", "timestamp": "2025-12-21T10:00:00+09:00"}' , name= 'get_device_image' ), type = 'function' , index= 1 ) ] ) ) ], XXXXXXXXXXXXXXXX ) ãããšä»åºŠã¯tool_callsã«æ¬¡ã«å®è¡ãã¹ãMCPããŒã«ãæå®ãããŠããŸãã get_device_imageãæå®ã®ãã©ã¡ãŒã¿ä»ãã§å®è¡ãããšèšãããšã§ããã èšããããšããã«ããŒã«ãå®è¡ããŸãã get_device_imageã¯æå®ããããã©ã¡ãŒã¿ãå
ã«ããµã ãã€ã«ããŒã¿ãååŸããŸãã ååŸãããµã ãã€ã«ããŒã¿ãBase64圢åŒã«å€æããå
çšã®äŸãšåæ§ã«LLMã«ã¡ãã»ãŒãžãšããŠå
¥åããŸããïŒâ»ç»åããŒã¿ã¯éãã®ã§èšèŒããŸããïŒ messages = [ { 'role' : 'assistant' , 'content' : 'ããªãã¯æè»ã«å
¥åå
容ã顿šã§ããAIã¢ã·ã¹ã¿ã³ãã§ãã質åãžã®åçã¯åºæ¥ãéãå®çµã«åçããŠãããŸãã \n\n ## æ¥ææ
å ± \n çŸåšã®æ¥æ: 2025-12-22T14:05:27.941094+00:00 \n çŸåšã®æ¥ä»: 2025-12-22 \n\n çžå¯Ÿçãªæ¥ä»è¡šçŸïŒä»æ¥ãææ¥ãæšæ¥ãªã©ïŒã¯äžèšã®çŸå𿥿ãåºæºãšã㊠\n èšèªãã顿šã§ããã¿ã€ã ãŸãŒã³ã«å€æã®äž \n ISO 8601æ¡åŒµåœ¢åŒïŒã¿ã€ã ãŸãŒã³ä»ãïŒã§è§£éããŠãã ããã' }, { 'role' : 'user' , 'content' : 'æšæ¥ã®10æã®ç»åã«äººãæ ã£ãŠãããã©ãã調ã¹ãŠ' }, { 'role' : 'assistant' , 'content' : 'æšæ¥ã®10æã®ç»åã調ã¹ãããã«ããŸãã¢ã¯ã»ã¹å¯èœãªããã€ã¹ã®äžèЧãååŸãããŠããã ããŸãã' , 'tool_calls' : [ { 'id' : 'tooluse_zVu7PAOFSHqr5GYrKxGM7A' , 'type' : 'function' , 'function' : { 'name' : 'list_devices' , 'arguments' : '{"item_id": null}' } } ] }, { 'role' : 'tool' , 'tool_call_id' : 'tooluse_zVu7PAOFSHqr5GYrKxGM7A' , 'content' : '{"result": [{"device_id": "xxxxxxxx", "serial": "xxxxxxxx", "setting": {"name": "XXXXXXXX"}, "status": {"video_streaming": true}, "model": {"description": "One (SF-1)"}}]}' }, { 'role' : 'assistant' , 'content' : 'ããã€ã¹ã1å°èŠã€ãããŸãããæšæ¥ïŒ2025-12-21ïŒã®10æã®ç»åãååŸããŠç¢ºèªããŸãã' , 'tool_calls' : [ { 'id' : 'tooluse_dGHdMM4PQt-fAdNBmMTEhg' , 'type' : 'function' , 'function' : { 'name' : 'get_device_image' , 'arguments' : '{"device_id": "xxxxxxxx", "timestamp": "2025-12-21T10:00:00+09:00"}' } } ] }, { 'role' : 'tool' , 'tool_call_id' : 'tooluse_dGHdMM4PQt-fAdNBmMTEhg' , 'content' : 'get_device_image' }, { 'role' : 'user' , 'content' : [ { 'type' : 'image_url' , 'image_url' : { 'url' : 'data:image/jpeg;base64,XXXXXXXX' } }, { 'type' : 'text' , 'text' : '以éã®MCPããŒã«åŒã³åºãã§ç»åãå¿
èŠãªå Žåã¯ãç»åããŒã¿ãMCPããŒã«ã®argumentsã«èšå®ããŠãã ããã' } ] } ] â»toolsã¯éè€ããã®ã§èšèŒãçããŸã ã¬ã¹ãã³ã¹ã¯ä»¥äžã®éãã§ãã ChatCompletion( id = 'chatcmpl-57a6d457-306a-4f27-b14e-a4696f11172d' , choices=[ Choice( finish_reason= 'stop' , index= 0 , logprobs= None , message=ChatCompletionMessage( content= 'æšæ¥ïŒ2025-12-21ïŒã®10æé ã®ç»åã確èªããŸããã \n\n ç»åãèŠãéãã**äººã¯æ ã£ãŠããŸãã**ã \n\n ç»åã«ã¯ä»¥äžãåã£ãŠããŸãïŒ \n - èµ€ãèªåè²©å£²æ© \n - ååãé³åãããæšè£œã®æ£ \n - çœãã¿ã€ã«åŒµãã®åº \n - å³äžã«çœãæ©åšïŒãšã¢ã³ã³ã®å®€å€æ©ã®ãããªãã®ïŒ \n\n åºèãäºåæã®ãããªå Žæã®ç£èŠã«ã¡ã©ã®æ åã®ããã§ããããã®æç¹ã§ã¯äººã®å§¿ã¯ç¢ºèªã§ããŸããã§ããã' , refusal= None , role= 'assistant' , annotations= None , audio= None , function_call= None , tool_calls= None ) ) ], XXXXXXXXXXXXXXXX ) ãã以äžMCPããŒã«ãå®è¡ããå¿
èŠãç¡ãã®ã§ãtool_callsã¯Noneãšãªã£ãŠããŸãã ãŸããLLMã¯èšå®ããç»åããŒã¿ãããŒã¹ã«é©åã«ããã³ããã§æå®ãããå
容ãåŠçããã¬ã¹ãã³ã¹ãè¿ããŠããŸãã å
¥åãããããã³ããã«åºã¥ããŠãMCPãµãŒããŒãèšå®ãã€ã€æé©ãªã¬ã¹ãã³ã¹ãå°ãåºãææ³ã¯äžèšã®ç¹°ãè¿ãã§ãã ãããã£ãŠã¿ããšé©åãªæ
å ±ãã»ããããŠããå¿
èŠã¯ãããŸããããããã®æ
å ±ãå
ã«LLMãåŠäœã«æè»ã«ããã¹ãæ
å ±ãåŠçããå¿
èŠã«å¿ãæé©ãªMCPããŒã«ãéžæãã¬ã¹ãã³ã¹ãçæããŠããããããåãããŸãã åæ§ã«æ§ã
ãªãµãŒãã¹ã®MCPããŒã«ãçµã¿èŸŒãã ãã§ã容æã«LLMãä»ãããµãŒãã¹éèªå飿ºãå®çŸã§ããŸãã MCPããŒã«ã®æ
å ±ãLLMã«èšå®ããã«ã¯ åã¡ãœããã®æ
å ±ãæœåºããç»é²ããå¿
èŠããããŸãã åèçšåºŠã§ãããè»œãæžããŠã¿ãã®ã§è²Œã£ãŠãããŸãã è€æ°ã®MCPãµãŒããŒãçµ±å管çã§ããããã«ãããŠã¿ãŸããã import json import logging from mcp.server.fastmcp import FastMCP class MCP_Manager (): def __init__ (self): #self._mcps = {} self._tools = {} self.logger = logging.getLogger(__name__) async def register_tool (self, mcp: FastMCP): """æå®ãããMCPãµãŒããŒãMcpManagerã«ç»é²ãã""" for tool in await mcp.list_tools(): doc = tool.description description = doc.replace( ' \n ' , '' ) if doc else f "{tool.name}" properties = {} # ç°¡åãªãã©ã¡ãŒã¿æšè«ïŒå®éã«ã¯ãã詳现ãªå®è£
ãå¿
èŠïŒ required = tool.inputSchema.get( "required" , []) for param_name, param in tool.inputSchema[ "properties" ].items(): self.logger.info(f "Param : {param}" ) if param_name != 'self' : param_type = "string" # ããã©ã«ã # äžæŠå°éã«å®è£
ããïŒãã£ãšããããæ¹ããããšæããïŒ _any = param.get( "anyOf" ) if _any is not None : param = _any[ 0 ] if "items" in param.keys(): def_name = param[ "items" ][ "$ref" ].split( "/" )[- 1 ] properties[param_name] = { "type" : param[ "type" ], "enum" : tool.inputSchema[ "$defs" ][def_name][ "enum" ], "description" : f "{param_name} parameter {', '.join(tool.inputSchema[" $defs "][def_name][" enum "])}" } else : properties[param_name] = { "type" : param[ "type" ], "description" : param[ "description" ] if description in param.keys() else f "{param_name} parameter" , } else : if param[ "type" ] == int : param_type = "integer" elif param[ "type" ] == float : param_type = "number" elif param[ "type" ] == bool : param_type = "boolean" properties[param_name] = { "type" : param_type, "description" : param[ "description" ] if description in param.keys() else f "{param_name} parameter" , } if param_name not in required: # Defaultå€ã¯çŸç¶ã¯åèçšåºŠã«ãããªããªããäžå¿ã»ãã properties[param_name][ "default" ] = param.get( "default" , None ) self._tools[tool.name] = { "mcp" : mcp, "name" : tool.name, "description" : description, "properties" : properties, "required" : required } self.logger.info(f "Registered tool: {tool.name} with properties: {properties} and required: {required}" ) @ property def tools (self): """ç»é²ãããããŒã«ã®äžèЧãBedrock API仿§ã«æºæ ãã圢ã§è¿åŽãã""" return [{ "type" : "function" , "function" : { "name" : v[ "name" ], "description" : v[ "description" ], "parameters" : { "type" : "object" , "properties" : v[ "properties" ], "required" : v[ "required" ] } }} for k,v in self._tools.items()] async def call_tool ( self, tool_name: str , tool_args: dict ): """æå®ãããããŒã«ãå®è¡ããçµæãè¿ã""" self.logger.info(f "Function call detected: {tool_name}" ) # FastMCPããŒã«ãå®è¡ if tool_name in self._tools.keys(): try : # ãã©ã¡ãŒã¿ãŒã®è£å®ãè¡ã (defaultå€ã¯claudeã§ã¯è£å®ãããªã) for k,v in self._tools[tool_name][ "properties" ].items(): if k not in tool_args.keys(): if "default" in v: tool_args[k] = v[ "default" ] self.logger.info(f "Executing tool: {tool_name} with args {tool_args}" ) # åŸã§å¿
èŠæ§ã«å¿ãä¿®æ£ãã tool_result = await self._tools[tool_name][ "mcp" ].call_tool( tool_name, tool_args ) self.logger.info(f "Tool Result: {tool_result} Type: {type(tool_result)} Len: {len(tool_result)}" ) return tool_result except Exception as e: self.logger.error(f "Tool execution error: {e}" ) error_message = f "ããŒã« '{tool_name}' ã®å®è¡äžã«ãšã©ãŒãçºçããŸãã: {str(e)}" #return error_message raise Exception (error_message) return {} ãŸãšã MCPã®æŽ»çšã«ãããæ§ã
ãªããŒã«ããµãŒãã¹ãæè»äžã€çµ±äžçã«æŽ»çšããäºãå¯èœãšãªããŸãã æŽã«æè¿ã§ã¯ããäžäœã®ã¬ã€ã€ãŒã§ã®ã³ãã¥ãã±ãŒã·ã§ã³ãå¯èœãšãªããšãŒãžã§ã³ãéã³ãã¥ãã±ãŒã·ã§ã³ã»ãããã³ã«ãæµè¡ã£ãŠããŠããã äž»èŠãªåè£ãšããŠACPãA2AãååšããŠããŸãããä»åŸA2Aã«çµ±åããããããªè©±ãããããã§ãã ãããã®æŽ»çšã«ãããçæAIã®æŽ»çšã®å¹
ãæŽã«åºããäºãæåŸ
ãããŸãã å°ãäžèšã®å®è£
äŸãçæAIãæŽ»çšããŠäœæããŠããŸããïŒæ¬åœã«äŸ¿å©ã§ãïŒ äžæ¹ã§ãã®ãŸãŸã§ã¯æŽ»çšã§ããã现ããªä¿®æ£ãå ããå¿
èŠã¯ãããŸããã â»ãã¯ãç²ç®çã«äœ¿ãã®ã§ã¯ãªããå
容ãçè§£ãã€ã€é©åã«æŽ»çšãã¹ãã ãšæ¹ããŠæããŠããŸãã ææã« ã»ãŒãã£ãŒã§ã¯éçºå¹çåäžããããã¯ãã®äŸ¡å€åäžã«åããŠãç©æ¥µçã«çæAI掻çšã®æ€èšãé²ããŠããŸãã ææ°ã®æè¡ãçµã¿åããã€ã€ãæŽãªãæé·ãžåããŠæ§ã
ãªéçºã«é¢ããåãçµã¿ãè¡ã£ãŠããŸãããããã«äžç·ã«é¢ãã£ãŠããããšã³ãžãã¢ãããçµ¶è³åéããŠããŸãïŒïŒïŒïŒ safie.co.jp