Portal Community

Dockerfile

# ── Build stage ────────────────────────────────────────────────
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

COPY *.csproj ./
RUN dotnet restore

COPY . .
RUN dotnet publish -c Release -o /app/publish --no-restore

# ── Runtime stage ───────────────────────────────────────────────
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app

# Non-root user for security
RUN addgroup --system mcp && adduser --system --ingroup mcp mcp
USER mcp

COPY --from=build /app/publish .

EXPOSE 8080

ENV ASPNETCORE_URLS=http://+:8080
ENV ASPNETCORE_ENVIRONMENT=Production

ENTRYPOINT ["dotnet", "MyCompany.Octopus.ZendeskMcpServer.dll"]
# Build and tag
docker build -t mycompany/zendesk-mcp-server:1.2.0 .
docker build -t mycompany/zendesk-mcp-server:latest .

# Run locally for testing
docker run -p 8080:8080 \
  -e Zendesk__Subdomain=mycompany \
  -e Zendesk__AdminEmail=admin@mycompany.com \
  -e Zendesk__CredentialId=50 \
  -e Auth__Authority=https://... \
  -e Auth__Audience=api://... \
  mycompany/zendesk-mcp-server:1.2.0

Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: zendesk-mcp-server
  namespace: octopus-tools
spec:
  replicas: 2
  selector:
    matchLabels:
      app: zendesk-mcp-server
  template:
    metadata:
      labels:
        app: zendesk-mcp-server
    spec:
      containers:
      - name: server
        image: mycompany/zendesk-mcp-server:1.2.0
        ports:
        - containerPort: 8080
        env:
        - name: Zendesk__Subdomain
          value: "mycompany"
        - name: Zendesk__AdminEmail
          value: "admin@mycompany.com"
        - name: Zendesk__ApiKey
          valueFrom:
            secretKeyRef:
              name: zendesk-mcp-secrets
              key: api-key
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        resources:
          requests:
            memory: "128Mi"
            cpu:    "100m"
          limits:
            memory: "512Mi"
            cpu:    "500m"
---
apiVersion: v1
kind: Service
metadata:
  name: zendesk-mcp-server
  namespace: octopus-tools
spec:
  selector:
    app: zendesk-mcp-server
  ports:
  - port: 443
    targetPort: 8080
  type: ClusterIP

Versioning Strategy

Change TypeVersion BumpExamples
New tool addedMinor (1.x.0)Add zendesk_get_user tool
Tool description improvedPatch (1.x.y)Better wording in a description field
New optional parameter addedMinor (1.x.0)Add optional max_results to search tool
Required parameter addedMajor (x.0.0)Existing callers will fail — breaking change
Tool renamed or removedMajor (x.0.0)Octopus cached the old name; agents break
Response shape changedMajor (x.0.0)LLM-parsed fields disappear or change type
Tool renames break agents. If you rename a tool between deployments, any Octopus agent that cached the old name will send calls to a non-existent endpoint until the agent config is refreshed. Use additive changes (new tools, new optional params) to avoid breaking existing agents.

Health Check Implementation

// Add deep health check — verify external connectivity
app.MapGet("/health", async (IZendeskClient zendesk) =>
{
    try
    {
        // Cheap connectivity check — e.g. list 1 ticket
        var healthy = await zendesk.PingAsync();
        return healthy
            ? Results.Ok(new  { status = "ok",      version = "1.2.0" })
            : Results.Json(new { status = "degraded",
                                 reason = "Zendesk API unreachable" },
                           statusCode: 503);
    }
    catch (Exception ex)
    {
        return Results.Json(new { status = "unhealthy", error = ex.Message },
                            statusCode: 503);
    }
});

Publishing to the Community Registry

  1. Tag your Docker image with a stable, versioned tag (e.g. mycompany/zendesk-mcp-server:1.2.0)
  2. Push to Docker Hub or a public container registry
  3. Publish a GitHub repository with:
    • README.md — tools list, configuration reference, getting started
    • SECURITY.md — credential handling, tenant isolation description
    • docker-compose.yml — local test setup
  4. Open a Community MCP Server Submission thread on community.bizfirstai.com with:
    • Server name and purpose
    • Tool list (names and one-line descriptions)
    • Docker image reference
    • Link to source repository
  5. The community team verifies the MCP contract and SECURITY.md, then adds your server to the registry listing.

Production Deployment Checklist

#Item
1HTTPS enforced — no plain HTTP accepted
2Bearer token validated on every request (no public endpoints except /health)
3Secrets loaded from environment variables or secret manager — not baked into image
4Non-root container user
5Liveness and readiness probes configured
6Resource limits set (memory + CPU)
7Structured JSON logging enabled
8All tool handlers return structured error JSON — no unhandled exceptions
9Tenant ID read from X-Octopus-Tenant-Id header and applied to all data access
10Integration tests pass against the staging external service