testmcpy 0.1.2__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {testmcpy-0.1.2 → testmcpy-0.1.4}/PKG-INFO +1 -1
- {testmcpy-0.1.2 → testmcpy-0.1.4}/pyproject.toml +1 -1
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy/config.py +23 -13
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy/src/llm_integration.py +25 -13
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy/src/test_runner.py +3 -3
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy.egg-info/PKG-INFO +1 -1
- {testmcpy-0.1.2 → testmcpy-0.1.4}/LICENSE +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/README.md +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/setup.cfg +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy/__init__.py +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy/cli.py +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy/evals/__init__.py +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy/evals/base_evaluators.py +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy/research/claude_sdk_detailed_exploration.py +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy/research/claude_sdk_poc.py +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy/research/claude_sdk_working_poc.py +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy/research/test_ollama_tools.py +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy/src/__init__.py +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy/src/mcp_client.py +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy.egg-info/SOURCES.txt +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy.egg-info/dependency_links.txt +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy.egg-info/entry_points.txt +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy.egg-info/requires.txt +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/testmcpy.egg-info/top_level.txt +0 -0
- {testmcpy-0.1.2 → testmcpy-0.1.4}/tests/test_url_protection.py +0 -0
|
@@ -11,7 +11,7 @@ include = ["testmcpy*"]
|
|
|
11
11
|
|
|
12
12
|
[project]
|
|
13
13
|
name = "testmcpy"
|
|
14
|
-
version = "0.1.
|
|
14
|
+
version = "0.1.4"
|
|
15
15
|
description = "A comprehensive testing framework for validating LLM tool calling capabilities with MCP services"
|
|
16
16
|
authors = [{name = "Preset", email = "amin@preset.io"}]
|
|
17
17
|
license = {text = "Apache-2.0"}
|
|
@@ -192,26 +192,36 @@ class Config:
|
|
|
192
192
|
def mcp_auth_token(self) -> Optional[str]:
|
|
193
193
|
"""
|
|
194
194
|
Get MCP auth token with the following priority:
|
|
195
|
-
1.
|
|
196
|
-
2.
|
|
195
|
+
1. Dynamically generated JWT from MCP_AUTH_API_URL if configured
|
|
196
|
+
2. Static MCP_AUTH_TOKEN or SUPERSET_MCP_TOKEN
|
|
197
197
|
|
|
198
198
|
For dynamic tokens, caches the JWT for 50 minutes to avoid excessive API calls.
|
|
199
199
|
"""
|
|
200
|
-
# Check
|
|
200
|
+
# Check if dynamic JWT credentials are configured
|
|
201
|
+
has_dynamic_config = all([
|
|
202
|
+
self.get("MCP_AUTH_API_URL"),
|
|
203
|
+
self.get("MCP_AUTH_API_TOKEN"),
|
|
204
|
+
self.get("MCP_AUTH_API_SECRET")
|
|
205
|
+
])
|
|
206
|
+
|
|
207
|
+
# If dynamic JWT is configured, use it (with caching)
|
|
208
|
+
if has_dynamic_config:
|
|
209
|
+
# Check if we have a valid cached token
|
|
210
|
+
if self._cached_token and self._token_expiry:
|
|
211
|
+
if time.time() < self._token_expiry:
|
|
212
|
+
return self._cached_token
|
|
213
|
+
|
|
214
|
+
# Try to fetch a new JWT token
|
|
215
|
+
jwt_token = self._fetch_jwt_token()
|
|
216
|
+
if jwt_token:
|
|
217
|
+
return jwt_token
|
|
218
|
+
# If fetch fails, fall through to static token
|
|
219
|
+
|
|
220
|
+
# Fall back to static token
|
|
201
221
|
static_token = self.get("MCP_AUTH_TOKEN") or self.get("SUPERSET_MCP_TOKEN")
|
|
202
222
|
if static_token:
|
|
203
223
|
return static_token
|
|
204
224
|
|
|
205
|
-
# Check if we have a valid cached token
|
|
206
|
-
if self._cached_token and self._token_expiry:
|
|
207
|
-
if time.time() < self._token_expiry:
|
|
208
|
-
return self._cached_token
|
|
209
|
-
|
|
210
|
-
# Try to fetch a new JWT token
|
|
211
|
-
jwt_token = self._fetch_jwt_token()
|
|
212
|
-
if jwt_token:
|
|
213
|
-
return jwt_token
|
|
214
|
-
|
|
215
225
|
return None
|
|
216
226
|
|
|
217
227
|
@property
|
|
@@ -17,12 +17,19 @@ from urllib.parse import urlparse
|
|
|
17
17
|
# Import MCP components (we'll handle the import error gracefully)
|
|
18
18
|
try:
|
|
19
19
|
from .mcp_client import MCPClient, MCPTool, MCPToolCall, MCPToolResult
|
|
20
|
+
from ..config import get_config
|
|
20
21
|
except ImportError:
|
|
21
22
|
# Fallback for when running as script
|
|
22
23
|
import sys
|
|
23
24
|
import os
|
|
24
25
|
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|
25
26
|
from mcp_client import MCPClient, MCPTool, MCPToolCall, MCPToolResult
|
|
27
|
+
# Config will fall back to environment variables
|
|
28
|
+
def get_config():
|
|
29
|
+
class FallbackConfig:
|
|
30
|
+
def get(self, key, default=None):
|
|
31
|
+
return os.getenv(key, default)
|
|
32
|
+
return FallbackConfig()
|
|
26
33
|
|
|
27
34
|
|
|
28
35
|
@dataclass
|
|
@@ -271,10 +278,10 @@ class OpenAIProvider(LLMProvider):
|
|
|
271
278
|
async def initialize(self):
|
|
272
279
|
"""Initialize OpenAI provider."""
|
|
273
280
|
if not self.api_key and self.base_url == "https://api.openai.com/v1":
|
|
274
|
-
|
|
275
|
-
self.api_key =
|
|
281
|
+
config = get_config()
|
|
282
|
+
self.api_key = config.get("OPENAI_API_KEY", "")
|
|
276
283
|
if not self.api_key:
|
|
277
|
-
raise ValueError("OpenAI API key not provided")
|
|
284
|
+
raise ValueError("OpenAI API key not provided. Set OPENAI_API_KEY in ~/.testmcpy or environment.")
|
|
278
285
|
|
|
279
286
|
async def generate_with_tools(
|
|
280
287
|
self,
|
|
@@ -593,18 +600,20 @@ class AnthropicProvider(LLMProvider):
|
|
|
593
600
|
mcp_url: Optional[str] = None
|
|
594
601
|
):
|
|
595
602
|
self.model = model
|
|
596
|
-
|
|
603
|
+
# Use config system for API key
|
|
604
|
+
config = get_config()
|
|
605
|
+
self.api_key = api_key or config.get("ANTHROPIC_API_KEY", "")
|
|
597
606
|
self.base_url = base_url
|
|
598
607
|
self.client = httpx.AsyncClient(timeout=60.0)
|
|
599
|
-
# Use MCP_URL from
|
|
608
|
+
# Use MCP_URL from config if not provided
|
|
600
609
|
if mcp_url is None:
|
|
601
|
-
mcp_url =
|
|
610
|
+
mcp_url = config.mcp_url
|
|
602
611
|
self.tool_discovery = ToolDiscoveryService(mcp_url)
|
|
603
612
|
|
|
604
613
|
async def initialize(self):
|
|
605
614
|
"""Initialize Anthropic provider."""
|
|
606
615
|
if not self.api_key:
|
|
607
|
-
raise ValueError("Anthropic API key not provided. Set ANTHROPIC_API_KEY
|
|
616
|
+
raise ValueError("Anthropic API key not provided. Set ANTHROPIC_API_KEY in ~/.testmcpy, .env, or environment.")
|
|
608
617
|
|
|
609
618
|
# Try to pre-discover tools, but don't fail if MCP service is unavailable
|
|
610
619
|
try:
|
|
@@ -807,10 +816,12 @@ class ClaudeSDKProvider(LLMProvider):
|
|
|
807
816
|
mcp_url: Optional[str] = None
|
|
808
817
|
):
|
|
809
818
|
self.model = model
|
|
810
|
-
|
|
811
|
-
|
|
819
|
+
# Use config system for API key
|
|
820
|
+
config = get_config()
|
|
821
|
+
self.api_key = api_key or config.get("ANTHROPIC_API_KEY", "")
|
|
822
|
+
# Use MCP_URL from config if not provided
|
|
812
823
|
if mcp_url is None:
|
|
813
|
-
mcp_url =
|
|
824
|
+
mcp_url = config.mcp_url
|
|
814
825
|
self.mcp_url = mcp_url
|
|
815
826
|
self.tool_discovery = ToolDiscoveryService(mcp_url)
|
|
816
827
|
self._sdk_tools: List[Any] = []
|
|
@@ -819,7 +830,7 @@ class ClaudeSDKProvider(LLMProvider):
|
|
|
819
830
|
async def initialize(self):
|
|
820
831
|
"""Initialize Claude SDK provider."""
|
|
821
832
|
if not self.api_key:
|
|
822
|
-
raise ValueError("Anthropic API key not provided. Set ANTHROPIC_API_KEY
|
|
833
|
+
raise ValueError("Anthropic API key not provided. Set ANTHROPIC_API_KEY in ~/.testmcpy, .env, or environment.")
|
|
823
834
|
|
|
824
835
|
# IMPORTANT: Claude Agent SDK is designed for stdio-based MCP servers (command-line tools),
|
|
825
836
|
# not HTTP-based MCP services. For HTTP MCP services, use the 'anthropic' provider instead.
|
|
@@ -1015,9 +1026,10 @@ class ClaudeCodeProvider(LLMProvider):
|
|
|
1015
1026
|
):
|
|
1016
1027
|
self.model = model
|
|
1017
1028
|
self.claude_cli_path = claude_cli_path or self._find_claude_cli()
|
|
1018
|
-
# Use MCP_URL from
|
|
1029
|
+
# Use MCP_URL from config if not provided
|
|
1030
|
+
config = get_config()
|
|
1019
1031
|
if mcp_url is None:
|
|
1020
|
-
mcp_url =
|
|
1032
|
+
mcp_url = config.mcp_url
|
|
1021
1033
|
self.tool_discovery = ToolDiscoveryService(mcp_url)
|
|
1022
1034
|
|
|
1023
1035
|
def _find_claude_cli(self) -> str:
|
|
@@ -10,9 +10,9 @@ import json
|
|
|
10
10
|
import re
|
|
11
11
|
from datetime import datetime, timedelta
|
|
12
12
|
|
|
13
|
-
from
|
|
14
|
-
from
|
|
15
|
-
from evals.base_evaluators import BaseEvaluator, EvalResult, create_evaluator
|
|
13
|
+
from .mcp_client import MCPClient, MCPToolCall, MCPToolResult
|
|
14
|
+
from .llm_integration import LLMProvider, create_llm_provider
|
|
15
|
+
from ..evals.base_evaluators import BaseEvaluator, EvalResult, create_evaluator
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class RateLimitTracker:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|