Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ id = "nearai.search"
description = "Search through the NEAR AI MCP server."
effects = ["dispatch_capability", "network", "use_secret"]
runtime_credentials = [
{ handle = "llm_nearai_api_key", audience = { scheme = "https", host_pattern = "*.near.ai" }, target = { type = "header", name = "authorization", prefix = "Bearer " } },
{ handle = "llm_nearai_api_key", source = { type = "product_auth_account", provider = "nearai" }, audience = { scheme = "https", host_pattern = "*.near.ai" }, target = { type = "header", name = "authorization", prefix = "Bearer " } },
]
default_permission = "ask"
visibility = "model"
Expand Down
15 changes: 14 additions & 1 deletion crates/ironclaw_reborn_composition/src/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1863,7 +1863,8 @@ mod tests {
CapabilityGrant, CapabilityGrantId, CapabilityId, CapabilitySet, EffectKind,
ExecutionContext, ExtensionId, GrantConstraints, InvocationId, MountAlias, MountGrant,
NetworkPolicy, NetworkScheme, NetworkTargetPattern, Principal, ResourceEstimate,
ResourceScope, RuntimeKind, ScopedPath, SecretHandle, TrustClass, UserId, VirtualPath,
ResourceScope, RuntimeCredentialAccountProviderId, RuntimeCredentialRequirementSource,
RuntimeKind, ScopedPath, SecretHandle, TrustClass, UserId, VirtualPath,
};
use ironclaw_host_runtime::{
RuntimeCapabilityOutcome, RuntimeCapabilityRequest, RuntimeFailureKind,
Expand Down Expand Up @@ -2191,6 +2192,18 @@ mod tests {
search.runtime_credentials[0].handle,
SecretHandle::new("llm_nearai_api_key").unwrap()
);
// NEAR AI MCP credential is sourced from a product-auth account so that the
// user-facing setup flow is the manual-token product-auth surface (shared
// with GitHub WASM), not an out-of-band SecretStore handle drop.
// The 'handle' field remains the staging slot name the MCP egress planner
// reads from RuntimeSecretInjectionStore after the obligation handler resolves
// the access secret via RuntimeCredentialAccountResolver.
assert_eq!(
search.runtime_credentials[0].source,
RuntimeCredentialRequirementSource::ProductAuthAccount {
provider: RuntimeCredentialAccountProviderId::new("nearai").unwrap(),
}
);
assert_eq!(
search.runtime_credentials[0].audience.host_pattern,
"private.near.ai"
Expand Down
14 changes: 11 additions & 3 deletions crates/ironclaw_reborn_composition/src/nearai_mcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,17 @@ struct NearAiMcpEgressPlanner {

impl McpHostHttpEgressPlanner for NearAiMcpEgressPlanner {
fn plan(&self, request: McpHostHttpEgressPlanRequest<'_>) -> McpHostHttpEgressPlan {
// This is a narrow NEAR AI MCP adapter. Do not grow this into the
// generic product-auth-to-MCP planner; nearai/ironclaw#4176 owns that
// bridge, including account selection and typed AuthRequired recovery.
// This is a narrow NEAR AI MCP adapter. Do not grow this into a generic
// product-auth-to-MCP planner. Account selection and typed AuthRequired
// recovery happen upstream: the bundled manifest declares the credential
// with `source = product_auth_account(provider = "nearai")`, so the
// authorization layer emits `Obligation::InjectCredentialAccountOnce`
// before this planner runs. The obligation handler resolves the
// configured nearai product-auth account via
// `RuntimeCredentialAccountResolver`, stages the access secret into
// `RuntimeSecretInjectionStore` under the `llm_nearai_api_key` slot, and
// this planner only references that slot via the `StagedObligation`
// source. Closes the MCP slice of nearai/ironclaw#4176.
if request.provider.as_str() != NEARAI_EXTENSION_ID
|| !nearai_mcp_url_allowed(request.url, &self.endpoint)
{
Expand Down
Loading