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

# Skills

`options.skills` controls which skills the `Skill` tool can invoke in the current session. The SDK compiles it into the CLI's `Skill` allowlist and merges it with `allowed_tools` before passing to qodercli.

<div id="sdk-does-not-load-built-in-skills" />

<div id="sdkdoesnotloadbuiltinskills" />

## SDK Does Not Load Built-in Skills

When the SDK launches the CLI it **always** appends `--disable-builtin-skills`, so the session never sees the CLI's factory built-in skills (`simplify`, `debug`, `security-review`, `quest`, `batch`, `agent-creator`, `hook-config`, `mcp-config`, `skill-creator`, etc.). No `source: 'built-in'` entries appear in `get_server_info()['skills']`, and the model's system prompt does not see them either.

This is fixed SDK behavior with no opt-in; if you want the capability of a CLI built-in skill, either ship your own copy of the SKILL.md via a plugin / user dir / project dir, or just reuse the CLI's default scenarios.

The session can still pick up skills contributed from these sources:

* **plugin skills**: loaded via `options.plugins`, addressed with the plugin-qualified name (`plugin:skill`).
* **user / project skills**: discovered when you opt into `user` / `project` / `local` via `options.setting_sources`.
* **Agent-preloaded skills**: declared on `options.agents[name].skills`, scoped to that subagent only.

To reliably confirm what was discovered in a session, read `client.get_server_info()['skills']` at runtime — don't hard-code the set.

<div id="using-cli-default-policy" />

<div id="usingclidefaultpolicy" />

## Using CLI Default Policy

When `skills` is not passed, the SDK does not inject an additional `Skill` allowlist, leaving everything to the CLI's own policy. Because built-ins are disabled, a session with no `setting_sources` / `plugins` will see an empty `get_server_info()['skills']` list.

```python theme={null}
from qoder_agent_sdk import query, QoderAgentOptions

async for msg in query(
    prompt="Analyze the test coverage of this project",
    options=QoderAgentOptions(cwd="/path/to/project"),
):
    print(msg)
```

<div id="enabling-all-discovered-skills" />

<div id="enablingalldiscoveredskills" />

## Enabling All Discovered Skills

```python theme={null}
async for msg in query(
    prompt="Use an appropriate skill to perform a code review",
    options=QoderAgentOptions(
        cwd="/path/to/project",
        setting_sources=["project"],
        skills="all",
    ),
):
    print(msg)
```

`skills="all"` allows the `Skill` tool to invoke every skill the CLI currently discovers (sources are determined by `setting_sources` / `plugins`; built-ins are no longer included).

<div id="enabling-only-specific-skills" />

<div id="enablingonlyspecificskills" />

## Enabling Only Specific Skills

```python theme={null}
async for msg in query(
    prompt="Use the review skill to inspect recent changes",
    options=QoderAgentOptions(
        cwd="/path/to/project",
        setting_sources=["project"],
        skills=["review"],
    ),
):
    print(msg)
```

<div id="enabling-plugin-skills" />

<div id="enablingpluginskills" />

## Enabling Plugin Skills

Plugin skills use the plugin-qualified name `plugin:skill`. For plugin loading methods, see [Plugins documentation](/en/cli/sdk/python/plugins).

```python theme={null}
async for msg in query(
    prompt="Use the echo skill provided by the plugin to handle this input",
    options=QoderAgentOptions(
        plugins=[{"type": "local", "path": "/path/to/sdk-test-plugin"}],
        skills=["sdk-test-plugin:sdk-echo"],
    ),
):
    print(msg)
```

<div id="merging-with-explicit-tool-allowlist" />

<div id="mergingwithexplicittoolallowlist" />

## Merging with Explicit Tool Allowlist

```python theme={null}
async for msg in query(
    prompt="Read the source and use the review skill to produce a list of issues",
    options=QoderAgentOptions(
        cwd="/path/to/project",
        setting_sources=["project"],
        allowed_tools=["FileRead", "Grep"],
        skills=["review"],
    ),
):
    print(msg)
```

The above configuration ultimately allows `FileRead`, `Grep`, and `Skill(review)`. The SDK merges and deduplicates entries, so it never writes duplicates with the same name.

<div id="hiding-discovered-skills" />

<div id="hidingdiscoveredskills" />

## Hiding Discovered Skills

`options.skills` controls tool permissions, not discovery filtering. To truly keep a plugin / user / project skill out of the init response and the model's system prompt, use `settings.skillOverrides`.

```python theme={null}
options = QoderAgentOptions(
    plugins=[{"type": "local", "path": "/path/to/sdk-test-plugin"}],
    settings={
        "skillOverrides": {
            "sdk-test-plugin:sdk-echo": "off",
        },
    },
)
```

* `"off"`: Completely hidden — not in `init.skills`, not in the model system prompt; `Skill` tool invocations are also rejected.
* Other values: `"on"` (default), `"name-only"` (shows name only, not description), `"user-invocable-only"` (invisible to the model; the user can still trigger it via `/name`).
* Scope of effect: every SDK-visible source (plugin, user, project, etc.) respects this override; CLI built-ins are already blocked by `--disable-builtin-skills`, so overrides for them have nothing to act on.
* Key naming rules: Plugin skills use the plugin-qualified name `plugin:skill`; non-plugin skills use bare names. Both forms can be written simultaneously; matching is attempted against the fully qualified name first, then falls back to the bare name.

> `options.skills` only controls tool invocation permissions; **it cannot be used to hide skill discovery / context exposure**.

<div id="reading-skills-discovered-in-the-current-session" />

<div id="readingskillsdiscoveredinthecurrentsession" />

## Reading Skills Discovered in the Current Session

The initialization result includes skills discovered by the CLI in this session, which the host UI can use to display "currently available capabilities." `query()` is a one-shot stream with no convenience query interface; use `QoderSDKClient` to read after the handshake.

```python theme={null}
from qoder_agent_sdk import QoderAgentOptions, QoderSDKClient

options = QoderAgentOptions(
    cwd="/path/to/project",
    setting_sources=["project"],
    skills="all",
)

async with QoderSDKClient(options) as client:
    info = await client.get_server_info()
    if info:
        for skill in info.get("skills", []):
            print(skill["name"], skill.get("source"))
```

> `skills` is context and tool visibility control, not a security boundary. Unlisted skills are not exposed to the model via the `Skill` tool, but skill files are still on disk and can still be accessed by regular file reading tools.

<div id="custom-agent-preloading-skills" />

<div id="customagentpreloadingskills" />

## Custom Agent Preloading Skills

If you define custom subagents via `options.agents`, you can declare `skills` in the `AgentDefinition`. When the main session invokes the `Agent` tool, the subagent will run with the specified skills loaded.

```python theme={null}
from qoder_agent_sdk import AgentDefinition, QoderAgentOptions

options = QoderAgentOptions(
    cwd="/path/to/project",
    allowed_tools=["Agent"],
    agents={
        "sdk-skill-helper": AgentDefinition(
            description="Invoke when the sdk-agent-marker skill is needed.",
            prompt="You are a helper agent that only reads and runs the specified skill.",
            skills=["sdk-agent-marker"],
            maxTurns=2,
        ),
    },
)
```

These `skills` only affect that Agent's context and are not equivalent to enabling the same-named skill for the main session — the main session's `allowed_tools` is not affected by this change.

<div id="options-reference" />

<div id="optionsreference" />

## Options Reference

| Field             | Type                                                | Description                                                                                |
| ----------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `skills`          | `list[str] \| Literal["all"] \| None`               | Controls which skills the main session can invoke via the `Skill` tool                     |
| `agents`          | `dict[str, AgentDefinition] \| None`                | Custom Agents; `AgentDefinition.skills` is each Agent's independent preload list           |
| `allowed_tools`   | `list[str]`                                         | Tool allowlist; merged with `Skill(...)` entries compiled from `skills` with deduplication |
| `setting_sources` | `list[Literal["user", "project", "local"]] \| None` | Controls whether the CLI scans user / project dirs for skills (default empty = sandboxed)  |
| `plugins`         | `list[PluginSpec] \| None`                          | Loads plugins; skills inside contribute to the discovered set                              |

`settings` (dict / path / JSON string) has several skill-related fields that the SDK passes through as a dict; actual effects depend on whether the CLI version implements them:

| Field                           | Purpose                                                                                                                                        |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `skillOverrides`                | Set `"on" \| "name-only" \| "user-invocable-only" \| "off"` per skill name; plugin, user, project, and other sources all respect this override |
| `skillListingMaxDescChars`      | Character limit per description in the skill listing (cc-sdk default 1536); exceeding it triggers truncation                                   |
| `skillListingBudgetFraction`    | Context window fraction reserved for the skill listing (cc-sdk default 0.01 = 1%); exceeding it triggers compression                           |
| `strictPluginOnlyCustomization` | Restrict one or more of `skills`, `agents`, `hooks`, `mcp` to only accept contributions from plugin sources                                    |

<div id="return-value-reference" />

<div id="returnvaluereference" />

## Return Value Reference

The dict returned by `client.get_server_info()` includes:

```python theme={null}
{
    "commands": [{"name": str, "description": str, ...}, ...],
    "agents": [{"name": str, "description": str, "model": str | None}, ...],
    "skills": [{"name": str, "description": str | None, "source": str | None}, ...],
    "plugins": [{"name": str, "path": str, "source": str | None}, ...],
    # Also includes models / account / output_style and other fields
}
```

<div id="best-practices" />

<div id="bestpractices" />

## Best Practices

* **Enable `skills` as needed**: `skills="all"` is ideal for development and debugging; end-user-facing products should typically pass an explicit list.
* **Want a CLI built-in's behavior? Ship your own copy**: the SDK will not inject `simplify` / `security-review` and the rest. Provide your own SKILL.md via a plugin or a `setting_sources`-visible directory.
* **Don't treat `skills` as a sandbox**: Security boundaries should be controlled collectively by `allowed_tools`, `disallowed_tools`, `can_use_tool`, permission mode, and sandboxing.
* **Use `get_server_info()['skills']` for the UI**: This is the stable entry point for the CLI discovery pipeline, used to display "currently available skills."
* **Manage subagent `skills` separately**: They are independent lists from the main session's `options.skills` and do not override each other.

<div id="current-limitations" />

<div id="currentlimitations" />

## Current Limitations

* In some qodercli versions, local plugin skills do not appear in `get_server_info()['skills']`. This is a CLI-side discovery pipeline issue and does not require changes to how the SDK is called.
* `--disable-slash-commands` is a CLI capability for one-shot disabling of all slash-command skills; the SDK does not currently expose a first-class option, and depending on non-public paths like `extra_args` is not recommended.
* `settings.skillListingMaxDescChars` and `settings.skillListingBudgetFraction` are listing-budget control fields that the SDK passes through; the current qodercli has not implemented listing budget control, so passing them does not raise an error but also does not change behavior.
* The Python SDK does not yet provide a `client.supported_skills()` convenience method; read from `get_server_info()['skills']` instead (already on the backlog).
