Skip to main content
Glama
OctopusDeploy

Octopus Deploy MCP Server

Official

Find certificates in an Octopus Deploy space

find_certificates
Read-onlyIdempotent

Retrieve 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

TableJSON Schema
NameRequiredDescriptionDefault
spaceNameYes
certificateIdNoThe ID of a specific certificate to retrieve. If omitted, lists all certificates.
skipNoNumber of certificates to skip for pagination (only used when listing)
takeNoNumber of certificates to take for pagination (only used when listing)
searchNoSearch term to filter certificates (only used when listing)
archivedNoFilter by archived status (only used when listing)
tenantNoFilter by tenant (only used when listing)
firstResultNoIndex of first result to return (only used when listing)
orderByNoField to order results by (only used when listing)
idsNoFilter by specific certificate IDs (only used when listing)
partialNameNoFilter by partial name match (only used when listing)

Implementation Reference

  • 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,
                }),
              },
            ],
          };
        }
      );
    }
  • 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)"),
    },
  • 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,
    });
  • 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,
      };
    }
  • 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;
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false. The description adds behavioral context about dual retrieval modes and filtering, enhancing transparency without contradicting annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise with two short paragraphs, front-loading the main purpose. Every sentence adds value without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the 11 parameters and no output schema, the description covers the two usage modes and common filters. It is fairly complete, though it could mention the return format.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is high (91%), so the schema already documents parameters well. The description adds context about the dual behavior but doesn't significantly extend parameter meaning beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The title and description clearly state the tool finds certificates in an Octopus Deploy space, with specific verb 'Find' and resource 'certificates'. It distinguishes from sibling find_* tools by focusing on certificates.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly explains when to use certificateId (single certificate) vs omit (list all) and mentions optional filters. However, it doesn't state when not to use this tool or provide alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/OctopusDeploy/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server