@smartbear/mcp
Version:
MCP server for interacting SmartBear Products
178 lines (177 loc) • 7.82 kB
JavaScript
import { z } from "zod";
import { addOpenAPISpecToSchema } from "./utils.js";
// Type definitions for PactFlow AI API
export const GenerationLanguages = [
"javascript",
"typescript",
"java",
"golang",
"dotnet",
"kotlin",
"swift",
"php",
];
export const HttpMethods = [
"GET",
"PUT",
"POST",
"DELETE",
"OPTIONS",
"HEAD",
"PATCH",
"TRACE",
];
// zod schemas
export const FileInputSchema = z.object({
filename: z
.string()
.optional()
.describe("Filename (helps identify file type and context)"),
language: z
.string()
.optional()
.describe("Programming language (e.g., 'javascript', 'java', 'python') for better analysis"),
body: z
.string()
.describe("Complete file contents - client code, models, test files, etc."),
});
export const OpenAPISchema = z
.object({
openapi: z
.string()
.optional()
.describe("For OpenAPI version (e.g., '3.0.0')"),
swagger: z
.string()
.describe("For OpenAPI documents version 2.x (e.g., '2.0')")
.optional(),
paths: z
.record(z.string(), z.record(z.string(), z.any()))
.describe("OpenAPI paths object containing all API endpoints"),
components: z
.record(z.string(), z.record(z.string(), z.any()))
.optional()
.describe("OpenAPI components section (schemas, responses, etc.)"),
})
.passthrough()
.describe("The complete OpenAPI document describing the API.")
.refine((data) => data.openapi || data.swagger, {
message: "Either 'openapi' (for v3+) or 'swagger' (for v2) must be provided",
path: ["openapi"],
});
export const EndpointMatcherSchema = z
.object({
path: z
.string()
.optional()
.describe("Path pattern to match specific endpoints (e.g., '/users/{id}', '/users/*', '/users/**'). Supports glob patterns: ? (single char), * (excluding /), ** (including /)"),
methods: z
.array(z.enum(HttpMethods))
.optional()
.describe("HTTP methods to include (e.g., ['GET', 'POST']). If not specified, all methods are matched"),
statusCodes: z
.array(z.union([z.number(), z.string()]))
.optional()
.describe("Response status codes to include (e.g., [200, '2XX', 404]). Use 'X' as wildcard (e.g., '2XX' for 200-299). Defaults to successful codes (2XX)"),
operationId: z
.string()
.optional()
.describe("OpenAPI operation ID to match (e.g., 'getUserById', 'get*'). Supports glob patterns"),
})
.optional()
.describe("REQUIRED: Matcher to specify which endpoints from the OpenAPI document to generate tests for. At least one matcher field must be provided");
export const RemoteOpenAPIDocumentSchema = z
.object({
authToken: z
.string()
.describe("Auth Bearer Token if the OpenAPI spec requires authentication.")
.optional(),
authScheme: z
.string()
.describe("Authentication scheme (e.g., 'Bearer', 'Basic'). Default scheme passed should be Bearer if authToken is specified and this field is not set.")
.default("Bearer")
.optional(),
url: z
.string()
.url("Must be a valid openapi url")
.describe("URL of the remote OpenAPI document.")
.optional(),
})
.describe("Use this schema to fetch openapi documents present over a url.");
export const OpenAPIWithMatcherSchema = z
.object({
document: OpenAPISchema.describe("The OpenAPI document describing the API being tested. if document is not provided, don't add the field if remoteOpenAPIDocument is provided.").optional(),
matcher: EndpointMatcherSchema,
remoteDocument: RemoteOpenAPIDocumentSchema.optional().describe("The remote OpenAPI document to use for the review/generation in case openapi document is not provided. If provided do not include the document field under openapi."),
})
.describe("If provided, the OpenAPI document which describes the API being tested and is accompanied by a matcher which will be used to identify the interactions in the OpenAPI document which are relevant to the Pact refinement process.")
.transform(addOpenAPISpecToSchema);
export const RefineInputSchema = z.object({
pactTests: FileInputSchema.describe("Primary pact tests that needs to be refined."),
code: z
.array(FileInputSchema)
.describe("Collection of source code files to analyze and extract API interactions from. Include client code, data models, existing tests, or any code that makes API calls")
.optional(),
userInstructions: z
.string()
.describe("Optional free-form instructions that provide additional context or specify areas of focus during the refinement process of the Pact test.")
.optional(),
errorMessages: z
.array(z.string())
.describe("Optional error output from failed contract test runs. These can be used to better understand the context or failures observed and guide the recommendations toward resolving specific issues.")
.optional(),
openapi: OpenAPIWithMatcherSchema.optional(),
});
export const RequestResponsePairSchema = z
.object({
request: FileInputSchema,
response: FileInputSchema,
})
.describe("Direct request/response pair for a specific interaction. Use this when you have concrete examples of API requests and responses");
export const GenerationInputSchema = z.object({
language: z
.enum(GenerationLanguages)
.optional()
.describe("Target language for the generated Pact tests. If not provided, will be inferred from other inputs."),
requestResponse: RequestResponsePairSchema.optional(),
code: z
.array(FileInputSchema)
.optional()
.describe("Collection of source code files to analyze and extract API interactions from. Include client code, data models, existing tests, or any code that makes API calls"),
openapi: OpenAPIWithMatcherSchema.optional(),
additionalInstructions: z
.string()
.optional()
.describe("Optional free-form instructions to guide the generation process (e.g., 'Focus on error scenarios', 'Include authentication headers', 'Use specific test framework patterns')"),
testTemplate: FileInputSchema.optional().describe("Optional test template to use as a basis for generation. Helps ensure generated tests follow your specific patterns, frameworks, and coding standards"),
});
export const MatcherRecommendationInputSchema = z.array(EndpointMatcherSchema);
export const AiCreditsSchema = z
.object({
total: z.number().describe("The total number of AI credits available."),
used: z.number().describe("The number of AI credits used."),
})
.describe("AI credits information.");
export const OrganizationEntitlementsSchema = z
.object({
name: z.string().describe("The name of the organization."),
planAiEnabled: z
.boolean()
.describe("Whether AI features are enabled at the plan level."),
preferencesAiEnabled: z
.boolean()
.describe("Whether AI features are enabled at the preferences level."),
aiCredits: AiCreditsSchema.describe("AI credits information."),
})
.describe("Organization entitlements information.");
export const UserEntitlementsSchema = z
.object({
aiPermissions: z.array(z.string()).describe("List of AI permissions."),
})
.describe("User entitlements information.");
export const EntitlementsSchema = z
.object({
organizationEntitlements: OrganizationEntitlementsSchema.describe("Organization entitlements information."),
userEntitlements: UserEntitlementsSchema.describe("User entitlements information."),
})
.describe("Entitlements information.");