Skip to content

Skill Development

This guide walks through building a Komand skill from project creation to packaging.

  • .NET 10 SDKdownload
  • Komand SDK NuGet packageKomand.Sdk
  • A Komand instance for integration testing (local or managed)

Using the SDK template:

Terminal window
dotnet new komand-skill -n MySkill
cd MySkill

Or manually:

Terminal window
dotnet new classlib -n MySkill
cd MySkill
dotnet add package Komand.Sdk

The template scaffolds:

MySkill/
├── MySkill.csproj
├── MySkill.cs # Skill implementation
├── Models/
│ ├── MySkillInput.cs # Input record
│ └── MySkillOutput.cs # Output record
├── Tests/
│ └── MySkillTests.cs # Unit tests
└── skill.json # Marketplace metadata

Every skill implements IKomandSkill and exposes a SkillDefinition:

public class ContactLookupSkill : IKomandSkill
{
public SkillDefinition Definition => new()
{
SkillId = "my-contact-lookup",
Name = "Contact Lookup",
Description = "Search contacts by name or email",
Version = "1.0.0",
PublisherId = "your-publisher-id",
InputSchema = JsonSchema.FromType<ContactSearchInput>(),
OutputSchema = JsonSchema.FromType<ContactSearchOutput>(),
RequiredPermissions = ["crm:read"],
DeclaredToolAccess = ["database"]
};
}
FieldRequiredDescription
SkillIdYesUnique identifier, lowercase with hyphens
NameYesHuman-readable display name
DescriptionNoWhat the skill does (shown to agents and users)
VersionYesSemantic version (major.minor.patch)
PublisherIdYesYour publisher ID from the marketplace
InputSchemaNoJSON Schema for the input the skill accepts
OutputSchemaNoJSON Schema for the output the skill returns
RequiredPermissionsNoPermissions the skill needs to function (defaults to empty)
DeclaredToolAccessNoPlatform tools the skill may invoke

Use C# records for immutable, schema-friendly data models:

public record ContactSearchInput(
string Query,
int MaxResults = 10
);
public record ContactSearchOutput(
IReadOnlyList<Contact> Contacts,
int TotalCount
);
public record Contact(
string Name,
string Email,
string? Phone
);

The SDK generates JSON Schemas from these types automatically via JsonSchema.FromType<T>(). The platform validates all input against the schema before your code runs.

public async Task<SkillResult> ExecuteAsync(
SkillContext context,
CancellationToken ct)
{
var input = context.GetInput<ContactSearchInput>();
// Validate business rules beyond schema validation
if (string.IsNullOrWhiteSpace(input.Query))
return SkillResult.Failure("Search query is required");
if (input.MaxResults is < 1 or > 100)
return SkillResult.Failure("MaxResults must be between 1 and 100");
// Execute the actual work
var contacts = await SearchContactsAsync(input.Query, input.MaxResults, ct);
return SkillResult.Success(new ContactSearchOutput(
Contacts: contacts,
TotalCount: contacts.Count
));
}

The SkillContext provides runtime services to your skill:

MemberDescription
GetInput<T>()Deserialise and return the validated input
GetCredential(name)Retrieve a credential from the secure vault
LoggerStructured logger scoped to this execution
CancellationTokenToken that fires when the execution timeout is reached
MethodWhen to use
SkillResult.Success(data)Execution succeeded, return output
SkillResult.Failure(message)Execution failed, return error message

Skills never receive raw API keys. Instead, credentials are stored in the platform’s secure vault and accessed through SkillContext:

public async Task<SkillResult> ExecuteAsync(
SkillContext context,
CancellationToken ct)
{
var apiKey = context.GetCredential("salesforce-api-key")
?? return SkillResult.Failure("Salesforce API key not configured");
var client = new SalesforceClient(apiKey);
// ...
}

Users configure credentials when they install the skill. The platform injects them at runtime without exposing them to skill code directly.

The SDK provides SkillTestContext for unit testing without a running platform:

using Komand.Sdk.Testing;
public class ContactLookupSkillTests
{
private readonly ContactLookupSkill _skill = new();
[Fact]
public async Task Execute_WithValidQuery_ReturnsContacts()
{
// Arrange
var context = SkillTestContext.Create(
new ContactSearchInput("alice"));
// Act
var result = await _skill.ExecuteAsync(context, CancellationToken.None);
// Assert
result.IsSuccess.Should().BeTrue();
var output = result.GetOutput<ContactSearchOutput>();
output.Contacts.Should().NotBeEmpty();
}
[Fact]
public async Task Execute_WithEmptyQuery_ReturnsFailure()
{
// Arrange
var context = SkillTestContext.Create(
new ContactSearchInput(""));
// Act
var result = await _skill.ExecuteAsync(context, CancellationToken.None);
// Assert
result.IsSuccess.Should().BeFalse();
result.Error.Should().Contain("required");
}
[Theory]
[InlineData(0)]
[InlineData(101)]
public async Task Execute_WithInvalidMaxResults_ReturnsFailure(int maxResults)
{
var context = SkillTestContext.Create(
new ContactSearchInput("alice", maxResults));
var result = await _skill.ExecuteAsync(context, CancellationToken.None);
result.IsSuccess.Should().BeFalse();
}
}
var context = SkillTestContext.Create(
new ContactSearchInput("alice"),
credentials: new Dictionary<string, string>
{
["salesforce-api-key"] = "test-key-for-unit-tests"
});
Terminal window
dotnet komand skill pack

This creates a .komandskill package containing:

  • Compiled skill assembly
  • skill.json metadata
  • Input/output schemas
  • README and license
Terminal window
dotnet komand skill publish --api-key YOUR_PUBLISHER_KEY

See the Marketplace page for details on the review process and verification.

Define → Implement → Test → Package → Publish → Review → Verified
  1. Define — skill contract with input/output schemas and permissions
  2. Implement — business logic using SkillContext services
  3. Test — unit and integration tests using SDK test utilities
  4. Package — bundle into a .komandskill distributable
  5. Publish — upload to the Komand marketplace
  6. Review — security and quality review by the Komand team
  7. Verified — approved skills receive a verified badge and signature

Skills run in a sandboxed environment. This is a core security guarantee — not optional:

ConstraintDetail
File systemNo access outside the skill’s temp directory
NetworkNo outbound access unless network:outbound is declared
CPU / memoryHard limits enforced by the container/WASM runtime
TimeoutSkills exceeding their timeout are terminated (max 30 minutes)
Input validationAll inputs validated against the declared schema before execution
Credential isolationSkills receive scoped credentials, never raw vault access

Open-source agent frameworks have well-documented security issues — skills performing data exfiltration, prompt injection vulnerabilities, and uncontrolled resource usage. Komand’s sandbox model prevents these attack vectors by design.

Common permissions your skill can request:

PermissionScope
network:outboundMake HTTP requests to external services
crm:readRead CRM data (contacts, deals, activities)
crm:writeCreate or modify CRM data
calendar:readRead calendar events
calendar:writeCreate or modify calendar events
email:sendSend emails through the platform
storage:readRead files from skill storage
storage:writeWrite files to skill storage
databaseAccess the platform database tools

Request only the permissions your skill actually needs. Excessive permission requests are flagged during review.