gopher-mcp-python 0.1.30__tar.gz → 0.1.34__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.
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/PKG-INFO +4 -1
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/README.md +3 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/__init__.py +90 -58
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/agent.py +216 -76
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/config.py +38 -90
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/ffi/auth/loader.py +9 -9
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/ffi/library.py +140 -96
- gopher_mcp_python-0.1.34/gopher_mcp_python/oauth_authorization_url.py +49 -0
- gopher_mcp_python-0.1.34/gopher_mcp_python/oauth_browser.py +33 -0
- gopher_mcp_python-0.1.34/gopher_mcp_python/oauth_discovery.py +341 -0
- gopher_mcp_python-0.1.34/gopher_mcp_python/oauth_loopback.py +148 -0
- gopher_mcp_python-0.1.34/gopher_mcp_python/oauth_pkce.py +21 -0
- gopher_mcp_python-0.1.34/gopher_mcp_python/oauth_registration.py +89 -0
- gopher_mcp_python-0.1.34/gopher_mcp_python/oauth_resolver.py +362 -0
- gopher_mcp_python-0.1.34/gopher_mcp_python/oauth_runtime_options.py +29 -0
- gopher_mcp_python-0.1.34/gopher_mcp_python/oauth_server_targets.py +76 -0
- gopher_mcp_python-0.1.34/gopher_mcp_python/oauth_token_exchange.py +104 -0
- gopher_mcp_python-0.1.34/gopher_mcp_python/oauth_token_store.py +75 -0
- gopher_mcp_python-0.1.34/gopher_mcp_python/runtime_options.py +286 -0
- gopher_mcp_python-0.1.34/gopher_mcp_python/server_config.py +112 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python.egg-info/PKG-INFO +4 -1
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python.egg-info/SOURCES.txt +31 -1
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/pyproject.toml +1 -1
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/tests/test_agent_create_by.py +31 -8
- gopher_mcp_python-0.1.34/tests/test_agent_create_with_oauth.py +376 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/tests/test_agent_error_message.py +9 -3
- gopher_mcp_python-0.1.34/tests/test_agent_lifecycle.py +77 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/tests/test_agent_runtime_options.py +29 -2
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/tests/test_auth_exports.py +9 -6
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/tests/test_config.py +109 -1
- gopher_mcp_python-0.1.34/tests/test_dump_version.py +28 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/tests/test_ffi.py +175 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/tests/test_ffi_runtime_options.py +27 -1
- gopher_mcp_python-0.1.34/tests/test_header_create_by_url_example.py +54 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/tests/test_library_search_paths.py +41 -4
- gopher_mcp_python-0.1.34/tests/test_linux_native_packaging.py +196 -0
- gopher_mcp_python-0.1.34/tests/test_oauth_authorization_url.py +112 -0
- gopher_mcp_python-0.1.34/tests/test_oauth_browser.py +53 -0
- gopher_mcp_python-0.1.34/tests/test_oauth_create_with_url_integration.py +210 -0
- gopher_mcp_python-0.1.34/tests/test_oauth_discovery.py +240 -0
- gopher_mcp_python-0.1.34/tests/test_oauth_loopback.py +80 -0
- gopher_mcp_python-0.1.34/tests/test_oauth_pkce.py +23 -0
- gopher_mcp_python-0.1.34/tests/test_oauth_registration.py +56 -0
- gopher_mcp_python-0.1.34/tests/test_oauth_resolver.py +136 -0
- gopher_mcp_python-0.1.34/tests/test_oauth_runtime_options.py +48 -0
- gopher_mcp_python-0.1.34/tests/test_oauth_server_targets.py +58 -0
- gopher_mcp_python-0.1.34/tests/test_oauth_token_exchange.py +91 -0
- gopher_mcp_python-0.1.34/tests/test_oauth_token_store.py +74 -0
- gopher_mcp_python-0.1.34/tests/test_server_config.py +74 -0
- gopher_mcp_python-0.1.30/gopher_mcp_python/server_config.py +0 -47
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/LICENSE +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/auth/__init__.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/auth/errors.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/auth/gopher_auth.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/auth/scope_helpers.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/errors.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/ffi/__init__.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/ffi/auth/__init__.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/ffi/auth/auth_client.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/ffi/auth/auto_refresh.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/ffi/auth/config_loader.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/ffi/auth/oauth_client.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/ffi/auth/session_manager.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/ffi/auth/types.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/ffi/auth/validation_options.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python/result.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python.egg-info/dependency_links.txt +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python.egg-info/requires.txt +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/gopher_mcp_python.egg-info/top_level.txt +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/setup.cfg +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/setup.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/tests/test_gopher_auth.py +0 -0
- {gopher_mcp_python-0.1.30 → gopher_mcp_python-0.1.34}/tests/test_result.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gopher-mcp-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.34
|
|
4
4
|
Summary: Python SDK for Gopher MCP - AI Agent orchestration framework with native performance
|
|
5
5
|
Author-email: Gopher Security <dev@gophersecurity.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -61,6 +61,9 @@ Python SDK for gopher-mcp-python, providing AI agent orchestration with native C
|
|
|
61
61
|
- Python 3.8 or higher
|
|
62
62
|
- `venv` and `pip` for PyPI installation, examples, and development workflows
|
|
63
63
|
- Native gopher-mcp-python library (built from source)
|
|
64
|
+
- On Linux, system OpenSSL runtime libraries (`libssl` / `libcrypto`) from
|
|
65
|
+
your distribution. Native wheels do not bundle OpenSSL, so OS security
|
|
66
|
+
updates remain effective.
|
|
64
67
|
|
|
65
68
|
On Debian/Ubuntu, install the Python venv and pip packages before using the
|
|
66
69
|
PyPI install path, running examples, or setting up development dependencies:
|
|
@@ -17,6 +17,9 @@ Python SDK for gopher-mcp-python, providing AI agent orchestration with native C
|
|
|
17
17
|
- Python 3.8 or higher
|
|
18
18
|
- `venv` and `pip` for PyPI installation, examples, and development workflows
|
|
19
19
|
- Native gopher-mcp-python library (built from source)
|
|
20
|
+
- On Linux, system OpenSSL runtime libraries (`libssl` / `libcrypto`) from
|
|
21
|
+
your distribution. Native wheels do not bundle OpenSSL, so OS security
|
|
22
|
+
updates remain effective.
|
|
20
23
|
|
|
21
24
|
On Debian/Ubuntu, install the Python venv and pip packages before using the
|
|
22
25
|
PyPI install path, running examples, or setting up development dependencies:
|
|
@@ -22,11 +22,19 @@ Example:
|
|
|
22
22
|
>>> agent.dispose()
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
|
+
from importlib import import_module
|
|
26
|
+
|
|
25
27
|
from gopher_mcp_python.agent import GopherAgent
|
|
26
28
|
from gopher_mcp_python.config import (
|
|
27
29
|
GopherAgentConfig,
|
|
28
30
|
GopherAgentConfigBuilder,
|
|
31
|
+
)
|
|
32
|
+
from gopher_mcp_python.runtime_options import (
|
|
33
|
+
GopherAgentCreateOptions,
|
|
34
|
+
GopherAgentOAuthOptions,
|
|
29
35
|
GopherAgentRuntimeOptions,
|
|
36
|
+
GopherAgentTokenRecord,
|
|
37
|
+
GopherAgentTokenStore,
|
|
30
38
|
)
|
|
31
39
|
from gopher_mcp_python.result import AgentResult, AgentResultStatus, AgentResultBuilder
|
|
32
40
|
from gopher_mcp_python.errors import (
|
|
@@ -35,76 +43,100 @@ from gopher_mcp_python.errors import (
|
|
|
35
43
|
ConnectionError,
|
|
36
44
|
TimeoutError,
|
|
37
45
|
)
|
|
38
|
-
from gopher_mcp_python.server_config import ServerConfig
|
|
46
|
+
from gopher_mcp_python.server_config import ServerConfig, ServerConfigRoute
|
|
39
47
|
from gopher_mcp_python.ffi import GopherOrchLibrary, GopherOrchHandle
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
49
|
+
__version__ = "0.1.34"
|
|
50
|
+
|
|
51
|
+
_AUTH_EXPORTS = {
|
|
52
|
+
"GopherAuth",
|
|
53
|
+
"GopherAuthError",
|
|
54
|
+
"ConfigurationError",
|
|
55
|
+
"InsufficientScopesError",
|
|
56
|
+
"JwksError",
|
|
57
|
+
"TokenExchangeError",
|
|
58
|
+
"TokenValidationError",
|
|
59
|
+
"has_all_scopes",
|
|
60
|
+
"has_any_scope",
|
|
61
|
+
"has_scope",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_AUTH_FFI_EXPORTS = {
|
|
65
|
+
"AutoRefreshResult",
|
|
66
|
+
"RegistrationResponse",
|
|
67
|
+
"TokenResponse",
|
|
68
|
+
"ValidationResult",
|
|
69
|
+
"TokenPayload",
|
|
70
|
+
"GopherAuthContext",
|
|
71
|
+
"ERROR_DESCRIPTIONS",
|
|
72
|
+
"get_error_description",
|
|
73
|
+
"gopher_create_empty_auth_context",
|
|
74
|
+
"is_gopher_auth_error",
|
|
75
|
+
"GopherAuthClient",
|
|
76
|
+
"GopherAuthConfig",
|
|
77
|
+
"GopherOAuthClient",
|
|
78
|
+
"GopherSessionManager",
|
|
79
|
+
"GopherValidationOptions",
|
|
80
|
+
"gopher_auth_auto_refresh",
|
|
81
|
+
"gopher_auth_build_oidc_discovery_metadata",
|
|
82
|
+
"gopher_auth_build_oauth_server_metadata",
|
|
83
|
+
"gopher_auth_build_protected_resource_metadata",
|
|
84
|
+
"gopher_auth_extract_bearer_token",
|
|
85
|
+
"gopher_auth_extract_method",
|
|
86
|
+
"gopher_auth_extract_path",
|
|
87
|
+
"gopher_auth_url_decode",
|
|
88
|
+
"gopher_auth_url_encode",
|
|
89
|
+
"gopher_auth_validate_all_scopes",
|
|
90
|
+
"gopher_auth_validate_any_scopes",
|
|
91
|
+
"gopher_auth_validate_idp",
|
|
92
|
+
"gopher_create_validation_options",
|
|
93
|
+
"gopher_generate_www_authenticate_header",
|
|
94
|
+
"gopher_generate_www_authenticate_header_v2",
|
|
95
|
+
"gopher_get_auth_library_version",
|
|
96
|
+
"gopher_init_auth_library",
|
|
97
|
+
"gopher_is_auth_library_initialized",
|
|
98
|
+
"gopher_shutdown_auth_library",
|
|
99
|
+
"is_auth_available",
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def __getattr__(name: str):
|
|
104
|
+
if name in _AUTH_EXPORTS:
|
|
105
|
+
auth = import_module("gopher_mcp_python.auth")
|
|
106
|
+
value = getattr(auth, name)
|
|
107
|
+
globals()[name] = value
|
|
108
|
+
return value
|
|
109
|
+
|
|
110
|
+
if name == "GopherAuthFfiError":
|
|
111
|
+
from gopher_mcp_python.ffi.auth import GopherAuthError as GopherAuthFfiError
|
|
112
|
+
|
|
113
|
+
globals()[name] = GopherAuthFfiError
|
|
114
|
+
return GopherAuthFfiError
|
|
115
|
+
|
|
116
|
+
if name in _AUTH_FFI_EXPORTS:
|
|
117
|
+
ffi_auth = import_module("gopher_mcp_python.ffi.auth")
|
|
118
|
+
value = getattr(ffi_auth, name)
|
|
119
|
+
globals()[name] = value
|
|
120
|
+
return value
|
|
121
|
+
|
|
122
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
95
123
|
|
|
96
|
-
__version__ = "0.1.30"
|
|
97
124
|
|
|
98
125
|
__all__ = [
|
|
99
126
|
# Main classes
|
|
100
127
|
"GopherAgent",
|
|
101
128
|
"GopherAgentConfig",
|
|
102
129
|
"GopherAgentConfigBuilder",
|
|
130
|
+
"GopherAgentCreateOptions",
|
|
131
|
+
"GopherAgentOAuthOptions",
|
|
103
132
|
"GopherAgentRuntimeOptions",
|
|
133
|
+
"GopherAgentTokenRecord",
|
|
134
|
+
"GopherAgentTokenStore",
|
|
104
135
|
"AgentResult",
|
|
105
136
|
"AgentResultStatus",
|
|
106
137
|
"AgentResultBuilder",
|
|
107
138
|
"ServerConfig",
|
|
139
|
+
"ServerConfigRoute",
|
|
108
140
|
# Errors
|
|
109
141
|
"AgentError",
|
|
110
142
|
"ApiKeyError",
|
|
@@ -114,7 +146,9 @@ __all__ = [
|
|
|
114
146
|
"GopherOrchLibrary",
|
|
115
147
|
"GopherOrchHandle",
|
|
116
148
|
# Auth
|
|
149
|
+
"GopherAuth",
|
|
117
150
|
"GopherAuthError",
|
|
151
|
+
"GopherAuthFfiError",
|
|
118
152
|
"AutoRefreshResult",
|
|
119
153
|
"RegistrationResponse",
|
|
120
154
|
"TokenResponse",
|
|
@@ -150,8 +184,6 @@ __all__ = [
|
|
|
150
184
|
"gopher_is_auth_library_initialized",
|
|
151
185
|
"gopher_shutdown_auth_library",
|
|
152
186
|
"is_auth_available",
|
|
153
|
-
"GopherAuth",
|
|
154
|
-
"GopherAuthBaseError",
|
|
155
187
|
"ConfigurationError",
|
|
156
188
|
"InsufficientScopesError",
|
|
157
189
|
"JwksError",
|
|
@@ -27,10 +27,21 @@ Example with context manager:
|
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
29
|
import atexit
|
|
30
|
+
import asyncio
|
|
31
|
+
import weakref
|
|
30
32
|
from typing import Callable, Optional
|
|
31
33
|
|
|
34
|
+
import gopher_mcp_python.oauth_resolver as oauth_resolver
|
|
32
35
|
from gopher_mcp_python.config import GopherAgentConfig
|
|
36
|
+
from gopher_mcp_python.runtime_options import (
|
|
37
|
+
GopherAgentOAuthOptions,
|
|
38
|
+
GopherAgentRuntimeOptions,
|
|
39
|
+
RuntimeOptionsInput,
|
|
40
|
+
normalize_create_options,
|
|
41
|
+
normalize_runtime_options,
|
|
42
|
+
)
|
|
33
43
|
from gopher_mcp_python.result import AgentResult, AgentResultStatus
|
|
44
|
+
from gopher_mcp_python.server_config import ServerConfig, ServerConfigRoute
|
|
34
45
|
from gopher_mcp_python.errors import AgentError, TimeoutError
|
|
35
46
|
from gopher_mcp_python.ffi import GopherOrchLibrary, GopherOrchHandle
|
|
36
47
|
|
|
@@ -51,6 +62,7 @@ class GopherAgent:
|
|
|
51
62
|
"""
|
|
52
63
|
self._handle = handle
|
|
53
64
|
self._disposed = False
|
|
65
|
+
self._finalizer = weakref.finalize(self, _release_handle_best_effort, handle)
|
|
54
66
|
|
|
55
67
|
def __enter__(self) -> "GopherAgent":
|
|
56
68
|
"""Context manager entry."""
|
|
@@ -103,7 +115,7 @@ class GopherAgent:
|
|
|
103
115
|
@staticmethod
|
|
104
116
|
def create(config: GopherAgentConfig) -> "GopherAgent":
|
|
105
117
|
"""
|
|
106
|
-
Create a new GopherAgent instance.
|
|
118
|
+
Create a new GopherAgent instance, resolving SDK OAuth credentials if needed.
|
|
107
119
|
|
|
108
120
|
Args:
|
|
109
121
|
config: Agent configuration
|
|
@@ -114,46 +126,26 @@ class GopherAgent:
|
|
|
114
126
|
Raises:
|
|
115
127
|
AgentError: if agent creation fails
|
|
116
128
|
"""
|
|
117
|
-
if
|
|
118
|
-
GopherAgent.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
config.model,
|
|
131
|
-
config.api_key,
|
|
132
|
-
config.runtime_options,
|
|
133
|
-
)
|
|
134
|
-
else:
|
|
135
|
-
handle = lib.agent_create_by_json(
|
|
136
|
-
config.provider,
|
|
137
|
-
config.model,
|
|
138
|
-
config.server_config,
|
|
139
|
-
config.runtime_options,
|
|
140
|
-
)
|
|
141
|
-
except Exception as e:
|
|
142
|
-
raise AgentError(f"Failed to create agent: {e}")
|
|
143
|
-
|
|
144
|
-
if handle is None:
|
|
145
|
-
error = lib.get_last_error_message()
|
|
146
|
-
lib.clear_error()
|
|
147
|
-
raise AgentError(error or _build_create_error_message())
|
|
148
|
-
|
|
149
|
-
return GopherAgent(handle)
|
|
129
|
+
if config.has_api_key():
|
|
130
|
+
return GopherAgent.create_with_api_key(
|
|
131
|
+
config.provider,
|
|
132
|
+
config.model,
|
|
133
|
+
config.api_key,
|
|
134
|
+
config.runtime_options,
|
|
135
|
+
)
|
|
136
|
+
return GopherAgent.create_with_server_config(
|
|
137
|
+
config.provider,
|
|
138
|
+
config.model,
|
|
139
|
+
config.server_config,
|
|
140
|
+
config.runtime_options,
|
|
141
|
+
)
|
|
150
142
|
|
|
151
143
|
@staticmethod
|
|
152
144
|
def create_with_api_key(
|
|
153
145
|
provider: str,
|
|
154
146
|
model: str,
|
|
155
147
|
api_key: str,
|
|
156
|
-
runtime_options:
|
|
148
|
+
runtime_options: RuntimeOptionsInput = None,
|
|
157
149
|
) -> "GopherAgent":
|
|
158
150
|
"""
|
|
159
151
|
Create a new GopherAgent with API key.
|
|
@@ -167,22 +159,31 @@ class GopherAgent:
|
|
|
167
159
|
Returns:
|
|
168
160
|
GopherAgent instance
|
|
169
161
|
"""
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
.
|
|
162
|
+
create_options = normalize_create_options(runtime_options)
|
|
163
|
+
runtime_options = normalize_runtime_options(create_options)
|
|
164
|
+
oauth = create_options.oauth if create_options is not None else None
|
|
165
|
+
if _should_skip_oauth(runtime_options, oauth):
|
|
166
|
+
return GopherAgent._create_from_ffi(
|
|
167
|
+
lambda lib: lib.agent_create_by_api_key(
|
|
168
|
+
provider, model, api_key, runtime_options
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
return _create_from_api_config(
|
|
173
|
+
provider,
|
|
174
|
+
model,
|
|
175
|
+
api_key,
|
|
176
|
+
route=None,
|
|
177
|
+
runtime_options=runtime_options,
|
|
178
|
+
oauth=oauth,
|
|
175
179
|
)
|
|
176
|
-
if runtime_options is not None:
|
|
177
|
-
builder.runtime_options(runtime_options)
|
|
178
|
-
return GopherAgent.create(builder.build())
|
|
179
180
|
|
|
180
181
|
@staticmethod
|
|
181
182
|
def create_with_server_config(
|
|
182
183
|
provider: str,
|
|
183
184
|
model: str,
|
|
184
185
|
server_config: str,
|
|
185
|
-
runtime_options:
|
|
186
|
+
runtime_options: RuntimeOptionsInput = None,
|
|
186
187
|
) -> "GopherAgent":
|
|
187
188
|
"""
|
|
188
189
|
Create a new GopherAgent with JSON server config.
|
|
@@ -196,15 +197,29 @@ class GopherAgent:
|
|
|
196
197
|
Returns:
|
|
197
198
|
GopherAgent instance
|
|
198
199
|
"""
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
.
|
|
200
|
+
create_options = normalize_create_options(runtime_options)
|
|
201
|
+
runtime_options = normalize_runtime_options(create_options)
|
|
202
|
+
oauth = create_options.oauth if create_options is not None else None
|
|
203
|
+
if _should_skip_oauth(runtime_options, oauth):
|
|
204
|
+
return GopherAgent._create_from_ffi(
|
|
205
|
+
lambda lib: lib.agent_create_by_json(
|
|
206
|
+
provider, model, server_config, runtime_options
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
resolved_runtime_options = _run_oauth_coroutine(
|
|
211
|
+
lambda: oauth_resolver.resolve_runtime_options_with_oauth(
|
|
212
|
+
urls=[],
|
|
213
|
+
server_config=server_config,
|
|
214
|
+
runtime_options=runtime_options,
|
|
215
|
+
oauth=oauth,
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
return GopherAgent._create_from_ffi(
|
|
219
|
+
lambda lib: lib.agent_create_by_json(
|
|
220
|
+
provider, model, server_config, resolved_runtime_options
|
|
221
|
+
)
|
|
204
222
|
)
|
|
205
|
-
if runtime_options is not None:
|
|
206
|
-
builder.runtime_options(runtime_options)
|
|
207
|
-
return GopherAgent.create(builder.build())
|
|
208
223
|
|
|
209
224
|
@staticmethod
|
|
210
225
|
def create_with_server_id(
|
|
@@ -212,7 +227,7 @@ class GopherAgent:
|
|
|
212
227
|
model: str,
|
|
213
228
|
api_key: str,
|
|
214
229
|
server_id: str,
|
|
215
|
-
runtime_options:
|
|
230
|
+
runtime_options: RuntimeOptionsInput = None,
|
|
216
231
|
) -> "GopherAgent":
|
|
217
232
|
"""
|
|
218
233
|
Create a new GopherAgent scoped to a single MCP server by id.
|
|
@@ -231,10 +246,22 @@ class GopherAgent:
|
|
|
231
246
|
Returns:
|
|
232
247
|
GopherAgent instance
|
|
233
248
|
"""
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
249
|
+
create_options = normalize_create_options(runtime_options)
|
|
250
|
+
normalized_runtime_options = normalize_runtime_options(create_options)
|
|
251
|
+
oauth = create_options.oauth if create_options is not None else None
|
|
252
|
+
if _should_skip_oauth(normalized_runtime_options, oauth):
|
|
253
|
+
return GopherAgent._create_from_ffi(
|
|
254
|
+
lambda lib: lib.agent_create_by_server_id(
|
|
255
|
+
provider, model, api_key, server_id, normalized_runtime_options
|
|
256
|
+
)
|
|
237
257
|
)
|
|
258
|
+
return _create_from_api_config(
|
|
259
|
+
provider,
|
|
260
|
+
model,
|
|
261
|
+
api_key,
|
|
262
|
+
route=ServerConfigRoute("serverId", server_id),
|
|
263
|
+
runtime_options=normalized_runtime_options,
|
|
264
|
+
oauth=oauth,
|
|
238
265
|
)
|
|
239
266
|
|
|
240
267
|
@staticmethod
|
|
@@ -243,7 +270,7 @@ class GopherAgent:
|
|
|
243
270
|
model: str,
|
|
244
271
|
api_key: str,
|
|
245
272
|
server_name: str,
|
|
246
|
-
runtime_options:
|
|
273
|
+
runtime_options: RuntimeOptionsInput = None,
|
|
247
274
|
) -> "GopherAgent":
|
|
248
275
|
"""
|
|
249
276
|
Create a new GopherAgent scoped to a single MCP server by name.
|
|
@@ -262,10 +289,22 @@ class GopherAgent:
|
|
|
262
289
|
Returns:
|
|
263
290
|
GopherAgent instance
|
|
264
291
|
"""
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
292
|
+
create_options = normalize_create_options(runtime_options)
|
|
293
|
+
normalized_runtime_options = normalize_runtime_options(create_options)
|
|
294
|
+
oauth = create_options.oauth if create_options is not None else None
|
|
295
|
+
if _should_skip_oauth(normalized_runtime_options, oauth):
|
|
296
|
+
return GopherAgent._create_from_ffi(
|
|
297
|
+
lambda lib: lib.agent_create_by_server_name(
|
|
298
|
+
provider, model, api_key, server_name, normalized_runtime_options
|
|
299
|
+
)
|
|
268
300
|
)
|
|
301
|
+
return _create_from_api_config(
|
|
302
|
+
provider,
|
|
303
|
+
model,
|
|
304
|
+
api_key,
|
|
305
|
+
route=ServerConfigRoute("serverName", server_name),
|
|
306
|
+
runtime_options=normalized_runtime_options,
|
|
307
|
+
oauth=oauth,
|
|
269
308
|
)
|
|
270
309
|
|
|
271
310
|
@staticmethod
|
|
@@ -274,7 +313,7 @@ class GopherAgent:
|
|
|
274
313
|
model: str,
|
|
275
314
|
api_key: str,
|
|
276
315
|
gateway_id: str,
|
|
277
|
-
runtime_options:
|
|
316
|
+
runtime_options: RuntimeOptionsInput = None,
|
|
278
317
|
) -> "GopherAgent":
|
|
279
318
|
"""
|
|
280
319
|
Create a new GopherAgent scoped to a single MCP gateway by id.
|
|
@@ -293,10 +332,22 @@ class GopherAgent:
|
|
|
293
332
|
Returns:
|
|
294
333
|
GopherAgent instance
|
|
295
334
|
"""
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
335
|
+
create_options = normalize_create_options(runtime_options)
|
|
336
|
+
normalized_runtime_options = normalize_runtime_options(create_options)
|
|
337
|
+
oauth = create_options.oauth if create_options is not None else None
|
|
338
|
+
if _should_skip_oauth(normalized_runtime_options, oauth):
|
|
339
|
+
return GopherAgent._create_from_ffi(
|
|
340
|
+
lambda lib: lib.agent_create_by_gateway_id(
|
|
341
|
+
provider, model, api_key, gateway_id, normalized_runtime_options
|
|
342
|
+
)
|
|
299
343
|
)
|
|
344
|
+
return _create_from_api_config(
|
|
345
|
+
provider,
|
|
346
|
+
model,
|
|
347
|
+
api_key,
|
|
348
|
+
route=ServerConfigRoute("gatewayId", gateway_id),
|
|
349
|
+
runtime_options=normalized_runtime_options,
|
|
350
|
+
oauth=oauth,
|
|
300
351
|
)
|
|
301
352
|
|
|
302
353
|
@staticmethod
|
|
@@ -305,7 +356,7 @@ class GopherAgent:
|
|
|
305
356
|
model: str,
|
|
306
357
|
api_key: str,
|
|
307
358
|
gateway_name: str,
|
|
308
|
-
runtime_options:
|
|
359
|
+
runtime_options: RuntimeOptionsInput = None,
|
|
309
360
|
) -> "GopherAgent":
|
|
310
361
|
"""
|
|
311
362
|
Create a new GopherAgent scoped to a single MCP gateway by name.
|
|
@@ -324,10 +375,22 @@ class GopherAgent:
|
|
|
324
375
|
Returns:
|
|
325
376
|
GopherAgent instance
|
|
326
377
|
"""
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
378
|
+
create_options = normalize_create_options(runtime_options)
|
|
379
|
+
normalized_runtime_options = normalize_runtime_options(create_options)
|
|
380
|
+
oauth = create_options.oauth if create_options is not None else None
|
|
381
|
+
if _should_skip_oauth(normalized_runtime_options, oauth):
|
|
382
|
+
return GopherAgent._create_from_ffi(
|
|
383
|
+
lambda lib: lib.agent_create_by_gateway_name(
|
|
384
|
+
provider, model, api_key, gateway_name, normalized_runtime_options
|
|
385
|
+
)
|
|
330
386
|
)
|
|
387
|
+
return _create_from_api_config(
|
|
388
|
+
provider,
|
|
389
|
+
model,
|
|
390
|
+
api_key,
|
|
391
|
+
route=ServerConfigRoute("gatewayName", gateway_name),
|
|
392
|
+
runtime_options=normalized_runtime_options,
|
|
393
|
+
oauth=oauth,
|
|
331
394
|
)
|
|
332
395
|
|
|
333
396
|
@staticmethod
|
|
@@ -335,7 +398,7 @@ class GopherAgent:
|
|
|
335
398
|
provider: str,
|
|
336
399
|
model: str,
|
|
337
400
|
url: str,
|
|
338
|
-
runtime_options:
|
|
401
|
+
runtime_options: RuntimeOptionsInput = None,
|
|
339
402
|
) -> "GopherAgent":
|
|
340
403
|
"""
|
|
341
404
|
Create a new GopherAgent for a single MCP server reachable at a URL.
|
|
@@ -354,8 +417,21 @@ class GopherAgent:
|
|
|
354
417
|
Returns:
|
|
355
418
|
GopherAgent instance
|
|
356
419
|
"""
|
|
420
|
+
create_options = normalize_create_options(runtime_options)
|
|
421
|
+
normalized_runtime_options = normalize_runtime_options(create_options)
|
|
422
|
+
oauth = create_options.oauth if create_options is not None else None
|
|
423
|
+
if url != "" and not _should_skip_oauth(normalized_runtime_options, oauth):
|
|
424
|
+
normalized_runtime_options = _run_oauth_coroutine(
|
|
425
|
+
lambda: oauth_resolver.resolve_url_runtime_options_with_oauth(
|
|
426
|
+
url,
|
|
427
|
+
runtime_options=normalized_runtime_options,
|
|
428
|
+
oauth=oauth,
|
|
429
|
+
)
|
|
430
|
+
)
|
|
357
431
|
return GopherAgent._create_from_ffi(
|
|
358
|
-
lambda lib: lib.agent_create_by_url(
|
|
432
|
+
lambda lib: lib.agent_create_by_url(
|
|
433
|
+
provider, model, url, normalized_runtime_options
|
|
434
|
+
)
|
|
359
435
|
)
|
|
360
436
|
|
|
361
437
|
@staticmethod
|
|
@@ -379,6 +455,8 @@ class GopherAgent:
|
|
|
379
455
|
|
|
380
456
|
try:
|
|
381
457
|
handle = create_handle(lib)
|
|
458
|
+
except AgentError:
|
|
459
|
+
raise
|
|
382
460
|
except Exception as e:
|
|
383
461
|
raise AgentError(f"Failed to create agent: {e}")
|
|
384
462
|
|
|
@@ -449,14 +527,18 @@ class GopherAgent:
|
|
|
449
527
|
return AgentResult.error(str(e))
|
|
450
528
|
|
|
451
529
|
def dispose(self) -> None:
|
|
452
|
-
"""
|
|
530
|
+
"""
|
|
531
|
+
Dispose of the agent and free native resources deterministically.
|
|
532
|
+
|
|
533
|
+
A best-effort finalizer also releases forgotten agents during garbage
|
|
534
|
+
collection, but callers should prefer dispose() or a with block.
|
|
535
|
+
"""
|
|
453
536
|
if self._disposed:
|
|
454
537
|
return
|
|
455
538
|
|
|
456
539
|
self._disposed = True
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
lib.agent_release(self._handle)
|
|
540
|
+
if self._finalizer.alive:
|
|
541
|
+
self._finalizer()
|
|
460
542
|
|
|
461
543
|
def is_disposed(self) -> bool:
|
|
462
544
|
"""Check if agent is disposed."""
|
|
@@ -478,6 +560,64 @@ def _setup_cleanup_handler() -> None:
|
|
|
478
560
|
atexit.register(GopherAgent.shutdown)
|
|
479
561
|
|
|
480
562
|
|
|
563
|
+
def _release_handle_best_effort(handle: GopherOrchHandle) -> None:
|
|
564
|
+
try:
|
|
565
|
+
lib = GopherOrchLibrary.get_instance()
|
|
566
|
+
if lib is not None and handle is not None:
|
|
567
|
+
lib.agent_release(handle)
|
|
568
|
+
except Exception:
|
|
569
|
+
return
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
def _create_from_api_config(
|
|
573
|
+
provider: str,
|
|
574
|
+
model: str,
|
|
575
|
+
api_key: str,
|
|
576
|
+
route: Optional[ServerConfigRoute],
|
|
577
|
+
runtime_options: Optional[GopherAgentRuntimeOptions],
|
|
578
|
+
oauth: Optional[GopherAgentOAuthOptions],
|
|
579
|
+
) -> GopherAgent:
|
|
580
|
+
server_config = ServerConfig.fetch(api_key, route=route)
|
|
581
|
+
resolved_runtime_options = _run_oauth_coroutine(
|
|
582
|
+
lambda: oauth_resolver.resolve_runtime_options_with_oauth(
|
|
583
|
+
urls=[],
|
|
584
|
+
server_config=server_config,
|
|
585
|
+
runtime_options=runtime_options,
|
|
586
|
+
oauth=oauth,
|
|
587
|
+
)
|
|
588
|
+
)
|
|
589
|
+
return GopherAgent._create_from_ffi(
|
|
590
|
+
lambda lib: lib.agent_create_by_json(
|
|
591
|
+
provider, model, server_config, resolved_runtime_options
|
|
592
|
+
)
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def _run_oauth_coroutine(create_coroutine):
|
|
597
|
+
try:
|
|
598
|
+
asyncio.get_running_loop()
|
|
599
|
+
except RuntimeError:
|
|
600
|
+
return asyncio.run(create_coroutine())
|
|
601
|
+
raise AgentError(
|
|
602
|
+
"SDK OAuth auto-flow cannot run inside an active asyncio event loop. "
|
|
603
|
+
"Provide runtime_options with access_token/Authorization, or set "
|
|
604
|
+
'oauth.mode to "disabled".'
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
def _should_skip_oauth(
|
|
609
|
+
runtime_options: Optional[GopherAgentRuntimeOptions],
|
|
610
|
+
oauth: Optional[GopherAgentOAuthOptions],
|
|
611
|
+
) -> bool:
|
|
612
|
+
if oauth is not None and oauth.mode == "disabled":
|
|
613
|
+
return True
|
|
614
|
+
if runtime_options is None:
|
|
615
|
+
return False
|
|
616
|
+
if runtime_options.access_token is not None:
|
|
617
|
+
return True
|
|
618
|
+
return any(name.lower() == "authorization" for name in runtime_options.headers)
|
|
619
|
+
|
|
620
|
+
|
|
481
621
|
def _build_create_error_message() -> str:
|
|
482
622
|
"""
|
|
483
623
|
Build the AgentError message for a null native create*() result.
|
|
@@ -490,6 +630,6 @@ def _build_create_error_message() -> str:
|
|
|
490
630
|
"Failed to create agent: native library returned null without a "
|
|
491
631
|
"specific error. Most often this means every configured MCP server "
|
|
492
632
|
"failed to connect or returned no tools (TLS / network / bad URL), "
|
|
493
|
-
"or the LLM provider could not be initialized. Set
|
|
494
|
-
"
|
|
633
|
+
"or the LLM provider could not be initialized. Set DEBUG=1 to see "
|
|
634
|
+
"Python-side native library load diagnostics and native-side logs."
|
|
495
635
|
)
|