Skip to content

Commit 0f616f5

Browse files
authored
Merge pull request #89 from ks6088ts-labs/feature/issue-63_add-mcp-tool
add mcp tool
2 parents 885da46 + 16ba441 commit 0f616f5

File tree

10 files changed

+153
-24
lines changed

10 files changed

+153
-24
lines changed

.env.template

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,19 @@ COSMOSDB_CONTAINER_NAME="kabuto"
4545
COSMOSDB_PARTITION_KEY="/id"
4646

4747
# SQL Database Settings
48-
SQL_DATABASE_URI="sqlite:///template_langgraph.db"
49-
# SQL_DATABASE_URI="postgresql://user:password@localhost:5432/db"
48+
SQL_DATABASE_URI=""
49+
# SQL_DATABASE_URI="sqlite:///template_langgraph.db" # SQLite
50+
# SQL_DATABASE_URI="postgresql://user:password@localhost:5432/db" # PostgreSQL
5051

5152
# Azure AI Search Settings
5253
AI_SEARCH_ENDPOINT="https://xxx.search.windows.net/"
5354
AI_SEARCH_KEY="xxx"
5455
AI_SEARCH_INDEX_NAME="kabuto"
5556

57+
# MCP Settings
58+
MCP_CONFIG_PATH=""
59+
# MCP_CONFIG_PATH="./.vscode/mcp.json" # VS Code
60+
5661
# ---------
5762
# Internals
5863
# ---------

.vscode/mcp.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"servers": {
3+
"playwright": {
4+
"command": "npx",
5+
"args": ["-y", "@playwright/mcp@latest", "--vision"]
6+
},
7+
"filesystem": {
8+
"command": "npx",
9+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "./assets"]
10+
},
11+
"everything": {
12+
"command": "npx",
13+
"args": ["-y", "@modelcontextprotocol/server-everything"]
14+
}
15+
}
16+
}

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ streamlit: ## run Streamlit
133133
uv run streamlit run \
134134
template_langgraph/services/streamlits/main.py
135135

136+
.PHONY: mcp-insppector
137+
mcp-inspector: ## run MCP Inspector server
138+
npx -y @modelcontextprotocol/inspector
139+
136140
# ---
137141
# Project / Create indices
138142
# ---

docs/references.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
- [🤖 LangGraph Multi-Agent Supervisor](https://github.com/langchain-ai/langgraph-supervisor-py)
88
- [Software Design 誌「実践 LLM アプリケーション開発」第 24 回サンプルコード](https://github.com/mahm/softwaredesign-llm-application/tree/main/24)
99
- [Streamlit](https://python.langchain.com/docs/integrations/callbacks/streamlit/)
10+
- [LangChain MCP Adapters](https://github.com/langchain-ai/langchain-mcp-adapters)
11+
- [Research Agent with MCP Integration.](https://github.com/langchain-ai/deep_research_from_scratch/blob/main/src/deep_research_from_scratch/research_agent_mcp.py)
1012

1113
### Sample Codes
1214

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies = [
1313
"httpx>=0.28.1",
1414
"langchain-azure-ai>=0.1.4",
1515
"langchain-community>=0.3.27",
16+
"langchain-mcp-adapters>=0.1.9",
1617
"langchain-ollama>=0.3.6",
1718
"langchain-openai>=0.3.28",
1819
"langchain-text-splitters>=0.3.9",

template_langgraph/agents/chat_with_tools_agent/agent.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import asyncio
12
import json
23

34
from langchain_core.messages import ToolMessage
@@ -6,7 +7,7 @@
67
from template_langgraph.agents.chat_with_tools_agent.models import AgentState
78
from template_langgraph.llms.azure_openais import AzureOpenAiWrapper
89
from template_langgraph.loggers import get_logger
9-
from template_langgraph.tools.common import get_default_tools
10+
from template_langgraph.tools.common import get_default_tools, is_async_call_required
1011

1112
logger = get_logger(__name__)
1213

@@ -25,10 +26,13 @@ def __call__(self, inputs: dict):
2526
outputs = []
2627
for tool_call in message.tool_calls:
2728
try:
28-
tool_result = self.tools_by_name[tool_call["name"]].invoke(tool_call["args"])
29+
if is_async_call_required(tool_call["name"]):
30+
observation = asyncio.run(self.tools_by_name[tool_call["name"]].ainvoke(tool_call["args"]))
31+
else:
32+
observation = self.tools_by_name[tool_call["name"]].invoke(tool_call["args"])
2933
outputs.append(
3034
ToolMessage(
31-
content=json.dumps(tool_result.__str__(), ensure_ascii=False),
35+
content=json.dumps(observation.__str__(), ensure_ascii=False),
3236
name=tool_call["name"],
3337
tool_call_id=tool_call["id"],
3438
)

template_langgraph/tools/common.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,35 @@
44
from template_langgraph.tools.cosmosdb_tool import search_cosmosdb
55
from template_langgraph.tools.dify_tool import run_dify_workflow
66
from template_langgraph.tools.elasticsearch_tool import search_elasticsearch
7+
from template_langgraph.tools.mcp_tool import McpClientWrapper
78
from template_langgraph.tools.qdrant_tool import search_qdrant
89
from template_langgraph.tools.sql_database_tool import SqlDatabaseClientWrapper
910

1011
logger = get_logger(__name__)
1112

1213

1314
def get_default_tools():
14-
try:
15-
sql_database_tools = SqlDatabaseClientWrapper().get_tools(
15+
return (
16+
[
17+
search_ai_search,
18+
search_cosmosdb,
19+
run_dify_workflow,
20+
search_qdrant,
21+
search_elasticsearch,
22+
]
23+
+ SqlDatabaseClientWrapper().get_tools(
1624
llm=AzureOpenAiWrapper().chat_model,
1725
)
18-
except Exception as e:
19-
logger.error(f"Error occurred while getting SQL database tools: {e}")
20-
sql_database_tools = []
21-
return [
22-
search_ai_search,
23-
search_cosmosdb,
24-
run_dify_workflow,
25-
search_qdrant,
26-
search_elasticsearch,
27-
] + sql_database_tools
26+
+ McpClientWrapper().get_tools()
27+
)
28+
29+
30+
def is_async_call_required(tool_name: str) -> bool:
31+
# FIXME: adhoc impl
32+
if tool_name.startswith("browser_"):
33+
return True
34+
return tool_name in [
35+
"echo",
36+
"add",
37+
# add async tool names here
38+
]
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import asyncio
2+
import json
3+
from functools import lru_cache
4+
5+
from langchain_core.tools.base import BaseTool
6+
from langchain_mcp_adapters.client import MultiServerMCPClient
7+
from pydantic_settings import BaseSettings, SettingsConfigDict
8+
9+
10+
class Settings(BaseSettings):
11+
mcp_config_path: str = ""
12+
13+
model_config = SettingsConfigDict(
14+
env_file=".env",
15+
env_ignore_empty=True,
16+
extra="ignore",
17+
)
18+
19+
20+
@lru_cache
21+
def get_mcp_settings() -> Settings:
22+
"""Get mcp settings."""
23+
return Settings()
24+
25+
26+
class McpClientWrapper:
27+
def __init__(
28+
self,
29+
settings: Settings = None,
30+
):
31+
if settings is None:
32+
settings = get_mcp_settings()
33+
self.settings = settings
34+
35+
def get_tools(self) -> list[BaseTool]:
36+
if self.settings.mcp_config_path == "":
37+
return []
38+
with open(self.settings.mcp_config_path) as f:
39+
config = json.load(f)
40+
for _, value in config["servers"].items():
41+
value["transport"] = "stdio"
42+
client = MultiServerMCPClient(config["servers"])
43+
tools = asyncio.run(client.get_tools())
44+
return tools

template_langgraph/tools/sql_database_tool.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010
class Settings(BaseSettings):
11-
sql_database_uri: str = "sqlite:///template_langgraph.db"
11+
sql_database_uri: str = ""
1212

1313
model_config = SettingsConfigDict(
1414
env_file=".env",
@@ -30,15 +30,19 @@ def __init__(
3030
):
3131
if settings is None:
3232
settings = get_sql_database_settings()
33-
self.db = SQLDatabase.from_uri(
34-
database_uri=settings.sql_database_uri,
35-
)
33+
self.settings = settings
3634

3735
def get_tools(
3836
self,
3937
llm: BaseLanguageModel,
4038
) -> list[BaseTool]:
4139
"""Get SQL Database tools."""
40+
if self.settings.sql_database_uri == "":
41+
return []
42+
43+
self.db = SQLDatabase.from_uri(
44+
database_uri=self.settings.sql_database_uri,
45+
)
4246
return SQLDatabaseToolkit(
4347
db=self.db,
4448
llm=llm,

uv.lock

Lines changed: 41 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)