Skip to main content
Glama

skills_get_asset

Retrieve templates and static assets bundled with a skill. First list available files, then fetch specific content for use as a starting template.

Instructions

STEP 3c — Fetch a template or static resource bundled with a skill (markdown templates, config starters, example data files).

Two-phase use:

  1. Call with filename='list' (default) to see the full asset manifest

  2. Call again with the specific filename to fetch its content

Use the returned content as a starting template — adapt it to the specific task. Only call when skill instructions reference a specific asset file.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
skill_idYes
filenameNolist

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Core handler function `get_skill_asset(skill_id, filename)` implementing the full logic of the skills_get_asset tool. Queries Qdrant for assets, supports list mode (returns manifest) and fetch mode (returns content), with caching and case-insensitive fallback.
    """MCP tool: skills_get_asset — fetch a template or static resource bundled with a skill.
    
    Assets live in the skill's assets/ subdirectory. They are templates, output format
    specifications, config examples, or other static resources the agent uses to structure
    its output or as starting points for content generation.
    
    Two modes:
      1. List mode (filename omitted or "list"): returns metadata for all assets.
      2. Fetch mode (filename provided): returns the full content of that asset.
    """
    
    from __future__ import annotations
    
    import json
    import os
    
    from ..db.cache import TTLCache
    from ..db.qdrant_manager import qdrant_manager
    
    _asset_cache: TTLCache = TTLCache(
        ttl=float(os.getenv("CACHE_TTL_SECONDS", "300")),
        max_size=int(os.getenv("CACHE_MAX_SIZE", "1000")),
    )
    
    
    def get_skill_asset(skill_id: str, filename: str = "list") -> str:
        """Fetch a template or static resource bundled with a skill.
    
        Call this when the skill body instructions say "use assets/report-template.md"
        or similar. Assets are typically output format templates that the agent should
        populate with generated content.
    
        Args:
            skill_id: The skill_id returned by skills_find_relevant.
            filename: The asset filename to fetch (e.g. "report-template.md").
                      Pass "list" or omit to get a manifest of all asset files.
    
        Returns:
            JSON string.
            - List mode: {"skill_id": ..., "assets": [{filename, asset_type, description, file_path}, ...]}
            - Fetch mode: {"skill_id": ..., "filename": ..., "asset_type": ..., "description": ..., "content": ...}
            - Error: {"error": "..."}
        """
        # ── List mode ─────────────────────────────────────────────────────────────
    
        if filename in ("list", "", "all"):
            cache_key = f"asset_list|{skill_id}"
            cached = _asset_cache.get(cache_key)
            if cached is not None:
                return cached
    
            payloads = qdrant_manager.get_assets_for_skill(skill_id)
            assets = [
                {
                    "filename": p.get("filename", ""),
                    "asset_type": p.get("asset_type", "other"),
                    "description": p.get("description", ""),
                    "file_path": p.get("file_path", ""),
                }
                for p in payloads
            ]
            assets.sort(key=lambda a: a["filename"])
    
            result = json.dumps(
                {
                    "skill_id": skill_id,
                    "total": len(assets),
                    "assets": assets,
                },
                indent=2,
            )
            _asset_cache.set(cache_key, result)
            return result
    
        # ── Fetch mode ────────────────────────────────────────────────────────────
    
        cache_key = f"asset|{skill_id}|{filename}"
        cached = _asset_cache.get(cache_key)
        if cached is not None:
            return cached
    
        payload = qdrant_manager.get_asset(skill_id, filename)
        if payload is None:
            all_assets = qdrant_manager.get_assets_for_skill(skill_id)
            all_filenames = [p.get("filename", "") for p in all_assets]
            # Case-insensitive fallback
            match = next(
                (f for f in all_filenames if f.lower() == filename.lower()), None
            )
            if match and match != filename:
                payload = qdrant_manager.get_asset(skill_id, match)
            if payload is None:
                return json.dumps(
                    {
                        "error": (
                            f"Asset '{filename}' not found for skill '{skill_id}'. "
                            f"Call skills_get_asset(skill_id='{skill_id}', filename='list') "
                            f"to see available assets."
                        ),
                        "available": all_filenames,
                    }
                )
    
        result = json.dumps(
            {
                "skill_id": payload.get("skill_id", skill_id),
                "skill_name": payload.get("skill_name", ""),
                "filename": payload.get("filename", filename),
                "file_path": payload.get("file_path", ""),
                "asset_type": payload.get("asset_type", "other"),
                "description": payload.get("description", ""),
                "content": payload.get("content", ""),
            },
            indent=2,
        )
        _asset_cache.set(cache_key, result)
        return result
  • MCP tool decorator registration of `skills_get_asset` via FastMCP. The `_skills_get_asset` function delegates to `get_skill_asset` from the tools module.
    @mcp.tool(
        name="skills_get_asset",
        description=(
            "STEP 3c — Fetch a template or static resource bundled with a skill "
            "(markdown templates, config starters, example data files).\n\n"
            "Two-phase use:\n"
            "  1. Call with filename='list' (default) to see the full asset manifest\n"
            "  2. Call again with the specific filename to fetch its content\n\n"
            "Use the returned content as a starting template — adapt it to the specific task. "
            "Only call when skill instructions reference a specific asset file."
        ),
    )
    def _skills_get_asset(skill_id: str, filename: str = "list") -> str:
        return get_skill_asset(skill_id=skill_id, filename=filename)
  • Import of the `get_skill_asset` function into the local server module.
    from .tools.get_skill_asset import get_skill_asset
  • Alternative async handler `_skills_get_asset` for the Cloudflare Worker deployment. Implements the same logic (list/fetch modes with case-insensitive fallback) but uses OpenSearch/_scroll instead of Qdrant.
    async def _skills_get_asset(skill_id: str, filename: str = "list") -> str:
        QU, QK = _creds()
        if filename in ("list", "", "all"):
            payloads = await _scroll(QU, QK, C_ASSETS, _by_skill(skill_id))
            assets = sorted(
                [
                    {
                        "filename": p.get("filename", ""),
                        "asset_type": p.get("asset_type", "other"),
                        "description": p.get("description", ""),
                        "file_path": p.get("file_path", ""),
                    }
                    for p in payloads
                ],
                key=lambda a: a["filename"],
            )
            return json.dumps(
                {"skill_id": skill_id, "total": len(assets), "assets": assets}, indent=2
            )
    
        payloads = await _scroll(QU, QK, C_ASSETS, _by_skill_file(skill_id, filename), 1)
        if not payloads:
            all_p = await _scroll(QU, QK, C_ASSETS, _by_skill(skill_id))
            match = next(
                (p for p in all_p if p.get("filename", "").lower() == filename.lower()),
                None,
            )
            if match:
                payloads = [match]
    
        if not payloads:
            all_p = await _scroll(QU, QK, C_ASSETS, _by_skill(skill_id))
            return json.dumps(
                {
                    "error": f"Asset '{filename}' not found for skill '{skill_id}'.",
                    "available": [p.get("filename") for p in all_p if p.get("filename")],
                }
            )
    
        p = payloads[0]
        return json.dumps(
            {
                "skill_id": p.get("skill_id", skill_id),
                "skill_name": p.get("skill_name", ""),
                "filename": p.get("filename", filename),
                "file_path": p.get("file_path", ""),
                "asset_type": p.get("asset_type", "other"),
                "description": p.get("description", ""),
                "content": p.get("content", ""),
            },
            indent=2,
        )
  • src/worker.py:293-314 (registration)
    JSON-RPC tool manifest registration (name + inputSchema) for skills_get_asset in the Cloudflare Worker.
    "name": "skills_get_asset",
    "description": (
        "STEP 3c — Fetch a template or static resource bundled with a skill "
        "(markdown templates, config starters, example data files).\n\n"
        "Two-phase use:\n"
        "  1. Call with filename='list' (default) to see the full asset manifest\n"
        "  2. Call again with the specific filename to fetch its content\n\n"
        "Use the returned content as a starting template — adapt it to the specific task. "
        "Only call when skill instructions reference a specific asset file."
    ),
    "inputSchema": {
        "type": "object",
        "properties": {
            "skill_id": {"type": "string"},
            "filename": {
                "type": "string",
                "description": "Exact asset filename to fetch (e.g. 'report-template.md'), or 'list' for manifest",
                "default": "list",
            },
        },
        "required": ["skill_id"],
    },
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden. It adequately discloses the fetch behavior, two-phase workflow, and that the returned content is a starting template. However, it does not mention error handling or behavior for missing files, which would improve transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise, with two short paragraphs and a bulleted list for the two-phase workflow. Every sentence adds value, and the structure makes it easy to follow.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the presence of an output schema (handling return values) and the relatively simple nature of fetching assets, the description covers the essential workflow and usage conditions. It is complete enough for the agent to use the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description adds essential meaning. It explains that filename defaults to 'list' for asset listing and when set to a specific name fetches the asset's content. skill_id is implicitly required but not detailed; still, the description adds significant value beyond the raw schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Fetch' and the resource 'template or static resource bundled with a skill', distinguishing it from sibling tools like skills_get_body or skills_get_options. The step identifier provides context without ambiguity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly describes a two-phase usage pattern: first call with filename='list' to get the manifest, then with a specific filename to fetch content. Also states when to call: 'Only call when skill instructions reference a specific asset file.' This provides clear guidance on when and how to use the tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Jignesh-Ponamwar/skills-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server