feat: ship as a Claude Code plugin#114
Open
GuyNachshon wants to merge 2 commits intohuggingface:mainfrom
Open
Conversation
Adds Claude Code as a second frontend alongside the standalone `ml-intern` CLI.
Both share the same tools under `agent/tools/` — no duplication, no changes to
agent runtime behavior.
What's new:
- `packages/mcp_server/server.py` — MCP server that re-exposes `agent/tools/*`
to Claude Code via stdio. Uses `mcp.server.lowlevel.Server` to preserve the
existing JSON schemas verbatim (FastMCP 3.x re-derives schemas from Python
type hints, which would lose `oneOf`/operation-discriminated structures).
- `CLAUDE.md` — system prompt ported from `agent/prompts/system_prompt_v3.yaml`,
with `plan_tool` → TodoWrite and the `research` tool → Task subagent
substitutions noted.
- `.claude/agents/research.md` — research subagent (read-only HF tool subset).
- `.claude/commands/{ml-intern,research,inspect-dataset,finetune,run-job}.md`
- `.claude/hooks/`:
- `pre_tool_use_approval.py` — port of `_needs_approval` from
`agent/core/agent_loop.py`. Fail-safe on malformed input (forces a prompt
rather than silently allowing).
- `session_start_context.py` — injects HF username + local-mode banner.
- `session_end_upload.py` — uploads transcripts to
`smolagents/ml-intern-sessions` after running through `agent/core/redact.py`.
- `CLAUDE_CODE_GUIDE.md` — user docs.
- `.gitignore` — switch from blanket `.claude/` to specific exclusions
(`.claude/settings.local.json`, `__pycache__`) so shared config is tracked
but per-user overrides are not.
What's *not* changed:
- `agent/` is untouched. The standalone CLI still works exactly as before.
- No new Python deps. The MCP server uses `mcp` and `fastmcp` already in
`pyproject.toml`.
Behavior parity vs. standalone CLI (env var → `Config` field):
ML_INTERN_YOLO → yolo_mode (default 0)
ML_INTERN_CONFIRM_CPU_JOBS → confirm_cpu_jobs (default 1)
ML_INTERN_SAVE_SESSIONS → save_sessions (default 1)
ML_INTERN_SESSION_REPO → session_dataset_repo (default smolagents/ml-intern-sessions)
ML_INTERN_LOCAL_MODE → --local (default 0)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Packages the project-mode files added in the previous commit as a
redistributable Claude Code plugin. After merge, anyone can install
ml-intern into Claude Code with one command and use `/ml-intern`,
`/research`, `/finetune`, etc. in any repository:
/plugin marketplace add huggingface/ml-intern
/plugin install ml-intern@ml-intern
Why a separate `plugin/` dir and not just point Claude Code at this
repo's .claude/ directly:
Plugins must be self-contained — when a user installs, they get a
directory in their `~/.claude/plugins/cache/`, not a clone of this
repo. The MCP server inside the plugin can't `from agent.tools.*`.
Two ways to solve that. This PR ships option 1:
1. Vendor a slim subset of agent/tools/ under plugin/lib/ml_intern_lib/
(this PR — unblocks distribution today).
2. Publish ml-intern-tools to PyPI and have the plugin depend on it
(cleaner long-term, requires extracting agent/tools/ as a clean
sub-package; happy to do as a follow-up if maintainers prefer).
How vendoring stays in sync:
`scripts/sync_plugin_vendored.py` is idempotent. After editing
`agent/tools/*` or `agent/core/redact.py`, run it and commit. The script:
1. Copies tool files (minus research_tool, plan_tool,
private_hf_repo_tools — first two replaced by Claude Code natives,
third disabled upstream).
2. Copies agent/core/redact.py.
3. Rewrites imports `from agent.X` → `from ml_intern_lib.X`.
4. Verifies zero leftover `agent.*` imports.
A new GitHub Actions workflow (`.github/workflows/plugin-vendor-sync.yml`)
runs the sync script on every PR that touches the source or the vendored
copy and fails on diff — guarantees the plugin can never silently fall
out of sync.
Plugin slim deps:
The plugin's pyproject.toml deliberately drops litellm, boto3, fastapi,
prompt-toolkit, rich — none are needed when Claude Code is the runtime.
First-time install ~15s vs ~60s for the full repo.
Tools registered (15):
Local MCP server (plugin/lib/mcp_server.py):
explore_hf_docs, fetch_hf_docs, hf_papers, hf_inspect_dataset,
hf_jobs, hf_repo_files, hf_repo_git, github_find_examples,
github_list_repos, github_read_file, plus sandbox bash/read/write/edit
and sandbox_create.
Hosted MCP server: huggingface.co/mcp (HTTP + Bearer ${HF_TOKEN}).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat: ship as a Claude Code plugin
Summary
Packages the project-mode files added in # as a redistributable Claude Code plugin. After this PR merges, anyone can install ml-intern into Claude Code with one command and use
/ml-intern,/research,/finetune, etc. in any repository:Why a separate
plugin/dir?Plugins must be self-contained — when a user installs, they get a directory in their
~/.claude/plugins/cache/, not a clone of this repo. The MCP server inside the plugin can'tfrom agent.tools.* import ….Two ways to solve this:
agent/tools/andagent/core/redact.pyunderplugin/lib/ml_intern_lib/. This PR ships option 1.ml-intern-toolsto PyPI as a clean sub-package and have the plugin depend on it. Cleaner long-term; requires extractingagent/tools/with its own pyproject and changing how the standalone CLI imports tools. Happy to do as a follow-up if maintainers prefer.I went with option 1 because it unblocks distribution today. Open to switching to option 2 — see the related issue for discussion.
How vendoring stays in sync
scripts/sync_plugin_vendored.pyis idempotent. After editingagent/tools/*oragent/core/redact.py, run it and commit. The script:agent/tools/*.py(minusresearch_tool,plan_tool,private_hf_repo_tools— first two replaced by Claude Code natives, third disabled upstream).agent/core/redact.py.from agent.X→from ml_intern_lib.X.agent.*imports.A new GitHub Actions workflow (
.github/workflows/plugin-vendor-sync.yml) runs the sync script on every PR that touches the source or the vendored copy and fails on diff. The plugin can never silently fall out of sync.Plugin slim deps
The plugin's
pyproject.tomldeliberately dropslitellm,boto3,fastapi,prompt-toolkit,rich— none are needed when Claude Code is the runtime. First-time install ~15s vs. ~60s for the full repo.Tools registered (15)
Local stdio MCP server (
plugin/lib/mcp_server.py):explore_hf_docs,fetch_hf_docs,hf_papers,hf_inspect_dataset,hf_jobs,hf_repo_files,hf_repo_git,github_find_examples,github_list_repos,github_read_file, plus sandboxbash/read/write/editandsandbox_create.Hosted HTTP MCP server:
huggingface.co/mcpwithAuthorization: Bearer ${HF_TOKEN}.These appear in Claude Code as
mcp__ml-intern__ml-intern-tools__<name>(the plugin-prefixed form). Tool names match the standalone CLI exactly minus the prefix.Test plan
cd plugin && uv syncresolves cleanly (~15s).uv run python plugin/lib/mcp_server.py < /dev/nullboots cleanly._build_registry().scripts/sync_plugin_vendored.pyis idempotent — running twice produces zero diff.plugin-vendor-sync.ymlcorrectly fails whenagent/tools/is edited without re-running the script.agent/tools/source of truth.What's in this PR
Anticipated review feedback
session_stub.py,tool_spec.py,telemetry_stub.py)?" A handful of vendored tools (jobs_tool,sandbox_tool) callsession.send_event(Event(...))for in-process telemetry and readsession.hf_token. The MCP server has noSession— these shims swallow events and read tokens from env. Drop-in replacements; no behavior change.huggingfacebut I seeguynachshonURLs in the fork." Fork URLs were development-only; this PR's branches use upstream URLs throughout. Verified withgrep.plugin/uv.lock?" It gets generated on first install. Tracking it would create merge conflicts on every dependency change. Plugin install is fast enough (~15s) that lockfile reproducibility isn't critical — happy to add it back if maintainers prefer.Marketplace listing
Once merged, this repo becomes a Claude Code marketplace. The
.claude-plugin/marketplace.jsonat repo root is what/plugin marketplace add huggingface/ml-internresolves. The single plugin entry points at./plugin.