Skip to content

Commit 3b5232c

Browse files
seanzhougooglecopybara-github
authored andcommitted
feat: add sample mcp agent that connects to mcp server via sse endpoint directly
PiperOrigin-RevId: 760388717
1 parent 9e767b3 commit 3b5232c

File tree

6 files changed

+161
-0
lines changed

6 files changed

+161
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
This agent connects to a local MCP server via sse.
2+
3+
To run this agent, start the local MCP server first by :
4+
5+
```bash
6+
uv run filesystem_server.py
7+
```
8+
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
import os
17+
18+
from google.adk.agents.llm_agent import LlmAgent
19+
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
20+
from google.adk.tools.mcp_tool.mcp_toolset import SseServerParams
21+
22+
_allowed_path = os.path.dirname(os.path.abspath(__file__))
23+
24+
root_agent = LlmAgent(
25+
model='gemini-2.0-flash',
26+
name='enterprise_assistant',
27+
instruction=f"""\
28+
Help user accessing their file systems.
29+
30+
Allowed directory: {_allowed_path}
31+
""",
32+
tools=[
33+
MCPToolset(
34+
connection_params=SseServerParams(
35+
url='http://localhost:3000/sse',
36+
headers={'Accept': 'text/event-stream'},
37+
),
38+
# don't want agent to do write operation
39+
# you can also do below
40+
# tool_filter=lambda tool, ctx=None: tool.name
41+
# not in [
42+
# 'write_file',
43+
# 'edit_file',
44+
# 'create_directory',
45+
# 'move_file',
46+
# ],
47+
tool_filter=[
48+
'read_file',
49+
'read_multiple_files',
50+
'list_directory',
51+
'directory_tree',
52+
'search_files',
53+
'get_file_info',
54+
'list_allowed_directories',
55+
],
56+
)
57+
],
58+
)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import asyncio
16+
import os
17+
from pathlib import Path
18+
import sys
19+
from mcp.server.fastmcp import FastMCP
20+
21+
# Create an MCP server with a name
22+
mcp = FastMCP("Filesystem Server", host="localhost", port=3000)
23+
24+
25+
# Add a tool to read file contents
26+
@mcp.tool(description="Read contents of a file")
27+
def read_file(filepath: str) -> str:
28+
"""Read and return the contents of a file."""
29+
with open(filepath, "r") as f:
30+
return f.read()
31+
32+
33+
# Add a tool to list directory contents
34+
@mcp.tool(description="List contents of a directory")
35+
def list_directory(dirpath: str) -> list:
36+
"""List all files and directories in the given directory."""
37+
return os.listdir(dirpath)
38+
39+
40+
# Add a tool to get current working directory
41+
@mcp.tool(description="Get current working directory")
42+
def get_cwd() -> str:
43+
"""Return the current working directory."""
44+
return str(Path.cwd())
45+
46+
47+
# Graceful shutdown handler
48+
async def shutdown(signal, loop):
49+
"""Cleanup tasks tied to the service's shutdown."""
50+
print(f"\nReceived exit signal {signal.name}...")
51+
52+
# Get all running tasks
53+
tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
54+
55+
# Cancel all tasks
56+
for task in tasks:
57+
task.cancel()
58+
59+
print(f"Cancelling {len(tasks)} outstanding tasks")
60+
await asyncio.gather(*tasks, return_exceptions=True)
61+
62+
# Stop the loop
63+
loop.stop()
64+
print("Shutdown complete!")
65+
66+
67+
# Main entry point with graceful shutdown handling
68+
if __name__ == "__main__":
69+
try:
70+
# The MCP run function ultimately uses asyncio.run() internally
71+
mcp.run(transport="sse")
72+
except KeyboardInterrupt:
73+
print("\nServer shutting down gracefully...")
74+
# The asyncio event loop has already been stopped by the KeyboardInterrupt
75+
print("Server has been shut down.")
76+
except Exception as e:
77+
print(f"Unexpected error: {e}")
78+
sys.exit(1)
79+
finally:
80+
print("Thank you for using the Filesystem MCP Server!")
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from . import agent

0 commit comments

Comments
 (0)