Skip to content

Commit 7ea7a09

Browse files
theantagonist9509gemini-code-assist[bot]Yuan325
authored
feat(tools/dataplex-get-data-product): add dataplex-get-data-product tool (#3499)
> [!IMPORTANT] > **Stacked Changeset:** This PR depends on changes in previous PRs (#3337). > - **Please merge previous PRs first** before reviewing/merging this one. > - The file diff below will appear large as it includes its ancestors' commits. It will resolve itself once the preceding PRs are merged and this branch is synced. This PR implements the dataplex-get-data-product tool for the Dataplex (Knowledge Catalog) source, allowing users to retrieve detailed metadata for a specific Data Product. Changes overview: - Dataplex Source: Implemented GetDataProduct to fetch a single Data Product by location and ID. It maps SDK structures to a clean flat output, including display name, description, owner emails, and access groups. - New Tool: Created the dataplex-get-data-product tool exposing locationId and dataProductId parameters. - Tests: - Added unit tests for tool configuration parsing. - Added integration tests verifying successful retrieval of Data Products and proper 401 handling on unauthorized requests. - Documentation: Created the reference documentation page for the tool, updated the Dataplex source guide, and added the tool details to the capabilities list. --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
1 parent b67419d commit 7ea7a09

13 files changed

Lines changed: 517 additions & 18 deletions

File tree

‎cmd/internal/config_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1862,7 +1862,7 @@ func TestPrebuiltTools(t *testing.T) {
18621862
},
18631863
"data-products": tools.ToolsetConfig{
18641864
Name: "data-products",
1865-
ToolNames: []string{"search_entries", "lookup_entry", "search_aspect_types", "lookup_context", "list_data_products"},
1865+
ToolNames: []string{"search_entries", "lookup_entry", "search_aspect_types", "lookup_context", "list_data_products", "get_data_product"},
18661866
},
18671867
"enrich": tools.ToolsetConfig{
18681868
Name: "enrich",

‎cmd/internal/imports.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ import (
164164
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataplex/dataplexgeneratedatainsights"
165165
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataplex/dataplexgeneratedataprofile"
166166
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataplex/dataplexgetdatainsights"
167+
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataplex/dataplexgetdataproduct"
167168
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataplex/dataplexgetdataprofile"
168169
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataplex/dataplexgetdataqualityresults"
169170
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataplex/dataplexgetdiscoveryresults"

‎docs/KNOWLEDGE_CATALOG_README.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ The Knowledge Catalog (formerly known as Dataplex) Model Context Protocol (MCP)
77
An editor configured to use the Knowledge Catalog MCP server can use its AI capabilities to help you:
88

99
- **Search Catalog** - Search for entries in Knowledge Catalog
10-
- **Explore Metadata** - Lookup specific entries and search aspect types
10+
- **Explore Metadata** - Lookup specific entries, search aspect types, and list/retrieve Data Products
11+
- **Data Quality** - Search for data quality scans
1112

1213
## Prerequisites
1314

@@ -41,6 +42,8 @@ Once configured, the MCP server will automatically provide Knowledge Catalog cap
4142

4243
* "Search for entries related to 'sales' in Knowledge Catalog."
4344
* "Look up details for the entry 'projects/my-project/locations/us-central1/entryGroups/my-group/entries/my-entry'."
45+
* "List all Data Products."
46+
* "Get details of the Data Product 'projects/my-project/locations/us-central1/dataProducts/my-product'."
4447

4548
## Server Capabilities
4649

@@ -54,6 +57,7 @@ The Knowledge Catalog MCP server provides the following tools:
5457
| `lookup_context` | Retrieve rich metadata regarding one or more data assets along with their relationships. |
5558
| `search_dq_scans` | Search for Data Quality scans. |
5659
| `list_data_products` | List Data Products for the current project. |
60+
| `get_data_product` | Retrieve a specific Data Product. |
5761

5862
## Custom MCP Server Configuration
5963

‎docs/en/integrations/knowledge-catalog/prebuilt-configs/knowledge-catalog.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ aliases:
2020
* `lookup_entry`: Retrieves a specific entry from Knowledge Catalog.
2121
* `search_aspect_types`: Finds aspect types relevant to the query.
2222
* `lookup_context`: Retrieves rich metadata regarding one or more data assets along with their relationships.
23-
* `search_dq_scans`: Search for data quality scans in Dataplex.
23+
* `search_dq_scans`: Searches for data quality scans in Dataplex.
2424
* `list_data_products`: Lists Data Products across all locations.
25+
* `get_data_product`: Retrieves a specific Data Product.
2526
* `generate_data_insights`: Creates a new Dataplex Data Documentation scan template and triggers the run.
2627
* `get_data_insights`: Retrieves the final generated data insights for a completed scan.
2728
* `generate_data_profile`: Creates a new Dataplex Data Profile scan template and triggers the run.
@@ -34,6 +35,5 @@ aliases:
3435
* `get_run_status`: Retrieves the execution status of the latest background job run.
3536
* **Toolsets:**
3637
* `discovery`: Metadata discovery and search toolset (`search_entries`, `lookup_entry`, `search_aspect_types`, `lookup_context`, `search_dq_scans`).
37-
* `data-products`: Data Products and Data Assets curation and management toolset (`search_entries`, `lookup_entry`, `search_aspect_types`, `lookup_context`, `list_data_products`).
38+
* `data-products`: Data Products and Data Assets curation and management toolset (`search_entries`, `lookup_entry`, `search_aspect_types`, `lookup_context`, `list_data_products`, `get_data_product`).
3839
* `enrich`: Metadata enrichment pipeline orchestration and execution toolset (`search_entries`, `lookup_entry`, `lookup_context`, `generate_data_insights`, `get_data_insights`, `generate_data_profile`, `get_data_profile`, `discover_metadata`, `get_discovery_results`, `check_data_quality`, `get_data_quality_results`, `get_operation`, `get_run_status`).
39-

‎docs/en/integrations/knowledge-catalog/source.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,4 +381,11 @@ This abbreviated syntax works for the qualified predicates except for `label` in
381381
2. You can optionally filter by `display_name` (e.g., "`display_name:\"my-product\"`") or other fields using the Dataplex filter syntax.
382382
### Response
383383
1. Unless asked for a specific data product, respond with all entries returned.
384+
385+
## Tool: get_data_product
386+
### Request
387+
1. Use this tool to retrieve detailed metadata for a specific Data Product.
388+
2. You must provide `locationId` and `dataProductId`.
389+
### Response
390+
1. Present the retrieved metadata for the Data Product, including its display name, description, owner emails, asset count, labels, and access groups.
384391
```
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
title: "dataplex-get-data-product"
3+
type: docs
4+
weight: 1
5+
description: >
6+
A "dataplex-get-data-product" tool allows to retrieve a specific Data Product.
7+
---
8+
9+
## About
10+
11+
A `dataplex-get-data-product` tool retrieves detailed metadata for a specific Data Product in Knowledge Catalog (formerly known as Dataplex).
12+
13+
View the [Data Products guide][guide] for more information.
14+
15+
[guide]: https://docs.cloud.google.com/dataplex/docs/data-products-overview
16+
17+
## Compatible Sources
18+
19+
{{< compatible-sources >}}
20+
21+
## Requirements
22+
23+
### IAM Permissions
24+
25+
Knowledge Catalog uses [Identity and Access Management (IAM)][iam-overview] to control
26+
user and group access to Knowledge Catalog resources. Toolbox will use your
27+
[Application Default Credentials (ADC)][adc] to authorize and authenticate when
28+
interacting with [Knowledge Catalog][dataplex-docs].
29+
30+
In addition to [setting the ADC for your server][set-adc], you need to ensure
31+
the IAM identity has been given the correct IAM permissions for the tasks you
32+
intend to perform. See [Knowledge Catalog IAM permissions][iam-permissions]
33+
and [Knowledge Catalog IAM roles][iam-roles] for more information on
34+
applying IAM permissions and roles to an identity.
35+
36+
[iam-overview]: https://cloud.google.com/dataplex/docs/iam-and-access-control
37+
[adc]: https://cloud.google.com/docs/authentication#adc
38+
[set-adc]: https://cloud.google.com/docs/authentication/provide-credentials-adc
39+
[iam-permissions]: https://cloud.google.com/dataplex/docs/iam-permissions
40+
[iam-roles]: https://cloud.google.com/dataplex/docs/iam-roles
41+
[dataplex-docs]: https://cloud.google.com/dataplex
42+
43+
## Parameters
44+
45+
The `dataplex-get-data-product` tool has the following parameters:
46+
47+
| **field** | **type** | **required** | **description** |
48+
| ------------- | -------- | ------------ | --------------------------------------------------------------- |
49+
| locationId | string | true | The location ID (e.g. `us`, `us-central1`) of the Data Product. |
50+
| dataProductId | string | true | The unique ID of the Data Product. |
51+
52+
## Example
53+
54+
```yaml
55+
kind: tool
56+
name: get_data_product
57+
type: dataplex-get-data-product
58+
source: my-dataplex-source
59+
description: Use this tool to retrieve a Data Product.
60+
```
61+
62+
## Reference
63+
64+
| **field** | **type** | **required** | **description** |
65+
| ----------- | -------- | ------------ | -------------------------------------------------- |
66+
| type | string | true | Must be "dataplex-get-data-product". |
67+
| source | string | true | Name of the source the tool should execute on. |
68+
| description | string | true | Description of the tool that is passed to the LLM. |

‎docs/en/integrations/knowledge-catalog/tools/knowledge-catalog-list-data-products.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ description: >
1010

1111
A `dataplex-list-data-products` tool lists all Data Products in Knowledge Catalog (formerly known as Dataplex) across all locations (globally).
1212

13-
View the [Data Products usage guide][usage-guide] for more information.
13+
View the [Data Products guide][guide] for more information.
1414

15-
[usage-guide]: https://docs.cloud.google.com/dataplex/docs/use-data-products
15+
[guide]: https://docs.cloud.google.com/dataplex/docs/data-products-overview
1616

1717
## Compatible Sources
1818

@@ -46,7 +46,7 @@ The `dataplex-list-data-products` tool has the following optional parameters:
4646

4747
| **field** | **type** | **required** | **description** |
4848
| --------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
49-
| filter | string | false | Filter string to list data products. Based on the AIP-160 proposal. Use '=' for exact, and ':' for contains matching. String literals must be enclosed within "". Matching accross all fields at once is not yet supported. E.g. "display_name:\"my-product\"" |
49+
| filter | string | false | Filter string to list data products. Based on the AIP-160 proposal. Use '=' for exact, and ':' for contains matching. String literals must be enclosed within "". Matching across all fields at once is not yet supported. E.g. "display_name:\"my-product\"" |
5050
| pageSize | integer | false | Number of returned data products in the page. Defaults to `10`. |
5151
| orderBy | string | false | Specifies the ordering of results. |
5252

‎internal/prebuiltconfigs/tools/dataplex.yaml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ source: dataplex-source
5454
description: Lists Data Products across all locations.
5555
---
5656
kind: tool
57+
name: get_data_product
58+
type: dataplex-get-data-product
59+
source: dataplex-source
60+
description: Retrieves specific metadata regarding a Data Product.
61+
---
62+
kind: tool
5763
name: generate_data_insights
5864
type: dataplex-generate-data-insights
5965
source: dataplex-source
@@ -245,6 +251,7 @@ tools:
245251
- search_aspect_types
246252
- lookup_context
247253
- list_data_products
254+
- get_data_product
248255
---
249256
kind: toolset
250257
name: enrich

‎internal/sources/dataplex/dataplex.go‎

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ func (s *Source) ListDataProducts(
372372
pageSize int,
373373
orderBy string,
374374
) ([]*DataProductSummary, error) {
375-
if s.dataProductClient == nil {
375+
if s.GetDataProductClient() == nil {
376376
return nil, fmt.Errorf("dataplex data product client is not initialized")
377377
}
378378
if pageSize <= 0 {
@@ -386,7 +386,7 @@ func (s *Source) ListDataProducts(
386386
OrderBy: orderBy,
387387
}
388388

389-
it := s.dataProductClient.ListDataProducts(ctx, req)
389+
it := s.GetDataProductClient().ListDataProducts(ctx, req)
390390
var results []*DataProductSummary
391391

392392
for len(results) < pageSize {
@@ -401,14 +401,14 @@ func (s *Source) ListDataProducts(
401401
return nil, fmt.Errorf("failed to list data products: %w", err)
402402
}
403403
parts := strings.Split(dp.GetName(), "/")
404-
var locationId, dataProductId string
404+
var locID, prodID string
405405
if len(parts) >= 6 && parts[0] == "projects" && parts[2] == "locations" && parts[4] == "dataProducts" {
406-
locationId = parts[3]
407-
dataProductId = parts[5]
406+
locID = parts[3]
407+
prodID = parts[5]
408408
}
409409
results = append(results, &DataProductSummary{
410-
LocationID: locationId,
411-
DataProductID: dataProductId,
410+
LocationID: locID,
411+
DataProductID: prodID,
412412
DisplayName: dp.GetDisplayName(),
413413
OwnerEmails: dp.GetOwnerEmails(),
414414
AssetCount: dp.GetAssetCount(),
@@ -417,6 +417,68 @@ func (s *Source) ListDataProducts(
417417
return results, nil
418418
}
419419

420+
type AccessGroup struct {
421+
ID string `json:"id"`
422+
DisplayName string `json:"displayName"`
423+
Description string `json:"description"`
424+
GoogleGroup string `json:"googleGroup,omitempty"`
425+
ServiceAccount string `json:"serviceAccount,omitempty"`
426+
}
427+
428+
type DataProduct struct {
429+
LocationID string `json:"locationId"`
430+
DataProductID string `json:"dataProductId"`
431+
DisplayName string `json:"displayName"`
432+
Description string `json:"description"`
433+
OwnerEmails []string `json:"ownerEmails"`
434+
AssetCount int32 `json:"assetCount"`
435+
Labels map[string]string `json:"labels"`
436+
AccessGroups []AccessGroup `json:"accessGroups"`
437+
}
438+
439+
func (s *Source) GetDataProduct(ctx context.Context, locationID string, dataProductID string) (*DataProduct, error) {
440+
if s.GetDataProductClient() == nil {
441+
return nil, fmt.Errorf("dataplex data product client is not initialized")
442+
}
443+
name := fmt.Sprintf("projects/%s/locations/%s/dataProducts/%s", s.ProjectID(), locationID, dataProductID)
444+
req := &dataplexpb.GetDataProductRequest{
445+
Name: name,
446+
}
447+
resp, err := s.GetDataProductClient().GetDataProduct(ctx, req)
448+
if err != nil {
449+
return nil, err
450+
}
451+
452+
accessGroups := []AccessGroup{}
453+
for _, ag := range resp.GetAccessGroups() {
454+
accessGroups = append(accessGroups, AccessGroup{
455+
ID: ag.GetId(),
456+
DisplayName: ag.GetDisplayName(),
457+
Description: ag.GetDescription(),
458+
GoogleGroup: ag.GetPrincipal().GetGoogleGroup(),
459+
ServiceAccount: ag.GetPrincipal().GetServiceAccount(),
460+
})
461+
}
462+
463+
parts := strings.Split(resp.GetName(), "/")
464+
var locID, prodID string
465+
if len(parts) >= 6 && parts[0] == "projects" && parts[2] == "locations" && parts[4] == "dataProducts" {
466+
locID = parts[3]
467+
prodID = parts[5]
468+
}
469+
470+
return &DataProduct{
471+
LocationID: locID,
472+
DataProductID: prodID,
473+
DisplayName: resp.GetDisplayName(),
474+
Description: resp.GetDescription(),
475+
OwnerEmails: resp.GetOwnerEmails(),
476+
AssetCount: resp.GetAssetCount(),
477+
Labels: resp.GetLabels(),
478+
AccessGroups: accessGroups,
479+
}, nil
480+
}
481+
420482
func (s *Source) GenerateDataInsights(ctx context.Context, location, resourcePath string, publish bool) (string, error) {
421483
parent := fmt.Sprintf("projects/%s/locations/%s", s.ProjectID(), location)
422484
dataScanID := fmt.Sprintf("nq-doc-%s", uuid.New().String())

0 commit comments

Comments
 (0)