managed-research 0.1.0__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.
Files changed (65) hide show
  1. managed_research-0.1.0/.gitignore +7 -0
  2. managed_research-0.1.0/PKG-INFO +73 -0
  3. managed_research-0.1.0/README.md +59 -0
  4. managed_research-0.1.0/managed_research/__init__.py +53 -0
  5. managed_research-0.1.0/managed_research/auth.py +75 -0
  6. managed_research-0.1.0/managed_research/client.py +3 -0
  7. managed_research-0.1.0/managed_research/errors.py +8 -0
  8. managed_research-0.1.0/managed_research/mcp/__init__.py +5 -0
  9. managed_research-0.1.0/managed_research/mcp/__main__.py +5 -0
  10. managed_research-0.1.0/managed_research/mcp/registry.py +47 -0
  11. managed_research-0.1.0/managed_research/mcp/server.py +1165 -0
  12. managed_research-0.1.0/managed_research/mcp/tools/__init__.py +19 -0
  13. managed_research-0.1.0/managed_research/mcp/tools/approvals.py +93 -0
  14. managed_research-0.1.0/managed_research/mcp/tools/artifacts.py +155 -0
  15. managed_research-0.1.0/managed_research/mcp/tools/integrations.py +211 -0
  16. managed_research-0.1.0/managed_research/mcp/tools/logs.py +97 -0
  17. managed_research-0.1.0/managed_research/mcp/tools/projects.py +399 -0
  18. managed_research-0.1.0/managed_research/mcp/tools/runs.py +575 -0
  19. managed_research-0.1.0/managed_research/mcp/tools/usage.py +107 -0
  20. managed_research-0.1.0/managed_research/models/__init__.py +37 -0
  21. managed_research-0.1.0/managed_research/models/generated/__init__.py +1 -0
  22. managed_research-0.1.0/managed_research/models/generated/v1/__init__.py +39 -0
  23. managed_research-0.1.0/managed_research/models/generated/v1/approvals.py +42 -0
  24. managed_research-0.1.0/managed_research/models/generated/v1/artifacts.py +40 -0
  25. managed_research-0.1.0/managed_research/models/generated/v1/common.py +60 -0
  26. managed_research-0.1.0/managed_research/models/generated/v1/integrations.py +125 -0
  27. managed_research-0.1.0/managed_research/models/generated/v1/logs.py +38 -0
  28. managed_research-0.1.0/managed_research/models/generated/v1/projects.py +71 -0
  29. managed_research-0.1.0/managed_research/models/generated/v1/questions.py +40 -0
  30. managed_research-0.1.0/managed_research/models/generated/v1/runs.py +75 -0
  31. managed_research-0.1.0/managed_research/models/generated/v1/usage.py +37 -0
  32. managed_research-0.1.0/managed_research/py.typed +1 -0
  33. managed_research-0.1.0/managed_research/schema_sync.py +110 -0
  34. managed_research-0.1.0/managed_research/sdk/__init__.py +65 -0
  35. managed_research-0.1.0/managed_research/sdk/_base.py +18 -0
  36. managed_research-0.1.0/managed_research/sdk/approvals.py +52 -0
  37. managed_research-0.1.0/managed_research/sdk/artifacts.py +46 -0
  38. managed_research-0.1.0/managed_research/sdk/client.py +2371 -0
  39. managed_research-0.1.0/managed_research/sdk/integrations.py +93 -0
  40. managed_research-0.1.0/managed_research/sdk/logs.py +52 -0
  41. managed_research-0.1.0/managed_research/sdk/projects.py +91 -0
  42. managed_research-0.1.0/managed_research/sdk/runs.py +77 -0
  43. managed_research-0.1.0/managed_research/sdk/usage.py +44 -0
  44. managed_research-0.1.0/managed_research/transport/__init__.py +25 -0
  45. managed_research-0.1.0/managed_research/transport/http.py +175 -0
  46. managed_research-0.1.0/managed_research/transport/pagination.py +49 -0
  47. managed_research-0.1.0/managed_research/transport/retries.py +67 -0
  48. managed_research-0.1.0/managed_research/transport/streaming.py +43 -0
  49. managed_research-0.1.0/managed_research/version.py +10 -0
  50. managed_research-0.1.0/pyproject.toml +40 -0
  51. managed_research-0.1.0/schemas/generated/README.md +14 -0
  52. managed_research-0.1.0/scripts/sync_public_schemas.py +7 -0
  53. managed_research-0.1.0/tests/contract/test_public_surface.py +55 -0
  54. managed_research-0.1.0/tests/contract/test_schema_sync.py +59 -0
  55. managed_research-0.1.0/tests/mcp/test_mcp_integrations.py +194 -0
  56. managed_research-0.1.0/tests/mcp/test_mcp_stdio_transport.py +75 -0
  57. managed_research-0.1.0/tests/mcp/test_registry.py +16 -0
  58. managed_research-0.1.0/tests/models/test_generated_models.py +113 -0
  59. managed_research-0.1.0/tests/sdk/test_client_integrations.py +217 -0
  60. managed_research-0.1.0/tests/sdk/test_data_factory.py +1128 -0
  61. managed_research-0.1.0/tests/sdk/test_namespaces.py +81 -0
  62. managed_research-0.1.0/tests/sdk/test_package_exports.py +8 -0
  63. managed_research-0.1.0/tests/transport/test_helpers.py +62 -0
  64. managed_research-0.1.0/tests/transport/test_http.py +124 -0
  65. managed_research-0.1.0/uv.lock +304 -0
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ __pycache__/
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ build/
6
+ dist/
7
+ *.egg-info/
@@ -0,0 +1,73 @@
1
+ Metadata-Version: 2.4
2
+ Name: managed-research
3
+ Version: 0.1.0
4
+ Summary: Pure-Python SMR API client and MCP server.
5
+ Author: Synth Laboratories
6
+ License: MIT
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: httpx>=0.28.1
9
+ Requires-Dist: pynacl>=1.5.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest>=8.3.3; extra == 'dev'
12
+ Requires-Dist: ruff>=0.11.0; extra == 'dev'
13
+ Description-Content-Type: text/markdown
14
+
15
+ # managed-research
16
+
17
+ Pure-Python public SMR surfaces for Managed Research.
18
+
19
+ Current scope:
20
+
21
+ - SMR API client
22
+ - SMR MCP stdio server
23
+ - generated-schema sync path for public SMR contracts
24
+
25
+ Out of scope for this slice:
26
+
27
+ - runtime internals
28
+ - sandbox/session control
29
+ - private backend models
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ pip install -U managed-research
35
+ ```
36
+
37
+ For local development:
38
+
39
+ ```bash
40
+ uv sync --extra dev
41
+ ```
42
+
43
+ ## Run the MCP server
44
+
45
+ ```bash
46
+ uv run managed-research-mcp
47
+ ```
48
+
49
+ The package reads `SYNTH_API_KEY` and `SYNTH_BACKEND_URL` from the environment.
50
+
51
+ Python API surface:
52
+
53
+ ```python
54
+ from managed_research import ManagedResearchClient
55
+
56
+ client = ManagedResearchClient(api_key="sk_...")
57
+ projects = client.list_projects()
58
+ ```
59
+
60
+ ## Sync exported public schemas
61
+
62
+ ```bash
63
+ uv run python scripts/sync_public_schemas.py --source /path/to/exported/schemas
64
+ ```
65
+
66
+ If you prefer environment configuration, set `MANAGED_RESEARCH_SCHEMA_SOURCE`
67
+ instead of passing `--source`.
68
+
69
+ There is also a console entrypoint:
70
+
71
+ ```bash
72
+ uv run managed-research-sync-schemas --source /path/to/exported/schemas
73
+ ```
@@ -0,0 +1,59 @@
1
+ # managed-research
2
+
3
+ Pure-Python public SMR surfaces for Managed Research.
4
+
5
+ Current scope:
6
+
7
+ - SMR API client
8
+ - SMR MCP stdio server
9
+ - generated-schema sync path for public SMR contracts
10
+
11
+ Out of scope for this slice:
12
+
13
+ - runtime internals
14
+ - sandbox/session control
15
+ - private backend models
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ pip install -U managed-research
21
+ ```
22
+
23
+ For local development:
24
+
25
+ ```bash
26
+ uv sync --extra dev
27
+ ```
28
+
29
+ ## Run the MCP server
30
+
31
+ ```bash
32
+ uv run managed-research-mcp
33
+ ```
34
+
35
+ The package reads `SYNTH_API_KEY` and `SYNTH_BACKEND_URL` from the environment.
36
+
37
+ Python API surface:
38
+
39
+ ```python
40
+ from managed_research import ManagedResearchClient
41
+
42
+ client = ManagedResearchClient(api_key="sk_...")
43
+ projects = client.list_projects()
44
+ ```
45
+
46
+ ## Sync exported public schemas
47
+
48
+ ```bash
49
+ uv run python scripts/sync_public_schemas.py --source /path/to/exported/schemas
50
+ ```
51
+
52
+ If you prefer environment configuration, set `MANAGED_RESEARCH_SCHEMA_SOURCE`
53
+ instead of passing `--source`.
54
+
55
+ There is also a console entrypoint:
56
+
57
+ ```bash
58
+ uv run managed-research-sync-schemas --source /path/to/exported/schemas
59
+ ```
@@ -0,0 +1,53 @@
1
+ """Public Managed Research package."""
2
+
3
+ from managed_research.errors import SmrApiError
4
+ from managed_research.models import (
5
+ SmrActorStatus,
6
+ SmrApproval,
7
+ SmrArtifact,
8
+ SmrCapabilities,
9
+ SmrIntegrationStatus,
10
+ SmrLinearTeam,
11
+ SmrLinearTeamListing,
12
+ SmrOAuthStart,
13
+ SmrProject,
14
+ SmrProjectStatusSnapshot,
15
+ SmrProviderKeyStatus,
16
+ SmrQuestion,
17
+ SmrRun,
18
+ SmrRunEconomics,
19
+ SmrRunLogArchive,
20
+ )
21
+ from managed_research.sdk.client import (
22
+ ACTIVE_RUN_STATES,
23
+ DEFAULT_TIMEOUT_SECONDS,
24
+ ManagedResearchClient,
25
+ SmrControlClient,
26
+ first_id,
27
+ )
28
+ from managed_research.version import __version__
29
+
30
+ __all__ = [
31
+ "ACTIVE_RUN_STATES",
32
+ "DEFAULT_TIMEOUT_SECONDS",
33
+ "ManagedResearchClient",
34
+ "SmrActorStatus",
35
+ "SmrApiError",
36
+ "SmrApproval",
37
+ "SmrArtifact",
38
+ "SmrCapabilities",
39
+ "SmrControlClient",
40
+ "SmrIntegrationStatus",
41
+ "SmrLinearTeam",
42
+ "SmrLinearTeamListing",
43
+ "SmrOAuthStart",
44
+ "SmrProject",
45
+ "SmrProjectStatusSnapshot",
46
+ "SmrProviderKeyStatus",
47
+ "SmrQuestion",
48
+ "SmrRun",
49
+ "SmrRunEconomics",
50
+ "SmrRunLogArchive",
51
+ "__version__",
52
+ "first_id",
53
+ ]
@@ -0,0 +1,75 @@
1
+ """Auth and transport helpers for the public Managed Research package."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import base64
6
+ import json
7
+ import os
8
+ from pathlib import Path
9
+ from urllib.parse import urlparse, urlunparse
10
+
11
+ from nacl.public import PublicKey, SealedBox
12
+
13
+ BACKEND_URL_BASE = (os.getenv("SYNTH_BACKEND_URL") or "https://api.usesynth.ai").strip()
14
+
15
+
16
+ def _read_json_object(path: Path) -> dict[str, object]:
17
+ try:
18
+ raw = json.loads(path.read_text(encoding="utf-8"))
19
+ except Exception:
20
+ return {}
21
+ return raw if isinstance(raw, dict) else {}
22
+
23
+
24
+ def _config_search_paths() -> list[Path]:
25
+ home = Path.home()
26
+ return [
27
+ home / ".synth_ai" / "config.json",
28
+ home / ".config" / "synth" / "config.json",
29
+ ]
30
+
31
+
32
+ def get_api_key(env_key: str = "SYNTH_API_KEY", required: bool = True) -> str | None:
33
+ """Resolve the Synth API key from environment or local config."""
34
+ value = (os.getenv(env_key) or "").strip()
35
+ if value:
36
+ return value
37
+
38
+ for path in _config_search_paths():
39
+ payload = _read_json_object(path)
40
+ config_value = payload.get(env_key)
41
+ if isinstance(config_value, str) and config_value.strip():
42
+ return config_value.strip()
43
+
44
+ if required:
45
+ raise ValueError(f"{env_key} is required (set {env_key} or store it in local Synth config)")
46
+ return None
47
+
48
+
49
+ def _strip_terminal_segment(path: str, segment: str) -> str:
50
+ trimmed = path.rstrip("/")
51
+ if trimmed.endswith(segment):
52
+ return trimmed[: -len(segment)].rstrip("/")
53
+ return trimmed
54
+
55
+
56
+ def normalize_backend_base(url: str) -> str:
57
+ """Normalize a backend URL down to the service base."""
58
+ parsed = urlparse(url.strip())
59
+ path = _strip_terminal_segment(parsed.path, "/v1")
60
+ path = _strip_terminal_segment(path, "/api")
61
+ normalized = parsed._replace(path=path.rstrip("/"), query="", fragment="")
62
+ return urlunparse(normalized)
63
+
64
+
65
+ def encrypt_for_backend(pubkey_b64: str, secret: str | bytes) -> str:
66
+ """Encrypt a provider key using the backend's sealed-box public key."""
67
+ if isinstance(secret, bytes):
68
+ secret = secret.decode("utf-8")
69
+ try:
70
+ pubkey_raw = base64.b64decode(pubkey_b64, validate=True)
71
+ except Exception as exc:
72
+ raise RuntimeError("Invalid backend public key (not base64)") from exc
73
+ box = SealedBox(PublicKey(pubkey_raw))
74
+ ciphertext = box.encrypt(secret.encode("utf-8"))
75
+ return base64.b64encode(ciphertext).decode("utf-8")
@@ -0,0 +1,3 @@
1
+ """Compatibility wrapper for the public SMR client surface."""
2
+
3
+ from managed_research.sdk.client import * # noqa: F403
@@ -0,0 +1,8 @@
1
+ """Public error types for Managed Research."""
2
+
3
+
4
+ class SmrApiError(RuntimeError):
5
+ """Raised when the SMR API returns a non-success response."""
6
+
7
+
8
+ __all__ = ["SmrApiError"]
@@ -0,0 +1,5 @@
1
+ """Managed Research MCP exports."""
2
+
3
+ from managed_research.mcp.server import ManagedResearchMcpServer, main, run_stdio_server
4
+
5
+ __all__ = ["ManagedResearchMcpServer", "main", "run_stdio_server"]
@@ -0,0 +1,5 @@
1
+ """Run the Managed Research MCP server with ``python -m managed_research.mcp``."""
2
+
3
+ from managed_research.mcp.server import main
4
+
5
+ main()
@@ -0,0 +1,47 @@
1
+ """Shared MCP registry types and schema helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass
7
+ from typing import Any
8
+
9
+ JSONDict = dict[str, Any]
10
+ ToolHandler = Callable[[JSONDict], Any]
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class ToolDefinition:
15
+ """MCP tool metadata and handler."""
16
+
17
+ name: str
18
+ description: str
19
+ input_schema: JSONDict
20
+ handler: ToolHandler
21
+
22
+
23
+ _CONNECTION_PROPERTIES: JSONDict = {
24
+ "api_key": {
25
+ "type": "string",
26
+ "description": "Optional API key override (defaults to SYNTH_API_KEY).",
27
+ },
28
+ "backend_base": {
29
+ "type": "string",
30
+ "description": "Optional backend URL override (defaults to SYNTH_BACKEND_URL).",
31
+ },
32
+ }
33
+
34
+
35
+ def tool_schema(properties: JSONDict, required: list[str]) -> JSONDict:
36
+ """Attach shared connection properties to a tool schema."""
37
+ merged: JSONDict = dict(properties)
38
+ merged.update(_CONNECTION_PROPERTIES)
39
+ return {
40
+ "type": "object",
41
+ "properties": merged,
42
+ "required": required,
43
+ "additionalProperties": False,
44
+ }
45
+
46
+
47
+ __all__ = ["JSONDict", "ToolDefinition", "ToolHandler", "tool_schema"]