Build self-improving agents with programmatic tool discovery
Prototype MCP agent that writes and runs TypeScript code composing MCP tools together, with progressive tool discovery and reusable skills.
Why it matters
Enable AI agents to dynamically discover, compose, and execute MCP tools through TypeScript code generation while maintaining state and progressively building new skills across sessions.
Outcomes
What it gets done
Dynamically discover and compose MCP tools programmatically at runtime
Execute TypeScript code to orchestrate multi-tool workflows with state persistence
Build and accumulate reusable skills that agents can leverage in future tasks
Progressively expand agent capabilities through iterative tool experimentation
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/mcp-programmatic-mcp-prototype | bash Overview
Programmatic MCP Prototype
A prototype MCP agent that lets a model write TypeScript code composing multiple MCP tools together in one execution, run in an isolated Docker container, with progressive on-demand tool discovery, persisted workspace state, and reusable skill functions built from generated tool bindings. Use it to explore programmatic MCP tool use, where a model chains many tool calls, loops, and error handling in generated code, instead of the standard one-tool-call-per-turn pattern, particularly when a workflow needs to reuse results across runs.
What it does
An MCP-based agent prototype exploring programmatic tool use: instead of calling one MCP tool per model turn, the agent generates TypeScript bindings for every connected server's tools and lets the model write actual code that composes several tool calls together, with loops, conditionals, and error handling, executed in one pass inside an isolated Docker container. It also supports progressive tool discovery, search_tools and execute_tool meta-tools let the model find relevant tools on demand instead of loading hundreds of tool definitions upfront, state persistence to a workspace directory across executions, and skill building, packaging a multi-step operation into a reusable function the model can call again later.
When to use - and when NOT to
Use it to explore or prototype an agent architecture where tool use benefits from real code rather than a sequence of individual tool calls - chaining multiple operations without round-tripping through the model between each one, storing intermediate results for a later step, or building up a library of reusable skills over a session. It is explicitly a prototype: the core agent loop is described as a simple while loop that can be swapped with other implementations, meaning it is a reference architecture to build on rather than a finished, production-hardened agent framework.
Inputs and outputs
Input is a set of configured MCP servers, config/servers.ts, the example wires up bash, computer, and a container runner, plus an ANTHROPIC_API_KEY for the underlying Claude calls. On startup the code generator creates typed TypeScript functions for every discovered tool, which the agent loop then makes available to the model as source it can import and compose. Output is whatever the generated code produces: files written to the workspace directory, console output, or a returned value from a defined skill function, all executed inside a Docker container built from the project's container-runner image.
Integrations
Ships with example MCP server integrations for bash, shell commands, and computer, general computer-use style actions, proxied through an MCP Proxy Server that aggregates multiple MCP servers into one unified interface for the code generator to bind against. Google Sheets is referenced in the skill-building example (sheets.getCells), illustrating how an additional MCP server would plug into the same pattern. Code execution is isolated via a dedicated Docker image, mcp-runner:latest, built from the container-runner source.
Who it's for
Agent framework developers and researchers exploring programmatic, code-based tool composition as an alternative to turn-by-turn tool calling, who want a working reference implementation to extend rather than a polished end-user product.
Source README
programmatic-mcp-prototype
An MCP-based agent with support for:
- progressive tool discovery
- programmatic tool composition
- state persistence
- skill building
Architecture
- Core Agent Loop: Simple while loop that can be swapped with other implementations
- MCP Proxy Server: Aggregates multiple MCP servers into one unified interface
- Code Generator: Creates TypeScript bindings from MCP tool schemas
- Container Runner: Executes TypeScript code in isolated Docker containers
Key Features
1. Progressive Tool Discovery
The model can search for and discover tools dynamically instead of loading all tools upfront. Rather than exposing hundreds of tools at once, the agent provides search_tools and execute_tool meta-tools that allow the model to find relevant tools as needed, reducing context usage and improving response quality.
2. Programmatic Tool Composition
The agent can write TypeScript code that composes MCP tools together:
// Example: The model can write code like this
import * as bash from './generated/servers/bash';
import * as computer from './generated/servers/computer';
const files = await bash.ls({ path: './documents' });
for (const file of files) {
const content = await bash.readFile({ path: file });
console.log(`File ${file}: ${content.length} bytes`);
}
3. State Persistence
Store intermediate results and data in the workspace directory:
import * as fs from 'fs/promises';
// Save CSV for later use
const csvData = await processData();
await fs.writeFile('./generated/workspace/data.csv', csvData);
// Load it in a future execution
const data = await fs.readFile('./generated/workspace/data.csv', 'utf-8');
4. Skill Building
Create reusable meta-tools that combine multiple operations:
// Build a skill in ./generated/skills/
export async function saveSheetAsCsv(sheetId: string) {
import * as sheets from '../servers/sheets';
import * as bash from '../servers/bash';
const data = await sheets.getCells({ sheetId });
const csv = data.map(row => row.join(',')).join('\n');
const path = `../workspace/sheet-${sheetId}.csv`;
await bash.writeFile({ path, content: csv });
return path;
}
// Use the skill later
import { saveSheetAsCsv } from './generated/skills/save-sheet-as-csv';
const csvPath = await saveSheetAsCsv('abc123');
Setup
- Install dependencies:
npm install
Configure your MCP servers in
config/servers.tsSet your Anthropic API key:
export ANTHROPIC_API_KEY='your-key'
- Build Docker image for code execution:
docker build -t mcp-runner:latest src/servers/container-runner
Usage
Run the agent:
npm start
How It Works
- Startup: Connects to configured MCP servers (bash, computer, container)
- Code Generation: Creates TypeScript functions for each tool with proper types
- Agent Loop: Simple while loop that calls Claude with MCP tools
- Tool Execution: Routes tool calls to appropriate backend MCP servers
- Code Execution: Runs TypeScript in isolated Docker containers
Benefits of Programmatic Tool Use
- Composition: Chain multiple tools without waiting between calls
- State: Store variables and reuse results
- Loops/Conditionals: Handle complex logic in code
- Error Handling: Try/catch and retry logic
- Efficiency: Make many tool calls in one execution
- Skills Library: Build reusable patterns over time
FAQ
Common questions
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.