Skip to content

Add Create Domain MCP Tool #26609#27281

Open
aniruddhaadak80 wants to merge 7 commits intoopen-metadata:mainfrom
aniruddhaadak80:feat-mcp-create-domain
Open

Add Create Domain MCP Tool #26609#27281
aniruddhaadak80 wants to merge 7 commits intoopen-metadata:mainfrom
aniruddhaadak80:feat-mcp-create-domain

Conversation

@aniruddhaadak80
Copy link
Copy Markdown

Fixes #26609. Added the CreateDomainTool to OpenMetadata's MCP server. This allows AI agents to create explicit domain boundaries and data product assignments via Model Context Protocol, checking off the 'Domain & Data Product management' epic.

Copilot AI review requested due to automatic review settings April 11, 2026 14:10
@aniruddhaadak80 aniruddhaadak80 requested a review from a team as a code owner April 11, 2026 14:10
@github-actions
Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Comment thread openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/CreateDomainTool.java Outdated
Comment thread openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/CreateDomainTool.java Outdated
Comment thread openmetadata-mcp/src/main/resources/json/data/mcp/tools.json
Comment thread openmetadata-mcp/src/main/resources/json/data/mcp/tools.json Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR primarily adds a new MCP tool (create_domain) to the OpenMetadata MCP server so agents can create Domain entities via Model Context Protocol. It also includes several unrelated fixes across UI lineage popover styling and backend tag/query handling.

Changes:

  • Add create_domain to MCP tool definitions, dispatch, and implement CreateDomainTool.
  • Update user listing DAO queries to pass ListFilter query params into SQL bindings.
  • Minor UI lineage popover button styling/formatting and table data model tag merge adjustments.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/CanvasButtonPopover.component.tsx Minor TSX formatting + button style tweaks for the lineage popover wrapper.
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TableRepository.java Adjust tag merging behavior when adding a DataModel (table + column tags).
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java Pass filter.getQueryParams() into listCount/listBefore/listAfter query methods via @BindMap.
openmetadata-mcp/src/main/resources/json/data/mcp/tools.json Add the create_domain tool schema (and reformat some existing JSON sections).
openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/DefaultToolContext.java Route create_domain tool calls to CreateDomainTool.
openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/CreateDomainTool.java New MCP tool implementation for Domain creation (currently contains blocking compile/runtime issues).

Comment on lines +1 to +3
@"
package org.openmetadata.mcp.tools;

Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new Java file includes stray @" at the top and "@ at the end (looks like a copied PowerShell here-string delimiter), which will make the class fail to compile. Remove these markers so the file starts with the package ... declaration and ends after the closing brace.

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +66
CreateDomain createDomain = new CreateDomain();
createDomain.setName(name);
createDomain.setDescription(description);

try {
createDomain.setDomainType(org.openmetadata.schema.type.DomainType.fromValue(domainType));
} catch (Exception e) {
throw new IllegalArgumentException(
"Parameter 'domainType' has invalid value '" + domainType + "'. Valid values are: Aggregate, Source, Aligned, Consumer, other");
}
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createDomain.setDomainType(org.openmetadata.schema.type.DomainType.fromValue(domainType)) appears to be using the wrong enum type for CreateDomain. Elsewhere the API uses org.openmetadata.schema.api.domains.CreateDomain.DomainType (e.g., integration tests), so this is likely a compilation error or runtime mismatch. Also, the error message lists values that don’t match the actual enum names (e.g., CONSUMER_ALIGNED). Please use the CreateDomain DomainType enum and derive the "valid values" list from the enum to avoid drift.

Copilot uses AI. Check for mistakes.
},
"domainType": {
"type": "string",
"description": "Type"
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The create_domain tool schema leaves domainType as a free-form string. Since the server code validates against an enum, the MCP schema should publish the allowed values (an enum) so agents can reliably provide valid inputs and avoid trial-and-error failures.

Suggested change
"description": "Type"
"description": "Type",
"enum": [
"Source-aligned",
"Consumer-aligned",
"Aggregate",
"Functional"
]

Copilot uses AI. Check for mistakes.
Comment on lines 6359 to 6363
return EntityDAO.super.listCount(filter);
}
return listCount(
getTableName(), mySqlCondition, postgresCondition, team, Relationship.HAS.ordinal());
getTableName(), mySqlCondition, postgresCondition, team, Relationship.HAS.ordinal(), filter.getQueryParams());
}
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing filter.getQueryParams() into @BindMap params will also bind entries like team (and potentially relation/cursor keys if ever present). Since this query already binds :team via @BindFQN("team"), the map binding can override the hashed/quoted value and break team filtering (or create hard-to-debug precedence issues). Consider passing a copy of the params map with overlapping keys removed (at least team) or using a prefixed bind map to avoid collisions.

Copilot uses AI. Check for mistakes.
Comment on lines 83 to +88
case "create_metric":
result = new CreateMetricTool().execute(authorizer, limits, securityContext, params);
break;
case "create_domain":
result = new CreateDomainTool().execute(authorizer, limits, securityContext, params);
break;
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new MCP tool is being added to the dispatch switch, but there is no corresponding unit test like the existing CreateMetricToolTest / GlossaryToolTest coverage. Please add a CreateDomainToolTest to validate mapping + repository calls (and prevent regressions in parameter validation).

Copilot uses AI. Check for mistakes.
Comment on lines 83 to +88
case "create_metric":
result = new CreateMetricTool().execute(authorizer, limits, securityContext, params);
break;
case "create_domain":
result = new CreateDomainTool().execute(authorizer, limits, securityContext, params);
break;
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description focuses on adding the create_domain MCP tool, but this diff also includes unrelated changes in the UI lineage popover and in table tag merging / user listing SQL bindings. If these aren’t required for the MCP tool, consider splitting them into separate PRs to keep the change isolated and easier to review/revert.

Copilot uses AI. Check for mistakes.
@aniruddhaadak80 aniruddhaadak80 force-pushed the feat-mcp-create-domain branch from 3aab4d9 to e407b26 Compare April 11, 2026 14:28
@github-actions
Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Copilot AI review requested due to automatic review settings April 11, 2026 14:41
@aniruddhaadak80 aniruddhaadak80 force-pushed the feat-mcp-create-domain branch from e407b26 to 856f0e9 Compare April 11, 2026 14:41
@github-actions
Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment on lines +676 to +693
"experts": {
"type": "array",
"items": {
"type": "string"
}
},
"owners": {
"type": "array",
"items": {
"type": "string"
}
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
}
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new create_domain tool schema is missing description fields for experts, owners, and tags. Other MCP tool definitions (e.g. create_metric) document these array parameters, and the lack of descriptions makes the tool harder for agents to use correctly. Add clear descriptions for each of these properties (what values are expected, e.g. usernames/team names vs tag FQNs).

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +37
@Slf4j
public class CreateDomainTool implements McpTool {

@Override
public Map<String, Object> execute(
Authorizer authorizer, CatalogSecurityContext securityContext, Map<String, Object> params) {
throw new UnsupportedOperationException("CreateDomainTool requires limit validation.");
}

@Override
public Map<String, Object> execute(
Authorizer authorizer,
Limits limits,
CatalogSecurityContext securityContext,
Map<String, Object> params) {
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no unit test for the newly added CreateDomainTool. Other MCP create tools (e.g. CreateMetricToolTest) verify that the tool calls Entity.getEntityRepository(...).prepareInternal(...) and createOrUpdate(...) with the mapped entity. Add a CreateDomainToolTest with similar mocking to cover the happy path and at least one validation failure (e.g. missing/invalid domainType).

Copilot uses AI. Check for mistakes.
@aniruddhaadak80
Copy link
Copy Markdown
Author

aniruddhaadak80 commented Apr 13, 2026

Would it be possible to add the safe to test label here? I'd love to see the pipeline results for these changes.

@github-actions
Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@mohityadav766 mohityadav766 added the safe to test Add this label to run secure Github workflows on PRs label Apr 15, 2026
Limits limits,
CatalogSecurityContext securityContext,
Map<String, Object> params) {
Object nameRaw = params.get("name");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can rely on CreateDomain schema for this , instead of check individual fields @aniruddhaadak80

"properties": {
"name": {
"type": "string",
"description": "Unique name"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add proper descriptions here

"type": "string",
"description": "Domain description"
},
"domainType": {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for MCP it should be a clear description that LLM can use to undertand and send the created request

Copy link
Copy Markdown
Member

@mohityadav766 mohityadav766 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs work

@github-actions
Copy link
Copy Markdown
Contributor

The Java checkstyle failed.

Please run mvn spotless:apply in the root of your repository and commit the changes to this PR.
You can also use pre-commit to automate the Java code formatting.

You can install the pre-commit hooks with make install_test precommit_install.

Copilot AI review requested due to automatic review settings April 16, 2026 04:55
@github-actions
Copy link
Copy Markdown
Contributor

The Java checkstyle failed.

Please run mvn spotless:apply in the root of your repository and commit the changes to this PR.
You can also use pre-commit to automate the Java code formatting.

You can install the pre-commit hooks with make install_test precommit_install.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

Comment on lines +688 to +693
"tags": {
"type": "array",
"items": {
"type": "string"
}
}
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tags parameter is missing a description. Add one clarifying that values should be tag FQNs (e.g., Tier.Tier1) which will be applied as MANUAL classification tags.

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +64
try {
createDomain.setDomainType(org.openmetadata.schema.type.DomainType.fromValue(domainType));
} catch (Exception e) {
throw new IllegalArgumentException(
"Parameter 'domainType' has invalid value '" + domainType + "'. Valid values are: Aggregate, Source-aligned, Consumer-aligned");
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

domainType parsing catches Exception and hard-codes the list of valid values. This can hide unexpected failures and will drift if the DomainType enum changes. Catch IllegalArgumentException from DomainType.fromValue(...) and build the valid-values list from DomainType.values() (or reuse a shared helper, as done in CreateMetricTool).

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +82
if (!(parentRaw instanceof String)) {
throw new IllegalArgumentException(
"Parameter 'parent' must be a string. Received: " + parentRaw);
}
createDomain.setParent((String) parentRaw);
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When parent is provided as an empty/blank string, it passes the instanceof String check and is set on CreateDomain, which later becomes an EntityReference with a blank FQN and is likely to fail resolution. Treat blank strings the same as missing: either reject with a clear IllegalArgumentException or ignore the field when blank.

Suggested change
if (!(parentRaw instanceof String)) {
throw new IllegalArgumentException(
"Parameter 'parent' must be a string. Received: " + parentRaw);
}
createDomain.setParent((String) parentRaw);
if (!(parentRaw instanceof String parent) || parent.isBlank()) {
throw new IllegalArgumentException(
"Parameter 'parent' must be a non-blank string when provided. Received: "
+ parentRaw);
}
createDomain.setParent(parent);

Copilot uses AI. Check for mistakes.
Comment on lines +64 to +66

when(repo.createOrUpdate(isNull(), any(Domain.class), anyString(), isNull()))
.thenReturn(putResponse);
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test stubs repo.createOrUpdate(..., impersonatedBy) with isNull() for the 4th argument, but the tool reads it from ImpersonationContext (a thread-local). If another test sets impersonation and doesn’t clear it, this test can become flaky. Clear ImpersonationContext in @BeforeEach (and/or relax the stub to accept any value for impersonatedBy).

Copilot uses AI. Check for mistakes.
Comment on lines +676 to +681
"experts": {
"type": "array",
"items": {
"type": "string"
}
},
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The experts parameter is missing a description. Other tools in this file provide per-parameter descriptions, and agents rely on them to supply correct inputs. Add a short description (e.g., list of OpenMetadata usernames/login names).

Copilot uses AI. Check for mistakes.
"domainType": {
"type": "string",
"description": "Type",
"enum": ["Aggregate", "Source-aligned", "Consumer-aligned"]
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In create_domain, the domainType enum is the only enum in this file still formatted inline. Consider formatting it as a multi-line array like the other enum definitions to keep the JSON consistently formatted (and reduce future diff noise).

Suggested change
"enum": ["Aggregate", "Source-aligned", "Consumer-aligned"]
"enum": [
"Aggregate",
"Source-aligned",
"Consumer-aligned"
]

Copilot uses AI. Check for mistakes.
Comment on lines +682 to +687
"owners": {
"type": "array",
"items": {
"type": "string"
}
},
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The owners parameter is missing a description. Add one clarifying the expected inputs (e.g., OpenMetadata usernames or team names, which the backend will resolve to entity references).

Copilot uses AI. Check for mistakes.
Comment thread .github/workflows/temp-format.yml Outdated
Copilot AI review requested due to automatic review settings April 16, 2026 05:05
@aniruddhaadak80 aniruddhaadak80 review requested due to automatic review settings April 16, 2026 05:05
Copilot AI review requested due to automatic review settings April 16, 2026 05:06
@gitar-bot
Copy link
Copy Markdown

gitar-bot bot commented Apr 16, 2026

Code Review ✅ Approved 7 resolved / 7 findings

Implements the Create Domain MCP tool, resolving compilation issues from PowerShell syntax, correcting enum values, and adding comprehensive validation tests. All identified structural and configuration findings have been addressed.

✅ 7 resolved
Bug: File contains PowerShell here-string delimiters that break compilation

📄 openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/CreateDomainTool.java:1 📄 openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/CreateDomainTool.java:128
CreateDomainTool.java has @" on line 1 and "@ on line 128. These are PowerShell here-string delimiters and are not valid Java syntax. The file will fail to compile, making this feature completely non-functional.

Bug: Error message lists incorrect DomainType enum values

📄 openmetadata-mcp/src/main/java/org/openmetadata/mcp/tools/CreateDomainTool.java:65
The error message on line 65 states valid values are "Aggregate, Source, Aligned, Consumer, other", but the actual DomainType enum values (defined in domain.json) are "Source-aligned", "Consumer-aligned", and "Aggregate". This will mislead users/agents into providing invalid values that will always fail.

Quality: tools.json missing domainType enum constraint

📄 openmetadata-mcp/src/main/resources/json/data/mcp/tools.json:663-666
The domainType parameter in tools.json (line 663-665) is defined as a free-form string with only the description "Type". Unlike other enum fields in the same file (e.g., metricType, granularity), it lacks an enum constraint listing the valid values. MCP clients/LLMs won't know the valid options without this. The description is also too terse to be useful.

Quality: Missing newline at end of tools.json

📄 openmetadata-mcp/src/main/resources/json/data/mcp/tools.json:702
The file now lacks a trailing newline (line 702 shows \ No newline at end of file). While cosmetic, this can cause noisy diffs in future changes and some tools warn about it.

Quality: Test only asserts non-null; doesn't verify result content

📄 openmetadata-mcp/src/test/java/org/openmetadata/mcp/tools/CreateDomainToolTest.java:84
The test calls assertNotNull(result) but never inspects the returned map's content. Since CreateDomainTool.execute() builds and returns a result map (likely containing the domain's id, name, etc.), the test would pass even if the result map were empty or contained wrong data. Other tests in this codebase (e.g., SearchMetadataToolTest) use assertEquals to verify specific keys/values in the returned map.

Additionally, the test doesn't verify that authorizer or limits were invoked, so authorization and limit enforcement could silently regress.

...and 2 more resolved from earlier reviews

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment on lines +82 to +86
if (!(parentRaw instanceof String)) {
throw new IllegalArgumentException(
"Parameter 'parent' must be a string. Received: " + parentRaw);
}
createDomain.setParent((String) parentRaw);
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parent accepts an empty string (""), which will be treated as a non-null FQN by DomainMapper and routed into Entity.getEntityReferenceByName(...) with an empty name. That path is expected to throw (entity lookup by blank FQN), turning a user input issue into a 500. Treat blank parent as missing (ignore it) or validate that when provided it must be a non-blank string.

Suggested change
if (!(parentRaw instanceof String)) {
throw new IllegalArgumentException(
"Parameter 'parent' must be a string. Received: " + parentRaw);
}
createDomain.setParent((String) parentRaw);
if (!(parentRaw instanceof String parent)) {
throw new IllegalArgumentException(
"Parameter 'parent' must be a string. Received: " + parentRaw);
}
if (!parent.isBlank()) {
createDomain.setParent(parent);
}

Copilot uses AI. Check for mistakes.
Comment on lines +44 to +53
@BeforeEach
void setUp() {
authorizer = mock(Authorizer.class);
limits = mock(Limits.class);
securityContext = mock(CatalogSecurityContext.class);

Principal mockPrincipal = mock(Principal.class);
when(mockPrincipal.getName()).thenReturn("test-user");
when(securityContext.getUserPrincipal()).thenReturn(mockPrincipal);
}
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test stubs repo.createOrUpdate(..., impersonatedBy) assuming ImpersonationContext.getImpersonatedBy() is null, but it never clears the ThreadLocal. If another test leaves ImpersonationContext set, this test can become order-dependent/flaky. Add an @AfterEach that calls ImpersonationContext.clear() (and/or set it explicitly within the test).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New MCP Tools

3 participants