Programmatic tool calling plugin for Claude Code
This plugin installs the ipybox MCP server in Claude Code along with a codeact1 skill. It enables Claude Code to act by generating and executing code rather than JSON tool calls. The generated code calls MCP tools and previously saved code actions programmatically.
With this "code as tool" approach, saved code actions become tools themselves, available for use in future code actions. Over time, a library of code actions can be built, each composing other code actions and MCP tools.
The codeact skill provides guidance for Claude Code to discover MCP tools and code actions via agentic filesystem search, inspect the interfaces of relevant tools, generate code actions that use them, and save successful code actions for later reuse.
Tool search and inspection is performed on demand based on the current task. This progressive disclosure approach frees Claude Code from pre-loading tool sources into its context window, saving tokens and improving performance.
Code actions are stored with interface separated from implementation. This separation lets Claude Code inspect only the interface without being distracted by implementation details, further reducing token consumption during tool discovery.
Note
This plugin is a demonstration of the "code as tool" approach. It is a prototype that requires further optimization and refinement.
The codeact skill distributes responsibilities between ipybox and Claude Code:
ipybox:
- Generates typed Python tool APIs from MCP server tool schemas
- Executes Python code that uses the generated MCP tool APIs
- Uses a stateful and sandboxed IPython kernel for code execution
Claude Code:
- Discovers and selects MCP tools and stored code actions via agentic search
- Generates and executes code in ipybox that calls selected MCP tools and code actions
- Adds output parsers and structured result types to MCP tools that lack output schemas
- Saves successful code actions with a structure optimized for discovery and reuse
Environment setup
The example below uses the GitHub MCP server. Create a .env file with your GitHub personal access token (PAT) in the current working directory:
The plugin automatically loads environment variables from this file.
Plugin installation
Add the ipybox repository as plugin marketplace to Claude Code:
Then install one of the available plugins:
| Plugin | Description |
|---|---|
codeact-default |
Runs ipybox without a sandbox |
codeact-sandbox |
Runs ipybox with a sandboxed IPython kernel |
codeact-docker |
Runs ipybox as a Docker container |
Warning
Only one plugin from this marketplace should be active at a time. Having multiple plugins active simultaneously may cause conflicts.
Sandbox configuration
When using codeact-sandbox, you can optionally provide a sandbox-config.json file in the workspace directory to customize the sandbox configuration. If not provided, ipybox runs with the default sandbox.
Usage example
This example demonstrates a complete workflow:
- Registering an MCP server
- Using its tools programmatically
- Generating an output parser
- Chaining tools in a single code action
- Saving the code action for reuse
It uses two tools from the GitHub MCP server:
search_repositorieslist_commits
Register the GitHub MCP server
Register this MCP server at ipybox under name github:
{
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer ${GITHUB_API_KEY}"
}
}
The ${GITHUB_API_KEY} placeholder is automatically replaced with the value from your .env file.
This registers the GitHub MCP server and generates a typed Python API for its tools under mcptools/github/. Each tool becomes a module named after the tool (search_repositories.py, list_commits.py, etc.).
Use the GitHub MCP server programmatically
The codeact skill can be activated in Claude Code with phrases like "use the codeact skill" or similar:
Use the codeact skill to get the latest 5 commits of the 3 github repos
of torvalds with the most stars. For each repo, output name, stars and
the first line of commit messages, and the link to the commit
Claude Code first lists directories under mcptools/ to see which tools are available. It then reads the tool files search_repositories.py and list_commits.py to understand their interfaces, as these appear relevant to the task.
Claude Code generates two code actions. The first searches for the top 3 repos of Linus Torvalds sorted by stars:
import json
from mcptools.github import search_repositories, list_commits
# Search for torvalds' repositories sorted by stars (descending)
repos_result = search_repositories.run(search_repositories.Params(
query="user:torvalds",
sort=search_repositories.Sort.stars,
order=search_repositories.Order.desc,
perPage=10,
minimal_output=False
))
repos_data = json.loads(repos_result)
print(f"Found {repos_data['total_count']} repos")
# Get top 3 repos
top_repos = repos_data['items'][:3]
for repo in top_repos:
print(f"- {repo['name']}: {repo['stargazers_count']} stars")
Here, Claude Code makes assumptions about the tool response structure. The GitHub MCP server tools do not provide output schemas, so Claude Code guesses the structure from its training data. These assumptions may or may not be correct depending on the MCP server's popularity, but in this example they work. MCP tools that provide output schemas get a generated Result class with typed fields, but the GitHub MCP server tools return unstructured strings.
The second code action uses the repository information stored in top_repos to get the latest 5 commits of each repo:
# Get latest 5 commits for each of the top 3 repos
for repo in top_repos:
repo_name = repo['name']
stars = repo['stargazers_count']
print(f"\n{'='*60}")
print(f"📦 {repo_name} | ⭐ {stars:,} stars")
print(f"{'='*60}")
# Get commits
commits_result = list_commits.run(list_commits.Params(
owner="torvalds",
repo=repo_name,
perPage=5
))
commits_data = json.loads(commits_result)
for i, commit in enumerate(commits_data[:5], 1):
sha = commit['sha']
short_sha = sha[:7]
message = commit['commit']['message'].split('\n')[0] # First line only
url = commit['html_url']
print(f"\n{i}. {message}")
print(f" 🔗 {url}")
This prints the requested information. However, intermediate results were added to the agent's context window. To encourage Claude Code to chain search_repositories and list_commits in a single code action, we generate an output parser for search_repositories.
Generate an output parser
To compensate for the lack of output schemas, we generate an output parser for the search_repositories tool:
This adds a run_parsed() function to search_repositories.py, returning a structured ParseResult. Claude Code infers this type by interacting with the tool using example inputs. The codeact skill encourages Claude Code to prioritize run_parsed() over run() when selecting tools.
The parsing implementation details are stored in mcpparse/github/search_repositories.py. Keeping implementation separate from interface prevents polluting the interfaces that Claude Code inspects.
Chaining tools in a single code action
Running the same task again (optionally after restarting Claude Code), Claude Code now chains the tools in a single code action because it knows the structure of the search_repositories result up-front. It uses the new run_parsed() function and navigates the structured output based on the ParseResult type:
Use the codeact skill to get the latest 5 commits of the 3 github repos
of torvalds with the most stars. For each repo, output name, stars and
the first line of commit messages, and the link to the commit
import json
from mcptools.github import search_repositories, list_commits
# Get Torvalds' repos sorted by stars
repos_result = search_repositories.run_parsed(
search_repositories.Params(
query="user:torvalds",
sort=search_repositories.Sort.stars,
order=search_repositories.Order.desc,
perPage=3
)
)
# Get top 3 repos
top_repos = repos_result.repositories[:3]
print(f"Found {len(top_repos)} repos\n")
for repo in top_repos:
print(f"📦 {repo.name} ⭐ {repo.stargazers_count:,} stars")
print("-" * 60)
# Get latest 5 commits
commits_raw = list_commits.run(
list_commits.Params(
owner="torvalds",
repo=repo.name,
perPage=5
)
)
# Parse the commits JSON
commits = json.loads(commits_raw)
for commit in commits[:5]:
sha = commit["sha"][:7]
message = commit["commit"]["message"].split("\n")[0]
url = commit["html_url"]
print(f" {sha}: {message}")
print(f" {url}")
print()
Saving code actions as tools
Code actions can be saved and reused as tools in later code actions. To save the previous code action, use a prompt like:
Save this as code action under github category with name commits_of_top_repos.
Make username, top_n_repos and last_n_commits parameters
This creates a new package gentools/github/commits_of_top_repos/ with an api.py module that defines the typed interface and an impl.py module that contains the implementation.
The interface in api.py exposes the tool's parameters and return types and is relevant for understanding tool usage. The implementation in impl.py is not inspected by Claude Code during tool selection, saving token usage.
Using saved code actions as tools
After restarting Claude Code or clearing its context window, to enforce re-discovery of tools, the same task now uses the saved code action:
Use the codeact skill to get the latest 5 commits of the 3 github repos
of torvalds with the most stars. For each repo, output name, stars and
the first line of commit messages, and the link to the commit
from gentools.github.commits_of_top_repos import run
results = run(username="torvalds", top_n_repos=3, last_n_commits=5)
for repo in results:
print(f"\n## {repo.name} ({repo.stars:,} ⭐)")
print("-" * 60)
for commit in repo.commits:
print(f"• {commit.message}")
print(f" {commit.url}")
To conclude, the usage pattern in this example supports building a library of code actions. Each saved code action becomes a tool available for use in future code actions, enabling composition and reuse.
-
Executable Code Actions Elicit Better LLM Agents: https://arxiv.org/abs/2402.01030 ↩