codebuddy-agent-sdk 0.1.27__py3-none-macosx_11_0_arm64.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.
Potentially problematic release.
This version of codebuddy-agent-sdk might be problematic. Click here for more details.
- codebuddy_agent_sdk/__init__.py +126 -0
- codebuddy_agent_sdk/_binary.py +150 -0
- codebuddy_agent_sdk/_errors.py +54 -0
- codebuddy_agent_sdk/_message_parser.py +122 -0
- codebuddy_agent_sdk/_version.py +3 -0
- codebuddy_agent_sdk/bin/codebuddy +0 -0
- codebuddy_agent_sdk/client.py +311 -0
- codebuddy_agent_sdk/mcp/__init__.py +35 -0
- codebuddy_agent_sdk/mcp/create_sdk_mcp_server.py +154 -0
- codebuddy_agent_sdk/mcp/sdk_control_server_transport.py +95 -0
- codebuddy_agent_sdk/mcp/types.py +300 -0
- codebuddy_agent_sdk/py.typed +0 -0
- codebuddy_agent_sdk/query.py +535 -0
- codebuddy_agent_sdk/transport/__init__.py +6 -0
- codebuddy_agent_sdk/transport/base.py +26 -0
- codebuddy_agent_sdk/transport/subprocess.py +171 -0
- codebuddy_agent_sdk/types.py +330 -0
- codebuddy_agent_sdk-0.1.27.dist-info/METADATA +89 -0
- codebuddy_agent_sdk-0.1.27.dist-info/RECORD +20 -0
- codebuddy_agent_sdk-0.1.27.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"""Type definitions for SDK MCP Server."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import inspect
|
|
6
|
+
from collections.abc import Awaitable, Callable
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Literal, TypedDict, cast
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .sdk_control_server_transport import SdkControlServerTransport
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# ============= JSON-RPC Types =============
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class JSONRPCRequest(TypedDict, total=False):
|
|
18
|
+
"""JSON-RPC 2.0 request."""
|
|
19
|
+
|
|
20
|
+
jsonrpc: Literal["2.0"]
|
|
21
|
+
id: str | int
|
|
22
|
+
method: str
|
|
23
|
+
params: dict[str, Any] | list[Any] | None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class JSONRPCError(TypedDict, total=False):
|
|
27
|
+
"""JSON-RPC 2.0 error."""
|
|
28
|
+
|
|
29
|
+
code: int
|
|
30
|
+
message: str
|
|
31
|
+
data: Any
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class JSONRPCResponse(TypedDict, total=False):
|
|
35
|
+
"""JSON-RPC 2.0 response."""
|
|
36
|
+
|
|
37
|
+
jsonrpc: Literal["2.0"]
|
|
38
|
+
id: str | int | None
|
|
39
|
+
result: Any
|
|
40
|
+
error: JSONRPCError
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class JSONRPCNotification(TypedDict, total=False):
|
|
44
|
+
"""JSON-RPC 2.0 notification."""
|
|
45
|
+
|
|
46
|
+
jsonrpc: Literal["2.0"]
|
|
47
|
+
method: str
|
|
48
|
+
params: dict[str, Any] | list[Any] | None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
JSONRPCMessage = JSONRPCRequest | JSONRPCResponse | JSONRPCNotification
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# ============= MCP Tool Types =============
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TextContent(TypedDict, total=False):
|
|
58
|
+
"""Text content in tool result."""
|
|
59
|
+
|
|
60
|
+
type: Literal["text"]
|
|
61
|
+
text: str
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ImageContent(TypedDict, total=False):
|
|
65
|
+
"""Image content in tool result."""
|
|
66
|
+
|
|
67
|
+
type: Literal["image"]
|
|
68
|
+
data: str
|
|
69
|
+
mimeType: str
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class EmbeddedResource(TypedDict, total=False):
|
|
73
|
+
"""Embedded resource content in tool result."""
|
|
74
|
+
|
|
75
|
+
type: Literal["resource"]
|
|
76
|
+
resource: dict[str, Any]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
ToolResultContent = TextContent | ImageContent | EmbeddedResource
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class CallToolResult(TypedDict, total=False):
|
|
83
|
+
"""Result from calling a tool."""
|
|
84
|
+
|
|
85
|
+
content: list[ToolResultContent]
|
|
86
|
+
isError: bool
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# Tool handler type - takes arguments dict and returns CallToolResult
|
|
90
|
+
ToolHandler = Callable[[dict[str, Any]], CallToolResult | Awaitable[CallToolResult]]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass
|
|
94
|
+
class ToolInputProperty:
|
|
95
|
+
"""Property definition for tool input schema."""
|
|
96
|
+
|
|
97
|
+
type: str
|
|
98
|
+
description: str | None = None
|
|
99
|
+
enum: list[str] | None = None
|
|
100
|
+
default: Any = None
|
|
101
|
+
minimum: float | None = None
|
|
102
|
+
maximum: float | None = None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass
|
|
106
|
+
class ToolInputSchema:
|
|
107
|
+
"""JSON Schema for tool input."""
|
|
108
|
+
|
|
109
|
+
type: Literal["object"] = "object"
|
|
110
|
+
properties: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
111
|
+
required: list[str] = field(default_factory=list)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass
|
|
115
|
+
class SdkMcpToolDefinition:
|
|
116
|
+
"""
|
|
117
|
+
Tool definition for SDK MCP Server.
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
```python
|
|
121
|
+
tool_def = SdkMcpToolDefinition(
|
|
122
|
+
name="get_weather",
|
|
123
|
+
description="Get the current weather for a location",
|
|
124
|
+
input_schema=ToolInputSchema(
|
|
125
|
+
properties={
|
|
126
|
+
"location": {"type": "string", "description": "The city name"},
|
|
127
|
+
"units": {"type": "string", "enum": ["celsius", "fahrenheit"]},
|
|
128
|
+
},
|
|
129
|
+
required=["location"],
|
|
130
|
+
),
|
|
131
|
+
handler=get_weather_handler,
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
name: str
|
|
137
|
+
description: str
|
|
138
|
+
input_schema: ToolInputSchema
|
|
139
|
+
handler: ToolHandler
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@dataclass
|
|
143
|
+
class SdkMcpServerOptions:
|
|
144
|
+
"""
|
|
145
|
+
Options for creating an SDK MCP Server.
|
|
146
|
+
|
|
147
|
+
Attributes:
|
|
148
|
+
name: Server name (must be unique within the session)
|
|
149
|
+
version: Server version (defaults to "1.0.0")
|
|
150
|
+
tools: List of tool definitions to register
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
name: str
|
|
154
|
+
version: str = "1.0.0"
|
|
155
|
+
tools: list[SdkMcpToolDefinition] = field(default_factory=list)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@dataclass
|
|
159
|
+
class SdkMcpServerResult:
|
|
160
|
+
"""
|
|
161
|
+
Result type for create_sdk_mcp_server.
|
|
162
|
+
|
|
163
|
+
Attributes:
|
|
164
|
+
type: Type discriminator - always "sdk" for SDK MCP servers
|
|
165
|
+
name: Server name
|
|
166
|
+
server: The MCP server instance
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
type: Literal["sdk"]
|
|
170
|
+
name: str
|
|
171
|
+
server: SdkMcpServer
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class SdkMcpServer:
|
|
175
|
+
"""
|
|
176
|
+
SDK MCP Server implementation.
|
|
177
|
+
|
|
178
|
+
This class implements an MCP server that runs within the SDK process
|
|
179
|
+
and communicates with the CLI via the control protocol.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
def __init__(self, options: SdkMcpServerOptions):
|
|
183
|
+
self.name = options.name
|
|
184
|
+
self.version = options.version
|
|
185
|
+
self.tools: dict[str, SdkMcpToolDefinition] = {}
|
|
186
|
+
self._transport: SdkControlServerTransport | None = None
|
|
187
|
+
|
|
188
|
+
# Register tools
|
|
189
|
+
for tool_def in options.tools:
|
|
190
|
+
self.tools[tool_def.name] = tool_def
|
|
191
|
+
|
|
192
|
+
def connect(self, transport: SdkControlServerTransport) -> None:
|
|
193
|
+
"""Connect the server to a transport."""
|
|
194
|
+
self._transport = transport
|
|
195
|
+
|
|
196
|
+
async def handle_message(self, message: JSONRPCMessage) -> JSONRPCMessage | None:
|
|
197
|
+
"""Handle an incoming JSON-RPC message."""
|
|
198
|
+
# Check if it's a request (has method and id)
|
|
199
|
+
if "method" not in message:
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
method = cast(str, message.get("method", ""))
|
|
203
|
+
msg_id = cast("str | int | None", message.get("id"))
|
|
204
|
+
params = cast("dict[str, Any] | None", message.get("params", {}))
|
|
205
|
+
|
|
206
|
+
if method == "initialize":
|
|
207
|
+
return await self._handle_initialize(msg_id, params)
|
|
208
|
+
elif method == "tools/list":
|
|
209
|
+
return await self._handle_tools_list(msg_id)
|
|
210
|
+
elif method == "tools/call":
|
|
211
|
+
return await self._handle_tools_call(msg_id, params)
|
|
212
|
+
elif method == "notifications/initialized":
|
|
213
|
+
# Notification, no response needed
|
|
214
|
+
return None
|
|
215
|
+
else:
|
|
216
|
+
# Unknown method
|
|
217
|
+
if msg_id is not None:
|
|
218
|
+
return self._create_error_response(msg_id, -32601, f"Method not found: {method}")
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
async def _handle_initialize(
|
|
222
|
+
self, msg_id: str | int | None, params: dict[str, Any] | None
|
|
223
|
+
) -> JSONRPCMessage:
|
|
224
|
+
"""Handle initialize request."""
|
|
225
|
+
result = {
|
|
226
|
+
"protocolVersion": "2024-11-05",
|
|
227
|
+
"capabilities": {
|
|
228
|
+
"tools": {},
|
|
229
|
+
},
|
|
230
|
+
"serverInfo": {
|
|
231
|
+
"name": self.name,
|
|
232
|
+
"version": self.version,
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
return self._create_response(msg_id, result)
|
|
236
|
+
|
|
237
|
+
async def _handle_tools_list(self, msg_id: str | int | None) -> JSONRPCMessage:
|
|
238
|
+
"""Handle tools/list request."""
|
|
239
|
+
tools_list = []
|
|
240
|
+
for tool_def in self.tools.values():
|
|
241
|
+
tools_list.append(
|
|
242
|
+
{
|
|
243
|
+
"name": tool_def.name,
|
|
244
|
+
"description": tool_def.description,
|
|
245
|
+
"inputSchema": {
|
|
246
|
+
"type": tool_def.input_schema.type,
|
|
247
|
+
"properties": tool_def.input_schema.properties,
|
|
248
|
+
"required": tool_def.input_schema.required,
|
|
249
|
+
},
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
return self._create_response(msg_id, {"tools": tools_list})
|
|
253
|
+
|
|
254
|
+
async def _handle_tools_call(
|
|
255
|
+
self, msg_id: str | int | None, params: dict[str, Any] | None
|
|
256
|
+
) -> JSONRPCMessage:
|
|
257
|
+
"""Handle tools/call request."""
|
|
258
|
+
if not isinstance(params, dict):
|
|
259
|
+
return self._create_error_response(msg_id, -32602, "Invalid params")
|
|
260
|
+
|
|
261
|
+
tool_name = params.get("name", "")
|
|
262
|
+
arguments = params.get("arguments", {})
|
|
263
|
+
|
|
264
|
+
tool_def = self.tools.get(tool_name)
|
|
265
|
+
if not tool_def:
|
|
266
|
+
return self._create_error_response(msg_id, -32602, f"Tool not found: {tool_name}")
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
# Call the handler
|
|
270
|
+
result = tool_def.handler(arguments)
|
|
271
|
+
# Handle async handlers
|
|
272
|
+
if inspect.isawaitable(result):
|
|
273
|
+
result = await result
|
|
274
|
+
|
|
275
|
+
return self._create_response(msg_id, result)
|
|
276
|
+
except Exception as e:
|
|
277
|
+
# Return error as tool result
|
|
278
|
+
error_result: CallToolResult = {
|
|
279
|
+
"content": [{"type": "text", "text": str(e)}],
|
|
280
|
+
"isError": True,
|
|
281
|
+
}
|
|
282
|
+
return self._create_response(msg_id, error_result)
|
|
283
|
+
|
|
284
|
+
def _create_response(self, msg_id: str | int | None, result: Any) -> JSONRPCResponse:
|
|
285
|
+
"""Create a JSON-RPC response."""
|
|
286
|
+
return {
|
|
287
|
+
"jsonrpc": "2.0",
|
|
288
|
+
"id": msg_id,
|
|
289
|
+
"result": result,
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
def _create_error_response(
|
|
293
|
+
self, msg_id: str | int | None, code: int, message: str
|
|
294
|
+
) -> JSONRPCResponse:
|
|
295
|
+
"""Create a JSON-RPC error response."""
|
|
296
|
+
return {
|
|
297
|
+
"jsonrpc": "2.0",
|
|
298
|
+
"id": msg_id,
|
|
299
|
+
"error": {"code": code, "message": message},
|
|
300
|
+
}
|
|
File without changes
|