@smartbear/mcp 0.19.2 → 0.21.1

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 (63) hide show
  1. package/README.md +37 -7
  2. package/assets/icon.png +0 -0
  3. package/dist/bugsnag/client.js +19 -12
  4. package/dist/collaborator/client.js +10 -10
  5. package/dist/common/client-registry.js +2 -2
  6. package/dist/common/register-clients.js +2 -0
  7. package/dist/common/server.js +74 -111
  8. package/dist/common/shutdown.js +165 -0
  9. package/dist/common/transport-http.js +94 -12
  10. package/dist/common/transport-stdio.js +16 -2
  11. package/dist/common/zod-utils.js +62 -7
  12. package/dist/index.js +2 -0
  13. package/dist/package.json.js +1 -1
  14. package/dist/pactflow/client/prompts.js +19 -18
  15. package/dist/pactflow/client/tools.js +8 -13
  16. package/dist/pactflow/client.js +26 -12
  17. package/dist/qmetry/client/tools/testsuite-tools.js +2 -2
  18. package/dist/qmetry/client.js +1 -1
  19. package/dist/qtm4j/client.js +109 -0
  20. package/dist/qtm4j/config/constants.js +169 -0
  21. package/dist/qtm4j/config/field-resolution.types.js +34 -0
  22. package/dist/qtm4j/http/api-client.js +123 -0
  23. package/dist/qtm4j/http/auth-service.js +23 -0
  24. package/dist/qtm4j/resolver/cache/cache.js +52 -0
  25. package/dist/qtm4j/resolver/resolver-registry.js +70 -0
  26. package/dist/qtm4j/resolver/resolvers/common-attribute-resolver.js +56 -0
  27. package/dist/qtm4j/resolver/resolvers/component-resolver.js +56 -0
  28. package/dist/qtm4j/resolver/resolvers/label-resolver.js +56 -0
  29. package/dist/qtm4j/resolver/resolvers/resolver.js +6 -0
  30. package/dist/qtm4j/resolver/resolvers/test-case-uid-resolver.js +28 -0
  31. package/dist/qtm4j/schema/get-test-case.schema.js +153 -0
  32. package/dist/qtm4j/schema/get-test-steps.schema.js +74 -0
  33. package/dist/qtm4j/schema/project.schema.js +43 -0
  34. package/dist/qtm4j/schema/test-case.schema.js +41 -0
  35. package/dist/qtm4j/schema/update-test-case.schema.js +45 -0
  36. package/dist/qtm4j/tool/project/get-projects.js +111 -0
  37. package/dist/qtm4j/tool/project/set-project-context.js +99 -0
  38. package/dist/qtm4j/tool/test-case/create-test-case.js +113 -0
  39. package/dist/qtm4j/tool/test-case/get-test-cases.js +295 -0
  40. package/dist/qtm4j/tool/test-case/get-test-steps.js +111 -0
  41. package/dist/qtm4j/tool/test-case/update-test-case.js +158 -0
  42. package/dist/reflect/client.js +3 -3
  43. package/dist/reflect/prompt/sap-test.js +5 -5
  44. package/dist/reflect/tool/recording/add-prompt-step.js +6 -14
  45. package/dist/reflect/tool/recording/add-segment.js +4 -14
  46. package/dist/reflect/tool/recording/connect-to-session.js +3 -8
  47. package/dist/reflect/tool/recording/delete-previous-step.js +3 -8
  48. package/dist/reflect/tool/recording/get-screenshot.js +4 -14
  49. package/dist/reflect/tool/suites/cancel-suite-execution.js +4 -14
  50. package/dist/reflect/tool/suites/execute-suite.js +3 -8
  51. package/dist/reflect/tool/suites/get-suite-execution-status.js +4 -14
  52. package/dist/reflect/tool/suites/list-suite-executions.js +3 -8
  53. package/dist/reflect/tool/suites/list-suites.js +2 -1
  54. package/dist/reflect/tool/tests/get-test-status.js +3 -8
  55. package/dist/reflect/tool/tests/list-segments.js +5 -20
  56. package/dist/reflect/tool/tests/list-tests.js +2 -1
  57. package/dist/reflect/tool/tests/run-test.js +3 -8
  58. package/dist/swagger/client/api.js +11 -2
  59. package/dist/swagger/client/portal-types.js +0 -3
  60. package/dist/swagger/client/tools.js +0 -1
  61. package/dist/swagger/client.js +1 -1
  62. package/dist/zephyr/client.js +1 -1
  63. package/package.json +6 -4
package/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
  </div>
19
19
  <br />
20
20
 
21
- A Model Context Protocol (MCP) server which provides AI assistants with seamless access to SmartBear's suite of testing and monitoring tools, including [BugSnag](https://www.bugsnag.com/), [Reflect](https://reflect.run), [Swagger](https://www.smartbear.com/api-hub), [PactFlow](https://pactflow.io/), [Pact Broker](https://docs.pact.io/), [QMetry](https://www.qmetry.com/), [Zephyr](https://smartbear.com/test-management/zephyr/) and [Collaborator](https://smartbear.com/product/collaborator/).
21
+ A Model Context Protocol (MCP) server which provides AI assistants with seamless access to SmartBear's suite of testing and monitoring tools, including [BugSnag](https://www.bugsnag.com/), [Reflect](https://reflect.run), [Swagger](https://www.smartbear.com/api-hub), [PactFlow](https://pactflow.io/), [Pact Broker](https://docs.pact.io/), [QMetry](https://www.qmetry.com/), [QTM4J](https://www.qmetry.com/qmetry-test-management-for-jira), [Zephyr](https://smartbear.com/test-management/zephyr/) and [Collaborator](https://smartbear.com/product/collaborator/).
22
22
 
23
23
  ## What is MCP?
24
24
 
@@ -37,17 +37,31 @@ See individual guides for suggested prompts and supported tools and resources:
37
37
  - [QMetry](https://developer.smartbear.com/smartbear-mcp/docs/qmetry-integration) - QMetry Test Management capabilities
38
38
  - [Zephyr](https://developer.smartbear.com/smartbear-mcp/docs/zephyr-integration) - Zephyr Test Management capabilities
39
39
  - [Collaborator](https://developer.smartbear.com/smartbear-mcp/docs/collaborator-integration) - Review and Remote System Configuration management capabilities
40
+ - [QTM4J](https://developer.smartbear.com/smartbear-mcp/docs/qtm4j-integration) - QTM4J Test Management for Jira capabilities
40
41
 
42
+ ## Remote MCP Servers
43
+
44
+ For BugSnag, Swagger, and Zephyr, SmartBear hosts Remote MCP Servers that you can connect to directly from your MCP client via a URL — no installation, Node.js, or API tokens required. Authentication is handled through an OAuth browser flow.
45
+
46
+ | Product | Server URL |
47
+ |---|---|
48
+ | **Swagger** | `https://swagger.mcp.smartbear.com/mcp` |
49
+ | **BugSnag** | `https://bugsnag.mcp.smartbear.com/mcp` |
50
+ | **Zephyr** | `https://zephyr.mcp.smartbear.com/mcp` |
51
+
52
+ See the [Remote MCP Servers guide](https://developer.smartbear.com/smartbear-mcp/docs/remote-mcp-servers) for per-client setup instructions. You can connect to multiple remote servers at the same time.
53
+
54
+ > **Need Reflect, QMetry, QTM4J, PactFlow, or Collaborator?** These products are only available via the local npm package below, which bundles all products into a single MCP server.
41
55
 
42
56
  ## Prerequisites
43
57
 
44
58
  - Node.js 20+ and npm
45
- - Access to SmartBear products (BugSnag, Reflect, Swagger, QMetry, or Zephyr)
59
+ - Access to SmartBear products (BugSnag, Reflect, Swagger, QMetry, QTM4J or Zephyr)
46
60
  - Valid API tokens for the products you want to integrate
47
61
 
48
- ## Installation
62
+ ## Local MCP Server Installation (npm)
49
63
 
50
- The MCP server is distributed as an npm package [`@smartbear/mcp`](https://www.npmjs.com/package/@smartbear/mcp), making it easy to integrate into your development workflow.
64
+ For all products — or if you prefer running the server locally — the MCP server is distributed as an npm package [`@smartbear/mcp`](https://www.npmjs.com/package/@smartbear/mcp), making it easy to integrate into your development workflow.
51
65
 
52
66
  The server is started with the API key or auth token that you use with your SmartBear product(s). They are optional and can be removed from your configuration if you aren't using the product. For BugSnag, if you provide a project API key it will narrow down all searches to a single project in your BugSnag dashboard. Leave this field blank if you wish to interact across multiple projects at a time.
53
67
 
@@ -88,7 +102,9 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
88
102
  "ZEPHYR_BASE_URL": "${input:zephyr_base_url}",
89
103
  "COLLABORATOR_BASE_URL": "${input:collab_base_url}",
90
104
  "COLLABORATOR_USERNAME": "${input:collab_username}",
91
- "COLLABORATOR_LOGIN_TICKET": "${input:collab_login_ticket}"
105
+ "COLLABORATOR_LOGIN_TICKET": "${input:collab_login_ticket}",
106
+ "QTM4J_API_KEY": "${input:qtm4j_api_key}",
107
+ "QTM4J_BASE_URL": "${input:qtm4j_base_url}"
92
108
  }
93
109
  }
94
110
  },
@@ -200,7 +216,19 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
200
216
  "type": "promptString",
201
217
  "description": "Collab login ticket",
202
218
  "password": true
203
- }
219
+ },
220
+ {
221
+ "id": "qtm4j_api_key",
222
+ "type": "promptString",
223
+ "description": "QTM4J API Key",
224
+ "password": true
225
+ },
226
+ {
227
+ "id": "qtm4j_base_url",
228
+ "type": "promptString",
229
+ "description": "US region (default): https://qtmcloud.qmetry.com. Australia region: https://syd-qtmcloud.qmetry.com.",
230
+ "password": false
231
+ }
204
232
  ]
205
233
  }
206
234
  ```
@@ -237,7 +265,9 @@ Add the following configuration to your `claude_desktop_config.json` to launch t
237
265
  "ZEPHYR_BASE_URL": "https://api.zephyrscale.smartbear.com/v2",
238
266
  "COLLABORATOR_BASE_URL": "your collab base url",
239
267
  "COLLABORATOR_USERNAME": "your collab user name",
240
- "COLLABORATOR_LOGIN_TICKET": "your collab login ticket"
268
+ "COLLABORATOR_LOGIN_TICKET": "your collab login ticket",
269
+ "QTM4J_API_KEY": "your_qtm4j_key",
270
+ "QTM4J_BASE_URL": "https://qtmcloud.qmetry.com"
241
271
  }
242
272
  }
243
273
  }
Binary file
@@ -70,7 +70,7 @@ class BugsnagClient {
70
70
  return this._appEndpoint;
71
71
  }
72
72
  name = "BugSnag";
73
- toolPrefix = "bugsnag";
73
+ capabilityPrefix = "bugsnag";
74
74
  configPrefix = "Bugsnag";
75
75
  config = ConfigurationSchema;
76
76
  async configure(server, config) {
@@ -329,17 +329,24 @@ class BugsnagClient {
329
329
  register(tool.specification, tool.handle);
330
330
  }
331
331
  }
332
- registerResources(register) {
333
- register("event", "{id}", async (uri, variables, _extra) => {
334
- return {
335
- contents: [
336
- {
337
- uri: uri.href,
338
- text: JSON.stringify(await this.getEvent(variables.id))
339
- }
340
- ]
341
- };
342
- });
332
+ async registerResources(register) {
333
+ register(
334
+ {
335
+ title: "Event",
336
+ path: "{id}",
337
+ description: "Retrieve a specific event by its ID."
338
+ },
339
+ async (uri, variables, _extra) => {
340
+ return {
341
+ contents: [
342
+ {
343
+ uri: uri.href,
344
+ text: JSON.stringify(await this.getEvent(variables.id))
345
+ }
346
+ ]
347
+ };
348
+ }
349
+ );
343
350
  }
344
351
  }
345
352
  export {
@@ -7,7 +7,7 @@ const ConfigurationSchema = z.object({
7
7
  });
8
8
  class CollaboratorClient {
9
9
  name = "Collaborator";
10
- toolPrefix = "collaborator";
10
+ capabilityPrefix = "collaborator";
11
11
  configPrefix = "Collaborator";
12
12
  config = ConfigurationSchema;
13
13
  baseUrl;
@@ -63,7 +63,7 @@ class CollaboratorClient {
63
63
  async registerTools(register, _getInput) {
64
64
  register(
65
65
  {
66
- title: "Find Collaborator Review By ID",
66
+ title: "Find Review By ID",
67
67
  summary: "Finds a review in Collaborator by its review ID.",
68
68
  inputSchema: z.object({
69
69
  reviewId: z.string().describe("The Collaborator review ID to find.")
@@ -85,7 +85,7 @@ class CollaboratorClient {
85
85
  );
86
86
  register(
87
87
  {
88
- title: "Create Collaborator Review",
88
+ title: "Create Review",
89
89
  summary: "Creates a new review in Collaborator. All parameters are optional.",
90
90
  inputSchema: z.object({
91
91
  creator: z.string().optional().describe(
@@ -122,7 +122,7 @@ class CollaboratorClient {
122
122
  );
123
123
  register(
124
124
  {
125
- title: "Reject Collaborator Review",
125
+ title: "Reject Review",
126
126
  summary: "Rejects a review in Collaborator by its review ID and reason.",
127
127
  inputSchema: z.object({
128
128
  reviewId: z.union([z.string(), z.number()]).describe("The Collaborator review ID to reject."),
@@ -171,7 +171,7 @@ class CollaboratorClient {
171
171
  );
172
172
  register(
173
173
  {
174
- title: "Get Collaborator Reviews",
174
+ title: "Get Reviews",
175
175
  summary: "Retrieves reviews from Collaborator using ReviewService.getReviews. All parameters are optional and only provided ones are sent.",
176
176
  inputSchema: z.object({
177
177
  login: z.string().optional().describe("Collaborator username to filter reviews."),
@@ -207,7 +207,7 @@ class CollaboratorClient {
207
207
  );
208
208
  register(
209
209
  {
210
- title: "Create Collaborator Remote System Configuration",
210
+ title: "Create Remote System Configuration",
211
211
  summary: "Creates a remote system configuration in Collaborator (e.g., Bitbucket, GitHub, etc).",
212
212
  inputSchema: z.object({
213
213
  token: z.string().describe("Remote system token, e.g., BITBUCKET, GITHUB, etc."),
@@ -238,7 +238,7 @@ class CollaboratorClient {
238
238
  );
239
239
  register(
240
240
  {
241
- title: "Edit Collaborator Remote System Configuration",
241
+ title: "Edit Remote System Configuration",
242
242
  summary: "Edits parameters of an existing remote system configuration in Collaborator. Only title and config are editable after creation.",
243
243
  inputSchema: z.object({
244
244
  id: z.string().describe("ID of the remote system Configuration to edit."),
@@ -271,7 +271,7 @@ class CollaboratorClient {
271
271
  );
272
272
  register(
273
273
  {
274
- title: "Delete Collaborator Remote System Configuration",
274
+ title: "Delete Remote System Configuration",
275
275
  summary: "Deletes a remote system configuration in Collaborator by its ID.",
276
276
  inputSchema: z.object({
277
277
  id: z.union([z.string(), z.number()]).describe("ID of the remote system Configuration to delete.")
@@ -295,7 +295,7 @@ class CollaboratorClient {
295
295
  );
296
296
  register(
297
297
  {
298
- title: "Update Collaborator Remote System Configuration Webhook",
298
+ title: "Update Remote System Configuration Webhook",
299
299
  summary: "Updates the webhook for a remote system configuration in Collaborator by its ID.",
300
300
  inputSchema: z.object({
301
301
  id: z.union([z.string(), z.number()]).describe(
@@ -321,7 +321,7 @@ class CollaboratorClient {
321
321
  );
322
322
  register(
323
323
  {
324
- title: "Test Collaborator Remote System Configuration Connection",
324
+ title: "Test Remote System Configuration Connection",
325
325
  summary: "Tests the connection for a remote system configuration in Collaborator by its ID.",
326
326
  inputSchema: z.object({
327
327
  id: z.union([z.string(), z.number()]).describe(
@@ -1,5 +1,5 @@
1
1
  import { ZodURL } from "zod";
2
- import { unwrapZodType, isOptionalType } from "./zod-utils.js";
2
+ import { fullyUnwrapZodType, isOptionalType } from "./zod-utils.js";
3
3
  class ClientRegistry {
4
4
  entries = [];
5
5
  enabledClients = null;
@@ -35,7 +35,7 @@ class ClientRegistry {
35
35
  * @param value The actual config value to validate
36
36
  */
37
37
  validateAllowedEndpoint(zodType, value) {
38
- if (unwrapZodType(zodType) instanceof ZodURL) {
38
+ if (fullyUnwrapZodType(zodType) instanceof ZodURL) {
39
39
  const allowedEndpoints = process.env.MCP_ALLOWED_ENDPOINTS?.split(",");
40
40
  if (allowedEndpoints) {
41
41
  for (const endpoint of allowedEndpoints) {
@@ -2,6 +2,7 @@ import { BugsnagClient } from "../bugsnag/client.js";
2
2
  import { CollaboratorClient } from "../collaborator/client.js";
3
3
  import { PactflowClient } from "../pactflow/client.js";
4
4
  import { QmetryClient } from "../qmetry/client.js";
5
+ import { Qtm4jClient } from "../qtm4j/client.js";
5
6
  import { ReflectClient } from "../reflect/client.js";
6
7
  import { SwaggerClient } from "../swagger/client.js";
7
8
  import { ZephyrClient } from "../zephyr/client.js";
@@ -12,4 +13,5 @@ clientRegistry.register(new SwaggerClient());
12
13
  clientRegistry.register(new PactflowClient());
13
14
  clientRegistry.register(new QmetryClient());
14
15
  clientRegistry.register(new ZephyrClient());
16
+ clientRegistry.register(new Qtm4jClient());
15
17
  clientRegistry.register(new CollaboratorClient());
@@ -1,11 +1,11 @@
1
1
  import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { ZodObject, ZodIntersection, ZodOptional, ZodDefault, ZodRecord, ZodString, ZodNumber, ZodBoolean, ZodArray, ZodEnum, ZodLiteral, ZodUnion, ZodAny } from "zod";
2
+ import { ZodObject, ZodIntersection } from "zod";
3
3
  import Bugsnag from "./bugsnag.js";
4
4
  import { CacheService } from "./cache.js";
5
5
  import { MCP_SERVER_VERSION, MCP_SERVER_NAME } from "./info.js";
6
6
  import { executeElicitationOrPolyfill, isElicitationPolyfillResult } from "./pollyfills.js";
7
7
  import { ToolError } from "./tools.js";
8
- import { unwrapZodType } from "./zod-utils.js";
8
+ import { getTypeDescription, getDefaultValue, getReadableTypeName, isOptionalType } from "./zod-utils.js";
9
9
  class SmartBearMcpServer extends McpServer {
10
10
  cache;
11
11
  samplingSupported = false;
@@ -18,24 +18,15 @@ class SmartBearMcpServer extends McpServer {
18
18
  version: MCP_SERVER_VERSION
19
19
  },
20
20
  {
21
- instructions: `When creating or editing a Reflect test using a connected recording session, follow these guidelines:
22
-
23
- 1. After connecting to a session, get the list of segments for the session's platform type so you know what actions could be added via segments vs needing to create new steps. Do not list tests, only list segments.
24
- 2. Before performing an action, take a screenshot to understand the current state of the application.
25
- 3. Each add_prompt_step request should perform a single action or assertion. Do not combine multiple actions or assertions into a single step.
26
- 4. Only perform one action at a time unless you're sure the action won't move the application to a different screen. For example, you can send multiple add_prompt_step requests to fill out individual form fields if those fields are visible on the current screen.
27
- 5. Check the list of existing Segments to see if a Segment exists that achieves a similar goal to what you're trying to do next. If so, add the segment instead of creating new steps.
28
- 6. If a step fails, use delete_previous_step to remove it and try a different approach.
29
- 7. After completing a task, if the task required multiple prompt steps, add a final prompt step that validates the current state of the page based on what you see on the screen. In your validation, do not reference information that can change from run to run.`,
30
21
  capabilities: {
31
- resources: { listChanged: true },
32
- // Server supports dynamic resource lists
33
22
  tools: { listChanged: true },
34
23
  // Server supports dynamic tool lists
35
- logging: {},
36
- // Server supports logging messages
37
- prompts: {}
24
+ resources: { listChanged: true },
25
+ // Server supports dynamic resource lists
26
+ prompts: { listChanged: true },
38
27
  // Server supports sending prompts to Host
28
+ logging: {}
29
+ // Server supports logging messages
39
30
  }
40
31
  }
41
32
  );
@@ -68,15 +59,20 @@ class SmartBearMcpServer extends McpServer {
68
59
  this.clients.push(client);
69
60
  await client.registerTools(
70
61
  (params, cb) => {
71
- const toolName = `${client.toolPrefix}_${params.title.replace(/\s+/g, "_").toLowerCase()}`;
72
- const toolTitle = `${client.name}: ${params.title}`;
62
+ const toolName = this.getCapabilityName(client, params.title);
63
+ const toolTitle = this.getCapabilityTitle(client, params.title);
64
+ if (toolName.length > 64) {
65
+ throw new ToolError(
66
+ `The tool name "${toolName}" is too long. Tool names must be 64 characters or fewer for client compatibility. https://github.com/anthropics/claude-code/issues/34960`
67
+ );
68
+ }
73
69
  return super.registerTool(
74
70
  toolName,
75
71
  {
76
72
  title: toolTitle,
77
73
  description: this.getDescription(params),
78
- inputSchema: this.getInputSchema(params),
79
- outputSchema: this.getOutputSchema(params),
74
+ inputSchema: params.inputSchema ? this.schemaToRawShape(params.inputSchema) : {},
75
+ outputSchema: this.schemaToRawShape(params.outputSchema),
80
76
  annotations: this.getAnnotations(toolTitle, params)
81
77
  },
82
78
  async (args, extra) => {
@@ -104,13 +100,10 @@ class SmartBearMcpServer extends McpServer {
104
100
  ]
105
101
  };
106
102
  } else {
107
- Bugsnag.notify(
108
- e,
109
- (event) => {
110
- event.addMetadata("app", { tool: toolName });
111
- event.unhandled = true;
112
- }
113
- );
103
+ Bugsnag.notify(e, (event) => {
104
+ event.addMetadata("app", { tool: toolName });
105
+ event.unhandled = true;
106
+ });
114
107
  }
115
108
  throw e;
116
109
  }
@@ -137,25 +130,30 @@ ${result.instructions}`
137
130
  }
138
131
  );
139
132
  if (client.registerResources) {
140
- client.registerResources((name, path, cb) => {
141
- const url = `${client.toolPrefix}://${name}/${path}`;
133
+ await client.registerResources((params, cb) => {
134
+ const resourceName = this.getCapabilityName(client, params.title);
135
+ const slug = params.title.replace(/\s+/g, "_").toLowerCase();
136
+ const url = `${client.capabilityPrefix}://${slug}/${params.path}`;
142
137
  return super.registerResource(
143
- name,
138
+ resourceName,
144
139
  new ResourceTemplate(url, {
145
140
  list: void 0
146
141
  }),
147
- {},
142
+ {
143
+ title: this.getCapabilityTitle(client, params.title),
144
+ description: params.description
145
+ },
148
146
  async (url2, variables, extra) => {
149
147
  try {
150
148
  return await cb(url2, variables, extra);
151
149
  } catch (e) {
152
- Bugsnag.notify(
153
- e,
154
- (event) => {
155
- event.addMetadata("app", { resource: name, url: url2 });
156
- event.unhandled = true;
157
- }
158
- );
150
+ Bugsnag.notify(e, (event) => {
151
+ event.addMetadata("app", {
152
+ resource: resourceName,
153
+ url: url2
154
+ });
155
+ event.unhandled = true;
156
+ });
159
157
  throw e;
160
158
  }
161
159
  }
@@ -163,8 +161,28 @@ ${result.instructions}`
163
161
  });
164
162
  }
165
163
  if (client.registerPrompts) {
166
- client.registerPrompts((name, config, cb) => {
167
- return super.registerPrompt(name, config, cb);
164
+ await client.registerPrompts((params, cb) => {
165
+ return super.registerPrompt(
166
+ this.getCapabilityName(client, params.title),
167
+ {
168
+ title: this.getCapabilityTitle(client, params.title),
169
+ description: params.description,
170
+ argsSchema: this.schemaToRawShape(params.argsSchema) || {}
171
+ },
172
+ async (args, extra) => {
173
+ try {
174
+ return await cb(args, extra);
175
+ } catch (e) {
176
+ Bugsnag.notify(e, (event) => {
177
+ event.addMetadata("app", {
178
+ prompt: this.getCapabilityName(client, params.title)
179
+ });
180
+ event.unhandled = true;
181
+ });
182
+ throw e;
183
+ }
184
+ }
185
+ );
168
186
  });
169
187
  }
170
188
  }
@@ -198,19 +216,6 @@ ${result.instructions}`
198
216
  };
199
217
  return annotations;
200
218
  }
201
- getInputSchema(params) {
202
- const args = {};
203
- for (const param of params.parameters ?? []) {
204
- args[param.name] = param.type;
205
- if (param.description) {
206
- args[param.name] = args[param.name].describe(param.description);
207
- }
208
- if (!param.required) {
209
- args[param.name] = args[param.name].optional();
210
- }
211
- }
212
- return { ...args, ...this.schemaToRawShape(params.inputSchema) };
213
- }
214
219
  schemaToRawShape(schema) {
215
220
  if (schema) {
216
221
  if (schema instanceof ZodObject) {
@@ -228,34 +233,36 @@ ${result.instructions}`
228
233
  }
229
234
  return void 0;
230
235
  }
231
- getOutputSchema(params) {
232
- return this.schemaToRawShape(params.outputSchema);
236
+ getCapabilityTitle(client, title) {
237
+ return `${client.name}: ${title}`;
238
+ }
239
+ getCapabilityName(client, title) {
240
+ return `${client.capabilityPrefix}_${title.replace(/\s+/g, "_").toLowerCase()}`;
233
241
  }
234
242
  getDescription(params) {
235
243
  const {
236
244
  summary,
237
245
  useCases,
238
246
  examples,
239
- parameters,
240
247
  inputSchema,
241
248
  hints,
242
249
  outputDescription
243
250
  } = params;
244
251
  let description = summary;
245
- if ((parameters ?? []).length > 0) {
252
+ if (inputSchema && inputSchema instanceof ZodObject) {
253
+ let parameters = Object.keys(inputSchema.shape).map((key) => {
254
+ const field = inputSchema.shape[key];
255
+ const description2 = getTypeDescription(field);
256
+ const defaultValue = getDefaultValue(field);
257
+ return `- ${key} (${getReadableTypeName(field)})${isOptionalType(field) ? "" : " *required*"}${description2 ? `: ${description2}` : ""}${defaultValue !== null ? ` (default: ${JSON.stringify(defaultValue)})` : ""}`;
258
+ }).join("\n");
259
+ if (parameters.length === 0) {
260
+ parameters = "None";
261
+ }
246
262
  description += `
247
263
 
248
264
  **Parameters:**
249
- ${parameters?.map(
250
- (p) => `- ${p.name} (${this.getReadableTypeName(p.type)})${p.required ? " *required*" : ""}${p.description ? `: ${p.description}` : ""}${p.examples ? ` (e.g. ${p.examples.join(", ")})` : ""}${p.constraints ? `
251
- - ${p.constraints.join("\n - ")}` : ""}`
252
- ).join("\n")}`;
253
- }
254
- if (inputSchema && inputSchema instanceof ZodObject) {
255
- description += "\n\n**Parameters:**\n";
256
- description += Object.keys(inputSchema.shape).map(
257
- (key) => this.formatParameterDescription(key, inputSchema.shape[key])
258
- ).join("\n");
265
+ ${parameters}`;
259
266
  }
260
267
  if (outputDescription) {
261
268
  description += `
@@ -286,50 +293,6 @@ Expected Output: ${ex.expectedOutput}` : ""}`
286
293
  }
287
294
  return description.trim();
288
295
  }
289
- formatParameterDescription(key, field, description = null, isOptional = false, defaultValue = null) {
290
- description = description ?? (field.description || null);
291
- if (field instanceof ZodOptional) {
292
- field = field.unwrap();
293
- return this.formatParameterDescription(
294
- key,
295
- field,
296
- description,
297
- true,
298
- defaultValue
299
- );
300
- }
301
- if (field instanceof ZodDefault) {
302
- defaultValue = JSON.stringify(
303
- field.def.defaultValue
304
- );
305
- field = field.unwrap();
306
- return this.formatParameterDescription(
307
- key,
308
- field,
309
- description,
310
- true,
311
- defaultValue
312
- );
313
- }
314
- return `- ${key} (${this.getReadableTypeName(field)})${isOptional ? "" : " *required*"}${description ? `: ${description}` : ""}${defaultValue ? ` (default: ${defaultValue})` : ""}`;
315
- }
316
- getReadableTypeName(zodType) {
317
- zodType = unwrapZodType(zodType);
318
- if (zodType instanceof ZodRecord) {
319
- const record = zodType;
320
- return `record<${this.getReadableTypeName(record.def.keyType)}, ${this.getReadableTypeName(record.def.valueType)}>`;
321
- }
322
- if (zodType instanceof ZodString) return "string";
323
- if (zodType instanceof ZodNumber) return "number";
324
- if (zodType instanceof ZodBoolean) return "boolean";
325
- if (zodType instanceof ZodArray) return "array";
326
- if (zodType instanceof ZodObject) return "object";
327
- if (zodType instanceof ZodEnum) return "enum";
328
- if (zodType instanceof ZodLiteral) return "literal";
329
- if (zodType instanceof ZodUnion) return "union";
330
- if (zodType instanceof ZodAny) return "any";
331
- return "any";
332
- }
333
296
  }
334
297
  export {
335
298
  SmartBearMcpServer