> ## Documentation Index
> Fetch the complete documentation index at: https://flokoa.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# AgentTool: connect agents to external APIs

> Define OpenAPI-backed tool sets that your agents can call at runtime, targeting external URLs or internal Kubernetes services by name.

The `AgentTool` CRD lets you expose any HTTP API as a callable tool for your AI agents. Each `AgentTool` is backed by an OpenAPI specification — the operator stores this schema and makes it available to agents so their underlying LLM framework knows exactly which operations are available, what parameters each operation accepts, and what responses to expect. You can target external URLs or internal Kubernetes services, and you can share a single `AgentTool` across many agents in different namespaces.

## API reference

```
apiVersion: agent.flokoa.ai/v1alpha1
kind: AgentTool
```

***

## Basic structure

```yaml theme={null}
apiVersion: agent.flokoa.ai/v1alpha1
kind: AgentTool
metadata:
  name: weather-api
spec:
  type: openapi
  description: "Get current weather conditions and forecasts for any location"

  openApi:
    url: "https://api.weather.com/v1"
    openApiSchema:
      endpointPath: "/openapi.json"
```

The `description` field is critical — the LLM reads it to decide when and whether to invoke the tool. Write it as a clear, one-sentence summary of what the API does.

***

## OpenAPI schema sources

The OpenAPI schema is required for every `AgentTool`. It tells the LLM what operations are available and what shape the request and response payloads take. You can supply the schema in three ways:

<Tabs>
  <Tab title="endpointPath">
    The simplest option: the operator fetches the schema directly from a path on the target service. Use this when your service already serves its own OpenAPI spec.

    ```yaml theme={null}
    spec:
      type: openapi
      description: "Query the product catalogue"
      openApi:
        url: "https://api.example.com"
        openApiSchema:
          endpointPath: "/docs/openapi.json"
    ```
  </Tab>

  <Tab title="value (inline)">
    Embed the complete OpenAPI spec as a YAML object directly in the `AgentTool` manifest. This is useful for small, stable APIs where you want a single self-contained manifest.

    ```yaml theme={null}
    spec:
      type: openapi
      description: "Perform arithmetic calculations"
      openApi:
        url: "https://api.example.com"
        openApiSchema:
          value:
            openapi: "3.0.0"
            info:
              title: Calculator API
              version: "1.0"
            paths:
              /calculate:
                post:
                  summary: Perform a calculation
                  requestBody:
                    content:
                      application/json:
                        schema:
                          type: object
                          properties:
                            operation:
                              type: string
                              enum: ["add", "subtract", "multiply", "divide"]
                            a:
                              type: number
                            b:
                              type: number
                  responses:
                    "200":
                      description: Calculation result
                      content:
                        application/json:
                          schema:
                            type: object
                            properties:
                              result:
                                type: number
    ```
  </Tab>

  <Tab title="valueFrom (ConfigMap)">
    Reference an OpenAPI spec stored in a Kubernetes ConfigMap. This is the right choice for larger specs, or when multiple teams manage the spec independently of the `AgentTool` manifest.

    ```yaml theme={null}
    # 1. Store the spec in a ConfigMap
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: api-specs
    data:
      openapi.json: |
        {
          "openapi": "3.0.0",
          "info": {"title": "My API", "version": "1.0"},
          "paths": {}
        }
    ```

    ```yaml theme={null}
    # 2. Reference it from the AgentTool
    apiVersion: agent.flokoa.ai/v1alpha1
    kind: AgentTool
    metadata:
      name: my-tool
    spec:
      type: openapi
      description: "Call my internal API"
      openApi:
        url: "https://api.example.com"
        openApiSchema:
          valueFrom:
            name: api-specs
            key: openapi.json
    ```
  </Tab>
</Tabs>

***

## Target configuration

<Tabs>
  <Tab title="External URL">
    Point the tool at any publicly reachable or VPN-accessible HTTP endpoint. You can attach custom headers and a timeout:

    ```yaml theme={null}
    spec:
      type: openapi
      description: "Send transactional emails via SendGrid"
      openApi:
        url: "https://api.sendgrid.com/v3"
        timeoutSeconds: 30
        headers:
          Accept: "application/json"
          Content-Type: "application/json"
        openApiSchema:
          endpointPath: "/openapi.json"
    ```
  </Tab>

  <Tab title="Internal Kubernetes service">
    Use `serviceRef` to target a Service inside the cluster. Flokoa resolves the DNS name automatically so you never hard-code internal hostnames:

    ```yaml theme={null}
    spec:
      type: openapi
      description: "Check real-time inventory levels in the warehouse system"
      openApi:
        serviceRef:
          name: inventory-service
          namespace: backend
          port: 8080
        timeoutSeconds: 10
        openApiSchema:
          endpointPath: "/api/openapi.json"
    ```

    <Tip>
      Prefer `serviceRef` over `url` for in-cluster services. It is faster, avoids external network hops, and lets NetworkPolicies enforce access control cleanly.
    </Tip>
  </Tab>
</Tabs>

***

## Using tools in agents

You can attach tools to an `Agent` either by referencing an existing `AgentTool` CR or by defining the tool inline in the `Agent` spec.

**Referencing an AgentTool CR (recommended):**

```yaml theme={null}
apiVersion: agent.flokoa.ai/v1alpha1
kind: Agent
metadata:
  name: customer-service-agent
spec:
  tools:
    # Same namespace
    - toolRef:
        name: weather-api

    # Cross-namespace reference
    - toolRef:
        name: inventory-check
        namespace: shared-tools

  runtime:
    type: standard
    spec:
      container:
        name: agent
        image: ghcr.io/example/agent:v1.0.0
        ports:
          - containerPort: 8080
            name: http
```

**Inline tool definition (no separate CR needed):**

```yaml theme={null}
spec:
  tools:
    - name: petstore
      template:
        type: openapi
        description: "Browse and manage pet listings"
        openApi:
          url: "https://petstore3.swagger.io"
          openApiSchema:
            endpointPath: "/api/v3/openapi.json"
```

<Note>
  Inline tool definitions cannot be reused across multiple agents. Extract frequently used tools into standalone `AgentTool` CRs and share them by reference.
</Note>

***

## Status fields

The operator validates and stores each tool's OpenAPI spec. The resulting status looks like:

```yaml theme={null}
status:
  conditions:
    - type: Validated
      status: "True"
      lastTransitionTime: "2026-01-15T10:30:00Z"
      reason: ValidationSuccess
      message: "Spec is valid"
    - type: Stored
      status: "True"
      lastTransitionTime: "2026-01-15T10:30:00Z"
      reason: StorageSuccess
      message: "Spec stored in ConfigMap"

  observedGeneration: 1
```

| Field                   | Description                                              |
| ----------------------- | -------------------------------------------------------- |
| `conditions[Validated]` | Whether the OpenAPI spec passed schema validation        |
| `conditions[Stored]`    | Whether the spec was successfully written to a ConfigMap |
| `observedGeneration`    | The last spec generation reconciled by the operator      |

***

## Security

### API credentials via secrets

Never embed API keys or bearer tokens as plain strings in tool headers. Instead, inject them as environment variables in the agent container and reference them at runtime:

```yaml theme={null}
# 1. Store the credential in a Secret
apiVersion: v1
kind: Secret
metadata:
  name: sendgrid-credentials
stringData:
  api-key: "SG.xxx"
```

```yaml theme={null}
# 2. Inject the credential into the agent container env
apiVersion: agent.flokoa.ai/v1alpha1
kind: Agent
metadata:
  name: email-agent
spec:
  tools:
    - toolRef:
        name: send-email
  runtime:
    type: standard
    spec:
      container:
        name: agent
        image: ghcr.io/example/email-agent:v1.0.0
        env:
          - name: SENDGRID_API_KEY
            valueFrom:
              secretKeyRef:
                name: sendgrid-credentials
                key: api-key
        ports:
          - containerPort: 8080
```

### Network policies

Use a `NetworkPolicy` to restrict which services your agents may reach through their tools:

```yaml theme={null}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: agent-egress
spec:
  podSelector:
    matchLabels:
      flokoa.ai/agent: customer-service-agent
  policyTypes:
    - Egress
  egress:
    # Allow DNS
    - to:
        - namespaceSelector:
            matchLabels:
              name: kube-system
      ports:
        - protocol: UDP
          port: 53
    # Allow only the inventory service
    - to:
        - podSelector:
            matchLabels:
              app: inventory-service
      ports:
        - protocol: TCP
          port: 8080
```

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Tool is not appearing as callable in the agent">
    ```bash theme={null}
    # Check the tool's validation and storage conditions
    kubectl describe agenttool <name>

    # Verify the tool is referenced correctly in the agent
    kubectl get agent my-agent -o jsonpath='{.spec.tools}'
    ```

    * Confirm the `Validated` and `Stored` conditions are both `True`.
    * Check that `toolRef.name` and `toolRef.namespace` match the actual `AgentTool` metadata exactly.
    * Ensure the OpenAPI spec contains at least one operation (`paths` entry).
  </Accordion>

  <Accordion title="Timeout errors when the tool is called">
    ```yaml theme={null}
    # Increase the per-request timeout on the tool
    spec:
      openApi:
        timeoutSeconds: 120
    ```

    Also check whether the target service is under load, and whether any NetworkPolicies are delaying or dropping packets.
  </Accordion>

  <Accordion title="OpenAPI schema validation failures">
    ```bash theme={null}
    # Read the full validation error from the condition message
    kubectl get agenttool <name> -o jsonpath='{.status.conditions[?(@.type=="Validated")].message}'
    ```

    * For `endpointPath`: verify the path returns a valid JSON or YAML OpenAPI document.
    * For `valueFrom`: confirm the ConfigMap exists in the same namespace and the key contains valid YAML or JSON.
    * For `value`: check that the inline object is well-formed YAML with no tab characters.
    * Use an online validator (e.g., `editor.swagger.io`) to confirm spec correctness before applying.
  </Accordion>

  <Accordion title="Service resolution failures for internal services">
    ```bash theme={null}
    # Verify the target Service exists
    kubectl get svc inventory-service -n backend

    # Test DNS from inside an agent pod
    kubectl exec -it <agent-pod> -- nslookup inventory-service.backend.svc.cluster.local

    # Test HTTP connectivity
    kubectl exec -it <agent-pod> -- curl http://inventory-service.backend:8080/health
    ```

    * Ensure `serviceRef.namespace` matches the namespace where the Service lives.
    * Confirm `serviceRef.port` matches a port defined on the Service spec.
    * Check that NetworkPolicies in the target namespace allow ingress from the agent pod.
  </Accordion>
</AccordionGroup>

***

## Best practices

1. **Write a precise `description`** — the LLM relies entirely on this text to decide when to call the tool, so be specific about what the API does and what inputs it expects.
2. **Prefer `endpointPath`** when your service serves its own OpenAPI spec — it keeps the `AgentTool` manifest minimal and the spec always up to date.
3. **Use `valueFrom` for large or shared specs** — storing the spec in a ConfigMap allows it to be managed independently and rolled back separately.
4. **Use `serviceRef` for in-cluster targets** — it avoids external DNS round-trips and integrates naturally with Kubernetes NetworkPolicies.
5. **Set conservative timeouts** — a stuck tool call blocks the entire agent turn; use `timeoutSeconds` that match the API's p99 latency plus a buffer.
6. **Share tools via a dedicated namespace** — create reusable `AgentTool` CRs in `shared-tools` and reference them with `toolRef.namespace` rather than duplicating definitions.
7. **Store API credentials in Secrets** — inject them into the agent container environment rather than hard-coding them in tool headers.
8. **Test tools in isolation** before attaching them to agents — verify the target endpoint is reachable and returns a valid OpenAPI spec independently.
9. **Version your tool manifests** alongside application code so schema changes are auditable.
