Files
sentryagent-idp/openspec/changes/phase-5-scale-ecosystem/specs/developer-experience/spec.md
SentryAgent.ai Developer 389a764e8d feat(openspec): propose phase-5-scale-ecosystem change
6 workstreams, 119 tasks — Scale & Ecosystem:
- WS1: Rust SDK
- WS2: Agent-to-Agent (A2A) Authorization
- WS3: Advanced Analytics Dashboard
- WS4: Public API Gateway & Rate Limiting SaaS
- WS5: Developer Experience (DX) improvements
- WS6: AGNTCY Compliance Certification Package

Awaiting CEO approval to begin implementation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 15:33:08 +00:00

10 KiB

WS5: Developer Experience (DX) Improvements

Purpose

Reduce time-to-first-successful-agent-call to under 5 minutes for a new developer. Three concrete improvements: (1) upgrade the developer portal's API explorer from Swagger UI v4 to Stoplight Elements — a modern, component-based API documentation experience with better navigation, code samples, and mock server support; (2) add a scaffold generator endpoint that returns a language-specific starter project pre-wired with the developer's agent credentials as a downloadable ZIP; (3) add a sentryagent scaffold CLI command that calls the scaffold endpoint and extracts the ZIP into the current directory.

New Endpoint

GET /sdk/scaffold/:agentId

Summary: Generate and return a language-specific scaffold ZIP for the specified agent.

Authentication: Bearer token (tenant-scoped). The authenticated tenant must own the specified agent.

Path Parameter:

Parameter Type Description
agentId string (UUID) The agent for which to generate the scaffold

Query Parameters:

Parameter Type Required Default Constraints
language string no typescript Enum: typescript, python, go, java, rust

Response 200:

  • Content-Type: application/zip
  • Content-Disposition: attachment; filename="sentryagent-scaffold-{agentName}-{language}.zip"
  • Body: Binary ZIP archive stream

ZIP Archive Contents (TypeScript example):

sentryagent-scaffold-my-agent-typescript/
├── package.json            (name: my-agent, version: 0.1.0, deps: sentryagent-idp-sdk)
├── tsconfig.json           (strict mode, ES2022 target)
├── .env.example            (AGENTIDP_API_URL, AGENTIDP_CLIENT_ID=<pre-filled>, AGENTIDP_CLIENT_SECRET=<placeholder>)
├── .gitignore              (.env on first line)
├── src/
│   └── index.ts            (imports SDK, creates client from env, issues token, logs success)
└── README.md               (step-by-step: cp .env.example .env, fill secret, npm install, npm start)

ZIP Archive Contents (Python example):

sentryagent-scaffold-my-agent-python/
├── requirements.txt        (sentryagent-idp)
├── .env.example            (AGENTIDP_API_URL, AGENTIDP_CLIENT_ID=<pre-filled>, AGENTIDP_CLIENT_SECRET=<placeholder>)
├── .gitignore              (.env on first line)
├── main.py                 (imports SDK, creates client from env, issues token, prints success)
└── README.md               (step-by-step: cp .env.example .env, fill secret, pip install -r requirements.txt, python main.py)

ZIP Archive Contents (Go example):

sentryagent-scaffold-my-agent-go/
├── go.mod                  (module: my-agent, dep: github.com/sentryagent/sentryagent-idp-go)
├── .env.example            (AGENTIDP_API_URL, AGENTIDP_CLIENT_ID=<pre-filled>, AGENTIDP_CLIENT_SECRET=<placeholder>)
├── .gitignore              (.env on first line)
├── main.go                 (imports SDK, creates client from env, issues token, logs success)
└── README.md               (step-by-step instructions)

Error Responses:

Status Code Description
400 INVALID_LANGUAGE language query param is not one of the supported values
401 UNAUTHORIZED Missing or invalid Bearer token
403 FORBIDDEN Authenticated tenant does not own this agent
404 AGENT_NOT_FOUND No agent with agentId found
429 RATE_LIMITED Rate limit exceeded

Business Rules:

  • clientId is pre-filled in .env.example — taken from the agent's credentials in the database
  • clientSecret is always a <your-client-secret> placeholder — never returned in scaffold (credentials security policy)
  • The ZIP is generated in memory using archiver — no disk writes on the server
  • Scaffold generation is rate-limited to 10 requests per minute per tenant (separate from the main tier rate limit)
  • An audit log entry is created with event_type: "scaffold.generated", metadata.language

Developer Portal: Elements API Explorer Upgrade

File to modify: portal/app/api-explorer/page.tsx

Current state (Phase 4): Embeds swagger-ui-react (Swagger UI v4) loaded from NEXT_PUBLIC_API_URL/openapi.json.

New state (Phase 5): Replaces swagger-ui-react with @stoplight/elements (<API> component). Stoplight Elements provides: three-panel layout (navigation, docs, try-it), built-in code samples in multiple languages, mock server support, and better mobile responsiveness.

Implementation:

// portal/app/api-explorer/page.tsx (complete replacement)
'use client';

import { API } from '@stoplight/elements';
import '@stoplight/elements/styles.min.css';

export default function ApiExplorerPage() {
    return (
        <main className="h-screen w-full">
            <API
                apiDescriptionUrl={`${process.env.NEXT_PUBLIC_API_URL}/openapi.json`}
                router="hash"
                layout="sidebar"
                hideSchemas={false}
                tryItCredentialsPolicy="same-origin"
            />
        </main>
    );
}

Files modified:

  • portal/app/api-explorer/page.tsx — replace Swagger UI component with Elements <API> component
  • portal/package.json — replace swagger-ui-react with @stoplight/elements

CLI: sentryagent scaffold Command

File to create: cli/src/commands/scaffold.ts

Command syntax:

sentryagent scaffold --agent-id <id> [--language typescript|python|go|java|rust] [--out <directory>]

Options:

Option Alias Default Description
--agent-id <id> -a (required) Agent ID to scaffold for
--language <lang> -l typescript Target language for scaffold
--out <dir> -o . (current dir) Directory to extract scaffold ZIP into

Behavior:

  1. Load config from ~/.sentryagent/config.json — fail with helpful message if not configured
  2. Issue an API call: GET /sdk/scaffold/{agentId}?language={language} with Bearer token from POST /oauth2/token
  3. Receive ZIP stream, pipe through unzipper to extract into --out directory
  4. Print success message: Scaffold generated at ./{agentName}-{language}/
  5. Print next steps:
    Next steps:
      1. cd {agentName}-{language}
      2. cp .env.example .env
      3. Add your AGENTIDP_CLIENT_SECRET to .env
      4. npm install  (or equivalent for your language)
      5. npm start
    

Error handling:

  • Agent not found: print Agent {agentId} not found.
  • Forbidden: print You do not own agent {agentId}.
  • Invalid language: print Unsupported language '{lang}'. Choose: typescript, python, go, java, rust
  • Output directory does not exist: create it (with user prompt for confirmation if non-empty)

New CLI dependencies (add to cli/package.json):

  • unzipper — streaming ZIP extraction (pure JS, no native deps)

New Source Files

File Description
src/services/ScaffoldService.ts Business logic: build ZIP archive in memory using archiver
src/controllers/ScaffoldController.ts HTTP handler: stream ZIP response
src/routes/scaffold.ts Express router: GET /sdk/scaffold/:agentId
src/types/scaffold.ts TypeScript interfaces: ScaffoldLanguage, ScaffoldOptions, ScaffoldTemplate
src/templates/scaffold/typescript/ Template files for TypeScript scaffold (package.json, tsconfig.json, index.ts, .env.example, .gitignore, README.md)
src/templates/scaffold/python/ Template files for Python scaffold (requirements.txt, main.py, .env.example, .gitignore, README.md)
src/templates/scaffold/go/ Template files for Go scaffold (go.mod, main.go, .env.example, .gitignore, README.md)
src/templates/scaffold/java/ Template files for Java scaffold (pom.xml, Main.java, .env.example, .gitignore, README.md)
src/templates/scaffold/rust/ Template files for Rust scaffold (Cargo.toml, src/main.rs, .env.example, .gitignore, README.md)
cli/src/commands/scaffold.ts CLI scaffold command implementation

Modified Source Files

File Change
src/routes/index.ts Register scaffold router
src/app.ts No change needed (routes registered via index)
package.json (API) Add archiver and @types/archiver
portal/app/api-explorer/page.tsx Replace Swagger UI with Elements
portal/package.json Replace swagger-ui-react with @stoplight/elements
cli/src/index.ts Register scaffold command with Commander
cli/package.json Add unzipper and @types/unzipper
docs/openapi.yaml Add GET /sdk/scaffold/:agentId endpoint

ScaffoldService Interface

interface IScaffoldService {
    /**
     * Generate an in-memory ZIP archive for the given agent and language.
     * Returns a Node.js Readable stream of the ZIP binary.
     * Template variables injected: {{AGENT_ID}}, {{AGENT_NAME}}, {{CLIENT_ID}}, {{API_URL}}
     */
    generateScaffold(
        agentId: string,
        language: ScaffoldLanguage,
        apiUrl: string
    ): Promise<{ stream: NodeJS.ReadableStream; filename: string }>;
}

Prometheus Metrics

Metric Type Labels Description
agentidp_scaffold_generated_total Counter language Scaffold ZIPs generated by language
agentidp_scaffold_generation_duration_ms Histogram language Time to generate scaffold ZIP

Acceptance Criteria

  • GET /sdk/scaffold/:agentId?language=typescript returns a valid ZIP with all 6 template files
  • ZIP contains .env.example with AGENTIDP_CLIENT_ID pre-filled and AGENTIDP_CLIENT_SECRET=<your-client-secret> as placeholder
  • ZIP never contains the actual client secret
  • GET /sdk/scaffold/:agentId?language=python returns Python-specific template files
  • All 5 languages (typescript, python, go, java, rust) return valid ZIPs
  • HTTP 400 on unknown language query param
  • HTTP 403 when authenticated tenant does not own the agent
  • sentryagent scaffold --agent-id abc123 --language go extracts scaffold to current directory
  • sentryagent scaffold --agent-id abc123 --language python --out /tmp/myagent extracts to /tmp/myagent
  • Developer portal /api-explorer renders Elements v5 with sidebar layout — TypeScript build passes
  • Unit tests cover: scaffold generation (each language), forbidden access, invalid language
  • Integration tests cover: scaffold endpoint response type, content-disposition header, ZIP validity