{"record":{"id":"a5a56053667fbf92","repo":"OpenBB-finance/OpenBB","slug":"the-files-dict-must-include-a-skill-md-entry-a","errorCode":null,"errorMessage":"The 'files' dict must include a 'SKILL.md' entry as the main skill file.","messagePattern":"The 'files' dict must include a 'SKILL\\.md' entry as the main skill file\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"openbb_platform/extensions/mcp_server/openbb_mcp_server/app/app.py","lineNumber":783,"sourceCode":"            Field(\n                description=(\n                    \"Target skills provider to install into. \"\n                    \"Use 'bundled' for the server's built-in skills directory, \"\n                    \"or a vendor name: \"\n                    + \", \".join(f\"'{k}'\" for k in _VENDOR_SKILLS_PROVIDERS)\n                    + \".\"\n                ),\n            ),\n        ] = \"bundled\",\n    ) -> dict:\n        \"\"\"Install a skill (SKILL.md + supporting files) into a SkillsDirectoryProvider.\n\n        Creates the skill directory if needed, writes all files,\n        and registers the new skill with the target provider so it becomes\n        immediately available via list_resources / read_resource.\n        \"\"\"\n        if \"SKILL.md\" not in files:\n            raise ValueError(\n                \"The 'files' dict must include a 'SKILL.md' entry as the main skill file.\"\n            )\n\n        # Find the target SkillsDirectoryProvider\n        target_key = target.lower().strip()\n        target_provider: SkillsDirectoryProvider | None = None\n\n        for provider in mcp.providers:\n            if not isinstance(provider, SkillsDirectoryProvider):\n                continue\n\n            if target_key == \"bundled\":\n                if settings.default_skills_dir:\n                    bundled_root = Path(settings.default_skills_dir).resolve()\n                    if bundled_root in provider._roots:  # noqa: SLF001\n                        target_provider = provider\n                        break\n            else:","sourceCodeStart":765,"sourceCodeEnd":801,"githubUrl":"https://github.com/OpenBB-finance/OpenBB/blob/3e071fcc2cd9f891cac6040ae60296dba76dab46/openbb_platform/extensions/mcp_server/openbb_mcp_server/app/app.py#L765-L801","documentation":"Raised by the MCP server's install_skill tool when the provided files dict lacks a 'SKILL.md' key. A skill is defined by its SKILL.md manifest; supporting files alone are meaningless, so the server refuses the install before touching the filesystem. It is a strict contract check on the tool arguments.","triggerScenarios":"Calling install_skill(files={'README.md': '...'}, ...) with no SKILL.md entry; keying the manifest as 'skill.md' (lowercase) or 'SKILL.MD'; passing an empty or malformed files dict.","commonSituations":"Automated skill-upload scripts that iterate a directory but skip or rename SKILL.md; case-mismatched filenames on case-sensitive checks; clients copying example payloads that omit the manifest.","solutions":["Add a 'SKILL.md' entry (exact key) to the files dict with the skill's frontmatter and instructions.","Verify the key's casing is exactly 'SKILL.md' before calling the tool.","When uploading from a directory, fail fast locally if SKILL.md is absent rather than sending partial files."],"exampleFix":"# before\nawait install_skill(skill_name=\"my-skill\", target=\"bundled\", files={\"helper.py\": \"...\"})\n\n# after\nawait install_skill(\n    skill_name=\"my-skill\",\n    target=\"bundled\",\n    files={\"SKILL.md\": \"---\\nname: my-skill\\ndescription: ...\\n---\\nDo X\", \"helper.py\": \"...\"},\n)","handlingStrategy":"validation","validationCode":"def validate_skill_files(files: dict[str, str]) -> None:\n    if \"SKILL.md\" not in files:\n        raise ValueError(\"files must contain exactly 'SKILL.md' as the manifest key\")\n    if not files[\"SKILL.md\"].strip():\n        raise ValueError(\"SKILL.md content is empty\")","typeGuard":"def is_installable_skill(files: dict) -> bool:\n    return isinstance(files, dict) and \"SKILL.md\" in files and bool(str(files[\"SKILL.md\"]).strip())","tryCatchPattern":"try:\n    await install_skill(skill_name=name, target=target, files=files)\nexcept ValueError as e:\n    if \"SKILL.md\" in str(e):\n        files = {**files, \"SKILL.md\": default_manifest(name)}\n        await install_skill(skill_name=name, target=target, files=files)\n    else:\n        raise","preventionTips":["Always build the files dict starting from SKILL.md and append supporting files.","Match the key case exactly — 'SKILL.md', not 'skill.md'.","When zipping a directory for upload, fail fast if SKILL.md is missing."],"tags":["openbb","mcp","skills","validation"],"backgroundTag":null,"analyzedSha":"3e071fcc2cd9f891cac6040ae60296dba76dab46","analyzedAt":"2026-08-14T23:40:48.960Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}