@northcognitive/agentpack
v0.1.5
Published
TypeScript SDK for building Agentpack tools
Readme
@agentpack/sdk
Zero-Config TypeScript SDK for Agentpack Tools
Drop a .ts file in your repo and get a production-ready, containerized, gRPC-enabled tool—instantly. No servers. No Docker. No infrastructure code.
Installation
npm install @agentpack/sdk zod
# or
pnpm add @agentpack/sdk zod
# or
yarn add @agentpack/sdk zodQuick Start
1. Write ONLY Business Logic
// tools/greet.ts
import { tool, z } from '@agentpack/sdk';
export default tool({
name: 'greet',
description: 'Greet a person by name',
input: z.object({
name: z.string(),
}),
output: z.object({
greeting: z.string(),
}),
async handler(input, ctx) {
ctx.logger.info('Greeting user', { name: input.name });
return { greeting: `Hello, ${input.name}!` };
},
});2. That's It!
No step 2. Agentpack Builder automatically:
- Discovers tools in
tools/directory - Generates
.agentpack-manifest.json - Creates optimized Dockerfile
- Builds OCI image with gRPC runtime
- Deploys to container backend
Agentpack Runtime automatically:
- Loads tools from manifest
- Starts gRPC server (port 50051)
- Handles execution via Rust core
- Provides full observability
Zero-Config Architecture
┌─────────────────────────────────────────────────┐
│ YOU: Write tools/calculator.ts │
│ ↓ │
│ BUILDER: Scan → Manifest → Dockerfile → Image │
│ ↓ │
│ RUNTIME: Load → gRPC Server → Execute │
│ ↓ │
│ RUST CORE: Orchestrate with Tower middleware │
└─────────────────────────────────────────────────┘You own: Business logic (100% focus) Agentpack owns: Servers, containers, orchestration, observability
Features
Built-In, Zero Setup:
- ✅ Type-Safe: Full TypeScript inference from Zod schemas
- ✅ Validated: Automatic input/output validation
- ✅ Observable: OpenTelemetry tracing (no config)
- ✅ Secure: Secret management via dashboard
- ✅ Resilient: HTTP client with automatic retries
- ✅ Structured Logging: Request-scoped context
- ✅ gRPC: High-performance protocol (auto-configured)
- ✅ Containerized: OCI images with base runtime
- ✅ Sandboxed: gVisor/Firecracker isolation
Execution Context
Every tool handler receives ctx with production-ready utilities:
Secrets
// Get optional secret
const apiKey = await ctx.secrets.get('API_KEY');
// Get required secret (throws if missing)
const apiKey = await ctx.secrets.require('API_KEY');
// Secrets configured via Agentpack Dashboard:
// - vault://kv/my-secret
// - aws-sm://secret-name
// - gcp-sm://projects/*/secrets/*HTTP Client
// GET with automatic retries
const response = await ctx.http.get('https://api.example.com/data', {
params: { limit: 10 },
headers: { 'Authorization': `Bearer ${token}` },
});
// POST, PUT, DELETE also available
const response = await ctx.http.post('https://api.example.com/data', {
name: 'value',
});Logger
ctx.logger.info('Processing request', { userId: '123' });
ctx.logger.error('Something went wrong', { error: err.message });
// Logs automatically include:
// - Request ID
// - Trace ID
// - Tool name
// - TimestampTracer (OpenTelemetry)
const span = ctx.trace.startSpan('external-api-call');
try {
// ... do work ...
span.setAttribute('items.count', items.length);
} finally {
span.end();
}
// Traces sent to OpenTelemetry Collector automaticallyExamples
See examples/ directory:
Simple Tool (examples/calculator)
export default tool({
name: 'calculator',
description: 'Perform arithmetic operations',
input: z.object({
operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
a: z.number(),
b: z.number(),
}),
output: z.object({
result: z.number(),
}),
async handler(input, ctx) {
// Pure business logic
let result: number;
switch (input.operation) {
case 'add': result = input.a + input.b; break;
case 'subtract': result = input.a - input.b; break;
case 'multiply': result = input.a * input.b; break;
case 'divide':
if (input.b === 0) throw new Error('Division by zero');
result = input.a / input.b;
break;
}
return { result };
},
});External API Tool (examples/weather)
Demonstrates:
- Secret management (
ctx.secrets.require()) - HTTP client with retries (
ctx.http.get()) - Structured logging
- Input validation with Zod enums
API Reference
tool(definition)
Create a tool definition.
interface ToolDefinition<TInput, TOutput> {
name: string; // Unique tool identifier
description: string; // Human-readable description
input: ZodSchema<TInput>; // Zod input validation schema
output: ZodSchema<TOutput>; // Zod output validation schema
handler: ToolHandler<TInput, TOutput>; // Async execution function
}
type ToolHandler<TInput, TOutput> = (
input: TInput,
ctx: ExecutionContext
) => Promise<TOutput>;Returns: Tool instance (internal use by runtime)
Error Handling
import {
ToolError,
ValidationError,
SecretNotFoundError,
HttpError,
} from '@agentpack/sdk';
async handler(input, ctx) {
// Validation errors thrown automatically
// Secret not found
const key = await ctx.secrets.require('API_KEY'); // throws SecretNotFoundError
// HTTP errors
const response = await ctx.http.get('...'); // throws HttpError on failure
// Custom errors
if (!input.valid) {
throw new ToolError('Invalid input', 'INVALID_INPUT');
}
}Multi-File Tools
Tools can have dependencies:
tools/
├── calculator/
│ ├── index.ts # Entry point (exports default tool)
│ ├── operations.ts # Helper functions
│ └── types.ts # Shared types
└── weather.ts # Single-file toolBuilder discovers both patterns automatically:
- Single-file:
tools/weather.ts - Multi-file:
tools/calculator/index.ts
Testing
import { describe, it, expect } from 'vitest';
import { tool, z } from '@agentpack/sdk';
describe('my tool', () => {
it('should work', async () => {
const myTool = tool({
name: 'test',
description: 'Test tool',
input: z.object({ value: z.number() }),
output: z.object({ result: z.number() }),
async handler(input, ctx) {
return { result: input.value * 2 };
},
});
// Create mock context
const mockCtx = {
secrets: {
get: async () => undefined,
require: async (k) => k
},
http: { get: async () => ({ data: {} }) },
logger: { info: () => {}, error: () => {} },
trace: { startSpan: () => ({ end: () => {} }) },
metadata: { requestId: '123' },
};
const result = await myTool.execute({ value: 5 }, mockCtx);
expect(result.result).toBe(10);
});
});Deployment
Nothing to do! Builder + Runtime handle everything:
- Build:
agentpack build(or automatic via GitHub App) - Deploy: Automatic to Agentpack infrastructure
- Scale: Automatic based on load
- Monitor: Traces/logs in dashboard
Configuration
Optional agentpack.yaml for advanced settings:
version: 0.1
project: my-tools
agents:
- name: default
model: { provider: anthropic, name: claude-3-5-sonnet }
tools: ["*"] # All tools in tools/
resources:
- name: stripe_secret
type: secret
source: vault://kv/stripe
policies:
outbound_network:
allow: ["*.stripe.com", "*.openai.com"]Thin SDK Philosophy
This SDK is intentionally minimal:
- ✅
tool()function for definitions - ✅
ExecutionContexttypes - ✅ Error classes
- ❌ NO server creation
- ❌ NO Docker setup
- ❌ NO deployment code
All orchestration lives in Rust core for:
- Consistent cross-language behavior
- Performance (Tower middleware)
- Security (policy enforcement)
- Observability (OpenTelemetry)
Troubleshooting
"Tool not found" error
- Ensure tool is in
tools/directory - Check file exports
default tool({...}) - Verify
.agentpack-manifest.jsonwas generated
Validation errors
- Check Zod schema matches input
- Use
.optional()or.default()for optional fields - Test with mock data
Secret not found
- Configure secrets in Agentpack Dashboard
- Use correct secret key name
- Check environment variables in development
Related Packages
@agentpack/runtime-typescript- Internal gRPC runtime (private)agentpack- Python SDK (same zero-config approach)
License
Copyright © 2025 North Cognitive Inc. All Rights Reserved.
This software is proprietary and confidential. Unauthorized copying, distribution, or use is strictly prohibited.
