acp-sdk 0.0.6__py3-none-any.whl → 1.0.0rc1__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.
Files changed (69) hide show
  1. acp_sdk/client/__init__.py +1 -0
  2. acp_sdk/client/client.py +135 -0
  3. acp_sdk/models.py +219 -0
  4. acp_sdk/server/__init__.py +2 -0
  5. acp_sdk/server/agent.py +32 -0
  6. acp_sdk/server/bundle.py +133 -0
  7. acp_sdk/server/context.py +6 -0
  8. acp_sdk/server/server.py +137 -0
  9. acp_sdk/server/telemetry.py +45 -0
  10. acp_sdk/server/utils.py +12 -0
  11. acp_sdk-1.0.0rc1.dist-info/METADATA +53 -0
  12. acp_sdk-1.0.0rc1.dist-info/RECORD +15 -0
  13. acp/__init__.py +0 -138
  14. acp/cli/__init__.py +0 -6
  15. acp/cli/claude.py +0 -139
  16. acp/cli/cli.py +0 -471
  17. acp/client/__main__.py +0 -79
  18. acp/client/session.py +0 -372
  19. acp/client/sse.py +0 -145
  20. acp/client/stdio.py +0 -153
  21. acp/server/__init__.py +0 -3
  22. acp/server/__main__.py +0 -50
  23. acp/server/highlevel/__init__.py +0 -9
  24. acp/server/highlevel/agents/__init__.py +0 -5
  25. acp/server/highlevel/agents/agent_manager.py +0 -110
  26. acp/server/highlevel/agents/base.py +0 -20
  27. acp/server/highlevel/agents/templates.py +0 -21
  28. acp/server/highlevel/context.py +0 -185
  29. acp/server/highlevel/exceptions.py +0 -25
  30. acp/server/highlevel/prompts/__init__.py +0 -4
  31. acp/server/highlevel/prompts/base.py +0 -167
  32. acp/server/highlevel/prompts/manager.py +0 -50
  33. acp/server/highlevel/prompts/prompt_manager.py +0 -33
  34. acp/server/highlevel/resources/__init__.py +0 -23
  35. acp/server/highlevel/resources/base.py +0 -48
  36. acp/server/highlevel/resources/resource_manager.py +0 -94
  37. acp/server/highlevel/resources/templates.py +0 -80
  38. acp/server/highlevel/resources/types.py +0 -185
  39. acp/server/highlevel/server.py +0 -705
  40. acp/server/highlevel/tools/__init__.py +0 -4
  41. acp/server/highlevel/tools/base.py +0 -83
  42. acp/server/highlevel/tools/tool_manager.py +0 -53
  43. acp/server/highlevel/utilities/__init__.py +0 -1
  44. acp/server/highlevel/utilities/func_metadata.py +0 -210
  45. acp/server/highlevel/utilities/logging.py +0 -43
  46. acp/server/highlevel/utilities/types.py +0 -54
  47. acp/server/lowlevel/__init__.py +0 -3
  48. acp/server/lowlevel/helper_types.py +0 -9
  49. acp/server/lowlevel/server.py +0 -643
  50. acp/server/models.py +0 -17
  51. acp/server/session.py +0 -315
  52. acp/server/sse.py +0 -175
  53. acp/server/stdio.py +0 -83
  54. acp/server/websocket.py +0 -61
  55. acp/shared/__init__.py +0 -0
  56. acp/shared/context.py +0 -14
  57. acp/shared/exceptions.py +0 -14
  58. acp/shared/memory.py +0 -87
  59. acp/shared/progress.py +0 -40
  60. acp/shared/session.py +0 -413
  61. acp/shared/version.py +0 -3
  62. acp/types.py +0 -1258
  63. acp_sdk-0.0.6.dist-info/METADATA +0 -46
  64. acp_sdk-0.0.6.dist-info/RECORD +0 -57
  65. acp_sdk-0.0.6.dist-info/entry_points.txt +0 -2
  66. acp_sdk-0.0.6.dist-info/licenses/LICENSE +0 -22
  67. {acp/client → acp_sdk}/__init__.py +0 -0
  68. {acp → acp_sdk}/py.typed +0 -0
  69. {acp_sdk-0.0.6.dist-info → acp_sdk-1.0.0rc1.dist-info}/WHEEL +0 -0
acp/server/__main__.py DELETED
@@ -1,50 +0,0 @@
1
- import importlib.metadata
2
- import logging
3
- import sys
4
-
5
- import anyio
6
-
7
- from acp.server.models import InitializationOptions
8
- from acp.server.session import ServerSession
9
- from acp.server.stdio import stdio_server
10
- from acp.types import ServerCapabilities
11
-
12
- if not sys.warnoptions:
13
- import warnings
14
-
15
- warnings.simplefilter("ignore")
16
-
17
- logging.basicConfig(level=logging.INFO)
18
- logger = logging.getLogger("server")
19
-
20
-
21
- async def receive_loop(session: ServerSession):
22
- logger.info("Starting receive loop")
23
- async for message in session.incoming_messages:
24
- if isinstance(message, Exception):
25
- logger.error("Error: %s", message)
26
- continue
27
-
28
- logger.info("Received message from client: %s", message)
29
-
30
-
31
- async def main():
32
- version = importlib.metadata.version("mcp")
33
- async with stdio_server() as (read_stream, write_stream):
34
- async with (
35
- ServerSession(
36
- read_stream,
37
- write_stream,
38
- InitializationOptions(
39
- server_name="mcp",
40
- server_version=version,
41
- capabilities=ServerCapabilities(),
42
- ),
43
- ) as session,
44
- write_stream,
45
- ):
46
- await receive_loop(session)
47
-
48
-
49
- if __name__ == "__main__":
50
- anyio.run(main, backend="trio")
@@ -1,9 +0,0 @@
1
- """FastMCP - A more ergonomic interface for MCP servers."""
2
-
3
- from importlib.metadata import version
4
-
5
- from .server import Context, Server
6
- from .utilities.types import Image
7
-
8
- __version__ = version("acp-sdk")
9
- __all__ = ["Server", "Context", "Image"]
@@ -1,5 +0,0 @@
1
- from .agent_manager import AgentManager
2
- from .base import Agent
3
- from .templates import AgentTemplate
4
-
5
- __all__ = ["Agent", "AgentTemplate", "AgentManager"]
@@ -1,110 +0,0 @@
1
- from typing import TYPE_CHECKING, Any
2
-
3
- from acp.server.highlevel.agents.base import Agent
4
- from acp.server.highlevel.agents.templates import AgentTemplate
5
- from acp.server.highlevel.exceptions import AgentError
6
- from acp.server.highlevel.utilities.logging import get_logger
7
-
8
- if TYPE_CHECKING:
9
- from acp.server.highlevel.server import Context
10
-
11
- logger = get_logger(__name__)
12
-
13
-
14
- class AgentManager:
15
- """Manages FastMCP agents."""
16
-
17
- def __init__(self, warn_on_duplicate_agents: bool = True):
18
- self._agents: dict[str, Agent] = {}
19
- self._templates: dict[str, AgentTemplate] = {}
20
- self.warn_on_duplicate_agents = warn_on_duplicate_agents
21
-
22
- def get_template(self, name: str) -> AgentTemplate | None:
23
- """Get agent template by name."""
24
- return self._templates.get(name)
25
-
26
- def list_templates(self) -> list[AgentTemplate]:
27
- """List all registered agent templates."""
28
- return list(self._templates.values())
29
-
30
- def add_template(
31
- self,
32
- template: AgentTemplate,
33
- ) -> AgentTemplate:
34
- """Add a template to the server."""
35
- existing = self._templates.get(template.name)
36
- if existing:
37
- if self.warn_on_duplicate_agents:
38
- logger.warning(f"Agent template already exists: {template.name}")
39
- return existing
40
- self._templates[template.name] = template
41
- return template
42
-
43
- def get_agent(self, name: str) -> Agent | None:
44
- """Get agent by name."""
45
- return self._agents.get(name)
46
-
47
- def list_agents(self) -> list[Agent]:
48
- """List all registered agents."""
49
- return list(self._agents.values())
50
-
51
- def add_agent(
52
- self,
53
- agent: Agent,
54
- ) -> Agent:
55
- """Add an agent to the server."""
56
- existing = self._agents.get(agent.name)
57
- if existing:
58
- if self.warn_on_duplicate_agents:
59
- logger.warning(f"Agent already exists: {agent.name}")
60
- return existing
61
- self._agents[agent.name] = agent
62
- return agent
63
-
64
- async def create_agent(
65
- self, name: str, config: dict[str, Any], context: "Context"
66
- ) -> Agent:
67
- """Call an agent by name with arguments."""
68
- template = self.get_template(name)
69
- if not template:
70
- raise AgentError(f"Unknown agent template: {name}")
71
-
72
- agent = await template.create_fn(template.model_validate(config), context)
73
- existing = self._agents.get(agent.name)
74
- if existing:
75
- if self.warn_on_duplicate_agents:
76
- logger.warning(f"Agent already exists: {agent.name}")
77
- return existing
78
- self._agents[agent.name] = agent
79
- return agent
80
-
81
- async def destroy_agent(self, name: str, context: "Context") -> None:
82
- """Call an agent by name with arguments."""
83
- agent = self.get_agent(name)
84
- if not agent:
85
- raise AgentError(f"Unknown agent: {name}")
86
-
87
- if not agent.destroy_fn:
88
- raise AgentError(f"Agent cannot be destroyed: {name}")
89
-
90
- try:
91
- await agent.destroy_fn(context)
92
- except Exception as e:
93
- logger.warning(f"Error destroying agent {name}: {e}")
94
- finally:
95
- del self._agents[name]
96
- return
97
-
98
- async def run_agent(
99
- self, name: str, input: dict[str, Any], *, context: "Context"
100
- ) -> Any:
101
- """Run an agent by name with input."""
102
- agent = self.get_agent(name)
103
- if not agent:
104
- raise AgentError(f"Unknown agent: {name}")
105
-
106
- try:
107
- output = await agent.run_fn(agent.input.model_validate(input), ctx=context) # type: ignore
108
- return output.model_dump()
109
- except Exception as e:
110
- raise AgentError(f"Error running agent {name}: {e}") from e
@@ -1,20 +0,0 @@
1
- from typing import Awaitable, Callable, Type
2
-
3
- from pydantic import BaseModel, ConfigDict, Field
4
-
5
- from acp.server.highlevel.context import Context
6
-
7
-
8
- class Agent(BaseModel):
9
- """Internal agent info."""
10
-
11
- name: str = Field(description="Name of the agent")
12
- description: str | None = Field(description="Description of what the agent does")
13
-
14
- input: Type[BaseModel] = Field(description="Model for input")
15
- output: Type[BaseModel] = Field(description="Model for output")
16
-
17
- run_fn: Callable[[BaseModel, "Context"], Awaitable[BaseModel]] = Field(exclude=True)
18
- destroy_fn: Callable[["Context"], Awaitable[None]] | None = Field(exclude=True)
19
-
20
- model_config = ConfigDict(extra="allow")
@@ -1,21 +0,0 @@
1
- from typing import Awaitable, Callable, Type
2
-
3
- from pydantic import BaseModel, ConfigDict, Field
4
-
5
- from acp.server.highlevel.agents.base import Agent
6
- from acp.server.highlevel.context import Context
7
-
8
-
9
- class AgentTemplate(BaseModel):
10
- """A template for creating agents."""
11
-
12
- name: str = Field(description="Name of the agent")
13
- description: str | None = Field(description="Description of what the agent does")
14
-
15
- config: Type[BaseModel] = Field(description="Model for config")
16
- input: Type[BaseModel] = Field(description="Model for run input")
17
- output: Type[BaseModel] = Field(description="Model for run output")
18
-
19
- create_fn: Callable[[BaseModel, "Context"], Awaitable[Agent]] = Field(exclude=True)
20
-
21
- model_config = ConfigDict(extra="allow")
@@ -1,185 +0,0 @@
1
- from typing import TYPE_CHECKING, Any, Literal
2
-
3
- from pydantic import BaseModel
4
- from pydantic.networks import AnyUrl
5
-
6
- from acp.server.lowlevel.helper_types import ReadResourceContents
7
- from acp.shared.context import RequestContext
8
-
9
- if TYPE_CHECKING:
10
- from acp.server.highlevel.server import Server
11
-
12
-
13
- class Context(BaseModel):
14
- """Context object providing access to MCP capabilities.
15
-
16
- This provides a cleaner interface to MCP's RequestContext functionality.
17
- It gets injected into tool and resource functions that request it via type hints.
18
-
19
- To use context in a tool function, add a parameter with the Context type annotation:
20
-
21
- ```python
22
- @server.tool()
23
- def my_tool(x: int, ctx: Context) -> str:
24
- # Log messages to the client
25
- ctx.info(f"Processing {x}")
26
- ctx.debug("Debug info")
27
- ctx.warning("Warning message")
28
- ctx.error("Error message")
29
-
30
- # Report progress
31
- ctx.report_progress(50, 100)
32
-
33
- # Access resources
34
- data = ctx.read_resource("resource://data")
35
-
36
- # Get request info
37
- request_id = ctx.request_id
38
- client_id = ctx.client_id
39
-
40
- return str(x)
41
- ```
42
-
43
- The context parameter name can be anything as long as it's annotated with Context.
44
- The context is optional - tools that don't need it can omit the parameter.
45
- """
46
-
47
- _request_context: RequestContext | None
48
- _fastmcp: "Server | None"
49
-
50
- def __init__(
51
- self,
52
- *,
53
- request_context: RequestContext | None = None,
54
- fastmcp: "Server | None" = None,
55
- **kwargs: Any,
56
- ):
57
- super().__init__(**kwargs)
58
- self._request_context = request_context
59
- self._fastmcp = fastmcp
60
-
61
- @property
62
- def fastmcp(self) -> "Server":
63
- """Access to the FastMCP server."""
64
- if self._fastmcp is None:
65
- raise ValueError("Context is not available outside of a request")
66
- return self._fastmcp
67
-
68
- @property
69
- def request_context(self) -> RequestContext:
70
- """Access to the underlying request context."""
71
- if self._request_context is None:
72
- raise ValueError("Context is not available outside of a request")
73
- return self._request_context
74
-
75
- async def report_progress(
76
- self, progress: float, total: float | None = None
77
- ) -> None:
78
- """Report progress for the current operation.
79
-
80
- Args:
81
- progress: Current progress value e.g. 24
82
- total: Optional total value e.g. 100
83
- """
84
-
85
- progress_token = (
86
- self.request_context.meta.progressToken
87
- if self.request_context.meta
88
- else None
89
- )
90
-
91
- if progress_token is None:
92
- return
93
-
94
- await self.request_context.session.send_progress_notification(
95
- progress_token=progress_token, progress=progress, total=total
96
- )
97
-
98
- async def report_agent_run_progress(self, delta: BaseModel) -> None:
99
- """Report progress for the agent run operation.
100
-
101
- Args:
102
- delta: partial run output
103
- """
104
-
105
- progress_token = (
106
- self.request_context.meta.progressToken
107
- if self.request_context.meta
108
- else None
109
- )
110
-
111
- if not progress_token:
112
- return
113
-
114
- await self.request_context.session.send_agent_run_progress(
115
- progress_token=progress_token, delta=delta.model_dump()
116
- )
117
-
118
- async def read_resource(self, uri: str | AnyUrl) -> ReadResourceContents:
119
- """Read a resource by URI.
120
-
121
- Args:
122
- uri: Resource URI to read
123
-
124
- Returns:
125
- The resource content as either text or bytes
126
- """
127
- assert self._fastmcp is not None, (
128
- "Context is not available outside of a request"
129
- )
130
- return await self._fastmcp.read_resource(uri)
131
-
132
- async def log(
133
- self,
134
- level: Literal["debug", "info", "warning", "error"],
135
- message: str,
136
- *,
137
- logger_name: str | None = None,
138
- ) -> None:
139
- """Send a log message to the client.
140
-
141
- Args:
142
- level: Log level (debug, info, warning, error)
143
- message: Log message
144
- logger_name: Optional logger name
145
- **extra: Additional structured data to include
146
- """
147
- await self.request_context.session.send_log_message(
148
- level=level, data=message, logger=logger_name
149
- )
150
-
151
- @property
152
- def client_id(self) -> str | None:
153
- """Get the client ID if available."""
154
- return (
155
- getattr(self.request_context.meta, "client_id", None)
156
- if self.request_context.meta
157
- else None
158
- )
159
-
160
- @property
161
- def request_id(self) -> str:
162
- """Get the unique ID for this request."""
163
- return str(self.request_context.request_id)
164
-
165
- @property
166
- def session(self):
167
- """Access to the underlying session for advanced usage."""
168
- return self.request_context.session
169
-
170
- # Convenience methods for common log levels
171
- async def debug(self, message: str, **extra: Any) -> None:
172
- """Send a debug log message."""
173
- await self.log("debug", message, **extra)
174
-
175
- async def info(self, message: str, **extra: Any) -> None:
176
- """Send an info log message."""
177
- await self.log("info", message, **extra)
178
-
179
- async def warning(self, message: str, **extra: Any) -> None:
180
- """Send a warning log message."""
181
- await self.log("warning", message, **extra)
182
-
183
- async def error(self, message: str, **extra: Any) -> None:
184
- """Send an error log message."""
185
- await self.log("error", message, **extra)
@@ -1,25 +0,0 @@
1
- """Custom exceptions for FastMCP."""
2
-
3
-
4
- class FastMCPError(Exception):
5
- """Base error for FastMCP."""
6
-
7
-
8
- class ValidationError(FastMCPError):
9
- """Error in validating parameters or return values."""
10
-
11
-
12
- class ResourceError(FastMCPError):
13
- """Error in resource operations."""
14
-
15
-
16
- class ToolError(FastMCPError):
17
- """Error in tool operations."""
18
-
19
-
20
- class AgentError(FastMCPError):
21
- """Error in agent operations."""
22
-
23
-
24
- class InvalidSignature(Exception):
25
- """Invalid signature for use with FastMCP."""
@@ -1,4 +0,0 @@
1
- from .base import Prompt
2
- from .manager import PromptManager
3
-
4
- __all__ = ["Prompt", "PromptManager"]
@@ -1,167 +0,0 @@
1
- """Base classes for FastMCP prompts."""
2
-
3
- import inspect
4
- import json
5
- from collections.abc import Callable
6
- from typing import Any, Awaitable, Literal, Sequence
7
-
8
- import pydantic_core
9
- from pydantic import BaseModel, Field, TypeAdapter, validate_call
10
-
11
- from acp.types import EmbeddedResource, ImageContent, TextContent
12
-
13
- CONTENT_TYPES = TextContent | ImageContent | EmbeddedResource
14
-
15
-
16
- class Message(BaseModel):
17
- """Base class for all prompt messages."""
18
-
19
- role: Literal["user", "assistant"]
20
- content: CONTENT_TYPES
21
-
22
- def __init__(self, content: str | CONTENT_TYPES, **kwargs):
23
- if isinstance(content, str):
24
- content = TextContent(type="text", text=content)
25
- super().__init__(content=content, **kwargs)
26
-
27
-
28
- class UserMessage(Message):
29
- """A message from the user."""
30
-
31
- role: Literal["user", "assistant"] = "user"
32
-
33
- def __init__(self, content: str | CONTENT_TYPES, **kwargs):
34
- super().__init__(content=content, **kwargs)
35
-
36
-
37
- class AssistantMessage(Message):
38
- """A message from the assistant."""
39
-
40
- role: Literal["user", "assistant"] = "assistant"
41
-
42
- def __init__(self, content: str | CONTENT_TYPES, **kwargs):
43
- super().__init__(content=content, **kwargs)
44
-
45
-
46
- message_validator = TypeAdapter(UserMessage | AssistantMessage)
47
-
48
- SyncPromptResult = (
49
- str | Message | dict[str, Any] | Sequence[str | Message | dict[str, Any]]
50
- )
51
- PromptResult = SyncPromptResult | Awaitable[SyncPromptResult]
52
-
53
-
54
- class PromptArgument(BaseModel):
55
- """An argument that can be passed to a prompt."""
56
-
57
- name: str = Field(description="Name of the argument")
58
- description: str | None = Field(
59
- None, description="Description of what the argument does"
60
- )
61
- required: bool = Field(
62
- default=False, description="Whether the argument is required"
63
- )
64
-
65
-
66
- class Prompt(BaseModel):
67
- """A prompt template that can be rendered with parameters."""
68
-
69
- name: str = Field(description="Name of the prompt")
70
- description: str | None = Field(
71
- None, description="Description of what the prompt does"
72
- )
73
- arguments: list[PromptArgument] | None = Field(
74
- None, description="Arguments that can be passed to the prompt"
75
- )
76
- fn: Callable = Field(exclude=True)
77
-
78
- @classmethod
79
- def from_function(
80
- cls,
81
- fn: Callable[..., PromptResult],
82
- name: str | None = None,
83
- description: str | None = None,
84
- ) -> "Prompt":
85
- """Create a Prompt from a function.
86
-
87
- The function can return:
88
- - A string (converted to a message)
89
- - A Message object
90
- - A dict (converted to a message)
91
- - A sequence of any of the above
92
- """
93
- func_name = name or fn.__name__
94
-
95
- if func_name == "<lambda>":
96
- raise ValueError("You must provide a name for lambda functions")
97
-
98
- # Get schema from TypeAdapter - will fail if function isn't properly typed
99
- parameters = TypeAdapter(fn).json_schema()
100
-
101
- # Convert parameters to PromptArguments
102
- arguments = []
103
- if "properties" in parameters:
104
- for param_name, param in parameters["properties"].items():
105
- required = param_name in parameters.get("required", [])
106
- arguments.append(
107
- PromptArgument(
108
- name=param_name,
109
- description=param.get("description"),
110
- required=required,
111
- )
112
- )
113
-
114
- # ensure the arguments are properly cast
115
- fn = validate_call(fn)
116
-
117
- return cls(
118
- name=func_name,
119
- description=description or fn.__doc__ or "",
120
- arguments=arguments,
121
- fn=fn,
122
- )
123
-
124
- async def render(self, arguments: dict[str, Any] | None = None) -> list[Message]:
125
- """Render the prompt with arguments."""
126
- # Validate required arguments
127
- if self.arguments:
128
- required = {arg.name for arg in self.arguments if arg.required}
129
- provided = set(arguments or {})
130
- missing = required - provided
131
- if missing:
132
- raise ValueError(f"Missing required arguments: {missing}")
133
-
134
- try:
135
- # Call function and check if result is a coroutine
136
- result = self.fn(**(arguments or {}))
137
- if inspect.iscoroutine(result):
138
- result = await result
139
-
140
- # Validate messages
141
- if not isinstance(result, (list, tuple)):
142
- result = [result]
143
-
144
- # Convert result to messages
145
- messages = []
146
- for msg in result:
147
- try:
148
- if isinstance(msg, Message):
149
- messages.append(msg)
150
- elif isinstance(msg, dict):
151
- msg = message_validator.validate_python(msg)
152
- messages.append(msg)
153
- elif isinstance(msg, str):
154
- messages.append(
155
- UserMessage(content=TextContent(type="text", text=msg))
156
- )
157
- else:
158
- msg = json.dumps(pydantic_core.to_jsonable_python(msg))
159
- messages.append(Message(role="user", content=msg))
160
- except Exception:
161
- raise ValueError(
162
- f"Could not convert prompt result to message: {msg}"
163
- )
164
-
165
- return messages
166
- except Exception as e:
167
- raise ValueError(f"Error rendering prompt {self.name}: {e}")
@@ -1,50 +0,0 @@
1
- """Prompt management functionality."""
2
-
3
- from typing import Any
4
-
5
- from acp.server.highlevel.prompts.base import Message, Prompt
6
- from acp.server.highlevel.utilities.logging import get_logger
7
-
8
- logger = get_logger(__name__)
9
-
10
-
11
- class PromptManager:
12
- """Manages FastMCP prompts."""
13
-
14
- def __init__(self, warn_on_duplicate_prompts: bool = True):
15
- self._prompts: dict[str, Prompt] = {}
16
- self.warn_on_duplicate_prompts = warn_on_duplicate_prompts
17
-
18
- def get_prompt(self, name: str) -> Prompt | None:
19
- """Get prompt by name."""
20
- return self._prompts.get(name)
21
-
22
- def list_prompts(self) -> list[Prompt]:
23
- """List all registered prompts."""
24
- return list(self._prompts.values())
25
-
26
- def add_prompt(
27
- self,
28
- prompt: Prompt,
29
- ) -> Prompt:
30
- """Add a prompt to the manager."""
31
-
32
- # Check for duplicates
33
- existing = self._prompts.get(prompt.name)
34
- if existing:
35
- if self.warn_on_duplicate_prompts:
36
- logger.warning(f"Prompt already exists: {prompt.name}")
37
- return existing
38
-
39
- self._prompts[prompt.name] = prompt
40
- return prompt
41
-
42
- async def render_prompt(
43
- self, name: str, arguments: dict[str, Any] | None = None
44
- ) -> list[Message]:
45
- """Render a prompt by name with arguments."""
46
- prompt = self.get_prompt(name)
47
- if not prompt:
48
- raise ValueError(f"Unknown prompt: {name}")
49
-
50
- return await prompt.render(arguments)
@@ -1,33 +0,0 @@
1
- """Prompt management functionality."""
2
-
3
- from acp.server.highlevel.prompts.base import Prompt
4
- from acp.server.highlevel.utilities.logging import get_logger
5
-
6
- logger = get_logger(__name__)
7
-
8
-
9
- class PromptManager:
10
- """Manages FastMCP prompts."""
11
-
12
- def __init__(self, warn_on_duplicate_prompts: bool = True):
13
- self._prompts: dict[str, Prompt] = {}
14
- self.warn_on_duplicate_prompts = warn_on_duplicate_prompts
15
-
16
- def add_prompt(self, prompt: Prompt) -> Prompt:
17
- """Add a prompt to the manager."""
18
- logger.debug(f"Adding prompt: {prompt.name}")
19
- existing = self._prompts.get(prompt.name)
20
- if existing:
21
- if self.warn_on_duplicate_prompts:
22
- logger.warning(f"Prompt already exists: {prompt.name}")
23
- return existing
24
- self._prompts[prompt.name] = prompt
25
- return prompt
26
-
27
- def get_prompt(self, name: str) -> Prompt | None:
28
- """Get prompt by name."""
29
- return self._prompts.get(name)
30
-
31
- def list_prompts(self) -> list[Prompt]:
32
- """List all registered prompts."""
33
- return list(self._prompts.values())