Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,19 @@ COSMOSDB_CONTAINER_NAME="kabuto"
COSMOSDB_PARTITION_KEY="/id"

# SQL Database Settings
SQL_DATABASE_URI="sqlite:///template_langgraph.db"
# SQL_DATABASE_URI="postgresql://user:password@localhost:5432/db"
SQL_DATABASE_URI=""
# SQL_DATABASE_URI="sqlite:///template_langgraph.db" # SQLite
# SQL_DATABASE_URI="postgresql://user:password@localhost:5432/db" # PostgreSQL

# Azure AI Search Settings
AI_SEARCH_ENDPOINT="https://xxx.search.windows.net/"
AI_SEARCH_KEY="xxx"
AI_SEARCH_INDEX_NAME="kabuto"

# MCP Settings
MCP_CONFIG_PATH=""
# MCP_CONFIG_PATH="./.vscode/mcp.json" # VS Code

# ---------
# Internals
# ---------
Expand Down
16 changes: 16 additions & 0 deletions .vscode/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"servers": {
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest", "--vision"]
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "./assets"]
},
"everything": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-everything"]
}
}
}
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ streamlit: ## run Streamlit
uv run streamlit run \
template_langgraph/services/streamlits/main.py

.PHONY: mcp-insppector
mcp-inspector: ## run MCP Inspector server
npx -y @modelcontextprotocol/inspector

# ---
# Project / Create indices
# ---
Expand Down
2 changes: 2 additions & 0 deletions docs/references.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
- [🤖 LangGraph Multi-Agent Supervisor](https://github.com/langchain-ai/langgraph-supervisor-py)
- [Software Design 誌「実践 LLM アプリケーション開発」第 24 回サンプルコード](https://github.com/mahm/softwaredesign-llm-application/tree/main/24)
- [Streamlit](https://python.langchain.com/docs/integrations/callbacks/streamlit/)
- [LangChain MCP Adapters](https://github.com/langchain-ai/langchain-mcp-adapters)
- [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)

### Sample Codes

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dependencies = [
"httpx>=0.28.1",
"langchain-azure-ai>=0.1.4",
"langchain-community>=0.3.27",
"langchain-mcp-adapters>=0.1.9",
"langchain-ollama>=0.3.6",
"langchain-openai>=0.3.28",
"langchain-text-splitters>=0.3.9",
Expand Down
10 changes: 7 additions & 3 deletions template_langgraph/agents/chat_with_tools_agent/agent.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
import json

from langchain_core.messages import ToolMessage
Expand All @@ -6,7 +7,7 @@
from template_langgraph.agents.chat_with_tools_agent.models import AgentState
from template_langgraph.llms.azure_openais import AzureOpenAiWrapper
from template_langgraph.loggers import get_logger
from template_langgraph.tools.common import get_default_tools
from template_langgraph.tools.common import get_default_tools, is_async_call_required

logger = get_logger(__name__)

Expand All @@ -25,10 +26,13 @@ def __call__(self, inputs: dict):
outputs = []
for tool_call in message.tool_calls:
try:
tool_result = self.tools_by_name[tool_call["name"]].invoke(tool_call["args"])
if is_async_call_required(tool_call["name"]):
observation = asyncio.run(self.tools_by_name[tool_call["name"]].ainvoke(tool_call["args"]))
else:
observation = self.tools_by_name[tool_call["name"]].invoke(tool_call["args"])
outputs.append(
ToolMessage(
content=json.dumps(tool_result.__str__(), ensure_ascii=False),
content=json.dumps(observation.__str__(), ensure_ascii=False),
name=tool_call["name"],
tool_call_id=tool_call["id"],
)
Expand Down
35 changes: 23 additions & 12 deletions template_langgraph/tools/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,35 @@
from template_langgraph.tools.cosmosdb_tool import search_cosmosdb
from template_langgraph.tools.dify_tool import run_dify_workflow
from template_langgraph.tools.elasticsearch_tool import search_elasticsearch
from template_langgraph.tools.mcp_tool import McpClientWrapper
from template_langgraph.tools.qdrant_tool import search_qdrant
from template_langgraph.tools.sql_database_tool import SqlDatabaseClientWrapper

logger = get_logger(__name__)


def get_default_tools():
try:
sql_database_tools = SqlDatabaseClientWrapper().get_tools(
return (
[
search_ai_search,
search_cosmosdb,
run_dify_workflow,
search_qdrant,
search_elasticsearch,
]
+ SqlDatabaseClientWrapper().get_tools(
llm=AzureOpenAiWrapper().chat_model,
)
except Exception as e:
logger.error(f"Error occurred while getting SQL database tools: {e}")
sql_database_tools = []
return [
search_ai_search,
search_cosmosdb,
run_dify_workflow,
search_qdrant,
search_elasticsearch,
] + sql_database_tools
+ McpClientWrapper().get_tools()
)


def is_async_call_required(tool_name: str) -> bool:
# FIXME: adhoc impl
if tool_name.startswith("browser_"):
return True
return tool_name in [
"echo",
"add",
# add async tool names here
]
44 changes: 44 additions & 0 deletions template_langgraph/tools/mcp_tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import asyncio
import json
from functools import lru_cache

from langchain_core.tools.base import BaseTool
from langchain_mcp_adapters.client import MultiServerMCPClient
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
mcp_config_path: str = ""

model_config = SettingsConfigDict(
env_file=".env",
env_ignore_empty=True,
extra="ignore",
)


@lru_cache
def get_mcp_settings() -> Settings:
"""Get mcp settings."""
return Settings()


class McpClientWrapper:
def __init__(
self,
settings: Settings = None,
):
if settings is None:
settings = get_mcp_settings()
self.settings = settings

def get_tools(self) -> list[BaseTool]:
if self.settings.mcp_config_path == "":
return []
with open(self.settings.mcp_config_path) as f:
config = json.load(f)
for _, value in config["servers"].items():
value["transport"] = "stdio"
client = MultiServerMCPClient(config["servers"])
tools = asyncio.run(client.get_tools())
return tools
12 changes: 8 additions & 4 deletions template_langgraph/tools/sql_database_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


class Settings(BaseSettings):
sql_database_uri: str = "sqlite:///template_langgraph.db"
sql_database_uri: str = ""

model_config = SettingsConfigDict(
env_file=".env",
Expand All @@ -30,15 +30,19 @@ def __init__(
):
if settings is None:
settings = get_sql_database_settings()
self.db = SQLDatabase.from_uri(
database_uri=settings.sql_database_uri,
)
self.settings = settings

def get_tools(
self,
llm: BaseLanguageModel,
) -> list[BaseTool]:
"""Get SQL Database tools."""
if self.settings.sql_database_uri == "":
return []

self.db = SQLDatabase.from_uri(
database_uri=self.settings.sql_database_uri,
)
return SQLDatabaseToolkit(
db=self.db,
llm=llm,
Expand Down
44 changes: 41 additions & 3 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.