diff --git a/.github/workflows/early-access.yml b/.github/workflows/early-access.yml
new file mode 100644
index 0000000..f57adef
--- /dev/null
+++ b/.github/workflows/early-access.yml
@@ -0,0 +1,37 @@
+name: early-access
+
+on:
+ workflow_dispatch:
+ workflow_call:
+
+jobs:
+ publish-snapshot:
+ name: Publish Snapshot
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup Java
+ uses: actions/setup-java@v4
+ with:
+ java-version: 21
+ distribution: 'zulu'
+ server-id: central
+ server-username: MAVEN_USERNAME
+ server-password: MAVEN_CENTRAL_TOKEN
+ gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
+ gpg-passphrase: MAVEN_GPG_PASSPHRASE
+ cache: maven
+
+ - name: Publish snapshot to Maven Central
+ env:
+ MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
+ MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
+ MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
+ run: |
+ export GPG_TTY=$(tty)
+ mvn -Pdist -B --file pom.xml deploy
diff --git a/.github/workflows/main-build.yml b/.github/workflows/main-build.yml
index b3291f8..75e9146 100644
--- a/.github/workflows/main-build.yml
+++ b/.github/workflows/main-build.yml
@@ -19,26 +19,16 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - name: Checkout Wanaku Main project
- uses: actions/checkout@v4
+ - uses: actions/checkout@v4
with:
- repository: wanaku-ai/wanaku
+ ref: ${{ github.ref_name }}
persist-credentials: false
- ref: main
- path: wanaku
+ fetch-depth: 0
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: maven
- - name: Build Wanaku Main Project
- run: mvn -DskipTests clean install
- working-directory: ${{ github.workspace }}/wanaku
- - uses: actions/checkout@v4
- with:
- ref: main
- persist-credentials: false
- fetch-depth: 0
- name: Build with Maven
run: mvn -B package --file pom.xml
diff --git a/.github/workflows/pr-builds.yml b/.github/workflows/pr-builds.yml
index 18e0d6a..122656c 100644
--- a/.github/workflows/pr-builds.yml
+++ b/.github/workflows/pr-builds.yml
@@ -41,26 +41,15 @@ jobs:
fail-fast: true
steps:
- - name: Checkout Wanaku Main project
- uses: actions/checkout@v4
+ - uses: actions/checkout@v4
with:
- repository: wanaku-ai/wanaku
persist-credentials: false
- ref: main
- path: wanaku
+ fetch-depth: 0
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: maven
- - name: Build Wanaku Main Project
- run: mvn -DskipTests clean install
- working-directory: ${{ github.workspace }}/wanaku
- - uses: actions/checkout@v4
- with:
- ref: main
- persist-credentials: false
- fetch-depth: 0
- name: Build with Maven
run: mvn -B package --file pom.xml
diff --git a/.github/workflows/trigger-early-access.yml b/.github/workflows/trigger-early-access.yml
new file mode 100644
index 0000000..af399f0
--- /dev/null
+++ b/.github/workflows/trigger-early-access.yml
@@ -0,0 +1,22 @@
+name: Trigger Early Access
+
+on:
+ schedule:
+ # Run every 2 days at 00:00 UTC
+ - cron: '0 0 */2 * *'
+
+jobs:
+ trigger:
+ name: Trigger Early Access Workflow
+ runs-on: ubuntu-latest
+ steps:
+ - name: Trigger early-access workflow
+ uses: actions/github-script@v7
+ with:
+ script: |
+ await github.rest.actions.createWorkflowDispatch({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ workflow_id: 'early-access.yml',
+ ref: 'main'
+ })
diff --git a/capabilities-api/pom.xml b/capabilities-api/pom.xml
new file mode 100644
index 0000000..f21f87b
--- /dev/null
+++ b/capabilities-api/pom.xml
@@ -0,0 +1,22 @@
+
+
Implementations of this interface can be registered with a {@link RegistrationManager} + * to receive notifications when registration-related operations occur, such as successful + * registration, deregistration attempts, or periodic health check pings.
+ * + *Callbacks are invoked synchronously after the corresponding operation completes. + * If a callback throws an exception, it will be logged but will not prevent other + * registered callbacks from executing.
+ * + *Usage example:
+ *{@code
+ * RegistrationManager manager = ...;
+ * manager.addCallBack(new RegistrationCallback() {
+ * @Override
+ * public void onRegistration(RegistrationManager manager) {
+ * // Handle successful registration
+ * }
+ *
+ * @Override
+ * public void onPing(RegistrationManager manager, int status) {
+ * // Handle ping result
+ * }
+ *
+ * @Override
+ * public void onDeregistration(RegistrationManager manager, int status) {
+ * // Handle deregistration result
+ * }
+ * });
+ * }
+ *
+ * @see RegistrationManager#addCallBack(DiscoveryCallback)
+ */
+public interface DiscoveryCallback {
+
+ /**
+ * Invoked after a ping operation is sent to the discovery service.
+ * This callback is triggered when the service sends a heartbeat to indicate
+ * it is still active and operational.
+ *
+ * @param manager the {@link RegistrationManager} that performed the ping operation
+ * @param target the {@link ServiceTarget} that was pinged
+ * @param status the HTTP status code returned by the ping operation
+ * (200 indicates success, other values indicate various failure conditions)
+ */
+ void onPing(RegistrationManager manager, ServiceTarget target, int status);
+
+ /**
+ * Invoked after the service has been successfully registered with the discovery service.
+ * This callback is only called when registration completes successfully.
+ *
+ * @param manager the {@link RegistrationManager} that performed the registration
+ * @param target the {@link ServiceTarget} that was registered
+ */
+ void onRegistration(RegistrationManager manager, ServiceTarget target);
+
+ /**
+ * Invoked after a deregistration attempt is made with the discovery service.
+ * This callback is triggered when the service is being shut down or explicitly
+ * removed from the registry.
+ *
+ * @param manager the {@link RegistrationManager} that performed the deregistration operation
+ * @param target the {@link ServiceTarget} that was deregistered
+ * @param status the HTTP status code returned by the deregistration operation
+ * (200 indicates success, other values indicate various failure conditions)
+ */
+ void onDeregistration(RegistrationManager manager, ServiceTarget target, int status);
+}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/discovery/RegistrationManager.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/discovery/RegistrationManager.java
new file mode 100644
index 0000000..34090bd
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/discovery/RegistrationManager.java
@@ -0,0 +1,55 @@
+package ai.wanaku.capabilities.sdk.api.discovery;
+
+/**
+ * The `RegistrationManager` interface defines the contract for a class responsible for managing the lifecycle
+ * registration of a capability service within the Wanaku ecosystem.
+ *
+ * Implementations of this interface handle the processes of registering, deregistering, + * and monitoring the status of a service's registration. It acts as the primary interface + * for a capability service to interact with the Wanaku registration mechanism.
+ */ +public interface RegistrationManager { + + /** + * Registers the capability service with Wanaku + */ + void register(); + + /** + * Deregisters the capability service from the Wanaku registration system. + * This method notifies the registry that the service is no longer available + * or is shutting down, allowing for proper cleanup and resource release. + */ + void deregister(); + + /** + * Sends a "ping" or heartbeat signal to the Wanaku registration system. + * This method is used to periodically inform the registry that the service is still active + * and operational, preventing its registration from expiring due to inactivity. + */ + void ping(); + + /** + * Notifies the Wanaku registration system that the last attempted operation (tool call + * or resource acquisition) from this service failed. + * + * @param reason A descriptive string explaining the reason for the failure. + * This information can be used for logging, debugging, or alerting purposes + * by the registration system. + */ + void lastAsFail(String reason); + + /** + * Notifies the Wanaku registration system that the last attempted operation (tool call + * or resource acquisition) from this service was successful. + * This method can be used to update the service's status within the registry, + * indicating its continued health and availability. + */ + void lastAsSuccessful(); + + /** + * Adds a callback to be run after some operations have executed + * @param callback the callback to add + */ + void addCallBack(DiscoveryCallback callback); +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ConfigurationNotFoundException.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ConfigurationNotFoundException.java new file mode 100644 index 0000000..fc6e4c2 --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ConfigurationNotFoundException.java @@ -0,0 +1,64 @@ +package ai.wanaku.capabilities.sdk.api.exceptions; + +/** + * This exception can be thrown if a configuration is expected in some way, but it is not found. + */ +public class ConfigurationNotFoundException extends WanakuException { + + /** + * Constructs a new instance of this exception without a message or cause. + */ + public ConfigurationNotFoundException() {} + + /** + * Constructs a new instance of this exception with the specified detail message. + * + * @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method) + */ + public ConfigurationNotFoundException(String message) { + super(message); + } + + /** + * Constructs a new instance of this exception with the specified detail message and cause. + * + * @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method) + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method) + */ + public ConfigurationNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new instance of this exception with the specified cause. + * + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method) + */ + public ConfigurationNotFoundException(Throwable cause) { + super(cause); + } + + /** + * Constructs a new instance of this exception with the specified detail message, cause, + * enable suppression and writable stack trace. + * + * @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method) + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method) + * @param enableSuppression whether suppression is enabled + * @param writableStackTrace whether stack traces should be writtable + */ + public ConfigurationNotFoundException( + String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + /** + * Creates a new instance of this exception for the given tool name. + * + * @param toolName the name of the tool that was not found + * @return a new instance of this exception with a message indicating that the tool was not found + */ + public static ConfigurationNotFoundException forName(String toolName) { + return new ConfigurationNotFoundException(String.format("Tool %s not found", toolName)); + } +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/DataStoreResourceNotFoundException.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/DataStoreResourceNotFoundException.java new file mode 100644 index 0000000..d4065a4 --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/DataStoreResourceNotFoundException.java @@ -0,0 +1,68 @@ +package ai.wanaku.capabilities.sdk.api.exceptions; + +/** + * This exception is thrown when attempting to retrieve an item from the DataStore that does not exist. + * + * @see WanakuException + */ +public class DataStoreResourceNotFoundException extends WanakuException { + + /** + * Constructs an instance of the exception with no detail message or cause. + */ + public DataStoreResourceNotFoundException() { + super(); + } + + /** + * Constructs an instance of the exception with a specified detail message. + * + * @param message the detail message for this exception + */ + public DataStoreResourceNotFoundException(String message) { + super(message); + } + + /** + * Constructs an instance of the exception with a specified cause and a detail message. + * + * @param message the detail message for this exception + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + */ + public DataStoreResourceNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs an instance of the exception with a specified cause. + * + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + */ + public DataStoreResourceNotFoundException(Throwable cause) { + super(cause); + } + + /** + * Constructs an instance of the exception with a specified detail message, cause, suppression enabled or disabled, + * and writable stack trace enabled or disabled. + * + * @param message the detail message for this exception + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + * @param enableSuppression whether suppression is enabled or disabled + * @param writableStackTrace whether the stack trace should be writable + */ + public DataStoreResourceNotFoundException( + String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + /** + * Returns a new instance of this exception with a formatted string indicating that a data store resource could not be found. + * + * @param resourceName the name of the missing data store resource + * @return a new instance of this exception for the specified resource name + */ + public static DataStoreResourceNotFoundException forName(String resourceName) { + return new DataStoreResourceNotFoundException(String.format("DataStore resource %s not found", resourceName)); + } +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/EntityAlreadyExistsException.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/EntityAlreadyExistsException.java new file mode 100644 index 0000000..7675376 --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/EntityAlreadyExistsException.java @@ -0,0 +1,71 @@ +package ai.wanaku.capabilities.sdk.api.exceptions; + +/** + * This exception is thrown when an attempt is made to create an entity that already exists. + * + * This custom exception extends {@link WanakuException} and provides a + * clear indication of when a requested entity cannot be created because it already exists. + * It is mapped to HTTP status code 409 (Conflict) by the router. + */ +public class EntityAlreadyExistsException extends WanakuException { + + /** + * Default constructor for the exception, providing no additional information. + */ + public EntityAlreadyExistsException() { + super(); + } + + /** + * Constructor that allows specifying a custom error message for the exception. + * + * @param message The detailed message describing which entity already exists. + */ + public EntityAlreadyExistsException(String message) { + super(message); + } + + /** + * Constructor that provides both a custom error message and an underlying cause for the exception. + * + * @param message The detailed message describing which entity already exists. + * @param cause The root cause of the exception, providing additional context. + */ + public EntityAlreadyExistsException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor that specifies an underlying cause for the exception without a custom error message. + * + * @param cause The root cause of the exception, providing additional context. + */ + public EntityAlreadyExistsException(Throwable cause) { + super(cause); + } + + /** + * Constructor that allows specifying both a custom error message and an underlying cause for the exception, + * along with options to enable suppression or writable stack trace. + * + * @param message The detailed message describing which entity already exists. + * @param cause The root cause of the exception, providing additional context. + * @param enableSuppression Whether to allow suppressing this exception during exception propagation. + * @param writableStackTrace Whether to include a stack trace with this exception. + */ + public EntityAlreadyExistsException( + String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + /** + * Factory method that creates a new instance of the exception for a given entity name, + * providing a pre-formatted error message indicating that the entity already exists. + * + * @param entityName The name or identifier of the entity that already exists. + * @return A new instance of the {@link EntityAlreadyExistsException} class with a custom error message. + */ + public static EntityAlreadyExistsException forName(String entityName) { + return new EntityAlreadyExistsException(String.format("Entity %s already exists", entityName)); + } +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/InvalidResponseTypeException.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/InvalidResponseTypeException.java new file mode 100644 index 0000000..eb8579b --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/InvalidResponseTypeException.java @@ -0,0 +1,71 @@ +package ai.wanaku.capabilities.sdk.api.exceptions; + +/** + * This exception can be thrown if a response of certain type is expected, but it came differently. + * + * Typically used to indicate that an API or service returned a response in an unexpected format, + * such as an Integer object instead of String, or vice versa. + */ +public class InvalidResponseTypeException extends WanakuException { + + /** + * Constructs a new instance of this exception without a message or cause. + */ + public InvalidResponseTypeException() {} + + /** + * Constructs a new instance of this exception with the specified detail message. + * + * @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method) + */ + public InvalidResponseTypeException(String message) { + super(message); + } + + /** + * Constructs a new instance of this exception with the specified detail message and cause. + * + * @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method) + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method) + */ + public InvalidResponseTypeException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new instance of this exception with the specified cause. + * + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method) + */ + public InvalidResponseTypeException(Throwable cause) { + super(cause); + } + + /** + * Constructs a new instance of this exception with the specified detail message, cause, + * enable suppression and writable stack trace. + * + * @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method) + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method) + * @param enableSuppression whether suppression is enabled + * @param writableStackTrace whether stack traces should be writtable + */ + public InvalidResponseTypeException( + String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + /** + * Creates a new instance of this exception for the given tool name. + * + * Note that this method is incorrectly named - it should probably be renamed to something like + * {@code forResponseType} or similar. This will throw an exception with a message indicating + * that the response type was not found. + * + * @param toolName the name of the tool (or response type) that was expected but not found + * @return a new instance of this exception with a message indicating that the response type was not found + */ + public static InvalidResponseTypeException forName(String toolName) { + return new InvalidResponseTypeException(String.format("Tool %s not found", toolName)); + } +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/NamespaceNotFoundException.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/NamespaceNotFoundException.java new file mode 100644 index 0000000..8739c37 --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/NamespaceNotFoundException.java @@ -0,0 +1,67 @@ +package ai.wanaku.capabilities.sdk.api.exceptions; + +/** + * This exception is thrown when a specified namespace cannot be located or does not exist. + * @see WanakuException + */ +public class NamespaceNotFoundException extends WanakuException { + + /** + * Constructs an instance of the exception with no detail message or cause. + */ + public NamespaceNotFoundException() { + super(); + } + + /** + * Constructs an instance of the exception with a specified detail message. + * + * @param message the detail message for this exception + */ + public NamespaceNotFoundException(String message) { + super(message); + } + + /** + * Constructs an instance of the exception with a specified cause and a detail message. + * + * @param message the detail message for this exception + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + */ + public NamespaceNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs an instance of the exception with a specified cause. + * + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + */ + public NamespaceNotFoundException(Throwable cause) { + super(cause); + } + + /** + * Constructs an instance of the exception with a specified detail message, cause, suppression enabled or disabled, + * and writable stack trace enabled or disabled. + * + * @param message the detail message for this exception + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + * @param enableSuppression whether or not suppression is enabled or disabled + * @param writableStackTrace whether or not the stack trace should be writable + */ + public NamespaceNotFoundException( + String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + /** + * Returns a new instance of this exception with a formatted string indicating that a namespace could not be found. + * + * @param namespaceId the identifier of the missing namespace + * @return a new instance of this exception for the specified namespace identifier + */ + public static NamespaceNotFoundException forId(String namespaceId) { + return new NamespaceNotFoundException(String.format("Namespace %s not found", namespaceId)); + } +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/NonConvertableResponseException.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/NonConvertableResponseException.java new file mode 100644 index 0000000..0d3074c --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/NonConvertableResponseException.java @@ -0,0 +1,71 @@ +package ai.wanaku.capabilities.sdk.api.exceptions; + +/** + * This exception can be thrown if a response is not convertible to the required type. + * + * Typically used when attempting to deserialize or convert a response from a service or API, + * but encountering issues due to mismatched types, formats, or other errors. + */ +public class NonConvertableResponseException extends WanakuException { + + /** + * Constructs a new instance of this exception without a message or cause. + */ + public NonConvertableResponseException() {} + + /** + * Constructs a new instance of this exception with the specified detail message. + * + * @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method) + */ + public NonConvertableResponseException(String message) { + super(message); + } + + /** + * Constructs a new instance of this exception with the specified detail message and cause. + * + * @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method) + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method) + */ + public NonConvertableResponseException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new instance of this exception with the specified cause. + * + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method) + */ + public NonConvertableResponseException(Throwable cause) { + super(cause); + } + + /** + * Constructs a new instance of this exception with the specified detail message, cause, + * enable suppression and writable stack trace. + * + * @param message the detail message (which is saved for later retrieval by the {@link #getMessage()} method) + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method) + * @param enableSuppression whether or not suppression is enabled + * @param writableStackTrace whether or not stack traces should be writtable + */ + public NonConvertableResponseException( + String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + /** + * Creates a new instance of this exception for the given tool name. + * + * Note that this method is incorrectly named - it should probably be renamed to something like + * {@code forResponseType} or similar. This will throw an exception with a message indicating + * that the response was not convertible to the required type. + * + * @param toolName the name of the tool (or response) that could not be converted to the required type + * @return a new instance of this exception with a message indicating that the response was not convertible + */ + public static NonConvertableResponseException forName(String toolName) { + return new NonConvertableResponseException(String.format("Tool %s not found", toolName)); + } +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/PromptNotFoundException.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/PromptNotFoundException.java new file mode 100644 index 0000000..9f21fbd --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/PromptNotFoundException.java @@ -0,0 +1,15 @@ +package ai.wanaku.capabilities.sdk.api.exceptions; + +/** + * Exception thrown when a prompt cannot be found. + */ +public class PromptNotFoundException extends WanakuException { + /** + * Constructs a new PromptNotFoundException with the specified prompt name. + * + * @param promptName the name of the prompt that was not found + */ + public PromptNotFoundException(String promptName) { + super(String.format("Prompt '%s' not found", promptName)); + } +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ResourceNotFoundException.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ResourceNotFoundException.java new file mode 100644 index 0000000..61b009a --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ResourceNotFoundException.java @@ -0,0 +1,68 @@ +package ai.wanaku.capabilities.sdk.api.exceptions; + +/** + * This exception can be thrown if the resource is not found. + * + * This custom exception extends {@link WanakuException} and provides a + * clear indication of when a requested resource cannot be located or accessed. + */ +public class ResourceNotFoundException extends WanakuException { + + /** + * Default constructor for the exception, providing no additional information. + */ + public ResourceNotFoundException() {} + + /** + * Constructor that allows specifying a custom error message for the exception. + * + * @param message The detailed message describing why the resource was not found. + */ + public ResourceNotFoundException(String message) { + super(message); + } + + /** + * Constructor that provides both a custom error message and an underlying cause for the exception. + * + * @param message The detailed message describing why the resource was not found. + * @param cause The root cause of the exception, providing additional context. + */ + public ResourceNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor that specifies an underlying cause for the exception without a custom error message. + * + * @param cause The root cause of the exception, providing additional context. + */ + public ResourceNotFoundException(Throwable cause) { + super(cause); + } + + /** + * Constructor that allows specifying both a custom error message and an underlying cause for the exception, + * along with options to enable suppression or writable stack trace. + * + * @param message The detailed message describing why the resource was not found. + * @param cause The root cause of the exception, providing additional context. + * @param enableSuppression Whether to allow suppressing this exception during exception propagation. + * @param writableStackTrace Whether to include a stack trace with this exception. + */ + public ResourceNotFoundException( + String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + /** + * Factory method that creates a new instance of the exception for a given resource name, + * providing a pre-formatted error message indicating that the resource was not found. + * + * @param resourceName The name or identifier of the resource that was not found. + * @return A new instance of the {@link ResourceNotFoundException} class with a custom error message. + */ + public static ResourceNotFoundException forName(String resourceName) { + return new ResourceNotFoundException(String.format("Resource %s not found", resourceName)); + } +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ServiceNotFoundException.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ServiceNotFoundException.java new file mode 100644 index 0000000..c21646e --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ServiceNotFoundException.java @@ -0,0 +1,68 @@ +package ai.wanaku.capabilities.sdk.api.exceptions; + +/** + * This exception is thrown when a specified service cannot be located or does not exist. + * + * @see WanakuException + */ +public class ServiceNotFoundException extends WanakuException { + + /** + * Constructs an instance of the exception with no detail message or cause. + */ + public ServiceNotFoundException() { + super(); + } + + /** + * Constructs an instance of the exception with a specified detail message. + * + * @param message the detail message for this exception + */ + public ServiceNotFoundException(String message) { + super(message); + } + + /** + * Constructs an instance of the exception with a specified cause and a detail message. + * + * @param message the detail message for this exception + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + */ + public ServiceNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs an instance of the exception with a specified cause. + * + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + */ + public ServiceNotFoundException(Throwable cause) { + super(cause); + } + + /** + * Constructs an instance of the exception with a specified detail message, cause, suppression enabled or disabled, + * and writable stack trace enabled or disabled. + * + * @param message the detail message for this exception + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + * @param enableSuppression whether suppression is enabled or disabled + * @param writableStackTrace whether the stack trace should be writable + */ + public ServiceNotFoundException( + String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + /** + * Returns a new instance of this exception with a formatted string indicating that a service could not be found. + * + * @param serviceName the name of the missing service + * @return a new instance of this exception for the specified service name + */ + public static ServiceNotFoundException forName(String serviceName) { + return new ServiceNotFoundException(String.format("Service %s not found", serviceName)); + } +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ServiceUnavailableException.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ServiceUnavailableException.java new file mode 100644 index 0000000..e7ee711 --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ServiceUnavailableException.java @@ -0,0 +1,79 @@ +package ai.wanaku.capabilities.sdk.api.exceptions; + +/** + * This exception is thrown when a specified service is not available. + * + * @see WanakuException + */ +public class ServiceUnavailableException extends WanakuException { + + /** + * Constructs an instance of the exception with no detail message or cause. + */ + public ServiceUnavailableException() { + super(); + } + + /** + * Constructs an instance of the exception with a specified detail message. + * + * @param message the detail message for this exception + */ + public ServiceUnavailableException(String message) { + super(message); + } + + /** + * Constructs an instance of the exception with a specified cause and a detail message. + * + * @param message the detail message for this exception + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + */ + public ServiceUnavailableException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs an instance of the exception with a specified cause. + * + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + */ + public ServiceUnavailableException(Throwable cause) { + super(cause); + } + + /** + * Constructs an instance of the exception with a specified detail message, cause, suppression enabled or disabled, + * and writable stack trace enabled or disabled. + * + * @param message the detail message for this exception + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + * @param enableSuppression whether suppression is enabled or disabled + * @param writableStackTrace whether the stack trace should be writable + */ + public ServiceUnavailableException( + String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + /** + * Returns a new instance of this exception with a formatted string indicating that a service was not available. + * + * @param serviceName the name of the missing service + * @return a new instance of this exception for the specified service name + */ + public static ServiceUnavailableException forName(String serviceName) { + return new ServiceUnavailableException(String.format("Service is not available at %s", serviceName)); + } + + /** + * Returns a new instance of this exception with a formatted string indicating that a service was not available at + * a specific address. + * + * @param address the address of the missing service + * @return a new instance of this exception for the specified service name + */ + public static ServiceUnavailableException forAddress(String address) { + return new ServiceUnavailableException(String.format("Service is not available at the address %s", address)); + } +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ToolNotFoundException.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ToolNotFoundException.java new file mode 100644 index 0000000..3cd678f --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/ToolNotFoundException.java @@ -0,0 +1,67 @@ +package ai.wanaku.capabilities.sdk.api.exceptions; + +/** + * This exception is thrown when a specified tool cannot be located or does not exist. + * @see WanakuException + */ +public class ToolNotFoundException extends WanakuException { + + /** + * Constructs an instance of the exception with no detail message or cause. + */ + public ToolNotFoundException() { + super(); + } + + /** + * Constructs an instance of the exception with a specified detail message. + * + * @param message the detail message for this exception + */ + public ToolNotFoundException(String message) { + super(message); + } + + /** + * Constructs an instance of the exception with a specified cause and a detail message. + * + * @param message the detail message for this exception + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + */ + public ToolNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs an instance of the exception with a specified cause. + * + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + */ + public ToolNotFoundException(Throwable cause) { + super(cause); + } + + /** + * Constructs an instance of the exception with a specified detail message, cause, suppression enabled or disabled, + * and writable stack trace enabled or disabled. + * + * @param message the detail message for this exception + * @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method) + * @param enableSuppression whether or not suppression is enabled or disabled + * @param writableStackTrace whether or not the stack trace should be writable + */ + public ToolNotFoundException( + String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + /** + * Returns a new instance of this exception with a formatted string indicating that a tool could not be found. + * + * @param toolName the name of the missing tool + * @return a new instance of this exception for the specified tool name + */ + public static ToolNotFoundException forName(String toolName) { + return new ToolNotFoundException(String.format("Tool %s not found", toolName)); + } +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/WanakuException.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/WanakuException.java new file mode 100644 index 0000000..d75b2a3 --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/exceptions/WanakuException.java @@ -0,0 +1,59 @@ +package ai.wanaku.capabilities.sdk.api.exceptions; + +/** + * This is the base exception class. + * + * The {@link WanakuException} class extends the built-in {@link RuntimeException} to provide + * a custom exception hierarchy. + */ +public class WanakuException extends RuntimeException { + + /** + * Constructs an instance of this exception with no detail message or cause. + * + * This constructor provides a basic implementation for instances where no additional context is needed. + */ + public WanakuException() { + super(); + } + + /** + * Constructs an instance of this exception with the specified detail message and without a cause. + * + * @param message The message describing the reason for this exception being thrown. + */ + public WanakuException(String message) { + super(message); + } + + /** + * Constructs an instance of this exception with the specified detail message and underlying cause. + * + * @param message The message describing the reason for this exception being thrown. + * @param cause The root cause that led to this exception being thrown. + */ + public WanakuException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs an instance of this exception with the specified underlying cause but without a detail message. + * + * @param cause The root cause that led to this exception being thrown. + */ + public WanakuException(Throwable cause) { + super(cause); + } + + /** + * Constructs an instance of this exception with the specified detail message and underlying cause, along with suppression flags. + * + * @param message The message describing the reason for this exception being thrown. + * @param cause The root cause that led to this exception being thrown. + * @param enableSuppression Whether to suppress the reporting of this exception's cause. + * @param writableStackTrace Whether to make the stack trace associated with this exception writable. + */ + public WanakuException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/AudioContent.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/AudioContent.java new file mode 100644 index 0000000..c1ccf00 --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/AudioContent.java @@ -0,0 +1,71 @@ +package ai.wanaku.capabilities.sdk.api.types; + +import java.util.Objects; + +/** + * Represents audio content in a prompt message. + * Audio is provided as base64-encoded data with a MIME type. + */ +public class AudioContent implements PromptContent { + private static final String TYPE = "audio"; + + /** + * Base64-encoded audio data. + */ + private String data; + + /** + * MIME type of the audio (e.g., "audio/mp3", "audio/wav", "audio/ogg"). + */ + private String mimeType; + + public AudioContent() {} + + public AudioContent(String data, String mimeType) { + this.data = data; + this.mimeType = mimeType; + } + + @Override + public String getType() { + return TYPE; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMimeType() { + return mimeType; + } + + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + AudioContent that = (AudioContent) o; + return Objects.equals(data, that.data) && Objects.equals(mimeType, that.mimeType); + } + + @Override + public int hashCode() { + return Objects.hash(data, mimeType); + } + + @Override + public String toString() { + return "AudioContent{" + "type='" + + TYPE + '\'' + ", mimeType='" + + mimeType + '\'' + ", dataLength=" + + (data != null ? data.length() : 0) + '}'; + } +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/CallableReference.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/CallableReference.java new file mode 100644 index 0000000..fb885c4 --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/CallableReference.java @@ -0,0 +1,63 @@ +package ai.wanaku.capabilities.sdk.api.types; + +/** + * Represents a reference to a callable entity that can be invoked with arguments. + *+ * A callable reference is an abstraction for any object or function that can be invoked, + * potentially with arguments. This interface provides metadata about the callable entity + * including its name, description, type, input schema, and namespace. + *
+ * This interface is implemented by various tool and capability reference types to provide + * a common contract for invocable entities in the Wanaku system. + * + * @see ToolReference + * @see RemoteToolReference + */ +public interface CallableReference { + /** + * Gets the name of this callable reference. + * + * @return the name of this callable reference + */ + String getName(); + + /** + * Gets the human-readable description of this callable reference. + *
+ * The description provides information about what this callable does, + * its purpose, and how it should be used. + * + * @return a human-readable description of this callable reference + */ + String getDescription(); + + /** + * Gets the type associated with this callable reference. + *
+ * The type typically indicates the category or implementation mechanism + * of the callable (e.g., "http", "function", "tool"). + * + * @return the type associated with this callable reference + */ + String getType(); + + /** + * Gets the input schema for this callable reference. + *
+ * The input schema describes the expected structure, format, and validation + * rules for arguments that can be passed to this callable. + * + * @return the input schema for this callable reference + */ + InputSchema getInputSchema(); + + /** + * Gets the namespace in which this callable reference is registered. + *
+ * The namespace provides logical grouping and isolation for callable + * references within the Wanaku system. + * + * @return the namespace identifier + */ + String getNamespace(); +} diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/DataStore.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/DataStore.java new file mode 100644 index 0000000..4766e1b --- /dev/null +++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/DataStore.java @@ -0,0 +1,182 @@ +package ai.wanaku.capabilities.sdk.api.types; + +import java.util.Objects; + +/** + * Entity representing a data store entry for persisting arbitrary binary or text data. + * + *
DataStore provides a generic storage mechanism for files, configurations, or any other + * content that needs to be stored and retrieved through the Wanaku API. The data is typically + * stored as Base64-encoded content to safely handle binary files and special characters. + * + *
Usage Example: + *
{@code
+ * // Creating a new data store entry
+ * DataStore dataStore = new DataStore();
+ * dataStore.setName("config.yaml");
+ * dataStore.setData(Base64.getEncoder().encodeToString(fileBytes));
+ * dataStore.addLabel("environment", "production");
+ *
+ * // The ID will be automatically assigned by the persistence layer
+ * }
+ *
+ * Storage Format: + *
The data should typically be Base64-encoded when storing binary files + * or content with special characters. For example: + *
{@code
+ * byte[] fileBytes = Files.readAllBytes(path);
+ * dataStore.setData(Base64.getEncoder().encodeToString(fileBytes));
+ * }
+ *
+ * @param data the content to store (typically Base64-encoded)
+ */
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ /**
+ * Compares this DataStore to another object for equality.
+ * Two DataStore instances are considered equal if they have the same id, name, data, and labels.
+ *
+ * @param o the object to compare with
+ * @return {@code true} if the objects are equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DataStore dataStore = (DataStore) o;
+ return Objects.equals(id, dataStore.id)
+ && Objects.equals(name, dataStore.name)
+ && Objects.equals(data, dataStore.data)
+ && Objects.equals(getLabels(), dataStore.getLabels());
+ }
+
+ /**
+ * Returns a hash code value for this DataStore.
+ * The hash code is computed based on the id, name, data, and labels fields.
+ *
+ * @return a hash code value for this object
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, name, data, getLabels());
+ }
+
+ /**
+ * Returns a string representation of this DataStore.
+ * The string includes the id, name, data, and labels fields.
+ *
+ * @return a string representation of this DataStore
+ */
+ @Override
+ public String toString() {
+ return "DataStore{"
+ + "id='" + id + '\''
+ + ", name='" + name + '\''
+ + ", data='" + data + '\''
+ + ", labels=" + getLabels()
+ + '}';
+ }
+}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/EmbeddedResource.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/EmbeddedResource.java
new file mode 100644
index 0000000..4e0f635
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/EmbeddedResource.java
@@ -0,0 +1,54 @@
+package ai.wanaku.capabilities.sdk.api.types;
+
+import java.util.Objects;
+
+/**
+ * Represents an embedded resource in a prompt message.
+ * This allows prompts to reference other resources.
+ */
+public class EmbeddedResource implements PromptContent {
+ private static final String TYPE = "resource";
+
+ /**
+ * The resource being embedded.
+ */
+ private ResourceReference resource;
+
+ public EmbeddedResource() {}
+
+ public EmbeddedResource(ResourceReference resource) {
+ this.resource = resource;
+ }
+
+ @Override
+ public String getType() {
+ return TYPE;
+ }
+
+ public ResourceReference getResource() {
+ return resource;
+ }
+
+ public void setResource(ResourceReference resource) {
+ this.resource = resource;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ EmbeddedResource that = (EmbeddedResource) o;
+ return Objects.equals(resource, that.resource);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(resource);
+ }
+
+ @Override
+ public String toString() {
+ return "EmbeddedResource{" + "type='" + TYPE + '\'' + ", resource=" + resource + '}';
+ }
+}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/ForwardReference.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/ForwardReference.java
new file mode 100644
index 0000000..2d77c73
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/ForwardReference.java
@@ -0,0 +1,109 @@
+package ai.wanaku.capabilities.sdk.api.types;
+
+import java.util.Objects;
+
+/**
+ * Represents a reference to a forward service.
+ *
+ * This class holds information about the address of the forward service,
+ * allowing it to be easily accessed and modified.
+ */
+public class ForwardReference implements WanakuEntity
+ * Labels provide a flexible mechanism for attaching key-value metadata to entities,
+ * enabling categorization, filtering, and organization of resources within the
+ * Wanaku system.
+ *
+ * @param
+ * This immutable record is used throughout the Wanaku system to uniquely identify
+ * capabilities, tools, and resources by combining their name with their namespace.
+ * The combination of name and namespace provides a fully qualified identifier.
+ *
+ * @param name the entity name
+ * @param namespace the namespace in which the entity resides
+ */
+public record NameNamespacePair(String name, String namespace) {}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/Namespace.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/Namespace.java
new file mode 100644
index 0000000..d828a8f
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/Namespace.java
@@ -0,0 +1,73 @@
+package ai.wanaku.capabilities.sdk.api.types;
+
+/**
+ * Represents a namespace within the Wanaku system.
+ *
+ * Namespaces provide logical grouping and isolation for capabilities, tools, and resources.
+ * Each namespace has a unique identifier, a human-readable name, and a path that defines
+ * its location within the namespace hierarchy.
+ */
+public class Namespace extends LabelsAwareEntity
+ * The path defines the location of this namespace within the namespace hierarchy
+ * and is used for namespace resolution and organization.
+ *
+ * @return the namespace path
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * Sets the hierarchical path of this namespace.
+ *
+ * @param path the namespace path to set
+ */
+ public void setPath(String path) {
+ this.path = path;
+ }
+}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/PromptContent.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/PromptContent.java
new file mode 100644
index 0000000..e71af13
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/PromptContent.java
@@ -0,0 +1,27 @@
+package ai.wanaku.capabilities.sdk.api.types;
+
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+/**
+ * Base interface for different types of prompt content.
+ * According to MCP specification, content can be:
+ * - TextContent
+ * - ImageContent
+ * - AudioContent
+ * - EmbeddedResource
+ */
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
+@JsonSubTypes({
+ @JsonSubTypes.Type(value = TextContent.class, name = "text"),
+ @JsonSubTypes.Type(value = ImageContent.class, name = "image"),
+ @JsonSubTypes.Type(value = AudioContent.class, name = "audio"),
+ @JsonSubTypes.Type(value = EmbeddedResource.class, name = "resource")
+})
+public interface PromptContent {
+ /**
+ * Gets the type of content.
+ * @return The content type (e.g., "text", "image", "audio", "resource")
+ */
+ String getType();
+}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/PromptMessage.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/PromptMessage.java
new file mode 100644
index 0000000..cc58a5f
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/PromptMessage.java
@@ -0,0 +1,70 @@
+package ai.wanaku.capabilities.sdk.api.types;
+
+import java.util.Objects;
+
+/**
+ * Represents a message in an MCP prompt.
+ * Each message has a role and content according to the MCP specification.
+ */
+public class PromptMessage {
+ /**
+ * The role of the message sender.
+ * Valid values: "user", "assistant"
+ */
+ private String role;
+
+ /**
+ * The content of the message.
+ * This is a polymorphic field that can be one of:
+ * - TextContent
+ * - ImageContent
+ * - AudioContent
+ * - EmbeddedResource
+ */
+ private PromptContent content;
+
+ public PromptMessage() {}
+
+ public PromptMessage(String role, PromptContent content) {
+ this.role = role;
+ this.content = content;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ public PromptContent getContent() {
+ return content;
+ }
+
+ public void setContent(PromptContent content) {
+ this.content = content;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PromptMessage that = (PromptMessage) o;
+ return Objects.equals(role, that.role) && Objects.equals(content, that.content);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(role, content);
+ }
+
+ @Override
+ public String toString() {
+ return "PromptMessage{" + "role='" + role + '\'' + ", content=" + content + '}';
+ }
+}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/PromptReference.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/PromptReference.java
new file mode 100644
index 0000000..6317555
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/PromptReference.java
@@ -0,0 +1,217 @@
+package ai.wanaku.capabilities.sdk.api.types;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a prompt reference in the MCP protocol.
+ * Prompts are reusable templates that can leverage multiple tools and provide
+ * example interactions for LLMs.
+ */
+public class PromptReference implements WanakuEntity
+ * A remote tool reference contains metadata about a tool that is provided by an
+ * external service or remote MCP server, rather than being locally implemented.
+ * This class is used when tools are discovered through forward proxies or when
+ * integrating with remote capability providers.
+ *
+ * Remote tool references can be converted to standard {@link ToolReference}
+ * instances using the {@link #asToolReference(RemoteToolReference)} method,
+ * which assigns a placeholder URI to indicate the remote nature of the tool.
+ *
+ * @see ToolReference
+ * @see CallableReference
+ */
+public class RemoteToolReference extends LabelsAwareEntity
+ * This utility method creates a {@link ToolReference} from a {@link RemoteToolReference}
+ * by copying all metadata and assigning a placeholder URI {@code "
+ * The configuration URI points to the location where non-sensitive
+ * configuration data for this resource is stored.
+ *
+ * @return the configuration URI, or {@code null} if not configured
+ */
+ public String getConfigurationURI() {
+ return configurationURI;
+ }
+
+ /**
+ * Sets the URI location for the resource configuration.
+ *
+ * @param configurationURI the configuration URI to set
+ */
+ public void setConfigurationURI(String configurationURI) {
+ this.configurationURI = configurationURI;
+ }
+
+ /**
+ * Gets the URI location for the secrets associated with this resource.
+ *
+ * The secrets URI points to the location where sensitive data such as
+ * credentials, API keys, or tokens for this resource are stored.
+ *
+ * @return the secrets URI, or {@code null} if not configured
+ */
+ public String getSecretsURI() {
+ return secretsURI;
+ }
+
+ /**
+ * Sets the URI location for the secrets associated with this resource.
+ *
+ * @param secretsURI the secrets URI to set
+ */
+ public void setSecretsURI(String secretsURI) {
+ this.secretsURI = secretsURI;
+ }
+
+ /**
+ * Gets the namespace in which this resource is registered.
+ *
+ * @return the namespace identifier
+ */
+ public String getNamespace() {
+ return namespace;
+ }
+
+ /**
+ * Sets the namespace in which this resource is registered.
+ *
+ * @param namespace the namespace identifier to set
+ */
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ /**
+ * A nested class representing a parameter of the resource.
+ */
+ public static class Param {
+ /**
+ * The name of the parameter (e.g. "key", "value").
+ */
+ private String name;
+
+ /**
+ * The value of the parameter.
+ */
+ private String value;
+
+ /**
+ * Gets the name of the parameter.
+ *
+ * @return the parameter name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the name of the parameter.
+ *
+ * @param name The new name of the parameter.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Gets the value of the parameter.
+ *
+ * @return the parameter value
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * Sets the value of the parameter.
+ *
+ * @param value The new value of the parameter.
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceReference{" + "id='"
+ + id + '\'' + ", location='"
+ + location + '\'' + ", type='"
+ + type + '\'' + ", name='"
+ + name + '\'' + ", description='"
+ + description + '\'' + ", mimeType='"
+ + mimeType + '\'' + ", params="
+ + params + ", configurationURI='"
+ + configurationURI + '\'' + ", secretsURI='"
+ + secretsURI + '\'' + ", namespace='"
+ + namespace + '\'' + ", labels='"
+ + this.getLabels() + '\'' + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ResourceReference that = (ResourceReference) o;
+ return Objects.equals(id, that.id)
+ && Objects.equals(location, that.location)
+ && Objects.equals(type, that.type)
+ && Objects.equals(name, that.name)
+ && Objects.equals(description, that.description)
+ && Objects.equals(mimeType, that.mimeType)
+ && Objects.equals(params, that.params)
+ && Objects.equals(configurationURI, that.configurationURI)
+ && Objects.equals(secretsURI, that.secretsURI)
+ && Objects.equals(namespace, that.namespace)
+ && Objects.equals(this.getLabels(), that.getLabels());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ id,
+ location,
+ type,
+ name,
+ description,
+ mimeType,
+ params,
+ configurationURI,
+ secretsURI,
+ namespace,
+ this.getLabels());
+ }
+}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/TextContent.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/TextContent.java
new file mode 100644
index 0000000..f394135
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/TextContent.java
@@ -0,0 +1,54 @@
+package ai.wanaku.capabilities.sdk.api.types;
+
+import java.util.Objects;
+
+/**
+ * Represents text content in a prompt message.
+ * This is the most common content type for prompts.
+ */
+public class TextContent implements PromptContent {
+ private static final String TYPE = "text";
+
+ /**
+ * The text content.
+ */
+ private String text;
+
+ public TextContent() {}
+
+ public TextContent(String text) {
+ this.text = text;
+ }
+
+ @Override
+ public String getType() {
+ return TYPE;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ TextContent that = (TextContent) o;
+ return Objects.equals(text, that.text);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(text);
+ }
+
+ @Override
+ public String toString() {
+ return "TextContent{" + "type='" + TYPE + '\'' + ", text='" + text + '\'' + '}';
+ }
+}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/ToolReference.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/ToolReference.java
new file mode 100644
index 0000000..663750b
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/ToolReference.java
@@ -0,0 +1,229 @@
+package ai.wanaku.capabilities.sdk.api.types;
+
+import java.util.Objects;
+
+/**
+ * This class represents a reference to a tool with various attributes such as name, description, URI, type, and input schema.
+ */
+public class ToolReference extends LabelsAwareEntity
+ * The configuration URI points to the location where non-sensitive
+ * configuration data for this tool is stored.
+ *
+ * @return the configuration URI, or {@code null} if not configured
+ */
+ public String getConfigurationURI() {
+ return configurationURI;
+ }
+
+ /**
+ * Sets the URI location for the tool configuration.
+ *
+ * @param configurationURI the configuration URI to set
+ */
+ public void setConfigurationURI(String configurationURI) {
+ this.configurationURI = configurationURI;
+ }
+
+ /**
+ * Gets the URI location for the secrets associated with this tool.
+ *
+ * The secrets URI points to the location where sensitive data such as
+ * credentials, API keys, or tokens for this tool are stored.
+ *
+ * @return the secrets URI, or {@code null} if not configured
+ */
+ public String getSecretsURI() {
+ return secretsURI;
+ }
+
+ /**
+ * Sets the URI location for the secrets associated with this tool.
+ *
+ * @param secretsURI the secrets URI to set
+ */
+ public void setSecretsURI(String secretsURI) {
+ this.secretsURI = secretsURI;
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ToolReference that = (ToolReference) o;
+ return Objects.equals(id, that.id)
+ && Objects.equals(name, that.name)
+ && Objects.equals(description, that.description)
+ && Objects.equals(uri, that.uri)
+ && Objects.equals(type, that.type)
+ && Objects.equals(inputSchema, that.inputSchema)
+ && Objects.equals(configurationURI, that.configurationURI)
+ && Objects.equals(secretsURI, that.secretsURI)
+ && Objects.equals(this.getLabels(), that.getLabels());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ id, name, description, uri, type, inputSchema, configurationURI, secretsURI, this.getLabels());
+ }
+
+ @Override
+ public String toString() {
+ return "ToolReference{" + "id='"
+ + id + '\'' + ", name='"
+ + name + '\'' + ", description='"
+ + description + '\'' + ", uri='"
+ + uri + '\'' + ", type='"
+ + type + '\'' + ", inputSchema="
+ + inputSchema + ", configurations="
+ + configurationURI + ", secrets="
+ + secretsURI + ", labels='"
+ + this.getLabels() + '\'' + '}';
+ }
+}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/WanakuEntity.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/WanakuEntity.java
new file mode 100644
index 0000000..57a6140
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/WanakuEntity.java
@@ -0,0 +1,27 @@
+package ai.wanaku.capabilities.sdk.api.types;
+
+/**
+ * Base interface for all Wanaku entity types.
+ *
+ * This interface provides a common contract for entities that require identity management
+ * within the Wanaku system. All entities implementing this interface must provide an
+ * identifier of type {@code K}.
+ *
+ * @param
+ * This immutable record encapsulates error information that is returned to clients
+ * when API operations fail or encounter errors. It provides a simple, standardized
+ * way to communicate error details across the Wanaku system.
+ *
+ * The error message should be descriptive and provide enough context for clients
+ * to understand what went wrong and potentially take corrective action.
+ *
+ * @param message the error message describing what went wrong, or {@code null} if no message is available
+ */
+public record WanakuError(String message) {
+
+ /**
+ * Default constructor that creates a {@link WanakuError} with no message.
+ *
+ * This constructor is provided for cases where an error indicator is needed
+ * but no specific message is available at the time of creation.
+ */
+ public WanakuError() {
+ this(null);
+ }
+}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/WanakuResponse.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/WanakuResponse.java
new file mode 100644
index 0000000..e563ccd
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/WanakuResponse.java
@@ -0,0 +1,36 @@
+package ai.wanaku.capabilities.sdk.api.types;
+
+/**
+ * Represents a Wanaku response, which can contain either an error or data.
+ *
+ * @param
+ * This entity tracks service lifecycle information including when the service was last
+ * observed, its current active status, and a history of state transitions. Activity records
+ * are used by the service discovery mechanism to monitor service health and availability.
+ */
+public class ActivityRecord implements WanakuEntity
+ * The states list maintains a history of service state changes, allowing
+ * tracking of service health and availability over time.
+ *
+ * @return the list of service states
+ */
+ public List
+ * This utility class defines constant messages that represent common service states
+ * within the Wanaku discovery mechanism. These messages are used to communicate
+ * service health and availability status.
+ */
+public final class StandardMessages {
+ /**
+ * Message indicating that a service is operating normally and is healthy.
+ */
+ public static final String HEALTHY = "healthy";
+
+ /**
+ * Message indicating that a service has not been observed within the expected timeframe
+ * and may be unavailable.
+ */
+ public static final String MISSING_IN_ACTION = "missing in action";
+
+ /**
+ * Message indicating that a service has been automatically deregistered due to inactivity.
+ */
+ public static final String AUTO_DEREGISTRATION = "inactive due to service auto-deregistration";
+
+ private StandardMessages() {}
+}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/discovery/package-info.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/discovery/package-info.java
new file mode 100644
index 0000000..d28c6f5
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/discovery/package-info.java
@@ -0,0 +1,18 @@
+/**
+ * Service discovery and health monitoring types.
+ *
+ * This package contains types used for service discovery, health monitoring,
+ * and activity tracking within the Wanaku system. These types enable the
+ * router to discover available capability providers and monitor their
+ * health and availability over time.
+ *
+ * Key types include:
+ *
+ * This interface defines the contract for payloads that bundle together a primary
+ * payload object (such as a tool or resource reference) along with associated
+ * configuration and secrets data needed for provisioning and deployment.
+ *
+ * Implementations of this interface are used during capability provisioning to
+ * ensure that all necessary configuration and sensitive data are transmitted
+ * together with the capability reference.
+ *
+ * @param
+ * This typically contains the reference to a tool, resource, or other capability
+ * being provisioned.
+ *
+ * @return the payload object
+ */
+ T getPayload();
+
+ /**
+ * Gets the configuration data associated with this payload.
+ *
+ * Configuration data contains non-sensitive settings and parameters required
+ * for provisioning the capability. The format and content are specific to the
+ * type of capability being provisioned.
+ *
+ * @return the configuration data as a string, or {@code null} if no configuration is provided
+ */
+ String getConfigurationData();
+
+ /**
+ * Gets the secrets data associated with this payload.
+ *
+ * Secrets data contains sensitive information such as credentials, API keys,
+ * or tokens required for provisioning the capability. This data should be
+ * handled securely and not logged or persisted in plain text.
+ *
+ * @return the secrets data as a string, or {@code null} if no secrets are provided
+ */
+ String getSecretsData();
+}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/io/ResourcePayload.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/io/ResourcePayload.java
new file mode 100644
index 0000000..b433232
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/io/ResourcePayload.java
@@ -0,0 +1,80 @@
+package ai.wanaku.capabilities.sdk.api.types.io;
+
+import ai.wanaku.capabilities.sdk.api.types.ResourceReference;
+
+/**
+ * Payload for provisioning resource capabilities with associated configuration and secrets.
+ *
+ * This class bundles a {@link ResourceReference} with its corresponding configuration
+ * and secrets data, facilitating the complete provisioning of a resource capability
+ * within the Wanaku system.
+ *
+ * The payload includes:
+ *
+ * This class bundles a {@link ToolReference} with its corresponding configuration
+ * and secrets data, facilitating the complete provisioning of a tool capability
+ * within the Wanaku system.
+ *
+ * The payload includes:
+ *
+ * This package contains payload classes used for transmitting capabilities
+ * along with their associated configuration and secrets data. These payloads
+ * facilitate the complete provisioning of tools and resources, ensuring that
+ * all necessary configuration and sensitive data are bundled together.
+ *
+ * Key types include:
+ *
+ * This package contains the fundamental data structures used throughout Wanaku,
+ * including entity types for capabilities, tools, resources, namespaces, and
+ * their associated metadata. These types form the foundation of the Wanaku
+ * API and are used for data exchange between components.
+ *
+ * Key types include:
+ *
+ * This implementation is useful as a default or placeholder when no actual
+ * configuration storage is available or needed. All methods return empty
+ * maps or empty strings rather than null values.
+ */
+public class NoopConfigStore implements ConfigStore {
+ @Override
+ public Map
+ * This implementation extends {@link NoopConfigStore} to provide the same
+ * no-operation behavior for secret storage. It is useful as a default or
+ * placeholder when no actual secret storage is available or needed.
+ *
+ * @see NoopConfigStore
+ * @see SecretStore
+ */
+public class NoopSecretStore extends NoopConfigStore implements SecretStore {}
diff --git a/capabilities-config-providers/capabilities-config-provider-api/src/main/java/ai/wanaku/capabilities/sdk/config/provider/api/PropertyBasedStore.java b/capabilities-config-providers/capabilities-config-provider-api/src/main/java/ai/wanaku/capabilities/sdk/config/provider/api/PropertyBasedStore.java
new file mode 100644
index 0000000..7d22f4b
--- /dev/null
+++ b/capabilities-config-providers/capabilities-config-provider-api/src/main/java/ai/wanaku/capabilities/sdk/config/provider/api/PropertyBasedStore.java
@@ -0,0 +1,50 @@
+package ai.wanaku.capabilities.sdk.config.provider.api;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.stream.Collectors;
+
+/**
+ * An abstract base class for {@link ConfigStore} implementations that retrieve configurations from a
+ * {@link Properties} object.
+ * This class handles the common logic for accessing properties, providing a foundation for
+ * concrete property-based configuration stores.
+ */
+public abstract class PropertyBasedStore implements ConfigStore {
+
+ protected final Properties properties;
+
+ /**
+ * Constructs a new {@link PropertyBasedStore} with the given {@link PropertyProvider}.
+ * The properties are loaded from the provider during construction.
+ *
+ * @param propertyProvider The provider from which to obtain the {@link Properties} object.
+ * Must not be {@code null}.
+ */
+ protected PropertyBasedStore(PropertyProvider propertyProvider) {
+ this.properties = propertyProvider.getProperties();
+ }
+
+ @Override
+ public Map While the method signature {@code write(String id, String data)} is inherited,
+ * the semantic implication is that {@code data} contains confidential information.
+ */
+public interface SecretWriter extends ConfigWriter {}
diff --git a/capabilities-config-providers/capabilities-config-provider-file/pom.xml b/capabilities-config-providers/capabilities-config-provider-file/pom.xml
new file mode 100644
index 0000000..b2dd89d
--- /dev/null
+++ b/capabilities-config-providers/capabilities-config-provider-file/pom.xml
@@ -0,0 +1,33 @@
+
+
+ * Set environment variables WANAKU_SECRETS_ENCRYPTION_PASSWORD and
+ * WANAKU_SECRETS_ENCRYPTION_SALT to enable encryption.
+ */
+public class FileSecretWriter implements SecretWriter {
+
+ private static final String ENV_PASSWORD = "WANAKU_SECRETS_ENCRYPTION_PASSWORD";
+ private static final String ENV_SALT = "WANAKU_SECRETS_ENCRYPTION_SALT";
+
+ protected final File serviceHome;
+
+ public FileSecretWriter(String serviceHome) {
+ this(new File(serviceHome));
+ }
+
+ public FileSecretWriter(File serviceHome) {
+ this.serviceHome = serviceHome;
+ }
+
+ @Override
+ public URI write(String id, String data) {
+ try {
+ Path path = Paths.get(serviceHome.getAbsolutePath(), id);
+ byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
+
+ String password = System.getenv(ENV_PASSWORD);
+ String salt = System.getenv(ENV_SALT);
+
+ if (password != null && !password.isEmpty() && salt != null && !salt.isEmpty()) {
+ bytes = EncryptionHelper.encrypt(bytes, password, salt);
+ }
+
+ Files.write(path, bytes);
+ return path.toUri();
+ } catch (Exception e) {
+ throw new ConfigWriteException("Failed to write secret: " + id, e);
+ }
+ }
+}
diff --git a/capabilities-config-providers/capabilities-config-provider-file/src/main/java/ai/wanaku/capabilities/sdk/config/provider/file/FileStore.java b/capabilities-config-providers/capabilities-config-provider-file/src/main/java/ai/wanaku/capabilities/sdk/config/provider/file/FileStore.java
new file mode 100644
index 0000000..467c4b6
--- /dev/null
+++ b/capabilities-config-providers/capabilities-config-provider-file/src/main/java/ai/wanaku/capabilities/sdk/config/provider/file/FileStore.java
@@ -0,0 +1,23 @@
+package ai.wanaku.capabilities.sdk.config.provider.file;
+
+import ai.wanaku.capabilities.sdk.config.provider.api.PropertyBasedStore;
+import java.net.URI;
+
+/**
+ * An abstract base class for {@link PropertyBasedStore} implementations that source their properties
+ * from a file identified by a {@link URI}. This class simplifies the creation of file-based
+ * configuration stores by integrating with {@link PropertyFileProvider}.
+ */
+abstract class FileStore extends PropertyBasedStore {
+
+ /**
+ * Constructs a new {@code FileStore} by creating a {@link PropertyFileProvider}
+ * with the given {@link URI} and passing it to the superclass constructor.
+ * This sets up the store to read properties from the specified file.
+ *
+ * @param uri The {@link URI} of the file containing the properties. Must not be {@code null}.
+ */
+ FileStore(URI uri) {
+ super(new PropertyFileProvider(uri));
+ }
+}
diff --git a/capabilities-config-providers/capabilities-config-provider-file/src/main/java/ai/wanaku/capabilities/sdk/config/provider/file/PropertyFileProvider.java b/capabilities-config-providers/capabilities-config-provider-file/src/main/java/ai/wanaku/capabilities/sdk/config/provider/file/PropertyFileProvider.java
new file mode 100644
index 0000000..0a3878e
--- /dev/null
+++ b/capabilities-config-providers/capabilities-config-provider-file/src/main/java/ai/wanaku/capabilities/sdk/config/provider/file/PropertyFileProvider.java
@@ -0,0 +1,49 @@
+package ai.wanaku.capabilities.sdk.config.provider.file;
+
+import ai.wanaku.capabilities.sdk.api.exceptions.WanakuException;
+import ai.wanaku.capabilities.sdk.config.provider.api.PropertyProvider;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Properties;
+
+/**
+ * A concrete implementation of {@link PropertyProvider} that loads properties from a file
+ * specified by a {@link URI}. This provider handles reading properties from a standard
+ * Java properties file format.
+ */
+public class PropertyFileProvider implements PropertyProvider {
+ private final Properties properties;
+
+ /**
+ * Constructs a new {@link PropertyFileProvider} and attempts to load properties from
+ * the file specified by the given {@link URI}.
+ * If the URI is {@code null}, an empty {@link Properties} object is initialized.
+ *
+ * @param uri The {@link URI} of the properties file to load. If {@code null},
+ * an empty {@link Properties} object will be used.
+ * @throws WanakuException If the file specified by the URI is not found, or
+ * if an I/O error occurs during loading.
+ */
+ public PropertyFileProvider(URI uri) {
+ if (uri == null) {
+ properties = new Properties();
+ } else {
+ final String path = uri.getPath();
+
+ File file = new File(path);
+ properties = new Properties();
+ try (FileInputStream fis = new FileInputStream(file)) {
+ properties.load(fis);
+ } catch (IOException e) {
+ throw new WanakuException(e);
+ }
+ }
+ }
+
+ @Override
+ public Properties getProperties() {
+ return properties;
+ }
+}
diff --git a/capabilities-config-providers/capabilities-config-provider-file/src/main/java/ai/wanaku/capabilities/sdk/config/provider/file/SecretFileStore.java b/capabilities-config-providers/capabilities-config-provider-file/src/main/java/ai/wanaku/capabilities/sdk/config/provider/file/SecretFileStore.java
new file mode 100644
index 0000000..35566b7
--- /dev/null
+++ b/capabilities-config-providers/capabilities-config-provider-file/src/main/java/ai/wanaku/capabilities/sdk/config/provider/file/SecretFileStore.java
@@ -0,0 +1,60 @@
+package ai.wanaku.capabilities.sdk.config.provider.file;
+
+import ai.wanaku.capabilities.sdk.api.exceptions.WanakuException;
+import ai.wanaku.capabilities.sdk.config.provider.api.PropertyBasedStore;
+import ai.wanaku.capabilities.sdk.config.provider.api.PropertyProvider;
+import ai.wanaku.capabilities.sdk.config.provider.api.SecretStore;
+import java.io.StringReader;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Properties;
+
+/**
+ * Reads secrets from files, automatically decrypting if encryption is configured.
+ *
+ * Set environment variables WANAKU_SECRETS_ENCRYPTION_PASSWORD and
+ * WANAKU_SECRETS_ENCRYPTION_SALT to enable decryption.
+ */
+public class SecretFileStore extends PropertyBasedStore implements SecretStore {
+
+ private static final String ENV_PASSWORD = "WANAKU_SECRETS_ENCRYPTION_PASSWORD";
+ private static final String ENV_SALT = "WANAKU_SECRETS_ENCRYPTION_SALT";
+
+ public SecretFileStore(URI uri) {
+ super(new DecryptingPropertyProvider(uri));
+ }
+
+ private static class DecryptingPropertyProvider implements PropertyProvider {
+ private final Properties properties = new Properties();
+
+ DecryptingPropertyProvider(URI uri) {
+ if (uri == null) {
+ return;
+ }
+ try {
+ byte[] data = Files.readAllBytes(Paths.get(uri));
+
+ String password = System.getenv(ENV_PASSWORD);
+ String salt = System.getenv(ENV_SALT);
+
+ if (password != null && !password.isEmpty() && salt != null && !salt.isEmpty()) {
+ data = EncryptionHelper.decrypt(data, password, salt);
+ }
+
+ // Decode the secret file as UTF-8 and load via a Reader so we are not bound
+ // to the ISO-8859-1 encoding required by Properties.load(InputStream).
+ String content = new String(data, StandardCharsets.UTF_8);
+ properties.load(new StringReader(content));
+ } catch (Exception e) {
+ throw new WanakuException("Failed to load secrets from " + uri, e);
+ }
+ }
+
+ @Override
+ public Properties getProperties() {
+ return properties;
+ }
+ }
+}
diff --git a/capabilities-config-providers/capabilities-config-provider-file/src/main/resources/application.properties b/capabilities-config-providers/capabilities-config-provider-file/src/main/resources/application.properties
new file mode 100644
index 0000000..e69de29
diff --git a/capabilities-config-providers/capabilities-config-provider-file/src/test/java/ai/wanaku/capabilities/sdk/config/provider/file/EncryptionHelperTest.java b/capabilities-config-providers/capabilities-config-provider-file/src/test/java/ai/wanaku/capabilities/sdk/config/provider/file/EncryptionHelperTest.java
new file mode 100644
index 0000000..90f7759
--- /dev/null
+++ b/capabilities-config-providers/capabilities-config-provider-file/src/test/java/ai/wanaku/capabilities/sdk/config/provider/file/EncryptionHelperTest.java
@@ -0,0 +1,103 @@
+package ai.wanaku.capabilities.sdk.config.provider.file;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import org.junit.jupiter.api.Test;
+
+class EncryptionHelperTest {
+
+ private static final String PASSWORD = "test-password-123";
+ private static final String SALT = "test-salt-1234567890";
+
+ @Test
+ void encryptDecryptRoundTrip() throws Exception {
+ String original = "api.key=secret-value\npassword=secret-password";
+ byte[] encrypted = EncryptionHelper.encrypt(original.getBytes(StandardCharsets.UTF_8), PASSWORD, SALT);
+ byte[] decrypted = EncryptionHelper.decrypt(encrypted, PASSWORD, SALT);
+
+ assertEquals(original, new String(decrypted, StandardCharsets.UTF_8));
+ }
+
+ @Test
+ void encryptDecryptRoundTripEmptyPayload() throws Exception {
+ byte[] original = new byte[0];
+
+ byte[] encrypted = EncryptionHelper.encrypt(original, PASSWORD, SALT);
+ byte[] decrypted = EncryptionHelper.decrypt(encrypted, PASSWORD, SALT);
+
+ assertArrayEquals(original, decrypted);
+ }
+
+ @Test
+ void encryptDecryptRoundTripVeryShortPayloads() throws Exception {
+ byte[][] payloads = {
+ new byte[] {0x00},
+ new byte[] {0x01},
+ new byte[] {(byte) 0xFF},
+ new byte[] {0x00, 0x01},
+ new byte[] {(byte) 0x80, 0x7F}
+ };
+
+ for (byte[] original : payloads) {
+ byte[] encrypted = EncryptionHelper.encrypt(original, PASSWORD, SALT);
+ byte[] decrypted = EncryptionHelper.decrypt(encrypted, PASSWORD, SALT);
+
+ assertArrayEquals(original, decrypted);
+ }
+ }
+
+ @Test
+ void encryptDecryptRoundTripBinaryPayload() throws Exception {
+ byte[] original =
+ new byte[] {0x00, 0x01, 0x02, 0x03, (byte) 0xFF, (byte) 0x80, (byte) 0x7F, 0x10, 0x20, (byte) 0xFE};
+
+ byte[] encrypted = EncryptionHelper.encrypt(original, PASSWORD, SALT);
+ byte[] decrypted = EncryptionHelper.decrypt(encrypted, PASSWORD, SALT);
+
+ assertArrayEquals(original, decrypted);
+ }
+
+ @Test
+ void encryptedDataDiffersFromOriginal() throws Exception {
+ String original = "secret data";
+ byte[] encrypted = EncryptionHelper.encrypt(original.getBytes(StandardCharsets.UTF_8), PASSWORD, SALT);
+
+ assertNotEquals(original, new String(encrypted, StandardCharsets.UTF_8));
+ }
+
+ @Test
+ void differentIVsProduceDifferentCiphertext() throws Exception {
+ byte[] data = "same data".getBytes(StandardCharsets.UTF_8);
+ byte[] encrypted1 = EncryptionHelper.encrypt(data, PASSWORD, SALT);
+ byte[] encrypted2 = EncryptionHelper.encrypt(data, PASSWORD, SALT);
+
+ assertFalse(Arrays.equals(encrypted1, encrypted2));
+ }
+
+ @Test
+ void wrongPasswordFails() throws Exception {
+ byte[] encrypted = EncryptionHelper.encrypt("data".getBytes(StandardCharsets.UTF_8), PASSWORD, SALT);
+
+ assertThrows(Exception.class, () -> EncryptionHelper.decrypt(encrypted, "wrong-password", SALT));
+ }
+
+ @Test
+ void wrongSaltFails() throws Exception {
+ byte[] encrypted = EncryptionHelper.encrypt("data".getBytes(StandardCharsets.UTF_8), PASSWORD, SALT);
+
+ assertThrows(Exception.class, () -> EncryptionHelper.decrypt(encrypted, PASSWORD, "wrong-salt"));
+ }
+
+ @Test
+ void decryptNullDataThrowsIllegalArgumentException() {
+ assertThrows(IllegalArgumentException.class, () -> EncryptionHelper.decrypt(null, PASSWORD, SALT));
+ }
+
+ @Test
+ void decryptTooShortDataThrowsIllegalArgumentException() {
+ byte[] tooShort = new byte[15]; // IV_LENGTH is 16
+ assertThrows(IllegalArgumentException.class, () -> EncryptionHelper.decrypt(tooShort, PASSWORD, SALT));
+ }
+}
diff --git a/capabilities-config-providers/pom.xml b/capabilities-config-providers/pom.xml
new file mode 100644
index 0000000..d0547ff
--- /dev/null
+++ b/capabilities-config-providers/pom.xml
@@ -0,0 +1,20 @@
+
+
+ * This class handles the lifecycle of service instance data files, including reading existing
+ * service entries, writing new entries, and managing the data directory structure. Each service
+ * instance maintains its identity across restarts by persisting its ID to a binary data file.
+ *
+ * The manager creates data files with the naming convention {@code
+ *
+ *
+ * @see ai.wanaku.api.types
+ */
+package ai.wanaku.capabilities.sdk.api.types.discovery;
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/io/PromptPayload.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/io/PromptPayload.java
new file mode 100644
index 0000000..376cc0b
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/io/PromptPayload.java
@@ -0,0 +1,39 @@
+package ai.wanaku.capabilities.sdk.api.types.io;
+
+import ai.wanaku.capabilities.sdk.api.types.PromptReference;
+
+/**
+ * Represents a payload for prompt operations that may require provisioning.
+ */
+public final class PromptPayload implements ProvisionAwarePayload
+ *
+ */
+public class ResourcePayload implements ProvisionAwarePayload
+ *
+ */
+public final class ToolPayload implements ProvisionAwarePayload
+ *
+ *
+ * @see ai.wanaku.api.types
+ */
+package ai.wanaku.capabilities.sdk.api.types.io;
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/management/ServerInfo.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/management/ServerInfo.java
new file mode 100644
index 0000000..9772e66
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/management/ServerInfo.java
@@ -0,0 +1,33 @@
+package ai.wanaku.capabilities.sdk.api.types.management;
+
+/**
+ * Contains server information, such as the version of the server.
+ *
+ * This class encapsulates essential details about the server, making it
+ * easy to access its configuration.
+ */
+public class ServerInfo {
+
+ /**
+ * The version of the server software or platform running on this instance.
+ */
+ private String version;
+
+ /**
+ * Returns the current version of the server.
+ *
+ * @return The server version as a string.
+ */
+ public String getVersion() {
+ return version;
+ }
+
+ /**
+ * Sets the version of the server to a new value.
+ *
+ * @param version The updated server version as a string.
+ */
+ public void setVersion(String version) {
+ this.version = version;
+ }
+}
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/package-info.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/package-info.java
new file mode 100644
index 0000000..729853b
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/package-info.java
@@ -0,0 +1,22 @@
+/**
+ * Core type definitions and data models for the Wanaku system.
+ *
+ *
+ *
+ * @see ai.wanaku.capabilities.api.types.io
+ * @see ai.wanaku.capabilities.api.types.discovery
+ */
+package ai.wanaku.capabilities.sdk.api.types;
diff --git a/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/providers/ServiceTarget.java b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/providers/ServiceTarget.java
new file mode 100644
index 0000000..679b699
--- /dev/null
+++ b/capabilities-api/src/main/java/ai/wanaku/capabilities/sdk/api/types/providers/ServiceTarget.java
@@ -0,0 +1,136 @@
+package ai.wanaku.capabilities.sdk.api.types.providers;
+
+import ai.wanaku.capabilities.sdk.api.types.WanakuEntity;
+import java.util.Objects;
+
+/**
+ * Represents a target service endpoint that can be either a resource provider or a tool invoker.
+ *
+ * This class encapsulates information about a service, including its target and configurations,
+ * providing a structured way to represent and manage services in a system.
+ */
+public class ServiceTarget implements WanakuEntity