Skip to content

Repository files navigation

coact

Reuse your "AI stuff" across the layers of the modern agent stack. coact ("co-act" — skills and agents acting as one reusable substrate) turns the skills you already have into agent definitions, and turns those definitions into agents that actually run.

python functions/scripts  →  .claude/skills/  →  .claude/agents/  →  running agents
        (py2mcp, aw)            (skill pkg)        COMPLETE (coact)    REALIZE (coact)

coact owns the two transitions the rest of the ecosystem doesn't:

  • COMPLETE — start from a .claude/skills/ skill and complete it into a .claude/agents/ definition: add the agent-only extras (persona, return contract, tool allowlist, model, memory) that a skill doesn't carry.
  • REALIZE — take a completed definition and produce something that runs, choosing the right backend (the host agent, the Claude Agent SDK, or MCP-exposed tools for a foreign host).

It is glue, not a new framework: it builds on skill (skill data model + registries), aw (the AgenticStep runtime), and py2mcp (Python → MCP). See misc/docs/REUSE.md.

Install

pip install coact            # core: COMPLETE + host realize + analysis (no LLM needed)
pip install coact[sdk]       # + the Claude Agent SDK realize backend (and aw)
pip install coact[mcp]       # + the py2mcp/FastMCP realize backend
pip install coact[litellm]   # + the provider-agnostic LiteLLM realize backend
pip install coact[langgraph] # + the LangGraph realize backend (langchain+langgraph >= 1.0; bundles langchain-openai)
pip install coact[crewai]    # + the CrewAI realize backend

Quick start

from coact import complete, emit_agent, realize

# 1. Complete a skill into an agent definition (mechanical — no LLM).
agent = complete(".claude/skills/ux-analyst")

# 2. See it: a valid .claude/agents/ markdown file.
print(emit_agent(agent, "claude-agents-md"))

# 3. Realize it the cheap way: materialize files so Claude Code runs it.
realize(agent, backend="host")

Prefer to look before you leap? Everything has a dry-run:

from coact import plan_completion

plan = plan_completion(".claude/skills/ux-analyst")
print(plan.render())   # every synthesized field + WHERE it came from + warnings

That extends to the one backend that touches disk: realize(agent, backend="host", dry_run=True) returns the agent files and skill links it would write — without creating any of them.

A complete, runnable walk through all of this — a real skill → completeemitrealize(host)realize(sdk)estimate/inventory — lives in examples/ (no LLM, no API key needed):

python examples/walkthrough.py

CLI

coact plan      .claude/skills/ux-analyst                 # dry-run with provenance
coact complete  .claude/skills/ux-analyst --dest .claude/agents
coact emit      .claude/skills/ux-analyst --target sdk-agent-dict   # a non-default emit target
coact realize   .claude/skills/ux-analyst --backend host
coact realize   .claude/skills/ux-analyst --backend host --dry-run   # preview; writes nothing
coact diff      .claude/skills/ux-analyst .claude/agents/ux-analyst.md
coact estimate  .claude/agents/a.md .claude/agents/b.md   # the cost gate
coact inventory .                                         # skills + agents + MCP tools
coact back      .claude/agents/ux-analyst.md              # lossy agent → skill stub
coact scaffold  .claude/agents/a.md .claude/agents/b.md   # a starter fleet shim (you own it)
coact publish   mypkg.tools:summarize --name my-tools --dry-run   # → a Claude .mcpb (preview)
coact describe  "a tool that looks up the weather for a city"     # NL → a draft IntegrationSpec

Publish — ship a capability to a chatbot host

Beyond COMPLETE/REALIZE, the PUBLISH axis packages a capability (Python tools) as a deployable chatbot integration. The first target, claude-local-mcpb, builds a Claude Desktop .mcpb Desktop Extension — a one-click local (stdio) MCP server, built by py2mcp:

from coact import publish

publish(["mypkg.tools:summarize", "mypkg.tools:translate"],
        name="text-tools", dest="dist")          # → dist/text-tools.mcpb

Sources can be module:function refs, live callables, or a skill carrying a coact: mcp: block. dry_run=True (or --dry-run) previews the bundle without writing it. This is the local surface (stdio, no OAuth). Install: pip install coact[mcpb].

For the remote surface — a claude.ai custom connector (a hosted Streamable-HTTP MCP server reached from Anthropic's cloud over HTTPS + OAuth 2.1) — use the claude-remote-connector target, which scaffolds a deployable service:

from coact import publish_remote
publish_remote(["mypkg.tools:summarize"], name="my-conn", dest="out",
               connector_url="https://my-conn.example.com",   # this server's public URL
               idp_issuer="https://my-idp.example.com")        # your managed IdP
# → out/my-conn-connector/  (server/app.py + connector_config.json + DEPLOY.md + …)

The scaffold is an OAuth 2.1 resource server (validates a managed IdP's audience-bound JWTs; never issues tokens) built by py2mcp's http.mk_http_app; coact writes the deploy packaging, py2mcp/FastMCP serves the MCP. Follow the generated DEPLOY.md. Claude Code plugins, ChatGPT Apps, and Gemini are further planned targets on the same open-closed registry. Background: misc/docs/CHATBOT_INTEGRATION_LANDSCAPE.md.

There are two ways to get an IntegrationSpec. The mechanical ingress above (refs / callables / skills) uses no LLM. The opt-in ingress refines a natural-language description into a draft spec — proposed tools with inferred input schemas — routing generation through aix (multi-provider) via oa:

from coact import integration_spec_from_description

# This routes through aix/oa — it makes a real LLM call (needs a configured
# provider). Inject `llm=<callable>` to run it offline (e.g. in tests).
spec = integration_spec_from_description("expose os.path.basename as a tool")
print(spec.render())   # a DRAFT: a tool becomes a runnable ref only if the model
                       # binds it to module:function code — else it stays proposed

The draft is a design artifact: tools without a module:function handler are proposed (won't run until you bind them to real code). The LLM touches only this path — the code → .mcpb path stays LLM-free (DECISIONS.md D10/D18). Install: pip install coact[nl].

The model in one minute

A SKILL.md is procedural knowledge injected into the caller's turn; a subagent is a separate worker with its own context, persona, tools, model, and a defined return value. They overlap heavily — an agent is mostly a skill plus a thin "extras" envelope. COMPLETE synthesizes that envelope; the two extras that actually matter are:

  1. the persona (system prompt / identity), and
  2. the return contract (a schema so a manager can consume the agent's output).

coact keeps the skill on disk as the single source of truth and makes the agent reference it by name — it never copies a skill body into an agent. One AgentDefinition object serializes to both the filesystem .claude/agents/*.md and the Agent SDK form.

The coact: frontmatter (optional)

To make the lift reproducible, a skill may carry an additive coact: block (ignored by every other tool). When present it wins over policy; when absent coact infers + reports what it guessed.

---
name: ux-analyst
description: Analyze a captured UX evidence bundle for usability issues.
coact:
  tools: [Read, Grep, Glob]
  model: sonnet
  memory: project
  returns:
    schema_ref: ov.schemas:UxFindings
  mcp:
    - module: ov.analyzers
      functions: [score_contrast, find_tap_targets]
---

Realization backends

backend what "running" means cost
host (default) materialize .claude/agents/*.md + link skills; the host agent (Claude Code) executes cheapest — no fan-out
sdk a RunnableAgent backed by the Claude Agent SDK that satisfies aw.AgenticStep (execute(input, context) -> (artifact, info)), so it drops into aw workflows in-process
mcp expose a skill's declared Python tools as a FastMCP server (via py2mcp) for any MCP client tool server
litellm a RunnableLLMAgent (also aw.AgenticStep) backed by LiteLLM — realize the same definition against any provider (OpenAI, Gemini, Mistral, Ollama, …); proof the definition isn't Anthropic-specific in-process
langgraph a RunnableLLMGraphAgent (also aw.AgenticStep) backed by a LangGraph CompiledStateGraph (langchain.agents.create_agent); the graph is exposed (.agent) to drop as a node into your own StateGraph in-process
crewai a RunnableCrewAIAgent (also aw.AgenticStep) backed by a single crewai.Agent (Agent.kickoff); the Agent is exposed (.agent) for your own Crew in-process
agent_step = realize(agent, backend="sdk")          # aw-compatible runnable
artifact, info = agent_step.execute(task, context={})

server = realize(".claude/skills/ux-analyst", backend="mcp")   # FastMCP handle

# same definition, a different provider — map model selectors however you like
llm_step = realize(agent, backend="litellm", model_map={"sonnet": "openai/gpt-4o"})
artifact, info = llm_step.execute(task)             # info["backend"] == "litellm"

# one definition → a LangGraph node / CrewAI Agent you compose into your own topology
graph_step = realize(agent, backend="langgraph")    # graph_step.agent is a CompiledStateGraph
crew_step  = realize(agent, backend="crewai")       # crew_step.agent is a crewai.Agent

Two boundaries to know

  • Topology is out of scope. A subagent definition can't express graphs, conditional edges, or cycles, and subagents can't spawn subagents. coact emits definitions + tool/MCP wiring and stops. Multi-agent orchestration is left to the host's manager or a thin shim you own (against the Agent SDK or aw's workflow chaining) — coact is not LangGraph. It can realize a single definition into a LangGraph node or a CrewAI Agent (the langgraph/crewai backends above), but it builds no graph or crew of its own — you compose the exposed .agent into your topology.

  • A running fleet is an optimization, not a default. Multi-agent fan-out costs roughly an order of magnitude more tokens, and the premium is worst on interdependent tasks. So backend="host" (one agent runs the skills) is the default, and coact estimate shows the tradeoff before you spawn a fleet:

    from coact import estimate
    print(estimate([agent_a, agent_b]).render())

    If you do decide to fan out, scaffold_fleet emits a starter Python shim wiring the realized agents under an aw coordinator — a sequential hand-off you then reshape. It's the one topology-adjacent thing coact writes, and only a starter: coact emits it once and never runs it (the topology stays yours).

    from coact import scaffold_fleet
    scaffold_fleet([agent_a, agent_b], dest="fleet.py")   # a runnable starter you own

Design notes

  • No LLM on any mechanical path; persona drafting is optional and injected (complete(skill, llm=...)), never a hard provider dependency.
  • Open-closed registries for emit targets (claude-agents-md, sdk-agent-dict, plus crewai / openai-tools auto-registered when aw is installed) and realization backends — register your own without touching core.
  • Decisions are recorded in misc/docs/DECISIONS.md; the build brief is misc/docs/COACT_SPEC.md.

Skills

This package ships agent skills you can install into any agent host with gh skill (don't have it? install gh):

gh skill install thorwhalen/coact coact --agent claude-code
gh skill install thorwhalen/coact coact-complete --agent claude-code
gh skill install thorwhalen/coact coact-realize --agent claude-code
gh skill install thorwhalen/coact coact-analyze --agent claude-code
gh skill install thorwhalen/coact coact-publish --agent claude-code
gh skill install thorwhalen/coact coact-dev --agent claude-code
gh skill install thorwhalen/coact ai-assistant-architect --agent claude-code
gh skill install thorwhalen/coact ai-assistant-chat-ui --agent claude-code
gh skill install thorwhalen/coact ai-assistant-prompts-skills --agent claude-code
gh skill install thorwhalen/coact ai-assistant-command-mcp --agent claude-code
gh skill install thorwhalen/coact ai-assistant-agent-runtime --agent claude-code
Skill Use it when…
coact starting with coact — routing to the right capability (turn skills into agents, reuse AI assets across the stack)
coact-complete turning a .claude/skills/ skill into a .claude/agents/ agent definition (COMPLETE)
coact-realize turning an agent definition (or skill) into something that runs — host / Agent SDK / LiteLLM / LangGraph / CrewAI / MCP backends (REALIZE)
coact-analyze inspecting or moving between skill/agent layers — diff, fleet cost estimate, inventory, or harvesting an agent back into a skill
coact-publish packaging a Python capability or skill into a Claude Desktop .mcpb extension or remote claude.ai connector
coact-dev (developer) developing, debugging, or extending the coact package itself — emit targets, realize backends, COMPLETE/REALIZE internals
ai-assistant-architect adding, auditing, or maintaining an embedded AI assistant (chat + agentic) in your own app — the entry point that routes to the focused sub-skills
ai-assistant-chat-ui building or auditing the chat UI / streaming / wire-protocol layer (React + Vite + shadcn, SSE, assistant-ui, Vercel AI SDK)
ai-assistant-prompts-skills managing system prompts, user-editable prompts, or Anthropic-style skills (prompt registry, versioning, evals)
ai-assistant-command-mcp exposing application operations to an assistant via tools / function-calling / MCP (command dispatch → MCP, approval flow, scoping)
ai-assistant-agent-runtime adding or refactoring the agent runtime — the multi-step tool-using loop, optionally with durable execution

About

Reuse your AI stuff across the agent stack: COMPLETE skills into agent definitions and REALIZE them into running agents (glue over skill, aw, py2mcp).

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages