Too many MCP tools slowing your agent down? Might be a Skill Issue π
skillful-mcp eliminates tool bloat by turning your MCP servers into Agent Skills in an MCP-native way.
- π Progressive Disclosure β start with 4 tools, discover more as needed
- β‘ Code Mode β trigger and combine multiple tool calls with Python
- π Secure sandbox β code executes in a sandbox, not your shell
- π Any MCP client β works with Gemini CLI, Claude Code, Codex, and more
Connecting an agent to too many tools (or MCP servers) creates tool bloat. An agent with access to 5 servers might have 80+ tools loaded into its context window before the user says a word. Accuracy drops, latency increases, and adding capabilities makes the agent worse.
skillful-mcp fixes this through progressive disclosure. The agent sees just 4 tools and discovers specific schemas on-demand, collapsing thousands of tokens down to a lightweight index.
Agent <--MCP--> skillful-mcp <--MCP--> Database Server
<--MCP--> Filesystem Server
<--MCP--> API Server
skillful-mcp reads a standard mcp.json config, connects to each downstream
server, and exposes four tools:
| Tool | Description |
|---|---|
list_skills |
Returns the names of all configured downstream servers |
use_skill |
Lists the tools and resources available in a specific skill |
read_resource |
Reads a resource from a specific skill |
execute_code |
Runs Python code in a secure Monty sandbox |
The typical agent workflow:
- Call
list_skillsto see what's available - Call
use_skillto inspect a skill's tools and their input schemas - Use
execute_codeto orchestrate tool calls in a single round-trip
After discovering tools via use_skill, the agent can call them directly by
name inside execute_code β chaining outputs from one tool into another:
# Query users, then send each one a welcome email
users = query(sql="SELECT name, email FROM users WHERE welcomed = false")
for user in users:
send_email(to=user["email"], subject="Welcome!", body="Hi " + user["name"])
"Sent " + str(len(users)) + " welcome emails"All downstream tools are available as functions with positional and keyword
arguments. If two skills define a tool with the same name, the function is
prefixed with the skill name (e.g. database_search, docs_search). Tool
names returned by use_skill always match the function names in execute_code.
Download a binary
VERSION="0.0.1"
OS="linux" # or: darwin, windows
ARCH="amd64" # or: arm64
curl -L "https://github.com/kurtisvg/skillful-mcp/releases/download/v${VERSION}/skillful-mcp_${VERSION}_${OS}_${ARCH}" -o skillful-mcp
chmod +x skillful-mcpOr download from the releases page.
Docker
docker run --rm \
-v /path/to/mcp.json:/mcp.json \
ghcr.io/kurtisvg/skillful-mcp:latest \
--config /mcp.json --transport http --port 8080Go install (requires Go 1.25+)
go install github.com/kurtisvg/skillful-mcp@latestBuild from source
git clone https://github.com/kurtisvg/skillful-mcp.git
cd skillful-mcp
go build -o skillful-mcp .Create an mcp.json file with your downstream servers:
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@toolbox-sdk/server", "--prebuilt=postgres"],
"description": "Postgres database tools β query, inspect schemas, and manage tables. Use when the user needs to read or write data, explore table structures, or run SQL.",
"env": {
"POSTGRES_HOST": "${POSTGRES_HOST}",
"POSTGRES_USER": "${POSTGRES_USER}",
"POSTGRES_PASSWORD": "${POSTGRES_PASSWORD}",
"POSTGRES_DATABASE": "${POSTGRES_DATABASE}"
}
},
"github-issues": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/x/issues",
"headers": {
"Authorization": "Bearer ${GITHUB_TOKEN}"
},
"description": "GitHub issue management β create, search, update, and comment on issues. Use when the user mentions bugs, feature requests, or issue triage."
}
}
}skillful-mcp --config mcp.jsonOr over HTTP:
skillful-mcp --config mcp.json --transport http --port 8080Gemini CLI (~/.gemini/settings.json)
{
"mcpServers": {
"skillful": {
"command": "/path/to/skillful-mcp",
"args": ["--config", "/path/to/mcp.json"]
}
}
}Claude Code (.claude/settings.json)
{
"mcpServers": {
"skillful": {
"command": "/path/to/skillful-mcp",
"args": ["--config", "/path/to/mcp.json"]
}
}
}Codex CLI (~/.codex/config.toml)
[mcp_servers.skillful]
command = "/path/to/skillful-mcp"
args = ["--config", "/path/to/mcp.json"]Any MCP-compatible client works β just point it at the skillful-mcp binary.
The GitHub MCP server exposes
19+ toolsets β a perfect candidate for skill decomposition. Instead of one
massive server, split it into focused skills by feature group. The agent sees
4 skills instead of 40+ tools, and calls use_skill only when it needs a
specific capability.
{
"mcpServers": {
"github-issues": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/x/issues",
"headers": {
"Authorization": "Bearer ${GITHUB_TOKEN}"
},
"description": "GitHub issue management β create, search, update, and comment on issues. Use when the user mentions bugs, feature requests, or issue triage."
},
"github-labels": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/x/labels",
"headers": {
"Authorization": "Bearer ${GITHUB_TOKEN}"
},
"description": "GitHub label management β create, assign, and remove labels. Use when organizing or categorizing issues and pull requests."
},
"github-prs": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/x/pull_requests",
"headers": {
"Authorization": "Bearer ${GITHUB_TOKEN}"
},
"description": "GitHub pull request workflows β review, merge, and manage PRs. Use when the user asks about code review, PR status, or merging changes."
},
"github-actions": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/x/actions",
"headers": {
"Authorization": "Bearer ${GITHUB_TOKEN}"
},
"description": "GitHub Actions CI/CD β trigger, monitor, and debug workflows. Use when the user asks about build status, failed checks, or re-running pipelines."
}
}
}Each entry in mcpServers is a downstream server that becomes a skill. The key
is the skill name. The value depends on the transport type.
All string values support ${VAR} environment variable expansion. Missing
variables cause a startup error.
All server types support these optional fields:
| Field | Description |
|---|---|
description |
Override the server's instructions shown by list_skills |
allowedTools |
Only expose these tool names (default: all) |
allowedResources |
Only expose these resource URIs (default: all) |
Excluded tools are invisible everywhere β they won't appear in use_skill,
can't be called via execute_code, and won't cause name-conflict prefixing.
Spawns the server as a child process. Only env vars explicitly listed in env
are passed to the child β the parent environment is not inherited.
| Field | Required | Description |
|---|---|---|
command |
yes | Executable to run |
args |
no | Arguments array |
env |
no | Environment variables for the child process |
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@toolbox-sdk/server", "--prebuilt=postgres"],
"description": "Postgres database tools β query, inspect schemas, and manage tables. Use when the user needs to read or write data, explore table structures, or run SQL.",
"env": {
"POSTGRES_HOST": "${POSTGRES_HOST}",
"POSTGRES_USER": "${POSTGRES_USER}",
"POSTGRES_PASSWORD": "${POSTGRES_PASSWORD}",
"POSTGRES_DATABASE": "${POSTGRES_DATABASE}"
}
}
}
}Connects via Streamable HTTP.
| Field | Required | Description |
|---|---|---|
type |
yes | Must be "http" |
url |
yes | Server endpoint URL |
headers |
no | HTTP headers (e.g. auth tokens) |
{
"mcpServers": {
"remote-api": {
"type": "http",
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer ${API_KEY}"
}
}
}
}Connects via Server-Sent Events.
| Field | Required | Description |
|---|---|---|
type |
yes | Must be "sse" |
url |
yes | SSE endpoint URL |
headers |
no | HTTP headers |
| Flag | Default | Description |
|---|---|---|
--config |
./mcp.json |
Path to the config file |
--transport |
stdio |
Upstream transport: stdio or http |
--host |
localhost |
HTTP listen host |
--port |
8080 |
HTTP listen port |
--version |
Print version and exit |