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>
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:
clientIdis pre-filled in.env.example— taken from the agent's credentials in the databaseclientSecretis 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>componentportal/package.json— replaceswagger-ui-reactwith@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:
- Load config from
~/.sentryagent/config.json— fail with helpful message if not configured - Issue an API call:
GET /sdk/scaffold/{agentId}?language={language}with Bearer token fromPOST /oauth2/token - Receive ZIP stream, pipe through
unzipperto extract into--outdirectory - Print success message:
Scaffold generated at ./{agentName}-{language}/ - 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=typescriptreturns a valid ZIP with all 6 template files- ZIP contains
.env.examplewithAGENTIDP_CLIENT_IDpre-filled andAGENTIDP_CLIENT_SECRET=<your-client-secret>as placeholder - ZIP never contains the actual client secret
GET /sdk/scaffold/:agentId?language=pythonreturns Python-specific template files- All 5 languages (typescript, python, go, java, rust) return valid ZIPs
- HTTP 400 on unknown
languagequery param - HTTP 403 when authenticated tenant does not own the agent
sentryagent scaffold --agent-id abc123 --language goextracts scaffold to current directorysentryagent scaffold --agent-id abc123 --language python --out /tmp/myagentextracts to/tmp/myagent- Developer portal
/api-explorerrenders 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