ObjectStackObjectStack

AI Agents

The two platform agents (ask and build), how skills extend them, and the shape of an AgentSchema record

AI Agents

Part of the AI module. In the open edition, agents, tools, and skills are typed metadata: you author them as source with defineAgent / defineSkill / defineTool from the open @objectstack/spec/ai package, and an external AI client (Claude, Cursor, a local model — any MCP client) reaches your objects, queries, and business Actions through @objectstack/mcp (BYO-AI), all governed by RLS. This page describes that metadata and the AgentSchema it validates against. Like the rest of your metadata, you don't hand-write agents — Claude Code authors them from the objectstack-ai skill; you os validate and test the tools in the Console (the loop).

ObjectOS — the in-product chat runtime. The authoring format on this page is open. The in-product chat runtime that runs agents for end users — the two platform agents ask and build, all in-product chat, and the /api/v1/ai/* chat endpoints — ships in ObjectOS, not in the open-source framework, which uses @objectstack/mcp (BYO-AI) instead. The next two sections describe that ObjectOS runtime.

Per ADR-0063 the ObjectOS runtime ships exactly two platform agents, bound by the surface the user is in — the user never picks from a roster:

AgentSurfaceDoesEdition
askdata consoleRead / query / explore records + run the business actions the app exposes. RLS-bounded.ObjectOS
buildStudioAuthor metadata (objects, fields, views, flows) via plan → draft → verify → publish.ObjectOS

Within the ObjectOS runtime there is no per-turn intent classifier and no agent dropdown: the surface binds the agent (data console → ask, Studio → build). A build-shaped request that reaches ask is declined and redirected to the Builder, never silently re-routed. (For data query and source-mode authoring, the open edition ships neither agent and uses @objectstack/mcp (BYO-AI) instead — see the callout in the AI Overview.)

Connect your AI (BYO-AI over MCP)

Step-by-step client setup (Claude Code, Claude Desktop, .mcp.json, API keys) with verification and troubleshooting lives in Connect an MCP Client. The summary below covers the architecture.

Every deployment serves MCP at /api/v1/mcp by default (a core platform capability — set OS_MCP_SERVER_ENABLED=false to opt out), with two authentication tracks: The in-product entry point is the Setup → Connect an Agent page: per-client connect snippets, the portable SKILL.md download (GET /api/v1/mcp/skill), and API-key minting.

  • OAuth 2.1 (interactive clients — recommended). Each deployment is its own spec-compliant authorization server: the endpoint publishes .well-known/oauth-protected-resource / .well-known/oauth-authorization-server discovery metadata, clients self-register via Dynamic Client Registration (RFC 7591) and run an authorization-code + PKCE flow. Nothing is pre-registered with Anthropic or any central service, so self-hosted and private deployments work out of the box. You authorize the client in the browser; it then acts as an agent on your behalf (principalKind: 'agent'), its data access bounded by the intersection of the consent scopes and your own permissions/RLS — it can never exceed either. For object CRUD the consent scopes (data:read, data:write) are a real ceiling, not just a tool-family filter: they narrow the exposed tools and cap what the agent can do at the data layer — a data:read token can never write a record even where you could. Business actions are gated differently (#2849): actions:execute + the action's declared capabilities decide which actions the agent may invoke, and only actions the app author explicitly exposed to AI (ai: { exposed: true }) are invokable at all — but an invoked action's body runs as trusted application code with the app's full data authority, not under the agent's data ceiling. The author's AI opt-in, not the data layer, is the boundary for what actions can do.
  • API key (headless). Mint a key (POST /api/v1/keys, shown once) and send it as x-api-key — for CI, scripts, and agents without a browser.

Per client:

ClientHow to connect
Claude Codeclaude mcp add --transport http objectstack https://<your-deployment>/api/v1/mcp — a browser login opens on first use. Headless alternative: add --header "x-api-key: osk_...".
Claude DesktopSettings → Connectors → Add custom connector → paste the MCP URL → sign in when prompted.
claude.ai (web)Settings → Connectors → Add custom connector → paste the MCP URL.
Other MCP clientsAny client implementing MCP authorization discovers the flow automatically; header-based clients can send the API key instead.

Private / intranet deployments. OAuth requires HTTPS (localhost is exempt, per OAuth 2.1). Local clients — Claude Code and Claude Desktop — run on your machine, so they can reach an intranet-only deployment (an internal CA works). claude.ai web connectors connect from Anthropic's servers, so they additionally need the MCP endpoint reachable from the public internet — a network decision, not a platform switch. On a plain-HTTP non-localhost deployment the OAuth track stays dark and the endpoint is API-key-only, fail-closed.

You extend the platform with skills, not agents

"Agent skill" ≠ "authoring skill." The *.skill.ts / defineSkill files here are agent skills — runtime capability bundles the ask / build agents consume. They are a different thing from the SKILL.md authoring skills on the AI Skills System page, which teach a coding assistant to write your metadata and never run. Same word, two layers.

*.agent.ts is closed to third parties — the agent metadata type is allowRuntimeCreate:false, allowOrgOverride:false, reserved for the two platform agents and platform-owned subagents (ADR-0063 §2). To give the ask agent a new capability you author an agent skill (*.skill.ts) whose tools reference your Actions / Flows / queries; it then attaches to ask. Every skill declares surface: 'ask' | 'build' | 'both', and an agent's tool set is the union of its surface-compatible skills' tools — there is no global fall-through, so a skill reaches an agent only when their surfaces match (ADR-0064). Both surface:'ask' and surface:'build' skills run only where the in-UI AI runtime exists — ObjectOS. On the open framework there is no in-product agent to attach them to; author capability as Actions / Flows and reach it through @objectstack/mcp instead.

The shape of an agent

An agent is typed metadata validated by AgentSchema — exported, together with the defineAgent factory, from the open @objectstack/spec/ai package (the platform's own ask / build records use exactly these fields):

FieldMeaning
surface'ask' | 'build' — the product surface this agent binds (ADR-0063 §1)
roleFree-text persona string (e.g. "Senior Support Engineer") — not an enum
instructionsSystem prompt / prime directives
modelProvider + model config (provider: openai | azure_openai | anthropic | local)
skillsSkill names to attach (the primary Agent → Skill → Tool capability model)
toolsDirect tool references { type, name, description }type is action | flow | query | vector_search; name points at an existing Action/Flow/query
knowledgeRAG access: { topics: string[], indexes: string[] }

There is no type field and no fixed agent "type" taxonomy — behaviour comes from persona, instructions, skills, and tools. There are no triggers / schedule fields on an agent; drive agents from Flows/Workflows, or — on the ObjectOS runtime — via the in-product chat endpoint (/api/v1/ai/*). On the open-source framework, invoke the underlying Actions/Flows through @objectstack/mcp.

Agent tools are references to existing Actions, Flows, or queries — you do not define ad-hoc tool names with inline parameter schemas here. See Actions as Tools for how an Action becomes LLM-callable.

The agent definitions below illustrate agent anatomy — how an AgentSchema record is shaped (that schema and the defineAgent factory are part of the open @objectstack/spec/ai package). Note that the agent metadata type is allowRuntimeCreate:false / allowOrgOverride:false (ADR-0063 §2): the cloud / Enterprise in-product runtime honors only the two platform agents ask and build, so you do not ship a custom agent to that runtime — you extend it by authoring a skill (open metadata) that attaches to ask. In the open edition, express the same capability as Actions / Flows plus the skills/tools its references point at, and reach it through @objectstack/mcp (BYO-AI).

Sales Assistant Agent

import { defineAgent } from '@objectstack/spec/ai';

export const SalesAssistantAgent = defineAgent({
  name: 'sales_assistant',
  label: 'Sales Assistant',
  role: 'Sales Development Assistant',

  instructions: `You are a sales assistant AI.

Your responsibilities:
1. Qualify incoming leads (BANT criteria)
2. Suggest next best actions
3. Draft personalized emails
4. Analyze win/loss patterns

Always be professional and data-driven.`,

  model: {
    provider: 'openai',
    model: 'gpt-4',
    temperature: 0.7,
    maxTokens: 2000,
  },

  // References to Actions/Flows exposed as tools (see "Actions as Tools").
  tools: [
    { type: 'flow', name: 'analyze_lead', description: 'Analyze a lead and provide a qualification score' },
    { type: 'flow', name: 'suggest_next_action', description: 'Suggest the next best action for an opportunity' },
    { type: 'action', name: 'generate_email', description: 'Generate a personalized email template' },
  ],

  // RAG access: topics to recruit knowledge from + vector store indexes.
  knowledge: {
    topics: ['sales-playbook', 'leads', 'opportunities'],
    indexes: ['sales_docs'],
  },
});

Customer Service Agent

export const ServiceAgent = defineAgent({
  name: 'service_agent',
  label: 'Customer Service Agent',
  role: 'Customer Service Specialist',

  instructions: `You are a customer service AI agent.

Your responsibilities:
1. Triage incoming cases
2. Suggest relevant knowledge articles
3. Draft response templates
4. Escalate critical issues

Always be empathetic and solution-focused.`,

  model: {
    provider: 'openai',
    model: 'gpt-4',
    temperature: 0.5,
    maxTokens: 1500,
  },

  // `search_knowledge` is provided by the Knowledge Protocol tool, not declared inline.
  tools: [
    { type: 'flow', name: 'triage_case', description: 'Analyze a case and assign priority' },
    { type: 'action', name: 'generate_response', description: 'Generate a customer response' },
  ],

  knowledge: {
    topics: ['support-kb', 'cases'],
    indexes: ['support_docs'],
  },
});

Lead Enrichment Agent

export const LeadEnrichmentAgent = defineAgent({
  name: 'lead_enrichment',
  label: 'Lead Enrichment Agent',
  role: 'Data Enrichment Worker',

  instructions: `You enrich lead records with additional data.

Tasks:
1. Look up company information
2. Enrich contact details
3. Add firmographic data
4. Research technology stack

Use reputable data sources.`,

  model: {
    provider: 'openai',
    model: 'gpt-3.5-turbo',
    temperature: 0.3,
    maxTokens: 1000,
  },

  tools: [
    { type: 'flow', name: 'lookup_company', description: 'Look up company information' },
    { type: 'flow', name: 'enrich_contact', description: 'Enrich contact information' },
  ],
});

To run enrichment when a lead is created or on a schedule, trigger this agent from a Flow or Workflow — agents themselves carry no triggers/schedule fields.

Revenue Intelligence Agent

export const RevenueIntelligenceAgent = defineAgent({
  name: 'revenue_intelligence',
  label: 'Revenue Intelligence Agent',
  role: 'Revenue Operations Analyst',

  instructions: `You analyze sales data and provide insights.

Responsibilities:
1. Analyze pipeline health
2. Identify at-risk deals
3. Summarize trends
4. Generate executive summaries

Use the data tools to query records and aggregate metrics.`,

  model: {
    provider: 'openai',
    model: 'gpt-4',
    temperature: 0.2,
    maxTokens: 3000,
  },

  // Built-in data tools (query_records / get_record / aggregate_data) are
  // available to agents automatically once registered — see "Natural Language Queries".
  tools: [
    { type: 'flow', name: 'analyze_pipeline', description: 'Analyze sales pipeline health' },
  ],

  knowledge: {
    topics: ['opportunities', 'pipeline'],
    indexes: ['sales_docs'],
  },
});

See also

On this page