Skip to main content
Glama

import_github_core

Download MIT-licensed GitHub repositories and add them to the local IP core registry for immediate use in FPGA development workflows.

Instructions

Download an MIT-licensed GitHub repository and add it to the local IP core registry. Automatically uses FuseSoC CAPI2 metadata (.core file) if one exists in the repo. After import, the core is immediately available via get_ip_core and generate_ip.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
repoYesGitHub repo in 'owner/repo' format, e.g. 'ultraembedded/core_uart'
subdirNoSubdirectory within the repo to scope HDL search (for monorepos)
refNoBranch, tag, or commit SHA (default: repo's default branch)

Implementation Reference

  • Core implementation of import_core function that downloads a GitHub repo, validates license, fetches HDL files, optionally uses FuseSoC metadata, and writes the core manifest to the local registry.
    def import_core(
        owner_repo: str,
        subdir: str = "",
        ref: str = "",
        dest_parent: Path = Path(__file__).parent.parent / "cores",
    ) -> dict:
        """Download a GitHub repo and write it into dest_parent/<core_name>/.
    
        Automatically uses FuseSoC CAPI2 metadata if a .core file is found.
        Returns the manifest dict on success or an error dict.
        """
        parts = owner_repo.strip("/").split("/")
        if len(parts) != 2:
            return {"error": "repo must be 'owner/repo', e.g. 'ultraembedded/core_uart'"}
        owner, repo = parts
        subdir_norm = subdir.strip("/")
    
        # Repo metadata
        try:
            meta = _get(f"{GITHUB_API}/repos/{owner}/{repo}")
        except httpx.HTTPStatusError as e:
            return {"error": f"GitHub API {e.response.status_code}: {e.response.text}"}
        except Exception as e:
            return {"error": str(e)}
    
        license_id = (meta.get("license") or {}).get("spdx_id", "unknown")
        allowed = _allowed_licenses()
        if license_id not in allowed:
            return {
                "error": (
                    f"Repository license '{license_id}' is not in the allowed list: "
                    f"{', '.join(sorted(allowed))}. "
                    "Set FPGAZERO_ALLOWED_LICENSES to override."
                )
            }
        if not ref:
            ref = meta.get("default_branch", "main")
    
        # File tree
        try:
            tree = _fetch_tree(owner, repo, ref)
        except Exception as e:
            return {"error": f"Could not fetch file tree: {e}"}
    
        hdl_paths, core_paths = _filter_files(tree, subdir_norm)
        if not hdl_paths:
            scope = f"{owner_repo}/{subdir_norm}" if subdir_norm else owner_repo
            return {"error": f"No HDL files (.v/.sv/.vhd) found in {scope}"}
    
        # Try FuseSoC .core file for richer metadata
        fuse_manifest: dict | None = None
        if core_paths:
            try:
                raw = _download_raw(owner, repo, core_paths[0], ref)
                from registry.fusesoc import capi2_to_manifest_dict
                fuse_manifest = capi2_to_manifest_dict(raw, source=f"https://github.com/{owner_repo}")
            except Exception:
                pass  # non-fatal
    
        # Download HDL files (preserve relative paths)
        core_name = repo.lower().replace("-", "_").replace(".", "_")
        dest_dir  = dest_parent / core_name
        dest_dir.mkdir(parents=True, exist_ok=True)
    
        downloaded: list[str] = []
        for path in hdl_paths:
            try:
                content = _download_raw(owner, repo, path, ref)
                rel_path = Path(path)
                if subdir_norm:
                    rel_path = rel_path.relative_to(subdir_norm)
                target = dest_dir / rel_path
                target.parent.mkdir(parents=True, exist_ok=True)
                target.write_text(content, encoding="utf-8")
                downloaded.append(rel_path.as_posix())
            except Exception as e:
                # Write a stub so the manifest isn't broken
                rel_path = Path(path)
                if subdir_norm:
                    rel_path = rel_path.relative_to(subdir_norm)
                target = dest_dir / rel_path
                target.parent.mkdir(parents=True, exist_ok=True)
                target.write_text(f"// Download failed: {e}\n", encoding="utf-8")
                downloaded.append(rel_path.as_posix())
    
        # Build manifest (FuseSoC wins if available)
        description = meta.get("description") or f"Imported from github.com/{owner_repo}"
        topics      = meta.get("topics", [])
    
        manifest = fuse_manifest or {
            "name":        core_name,
            "version":     ref,
            "description": description,
            "author":      owner,
            "license":     license_id,
            "language":    _dominant_language(hdl_paths),
            "category":    _infer_category(" ".join(topics) + " " + repo + " " + description),
            "tags":        topics[:8],
            "parameters":  {},
            "ports":       {},
            "files":       downloaded,
        }
    
        # Always update files list to what was actually downloaded
        manifest["files"]  = downloaded
        manifest["source"] = f"https://github.com/{owner_repo}"
    
        (dest_dir / "core.json").write_text(json.dumps(manifest, indent=2), encoding="utf-8")
    
        return {
            "imported":      True,
            "core_name":     core_name,
            "source":        f"https://github.com/{owner_repo}",
            "ref":           ref,
            "license":       license_id,
            "files_fetched": downloaded,
            "fusesoc_used":  fuse_manifest is not None,
            "manifest":      manifest,
        }
  • CoreRegistry.import_github_core method that wraps the import_core function, passing the destination directory and reloading the cache after successful import.
    def import_github_core(
        self,
        owner_repo: str,
        subdir: str = "",
        ref: str = "",
    ) -> dict:
        """Download a GitHub repo and register it as a local core."""
        from registry.github import import_core
        result = import_core(
            owner_repo=owner_repo,
            subdir=subdir,
            ref=ref,
            dest_parent=self._dir,
        )
        if result.get("imported"):
            self._load_all()
        return result
  • Tool registration defining the 'import_github_core' tool with inputSchema specifying repo (required), subdir (optional), and ref (optional) parameters.
    types.Tool(
        name="import_github_core",
        description=(
            "Download an MIT-licensed GitHub repository and add it to the local IP core registry. "
            "Automatically uses FuseSoC CAPI2 metadata (.core file) if one exists in the repo. "
            "After import, the core is immediately available via get_ip_core and generate_ip."
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "repo": {
                    "type": "string",
                    "description": "GitHub repo in 'owner/repo' format, e.g. 'ultraembedded/core_uart'",
                },
                "subdir": {
                    "type": "string",
                    "description": "Subdirectory within the repo to scope HDL search (for monorepos)",
                },
                "ref": {
                    "type": "string",
                    "description": "Branch, tag, or commit SHA (default: repo's default branch)",
                },
            },
            "required": ["repo"],
        },
    ),
  • server.py:456-462 (registration)
    Dispatch handler case for 'import_github_core' that calls registry.import_github_core with the arguments from the tool invocation.
    case "import_github_core":
        result = await asyncio.to_thread(
            registry.import_github_core,
            owner_repo=arguments["repo"],
            subdir=arguments.get("subdir", ""),
            ref=arguments.get("ref", ""),
        )

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/bard0-design/fpgaZeroMCP'

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