Skip to content

Commit 81bd851

Browse files
authored
Add tag-based filtering to intelligent_tool_finder and streamline authentication (#127)
* Add tag-based filtering to intelligent_tool_finder and clean up agent authentication ## Enhanced intelligent_tool_finder with tag support - Add optional 'tags' parameter for filtering tools by server tags using AND logic - Support three modes: semantic search only, tags only, and combined semantic + tags - Maintain backward compatibility with existing query-only calls - Add comprehensive docstring warnings for AI agents about tag usage - Update mcpgw.json with new tool schema including tags parameter ## Fixed tags-only mode error - Resolve KeyError when using tags without semantic search - Add proper logging for tags-only mode vs similarity scores - Update container with patched server.py to handle missing similarity scores ## Streamlined agent authentication - Remove complex .env file handling logic from agents/agent.py - Primary authentication now uses .oauth-tokens/ directory - Keep simple environment variable fallback for system-level config - Remove deprecated --user-env-file and --agent-env-file parameters - Clean up authentication flow to use tokens > ingress.json > Cognito generation ## Related to issue #126 - Implements tag-based filtering enhancement as specified - Enables precise tool discovery by category with semantic search integration * Update README.md with tag-based tool filtering feature Add one-liner to What's New section highlighting the enhanced intelligent_tool_finder with tag-based filtering capability for precise categorical tool discovery.
1 parent ee6c01d commit 81bd851

File tree

4 files changed

+226
-193
lines changed

4 files changed

+226
-193
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ Transform how both autonomous AI agents and development teams access enterprise
364364

365365
## What's New
366366

367+
- **Tag-Based Tool Filtering** - Enhanced intelligent_tool_finder now supports filtering tools by server tags for precise categorical discovery alongside semantic search
367368
- **🔐 Keycloak Identity Provider Integration** - Enterprise-grade authentication with individual AI agent audit trails, group-based authorization, and production-ready service account management. [Learn more](docs/keycloak-integration.md)
368369
- **Amazon Bedrock AgentCore Integration** - Direct access to AWS services through managed MCP endpoints
369370
- **Three-Legged OAuth (3LO) Support** - External service integration (Atlassian, Google, GitHub)

agents/agent.py

Lines changed: 17 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
import httpx
6565
import re
6666

67-
# Import dotenv for loading environment variables
67+
# Import dotenv for loading basic environment variables
6868
from dotenv import load_dotenv
6969

7070
# Add the auth_server directory to the path to import cognito_utils
@@ -223,125 +223,18 @@ def enable_verbose_logging():
223223

224224
logger.info("Verbose logging enabled for httpx, httpcore, mcp libraries, and main logger")
225225

226-
def get_auth_mode_from_args() -> tuple[bool, str, str]:
226+
def get_auth_mode_from_args() -> bool:
227227
"""
228-
Parse command line arguments to determine authentication mode and env file names.
229-
This is done before loading environment variables to choose the correct .env file.
230-
228+
Parse command line arguments to determine authentication mode.
229+
231230
Returns:
232-
tuple: (use_session_cookie, user_env_file, agent_env_file)
233-
- use_session_cookie: True if using session cookie authentication, False for M2M authentication
234-
- user_env_file: Name of the env file for user authentication
235-
- agent_env_file: Name of the env file for agent authentication
231+
bool: True if using session cookie authentication, False for M2M authentication
236232
"""
237233
parser = argparse.ArgumentParser(add_help=False)
238234
parser.add_argument('--use-session-cookie', action='store_true',
239235
help='Use session cookie authentication instead of M2M')
240-
parser.add_argument('--user-env-file', type=str, default='.env.user',
241-
help='Name of the environment file for user authentication (default: .env.user)')
242-
parser.add_argument('--agent-env-file', type=str, default='.env.agent',
243-
help='Name of the environment file for agent authentication (default: .env.agent)')
244236
args, _ = parser.parse_known_args()
245-
return args.use_session_cookie, args.user_env_file, args.agent_env_file
246-
247-
def print_env_file_banner(env_file_name: str, use_session_cookie: bool, file_found: bool, file_path: str = None):
248-
"""
249-
Print a prominent banner showing which .env file is being used and why.
250-
251-
Args:
252-
env_file_name: Name of the .env file being used
253-
use_session_cookie: Whether session cookie authentication is being used
254-
file_found: Whether the .env file was found
255-
file_path: Full path to the .env file if found
256-
"""
257-
print("\n" + "="*80)
258-
print(" ENVIRONMENT CONFIGURATION")
259-
print("="*80)
260-
261-
auth_mode = "Session Cookie Authentication" if use_session_cookie else "M2M Authentication"
262-
print(f"Authentication Mode: {auth_mode}")
263-
print(f"Expected .env file: {env_file_name}")
264-
265-
if use_session_cookie:
266-
print(f"Reason: --use-session-cookie flag specified, using {env_file_name} for user credentials")
267-
else:
268-
print(f"Reason: M2M authentication (default), using {env_file_name} for machine credentials")
269-
270-
if file_found and file_path:
271-
print(f"✅ Found and loaded: {file_path}")
272-
else:
273-
print(f"⚠️ File not found: {env_file_name}")
274-
print(" Falling back to system environment variables")
275-
276-
print("="*80 + "\n")
277-
278-
def load_env_config(use_session_cookie: bool, user_env_file: str = '.env.user', agent_env_file: str = '.env.agent') -> Dict[str, Optional[str]]:
279-
"""
280-
Load configuration from .env file based on authentication mode.
281-
Uses .env.user for session cookie auth, .env.agent for M2M auth.
282-
283-
Args:
284-
use_session_cookie: True for session cookie auth (.env.user), False for M2M auth (.env.agent)
285-
286-
Returns:
287-
Dict[str, Optional[str]]: Dictionary containing environment variables
288-
"""
289-
env_config = {
290-
'client_id': None,
291-
'client_secret': None,
292-
'region': None,
293-
'user_pool_id': None,
294-
'domain': None,
295-
'anthropic_api_key': None
296-
}
297-
298-
# Choose .env file based on authentication mode
299-
env_file_name = user_env_file if use_session_cookie else agent_env_file
300-
logger.info(f"Using .env file: {env_file_name}")
301-
# Load environment variables using dotenv
302-
file_found = False
303-
file_path = None
304-
305-
# Try to load from .env file in the current directory
306-
env_file = os.path.join(os.path.dirname(__file__), env_file_name)
307-
if os.path.exists(env_file):
308-
logger.info(f"Found .env file: {env_file}")
309-
load_dotenv(env_file, override=True)
310-
file_found = True
311-
file_path = env_file
312-
logger.info(f"Loading environment variables from {env_file}")
313-
logger.info(f"user pool id {os.environ.get('COGNITO_USER_POOL_ID')}")
314-
else:
315-
# Try to load from .env file in the parent directory
316-
env_file = os.path.join(os.path.dirname(__file__), '..', env_file_name)
317-
if os.path.exists(env_file):
318-
logger.info(f"Found .env file in parent directory: {env_file}")
319-
load_dotenv(env_file, override=True)
320-
logger.info(f"Loading environment variables from {env_file}")
321-
else:
322-
# Try to load from current working directory
323-
env_file = os.path.join(os.getcwd(), env_file_name)
324-
if os.path.exists(env_file):
325-
logger.info(f"Found .env file in current working directory: {env_file}")
326-
load_dotenv(env_file, override=True)
327-
logger.info(f"Loading environment variables from {env_file}")
328-
else:
329-
# Fallback to default .env loading
330-
load_dotenv(override=True)
331-
logger.info("Loading environment variables from default .env file")
332-
333-
# Print banner showing which file is being used
334-
print_env_file_banner(env_file_name, use_session_cookie, file_found, file_path)
335-
336-
# Get values from environment
337-
env_config['client_id'] = os.getenv('COGNITO_CLIENT_ID')
338-
env_config['client_secret'] = os.getenv('COGNITO_CLIENT_SECRET')
339-
env_config['region'] = os.getenv('AWS_REGION')
340-
env_config['user_pool_id'] = os.getenv('COGNITO_USER_POOL_ID')
341-
env_config['domain'] = os.getenv('COGNITO_DOMAIN')
342-
env_config['anthropic_api_key'] = os.getenv('ANTHROPIC_API_KEY')
343-
344-
return env_config
237+
return args.use_session_cookie
345238

346239
def load_agent_credentials(agent_name: str) -> Optional[Dict[str, Any]]:
347240
"""
@@ -391,13 +284,18 @@ def parse_arguments() -> argparse.Namespace:
391284
Returns:
392285
argparse.Namespace: The parsed command line arguments
393286
"""
394-
# First, determine authentication mode and env file names to choose correct .env file
395-
use_session_cookie, user_env_file, agent_env_file = get_auth_mode_from_args()
396-
logger.info(f"Using session cookie authentication: {use_session_cookie}")
397-
logger.info(f"User env file: {user_env_file}, Agent env file: {agent_env_file}")
287+
# Load basic environment variables for fallback (system environment only)
288+
load_dotenv(override=False) # Only load if not already set
398289

399-
# Load environment configuration using the appropriate .env file
400-
env_config = load_env_config(use_session_cookie, user_env_file, agent_env_file)
290+
# Get environment variables for fallback values
291+
env_config = {
292+
'client_id': os.getenv('COGNITO_CLIENT_ID'),
293+
'client_secret': os.getenv('COGNITO_CLIENT_SECRET'),
294+
'region': os.getenv('AWS_REGION'),
295+
'user_pool_id': os.getenv('COGNITO_USER_POOL_ID'),
296+
'domain': os.getenv('COGNITO_DOMAIN'),
297+
'anthropic_api_key': os.getenv('ANTHROPIC_API_KEY')
298+
}
401299

402300
parser = argparse.ArgumentParser(description='Interactive LangGraph MCP Client with Flexible Authentication')
403301

@@ -441,11 +339,6 @@ def parse_arguments() -> argparse.Namespace:
441339
parser.add_argument('--access-token', type=str,
442340
help='Direct access token override (takes precedence over agent credentials)')
443341

444-
# Environment file configuration arguments
445-
parser.add_argument('--user-env-file', type=str, default=user_env_file,
446-
help=f'Name of the environment file for user authentication (default: {user_env_file})')
447-
parser.add_argument('--agent-env-file', type=str, default=agent_env_file,
448-
help=f'Name of the environment file for agent authentication (default: {agent_env_file})')
449342

450343
# Cognito authentication arguments - now optional if available in environment
451344
parser.add_argument('--client-id', type=str, default=env_config['client_id'],

registry/servers/mcpgw.json

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"supported_transports": ["streamable-http"],
77
"auth_type": "none",
88
"tags": ["registry", "management"],
9-
"num_tools": 5,
9+
"num_tools": 6,
1010
"num_stars": 0,
1111
"is_python": true,
1212
"license": "N/A",
@@ -262,6 +262,76 @@
262262
"title": "refresh_serviceArguments",
263263
"type": "object"
264264
}
265+
},
266+
{
267+
"name": "intelligent_tool_finder",
268+
"parsed_description": {
269+
"main": "Finds the most relevant MCP tool(s) across all registered and enabled services based on a natural language query and/or tag filtering. IMPORTANT FOR AI AGENTS: Only use the 'tags' parameter if the user explicitly provides specific tags. DO NOT infer or guess tags from the query - incorrect tags will exclude valid results. When tags are provided with a query, results must match BOTH. If unsure about tags, use natural_language_query alone for best results.",
270+
"args": "natural_language_query: Optional - Your query in natural language (recommended for AI agents unless user specifies tags). tags: Optional - List of tags to filter by using AND logic (CAUTION: Only use if explicitly provided by user). top_k_services: Number of top services from FAISS search (default: 3, ignored for tags-only). top_n_tools: Number of best tools to return (default: 1).",
271+
"returns": "List[Dict[str, Any]]: A list of dictionaries, each describing a recommended tool, its parent service, and similarity score (if semantic search used).",
272+
"raises": "Exception: If neither query nor tags is provided, or if FAISS index/model is unavailable."
273+
},
274+
"schema": {
275+
"$defs": {
276+
"IntelligentToolFinderParams": {
277+
"properties": {
278+
"natural_language_query": {
279+
"anyOf": [
280+
{
281+
"type": "string"
282+
},
283+
{
284+
"type": "null"
285+
}
286+
],
287+
"default": null,
288+
"description": "Your query in natural language describing the task you want to perform. Optional if tags are provided.",
289+
"title": "Natural Language Query"
290+
},
291+
"tags": {
292+
"anyOf": [
293+
{
294+
"items": {
295+
"type": "string"
296+
},
297+
"type": "array"
298+
},
299+
{
300+
"type": "null"
301+
}
302+
],
303+
"default": null,
304+
"description": "List of tags to filter tools by using AND logic. IMPORTANT: AI agents should ONLY use this if the user explicitly provides specific tags. DO NOT infer tags - incorrect tags will exclude valid results.",
305+
"title": "Tags"
306+
},
307+
"top_k_services": {
308+
"default": 3,
309+
"description": "Number of top services to consider from initial FAISS search (ignored if only tags provided).",
310+
"title": "Top K Services",
311+
"type": "integer"
312+
},
313+
"top_n_tools": {
314+
"default": 1,
315+
"description": "Number of best matching tools to return.",
316+
"title": "Top N Tools",
317+
"type": "integer"
318+
}
319+
},
320+
"title": "IntelligentToolFinderParams",
321+
"type": "object"
322+
}
323+
},
324+
"properties": {
325+
"params": {
326+
"$ref": "#/$defs/IntelligentToolFinderParams"
327+
}
328+
},
329+
"required": [
330+
"params"
331+
],
332+
"title": "intelligent_tool_finderArguments",
333+
"type": "object"
334+
}
265335
}
266336
]
267337
}

0 commit comments

Comments
 (0)