@aimform/apps
v0.1.1
Published
Aimform App Store SDK — define, validate, and publish apps for the Aimform platform
Maintainers
Readme
@aimform/apps
The official SDK for building, validating, and publishing apps on the Aimform platform.
@aimform/apps gives third-party developers everything they need to define app manifests, entity types, tools, onboarding flows, and profile sections — all with full TypeScript support and runtime validation via Zod.
Installation
npm install @aimform/apps
# or
pnpm add @aimform/appsQuick Start
Use defineApp() to create a complete app manifest:
import { defineApp } from "@aimform/apps";
const myApp = defineApp({
id: "my-slack-bot",
name: "Slack Bot",
version: "1.0.0",
avatar: "https://example.com/slack-icon.png",
description: "Bring Slack messages into Aimform.",
category: "communication",
entryPoint: {
type: "mcp",
url: "https://slack-bot.example.com/mcp",
},
auth: {
type: "oauth2",
provider: "slack",
},
declaresEntityTypes: [
{
name: "slack_message",
properties: [
{ name: "text", type: "text", label: "Message", required: true },
{ name: "channel", type: "string", label: "Channel" },
{ name: "sent_at", type: "date", label: "Sent" },
],
},
],
scopes: {
read: ["slack_message"],
write: ["slack_message"],
},
tools: [
{
name: "send_slack_message",
description: "Send a message to a Slack channel",
riskTier: "confirm",
exposedToAi: true,
inputSchema: {
type: "object",
properties: {
channel: { type: "string" },
text: { type: "string" },
},
required: ["channel", "text"],
},
},
],
onboarding: {
steps: [
{
id: "connect-slack",
type: "oauth_connect",
title: "Connect your Slack workspace",
provider: "slack",
},
],
},
profile: {
sections: [
{
id: "recent-messages",
type: "view",
title: "Recent Messages",
entityType: "slack_message",
fields: ["text", "channel", "sent_at"],
defaultSort: "sent_at",
},
],
},
});Entity Types
Entity types define the data model your app brings into Aimform. Use the fluent builder or inline array syntax:
import { defineEntityType } from "@aimform/apps";
// Fluent builder
const taskEntity = defineEntityType("task")
.addProperty("title", "string", { label: "Title", required: true, searchable: true })
.addProperty("status", "string", { label: "Status" })
.addProperty("due_date", "date", { label: "Due Date" })
.build();
// Inline array
const noteEntity = defineEntityType("note", [
{ name: "title", type: "string", label: "Title", required: true },
{ name: "content", type: "text", label: "Content", searchable: true },
{ name: "created_at", type: "date", label: "Created" },
]);Tools
Tools are actions exposed by your app that users and the AI assistant can invoke:
import { defineTool } from "@aimform/apps";
// Safe read-only tool (auto-executable)
const searchTasks = defineTool({
name: "search_tasks",
description: "Search tasks by title or status",
riskTier: "auto",
exposedToAi: true,
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "Search query" },
},
required: ["query"],
},
});
// Write operation (requires user confirmation)
const createTask = defineTool({
name: "create_task",
description: "Create a new task",
riskTier: "confirm",
exposedToAi: true,
inputSchema: {
type: "object",
properties: {
title: { type: "string", description: "Task title" },
dueDate: { type: "string", description: "Due date (ISO 8601)" },
},
required: ["title"],
},
});
// Blocked tool (requires explicit user action)
const deleteTask = defineTool({
name: "delete_task",
description: "Permanently delete a task",
riskTier: "blocked",
exposedToAi: false,
inputSchema: {
type: "object",
properties: {
taskId: { type: "string", description: "Task ID" },
},
required: ["taskId"],
},
});Risk Tiers
| Tier | Behavior |
|------|----------|
| auto | Executes automatically without user confirmation. Use for safe read-only operations. |
| confirm | Requires user confirmation before execution. Use for write/delete operations. |
| blocked | Never auto-executes. Requires explicit user trigger. Use for dangerous or irreversible actions. |
Onboarding Steps
Onboarding steps guide users through connecting your app:
import { defineOnboardingStep } from "@aimform/apps";
// OAuth2 connection
const connectStep = defineOnboardingStep({
id: "connect",
type: "oauth_connect",
title: "Connect your account",
description: "Authorize access to your data.",
provider: "google",
});
// API key entry
const apiKeyStep = defineOnboardingStep({
id: "api-key",
type: "api_key_form",
title: "Enter your API key",
description: "Find your API key in your account settings.",
apiKeyLabel: "API Key",
apiKeyHelpText: "Paste the API key from your provider dashboard.",
});
// Permission review
const reviewStep = defineOnboardingStep({
id: "review",
type: "permission_review",
title: "Review permissions",
description: "This app will be able to read your emails.",
});
// Custom setup
const customStep = defineOnboardingStep({
id: "setup",
type: "custom",
title: "Configure your workspace",
customUrl: "https://my-app.example.com/setup",
});Profile Sections
Profile sections display your app's data in Aimform entity detail views:
import { defineProfileSection } from "@aimform/apps";
// Table/list view
const listView = defineProfileSection({
id: "recent-items",
type: "view",
title: "Recent Items",
entityType: "task",
fields: ["title", "status", "due_date"],
defaultSort: "due_date",
});
// Document view (rich content)
const docView = defineProfileSection({
id: "note-content",
type: "document",
title: "Note",
entityType: "note",
documentType: "text/markdown",
});
// Freeform board
const board = defineProfileSection({
id: "kanban",
type: "space",
title: "Project Board",
entityType: "task",
});Full Gmail App Example
A complete Gmail app manifest using all the SDK features:
import {
defineApp,
defineEntityType,
defineTool,
defineOnboardingStep,
defineProfileSection,
defineScopes,
} from "@aimform/apps";
const gmail = defineApp({
id: "gmail",
name: "Gmail",
version: "1.0.0",
avatar: "https://example.com/gmail-icon.png",
description: "Read, search, and send emails from Gmail.",
category: "communication",
entryPoint: {
type: "mcp",
url: "https://gmail-app.example.com/mcp",
},
auth: {
type: "oauth2",
provider: "google",
},
declaresEntityTypes: [
defineEntityType("email_thread", [
{ name: "subject", type: "string", label: "Subject", required: true, searchable: true },
{ name: "snippet", type: "text", label: "Snippet", searchable: true },
{ name: "sender", type: "email", label: "Sender" },
{ name: "recipients", type: "json", label: "Recipients" },
{ name: "received_at", type: "date", label: "Received" },
{ name: "is_read", type: "boolean", label: "Read" },
{ name: "labels", type: "json", label: "Labels" },
]) as ReturnType<typeof defineEntityType>,
],
scopes: defineScopes(["email_thread"], ["email_thread"]),
tools: [
defineTool({
name: "search_emails",
description: "Search Gmail messages by query",
riskTier: "auto",
exposedToAi: true,
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "Gmail search query (supports Gmail search operators)" },
maxResults: { type: "number", description: "Maximum results to return", default: 10 },
},
required: ["query"],
},
}),
defineTool({
name: "get_email",
description: "Get full content of a specific email",
riskTier: "auto",
exposedToAi: true,
inputSchema: {
type: "object",
properties: {
emailId: { type: "string", description: "Email message ID" },
},
required: ["emailId"],
},
}),
defineTool({
name: "send_email",
description: "Send an email via Gmail",
riskTier: "confirm",
exposedToAi: true,
inputSchema: {
type: "object",
properties: {
to: { type: "string", description: "Recipient email address(es)" },
cc: { type: "string", description: "CC recipients" },
subject: { type: "string", description: "Email subject" },
body: { type: "string", description: "Email body (plain text or HTML)" },
},
required: ["to", "subject", "body"],
},
}),
defineTool({
name: "delete_email",
description: "Delete an email (moves to trash)",
riskTier: "confirm",
exposedToAi: true,
inputSchema: {
type: "object",
properties: {
emailId: { type: "string", description: "Email message ID" },
},
required: ["emailId"],
},
}),
],
onboarding: {
steps: [
defineOnboardingStep({
id: "connect-gmail",
type: "oauth_connect",
title: "Connect your Gmail account",
description: "We'll redirect you to Google to authorize access to your Gmail.",
provider: "google",
}),
defineOnboardingStep({
id: "review-permissions",
type: "permission_review",
title: "Review permissions",
description: "This app needs access to read, search, and send emails from your Gmail account.",
}),
],
},
profile: {
sections: [
defineProfileSection({
id: "recent-emails",
type: "view",
title: "Recent Emails",
entityType: "email_thread",
fields: ["subject", "snippet", "sender", "received_at", "is_read"],
defaultSort: "received_at",
}),
defineProfileSection({
id: "email-content",
type: "document",
title: "Email Body",
entityType: "email_thread",
documentType: "text/html",
}),
],
},
});Validation
The SDK includes runtime validation via Zod schemas, accessible from the server entry point:
import { validateManifest, scanApp } from "@aimform/apps/server";
// Validate a manifest loaded from JSON
const rawManifest = JSON.parse(
await Deno.readTextFile("aimform.manifest.json"),
);
const result = validateManifest(rawManifest);
if (!result.success) {
console.error("Manifest is invalid:");
for (const error of result.errors) {
console.error(` - ${error}`);
}
process.exit(1);
}
// Deep scan for warnings and blocking issues
const report = scanApp(result.data);
if (!report.passed) {
console.error("Scan found blocking issues:");
for (const error of report.errors) {
console.error(` - ${error}`);
}
}
if (report.warnings.length > 0) {
console.warn("Warnings (non-blocking):");
for (const warning of report.warnings) {
console.warn(` - ${warning}`);
}
}API Reference
Main Entry (@aimform/apps)
| Export | Description |
|--------|-------------|
| defineApp(manifest) | Create a complete app manifest |
| defineEntityType(name, properties?) | Define an entity type (returns builder or declaration) |
| defineTool(config) | Define a tool/action |
| defineOnboardingStep(step) | Define an onboarding step |
| defineProfileSection(section) | Define a profile section |
| defineScopes(read, write?) | Define scoped permissions |
| OAUTH2_PROVIDERS | Constants for known OAuth2 providers |
| InferAppManifest<T> | Infer the type of a specific manifest |
Server Entry (@aimform/apps/server)
| Export | Description |
|--------|-------------|
| validateManifest(input) | Validate unknown input against the manifest schema |
| scanApp(manifest) | Deep scan a manifest for warnings and errors |
| aimformAppManifestSchema | Raw Zod schema for custom validation logic |
Client Entry (@aimform/apps/client)
Type-only exports for browser-safe imports. All manifest types are re-exported:
AimformAppManifest, ToolDeclaration, EntityTypeDeclaration, AppScopes, etc.
Types
AimformAppManifest
The top-level manifest type. See the Quick Start example for full shape.
EntityTypeDeclaration
interface EntityTypeDeclaration {
name: string;
properties: PropertyDefinition[];
}ToolDeclaration
interface ToolDeclaration {
name: string;
description?: string;
riskTier: "auto" | "confirm" | "blocked";
exposedToAi: boolean;
inputSchema?: Record<string, unknown>;
}OnboardingStep
interface OnboardingStep {
id: string;
type: "oauth_connect" | "api_key_form" | "permission_review" | "custom";
title: string;
description?: string;
provider?: string;
apiKeyLabel?: string;
apiKeyHelpText?: string;
customUrl?: string;
}ProfileSection
interface ProfileSection {
id: string;
type: "view" | "document" | "space";
title: string;
entityType?: string;
fields?: string[];
defaultSort?: string;
documentType?: string;
}AuthConfig
interface AuthConfig {
type: "oauth2" | "api_key" | "none";
provider?: string;
}EntryPointConfig
interface EntryPointConfig {
type: "mcp" | "sandboxed_code" | "external_call";
url?: string;
}License
MIT
