Find certificates in an Octopus Deploy space
find_certificatesRetrieve a specific certificate by ID or list all certificates with optional filters such as name, archived status, or tenant.
Instructions
Find certificates in a space - can retrieve a single certificate by ID or list all certificates
This unified tool can either:
Get detailed information about a specific certificate when certificateId is provided
List all certificates in a space when certificateId is omitted
You can optionally filter by various parameters like name, archived status, tenant, etc. when listing.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| spaceName | Yes | ||
| certificateId | No | The ID of a specific certificate to retrieve. If omitted, lists all certificates. | |
| skip | No | Number of certificates to skip for pagination (only used when listing) | |
| take | No | Number of certificates to take for pagination (only used when listing) | |
| search | No | Search term to filter certificates (only used when listing) | |
| archived | No | Filter by archived status (only used when listing) | |
| tenant | No | Filter by tenant (only used when listing) | |
| firstResult | No | Index of first result to return (only used when listing) | |
| orderBy | No | Field to order results by (only used when listing) | |
| ids | No | Filter by specific certificate IDs (only used when listing) | |
| partialName | No | Filter by partial name match (only used when listing) |
Implementation Reference
- src/tools/findCertificates.ts:37-121 (handler)The main handler function that executes the find_certificates tool logic. It creates an API client, resolves the space ID, and either gets a single certificate by ID (lines 55-84) or lists all certificates with optional filters (lines 86-118). Returns formatted JSON content for MCP.
async ({ spaceName, certificateId, skip, take, search, archived, tenant, firstResult, orderBy, ids, partialName, }) => { const configuration = getClientConfigurationFromEnvironment(); const client = await Client.create(configuration); const spaceId = await resolveSpaceId(client, spaceName); // If certificateId is provided, get a single certificate if (certificateId) { validateEntityId(certificateId, 'certificate', ENTITY_PREFIXES.certificate); try { const response = await client.get<CertificateResource>( "~/api/{spaceId}/certificates/{id}", { spaceId, id: certificateId, } ); const certificate = mapCertificateResource(response); return { content: [ { type: "text", text: JSON.stringify(certificate), }, ], }; } catch (error) { handleOctopusApiError(error, { entityType: 'certificate', entityId: certificateId, spaceName }); } } // Otherwise, list all certificates const response = await client.get<ResourceCollection<CertificateResource>>( "~/api/{spaceId}/certificates{?skip,take,search,archived,tenant,firstResult,orderBy,ids,partialName}", { spaceId, skip, take, search, archived, tenant, firstResult, orderBy, ids, partialName, } ); const certificates = response.Items.map((cert: CertificateResource) => mapCertificateResource(cert)); return { content: [ { type: "text", text: JSON.stringify({ totalResults: response.TotalResults, itemsPerPage: response.ItemsPerPage, numberOfPages: response.NumberOfPages, lastPageNumber: response.LastPageNumber, items: certificates, }), }, ], }; } ); } - src/tools/findCertificates.ts:22-34 (schema)Input schema using Zod for the find_certificates tool. Defines parameters: spaceName (required), certificateId, skip, take, search, archived, tenant, firstResult, orderBy, ids, and partialName.
inputSchema: { spaceName: z.string(), certificateId: z.string().optional().describe("The ID of a specific certificate to retrieve. If omitted, lists all certificates."), skip: z.number().optional().describe("Number of certificates to skip for pagination (only used when listing)"), take: z.number().optional().describe("Number of certificates to take for pagination (only used when listing)"), search: z.string().optional().describe("Search term to filter certificates (only used when listing)"), archived: z.boolean().optional().describe("Filter by archived status (only used when listing)"), tenant: z.string().optional().describe("Filter by tenant (only used when listing)"), firstResult: z.number().optional().describe("Index of first result to return (only used when listing)"), orderBy: z.string().optional().describe("Field to order results by (only used when listing)"), ids: z.array(z.string()).optional().describe("Filter by specific certificate IDs (only used when listing)"), partialName: z.string().optional().describe("Filter by partial name match (only used when listing)"), }, - src/tools/findCertificates.ts:10-127 (registration)Registration of the tool with the MCP server via server.registerTool() and the global tool registry via registerToolDefinition(). The tool is registered under the 'certificates' toolset as read-only.
export function registerFindCertificatesTool(server: McpServer) { server.registerTool( "find_certificates", { title: "Find certificates in an Octopus Deploy space", description: `Find certificates in a space - can retrieve a single certificate by ID or list all certificates This unified tool can either: - Get detailed information about a specific certificate when certificateId is provided - List all certificates in a space when certificateId is omitted You can optionally filter by various parameters like name, archived status, tenant, etc. when listing.`, inputSchema: { spaceName: z.string(), certificateId: z.string().optional().describe("The ID of a specific certificate to retrieve. If omitted, lists all certificates."), skip: z.number().optional().describe("Number of certificates to skip for pagination (only used when listing)"), take: z.number().optional().describe("Number of certificates to take for pagination (only used when listing)"), search: z.string().optional().describe("Search term to filter certificates (only used when listing)"), archived: z.boolean().optional().describe("Filter by archived status (only used when listing)"), tenant: z.string().optional().describe("Filter by tenant (only used when listing)"), firstResult: z.number().optional().describe("Index of first result to return (only used when listing)"), orderBy: z.string().optional().describe("Field to order results by (only used when listing)"), ids: z.array(z.string()).optional().describe("Filter by specific certificate IDs (only used when listing)"), partialName: z.string().optional().describe("Filter by partial name match (only used when listing)"), }, annotations: READ_ONLY_TOOL_ANNOTATIONS, }, async ({ spaceName, certificateId, skip, take, search, archived, tenant, firstResult, orderBy, ids, partialName, }) => { const configuration = getClientConfigurationFromEnvironment(); const client = await Client.create(configuration); const spaceId = await resolveSpaceId(client, spaceName); // If certificateId is provided, get a single certificate if (certificateId) { validateEntityId(certificateId, 'certificate', ENTITY_PREFIXES.certificate); try { const response = await client.get<CertificateResource>( "~/api/{spaceId}/certificates/{id}", { spaceId, id: certificateId, } ); const certificate = mapCertificateResource(response); return { content: [ { type: "text", text: JSON.stringify(certificate), }, ], }; } catch (error) { handleOctopusApiError(error, { entityType: 'certificate', entityId: certificateId, spaceName }); } } // Otherwise, list all certificates const response = await client.get<ResourceCollection<CertificateResource>>( "~/api/{spaceId}/certificates{?skip,take,search,archived,tenant,firstResult,orderBy,ids,partialName}", { spaceId, skip, take, search, archived, tenant, firstResult, orderBy, ids, partialName, } ); const certificates = response.Items.map((cert: CertificateResource) => mapCertificateResource(cert)); return { content: [ { type: "text", text: JSON.stringify({ totalResults: response.TotalResults, itemsPerPage: response.ItemsPerPage, numberOfPages: response.NumberOfPages, lastPageNumber: response.LastPageNumber, items: certificates, }), }, ], }; } ); } registerToolDefinition({ toolName: "find_certificates", config: { toolset: "certificates", readOnly: true }, registerFn: registerFindCertificatesTool, }); - src/types/certificateTypes.ts:49-80 (schema)Helper function mapCertificateResource() that transforms the raw API response into a cleaned-up certificate object with camelCase properties. Also includes the CertificateResource interface (line 27) defining the shape of certificate data from the API.
export function mapCertificateResource(cert: CertificateResource) { return { spaceId: cert.SpaceId, id: cert.Id, name: cert.Name, notes: cert.Notes, thumbprint: cert.Thumbprint, subjectDistinguishedName: cert.SubjectDistinguishedName, issuerDistinguishedName: cert.IssuerDistinguishedName, subjectCommonName: cert.SubjectCommonName, subjectOrganization: cert.SubjectOrganization, issuerCommonName: cert.IssuerCommonName, issuerOrganization: cert.IssuerOrganization, notAfter: cert.NotAfter, notBefore: cert.NotBefore, isExpired: cert.IsExpired, version: cert.Version, serialNumber: cert.SerialNumber, signatureAlgorithmName: cert.SignatureAlgorithmName, environmentIds: cert.EnvironmentIds, tenantIds: cert.TenantIds, tenantTags: cert.TenantTags, certificateDataFormat: cert.CertificateDataFormat, archived: cert.Archived, replacedBy: cert.ReplacedBy, selfSigned: cert.SelfSigned, selfSignedCertificateCurve: cert.SelfSignedCertificateCurve, hasPrivateKey: cert.HasPrivateKey, tenantedDeploymentParticipation: cert.TenantedDeploymentParticipation, subjectAlternativeNames: cert.SubjectAlternativeNames, }; } - src/helpers/errorHandling.ts:20-80 (helper)Error handling helper used by the handler to produce user-friendly error messages for 404, authentication, and connection errors. Also includes validateEntityId and the ENTITY_PREFIXES constant with 'Certificates-' prefix.
export function handleOctopusApiError( error: unknown, context: { entityType?: string; entityId?: string; spaceName?: string; helpText?: string; }, ): never { const { entityType, entityId, spaceName, helpText } = context; // Handle 404/not found errors if ( isErrorWithMessage(error, "not found") || isErrorWithMessage(error, "404") ) { if (entityType && entityId && spaceName) { throw new Error( `${entityType.charAt(0).toUpperCase() + entityType.slice(1)} '${entityId}' not found in space '${spaceName}'. ` + (helpText || `Verify the ${entityType} ID is correct using list_${entityType}s.`), ); } if (spaceName) { throw new Error( `Space '${spaceName}' not found. Use list_spaces to see available spaces. Space names are case-sensitive.`, ); } } // Handle authentication errors if ( isErrorWithMessage(error, "authentication") || isErrorWithMessage(error, "401") || isErrorWithMessage( error, "You must be logged in to request this resource", ) || isErrorWithMessage(error, "provide a valid API key") ) { throw new Error( "Authentication failed. Ensure a valid API key (OCTOPUS_API_KEY) or access token (OCTOPUS_ACCESS_TOKEN) is provided. " + "You can generate an API key from your Octopus Deploy user profile.", ); } // Handle connection errors if ( isErrorWithMessage(error, "connect") || isErrorWithMessage(error, "timeout") ) { throw new Error( "Cannot connect to Octopus Deploy instance. Check that OCTOPUS_SERVER_URL environment variable is set correctly " + "(e.g., 'https://your-instance.octopus.app') and that the instance is accessible.", ); } // Re-throw the original error if no specific handling applies throw error; }