Skip to content

Commit 5990638

Browse files
committed
Progressing with agent-testing. Refactored mcp to have a wrapper run call. Verified simplest agent answering. Implemented new secret loading for google api
1 parent f3b43ad commit 5990638

File tree

6 files changed

+108
-73
lines changed

6 files changed

+108
-73
lines changed

agent-test/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
config.yaml
2+
*.secrets.yaml
3+
*.env

agent-test/mcp-agent.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
google:
2+
default_model: gemini-2.5-flash
3+
api_key: "${GOOGLE_API_KEY}"
4+
vertexai: false

agent-test/spendee_agent.py

100644100755
Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
#!/usr/bin/env python3
2+
13
import asyncio
24
import os
35

6+
import dotenv
47
from mcp_agent.app import MCPApp
58
from mcp_agent.agents.agent import Agent
6-
from mcp_agent.workflows.llm.augmented_llm_openai import GoogleAugmentedLLM
9+
from mcp_agent.workflows.llm.augmented_llm_google import GoogleAugmentedLLM
10+
11+
12+
13+
dotenv.load_dotenv()
714

815
app = MCPApp(name="hello_world_agent")
916

@@ -12,7 +19,7 @@ async def example_usage():
1219
logger = mcp_agent_app.logger
1320
# This agent can read the filesystem or fetch URLs
1421
finder_agent = Agent(
15-
name="Lumi-test",
22+
name="test",
1623
instruction="""You are a helpful assistant""",
1724
#server_names=["fetch", "filesystem"], # MCP servers this Agent can use
1825
)

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ pytest-asyncio
1616
python-dotenv
1717

1818
# agent
19-
mcp-agent
19+
mcp-agent[google]

setup.sh

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ set -euo pipefail
33

44
cd "$(dirname "$0")"
55

6-
REQUIRED_PASSWORD="spendee-password"
7-
86
# --- Mise environment setup ---
97
if ! command -v curl >/dev/null 2>&1; then
108
echo "curl not found. Installing curl via apt..."
@@ -42,21 +40,47 @@ mise install
4240

4341

4442
# --- Bitwarden credential setup ---
43+
44+
function load_spendee_env() {
45+
REQUIRED_PASSWORD="spendee-password"
46+
if ! bws secret list | jq -r '.[] | .key' | grep -qx "$REQUIRED_PASSWORD"; then
47+
echo "Secret key '$REQUIRED_PASSWORD' not found in Bitwarden secrets. Verify if access key is right, or access to the project/key was set."
48+
exit 1
49+
fi
50+
PASSWORD=$(bws secret list | jq -r '.[] | select(.key == "'$REQUIRED_PASSWORD'") | .value')
51+
EMAIL_LINE=$(bws secret list | jq -r '.[] | select(.key == "'$REQUIRED_PASSWORD'") | .note') # example value: EMAIL=lumi.8.lumiere@gmail.com
52+
53+
echo "export PASSWORD='${PASSWORD}'" > .env
54+
echo "export ${EMAIL_LINE}" >> .env
55+
}
56+
57+
function load_mcp_agent_env() {
58+
REQUIRED_API_KEY="GOOGLE_API_KEY"
59+
if ! bws secret list | jq -r '.[] | .key' | grep -qx "$REQUIRED_API_KEY"; then
60+
echo "Secret key '$REQUIRED_API_KEY' not found in Bitwarden secrets. Verify if access key is right, or access to the project/key was set."
61+
exit 1
62+
fi
63+
GOOGLE_API_KEY=$(bws secret list | jq -r '.[] | select(.key == "'$REQUIRED_API_KEY'") | .value')
64+
echo "GOOGLE_API_KEY='${GOOGLE_API_KEY}'" > agent-test/.env
65+
echo "GGOOGLE_API_KEY set in agent-test/.env"
66+
}
67+
4568
if [ ! -f ".env" ]; then
4669
if [ -z "${BWS_ACCESS_TOKEN:-}" ]; then
4770
echo "Neither .env file is set, nor BWS_ACCESS_TOKEN environment variable is not set or empty. Setup can not be complete without them."
4871
exit 1
4972
fi
5073
echo ".env file does not exist, loading it via Bitwarden"
51-
if ! bws secret list | jq -r '.[] | .key' | grep -qx "$REQUIRED_PASSWORD"; then
52-
echo "Secret key '$REQUIRED_PASSWORD' not found in Bitwarden secrets. Verify if access key is right, or access to the project/key was set."
74+
load_spendee_env
75+
fi
76+
77+
if [ ! -f "agent-test/.env" ]; then
78+
if [ -z "${BWS_ACCESS_TOKEN:-}" ]; then
79+
echo "Neither .env file is set, nor BWS_ACCESS_TOKEN environment variable is not set or empty. Setup can not be complete without them."
5380
exit 1
5481
fi
55-
PASSWORD=$(bws secret list | jq -r '.[] | select(.key == "'$REQUIRED_PASSWORD'") | .value')
56-
EMAIL_LINE=$(bws secret list | jq -r '.[] | select(.key == "'$REQUIRED_PASSWORD'") | .note') # example value: EMAIL=lumi.8.lumiere@gmail.com
57-
58-
echo "PASSWORD='${PASSWORD}'" > .env
59-
echo "${EMAIL_LINE}" >> .env
82+
echo "agent-test/.env file does not exist, loading it via Bitwarden"
83+
load_mcp_agent_env
6084
fi
6185

6286

@@ -70,6 +94,6 @@ echo "Activate the python virtual environment"
7094
source .venv/bin/activate
7195

7296
echo "Install dependencies"
73-
pip install -r requirements.txt
97+
#pip install -r requirements.txt
7498

7599
echo "Setup completed!"

spendee/spendee_mcp.py

Lines changed: 58 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -33,46 +33,63 @@
3333
# setup for "Transport Type": "SSE"
3434
# "Server URL" to "http://localhost:8000/sse"
3535

36-
EMAIL = os.getenv('EMAIL')
37-
PASSWORD = os.getenv('PASSWORD')
3836

39-
ACCEPTED_TOKEN = os.environ.get("MCP_TOKEN", "spendee-token")
40-
PORT = int(os.environ.get("MCP_PORT", 8000))
4137
DEBUG_MODE = os.environ.get("DEBUG_MODE", "") != ""
42-
TRANSFER_MODE = os.environ.get("TRANSFER_MODE", "sse").lower()
43-
44-
45-
if TRANSFER_MODE not in ["sse", "streamable-http"]:
46-
raise ValueError("TRANSFER_MODE must be either 'sse' or 'streamable-http'")
47-
48-
if not EMAIL or not PASSWORD:
49-
raise ValueError('Please set EMAIL and PASSWORD as an ENV variable.')
5038

5139
logging.basicConfig(level=logging.DEBUG)
5240
logger = logging.getLogger(__name__)
5341

54-
mcp = FastMCP("spendee", host="0.0.0.0", port=PORT)
5542

56-
spendee = SpendeeFirestore(EMAIL, PASSWORD)
57-
58-
# Automatically register all MCP tools from the client
59-
for name, func in MCP_TOOLS.items():
60-
# Bind the method to the spendee instance if it's a class method
61-
bound_func = getattr(spendee, name)
62-
mcp.tool()(bound_func)
63-
64-
def main():
65-
#debug_secret(ACCEPTED_TOKEN, "MCP_TOKEN")
66-
#debug_secret(PASSWORD, "PASSWORD")
43+
def run(email=None, password=None, port=8000, accepted_token="spendee-token", transfer_mode="sse"):
44+
45+
if transfer_mode not in ["sse", "streamable-http"]:
46+
raise ValueError("TRANSFER_MODE must be either 'sse' or 'streamable-http'")
47+
48+
if not email or not password:
49+
raise ValueError('Please set EMAIL and PASSWORD as an ENV variable.')
50+
51+
mcp = FastMCP("spendee", host="0.0.0.0", port=port)
52+
53+
spendee = SpendeeFirestore(EMAIL, PASSWORD)
54+
55+
# Automatically register all MCP tools from the client
56+
for name, func in MCP_TOOLS.items():
57+
# Bind the method to the spendee instance if it's a class method
58+
bound_func = getattr(spendee, name)
59+
mcp.tool()(bound_func)
60+
61+
async def check_bearer_auth(request, error_response=None):
62+
if accepted_token == "disabled":
63+
logger.info("Bearer token authentication is disabled.")
64+
return None
65+
66+
if DEBUG_MODE:
67+
logger.debug(f"Incoming request: method={request.method}, url={request.url}")
68+
logger.debug(f"Request headers: {dict(request.headers)}")
69+
70+
auth_header = request.headers.get("authorization")
71+
if not auth_header or not auth_header.lower().startswith("bearer "):
72+
logger.warning("Missing or invalid Authorization header.")
73+
if error_response:
74+
return await error_response("Missing or invalid Authorization header.", 401)
75+
raise HTTPException(401, "Missing or invalid Authorization header.")
76+
77+
token = auth_header.split(" ", 1)[1]
78+
if token != accepted_token:
79+
logger.warning("Invalid token.")
80+
if error_response:
81+
return await error_response("Invalid token.", 401)
82+
raise HTTPException(401, "Invalid token.")
83+
return None
6784

6885
logger.info("Starting Spendee MCP Server")
6986
# I failed to unify both transfer modes, because of some lifecycle issues
70-
if TRANSFER_MODE == "streamable-http":
87+
if transfer_mode == "streamable-http":
7188
logger.info("Using Streamable HTTP transport")
72-
streaming_server()
89+
streaming_server(mcp, port, check_bearer_auth)
7390
else:
7491
logger.info("Using SSE transport")
75-
sse_server()
92+
sse_server(mcp, port, check_bearer_auth)
7693

7794
# Authentication middleware and server setup
7895

@@ -82,32 +99,8 @@ def debug_secret(secret, name):
8299
logger.debug(f"sha256('{salt}' + token): {token_hash}")
83100
logger.debug(f"You can verify with: echo -n \"{salt}${name}\" | sha256sum")
84101

85-
async def check_bearer_auth(request, error_response=None):
86-
if ACCEPTED_TOKEN == "disabled":
87-
logger.info("Bearer token authentication is disabled.")
88-
return None
89-
90-
if DEBUG_MODE:
91-
logger.debug(f"Incoming request: method={request.method}, url={request.url}")
92-
logger.debug(f"Request headers: {dict(request.headers)}")
93-
94-
auth_header = request.headers.get("authorization")
95-
if not auth_header or not auth_header.lower().startswith("bearer "):
96-
logger.warning("Missing or invalid Authorization header.")
97-
if error_response:
98-
return await error_response("Missing or invalid Authorization header.", 401)
99-
raise HTTPException(401, "Missing or invalid Authorization header.")
100-
101-
token = auth_header.split(" ", 1)[1]
102-
if token != ACCEPTED_TOKEN:
103-
logger.warning("Invalid token.")
104-
if error_response:
105-
return await error_response("Invalid token.", 401)
106-
raise HTTPException(401, "Invalid token.")
107-
return None
108-
109102

110-
def streaming_server():
103+
def streaming_server(mcp, port, check_bearer_auth):
111104
# maybe this would be simpler: https://gofastmcp.com/deployment/self-hosted#environment-variables
112105
session_manager = StreamableHTTPSessionManager(
113106
app=mcp._mcp_server,
@@ -130,12 +123,12 @@ async def lifespan(app: FastAPI):
130123
app = FastAPI(lifespan=lifespan)
131124
app.mount("/mcp", auth_middleware)
132125

133-
logger.info(f"Starting Spendee MCP Server with Bearer Token Authentication on port {PORT}")
134-
logger.info(f"Access the MCP endpoint at http://0.0.0.0:{PORT}/mcp")
135-
uvicorn.run(app, host="0.0.0.0", port=PORT)
126+
logger.info(f"Starting Spendee MCP Server with Bearer Token Authentication on port {port}")
127+
logger.info(f"Access the MCP endpoint at http://0.0.0.0:{port}/mcp")
128+
uvicorn.run(app, host="0.0.0.0", port=port)
136129

137130

138-
def sse_server():
131+
def sse_server(mcp, port, check_bearer_auth):
139132
sse_transport = SseServerTransport("/messages/")
140133

141134
async def error_response(msg, status):
@@ -169,10 +162,15 @@ async def sse_auth_middleware(scope: Scope, receive: Receive, send: Send):
169162
Mount("/", sse_auth_middleware),
170163
],
171164
)
172-
uvicorn.run(app, host="0.0.0.0", port=PORT)
165+
uvicorn.run(app, host="0.0.0.0", port=port)
173166

174167

175168
if __name__ == "__main__":
176-
main()
177-
else:
178-
main()
169+
170+
EMAIL = os.getenv('EMAIL')
171+
PASSWORD = os.getenv('PASSWORD')
172+
ACCEPTED_TOKEN = os.environ.get("MCP_TOKEN", "spendee-token")
173+
PORT = int(os.environ.get("MCP_PORT", 8000))
174+
TRANSFER_MODE = os.environ.get("TRANSFER_MODE", "sse").lower()
175+
176+
run(email=EMAIL, password=PASSWORD, port=PORT, accepted_token=ACCEPTED_TOKEN, transfer_mode=TRANSFER_MODE)

0 commit comments

Comments
 (0)