subforge-mcp
CLAUDE.md0 starsChanged 57 days agoMITView on GitHub
MCP server exposing every kind of local LM Studio model over stdio — LLMs (chat/delegate_task), embedding models (rank_by_similarity) and decision models (decide/ask_decision/eval_decisions) — plus consent-gated load/unload. Single package, no monorepo.
- Reads credentials
- Installs packages
In this repository5 other files it gives its agents.
- code-reviewSkill
- developerSkill
- orchestratorSkill
- plannerSkill
- qaSkill
The file
We split this CLAUDE.md into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
Trunk-based: All work lands on main via PR, squash-merged.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
subforge-mcp
MCP server exposing every kind of local LM Studio model over stdio — LLMs (chat/delegate_task), embedding models (rank_by_similarity) and decision models (decide/ask_decision/eval_decisions) — plus consent-gated load/unload. Single package, no monorepo.
Commands
pnpm install
pnpm run build # tsc -> dist/
pnpm test # node:test, runs tests/*.test.ts directly against src/
pnpm run test:live # tests/decision-live.test.ts against a real LM Studio (decision model must be loaded)
pnpm run typecheck # tsc -p tsconfig.test.json (src + tests, noEmit)
pnpm run lint # oxlint --type-aware
pnpm run lint:fixConventions
- Trunk-based. All work lands on
mainvia PR, squash-merged. No long-lived branches. - Conventional Commits required on PR titles (
feat: ...,fix: ...,feat!: .../BREAKING CHANGE:for majors) — enforced by.husky/commit-msglocally and thepr-title-lintCI workflow. semantic-release reads this history onmainto pick the next version, so a mislabeled type/PR title directly produces the wrong release. - Version is derived, not hand-edited.
package.json#versionandCHANGELOG.mdare written by semantic-release on merge (.github/workflows/release.yml) — never bump them by hand.src/index.tsreads the version at runtime frompackage.jsonrather than hardcoding it. - Tests import
src/*.tsdirectly (notdist/) — Node's native TS support runs them without a build step. Keeptests/helpers.tsmocks in sync with any change to theToolExtrashape insrc/index.ts. - No implicit LM Studio model loads. Every inference endpoint (
/v1/chat/completions,/v1/embeddings,/v1/systemone) JIT-loads an unloaded model, so every tool that calls one checks first:requireLoadedModel(chat, delegate_task, rank_by_similarity) orresolveDecisionModel(decision tools), which also reject a model of the wrong type.load_modelis the only tool that loads one, and only behind an MCP elicitation prompt. Don't add a code path that loads a model without going through that.unload_modelis intentionally the one operation not gated behind elicitation — freeing memory is low-risk and reversible, unlike consuming it — so its lack of a consent prompt is deliberate, not an oversight. - Decision tools never throw.
decide/ask_decision/eval_decisionscatch everything and return{ ok: false, error: { code, message } }with probability 0 — callers gate on probability, so a failure must never look like an answer.eval_decisionsstops at the first fatal error (LM Studio down, timeout, wrong/unloaded model) instead of waiting out every case. See docs/adr/0003. - Imports inside
src/use.tsextensions (./decisions.ts) so tests can run the sources directly;rewriteRelativeImportExtensionsturns them into.jsindist/. delegate_tasknever runs outsidedir. All three of its file tools (read_file/list_dir/edit_file) go throughsafeResolvePath— don't add a code path that lets the loop touch anything outside the caller-supplied directory, and never add a shell/Bash tool to its tool definitions. Tests for it use thewithTempDirfixture intests/helpers.ts, which creates and cleans up a real temp directory for exercising real filesystem reads/edits.
CI shape
ci.yml(PRs + push to main):lint/test/typecheckin parallel, thenoutdated(allowed to fail) andaudit(blocking) once those pass.pr-title-lint.yml: rejects non-Conventional-Commit PR titles.release.yml(push to main only): build, thensemantic-release(version, changelog, GitHub release, npm publish). Checks out with aRELEASE_TOKENsecret (fine-grained PAT, Contents read/write) instead of the default token, because main's branch ruleset requires a PR for every update and the defaultgithub-actions[bot]identity can't bypass it — the PAT authenticates as a repo admin, who's on the ruleset's bypass list.
Dependabot config (dependabot.yml + its auto-merge workflow) lives on the separate dependabot-config branch, not on main — see that branch's own PR.
Agentic workflow
This repo uses a fixed set of Claude Code sub-agents (.claude/agents/) entered through matching skills (.claude/skills/). A SessionStart hook loads orchestrator's guidance automatically — new sessions default to delegating rather than implementing inline. See docs/adr/0001 for why.
| Skill | Use it for | Calls |
orchestrator | Default entry point for any non-trivial feature/fix/refactor. Plans, dispatches, reviews, integrates. | planner, developer, code-review, qa |
planner | "What would it take to do X" — a read-only, file-grounded task breakdown, no code written. | none |
developer | Implementing one concrete, already-scoped task, in its own worktree (.worktrees/<branch>). | none |
code-review | Reviewing that same worktree once the developer subagent reports finished — zero developer context, never mid-development. /code-review <branch> works standalone too. See docs/adr/0002. | none |
qa | Runtime smoke-testing a user/tool-facing change (new/changed MCP tool behavior). Skip for refactors and internal-only changes. | none |
Model/effort policy: each sub-agent's .claude/agents/*.md defaults to Sonnet or Haiku at effort: medium or lower. The orchestrator may freely pick Sonnet vs. Haiku per task within that envelope (via the Agent call's model override) — no approval needed. Going past the envelope (Opus, or effort above medium) always requires asking the user first; it's a cost/blast-radius decision, not a judgment call to make silently.
Architecture decisions land in docs/adr/ (one file per decision, docs/adr/template.md to start a new one) — check there before assuming why something is structured a certain way.
MCP servers
.mcp.json declares a project-scoped github MCP server for anyone who clones this repo without the GitHub MCP already configured globally. It reads its token from ${SUBFORGE_GITHUB_TOKEN} (never hardcode a token into .mcp.json — it's committed). Set it locally with a PAT scoped to this repo, e.g. export SUBFORGE_GITHUB_TOKEN=$(gh auth token).
Tooling
- rtk condenses local command output for the assistant; it's configured at the user's global Claude Code profile level (not per-repo) — see the user's own setup, nothing repo-specific needed.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

