@prismer/runtime 2.0.6 → 2.0.8

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 (207) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/built-in-skills/agent-coordination/SKILL.md +235 -0
  3. package/built-in-skills/agent-meta/SKILL.md +52 -0
  4. package/built-in-skills/assets/SKILL.md +131 -0
  5. package/built-in-skills/canvas-design/LICENSE.txt +202 -0
  6. package/built-in-skills/canvas-design/SKILL.md +156 -0
  7. package/built-in-skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +93 -0
  8. package/built-in-skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  9. package/built-in-skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  10. package/built-in-skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +93 -0
  11. package/built-in-skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  12. package/built-in-skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +93 -0
  13. package/built-in-skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  14. package/built-in-skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  15. package/built-in-skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
  16. package/built-in-skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  17. package/built-in-skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  18. package/built-in-skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  19. package/built-in-skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +93 -0
  20. package/built-in-skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  21. package/built-in-skills/canvas-design/canvas-fonts/DMMono-OFL.txt +93 -0
  22. package/built-in-skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  23. package/built-in-skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +94 -0
  24. package/built-in-skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  25. package/built-in-skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  26. package/built-in-skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +93 -0
  27. package/built-in-skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  28. package/built-in-skills/canvas-design/canvas-fonts/Gloock-OFL.txt +93 -0
  29. package/built-in-skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  30. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  31. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
  32. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  33. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  34. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  35. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  36. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  37. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  38. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  39. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  40. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
  41. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  42. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  43. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  44. package/built-in-skills/canvas-design/canvas-fonts/Italiana-OFL.txt +93 -0
  45. package/built-in-skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  46. package/built-in-skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  47. package/built-in-skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
  48. package/built-in-skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  49. package/built-in-skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  50. package/built-in-skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  51. package/built-in-skills/canvas-design/canvas-fonts/Jura-OFL.txt +93 -0
  52. package/built-in-skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
  53. package/built-in-skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  54. package/built-in-skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  55. package/built-in-skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  56. package/built-in-skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  57. package/built-in-skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
  58. package/built-in-skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  59. package/built-in-skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  60. package/built-in-skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +93 -0
  61. package/built-in-skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  62. package/built-in-skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
  63. package/built-in-skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  64. package/built-in-skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  65. package/built-in-skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
  66. package/built-in-skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  67. package/built-in-skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  68. package/built-in-skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +93 -0
  69. package/built-in-skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +93 -0
  70. package/built-in-skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  71. package/built-in-skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  72. package/built-in-skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +93 -0
  73. package/built-in-skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  74. package/built-in-skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +93 -0
  75. package/built-in-skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  76. package/built-in-skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  77. package/built-in-skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +93 -0
  78. package/built-in-skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  79. package/built-in-skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
  80. package/built-in-skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  81. package/built-in-skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  82. package/built-in-skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  83. package/built-in-skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  84. package/built-in-skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +93 -0
  85. package/built-in-skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  86. package/built-in-skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
  87. package/built-in-skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  88. package/built-in-skills/claim-agent-ownership/SKILL.md +254 -0
  89. package/built-in-skills/claude-api/LICENSE.txt +202 -0
  90. package/built-in-skills/claude-api/SKILL.md +324 -0
  91. package/built-in-skills/claude-api/csharp/claude-api.md +402 -0
  92. package/built-in-skills/claude-api/curl/examples.md +216 -0
  93. package/built-in-skills/claude-api/curl/managed-agents.md +336 -0
  94. package/built-in-skills/claude-api/go/claude-api.md +421 -0
  95. package/built-in-skills/claude-api/go/managed-agents/README.md +561 -0
  96. package/built-in-skills/claude-api/java/claude-api.md +432 -0
  97. package/built-in-skills/claude-api/java/managed-agents/README.md +442 -0
  98. package/built-in-skills/claude-api/php/claude-api.md +375 -0
  99. package/built-in-skills/claude-api/php/managed-agents/README.md +435 -0
  100. package/built-in-skills/claude-api/python/claude-api/README.md +420 -0
  101. package/built-in-skills/claude-api/python/claude-api/batches.md +185 -0
  102. package/built-in-skills/claude-api/python/claude-api/files-api.md +165 -0
  103. package/built-in-skills/claude-api/python/claude-api/streaming.md +162 -0
  104. package/built-in-skills/claude-api/python/claude-api/tool-use.md +590 -0
  105. package/built-in-skills/claude-api/python/managed-agents/README.md +332 -0
  106. package/built-in-skills/claude-api/ruby/claude-api.md +113 -0
  107. package/built-in-skills/claude-api/ruby/managed-agents/README.md +389 -0
  108. package/built-in-skills/claude-api/shared/agent-design.md +101 -0
  109. package/built-in-skills/claude-api/shared/error-codes.md +213 -0
  110. package/built-in-skills/claude-api/shared/live-sources.md +135 -0
  111. package/built-in-skills/claude-api/shared/managed-agents-api-reference.md +378 -0
  112. package/built-in-skills/claude-api/shared/managed-agents-client-patterns.md +209 -0
  113. package/built-in-skills/claude-api/shared/managed-agents-core.md +238 -0
  114. package/built-in-skills/claude-api/shared/managed-agents-environments.md +215 -0
  115. package/built-in-skills/claude-api/shared/managed-agents-events.md +195 -0
  116. package/built-in-skills/claude-api/shared/managed-agents-memory.md +197 -0
  117. package/built-in-skills/claude-api/shared/managed-agents-multiagent.md +99 -0
  118. package/built-in-skills/claude-api/shared/managed-agents-onboarding.md +114 -0
  119. package/built-in-skills/claude-api/shared/managed-agents-outcomes.md +106 -0
  120. package/built-in-skills/claude-api/shared/managed-agents-overview.md +68 -0
  121. package/built-in-skills/claude-api/shared/managed-agents-self-hosted-sandboxes.md +173 -0
  122. package/built-in-skills/claude-api/shared/managed-agents-tools.md +321 -0
  123. package/built-in-skills/claude-api/shared/managed-agents-webhooks.md +110 -0
  124. package/built-in-skills/claude-api/shared/model-migration.md +779 -0
  125. package/built-in-skills/claude-api/shared/models.md +121 -0
  126. package/built-in-skills/claude-api/shared/prompt-caching.md +171 -0
  127. package/built-in-skills/claude-api/shared/tool-use-concepts.md +327 -0
  128. package/built-in-skills/claude-api/typescript/claude-api/README.md +333 -0
  129. package/built-in-skills/claude-api/typescript/claude-api/batches.md +106 -0
  130. package/built-in-skills/claude-api/typescript/claude-api/files-api.md +98 -0
  131. package/built-in-skills/claude-api/typescript/claude-api/streaming.md +178 -0
  132. package/built-in-skills/claude-api/typescript/claude-api/tool-use.md +527 -0
  133. package/built-in-skills/claude-api/typescript/managed-agents/README.md +359 -0
  134. package/built-in-skills/doc-coauthoring/SKILL.md +375 -0
  135. package/built-in-skills/frontend-design/LICENSE.txt +177 -0
  136. package/built-in-skills/frontend-design/SKILL.md +42 -0
  137. package/built-in-skills/human-approval/SKILL.md +114 -0
  138. package/built-in-skills/image-generate/SKILL.md +327 -0
  139. package/built-in-skills/ingest/SKILL.md +105 -0
  140. package/built-in-skills/internal-comms/LICENSE.txt +202 -0
  141. package/built-in-skills/internal-comms/SKILL.md +32 -0
  142. package/built-in-skills/internal-comms/examples/3p-updates.md +47 -0
  143. package/built-in-skills/internal-comms/examples/company-newsletter.md +65 -0
  144. package/built-in-skills/internal-comms/examples/faq-answers.md +30 -0
  145. package/built-in-skills/internal-comms/examples/general-comms.md +16 -0
  146. package/built-in-skills/liteparse/SKILL.md +156 -0
  147. package/built-in-skills/mcp-builder/LICENSE.txt +202 -0
  148. package/built-in-skills/mcp-builder/SKILL.md +236 -0
  149. package/built-in-skills/mcp-builder/reference/evaluation.md +602 -0
  150. package/built-in-skills/mcp-builder/reference/mcp_best_practices.md +249 -0
  151. package/built-in-skills/mcp-builder/reference/node_mcp_server.md +970 -0
  152. package/built-in-skills/mcp-builder/reference/python_mcp_server.md +719 -0
  153. package/built-in-skills/mcp-builder/scripts/connections.py +151 -0
  154. package/built-in-skills/mcp-builder/scripts/evaluation.py +373 -0
  155. package/built-in-skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  156. package/built-in-skills/mcp-builder/scripts/requirements.txt +2 -0
  157. package/built-in-skills/memory/SKILL.md +106 -0
  158. package/built-in-skills/memory-curation/SKILL.md +135 -0
  159. package/built-in-skills/office-artifacts/SKILL.md +198 -0
  160. package/built-in-skills/prismer-im-collab/SKILL.md +148 -0
  161. package/built-in-skills/skill-authoring/SKILL.md +124 -0
  162. package/built-in-skills/skill-authoring/skill.json +74 -0
  163. package/built-in-skills/skill-creator/LICENSE.txt +202 -0
  164. package/built-in-skills/skill-creator/SKILL.md +485 -0
  165. package/built-in-skills/skill-creator/agents/analyzer.md +274 -0
  166. package/built-in-skills/skill-creator/agents/comparator.md +202 -0
  167. package/built-in-skills/skill-creator/agents/grader.md +223 -0
  168. package/built-in-skills/skill-creator/assets/eval_review.html +146 -0
  169. package/built-in-skills/skill-creator/eval-viewer/generate_review.py +471 -0
  170. package/built-in-skills/skill-creator/eval-viewer/viewer.html +1325 -0
  171. package/built-in-skills/skill-creator/references/schemas.md +430 -0
  172. package/built-in-skills/skill-creator/scripts/__init__.py +0 -0
  173. package/built-in-skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  174. package/built-in-skills/skill-creator/scripts/generate_report.py +326 -0
  175. package/built-in-skills/skill-creator/scripts/improve_description.py +247 -0
  176. package/built-in-skills/skill-creator/scripts/package_skill.py +136 -0
  177. package/built-in-skills/skill-creator/scripts/quick_validate.py +103 -0
  178. package/built-in-skills/skill-creator/scripts/run_eval.py +310 -0
  179. package/built-in-skills/skill-creator/scripts/run_loop.py +328 -0
  180. package/built-in-skills/skill-creator/scripts/utils.py +47 -0
  181. package/built-in-skills/slack-gif-creator/LICENSE.txt +202 -0
  182. package/built-in-skills/slack-gif-creator/SKILL.md +271 -0
  183. package/built-in-skills/slack-gif-creator/core/easing.py +234 -0
  184. package/built-in-skills/slack-gif-creator/core/frame_composer.py +176 -0
  185. package/built-in-skills/slack-gif-creator/core/gif_builder.py +269 -0
  186. package/built-in-skills/slack-gif-creator/core/validators.py +136 -0
  187. package/built-in-skills/slack-gif-creator/requirements.txt +4 -0
  188. package/built-in-skills/tasks/SKILL.md +398 -0
  189. package/built-in-skills/team/SKILL.md +76 -0
  190. package/built-in-skills/web-artifacts-builder/LICENSE.txt +202 -0
  191. package/built-in-skills/web-artifacts-builder/SKILL.md +104 -0
  192. package/built-in-skills/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
  193. package/built-in-skills/web-artifacts-builder/scripts/init-artifact.sh +334 -0
  194. package/built-in-skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  195. package/built-in-skills/webapp-testing/LICENSE.txt +202 -0
  196. package/built-in-skills/webapp-testing/SKILL.md +96 -0
  197. package/built-in-skills/webapp-testing/examples/console_logging.py +35 -0
  198. package/built-in-skills/webapp-testing/examples/element_discovery.py +40 -0
  199. package/built-in-skills/webapp-testing/examples/static_html_automation.py +33 -0
  200. package/built-in-skills/webapp-testing/scripts/with_server.py +106 -0
  201. package/dist/cli.cjs +10239 -3309
  202. package/dist/cli.js +10111 -3183
  203. package/dist/index.cjs +11275 -4344
  204. package/dist/index.d.cts +932 -43
  205. package/dist/index.d.ts +932 -43
  206. package/dist/index.js +13087 -6158
  207. package/package.json +4 -2
@@ -0,0 +1,151 @@
1
+ """Lightweight connection handling for MCP servers."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from contextlib import AsyncExitStack
5
+ from typing import Any
6
+
7
+ from mcp import ClientSession, StdioServerParameters
8
+ from mcp.client.sse import sse_client
9
+ from mcp.client.stdio import stdio_client
10
+ from mcp.client.streamable_http import streamablehttp_client
11
+
12
+
13
+ class MCPConnection(ABC):
14
+ """Base class for MCP server connections."""
15
+
16
+ def __init__(self):
17
+ self.session = None
18
+ self._stack = None
19
+
20
+ @abstractmethod
21
+ def _create_context(self):
22
+ """Create the connection context based on connection type."""
23
+
24
+ async def __aenter__(self):
25
+ """Initialize MCP server connection."""
26
+ self._stack = AsyncExitStack()
27
+ await self._stack.__aenter__()
28
+
29
+ try:
30
+ ctx = self._create_context()
31
+ result = await self._stack.enter_async_context(ctx)
32
+
33
+ if len(result) == 2:
34
+ read, write = result
35
+ elif len(result) == 3:
36
+ read, write, _ = result
37
+ else:
38
+ raise ValueError(f"Unexpected context result: {result}")
39
+
40
+ session_ctx = ClientSession(read, write)
41
+ self.session = await self._stack.enter_async_context(session_ctx)
42
+ await self.session.initialize()
43
+ return self
44
+ except BaseException:
45
+ await self._stack.__aexit__(None, None, None)
46
+ raise
47
+
48
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
49
+ """Clean up MCP server connection resources."""
50
+ if self._stack:
51
+ await self._stack.__aexit__(exc_type, exc_val, exc_tb)
52
+ self.session = None
53
+ self._stack = None
54
+
55
+ async def list_tools(self) -> list[dict[str, Any]]:
56
+ """Retrieve available tools from the MCP server."""
57
+ response = await self.session.list_tools()
58
+ return [
59
+ {
60
+ "name": tool.name,
61
+ "description": tool.description,
62
+ "input_schema": tool.inputSchema,
63
+ }
64
+ for tool in response.tools
65
+ ]
66
+
67
+ async def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> Any:
68
+ """Call a tool on the MCP server with provided arguments."""
69
+ result = await self.session.call_tool(tool_name, arguments=arguments)
70
+ return result.content
71
+
72
+
73
+ class MCPConnectionStdio(MCPConnection):
74
+ """MCP connection using standard input/output."""
75
+
76
+ def __init__(self, command: str, args: list[str] = None, env: dict[str, str] = None):
77
+ super().__init__()
78
+ self.command = command
79
+ self.args = args or []
80
+ self.env = env
81
+
82
+ def _create_context(self):
83
+ return stdio_client(
84
+ StdioServerParameters(command=self.command, args=self.args, env=self.env)
85
+ )
86
+
87
+
88
+ class MCPConnectionSSE(MCPConnection):
89
+ """MCP connection using Server-Sent Events."""
90
+
91
+ def __init__(self, url: str, headers: dict[str, str] = None):
92
+ super().__init__()
93
+ self.url = url
94
+ self.headers = headers or {}
95
+
96
+ def _create_context(self):
97
+ return sse_client(url=self.url, headers=self.headers)
98
+
99
+
100
+ class MCPConnectionHTTP(MCPConnection):
101
+ """MCP connection using Streamable HTTP."""
102
+
103
+ def __init__(self, url: str, headers: dict[str, str] = None):
104
+ super().__init__()
105
+ self.url = url
106
+ self.headers = headers or {}
107
+
108
+ def _create_context(self):
109
+ return streamablehttp_client(url=self.url, headers=self.headers)
110
+
111
+
112
+ def create_connection(
113
+ transport: str,
114
+ command: str = None,
115
+ args: list[str] = None,
116
+ env: dict[str, str] = None,
117
+ url: str = None,
118
+ headers: dict[str, str] = None,
119
+ ) -> MCPConnection:
120
+ """Factory function to create the appropriate MCP connection.
121
+
122
+ Args:
123
+ transport: Connection type ("stdio", "sse", or "http")
124
+ command: Command to run (stdio only)
125
+ args: Command arguments (stdio only)
126
+ env: Environment variables (stdio only)
127
+ url: Server URL (sse and http only)
128
+ headers: HTTP headers (sse and http only)
129
+
130
+ Returns:
131
+ MCPConnection instance
132
+ """
133
+ transport = transport.lower()
134
+
135
+ if transport == "stdio":
136
+ if not command:
137
+ raise ValueError("Command is required for stdio transport")
138
+ return MCPConnectionStdio(command=command, args=args, env=env)
139
+
140
+ elif transport == "sse":
141
+ if not url:
142
+ raise ValueError("URL is required for sse transport")
143
+ return MCPConnectionSSE(url=url, headers=headers)
144
+
145
+ elif transport in ["http", "streamable_http", "streamable-http"]:
146
+ if not url:
147
+ raise ValueError("URL is required for http transport")
148
+ return MCPConnectionHTTP(url=url, headers=headers)
149
+
150
+ else:
151
+ raise ValueError(f"Unsupported transport type: {transport}. Use 'stdio', 'sse', or 'http'")
@@ -0,0 +1,373 @@
1
+ """MCP Server Evaluation Harness
2
+
3
+ This script evaluates MCP servers by running test questions against them using Claude.
4
+ """
5
+
6
+ import argparse
7
+ import asyncio
8
+ import json
9
+ import re
10
+ import sys
11
+ import time
12
+ import traceback
13
+ import xml.etree.ElementTree as ET
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ from anthropic import Anthropic
18
+
19
+ from connections import create_connection
20
+
21
+ EVALUATION_PROMPT = """You are an AI assistant with access to tools.
22
+
23
+ When given a task, you MUST:
24
+ 1. Use the available tools to complete the task
25
+ 2. Provide summary of each step in your approach, wrapped in <summary> tags
26
+ 3. Provide feedback on the tools provided, wrapped in <feedback> tags
27
+ 4. Provide your final response, wrapped in <response> tags
28
+
29
+ Summary Requirements:
30
+ - In your <summary> tags, you must explain:
31
+ - The steps you took to complete the task
32
+ - Which tools you used, in what order, and why
33
+ - The inputs you provided to each tool
34
+ - The outputs you received from each tool
35
+ - A summary for how you arrived at the response
36
+
37
+ Feedback Requirements:
38
+ - In your <feedback> tags, provide constructive feedback on the tools:
39
+ - Comment on tool names: Are they clear and descriptive?
40
+ - Comment on input parameters: Are they well-documented? Are required vs optional parameters clear?
41
+ - Comment on descriptions: Do they accurately describe what the tool does?
42
+ - Comment on any errors encountered during tool usage: Did the tool fail to execute? Did the tool return too many tokens?
43
+ - Identify specific areas for improvement and explain WHY they would help
44
+ - Be specific and actionable in your suggestions
45
+
46
+ Response Requirements:
47
+ - Your response should be concise and directly address what was asked
48
+ - Always wrap your final response in <response> tags
49
+ - If you cannot solve the task return <response>NOT_FOUND</response>
50
+ - For numeric responses, provide just the number
51
+ - For IDs, provide just the ID
52
+ - For names or text, provide the exact text requested
53
+ - Your response should go last"""
54
+
55
+
56
+ def parse_evaluation_file(file_path: Path) -> list[dict[str, Any]]:
57
+ """Parse XML evaluation file with qa_pair elements."""
58
+ try:
59
+ tree = ET.parse(file_path)
60
+ root = tree.getroot()
61
+ evaluations = []
62
+
63
+ for qa_pair in root.findall(".//qa_pair"):
64
+ question_elem = qa_pair.find("question")
65
+ answer_elem = qa_pair.find("answer")
66
+
67
+ if question_elem is not None and answer_elem is not None:
68
+ evaluations.append({
69
+ "question": (question_elem.text or "").strip(),
70
+ "answer": (answer_elem.text or "").strip(),
71
+ })
72
+
73
+ return evaluations
74
+ except Exception as e:
75
+ print(f"Error parsing evaluation file {file_path}: {e}")
76
+ return []
77
+
78
+
79
+ def extract_xml_content(text: str, tag: str) -> str | None:
80
+ """Extract content from XML tags."""
81
+ pattern = rf"<{tag}>(.*?)</{tag}>"
82
+ matches = re.findall(pattern, text, re.DOTALL)
83
+ return matches[-1].strip() if matches else None
84
+
85
+
86
+ async def agent_loop(
87
+ client: Anthropic,
88
+ model: str,
89
+ question: str,
90
+ tools: list[dict[str, Any]],
91
+ connection: Any,
92
+ ) -> tuple[str, dict[str, Any]]:
93
+ """Run the agent loop with MCP tools."""
94
+ messages = [{"role": "user", "content": question}]
95
+
96
+ response = await asyncio.to_thread(
97
+ client.messages.create,
98
+ model=model,
99
+ max_tokens=4096,
100
+ system=EVALUATION_PROMPT,
101
+ messages=messages,
102
+ tools=tools,
103
+ )
104
+
105
+ messages.append({"role": "assistant", "content": response.content})
106
+
107
+ tool_metrics = {}
108
+
109
+ while response.stop_reason == "tool_use":
110
+ tool_use = next(block for block in response.content if block.type == "tool_use")
111
+ tool_name = tool_use.name
112
+ tool_input = tool_use.input
113
+
114
+ tool_start_ts = time.time()
115
+ try:
116
+ tool_result = await connection.call_tool(tool_name, tool_input)
117
+ tool_response = json.dumps(tool_result) if isinstance(tool_result, (dict, list)) else str(tool_result)
118
+ except Exception as e:
119
+ tool_response = f"Error executing tool {tool_name}: {str(e)}\n"
120
+ tool_response += traceback.format_exc()
121
+ tool_duration = time.time() - tool_start_ts
122
+
123
+ if tool_name not in tool_metrics:
124
+ tool_metrics[tool_name] = {"count": 0, "durations": []}
125
+ tool_metrics[tool_name]["count"] += 1
126
+ tool_metrics[tool_name]["durations"].append(tool_duration)
127
+
128
+ messages.append({
129
+ "role": "user",
130
+ "content": [{
131
+ "type": "tool_result",
132
+ "tool_use_id": tool_use.id,
133
+ "content": tool_response,
134
+ }]
135
+ })
136
+
137
+ response = await asyncio.to_thread(
138
+ client.messages.create,
139
+ model=model,
140
+ max_tokens=4096,
141
+ system=EVALUATION_PROMPT,
142
+ messages=messages,
143
+ tools=tools,
144
+ )
145
+ messages.append({"role": "assistant", "content": response.content})
146
+
147
+ response_text = next(
148
+ (block.text for block in response.content if hasattr(block, "text")),
149
+ None,
150
+ )
151
+ return response_text, tool_metrics
152
+
153
+
154
+ async def evaluate_single_task(
155
+ client: Anthropic,
156
+ model: str,
157
+ qa_pair: dict[str, Any],
158
+ tools: list[dict[str, Any]],
159
+ connection: Any,
160
+ task_index: int,
161
+ ) -> dict[str, Any]:
162
+ """Evaluate a single QA pair with the given tools."""
163
+ start_time = time.time()
164
+
165
+ print(f"Task {task_index + 1}: Running task with question: {qa_pair['question']}")
166
+ response, tool_metrics = await agent_loop(client, model, qa_pair["question"], tools, connection)
167
+
168
+ response_value = extract_xml_content(response, "response")
169
+ summary = extract_xml_content(response, "summary")
170
+ feedback = extract_xml_content(response, "feedback")
171
+
172
+ duration_seconds = time.time() - start_time
173
+
174
+ return {
175
+ "question": qa_pair["question"],
176
+ "expected": qa_pair["answer"],
177
+ "actual": response_value,
178
+ "score": int(response_value == qa_pair["answer"]) if response_value else 0,
179
+ "total_duration": duration_seconds,
180
+ "tool_calls": tool_metrics,
181
+ "num_tool_calls": sum(len(metrics["durations"]) for metrics in tool_metrics.values()),
182
+ "summary": summary,
183
+ "feedback": feedback,
184
+ }
185
+
186
+
187
+ REPORT_HEADER = """
188
+ # Evaluation Report
189
+
190
+ ## Summary
191
+
192
+ - **Accuracy**: {correct}/{total} ({accuracy:.1f}%)
193
+ - **Average Task Duration**: {average_duration_s:.2f}s
194
+ - **Average Tool Calls per Task**: {average_tool_calls:.2f}
195
+ - **Total Tool Calls**: {total_tool_calls}
196
+
197
+ ---
198
+ """
199
+
200
+ TASK_TEMPLATE = """
201
+ ### Task {task_num}
202
+
203
+ **Question**: {question}
204
+ **Ground Truth Answer**: `{expected_answer}`
205
+ **Actual Answer**: `{actual_answer}`
206
+ **Correct**: {correct_indicator}
207
+ **Duration**: {total_duration:.2f}s
208
+ **Tool Calls**: {tool_calls}
209
+
210
+ **Summary**
211
+ {summary}
212
+
213
+ **Feedback**
214
+ {feedback}
215
+
216
+ ---
217
+ """
218
+
219
+
220
+ async def run_evaluation(
221
+ eval_path: Path,
222
+ connection: Any,
223
+ model: str = "claude-3-7-sonnet-20250219",
224
+ ) -> str:
225
+ """Run evaluation with MCP server tools."""
226
+ print("🚀 Starting Evaluation")
227
+
228
+ client = Anthropic()
229
+
230
+ tools = await connection.list_tools()
231
+ print(f"📋 Loaded {len(tools)} tools from MCP server")
232
+
233
+ qa_pairs = parse_evaluation_file(eval_path)
234
+ print(f"📋 Loaded {len(qa_pairs)} evaluation tasks")
235
+
236
+ results = []
237
+ for i, qa_pair in enumerate(qa_pairs):
238
+ print(f"Processing task {i + 1}/{len(qa_pairs)}")
239
+ result = await evaluate_single_task(client, model, qa_pair, tools, connection, i)
240
+ results.append(result)
241
+
242
+ correct = sum(r["score"] for r in results)
243
+ accuracy = (correct / len(results)) * 100 if results else 0
244
+ average_duration_s = sum(r["total_duration"] for r in results) / len(results) if results else 0
245
+ average_tool_calls = sum(r["num_tool_calls"] for r in results) / len(results) if results else 0
246
+ total_tool_calls = sum(r["num_tool_calls"] for r in results)
247
+
248
+ report = REPORT_HEADER.format(
249
+ correct=correct,
250
+ total=len(results),
251
+ accuracy=accuracy,
252
+ average_duration_s=average_duration_s,
253
+ average_tool_calls=average_tool_calls,
254
+ total_tool_calls=total_tool_calls,
255
+ )
256
+
257
+ report += "".join([
258
+ TASK_TEMPLATE.format(
259
+ task_num=i + 1,
260
+ question=qa_pair["question"],
261
+ expected_answer=qa_pair["answer"],
262
+ actual_answer=result["actual"] or "N/A",
263
+ correct_indicator="✅" if result["score"] else "❌",
264
+ total_duration=result["total_duration"],
265
+ tool_calls=json.dumps(result["tool_calls"], indent=2),
266
+ summary=result["summary"] or "N/A",
267
+ feedback=result["feedback"] or "N/A",
268
+ )
269
+ for i, (qa_pair, result) in enumerate(zip(qa_pairs, results))
270
+ ])
271
+
272
+ return report
273
+
274
+
275
+ def parse_headers(header_list: list[str]) -> dict[str, str]:
276
+ """Parse header strings in format 'Key: Value' into a dictionary."""
277
+ headers = {}
278
+ if not header_list:
279
+ return headers
280
+
281
+ for header in header_list:
282
+ if ":" in header:
283
+ key, value = header.split(":", 1)
284
+ headers[key.strip()] = value.strip()
285
+ else:
286
+ print(f"Warning: Ignoring malformed header: {header}")
287
+ return headers
288
+
289
+
290
+ def parse_env_vars(env_list: list[str]) -> dict[str, str]:
291
+ """Parse environment variable strings in format 'KEY=VALUE' into a dictionary."""
292
+ env = {}
293
+ if not env_list:
294
+ return env
295
+
296
+ for env_var in env_list:
297
+ if "=" in env_var:
298
+ key, value = env_var.split("=", 1)
299
+ env[key.strip()] = value.strip()
300
+ else:
301
+ print(f"Warning: Ignoring malformed environment variable: {env_var}")
302
+ return env
303
+
304
+
305
+ async def main():
306
+ parser = argparse.ArgumentParser(
307
+ description="Evaluate MCP servers using test questions",
308
+ formatter_class=argparse.RawDescriptionHelpFormatter,
309
+ epilog="""
310
+ Examples:
311
+ # Evaluate a local stdio MCP server
312
+ python evaluation.py -t stdio -c python -a my_server.py eval.xml
313
+
314
+ # Evaluate an SSE MCP server
315
+ python evaluation.py -t sse -u https://example.com/mcp -H "Authorization: Bearer token" eval.xml
316
+
317
+ # Evaluate an HTTP MCP server with custom model
318
+ python evaluation.py -t http -u https://example.com/mcp -m claude-3-5-sonnet-20241022 eval.xml
319
+ """,
320
+ )
321
+
322
+ parser.add_argument("eval_file", type=Path, help="Path to evaluation XML file")
323
+ parser.add_argument("-t", "--transport", choices=["stdio", "sse", "http"], default="stdio", help="Transport type (default: stdio)")
324
+ parser.add_argument("-m", "--model", default="claude-3-7-sonnet-20250219", help="Claude model to use (default: claude-3-7-sonnet-20250219)")
325
+
326
+ stdio_group = parser.add_argument_group("stdio options")
327
+ stdio_group.add_argument("-c", "--command", help="Command to run MCP server (stdio only)")
328
+ stdio_group.add_argument("-a", "--args", nargs="+", help="Arguments for the command (stdio only)")
329
+ stdio_group.add_argument("-e", "--env", nargs="+", help="Environment variables in KEY=VALUE format (stdio only)")
330
+
331
+ remote_group = parser.add_argument_group("sse/http options")
332
+ remote_group.add_argument("-u", "--url", help="MCP server URL (sse/http only)")
333
+ remote_group.add_argument("-H", "--header", nargs="+", dest="headers", help="HTTP headers in 'Key: Value' format (sse/http only)")
334
+
335
+ parser.add_argument("-o", "--output", type=Path, help="Output file for evaluation report (default: stdout)")
336
+
337
+ args = parser.parse_args()
338
+
339
+ if not args.eval_file.exists():
340
+ print(f"Error: Evaluation file not found: {args.eval_file}")
341
+ sys.exit(1)
342
+
343
+ headers = parse_headers(args.headers) if args.headers else None
344
+ env_vars = parse_env_vars(args.env) if args.env else None
345
+
346
+ try:
347
+ connection = create_connection(
348
+ transport=args.transport,
349
+ command=args.command,
350
+ args=args.args,
351
+ env=env_vars,
352
+ url=args.url,
353
+ headers=headers,
354
+ )
355
+ except ValueError as e:
356
+ print(f"Error: {e}")
357
+ sys.exit(1)
358
+
359
+ print(f"🔗 Connecting to MCP server via {args.transport}...")
360
+
361
+ async with connection:
362
+ print("✅ Connected successfully")
363
+ report = await run_evaluation(args.eval_file, connection, args.model)
364
+
365
+ if args.output:
366
+ args.output.write_text(report)
367
+ print(f"\n✅ Report saved to {args.output}")
368
+ else:
369
+ print("\n" + report)
370
+
371
+
372
+ if __name__ == "__main__":
373
+ asyncio.run(main())
@@ -0,0 +1,22 @@
1
+ <evaluation>
2
+ <qa_pair>
3
+ <question>Calculate the compound interest on $10,000 invested at 5% annual interest rate, compounded monthly for 3 years. What is the final amount in dollars (rounded to 2 decimal places)?</question>
4
+ <answer>11614.72</answer>
5
+ </qa_pair>
6
+ <qa_pair>
7
+ <question>A projectile is launched at a 45-degree angle with an initial velocity of 50 m/s. Calculate the total distance (in meters) it has traveled from the launch point after 2 seconds, assuming g=9.8 m/s². Round to 2 decimal places.</question>
8
+ <answer>87.25</answer>
9
+ </qa_pair>
10
+ <qa_pair>
11
+ <question>A sphere has a volume of 500 cubic meters. Calculate its surface area in square meters. Round to 2 decimal places.</question>
12
+ <answer>304.65</answer>
13
+ </qa_pair>
14
+ <qa_pair>
15
+ <question>Calculate the population standard deviation of this dataset: [12, 15, 18, 22, 25, 30, 35]. Round to 2 decimal places.</question>
16
+ <answer>7.61</answer>
17
+ </qa_pair>
18
+ <qa_pair>
19
+ <question>Calculate the pH of a solution with a hydrogen ion concentration of 3.5 × 10^-5 M. Round to 2 decimal places.</question>
20
+ <answer>4.46</answer>
21
+ </qa_pair>
22
+ </evaluation>
@@ -0,0 +1,2 @@
1
+ anthropic>=0.39.0
2
+ mcp>=1.1.0
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: memory
3
+ description: Persist and retrieve agent memory across sessions — write durable notes, read by path, recall via semantic search, list/delete, and consolidate. Use whenever the user asks to remember/forget something, when you need to look up past decisions or context, or when episodic state matters beyond the current turn. Executes via the `cloud memory` and `cloud recall` CLI.
4
+ ---
5
+
6
+ # Memory
7
+
8
+ Use this skill for **durable episodic memory** — facts, decisions, feedback, and project context that need to survive across sessions. Memory has four canonical types: `user`, `feedback`, `project`, `reference`. The index is `MEMORY.md`; topic files live under semantic paths.
9
+
10
+ ## When to use
11
+
12
+ - The user explicitly says **"remember X"** or **"forget X"** → write or delete immediately.
13
+ - The user references a past decision, preference, or detail you don't have in current context → recall first.
14
+ - Before answering a question that depends on prior agreement (architecture, preferences, deadlines), check memory.
15
+ - After a non-obvious clarification or correction lands, write it so the next session keeps the lesson.
16
+
17
+ ## CLI Reference
18
+
19
+ ### Write
20
+
21
+ ```bash
22
+ # Single memory file with full frontmatter (path is required, content is the body)
23
+ cloud memory write \
24
+ --path "decisions/database-choice.md" \
25
+ --type project \
26
+ --description "We chose PostgreSQL over MySQL; deadline 2026-06-01." \
27
+ --content "## Decision\nPostgres 16 because pgvector + better JSON ops."
28
+
29
+ # Quick fact (no path → auto-named under inbox/)
30
+ cloud memory write --type feedback --content "User prefers terse end-of-turn summaries"
31
+
32
+ # From a journal blob — service extracts structured entries
33
+ cloud memory extract --journal "Long stream-of-consciousness session notes..."
34
+ ```
35
+
36
+ ### Read
37
+
38
+ ```bash
39
+ cloud memory read --path "decisions/database-choice.md" # full file
40
+ cloud memory read <file-id> # by id
41
+ cloud memory list # everything
42
+ cloud memory list --type feedback # by type
43
+ cloud memory list --updated-after "2026-05-01" # by recency
44
+ ```
45
+
46
+ ### Recall (semantic search)
47
+
48
+ ```bash
49
+ cloud recall "what database did we choose?" # default: hybrid
50
+ cloud recall "timeout retry" --strategy keyword # exact-match fast path
51
+ cloud recall "the thing with the auth bug" --strategy llm # LLM-assisted; slowest, best for fuzzy
52
+ cloud recall --layer memory --top-k 5 "..." # narrow to one layer
53
+ ```
54
+
55
+ ### Maintenance
56
+
57
+ ```bash
58
+ cloud memory delete <file-id> # remove a stale memory
59
+ cloud memory consolidate # trigger Dream — merge/dedupe/mark stale
60
+ cloud memory compact <conversation-id> # summarize a long conversation into memory
61
+ ```
62
+
63
+ ## Memory Types
64
+
65
+ | Type | What it is | When to write |
66
+ |---|---|---|
67
+ | `user` | Who the user is, role, preferences, expertise | When you learn role/responsibility/preference details that should shape future behavior |
68
+ | `feedback` | Approach corrections + validated approaches | After a correction ("don't do X") OR a non-obvious approval ("yes that was right") |
69
+ | `project` | Goals, deadlines, decisions, ongoing initiatives | When you learn who/what/why/by-when that isn't derivable from code |
70
+ | `reference` | Pointers to external systems (Linear, Slack, dashboards) | When the user names a tool/channel and its purpose |
71
+
72
+ ## Operating Rules
73
+
74
+ ### Write
75
+
76
+ - **Don't save secrets, credentials, personal data, or one-off debugging chatter.** Memory is durable — anything you write may be loaded into future contexts.
77
+ - Don't save **generic programming advice** that isn't tied to this project. The model already knows generic things.
78
+ - Don't save **ephemeral task state** (in-progress work, current-conversation context) — that belongs in plans/tasks, not memory.
79
+ - Don't save things derivable from the **current project state** (file paths, conventions, git history). Reading the code is authoritative.
80
+ - Don't save things already in **CLAUDE.md**.
81
+ - For `feedback` and `project` types, include a **Why** line (the reason the user gave) and a **How to apply** line so future-you can judge edge cases. Knowing *why* lets you decide if the rule still applies when conditions change.
82
+ - Convert relative dates to **absolute dates** before writing ("Thursday" → "2026-05-22") so memory stays interpretable as time passes.
83
+ - If a fact may become stale, embed the condition or date that makes it valid.
84
+
85
+ ### Read / Recall
86
+
87
+ - **Read `MEMORY.md` first** when you don't know the exact path. It's the index.
88
+ - Treat recall results as **leads, not evidence**. Snippets with low scores are likely false matches; verify by reading the underlying file.
89
+ - Don't let memory **override explicit current user instructions** — if the user says ignore memory or contradicts it, trust the current input and update or remove the stale entry.
90
+ - Before recommending action based on memory that names a specific function/file/flag, **verify it still exists** (grep / read). Memory is frozen in time.
91
+ - For *current* or *recent* state ("what changed this week"), prefer `git log` over recalling activity-log memories.
92
+
93
+ ### Delete
94
+
95
+ - When updating an outdated memory, **prefer editing** over deleting + rewriting (preserves the link graph).
96
+ - When the user says "forget X", search first, confirm the match, then delete. Don't silently fail if recall finds nothing — tell the user.
97
+
98
+ ## Output reporting
99
+
100
+ After writing memory, echo the path, type, and one-line description back to the user so they can verify what got persisted.
101
+
102
+ After recalling, list match titles + paths + scores; do **not** paste full file content unless the user asks. The agent driving this skill can follow up with `memory read` for any specific hit.
103
+
104
+ ## Backing capabilities (D22 mapping)
105
+
106
+ Replaces these v1.x built-in skills: `memory-read`, `memory-write`, `memory-recall`.