wcgw 5.0.1__py3-none-any.whl → 5.1.0__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.

Potentially problematic release.


This version of wcgw might be problematic. Click here for more details.

Files changed (39) hide show
  1. wcgw/client/bash_state/bash_state.py +2 -2
  2. wcgw/client/diff-instructions.txt +2 -2
  3. wcgw/client/file_ops/diff_edit.py +14 -2
  4. wcgw/client/file_ops/extensions.py +137 -0
  5. wcgw/client/file_ops/search_replace.py +1 -2
  6. wcgw/client/mcp_server/server.py +10 -18
  7. wcgw/client/memory.py +4 -1
  8. wcgw/client/tool_prompts.py +16 -15
  9. wcgw/client/tools.py +99 -39
  10. {wcgw-5.0.1.dist-info → wcgw-5.1.0.dist-info}/METADATA +2 -1
  11. wcgw-5.1.0.dist-info/RECORD +37 -0
  12. wcgw_cli/anthropic_client.py +8 -4
  13. wcgw_cli/openai_client.py +7 -3
  14. mcp_wcgw/__init__.py +0 -114
  15. mcp_wcgw/client/__init__.py +0 -0
  16. mcp_wcgw/client/__main__.py +0 -79
  17. mcp_wcgw/client/session.py +0 -234
  18. mcp_wcgw/client/sse.py +0 -142
  19. mcp_wcgw/client/stdio.py +0 -128
  20. mcp_wcgw/py.typed +0 -0
  21. mcp_wcgw/server/__init__.py +0 -514
  22. mcp_wcgw/server/__main__.py +0 -50
  23. mcp_wcgw/server/models.py +0 -16
  24. mcp_wcgw/server/session.py +0 -288
  25. mcp_wcgw/server/sse.py +0 -178
  26. mcp_wcgw/server/stdio.py +0 -83
  27. mcp_wcgw/server/websocket.py +0 -61
  28. mcp_wcgw/shared/__init__.py +0 -0
  29. mcp_wcgw/shared/context.py +0 -14
  30. mcp_wcgw/shared/exceptions.py +0 -9
  31. mcp_wcgw/shared/memory.py +0 -87
  32. mcp_wcgw/shared/progress.py +0 -40
  33. mcp_wcgw/shared/session.py +0 -288
  34. mcp_wcgw/shared/version.py +0 -3
  35. mcp_wcgw/types.py +0 -1060
  36. wcgw-5.0.1.dist-info/RECORD +0 -58
  37. {wcgw-5.0.1.dist-info → wcgw-5.1.0.dist-info}/WHEEL +0 -0
  38. {wcgw-5.0.1.dist-info → wcgw-5.1.0.dist-info}/entry_points.txt +0 -0
  39. {wcgw-5.0.1.dist-info → wcgw-5.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,514 +0,0 @@
1
- """
2
- MCP Server Module
3
-
4
- This module provides a framework for creating an MCP (Model Context Protocol) server.
5
- It allows you to easily define and handle various types of requests and notifications
6
- in an asynchronous manner.
7
-
8
- Usage:
9
- 1. Create a Server instance:
10
- server = Server("your_server_name")
11
-
12
- 2. Define request handlers using decorators:
13
- @server.list_prompts()
14
- async def handle_list_prompts() -> list[types.Prompt]:
15
- # Implementation
16
-
17
- @server.get_prompt()
18
- async def handle_get_prompt(
19
- name: str, arguments: dict[str, str] | None
20
- ) -> types.GetPromptResult:
21
- # Implementation
22
-
23
- @server.list_tools()
24
- async def handle_list_tools() -> list[types.Tool]:
25
- # Implementation
26
-
27
- @server.call_tool()
28
- async def handle_call_tool(
29
- name: str, arguments: dict | None
30
- ) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
31
- # Implementation
32
-
33
- @server.list_resource_templates()
34
- async def handle_list_resource_templates() -> list[types.ResourceTemplate]:
35
- # Implementation
36
-
37
- 3. Define notification handlers if needed:
38
- @server.progress_notification()
39
- async def handle_progress(
40
- progress_token: str | int, progress: float, total: float | None
41
- ) -> None:
42
- # Implementation
43
-
44
- 4. Run the server:
45
- async def main():
46
- async with mcp_wcgwserver.stdio.stdio_server() as (read_stream, write_stream):
47
- await server.run(
48
- read_stream,
49
- write_stream,
50
- InitializationOptions(
51
- server_name="your_server_name",
52
- server_version="your_version",
53
- capabilities=server.get_capabilities(
54
- notification_options=NotificationOptions(),
55
- experimental_capabilities={},
56
- ),
57
- ),
58
- )
59
-
60
- asyncio.run(main())
61
-
62
- The Server class provides methods to register handlers for various MCP requests and
63
- notifications. It automatically manages the request context and handles incoming
64
- messages from the client.
65
- """
66
-
67
- import contextvars
68
- import logging
69
- import warnings
70
- from collections.abc import Awaitable, Callable
71
- from typing import Any, Optional, Sequence
72
-
73
- from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
74
- from pydantic import AnyUrl
75
-
76
- import mcp_wcgw.types as types
77
- from mcp_wcgw.server.models import InitializationOptions
78
- from mcp_wcgw.server.session import ServerSession
79
- from mcp_wcgw.server.stdio import stdio_server as stdio_server
80
- from mcp_wcgw.shared.context import RequestContext
81
- from mcp_wcgw.shared.exceptions import McpError
82
- from mcp_wcgw.shared.session import RequestResponder
83
-
84
- logger = logging.getLogger(__name__)
85
-
86
- request_ctx: contextvars.ContextVar[RequestContext[ServerSession]] = (
87
- contextvars.ContextVar("request_ctx")
88
- )
89
-
90
-
91
- class NotificationOptions:
92
- def __init__(
93
- self,
94
- prompts_changed: bool = False,
95
- resources_changed: bool = False,
96
- tools_changed: bool = False,
97
- ):
98
- self.prompts_changed = prompts_changed
99
- self.resources_changed = resources_changed
100
- self.tools_changed = tools_changed
101
-
102
-
103
- class Server:
104
- def __init__(self, name: str):
105
- self.name = name
106
- self.request_handlers: dict[
107
- type, Callable[..., Awaitable[types.ServerResult]]
108
- ] = {
109
- types.PingRequest: _ping_handler,
110
- }
111
- self.notification_handlers: dict[type, Callable[..., Awaitable[None]]] = {}
112
- self.notification_options = NotificationOptions()
113
- logger.debug(f"Initializing server '{name}'")
114
-
115
- def create_initialization_options(
116
- self,
117
- notification_options: NotificationOptions | None = None,
118
- experimental_capabilities: dict[str, dict[str, Any]] | None = None,
119
- ) -> InitializationOptions:
120
- """Create initialization options from this server instance."""
121
-
122
- def pkg_version(package: str) -> str:
123
- try:
124
- from importlib.metadata import version
125
-
126
- v = version(package)
127
- if v is not None:
128
- return v
129
- except Exception:
130
- pass
131
-
132
- return "unknown"
133
-
134
- return InitializationOptions(
135
- server_name=self.name,
136
- server_version=pkg_version("mcp"),
137
- capabilities=self.get_capabilities(
138
- notification_options or NotificationOptions(),
139
- experimental_capabilities or {},
140
- ),
141
- )
142
-
143
- def get_capabilities(
144
- self,
145
- notification_options: NotificationOptions,
146
- experimental_capabilities: dict[str, dict[str, Any]],
147
- ) -> types.ServerCapabilities:
148
- """Convert existing handlers to a ServerCapabilities object."""
149
- prompts_capability = None
150
- resources_capability = None
151
- tools_capability = None
152
- logging_capability = None
153
-
154
- # Set prompt capabilities if handler exists
155
- if types.ListPromptsRequest in self.request_handlers:
156
- prompts_capability = types.PromptsCapability(
157
- listChanged=notification_options.prompts_changed
158
- )
159
-
160
- # Set resource capabilities if handler exists
161
- if types.ListResourcesRequest in self.request_handlers:
162
- resources_capability = types.ResourcesCapability(
163
- subscribe=False, listChanged=notification_options.resources_changed
164
- )
165
-
166
- # Set tool capabilities if handler exists
167
- if types.ListToolsRequest in self.request_handlers:
168
- tools_capability = types.ToolsCapability(
169
- listChanged=notification_options.tools_changed
170
- )
171
-
172
- # Set logging capabilities if handler exists
173
- if types.SetLevelRequest in self.request_handlers:
174
- logging_capability = types.LoggingCapability()
175
-
176
- return types.ServerCapabilities(
177
- prompts=prompts_capability,
178
- resources=resources_capability,
179
- tools=tools_capability,
180
- logging=logging_capability,
181
- experimental=experimental_capabilities,
182
- )
183
-
184
- @property
185
- def request_context(self) -> RequestContext[ServerSession]:
186
- """If called outside of a request context, this will raise a LookupError."""
187
- return request_ctx.get()
188
-
189
- def list_prompts(self):
190
- def decorator(func: Callable[[], Awaitable[list[types.Prompt]]]):
191
- logger.debug("Registering handler for PromptListRequest")
192
-
193
- async def handler(_: Any):
194
- prompts = await func()
195
- return types.ServerResult(types.ListPromptsResult(prompts=prompts))
196
-
197
- self.request_handlers[types.ListPromptsRequest] = handler
198
- return func
199
-
200
- return decorator
201
-
202
- def get_prompt(self):
203
- def decorator(
204
- func: Callable[
205
- [str, dict[str, str] | None], Awaitable[types.GetPromptResult]
206
- ],
207
- ):
208
- logger.debug("Registering handler for GetPromptRequest")
209
-
210
- async def handler(req: types.GetPromptRequest):
211
- prompt_get = await func(req.params.name, req.params.arguments)
212
- return types.ServerResult(prompt_get)
213
-
214
- self.request_handlers[types.GetPromptRequest] = handler
215
- return func
216
-
217
- return decorator
218
-
219
- def list_resources(self):
220
- def decorator(func: Callable[[], Awaitable[list[types.Resource]]]):
221
- logger.debug("Registering handler for ListResourcesRequest")
222
-
223
- async def handler(_: Any):
224
- resources = await func()
225
- return types.ServerResult(
226
- types.ListResourcesResult(resources=resources)
227
- )
228
-
229
- self.request_handlers[types.ListResourcesRequest] = handler
230
- return func
231
-
232
- return decorator
233
-
234
- def list_resource_templates(self):
235
- def decorator(func: Callable[[], Awaitable[list[types.ResourceTemplate]]]):
236
- logger.debug("Registering handler for ListResourceTemplatesRequest")
237
-
238
- async def handler(_: Any):
239
- templates = await func()
240
- return types.ServerResult(
241
- types.ListResourceTemplatesResult(resourceTemplates=templates)
242
- )
243
-
244
- self.request_handlers[types.ListResourceTemplatesRequest] = handler
245
- return func
246
-
247
- return decorator
248
-
249
- def read_resource(self):
250
- def decorator(func: Callable[[AnyUrl], Awaitable[str | bytes]]):
251
- logger.debug("Registering handler for ReadResourceRequest")
252
-
253
- async def handler(req: types.ReadResourceRequest):
254
- result = await func(req.params.uri)
255
- match result:
256
- case str(s):
257
- content = types.TextResourceContents(
258
- uri=req.params.uri,
259
- text=s,
260
- mimeType="text/plain",
261
- )
262
- case bytes(b):
263
- import base64
264
-
265
- content = types.BlobResourceContents(
266
- uri=req.params.uri,
267
- blob=base64.urlsafe_b64encode(b).decode(),
268
- mimeType="application/octet-stream",
269
- )
270
-
271
- return types.ServerResult(
272
- types.ReadResourceResult(
273
- contents=[content],
274
- )
275
- )
276
-
277
- self.request_handlers[types.ReadResourceRequest] = handler
278
- return func
279
-
280
- return decorator
281
-
282
- def set_logging_level(self):
283
- def decorator(func: Callable[[types.LoggingLevel], Awaitable[None]]):
284
- logger.debug("Registering handler for SetLevelRequest")
285
-
286
- async def handler(req: types.SetLevelRequest):
287
- await func(req.params.level)
288
- return types.ServerResult(types.EmptyResult())
289
-
290
- self.request_handlers[types.SetLevelRequest] = handler
291
- return func
292
-
293
- return decorator
294
-
295
- def subscribe_resource(self):
296
- def decorator(func: Callable[[AnyUrl], Awaitable[None]]):
297
- logger.debug("Registering handler for SubscribeRequest")
298
-
299
- async def handler(req: types.SubscribeRequest):
300
- await func(req.params.uri)
301
- return types.ServerResult(types.EmptyResult())
302
-
303
- self.request_handlers[types.SubscribeRequest] = handler
304
- return func
305
-
306
- return decorator
307
-
308
- def unsubscribe_resource(self):
309
- def decorator(func: Callable[[AnyUrl], Awaitable[None]]):
310
- logger.debug("Registering handler for UnsubscribeRequest")
311
-
312
- async def handler(req: types.UnsubscribeRequest):
313
- await func(req.params.uri)
314
- return types.ServerResult(types.EmptyResult())
315
-
316
- self.request_handlers[types.UnsubscribeRequest] = handler
317
- return func
318
-
319
- return decorator
320
-
321
- def list_tools(self):
322
- def decorator(func: Callable[[], Awaitable[list[types.Tool]]]):
323
- logger.debug("Registering handler for ListToolsRequest")
324
-
325
- async def handler(_: Any):
326
- tools = await func()
327
- return types.ServerResult(types.ListToolsResult(tools=tools))
328
-
329
- self.request_handlers[types.ListToolsRequest] = handler
330
- return func
331
-
332
- return decorator
333
-
334
- def call_tool(self):
335
- def decorator(
336
- func: Callable[
337
- ...,
338
- Awaitable[
339
- Sequence[
340
- types.TextContent | types.ImageContent | types.EmbeddedResource
341
- ]
342
- ],
343
- ],
344
- ):
345
- logger.debug("Registering handler for CallToolRequest")
346
-
347
- async def handler(req: types.CallToolRequest):
348
- try:
349
- results = await func(req.params.name, (req.params.arguments or {}))
350
- return types.ServerResult(
351
- types.CallToolResult(content=list(results), isError=False)
352
- )
353
- except Exception as e:
354
- return types.ServerResult(
355
- types.CallToolResult(
356
- content=[types.TextContent(type="text", text=str(e))],
357
- isError=True,
358
- )
359
- )
360
-
361
- self.request_handlers[types.CallToolRequest] = handler
362
- return func
363
-
364
- return decorator
365
-
366
- def progress_notification(self):
367
- def decorator(
368
- func: Callable[[str | int, float, float | None], Awaitable[None]],
369
- ):
370
- logger.debug("Registering handler for ProgressNotification")
371
-
372
- async def handler(req: types.ProgressNotification):
373
- await func(
374
- req.params.progressToken, req.params.progress, req.params.total
375
- )
376
-
377
- self.notification_handlers[types.ProgressNotification] = handler
378
- return func
379
-
380
- return decorator
381
-
382
- def cancellation_notification(self):
383
- def decorator(
384
- func: Callable[[Optional[int], Optional[str]], Awaitable[None]],
385
- ):
386
- logger.debug("Registering handler for ProgressNotification")
387
-
388
- async def handler(req: types.CancellationNotification):
389
- await func(req.params.requestId, req.params.reason)
390
-
391
- self.notification_handlers[types.CancellationNotification] = handler
392
- return func
393
-
394
- return decorator
395
-
396
- def completion(self):
397
- """Provides completions for prompts and resource templates"""
398
-
399
- def decorator(
400
- func: Callable[
401
- [
402
- types.PromptReference | types.ResourceReference,
403
- types.CompletionArgument,
404
- ],
405
- Awaitable[types.Completion | None],
406
- ],
407
- ):
408
- logger.debug("Registering handler for CompleteRequest")
409
-
410
- async def handler(req: types.CompleteRequest):
411
- completion = await func(req.params.ref, req.params.argument)
412
- return types.ServerResult(
413
- types.CompleteResult(
414
- completion=completion
415
- if completion is not None
416
- else types.Completion(values=[], total=None, hasMore=None),
417
- )
418
- )
419
-
420
- self.request_handlers[types.CompleteRequest] = handler
421
- return func
422
-
423
- return decorator
424
-
425
- async def run(
426
- self,
427
- read_stream: MemoryObjectReceiveStream[types.JSONRPCMessage | Exception],
428
- write_stream: MemoryObjectSendStream[types.JSONRPCMessage],
429
- initialization_options: InitializationOptions,
430
- # When False, exceptions are returned as messages to the client.
431
- # When True, exceptions are raised, which will cause the server to shut down
432
- # but also make tracing exceptions much easier during testing and when using
433
- # in-process servers.
434
- raise_exceptions: bool = False,
435
- ):
436
- with warnings.catch_warnings(record=True) as w:
437
- async with ServerSession(
438
- read_stream, write_stream, initialization_options
439
- ) as session:
440
- async for message in session.incoming_messages:
441
- logger.debug(f"Received message: {message}")
442
-
443
- match message:
444
- case RequestResponder(request=types.ClientRequest(root=req)):
445
- logger.info(
446
- f"Processing request of type {type(req).__name__}"
447
- )
448
- if type(req) in self.request_handlers:
449
- handler = self.request_handlers[type(req)]
450
- logger.debug(
451
- f"Dispatching request of type {type(req).__name__}"
452
- )
453
-
454
- token = None
455
- try:
456
- # Set our global state that can be retrieved via
457
- # app.get_request_context()
458
- token = request_ctx.set(
459
- RequestContext(
460
- message.request_id,
461
- message.request_meta,
462
- session,
463
- )
464
- )
465
- response = await handler(req)
466
- except McpError as err:
467
- response = err.error
468
- except Exception as err:
469
- if raise_exceptions:
470
- raise err
471
- response = types.ErrorData(
472
- code=0, message=str(err), data=None
473
- )
474
- finally:
475
- # Reset the global state after we are done
476
- if token is not None:
477
- request_ctx.reset(token)
478
-
479
- await message.respond(response)
480
- else:
481
- await message.respond(
482
- types.ErrorData(
483
- code=types.METHOD_NOT_FOUND,
484
- message="Method not found",
485
- )
486
- )
487
-
488
- logger.debug("Response sent")
489
- case types.ClientNotification(root=notify):
490
- if type(notify) in self.notification_handlers:
491
- assert type(notify) in self.notification_handlers
492
-
493
- handler = self.notification_handlers[type(notify)]
494
- logger.debug(
495
- f"Dispatching notification of type "
496
- f"{type(notify).__name__}"
497
- )
498
-
499
- try:
500
- await handler(notify)
501
- except Exception as err:
502
- logger.error(
503
- f"Uncaught exception in notification handler: "
504
- f"{err}"
505
- )
506
-
507
- for warning in w:
508
- logger.info(
509
- f"Warning: {warning.category.__name__}: {warning.message}"
510
- )
511
-
512
-
513
- async def _ping_handler(request: types.PingRequest) -> types.ServerResult:
514
- return types.ServerResult(types.EmptyResult())
@@ -1,50 +0,0 @@
1
- import importlib.metadata
2
- import logging
3
- import sys
4
-
5
- import anyio
6
-
7
- from mcp_wcgw.server.models import InitializationOptions
8
- from mcp_wcgw.server.session import ServerSession
9
- from mcp_wcgw.server.stdio import stdio_server
10
- from mcp_wcgw.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")
mcp_wcgw/server/models.py DELETED
@@ -1,16 +0,0 @@
1
- """
2
- This module provides simpler types to use with the server for managing prompts
3
- and tools.
4
- """
5
-
6
- from pydantic import BaseModel
7
-
8
- from mcp_wcgw.types import (
9
- ServerCapabilities,
10
- )
11
-
12
-
13
- class InitializationOptions(BaseModel):
14
- server_name: str
15
- server_version: str
16
- capabilities: ServerCapabilities