@smartbear/mcp 0.27.1 → 0.28.0

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.
@@ -0,0 +1,22 @@
1
+ function handleInitializeMessage(server, message) {
2
+ if (typeof message !== "object" || message === null || !("method" in message) || message.method !== "initialize") {
3
+ return;
4
+ }
5
+ const params = message.params;
6
+ const clientInfo = params?.clientInfo;
7
+ if (clientInfo) {
8
+ server.setClientInfo(clientInfo);
9
+ }
10
+ if (params?.protocolVersion === "2025-11-25") {
11
+ const clientCapabilities = params.capabilities ?? {};
12
+ if (Object.hasOwn(clientCapabilities, "sampling")) {
13
+ server.setSamplingSupported(true);
14
+ }
15
+ if (Object.hasOwn(clientCapabilities, "elicitation")) {
16
+ server.setElicitationSupported(true);
17
+ }
18
+ }
19
+ }
20
+ export {
21
+ handleInitializeMessage
22
+ };
@@ -10,6 +10,7 @@ class SmartBearMcpServer extends McpServer {
10
10
  cache;
11
11
  samplingSupported = false;
12
12
  elicitationSupported = false;
13
+ clientInfo;
13
14
  clients = [];
14
15
  enabledToolsets;
15
16
  constructor(enabledToolsets) {
@@ -48,6 +49,12 @@ class SmartBearMcpServer extends McpServer {
48
49
  isElicitationSupported() {
49
50
  return this.elicitationSupported;
50
51
  }
52
+ setClientInfo(info) {
53
+ this.clientInfo = info;
54
+ }
55
+ getClientInfo() {
56
+ return this.clientInfo;
57
+ }
51
58
  getClients() {
52
59
  return this.clients;
53
60
  }
@@ -5,6 +5,7 @@ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
5
5
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
6
6
  import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
7
7
  import { clientRegistry } from "./client-registry.js";
8
+ import { handleInitializeMessage } from "./initialize.js";
8
9
  import { withRequestContext } from "./request-context.js";
9
10
  import { SmartBearMcpServer } from "./server.js";
10
11
  import { isDraining, registerShutdownHandler } from "./shutdown.js";
@@ -211,19 +212,7 @@ async function createNewTransport(req, res, transports) {
211
212
  transports.set(newSessionId, { server, transport });
212
213
  }
213
214
  });
214
- transport.onmessage = (message) => {
215
- if ("method" in message && message.method === "initialize") {
216
- if (message.params?.protocolVersion === "2025-11-25") {
217
- const clientCapabilities = message.params?.capabilities;
218
- if (Object.hasOwn(clientCapabilities, "sampling")) {
219
- server.setSamplingSupported(true);
220
- }
221
- if (Object.hasOwn(clientCapabilities, "elicitation")) {
222
- server.setElicitationSupported(true);
223
- }
224
- }
225
- }
226
- };
215
+ transport.onmessage = (message) => handleInitializeMessage(server, message);
227
216
  transport.onclose = () => {
228
217
  if (transport.sessionId) {
229
218
  console.log(`[MCP] Session closed: ${transport.sessionId}`);
@@ -2,6 +2,7 @@ import { enableCompileCache } from "node:module";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
3
  import { clientRegistry } from "./client-registry.js";
4
4
  import { USER_AGENT } from "./info.js";
5
+ import { handleInitializeMessage } from "./initialize.js";
5
6
  import { SmartBearMcpServer } from "./server.js";
6
7
  import { registerShutdownHandler } from "./shutdown.js";
7
8
  import { isOptionalType, getTypeDescription } from "./zod-utils.js";
@@ -57,19 +58,7 @@ ${message.join("\n")}` : "No clients support environment variable configuration.
57
58
  console.error("[MCP][shutdown] Error closing stdio transport:", err);
58
59
  }
59
60
  });
60
- transport.onmessage = (message) => {
61
- if ("method" in message && message.method === "initialize") {
62
- if (message.params?.protocolVersion === "2025-11-25") {
63
- const clientCapabilities = message.params?.capabilities;
64
- if (Object.hasOwn(clientCapabilities, "sampling")) {
65
- server.setSamplingSupported(true);
66
- }
67
- if (Object.hasOwn(clientCapabilities, "elicitation")) {
68
- server.setElicitationSupported(true);
69
- }
70
- }
71
- }
72
- };
61
+ transport.onmessage = (message) => handleInitializeMessage(server, message);
73
62
  await server.connect(transport);
74
63
  }
75
64
  function getEnvVarName(clientPrefix, key) {
@@ -1,4 +1,4 @@
1
- const version = "0.27.1";
1
+ const version = "0.28.0";
2
2
  const config = { "mcpServerName": "SmartBear MCP Server" };
3
3
  const packageJson = {
4
4
  version,
@@ -170,6 +170,10 @@ class PactflowClient {
170
170
  if (Array.isArray(contextToken)) {
171
171
  contextToken = contextToken[0];
172
172
  }
173
+ const clientInfo = this._server?.getClientInfo();
174
+ const sourceApplicationHeader = {
175
+ SOURCE_APPLICATION: clientInfo ? `${clientInfo.name}/${clientInfo.version}` : "unknown"
176
+ };
173
177
  if (contextToken) {
174
178
  let authHeader = contextToken;
175
179
  if (!contextToken.startsWith("Basic ") && !contextToken.startsWith("Bearer ")) {
@@ -178,7 +182,8 @@ class PactflowClient {
178
182
  return {
179
183
  Authorization: authHeader,
180
184
  "Content-Type": "application/json",
181
- "User-Agent": USER_AGENT
185
+ "User-Agent": USER_AGENT,
186
+ ...sourceApplicationHeader
182
187
  };
183
188
  }
184
189
  if (this.token) {
@@ -189,14 +194,16 @@ class PactflowClient {
189
194
  return {
190
195
  Authorization: authHeader,
191
196
  "Content-Type": "application/json",
192
- "User-Agent": USER_AGENT
197
+ "User-Agent": USER_AGENT,
198
+ ...sourceApplicationHeader
193
199
  };
194
200
  } else if (this.username && this.password) {
195
201
  const authString = `${this.username}:${this.password}`;
196
202
  return {
197
203
  Authorization: `Basic ${Buffer.from(authString).toString("base64")}`,
198
204
  "Content-Type": "application/json",
199
- "User-Agent": USER_AGENT
205
+ "User-Agent": USER_AGENT,
206
+ ...sourceApplicationHeader
200
207
  };
201
208
  }
202
209
  return void 0;
@@ -677,7 +677,8 @@ class SwaggerAPI {
677
677
  contentType = "markdown",
678
678
  source = "internal",
679
679
  order = 0,
680
- parentId = null
680
+ parentId = null,
681
+ pageSlug
681
682
  } = args;
682
683
  if (contentType === "html" && source === "internal" && pageContent !== void 0) {
683
684
  throw new ToolError(
@@ -694,12 +695,12 @@ class SwaggerAPI {
694
695
  );
695
696
  }
696
697
  const section = sections.items[0];
697
- const pageSlug = normalizeSlug(pageTitle);
698
+ const resolvedPageSlug = pageSlug || normalizeSlug(pageTitle);
698
699
  const normalizedTitle = pageTitle.slice(0, 255);
699
700
  const tocItem = await this.createTableOfContents(section.id, {
700
701
  type: "new",
701
702
  title: normalizedTitle,
702
- slug: pageSlug,
703
+ slug: resolvedPageSlug,
703
704
  order,
704
705
  parentId,
705
706
  content: {
@@ -724,7 +725,7 @@ class SwaggerAPI {
724
725
  sectionSlug: section.slug,
725
726
  pageDetails: {
726
727
  tableOfContentsId: tocItem.id,
727
- slug: pageSlug,
728
+ slug: resolvedPageSlug,
728
729
  title: normalizedTitle,
729
730
  content: {
730
731
  type: contentType,
@@ -65,6 +65,31 @@ class FunctionalTestingAPI {
65
65
  }
66
66
  return response.json();
67
67
  }
68
+ async listSuites() {
69
+ const headers = this.getFtHeaders();
70
+ let response;
71
+ try {
72
+ response = await fetch(`${this.baseUrl}/suites`, {
73
+ method: "GET",
74
+ headers
75
+ });
76
+ } catch {
77
+ throw new ToolError(
78
+ "Swagger Functional Testing service is currently unreachable. Retry after a moment."
79
+ );
80
+ }
81
+ if (response.status === 401 || response.status === 403) {
82
+ throw new ToolError(
83
+ "Authentication failed. Verify your API token is valid and has not expired."
84
+ );
85
+ }
86
+ if (!response.ok) {
87
+ throw new ToolError(
88
+ `Failed to list Functional Testing suites: ${response.status} ${response.statusText}`
89
+ );
90
+ }
91
+ return response.json();
92
+ }
68
93
  }
69
94
  export {
70
95
  FUNCTIONAL_TESTING_API_KEY_HEADER,
@@ -22,6 +22,14 @@ const FUNCTIONAL_TESTING_TOOLS = [
22
22
  inputSchema: GetFunctionalTestingExecutionTestSchema,
23
23
  handler: "getFunctionalTestingExecution",
24
24
  idempotent: false
25
+ },
26
+ {
27
+ title: "List Suites",
28
+ toolset: "Functional Testing",
29
+ summary: "Lists all test suites available in your Swagger Functional Testing workspace. Use this tool when you need to discover available suites before running them or checking their execution history. Do not use this tool to retrieve individual tests or test suite execution results.",
30
+ handler: "listFunctionalTestingSuites",
31
+ idempotent: true,
32
+ readOnly: true
25
33
  }
26
34
  ];
27
35
  export {
@@ -216,7 +216,10 @@ const CreateDocumentationPageArgsSchema = z.object({
216
216
  portalId: z.string().describe("Portal UUID or subdomain - unique identifier for the portal"),
217
217
  productId: z.string().uuid().describe("Product UUID - unique identifier for the product"),
218
218
  pageTitle: z.string().describe(
219
- "Title of the documentation page - will be displayed in navigation (3-255 characters, used to generate the page slug)"
219
+ "Title of the documentation page - will be displayed in navigation (3-255 characters)"
220
+ ),
221
+ pageSlug: z.string().optional().describe(
222
+ "URL slug for the documentation page. 3-255 characters, lowercase, alphanumeric with hyphens, underscores, or dots (e.g. 'my-page'). If not provided, the slug is generated from the page title."
220
223
  ),
221
224
  pageContent: z.string().optional().describe(
222
225
  "Content of the documentation page. Provide HTML when contentType is 'html', Markdown when contentType is 'markdown'."
@@ -191,6 +191,9 @@ class SwaggerClient {
191
191
  }
192
192
  return fn(this.ftApi);
193
193
  }
194
+ async listFunctionalTestingSuites() {
195
+ return this.withFunctionalTesting((ftApi) => ftApi.listSuites());
196
+ }
194
197
  async registerTools(register, _getInput) {
195
198
  TOOLS.forEach((tool) => {
196
199
  if (tool.toolset === "Functional Testing" && !this.ftApi) {