SDK AI Agent Guide
The @portaidentity/sdk/agent entrypoint enables AI agents (LLMs with function-calling) to manage Porta infrastructure through structured tool definitions. This is designed for MCP servers, OpenAI function-calling, LangChain tools, and similar agent frameworks.
Overview
The agent layer provides:
- Tool Definitions — Structured descriptions of all SDK operations, compatible with LLM function-calling schemas
- Tool Executor — A dispatcher that maps tool names to SDK method calls with parameter validation
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ AI Agent │────▶│ SDK Agent │────▶│ SDK Client │
│ (LLM) │ │ Layer │ │ (transport) │
└──────────────┘ └──────────────┘ └──────────────┘
function call executeTool() domain.method()Quick Start
import { createPortaClient } from '@portaidentity/sdk';
import { createNodeTransport, createClientCredentialsAuth } from '@portaidentity/sdk/node';
import { getToolDefinitions, executeTool } from '@portaidentity/sdk/agent';
// 1. Create an authenticated client
const porta = createPortaClient({
transport: createNodeTransport({
baseUrl: 'https://porta.local:3443/api/admin',
auth: createClientCredentialsAuth({
tokenEndpoint: 'https://porta.local:3443/super-admin/token',
clientId: process.env.PORTA_CLIENT_ID!,
clientSecret: process.env.PORTA_CLIENT_SECRET!,
}),
}),
});
// 2. Get tool definitions for the AI model
const tools = getToolDefinitions();
// → 75 tool definitions with name, description, parameters, returns
// 3. Execute a tool from AI agent output
const result = await executeTool(porta, 'organizations.list', { pageSize: 10 });Tool Definitions
Each tool definition includes:
| Field | Type | Description |
|---|---|---|
name | string | Unique tool name (e.g., organizations.create) |
description | string | Human-readable description for the LLM |
parameters | ToolParameter[] | Input parameters with types and descriptions |
returns | string | Description of the return value |
sideEffects | boolean | Whether the tool modifies state |
prerequisites | string[] | What must exist before calling this tool |
relatedTools | string[] | Tools commonly used together |
Example Tool Definition
{
name: 'organizations.create',
description: 'Create a new organization (tenant) in Porta',
parameters: [
{ name: 'name', type: 'string', required: true, description: 'Organization display name' },
{ name: 'slug', type: 'string', required: false, description: 'URL-safe identifier (auto-generated if omitted)' },
{ name: 'defaultLocale', type: 'string', required: false, description: 'Default locale (e.g., "en")' },
],
returns: 'Organization object with id, name, slug, status, timestamps',
sideEffects: true,
prerequisites: ['Authenticated as porta-admin'],
relatedTools: ['applications.create', 'organizations.list'],
}Listing All Tools
const tools = getToolDefinitions();
console.log(`Available tools: ${tools.length}`);
// Group by domain
const domains = new Set(tools.map((t) => t.name.split('.')[0]));
console.log('Domains:', [...domains]);
// → organizations, applications, clients, users, roles, permissions, ...Executing Tools
The executeTool() function dispatches a tool call to the correct SDK domain method:
import { executeTool } from '@portaidentity/sdk/agent';
// The AI agent says: "call organizations.create with { name: 'Acme Corp' }"
const result = await executeTool(porta, 'organizations.create', {
name: 'Acme Corp',
slug: 'acme',
});
// result is the Organization object returned by porta.organizations.create()Error Handling
const result = await executeTool(porta, toolName, toolArgs);
if (!result.success) {
// The agent layer deliberately returns a minimal error and does not expose
// transport, server, or validation details to the model.
return { success: false, error: result.error };
}
return { success: true, data: result.data };MCP Server Integration
To build an MCP server that exposes Porta tools:
import { McpServer } from '@anthropic-ai/mcp-sdk';
import { createPortaClient } from '@portaidentity/sdk';
import { createNodeTransport, createClientCredentialsAuth } from '@portaidentity/sdk/node';
import { getToolDefinitions, executeTool } from '@portaidentity/sdk/agent';
const porta = createPortaClient({
transport: createNodeTransport({
baseUrl: process.env.PORTA_API_URL!,
auth: createClientCredentialsAuth({
tokenEndpoint: process.env.PORTA_TOKEN_ENDPOINT!,
clientId: process.env.PORTA_CLIENT_ID!,
clientSecret: process.env.PORTA_CLIENT_SECRET!,
}),
}),
});
const server = new McpServer({ name: 'porta-admin', version: '1.0.0' });
// Register all Porta tools
for (const tool of getToolDefinitions()) {
server.tool(
tool.name,
tool.description,
Object.fromEntries(
tool.parameters.map((p) => [p.name, { type: p.type, description: p.description }]),
),
async (args) => {
const result = await executeTool(porta, tool.name, args);
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
},
);
}
await server.start();OpenAI Function-Calling Integration
import { getToolDefinitions, executeTool } from '@portaidentity/sdk/agent';
// Convert to OpenAI function-calling format
const openAiTools = getToolDefinitions().map((tool) => ({
type: 'function' as const,
function: {
name: tool.name,
description: tool.description,
parameters: {
type: 'object',
properties: Object.fromEntries(
tool.parameters.map((p) => [
p.name,
{
type: p.type,
description: p.description,
},
]),
),
required: tool.parameters.filter((p) => p.required).map((p) => p.name),
},
},
}));
// Use with OpenAI API
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: 'List all organizations' }],
tools: openAiTools,
});
// Execute tool calls from the response
for (const toolCall of response.choices[0].message.tool_calls ?? []) {
const args = JSON.parse(toolCall.function.arguments);
const result = await executeTool(porta, toolCall.function.name, args);
console.log(`${toolCall.function.name}:`, result);
}Available Tool Domains
| Domain | Tools | Description |
|---|---|---|
organizations | 7 | Org CRUD, status lifecycle, delete |
applications | 6 | App CRUD, module deletion |
clients | 7 | Client CRUD, secrets |
users | 12 | User CRUD, invite, password, status |
roles | 5 | Application roles and assigned-permission reads |
permissions | 4 | Application permission CRUD |
userRoles | 4 | User-role assignments and effective permissions |
customClaims | 6 | Claim definitions and user claim values |
config | 3 | System configuration |
keys | 3 | Signing key management |
audit | 1 | Audit log |
stats | 2 | Dashboard statistics |
sessions | 3 | Session management |
bulk | 2 | Bulk status operations |
twoFactor | 7 | 2FA administration and policy |
exports | 1 | Selective portability manifest export |
imports | 2 | Portability preview and atomic import |
Portability tools are exports.manifest, imports.preview, and imports.apply. An agent must preview the exact manifest and obtain operator approval before apply. A successful apply can return new confidential-client secrets once; do not include those values in model messages, logs, or retained tool history.
RBAC Tool Examples
Role and permission tools always take an appId, which keeps each RBAC definition inside its application boundary. User-role tools additionally take the organization and selected user IDs.
await executeTool(porta, 'permissions.create', {
appId: 'application-id',
input: { name: 'Edit deals', slug: 'sales:deal:write' },
});
await executeTool(porta, 'userRoles.assign', {
orgId: 'organization-id',
userId: 'user-id',
roleIds: ['role-id'],
});roles.delete, permissions.delete, and userRoles.remove return a reauthenticationRequired flag. If it is true, discard the current admin authentication and authenticate again before sending another command. These agent tools perform the requested operation directly, so the calling agent must obtain user confirmation before destructive calls.
Security Considerations
- The agent operates with the same permissions as the SDK client's authentication. Use a dedicated service account with minimal required permissions.
- Side-effect awareness: Tools with
sideEffects: truemodify state. AI agents should confirm destructive actions such asorganizations.deletewith the user. - Rate limiting: The Porta API enforces rate limits. Agent loops that make many rapid requests may be throttled.
- No credential exposure: Never pass credentials through tool parameters. Authentication is handled by the transport layer.
See Also
- SDK Overview — Installation, quick start, full API reference
- SDK Node.js Usage — Server-side setup with auth providers
- SDK Browser Usage — Browser/SPA integration