datahub-agent-context 1.3.1.10rc1__tar.gz → 1.4.0rc2__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 (75) hide show
  1. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/PKG-INFO +18 -11
  2. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/README.md +3 -2
  3. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/setup.py +8 -0
  4. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/__init__.py +11 -3
  5. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/_version.py +1 -1
  6. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/cli.py +152 -0
  7. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/context.py +110 -0
  8. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/langchain_tools/builder.py +6 -4
  9. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/base.py +6 -3
  10. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/mcp_tools/save_document.py +634 -0
  11. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/__init__.py +0 -0
  12. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/generate_udfs.py +306 -0
  13. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/generators/__init__.py +21 -0
  14. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/generators/configuration.py +104 -0
  15. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/generators/cortex_agent.py +725 -0
  16. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/generators/network_rules.py +53 -0
  17. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/generators/stored_procedure.py +87 -0
  18. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/snowflake.py +662 -0
  19. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/__init__.py +1 -0
  20. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/add_glossary_terms.py +61 -0
  21. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/add_owners.py +59 -0
  22. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/add_structured_properties.py +57 -0
  23. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/add_tags.py +61 -0
  24. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/base.py +45 -0
  25. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/get_dataset_queries.py +68 -0
  26. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/get_entities.py +47 -0
  27. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/get_lineage.py +61 -0
  28. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/get_lineage_paths_between.py +69 -0
  29. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/get_me.py +51 -0
  30. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/grep_documents.py +70 -0
  31. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/list_schema_fields.py +80 -0
  32. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/remove_domains.py +45 -0
  33. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/remove_glossary_terms.py +57 -0
  34. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/remove_owners.py +56 -0
  35. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/remove_structured_properties.py +56 -0
  36. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/remove_tags.py +57 -0
  37. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/search_datahub.py +71 -0
  38. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/search_documents.py +58 -0
  39. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/set_domains.py +55 -0
  40. datahub_agent_context-1.4.0rc2/src/datahub_agent_context/snowflake/udfs/update_description.py +60 -0
  41. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context.egg-info/PKG-INFO +18 -11
  42. datahub_agent_context-1.4.0rc2/src/datahub_agent_context.egg-info/SOURCES.txt +72 -0
  43. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context.egg-info/requires.txt +15 -8
  44. datahub_agent_context-1.3.1.10rc1/src/datahub_agent_context/context.py +0 -97
  45. datahub_agent_context-1.3.1.10rc1/src/datahub_agent_context.egg-info/SOURCES.txt +0 -40
  46. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/pyproject.toml +0 -0
  47. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/setup.cfg +0 -0
  48. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/langchain_tools/__init__.py +0 -0
  49. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/__init__.py +0 -0
  50. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/_token_estimator.py +0 -0
  51. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/descriptions.py +0 -0
  52. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/documents.py +0 -0
  53. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/domains.py +0 -0
  54. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/entities.py +0 -0
  55. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/get_me.py +0 -0
  56. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/gql/__init__.py +0 -0
  57. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/gql/document_search.gql +0 -0
  58. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/gql/document_semantic_search.gql +0 -0
  59. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/gql/entity_details.gql +0 -0
  60. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/gql/queries.gql +0 -0
  61. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/gql/query_entity.gql +0 -0
  62. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/gql/read_documents.gql +0 -0
  63. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/gql/search.gql +0 -0
  64. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/helpers.py +0 -0
  65. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/lineage.py +0 -0
  66. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/owners.py +0 -0
  67. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/queries.py +0 -0
  68. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/search.py +0 -0
  69. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/structured_properties.py +0 -0
  70. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/tags.py +0 -0
  71. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/mcp_tools/terms.py +0 -0
  72. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context/py.typed +0 -0
  73. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context.egg-info/dependency_links.txt +0 -0
  74. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context.egg-info/not-zip-safe +0 -0
  75. {datahub_agent_context-1.3.1.10rc1 → datahub_agent_context-1.4.0rc2}/src/datahub_agent_context.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datahub-agent-context
3
- Version: 1.3.1.10rc1
3
+ Version: 1.4.0rc2
4
4
  Summary: DataHub Agent Context - MCP Tools for AI Agents
5
5
  Home-page: https://datahub.io/
6
6
  License: Apache License 2.0
@@ -29,26 +29,32 @@ Classifier: Topic :: Software Development
29
29
  Requires-Python: >=3.9
30
30
  Description-Content-Type: text/markdown
31
31
  Requires-Dist: jmespath<2.0.0,>=1.0.0
32
+ Requires-Dist: acryl-datahub==1.4.0rc2
32
33
  Requires-Dist: cachetools<7.0.0,>=5.0.0
33
- Requires-Dist: acryl-datahub==1.3.1.10rc1
34
+ Requires-Dist: httpcore<2.0,>=1.0.9
34
35
  Requires-Dist: pydantic<3.0.0,>=2.0.0
35
- Requires-Dist: json-repair<1.0.0,>=0.25.0
36
36
  Requires-Dist: h11<1.0,>=0.16
37
37
  Requires-Dist: google-re2<2.0,>=1.0
38
- Requires-Dist: httpcore<2.0,>=1.0.9
38
+ Requires-Dist: json-repair<1.0.0,>=0.25.0
39
39
  Provides-Extra: dev
40
- Requires-Dist: types-jmespath<2.0.0,>=1.0.0; extra == "dev"
40
+ Requires-Dist: types-requests<3.0.0,>=2.0.0; extra == "dev"
41
+ Requires-Dist: click<9.0.0,>=8.0.0; extra == "dev"
41
42
  Requires-Dist: types-PyYAML<7.0.0,>=6.0.0; extra == "dev"
42
- Requires-Dist: mypy==1.17.1; extra == "dev"
43
43
  Requires-Dist: types-toml<1.0.0,>=0.10.0; extra == "dev"
44
+ Requires-Dist: langchain-core<2.0.0,>=1.2.7; extra == "dev"
44
45
  Requires-Dist: types-cachetools<7.0.0,>=5.0.0; extra == "dev"
45
- Requires-Dist: pytest<9.0.0,>=8.3.4; extra == "dev"
46
46
  Requires-Dist: pytest-cov<7.0.0,>=2.8.0; extra == "dev"
47
- Requires-Dist: types-requests<3.0.0,>=2.0.0; extra == "dev"
48
- Requires-Dist: tox<5.0.0,>=4.0.0; extra == "dev"
47
+ Requires-Dist: mypy==1.17.1; extra == "dev"
49
48
  Requires-Dist: ruff==0.11.7; extra == "dev"
49
+ Requires-Dist: types-jmespath<2.0.0,>=1.0.0; extra == "dev"
50
+ Requires-Dist: snowflake-connector-python<4.0.0,>=3.0.0; extra == "dev"
51
+ Requires-Dist: pytest<9.0.0,>=8.3.4; extra == "dev"
52
+ Requires-Dist: tox<5.0.0,>=4.0.0; extra == "dev"
50
53
  Provides-Extra: langchain
51
54
  Requires-Dist: langchain-core<2.0.0,>=1.2.7; extra == "langchain"
55
+ Provides-Extra: snowflake
56
+ Requires-Dist: snowflake-connector-python<4.0.0,>=3.0.0; extra == "snowflake"
57
+ Requires-Dist: click<9.0.0,>=8.0.0; extra == "snowflake"
52
58
  Dynamic: classifier
53
59
  Dynamic: description
54
60
  Dynamic: description-content-type
@@ -105,7 +111,7 @@ from datahub_agent_context.mcp_tools.entities import get_entities
105
111
  client = DataHubClient.from_env()
106
112
 
107
113
  # Search for datasets
108
- with client.graph as graph:
114
+ with client as client:
109
115
  results = search(
110
116
  query="user_data",
111
117
  filters={"entity_type": ["dataset"]},
@@ -113,7 +119,7 @@ with client.graph as graph:
113
119
  )
114
120
 
115
121
  # Get detailed entity information
116
- with client.graph as graph:
122
+ with client as client:
117
123
  entities = get_entities(
118
124
  urns=[result["entity"]["urn"] for result in results["searchResults"]]
119
125
  )
@@ -175,6 +181,7 @@ agent = create_agent(model, tools=tools, system_prompt="...")
175
181
  - `add_owners()`, `remove_owners()` - Manage owners
176
182
  - `add_glossary_terms()`, `remove_glossary_terms()` - Manage glossary terms
177
183
  - `add_structured_properties()`, `remove_structured_properties()` - Manage structured properties
184
+ - `save_document()` - Save or update a Document.
178
185
 
179
186
  #### User Tools
180
187
 
@@ -43,7 +43,7 @@ from datahub_agent_context.mcp_tools.entities import get_entities
43
43
  client = DataHubClient.from_env()
44
44
 
45
45
  # Search for datasets
46
- with client.graph as graph:
46
+ with client as client:
47
47
  results = search(
48
48
  query="user_data",
49
49
  filters={"entity_type": ["dataset"]},
@@ -51,7 +51,7 @@ with client.graph as graph:
51
51
  )
52
52
 
53
53
  # Get detailed entity information
54
- with client.graph as graph:
54
+ with client as client:
55
55
  entities = get_entities(
56
56
  urns=[result["entity"]["urn"] for result in results["searchResults"]]
57
57
  )
@@ -113,6 +113,7 @@ agent = create_agent(model, tools=tools, system_prompt="...")
113
113
  - `add_owners()`, `remove_owners()` - Manage owners
114
114
  - `add_glossary_terms()`, `remove_glossary_terms()` - Manage glossary terms
115
115
  - `add_structured_properties()`, `remove_structured_properties()` - Manage structured properties
116
+ - `save_document()` - Save or update a Document.
116
117
 
117
118
  #### User Tools
118
119
 
@@ -69,9 +69,16 @@ langchain_requirements = {
69
69
  "langchain-core>=1.2.7,<2.0.0",
70
70
  }
71
71
 
72
+ snowflake_requirements = {
73
+ "click>=8.0.0,<9.0.0",
74
+ "snowflake-connector-python>=3.0.0,<4.0.0",
75
+ }
76
+
72
77
  dev_requirements = {
73
78
  *lint_requirements,
74
79
  *mypy_stubs,
80
+ *snowflake_requirements,
81
+ *langchain_requirements,
75
82
  "pytest>=8.3.4,<9.0.0",
76
83
  "pytest-cov>=2.8.0,<7.0.0",
77
84
  "tox>=4.0.0,<5.0.0",
@@ -123,5 +130,6 @@ setuptools.setup(
123
130
  extras_require={
124
131
  "dev": list(dev_requirements),
125
132
  "langchain": list(langchain_requirements),
133
+ "snowflake": list(snowflake_requirements),
126
134
  },
127
135
  )
@@ -17,9 +17,17 @@
17
17
  from datahub_agent_context._version import __version__
18
18
  from datahub_agent_context.context import (
19
19
  DataHubContext,
20
+ get_datahub_client,
20
21
  get_graph,
21
- reset_graph,
22
- set_graph,
22
+ reset_client,
23
+ set_client,
23
24
  )
24
25
 
25
- __all__ = ["__version__", "DataHubContext", "get_graph", "set_graph", "reset_graph"]
26
+ __all__ = [
27
+ "__version__",
28
+ "DataHubContext",
29
+ "get_datahub_client",
30
+ "get_graph",
31
+ "set_client",
32
+ "reset_client",
33
+ ]
@@ -13,4 +13,4 @@
13
13
  # limitations under the License.
14
14
 
15
15
  __package_name__ = "datahub-agent-context"
16
- __version__ = "1.3.1.10rc1"
16
+ __version__ = "1.4.0rc2"
@@ -0,0 +1,152 @@
1
+ import logging
2
+
3
+ import click
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+
8
+ @click.group()
9
+ def agent() -> None:
10
+ """Helper commands for Creating and managing Agent on DataHub."""
11
+ pass
12
+
13
+
14
+ @agent.group()
15
+ def create() -> None:
16
+ """Create an agent on DataHub."""
17
+ pass
18
+
19
+
20
+ @create.command(name="snowflake")
21
+ @click.option(
22
+ "--sf-account",
23
+ default=None,
24
+ help="Snowflake account identifier (auto-detected if not provided)",
25
+ )
26
+ @click.option(
27
+ "--sf-user",
28
+ default=None,
29
+ help="Snowflake user name (auto-detected if not provided)",
30
+ )
31
+ @click.option(
32
+ "--sf-role",
33
+ default=None,
34
+ help="Snowflake role (auto-detected if not provided)",
35
+ )
36
+ @click.option(
37
+ "--sf-warehouse",
38
+ default=None,
39
+ help="Snowflake warehouse name (auto-detected if not provided)",
40
+ )
41
+ @click.option(
42
+ "--sf-database",
43
+ default=None,
44
+ help="Snowflake database name (auto-detected if not provided)",
45
+ )
46
+ @click.option(
47
+ "--sf-schema",
48
+ default=None,
49
+ help="Snowflake schema name (auto-detected if not provided)",
50
+ )
51
+ @click.option("--datahub-url", required=True, help="DataHub instance URL")
52
+ @click.option("--datahub-token", required=True, help="DataHub Personal Access Token")
53
+ @click.option(
54
+ "--agent-name", default="DATAHUB_SQL_AGENT", help="Agent name in Snowflake"
55
+ )
56
+ @click.option(
57
+ "--agent-display-name",
58
+ default="DataHub SQL Assistant",
59
+ help="Agent display name in Snowflake UI",
60
+ )
61
+ @click.option(
62
+ "--agent-color",
63
+ default="blue",
64
+ help="Agent color in Snowflake UI",
65
+ type=click.Choice(["blue", "red", "green", "yellow", "purple", "orange"]),
66
+ )
67
+ @click.option(
68
+ "--output-dir",
69
+ default="./snowflake_setup",
70
+ help="Output directory for generated SQL files",
71
+ )
72
+ @click.option(
73
+ "--execute",
74
+ is_flag=True,
75
+ default=False,
76
+ help="Connect to Snowflake and execute the SQL scripts directly",
77
+ )
78
+ @click.option(
79
+ "--sf-password",
80
+ help="Snowflake password (required if --execute is used with password authentication)",
81
+ )
82
+ @click.option(
83
+ "--sf-authenticator",
84
+ default="snowflake",
85
+ type=click.Choice(["snowflake", "externalbrowser", "oauth"], case_sensitive=False),
86
+ help="Authentication method: 'snowflake' (password), 'externalbrowser' (SSO), or 'oauth' (token-based). Default: snowflake",
87
+ )
88
+ @click.option(
89
+ "--enable-mutations/--no-enable-mutations",
90
+ default=True,
91
+ help="Include mutation/write tools (tags, descriptions, owners, etc.). Default: enabled",
92
+ )
93
+ def create_snowflake(
94
+ sf_account: str | None,
95
+ sf_user: str | None,
96
+ sf_role: str | None,
97
+ sf_warehouse: str | None,
98
+ sf_database: str | None,
99
+ sf_schema: str | None,
100
+ datahub_url: str,
101
+ datahub_token: str,
102
+ agent_name: str,
103
+ agent_display_name: str,
104
+ agent_color: str,
105
+ output_dir: str,
106
+ execute: bool,
107
+ sf_password: str | None,
108
+ sf_authenticator: str,
109
+ enable_mutations: bool,
110
+ ) -> None:
111
+ """Create a Snowflake agent on DataHub.
112
+
113
+ Snowflake connection parameters (account, user, role, warehouse, database, schema)
114
+ will be auto-detected from your current session if not provided explicitly.
115
+
116
+ Authentication methods:
117
+ - snowflake (default): Standard password authentication
118
+ - externalbrowser: SSO authentication via browser (no password required)
119
+ - oauth: OAuth token-based authentication
120
+
121
+ Examples:
122
+ # Generate SQL files only (default)
123
+ $ datahub agent create snowflake --datahub-url=... --datahub-token=...
124
+
125
+ # Execute with password authentication
126
+ $ datahub agent create snowflake --execute --sf-password=secret ...
127
+
128
+ # Execute with SSO authentication (browser-based)
129
+ $ datahub agent create snowflake --execute --sf-authenticator=externalbrowser ...
130
+ """
131
+ from datahub_agent_context.snowflake.snowflake import create_snowflake_agent
132
+
133
+ ctx = click.get_current_context()
134
+ ctx.invoke(
135
+ create_snowflake_agent,
136
+ sf_account=sf_account,
137
+ sf_user=sf_user,
138
+ sf_role=sf_role,
139
+ sf_warehouse=sf_warehouse,
140
+ sf_database=sf_database,
141
+ sf_schema=sf_schema,
142
+ datahub_url=datahub_url,
143
+ datahub_token=datahub_token,
144
+ agent_name=agent_name,
145
+ agent_display_name=agent_display_name,
146
+ agent_color=agent_color,
147
+ output_dir=output_dir,
148
+ execute=execute,
149
+ sf_password=sf_password,
150
+ sf_authenticator=sf_authenticator,
151
+ enable_mutations=enable_mutations,
152
+ )
@@ -0,0 +1,110 @@
1
+ """Context management for DataHub tools.
2
+
3
+ This module provides a context manager pattern for managing DataHubClient instances
4
+ across tool calls without explicit parameter passing.
5
+ """
6
+
7
+ import contextvars
8
+ from typing import TYPE_CHECKING, Optional
9
+
10
+ if TYPE_CHECKING:
11
+ from datahub.ingestion.graph.client import DataHubGraph
12
+ from datahub.sdk.main_client import DataHubClient
13
+
14
+ # Context variable to store the current DataHubClient instance
15
+ _client_context: contextvars.ContextVar[Optional["DataHubClient"]] = (
16
+ contextvars.ContextVar("datahub_client", default=None)
17
+ )
18
+
19
+
20
+ def get_datahub_client() -> "DataHubClient":
21
+ """Get the current DataHubClient from context.
22
+
23
+ Returns:
24
+ DataHubClient instance from context
25
+
26
+ Raises:
27
+ RuntimeError: If no client is set in context
28
+ """
29
+ client = _client_context.get()
30
+ if client is None:
31
+ raise RuntimeError(
32
+ "No DataHubClient in context. "
33
+ "Make sure to use DataHubContext context manager or set_client() before calling tools."
34
+ )
35
+ return client
36
+
37
+
38
+ def get_graph() -> "DataHubGraph":
39
+ """Get the current DataHubGraph from context (convenience method).
40
+
41
+ Returns:
42
+ DataHubGraph instance from the client in context
43
+
44
+ Raises:
45
+ RuntimeError: If no client is set in context
46
+ """
47
+ return get_datahub_client()._graph
48
+
49
+
50
+ def set_client(client: "DataHubClient") -> contextvars.Token:
51
+ """Set the DataHubClient in context.
52
+
53
+ Args:
54
+ client: DataHubClient instance to set
55
+
56
+ Returns:
57
+ Token that can be used to reset the context
58
+ """
59
+ return _client_context.set(client)
60
+
61
+
62
+ def reset_client(token: contextvars.Token) -> None:
63
+ """Reset the DataHubClient context to its previous value.
64
+
65
+ Args:
66
+ token: Token returned by set_client()
67
+ """
68
+ _client_context.reset(token)
69
+
70
+
71
+ class DataHubContext:
72
+ """Context manager for DataHub tool execution.
73
+
74
+ This context manager sets the DataHubClient in context for the duration
75
+ of the with block, allowing tools to access it without explicit parameter passing.
76
+
77
+ Example:
78
+ from datahub.sdk.main_client import DataHubClient
79
+ from datahub_agent_context.context import DataHubContext
80
+ from datahub_agent_context.mcp_tools import search
81
+
82
+ client = DataHubClient(...)
83
+
84
+ with DataHubContext(client):
85
+ results = search(query="users") # No client parameter needed!
86
+ """
87
+
88
+ def __init__(self, client: "DataHubClient"):
89
+ """Initialize the context manager.
90
+
91
+ Args:
92
+ client: DataHubClient instance to use in this context
93
+ """
94
+ self.client = client
95
+ self._token: Optional[contextvars.Token] = None
96
+
97
+ def __enter__(self) -> "DataHubClient":
98
+ """Enter the context and set the client.
99
+
100
+ Returns:
101
+ The DataHubClient instance
102
+ """
103
+ self._token = set_client(self.client)
104
+ return self.client
105
+
106
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
107
+ """Exit the context and reset the client."""
108
+ if self._token is not None:
109
+ reset_client(self._token)
110
+ self._token = None
@@ -3,7 +3,7 @@
3
3
  import functools
4
4
  from typing import TYPE_CHECKING, Callable
5
5
 
6
- from datahub_agent_context.context import set_graph
6
+ from datahub_agent_context.context import set_client
7
7
  from datahub_agent_context.mcp_tools import get_me
8
8
  from datahub_agent_context.mcp_tools.documents import grep_documents, search_documents
9
9
  from datahub_agent_context.mcp_tools.domains import remove_domains, set_domains
@@ -32,6 +32,7 @@ from datahub_agent_context.mcp_tools.lineage import (
32
32
  )
33
33
  from datahub_agent_context.mcp_tools.owners import add_owners, remove_owners
34
34
  from datahub_agent_context.mcp_tools.queries import get_dataset_queries
35
+ from datahub_agent_context.mcp_tools.save_document import save_document
35
36
  from datahub_agent_context.mcp_tools.search import search
36
37
  from datahub_agent_context.mcp_tools.tags import add_tags, remove_tags
37
38
  from datahub_agent_context.mcp_tools.terms import (
@@ -57,14 +58,14 @@ def create_context_wrapper(func: Callable, client: "DataHubClient") -> Callable:
57
58
  @functools.wraps(func)
58
59
  def wrapper(*args, **kwargs):
59
60
  # Set graph in context for this function call
60
- token = set_graph(client._graph)
61
+ token = set_client(client)
61
62
  try:
62
63
  return func(*args, **kwargs)
63
64
  finally:
64
65
  # Always reset context, even if function raises
65
- from datahub_agent_context.context import reset_graph
66
+ from datahub_agent_context.context import reset_client
66
67
 
67
- reset_graph(token)
68
+ reset_client(token)
68
69
 
69
70
  return wrapper
70
71
 
@@ -123,5 +124,6 @@ def build_langchain_tools(
123
124
  tools.append(tool(create_context_wrapper(remove_tags, client)))
124
125
  tools.append(tool(create_context_wrapper(add_glossary_terms, client)))
125
126
  tools.append(tool(create_context_wrapper(remove_glossary_terms, client)))
127
+ tools.append(tool(create_context_wrapper(save_document, client)))
126
128
 
127
129
  return tools
@@ -152,9 +152,12 @@ def _is_datahub_cloud(graph: DataHubGraph) -> bool:
152
152
  )
153
153
  return False
154
154
 
155
- is_cloud = hasattr(graph, "frontend_base_url") and graph.frontend_base_url
156
- logger.debug(f"Cloud detection: {is_cloud}")
157
- return bool(is_cloud)
155
+ try:
156
+ is_cloud = hasattr(graph, "frontend_base_url") and graph.frontend_base_url
157
+ logger.debug(f"Cloud detection: {is_cloud}")
158
+ return bool(is_cloud)
159
+ except ValueError:
160
+ return False
158
161
 
159
162
 
160
163
  def _is_field_validation_error(error_msg: str) -> bool: