Coverage for mcpgateway / plugins / framework / hooks / agents.py: 100%
39 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-09 03:05 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-09 03:05 +0000
1# -*- coding: utf-8 -*-
2"""Location: ./mcpgateway/plugins/models/agents.py
3Copyright 2025
4SPDX-License-Identifier: Apache-2.0
5Authors: Teryl Taylor, Fred Araujo
7Pydantic models for agent plugins.
8This module implements the pydantic models associated with
9the base plugin layer including configurations, and contexts.
10"""
12# Standard
13from enum import Enum
14from typing import Any, Dict, List, Optional
16# Third-Party
17from pydantic import Field, field_validator
19# First-Party
20from mcpgateway.plugins.framework.hooks.http import HttpHeaderPayload
21from mcpgateway.plugins.framework.models import PluginPayload, PluginResult
22from mcpgateway.plugins.framework.protocols import MessageLike # noqa: F401 # pylint: disable=unused-import
23from mcpgateway.plugins.framework.utils import coerce_messages
26class AgentHookType(str, Enum):
27 """Agent hook points.
29 Attributes:
30 AGENT_PRE_INVOKE: Before agent invocation.
31 AGENT_POST_INVOKE: After agent responds.
33 Examples:
34 >>> AgentHookType.AGENT_PRE_INVOKE
35 <AgentHookType.AGENT_PRE_INVOKE: 'agent_pre_invoke'>
36 >>> AgentHookType.AGENT_PRE_INVOKE.value
37 'agent_pre_invoke'
38 >>> AgentHookType('agent_post_invoke')
39 <AgentHookType.AGENT_POST_INVOKE: 'agent_post_invoke'>
40 >>> list(AgentHookType)
41 [<AgentHookType.AGENT_PRE_INVOKE: 'agent_pre_invoke'>, <AgentHookType.AGENT_POST_INVOKE: 'agent_post_invoke'>]
42 """
44 AGENT_PRE_INVOKE = "agent_pre_invoke"
45 AGENT_POST_INVOKE = "agent_post_invoke"
48class AgentPreInvokePayload(PluginPayload):
49 """Agent payload for pre-invoke hook.
51 Attributes:
52 agent_id: The agent identifier (can be modified for routing).
53 messages: Conversation messages (accepts any MessageLike-satisfying objects).
54 tools: Optional list of tools available to agent.
55 headers: Optional HTTP headers.
56 model: Optional model override.
57 system_prompt: Optional system instructions.
58 parameters: Optional LLM parameters (temperature, max_tokens, etc.).
60 Examples:
61 >>> payload = AgentPreInvokePayload(agent_id="agent-123", messages=[])
62 >>> payload.agent_id
63 'agent-123'
64 >>> payload.messages
65 []
66 >>> payload.tools is None
67 True
68 """
70 agent_id: str
71 messages: List[Any] # Elements satisfy MessageLike protocol (role, content attributes)
72 tools: Optional[List[str]] = None
73 headers: Optional[HttpHeaderPayload] = None
74 model: Optional[str] = None
75 system_prompt: Optional[str] = None
76 parameters: Optional[Dict[str, Any]] = Field(default_factory=dict)
78 @field_validator("messages", mode="before")
79 @classmethod
80 def _coerce_messages(cls, v: Any) -> Any:
81 """Convert nested dicts in messages list to objects with attribute access.
83 Args:
84 v: The raw messages value to coerce.
86 Returns:
87 The coerced messages list.
88 """
89 return coerce_messages(v)
92class AgentPostInvokePayload(PluginPayload):
93 """Agent payload for post-invoke hook.
95 Attributes:
96 agent_id: The agent identifier.
97 messages: Response messages from agent (accepts any MessageLike-satisfying objects).
98 tool_calls: Optional tool invocations made by agent.
100 Examples:
101 >>> payload = AgentPostInvokePayload(agent_id="agent-123", messages=[])
102 >>> payload.agent_id
103 'agent-123'
104 >>> payload.messages
105 []
106 >>> payload.tool_calls is None
107 True
108 """
110 agent_id: str
111 messages: List[Any] # Elements satisfy MessageLike protocol (role, content attributes)
112 tool_calls: Optional[List[Dict[str, Any]]] = None
114 @field_validator("messages", mode="before")
115 @classmethod
116 def _coerce_messages(cls, v: Any) -> Any:
117 """Convert nested dicts in messages list to objects with attribute access.
119 Args:
120 v: The raw messages value to coerce.
122 Returns:
123 The coerced messages list.
124 """
125 return coerce_messages(v)
128AgentPreInvokeResult = PluginResult[AgentPreInvokePayload]
129AgentPostInvokeResult = PluginResult[AgentPostInvokePayload]
132def _register_agent_hooks() -> None:
133 """Register agent hooks in the global registry.
135 This is called lazily to avoid circular import issues.
136 """
137 # Import here to avoid circular dependency at module load time
138 # First-Party
139 from mcpgateway.plugins.framework.hooks.registry import get_hook_registry # pylint: disable=import-outside-toplevel
141 registry = get_hook_registry()
143 # Only register if not already registered (idempotent)
144 if not registry.is_registered(AgentHookType.AGENT_PRE_INVOKE):
145 registry.register_hook(AgentHookType.AGENT_PRE_INVOKE, AgentPreInvokePayload, AgentPreInvokeResult)
146 registry.register_hook(AgentHookType.AGENT_POST_INVOKE, AgentPostInvokePayload, AgentPostInvokeResult)
149_register_agent_hooks()