Skip to content

Update conflict detection to respect gateway_id in Prompt/Resource services#2585

Merged
crivetimihai merged 2 commits intomainfrom
2520_namespacing_constraints
Jan 31, 2026
Merged

Update conflict detection to respect gateway_id in Prompt/Resource services#2585
crivetimihai merged 2 commits intomainfrom
2520_namespacing_constraints

Conversation

@kevalmahajan
Copy link
Copy Markdown
Member

@kevalmahajan kevalmahajan commented Jan 30, 2026

🐛 Bug-fix PR

📌 Summary

Closes #2520

Implemented proper Gateway Namespacing for Resources and Prompts. Historically, the application logic enforced global uniqueness for Resource URIs and Prompt Names per team/owner, ignoring which Gateway they belonged to. This change updates the ResourceService and PromptService (and their Create schemas) to respect gateway_id during registration and conflict detection. Ideally, the same URI or Name should be allowed if it is associated with a different Gateway.

  • Allowed same URI or prompt name from different gateways.
  • Added test cases to test Namespacing for Resources and Prompts

🔁 Reproduction Steps

  1. Register a resource with URI my-uri on Gateway A (Success).
  2. Attempt to register a resource with URI my-uri on Gateway B (same team/owner).
  • Expected: Registration succeeds (namespaces are separate).
  • Actual (Before Fix): Registration fails with ConflictError, claiming the resource already exists.

🐞 Root Cause

The register_resource and register_prompt methods in the service layer checked for conflicts by querying the database for any existing record with the same uri/name and team_id/visibility. The query filters completely omitted the gateway_id field. As a result, the application was mistakenly treating different gateways as sharing a single global namespace. Additionally, the Create schemas were missing the gateway_id field entirely, preventing the API from accepting this context.

💡 Fix Description

Schema Updates:

  • Updated ResourceCreate and PromptCreate Pydantic models in schemas.py to accept an optional gateway_id.

Service Logic:

  • Updated ResourceService.register_resource to:
    • Extract gateway_id
    • Include it in the conflict detection query
      (WHERE uri = ... AND gateway_id = ...)
  • Updated PromptService.register_prompt to:
    • Extract gateway_id
    • Pass it to the name computation logic
    • Include it in the conflict detection query
  • Ensured both services persist gateway_id to the database
    when creating new records.

Tests:

  • Added TestResourceGatewayNamespacing unit tests to verify that:
    • Identical identifiers are allowed across different gateways
    • Identical identifiers are rejected within the same gateway
  • Added TestPromptGatewayNamespacing unit tests with the same guarantees

🧪 Verification

Check Command Status
Lint suite make lint
Unit tests make test
Coverage ≥ 90 % make coverage
Manual regression no longer fails steps / screenshots

📐 MCP Compliance (if relevant)

  • Matches current MCP spec
  • No breaking change to MCP clients

✅ Checklist

  • Code formatted (make black isort pre-commit)
  • No secrets/credentials committed

@kevalmahajan kevalmahajan force-pushed the 2520_namespacing_constraints branch from 54f2ad7 to 18ac3c8 Compare January 30, 2026 11:54
@kevalmahajan kevalmahajan changed the title namespace prompt names and resource URIs by gateway ID, ensuring uniq… Update conflict detection to respect gateway_id in Prompt/Resource services Jan 30, 2026
@crivetimihai crivetimihai self-assigned this Jan 31, 2026
…ueness

Signed-off-by: Keval Mahajan mahajankeval23@gmail.com
@crivetimihai crivetimihai force-pushed the 2520_namespacing_constraints branch from 18ac3c8 to c59782c Compare January 31, 2026 18:50
@crivetimihai
Copy link
Copy Markdown
Member

Additional Fixes Applied

After reviewing and testing the original PR changes, I identified and fixed several issues:

Bug Fixes

1. ORM Event Handler Gateway Lookup (HIGH - Root Cause)

  • The before_insert/before_update event handlers for Tool and Prompt in db.py accessed target.gateway to compute the gateway-prefixed name
  • When only gateway_id was set (without the relationship loaded), target.gateway returned None, causing names to be stored without the gateway prefix
  • Fix: Updated event handlers to:
    • Check for loaded gateway relationship first
    • Fall back to _gateway_name_cache attribute if set by service layer
    • Query the database as last resort (with exception handling)

2. sa.texttext() (HIGH)

  • The fallback database query used sa.text() but sa was not imported
  • Fix: Changed to text() which is already imported from sqlalchemy

3. Unused Variable (MEDIUM)

  • prompt_gateway_ids list was populated but never used in bulk registration
  • Fix: Removed the unused variable

Service Layer Enhancements

Single Registration (register_prompt, register_resource):

  • Now sets db_prompt.gateway and db_prompt._gateway_name_cache before db.add() to help the event handler

Bulk Registration (register_prompts_bulk, register_resources_bulk):

  • Collects and looks up all gateway_ids upfront
  • Uses (name, gateway_id) / (uri, gateway_id) tuples as keys for conflict detection maps
  • Passes gateway context to _compute_prompt_name
  • Sets gateway relationship and cache on new prompt/resource objects

Test Results

  • ✅ All 5309 unit tests pass
  • ✅ Integration tests confirm:
    • Same name/URI can exist on different gateways (properly namespaced)
    • Duplicate on same gateway correctly fails with conflict error
    • Database stores gateway-prefixed names correctly

@crivetimihai crivetimihai force-pushed the 2520_namespacing_constraints branch from c59782c to a404996 Compare January 31, 2026 19:07
@crivetimihai
Copy link
Copy Markdown
Member

Final linting and security fixes:

  • Added # nosec B110 to intentional exception handlers in db.py ORM event handlers (for gateway name lookup fallback)
  • Fixed test file lint issues:
    • Changed unused res variable to _ (F841)
    • Removed trailing whitespace (W293)
    • Fixed comment spacing to use two spaces before # (E262)

All checks pass:

  • make bandit - 0 issues
  • All 46 prompt service tests pass

@crivetimihai crivetimihai force-pushed the 2520_namespacing_constraints branch from a404996 to 296a984 Compare January 31, 2026 19:17
@crivetimihai
Copy link
Copy Markdown
Member

Improved test coverage for gateway-scoped conflict queries:

The namespacing tests now properly validate that gateway_id is included in the conflict query:

  • test_prompt_namespacing_different_gateways: Captures executed queries and asserts gateway_id is in the conflict check SQL
  • test_prompt_namespacing_same_gateway: Uses side_effect to verify query includes gateway_id before returning conflict
  • test_prompt_namespacing_local_prompts: Verifies gateway_id filtering when gateway_id=None
  • test_resource_namespacing_different_gateways: Captures and validates conflict query contains gateway_id
  • test_resource_namespacing_same_gateway: Asserts gateway_id in query via side_effect
  • test_resource_namespacing_local_resources: Validates gateway_id filtering for local resources

All 136 service tests pass.

Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
@crivetimihai crivetimihai force-pushed the 2520_namespacing_constraints branch from 296a984 to 3d8fee5 Compare January 31, 2026 19:40
@crivetimihai crivetimihai merged commit 50fc936 into main Jan 31, 2026
51 checks passed
@crivetimihai crivetimihai deleted the 2520_namespacing_constraints branch January 31, 2026 19:54
hughhennelly pushed a commit to hughhennelly/mcp-context-forge that referenced this pull request Feb 8, 2026
…rvices (IBM#2585)

* namespace prompt names and resource URIs by gateway ID, ensuring uniqueness

Signed-off-by: Keval Mahajan mahajankeval23@gmail.com

* linting

Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Keval Mahajan mahajankeval23@gmail.com
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: hughhennnelly <hughhennelly06@gmail.com>
kcostell06 pushed a commit to kcostell06/mcp-context-forge that referenced this pull request Feb 24, 2026
…rvices (IBM#2585)

* namespace prompt names and resource URIs by gateway ID, ensuring uniqueness

Signed-off-by: Keval Mahajan mahajankeval23@gmail.com

* linting

Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Keval Mahajan mahajankeval23@gmail.com
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TESTING][REGRESSION]: Add regression tests for gateway namespacing constraints

2 participants