glaip-sdk 0.7.14__py3-none-any.whl → 0.7.15__py3-none-any.whl

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.
glaip_sdk/agents/base.py CHANGED
@@ -202,7 +202,11 @@ class Agent:
202
202
 
203
203
  self._agent_config = kwargs.pop("agent_config", Agent._UNSET) # type: ignore[assignment]
204
204
  self._tool_configs = kwargs.pop("tool_configs", Agent._UNSET) # type: ignore[assignment]
205
- self._mcp_configs = kwargs.pop("mcp_configs", Agent._UNSET) # type: ignore[assignment]
205
+ mcp_configs = kwargs.pop("mcp_configs", Agent._UNSET)
206
+ if mcp_configs is not Agent._UNSET and isinstance(mcp_configs, dict):
207
+ self._mcp_configs = self._normalize_mcp_configs(mcp_configs)
208
+ else:
209
+ self._mcp_configs = mcp_configs # type: ignore[assignment]
206
210
  self._a2a_profile = kwargs.pop("a2a_profile", Agent._UNSET) # type: ignore[assignment]
207
211
 
208
212
  # Warn about unexpected kwargs
@@ -783,6 +787,47 @@ class Agent:
783
787
 
784
788
  return resolved
785
789
 
790
+ def _normalize_mcp_configs(self, mcp_configs: dict[Any, Any]) -> dict[Any, Any]:
791
+ """Normalize mcp_configs by wrapping misplaced transport keys in 'config'.
792
+
793
+ This ensures that flat transport settings (e.g. {'url': '...'}) provided
794
+ by the user are correctly moved into the 'config' block required by the
795
+ Platform, ensuring parity between local and remote execution.
796
+
797
+ Args:
798
+ mcp_configs: The raw mcp_configs dictionary.
799
+
800
+ Returns:
801
+ Normalized mcp_configs dictionary.
802
+ """
803
+ from glaip_sdk.runner.langgraph import _MCP_TRANSPORT_KEYS # noqa: PLC0415
804
+
805
+ normalized = {}
806
+ for mcp_key, override in mcp_configs.items():
807
+ if not isinstance(override, dict):
808
+ normalized[mcp_key] = override
809
+ continue
810
+
811
+ misplaced = {k: v for k, v in override.items() if k in _MCP_TRANSPORT_KEYS}
812
+
813
+ if misplaced:
814
+ new_override = override.copy()
815
+ config_block = new_override.get("config", {})
816
+ if not isinstance(config_block, dict):
817
+ config_block = {}
818
+
819
+ config_block.update(misplaced)
820
+ new_override["config"] = config_block
821
+
822
+ for k in misplaced:
823
+ new_override.pop(k, None)
824
+
825
+ normalized[mcp_key] = new_override
826
+ else:
827
+ normalized[mcp_key] = override
828
+
829
+ return normalized
830
+
786
831
  def _resolve_agents(self, registry: AgentRegistry) -> list[str]:
787
832
  """Resolve sub-agent references using AgentRegistry.
788
833
 
@@ -70,6 +70,10 @@ def _swallow_aip_logs(level: int = logging.ERROR) -> None:
70
70
  logger = LoggerManager().get_logger(__name__)
71
71
 
72
72
 
73
+ # Constants for MCP configuration validation
74
+ _MCP_TRANSPORT_KEYS = {"url", "command", "args", "env", "timeout", "headers"}
75
+
76
+
73
77
  def _convert_chat_history_to_messages(
74
78
  chat_history: list[dict[str, str]] | None,
75
79
  ) -> list[BaseMessage]:
@@ -870,39 +874,126 @@ class LangGraphRunner(BaseRunner):
870
874
  base_config: dict[str, Any],
871
875
  override: dict[str, Any] | None,
872
876
  ) -> dict[str, Any]:
873
- """Merge a single MCP config with runtime override.
877
+ """Merge a single MCP config with a runtime override, handling normalization and parity fixes.
878
+
879
+ This method orchestrates the merging of base MCP settings (from the object definition)
880
+ with runtime overrides. It enforces Platform parity by prioritizing the nested 'config'
881
+ block while maintaining robustness for local development by auto-fixing flat transport keys.
882
+
883
+ The merge follows these priority rules (highest to lowest):
884
+ 1. Misplaced flat keys in the override (e.g., 'url' at top level) - Auto-fixed with warning.
885
+ 2. Nested 'config' block in the override (Matches Platform/Constructor schema).
886
+ 3. Authentication objects in the override (Converted to HTTP headers).
887
+ 4. Structural settings in the override (e.g., 'allowed_tools').
888
+ 5. Base configuration from the MCP object definition.
889
+
890
+ Examples:
891
+ >>> # 1. Strict Nested Style (Recommended)
892
+ >>> override = {"config": {"url": "https://new.api"}, "allowed_tools": ["t1"]}
893
+ >>> self._merge_single_mcp_config("mcp", base, override)
894
+ >>> # Result: {"url": "https://new.api", "allowed_tools": ["t1"], ...}
895
+
896
+ >>> # 2. Flat Legacy Style (Auto-fixed with warning)
897
+ >>> override = {"url": "https://new.api"}
898
+ >>> self._merge_single_mcp_config("mcp", base, override)
899
+ >>> # Result: {"url": "https://new.api", ...}
900
+
901
+ >>> # 3. Header Merging (Preserves Auth)
902
+ >>> base = {"headers": {"Authorization": "Bearer token"}}
903
+ >>> override = {"headers": {"X-Custom": "val"}}
904
+ >>> self._merge_single_mcp_config("mcp", base, override)
905
+ >>> # Result: {"headers": {"Authorization": "Bearer token", "X-Custom": "val"}, ...}
874
906
 
875
907
  Args:
876
- server_name: Name of the MCP server.
877
- base_config: Base config from adapter.
878
- override: Optional runtime override config.
908
+ server_name: Name of the MCP server being configured.
909
+ base_config: Base configuration dictionary derived from the MCP object.
910
+ override: Optional dictionary of runtime overrides.
879
911
 
880
912
  Returns:
881
- Merged config dict.
913
+ A fully merged and normalized configuration dictionary ready for the local runner.
882
914
  """
883
915
  merged = base_config.copy()
884
916
 
885
917
  if not override:
886
918
  return merged
887
919
 
888
- from glaip_sdk.runner.mcp_adapter.mcp_config_builder import ( # noqa: PLC0415
889
- MCPConfigBuilder,
890
- )
920
+ # 1. Check for misplaced keys and warn (DX/Parity guidance)
921
+ self._warn_if_mcp_override_misplaced(server_name, override)
922
+
923
+ # 2. Apply Authentication (Converted to headers)
924
+ self._apply_mcp_auth_override(server_name, merged, override)
925
+
926
+ # 3. Apply Transport Settings (Nested 'config')
927
+ if "config" in override and isinstance(override["config"], dict):
928
+ merged.update(override["config"])
891
929
 
892
- # Handle authentication override
893
- if "authentication" in override:
894
- headers = MCPConfigBuilder.build_headers_from_auth(override["authentication"])
895
- if headers:
896
- merged["headers"] = headers
897
- logger.debug("Applied runtime authentication headers for MCP '%s'", server_name)
930
+ # 4. Apply Structural Settings (e.g., allowed_tools)
931
+ if "allowed_tools" in override:
932
+ merged["allowed_tools"] = override["allowed_tools"]
898
933
 
899
- # Merge other config keys (excluding authentication since we converted it)
934
+ # 5. Preserve unknown top-level keys (backward compatibility)
935
+ known_keys = _MCP_TRANSPORT_KEYS | {"config", "authentication", "allowed_tools"}
900
936
  for key, value in override.items():
901
- if key != "authentication":
937
+ if key not in known_keys:
902
938
  merged[key] = value
903
939
 
940
+ # 6. Apply Auto-fix for misplaced keys (Local Success)
941
+ for key in [k for k in override if k in _MCP_TRANSPORT_KEYS]:
942
+ val = override[key]
943
+ # Special case: Merge headers instead of overwriting to preserve auth
944
+ if key == "headers" and isinstance(val, dict) and isinstance(merged.get("headers"), dict):
945
+ merged["headers"].update(val)
946
+ else:
947
+ merged[key] = val
948
+
904
949
  return merged
905
950
 
951
+ def _warn_if_mcp_override_misplaced(self, server_name: str, override: dict[str, Any]) -> None:
952
+ """Log a warning if transport keys are found at the top level of an override.
953
+
954
+ Args:
955
+ server_name: Name of the MCP server.
956
+ override: The raw override dictionary.
957
+ """
958
+ misplaced = [k for k in override if k in _MCP_TRANSPORT_KEYS]
959
+ if misplaced:
960
+ logger.warning(
961
+ "MCP '%s' override contains transport keys at the top level: %s. "
962
+ "This structure is inconsistent with the Platform and MCP constructor. "
963
+ "Transport settings should be nested within a 'config' dictionary. "
964
+ "Example: mcp_configs={'%s': {'config': {'%s': '...'}}}. "
965
+ "Automatically merging top-level keys for local execution parity.",
966
+ server_name,
967
+ misplaced,
968
+ server_name,
969
+ misplaced[0],
970
+ )
971
+
972
+ def _apply_mcp_auth_override(
973
+ self,
974
+ server_name: str,
975
+ merged_config: dict[str, Any],
976
+ override: dict[str, Any],
977
+ ) -> None:
978
+ """Convert authentication override to headers and apply to config.
979
+
980
+ Args:
981
+ server_name: Name of the MCP server.
982
+ merged_config: The configuration being built (mutated in place).
983
+ override: The raw override dictionary.
984
+ """
985
+ if "authentication" not in override:
986
+ return
987
+
988
+ from glaip_sdk.runner.mcp_adapter.mcp_config_builder import ( # noqa: PLC0415
989
+ MCPConfigBuilder,
990
+ )
991
+
992
+ headers = MCPConfigBuilder.build_headers_from_auth(override["authentication"])
993
+ if headers:
994
+ merged_config["headers"] = headers
995
+ logger.debug("Applied runtime authentication headers for MCP '%s'", server_name)
996
+
906
997
  def _validate_sub_agent_for_local_mode(self, sub_agent: Any) -> None:
907
998
  """Validate that a sub-agent reference is supported for local execution.
908
999
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glaip-sdk
3
- Version: 0.7.14
3
+ Version: 0.7.15
4
4
  Summary: Python SDK and CLI for GL AIP (GDP Labs AI Agent Package) - Build, run, and manage AI agents
5
5
  Author-email: Raymond Christopher <raymond.christopher@gdplabs.id>
6
6
  License: MIT
@@ -5,7 +5,7 @@ glaip_sdk/exceptions.py,sha256=iAChFClkytXRBLP0vZq1_YjoZxA9i4m4bW1gDLiGR1g,2321
5
5
  glaip_sdk/icons.py,sha256=J5THz0ReAmDwIiIooh1_G3Le-mwTJyEjhJDdJ13KRxM,524
6
6
  glaip_sdk/rich_components.py,sha256=44Z0V1ZQleVh9gUDGwRR5mriiYFnVGOhm7fFxZYbP8c,4052
7
7
  glaip_sdk/agents/__init__.py,sha256=VfYov56edbWuySXFEbWJ_jLXgwnFzPk1KB-9-mfsUCc,776
8
- glaip_sdk/agents/base.py,sha256=Aq7bDGHAw7214wW-9Re_uAzbaGphIUpIIbuibuDf7i4,49131
8
+ glaip_sdk/agents/base.py,sha256=OCJP2yBOo1rYqUAzpwdcEb2ZjIGHj9-ivDvldzfQX48,50813
9
9
  glaip_sdk/cli/__init__.py,sha256=xCCfuF1Yc7mpCDcfhHZTX0vizvtrDSLeT8MJ3V7m5A0,156
10
10
  glaip_sdk/cli/account_store.py,sha256=u_memecwEQssustZs2wYBrHbEmKUlDfmmL-zO1F3n3A,19034
11
11
  glaip_sdk/cli/agent_config.py,sha256=YAbFKrTNTRqNA6b0i0Q3pH-01rhHDRi5v8dxSFwGSwM,2401
@@ -151,7 +151,7 @@ glaip_sdk/registry/tool.py,sha256=c0Ja4rFYMOKs_1yjDLDZxCId4IjQzprwXzX0iIL8Fio,14
151
151
  glaip_sdk/runner/__init__.py,sha256=orJ3nLR9P-n1qMaAMWZ_xRS4368YnDpdltg-bX5BlUk,2210
152
152
  glaip_sdk/runner/base.py,sha256=KIjcSAyDCP9_mn2H4rXR5gu1FZlwD9pe0gkTBmr6Yi4,2663
153
153
  glaip_sdk/runner/deps.py,sha256=Du3hr2R5RHOYCRAv7RVmx661x-ayVXIeZ8JD7ODirTA,3884
154
- glaip_sdk/runner/langgraph.py,sha256=sKF8JMMRicr9iiDNA8hIoZVfvfhjT_TMqlYfvW3yT6w,36379
154
+ glaip_sdk/runner/langgraph.py,sha256=HB1n6w44LdyE7OSB2iSEtwhm_IeD7fGi_20erCZurX4,40869
155
155
  glaip_sdk/runner/logging_config.py,sha256=OrQgW23t42qQRqEXKH8U4bFg4JG5EEkUJTlbvtU65iE,2528
156
156
  glaip_sdk/runner/mcp_adapter/__init__.py,sha256=Rdttfg3N6kg3-DaTCKqaGXKByZyBt0Mwf6FV8s_5kI8,462
157
157
  glaip_sdk/runner/mcp_adapter/base_mcp_adapter.py,sha256=ic56fKgb3zgVZZQm3ClWUZi7pE1t4EVq8mOg6AM6hdA,1374
@@ -216,8 +216,8 @@ glaip_sdk/utils/rendering/steps/format.py,sha256=Chnq7OBaj8XMeBntSBxrX5zSmrYeGcO
216
216
  glaip_sdk/utils/rendering/steps/manager.py,sha256=BiBmTeQMQhjRMykgICXsXNYh1hGsss-fH9BIGVMWFi0,13194
217
217
  glaip_sdk/utils/rendering/viewer/__init__.py,sha256=XrxmE2cMAozqrzo1jtDFm8HqNtvDcYi2mAhXLXn5CjI,457
218
218
  glaip_sdk/utils/rendering/viewer/presenter.py,sha256=mlLMTjnyeyPVtsyrAbz1BJu9lFGQSlS-voZ-_Cuugv0,5725
219
- glaip_sdk-0.7.14.dist-info/METADATA,sha256=zUdx8wiJbp1JiDNlntuItt5_Y1krwQRZDhpyEcnGcDo,8528
220
- glaip_sdk-0.7.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
221
- glaip_sdk-0.7.14.dist-info/entry_points.txt,sha256=NkhO6FfgX9Zrjn63GuKphf-dLw7KNJvucAcXc7P3aMk,54
222
- glaip_sdk-0.7.14.dist-info/top_level.txt,sha256=td7yXttiYX2s94-4wFhv-5KdT0rSZ-pnJRSire341hw,10
223
- glaip_sdk-0.7.14.dist-info/RECORD,,
219
+ glaip_sdk-0.7.15.dist-info/METADATA,sha256=L7wF6kfY6yiNHvUsl6gGSVhFjMGllD9K3ZHA3UmIbkA,8528
220
+ glaip_sdk-0.7.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
221
+ glaip_sdk-0.7.15.dist-info/entry_points.txt,sha256=NkhO6FfgX9Zrjn63GuKphf-dLw7KNJvucAcXc7P3aMk,54
222
+ glaip_sdk-0.7.15.dist-info/top_level.txt,sha256=td7yXttiYX2s94-4wFhv-5KdT0rSZ-pnJRSire341hw,10
223
+ glaip_sdk-0.7.15.dist-info/RECORD,,