unitysvc-core 0.2.24__tar.gz → 0.2.25__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 (43) hide show
  1. {unitysvc_core-0.2.24/src/unitysvc_core.egg-info → unitysvc_core-0.2.25}/PKG-INFO +1 -1
  2. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/pyproject.toml +1 -1
  3. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/validators.py +29 -12
  4. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25/src/unitysvc_core.egg-info}/PKG-INFO +1 -1
  5. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/tests/test_validators.py +21 -4
  6. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/LICENSE +0 -0
  7. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/README.md +0 -0
  8. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/setup.cfg +0 -0
  9. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/__init__.py +0 -0
  10. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/__init__.py +0 -0
  11. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/base.py +0 -0
  12. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/documents.py +0 -0
  13. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/listing_data.py +0 -0
  14. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/listing_v1.py +0 -0
  15. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/offering_data.py +0 -0
  16. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/offering_v1.py +0 -0
  17. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/pricing.py +0 -0
  18. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/promotion_data.py +0 -0
  19. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/provider_data.py +0 -0
  20. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/provider_v1.py +0 -0
  21. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/service.py +0 -0
  22. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/models/service_group_data.py +0 -0
  23. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/py.typed +0 -0
  24. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/schema/base.json +0 -0
  25. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/schema/listing_v1.json +0 -0
  26. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/schema/offering_v1.json +0 -0
  27. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/schema/promotion_v1.json +0 -0
  28. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/schema/provider_v1.json +0 -0
  29. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/schema/service_group_v1.json +0 -0
  30. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/utils.py +0 -0
  31. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core/validator.py +0 -0
  32. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core.egg-info/SOURCES.txt +0 -0
  33. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core.egg-info/dependency_links.txt +0 -0
  34. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core.egg-info/requires.txt +0 -0
  35. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/src/unitysvc_core.egg-info/top_level.txt +0 -0
  36. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/tests/test_access_interface_routing.py +0 -0
  37. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/tests/test_hidden_path_skip.py +0 -0
  38. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/tests/test_pricing.py +0 -0
  39. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/tests/test_provider_account_rate_limits.py +0 -0
  40. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/tests/test_service_group.py +0 -0
  41. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/tests/test_service_status.py +0 -0
  42. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/tests/test_utils.py +0 -0
  43. {unitysvc_core-0.2.24 → unitysvc_core-0.2.25}/tests/test_validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitysvc-core
3
- Version: 0.2.24
3
+ Version: 0.2.25
4
4
  Summary: Shared data models and validation helpers for UnitySVC packages
5
5
  Author-email: Bo Peng <bo.peng@unitysvc.com>
6
6
  Maintainer-email: Bo Peng <bo.peng@unitysvc.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "unitysvc-core"
7
- version = "0.2.24"
7
+ version = "0.2.25"
8
8
  description = "Shared data models and validation helpers for UnitySVC packages"
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Bo Peng", email = "bo.peng@unitysvc.com" }]
@@ -815,20 +815,39 @@ def validate_mcp_offering(data: dict[str, Any] | None) -> list[str]:
815
815
  """
816
816
  if not data or not isinstance(data, dict):
817
817
  return []
818
- if data.get("service_type") != "mcp":
819
- return []
820
818
 
819
+ service_type = data.get("service_type")
820
+ capabilities = data.get("capabilities")
821
821
  channels = data.get("upstream_access_config")
822
+ mcp_channels = (
823
+ sorted(
824
+ name
825
+ for name, config in (channels or {}).items()
826
+ if isinstance(config, dict) and config.get("access_method") == "mcp"
827
+ )
828
+ if isinstance(channels, dict)
829
+ else []
830
+ )
831
+
832
+ if service_type != "mcp":
833
+ declarations: list[str] = []
834
+ if isinstance(capabilities, list) and "mcp-delegation" in capabilities:
835
+ declarations.append("capability 'mcp-delegation'")
836
+ if mcp_channels:
837
+ declarations.append("MCP upstream channel(s) " + ", ".join(repr(name) for name in mcp_channels))
838
+ if declarations:
839
+ return [
840
+ f"service_type {service_type!r} may not declare "
841
+ + "; ".join(declarations)
842
+ + "; use service_type 'mcp' instead"
843
+ ]
844
+ return []
845
+
822
846
  if not channels or not isinstance(channels, dict):
823
847
  return ["upstream_access_config: an MCP service must declare at least one channel"]
824
848
 
825
- if not any(
826
- isinstance(cfg, dict) and cfg.get("access_method") == "mcp" for cfg in channels.values()
827
- ):
828
- return [
829
- "upstream_access_config: an MCP service must have at least one channel "
830
- "with access_method 'mcp'"
831
- ]
849
+ if not mcp_channels:
850
+ return ["upstream_access_config: an MCP service must have at least one channel with access_method 'mcp'"]
832
851
 
833
852
  return []
834
853
 
@@ -901,9 +920,7 @@ def validate_listing_mcp_base_urls(user_access_interfaces: dict[str, Any] | None
901
920
  f"a non-empty 'namespace' in routing_key"
902
921
  )
903
922
  else:
904
- errors.extend(
905
- validate_mcp_namespace(namespace, f"{field}.routing_key.namespace")
906
- )
923
+ errors.extend(validate_mcp_namespace(namespace, f"{field}.routing_key.namespace"))
907
924
 
908
925
  return errors
909
926
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitysvc-core
3
- Version: 0.2.24
3
+ Version: 0.2.25
4
4
  Summary: Shared data models and validation helpers for UnitySVC packages
5
5
  Author-email: Bo Peng <bo.peng@unitysvc.com>
6
6
  Maintainer-email: Bo Peng <bo.peng@unitysvc.com>
@@ -583,9 +583,28 @@ class TestValidateMcpOffering:
583
583
  def test_valid_mcp_offering_passes(self) -> None:
584
584
  assert validate_mcp_offering(self._offering()) == []
585
585
 
586
- def test_non_mcp_offering_is_ignored(self) -> None:
586
+ def test_non_mcp_offering_without_mcp_declarations_is_ignored(self) -> None:
587
587
  assert validate_mcp_offering({"service_type": "llm"}) == []
588
588
 
589
+ def test_non_mcp_offering_cannot_declare_mcp_delegation(self) -> None:
590
+ errors = validate_mcp_offering({"service_type": "proxy", "capabilities": ["mcp-delegation"]})
591
+ assert errors
592
+ assert "mcp-delegation" in errors[0]
593
+ assert "service_type 'mcp'" in errors[0]
594
+
595
+ def test_non_mcp_offering_cannot_declare_an_mcp_channel(self) -> None:
596
+ errors = validate_mcp_offering(
597
+ {
598
+ "service_type": "proxy",
599
+ "upstream_access_config": {
600
+ "upstream": {"access_method": "mcp", "base_url": "https://mcp.example.test"}
601
+ },
602
+ }
603
+ )
604
+ assert errors
605
+ assert "MCP upstream channel" in errors[0]
606
+ assert "service_type 'mcp'" in errors[0]
607
+
589
608
  def test_requires_a_channel(self) -> None:
590
609
  errors = validate_mcp_offering(self._offering(upstream_access_config={}))
591
610
  assert errors
@@ -593,9 +612,7 @@ class TestValidateMcpOffering:
593
612
 
594
613
  def test_requires_an_mcp_access_method_channel(self) -> None:
595
614
  errors = validate_mcp_offering(
596
- self._offering(
597
- upstream_access_config={"x": {"access_method": "http", "base_url": "https://x"}}
598
- )
615
+ self._offering(upstream_access_config={"x": {"access_method": "http", "base_url": "https://x"}})
599
616
  )
600
617
  assert errors
601
618
  assert "access_method 'mcp'" in errors[0]
File without changes
File without changes
File without changes