Skip to content

Commit 871d2e1

Browse files
Merge pull request #177 from prane-eth/feat/mcp-resources-prompts
Feature: Added support for MCP resources and prompts
2 parents 14f83c4 + fdb7c6b commit 871d2e1

30 files changed

+6074
-1868
lines changed

atomic-agents/atomic_agents/base/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22

33
from .base_io_schema import BaseIOSchema
44
from .base_tool import BaseTool, BaseToolConfig
5+
from .base_resource import BaseResource, BaseResourceConfig
6+
from .base_prompt import BasePrompt, BasePromptConfig
57

68
__all__ = [
79
"BaseIOSchema",
810
"BaseTool",
911
"BaseToolConfig",
12+
"BaseResource",
13+
"BaseResourceConfig",
14+
"BasePrompt",
15+
"BasePromptConfig",
1016
]
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
from typing import Optional, Type, get_args, get_origin
2+
from abc import ABC, abstractmethod
3+
from pydantic import BaseModel
4+
5+
from atomic_agents.base.base_io_schema import BaseIOSchema
6+
7+
8+
class BasePromptConfig(BaseModel):
9+
"""
10+
Configuration for a prompt.
11+
12+
Attributes:
13+
title (Optional[str]): Overrides the default title of the prompt.
14+
description (Optional[str]): Overrides the default description of the prompt.
15+
"""
16+
17+
title: Optional[str] = None
18+
description: Optional[str] = None
19+
20+
21+
class BasePrompt[InputSchema: BaseIOSchema, OutputSchema: BaseIOSchema](ABC):
22+
"""
23+
Base class for prompts within the Atomic Agents framework.
24+
25+
Prompts enable agents to perform specific tasks by providing a standardized interface
26+
for input and output. Each prompt is defined with specific input and output schemas
27+
that enforce type safety and provide documentation.
28+
29+
Type Parameters:
30+
InputSchema: Schema defining the input data, must be a subclass of BaseIOSchema.
31+
OutputSchema: Schema defining the output data, must be a subclass of BaseIOSchema.
32+
33+
Attributes:
34+
config (BasePromptConfig): Configuration for the prompt, including optional title and description overrides.
35+
input_schema (Type[InputSchema]): Schema class defining the input data (derived from generic type parameter).
36+
output_schema (Type[OutputSchema]): Schema class defining the output data (derived from generic type parameter).
37+
prompt_name (str): The name of the prompt, derived from the input schema's title or overridden by the config.
38+
prompt_description (str): Description of the prompt, derived from the input schema's description or overridden by the config.
39+
"""
40+
41+
def __init__(self, config: BasePromptConfig = BasePromptConfig()):
42+
"""
43+
Initializes the BasePrompt with an optional configuration override.
44+
45+
Args:
46+
config (BasePromptConfig, optional): Configuration for the prompt, including optional title and description overrides.
47+
"""
48+
self.config = config
49+
50+
def __init_subclass__(cls, **kwargs):
51+
"""
52+
Hook called when a class is subclassed.
53+
54+
Captures generic type parameters during class creation and stores them as class attributes
55+
to work around the unreliable __orig_class__ attribute in modern Python generic syntax.
56+
"""
57+
super().__init_subclass__(**kwargs)
58+
if hasattr(cls, "__orig_bases__"):
59+
for base in cls.__orig_bases__:
60+
if get_origin(base) is BasePrompt:
61+
args = get_args(base)
62+
if len(args) == 2:
63+
cls._input_schema_cls = args[0]
64+
cls._output_schema_cls = args[1]
65+
break
66+
67+
@property
68+
def input_schema(self) -> Type[InputSchema]:
69+
"""
70+
Returns the input schema class for the prompt.
71+
72+
Returns:
73+
Type[InputSchema]: The input schema class.
74+
"""
75+
# Inheritance pattern: MyPrompt(BasePrompt[Schema1, Schema2])
76+
if hasattr(self.__class__, "_input_schema_cls"):
77+
return self.__class__._input_schema_cls
78+
79+
# Dynamic instantiation: MockPrompt[Schema1, Schema2]()
80+
if hasattr(self, "__orig_class__"):
81+
TI, _ = get_args(self.__orig_class__)
82+
return TI
83+
84+
# No type info available: MockPrompt()
85+
return BaseIOSchema
86+
87+
@property
88+
def output_schema(self) -> Type[OutputSchema]:
89+
"""
90+
Returns the output schema class for the prompt.
91+
92+
Returns:
93+
Type[OutputSchema]: The output schema class.
94+
"""
95+
# Inheritance pattern: MyPrompt(BasePrompt[Schema1, Schema2])
96+
if hasattr(self.__class__, "_output_schema_cls"):
97+
return self.__class__._output_schema_cls
98+
99+
# Dynamic instantiation: MockPrompt[Schema1, Schema2]()
100+
if hasattr(self, "__orig_class__"):
101+
_, TO = get_args(self.__orig_class__)
102+
return TO
103+
104+
# No type info available: MockPrompt()
105+
return BaseIOSchema
106+
107+
@property
108+
def prompt_name(self) -> str:
109+
"""
110+
Returns the name of the prompt.
111+
112+
Returns:
113+
str: The name of the prompt.
114+
"""
115+
return self.config.title or self.input_schema.model_json_schema()["title"]
116+
117+
@property
118+
def prompt_description(self) -> str:
119+
"""
120+
Returns the description of the prompt.
121+
122+
Returns:
123+
str: The description of the prompt.
124+
"""
125+
return self.config.description or self.input_schema.model_json_schema()["description"]
126+
127+
@abstractmethod
128+
def generate(self, params: InputSchema) -> OutputSchema:
129+
"""
130+
Executes the prompt with the provided parameters.
131+
132+
Args:
133+
params (InputSchema): Input parameters adhering to the input schema.
134+
135+
Returns:
136+
OutputSchema: Output resulting from executing the prompt, adhering to the output schema.
137+
138+
Raises:
139+
NotImplementedError: If the method is not implemented by a subclass.
140+
"""
141+
pass
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
from typing import Optional, Type, get_args, get_origin
2+
from abc import ABC, abstractmethod
3+
from pydantic import BaseModel
4+
5+
from atomic_agents.base.base_io_schema import BaseIOSchema
6+
7+
8+
class BaseResourceConfig(BaseModel):
9+
"""
10+
Configuration for a resource.
11+
12+
Attributes:
13+
title (Optional[str]): Overrides the default title of the resource.
14+
description (Optional[str]): Overrides the default description of the resource.
15+
"""
16+
17+
title: Optional[str] = None
18+
description: Optional[str] = None
19+
20+
21+
class BaseResource[InputSchema: BaseIOSchema, OutputSchema: BaseIOSchema](ABC):
22+
"""
23+
Base class for resources within the Atomic Agents framework.
24+
25+
Resources enable agents to perform specific tasks by providing a standardized interface
26+
for input and output. Each resource is defined with specific input and output schemas
27+
that enforce type safety and provide documentation.
28+
29+
Type Parameters:
30+
InputSchema: Schema defining the input data, must be a subclass of BaseIOSchema.
31+
OutputSchema: Schema defining the output data, must be a subclass of BaseIOSchema.
32+
33+
Attributes:
34+
config (BaseResourceConfig): Configuration for the resource, including optional title and description overrides.
35+
input_schema (Type[InputSchema]): Schema class defining the input data (derived from generic type parameter).
36+
output_schema (Type[OutputSchema]): Schema class defining the output data (derived from generic type parameter).
37+
resource_name (str): The name of the resource, derived from the input schema's title or overridden by the config.
38+
resource_description (str): Description of the resource, derived from the input schema's description or overridden by the config.
39+
"""
40+
41+
def __init__(self, config: BaseResourceConfig = BaseResourceConfig()):
42+
"""
43+
Initializes the BaseResource with an optional configuration override.
44+
45+
Args:
46+
config (BaseResourceConfig, optional): Configuration for the resource, including optional title and description overrides.
47+
"""
48+
self.config = config
49+
50+
def __init_subclass__(cls, **kwargs):
51+
"""
52+
Hook called when a class is subclassed.
53+
54+
Captures generic type parameters during class creation and stores them as class attributes
55+
to work around the unreliable __orig_class__ attribute in modern Python generic syntax.
56+
"""
57+
super().__init_subclass__(**kwargs)
58+
if hasattr(cls, "__orig_bases__"):
59+
for base in cls.__orig_bases__:
60+
if get_origin(base) is BaseResource:
61+
args = get_args(base)
62+
if len(args) == 2:
63+
cls._input_schema_cls = args[0]
64+
cls._output_schema_cls = args[1]
65+
break
66+
67+
@property
68+
def input_schema(self) -> Type[InputSchema]:
69+
"""
70+
Returns the input schema class for the resource.
71+
72+
Returns:
73+
Type[InputSchema]: The input schema class.
74+
"""
75+
# Inheritance pattern: MyResource(BaseResource[Schema1, Schema2])
76+
if hasattr(self.__class__, "_input_schema_cls"):
77+
return self.__class__._input_schema_cls
78+
79+
# Dynamic instantiation: MockResource[Schema1, Schema2]()
80+
if hasattr(self, "__orig_class__"):
81+
TI, _ = get_args(self.__orig_class__)
82+
return TI
83+
84+
# No type info available: MockResource()
85+
return BaseIOSchema
86+
87+
@property
88+
def output_schema(self) -> Type[OutputSchema]:
89+
"""
90+
Returns the output schema class for the resource.
91+
92+
Returns:
93+
Type[OutputSchema]: The output schema class.
94+
"""
95+
# Inheritance pattern: MyResource(BaseResource[Schema1, Schema2])
96+
if hasattr(self.__class__, "_output_schema_cls"):
97+
return self.__class__._output_schema_cls
98+
99+
# Dynamic instantiation: MockResource[Schema1, Schema2]()
100+
if hasattr(self, "__orig_class__"):
101+
_, TO = get_args(self.__orig_class__)
102+
return TO
103+
104+
# No type info available: MockResource()
105+
return BaseIOSchema
106+
107+
@property
108+
def resource_name(self) -> str:
109+
"""
110+
Returns the name of the resource.
111+
112+
Returns:
113+
str: The name of the resource.
114+
"""
115+
return self.config.title or self.input_schema.model_json_schema()["title"]
116+
117+
@property
118+
def resource_description(self) -> str:
119+
"""
120+
Returns the description of the resource.
121+
122+
Returns:
123+
str: The description of the resource.
124+
"""
125+
return self.config.description or self.input_schema.model_json_schema()["description"]
126+
127+
@abstractmethod
128+
def read(self, params: InputSchema) -> OutputSchema:
129+
"""
130+
Executes the resource with the provided parameters.
131+
132+
Args:
133+
params (InputSchema): Input parameters adhering to the input schema.
134+
135+
Returns:
136+
OutputSchema: Output resulting from executing the resource, adhering to the output schema.
137+
138+
Raises:
139+
NotImplementedError: If the method is not implemented by a subclass.
140+
"""
141+
pass
Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,39 @@
1-
from .mcp_tool_factory import (
2-
MCPToolFactory,
1+
from .mcp_factory import (
2+
MCPFactory,
33
MCPToolOutputSchema,
44
fetch_mcp_tools,
55
fetch_mcp_tools_async,
6+
fetch_mcp_resources,
7+
fetch_mcp_resources_async,
8+
fetch_mcp_prompts,
9+
fetch_mcp_prompts_async,
610
create_mcp_orchestrator_schema,
7-
fetch_mcp_tools_with_schema,
11+
fetch_mcp_attributes_with_schema,
812
)
913
from .schema_transformer import SchemaTransformer
10-
from .tool_definition_service import MCPTransportType, MCPToolDefinition, ToolDefinitionService
14+
from .mcp_definition_service import (
15+
MCPTransportType,
16+
MCPToolDefinition,
17+
MCPResourceDefinition,
18+
MCPPromptDefinition,
19+
MCPDefinitionService,
20+
)
1121

1222
__all__ = [
13-
"MCPToolFactory",
23+
"MCPFactory",
1424
"MCPToolOutputSchema",
1525
"fetch_mcp_tools",
1626
"fetch_mcp_tools_async",
27+
"fetch_mcp_resources",
28+
"fetch_mcp_resources_async",
29+
"fetch_mcp_prompts",
30+
"fetch_mcp_prompts_async",
1731
"create_mcp_orchestrator_schema",
18-
"fetch_mcp_tools_with_schema",
32+
"fetch_mcp_attributes_with_schema",
1933
"SchemaTransformer",
2034
"MCPTransportType",
2135
"MCPToolDefinition",
22-
"ToolDefinitionService",
36+
"MCPResourceDefinition",
37+
"MCPPromptDefinition",
38+
"MCPDefinitionService",
2339
]

0 commit comments

Comments
 (0)