skillgraph-mcp

command module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 29, 2026 License: MIT Imports: 1 Imported by: 0

README

skillgraph-mcp

Go CI Go Report Card

Too many MCP tools slowing your agent down? Might be a Skill Issue.

Fork notice. skillgraph-mcp builds on top of kurtisvg/skillful-mcp by Kurtis Van Gent (MIT). This fork adds: a semantic skill graph + plan_workflow tool, a self-evolving description loop (SkillOpt), parallel downstream connection on startup, graceful degradation when individual downstreams fail, CLI subcommands (doctor, validate, list-skills), and Mistral as the recommended LLM provider.

skillgraph-mcp eliminates tool bloat by building a semantic capability relationship graph and turning your MCP servers into Agent Skills in an MCP-native way.

  • Progressive Disclosure — agent sees 8 lightweight tools; downstream schemas loaded on demand
  • 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

Table of contents

Why?

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.

skillgraph-mcp fixes this through progressive disclosure. The agent sees 8 lightweight gateway tools and discovers specific downstream schemas on-demand, collapsing thousands of tokens down to a lightweight index.

Not an infrastructure gateway. Projects like Microsoft MCP Gateway and IBM AI Gateway MCP solve the ops problem — Kubernetes deployment, RBAC, adapter lifecycle. skillgraph-mcp solves the agent cognition problem: which tools exist, how they relate, and how to get better at routing them over time. They are complementary, not competing.

How it works

flowchart LR
    subgraph Agent["AI Agent"]
        A["Claude Code\nGemini CLI\nCodex..."]
    end

    subgraph GW["skillgraph-mcp gateway  (8 tools)"]
        direction TB
        subgraph Discover["Discovery"]
            T1["list_skills"]
            T2["use_skill"]
            T3["read_resource"]
        end
        subgraph Execute["Execution"]
            T4["execute_code"]
            T5["register_server"]
        end
        subgraph Graph["Graph & Planning"]
            T6["get_skill_graph"]
            T7["plan_workflow"]
            T8["read_lattice"]
        end
        SG[("Semantic\nSkill Graph")]
        SO["SkillOpt loop\ntraces → LLM → better descriptions"]
        Discover --> SG
        Graph --> SG
        Execute -.->|"writes traces"| SO
        SO -.->|"RebuildGraph"| SG
    end

    subgraph Servers["Downstream MCP Servers"]
        S1["brave-search"]
        S2["firecrawl"]
        S3["context7"]
        S4["gitnexus"]
        S5["...any MCP server"]
    end

    A -- "8 tools via MCP" --> GW
    GW -- "MCP (full schemas on demand)" --> Servers

skillgraph-mcp reads a standard mcp.json config, connects to each downstream server, and exposes eight tools:

Tool Description
list_skills Lists all configured skills with their descriptions
use_skill Lists the tools and resources of a specific skill. Tool names match the function names available in execute_code (hyphens are sanitised to underscores).
read_resource Reads a resource from a specific skill
execute_code Runs Python code in a secure gomonty sandbox
register_server Hot-registers a new downstream MCP server at runtime. Warning: see the security note under Gotchas before exposing this over HTTP.
get_skill_graph Returns the capability relationship graph
plan_workflow Receives a high-level goal and returns a recommended path of execution
read_lattice Reads files from the generated .mcp_lattice semantic documentation index

The typical agent workflow:

  1. Call list_skills to see what's available.
  2. Call get_skill_graph or plan_workflow to inspect relations and plan the steps.
  3. Call use_skill to inspect a skill's tools and their input schemas.
  4. Use execute_code to orchestrate tool calls in a single round-trip.
sequenceDiagram
    participant A as AI Agent
    participant GW as skillgraph-mcp
    participant S as Downstream Server

    A->>GW: list_skills
    GW-->>A: ["brave-search", "gitnexus", ...]

    A->>GW: plan_workflow("search and summarise repo issues")
    GW-->>A: 1. brave_search → 2. github_list_issues → 3. execute_code

    A->>GW: use_skill("brave-search")
    GW-->>A: brave_web_search(query, count, ...) schema

    A->>GW: execute_code(python)
    GW->>S: brave_web_search(...)
    S-->>GW: results
    GW-->>A: computed result + trace written
Self-evolving optimization (SkillOpt)

skillgraph-mcp implements a self-evolving skill optimization mechanism inspired by Microsoft's SkillOpt framework (arXiv:2605.23904):

  • Trajectory Logging: When the agent runs Python code via execute_code, the gateway records execution trajectories (rollouts) including arguments, results, and runtime errors under .mcp_lattice/traces/ (relative to the CWD where skillgraph-mcp is launched).
  • Background Refinement: A background daemon periodically reads these traces and — only when errors are present — prompts an LLM to propose text-space optimization edits (add/replace/delete tool descriptions or graph relations) in mcp.json.
  • Zero Latency: Updates are verified and applied atomically, ensuring that future routing and discovery prompts are optimized dynamically without model retraining.

To enable the SkillOpt and bootstrap refinement loops, set one of these environment variables:

Variable Provider Notes
LLM_BASE_URL Any OpenAI-compatible API LiteLLM proxy, Ollama, etc. Set LLM_API_KEY and LLM_MODEL alongside
OPENAI_API_KEY OpenAI Uses gpt-5.4-nano by default; override with LLM_MODEL
DEEPSEEK_API_KEY DeepSeek Uses deepseek-v4-flash
GEMINI_API_KEY Google Gemini Uses gemini-3.1-flash-lite

LLM_BASE_URL takes priority. When set, LLM_API_KEY is optional (Ollama and other local servers don't require auth).

Examples:

# Mistral (recommended — EU-hosted GDPR-friendly, 500k TPM free tier, OpenAI-compatible)
LLM_BASE_URL=https://api.mistral.ai/v1 LLM_API_KEY=$MISTRAL_API_KEY LLM_MODEL=mistral-small-latest skillgraph-mcp --config mcp.json

# Ollama (local, no auth, no data leaves the machine)
LLM_BASE_URL=http://localhost:11434/v1 LLM_MODEL=llama3.1 skillgraph-mcp --config mcp.json

# LiteLLM proxy (routes to any backend)
LLM_BASE_URL=http://localhost:4000 LLM_API_KEY=sk-... LLM_MODEL=anthropic/claude-3-5-haiku skillgraph-mcp --config mcp.json

# OpenAI directly
OPENAI_API_KEY=sk-... skillgraph-mcp --config mcp.json

Warning — privacy: SkillOpt sends execution trajectories (Python code, tool arguments, errors) to the configured LLM provider. If any downstream tool receives secrets as arguments — API keys, tokens, passwords — those values will be in the prompts sent to the LLM. Run with a local model (Ollama, vLLM) or no LLM at all if your traces may contain sensitive data.

Example code mode usage

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.

Getting started

Install
Download a binary
VERSION="0.1.0"
OS="linux"       # or: darwin, windows
ARCH="amd64"     # or: arm64

curl -L "https://github.com/jrodeiro5/skillgraph-mcp/releases/download/v${VERSION}/skillgraph-mcp_${VERSION}_${OS}_${ARCH}" -o skillgraph-mcp
chmod +x skillgraph-mcp

Or download from the releases page.

Docker
docker run --rm \
  -v /path/to/mcp.json:/mcp.json \
  ghcr.io/jrodeiro5/skillgraph-mcp:latest \
  --config /mcp.json --transport http --port 8080
Go install (requires Go 1.26+)
go install github.com/jrodeiro5/skillgraph-mcp@latest
Build from source
git clone https://github.com/jrodeiro5/skillgraph-mcp.git
cd skillgraph-mcp
go build -o skillgraph-mcp .
Create a config

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."
    }
  }
}
Run
skillgraph-mcp --config mcp.json

Or over HTTP:

skillgraph-mcp --config mcp.json --transport http --port 8080
Diagnostics & pre-flight

Before wiring the gateway into your agent it's worth checking that the environment is healthy and every downstream server actually starts. The binary ships with three read-only subcommands for this:

skillgraph-mcp doctor       --config mcp.json   # binary, config, lattice dir, LLM provider, runtime
skillgraph-mcp validate     --config mcp.json   # connect to every downstream in parallel, table of status + tool count
skillgraph-mcp list-skills  --config mcp.json   # connect + dump skill | tools | description

All three accept --json for machine-readable output. validate exits non-zero when any server fails to connect, so it works as a pre-commit / CI check.

Connect to your agent
Gemini CLI (~/.gemini/settings.json)
{
  "mcpServers": {
    "skillgraph": {
      "command": "/path/to/skillgraph-mcp",
      "args": ["--config", "/path/to/mcp.json"]
    }
  }
}
Claude Code (.claude/settings.json)
{
  "mcpServers": {
    "skillgraph": {
      "command": "/path/to/skillgraph-mcp",
      "args": ["--config", "/path/to/mcp.json"]
    }
  }
}
Codex CLI (~/.codex/config.toml)
[mcp_servers.skillgraph]
command = "/path/to/skillgraph-mcp"
args = ["--config", "/path/to/mcp.json"]

Any MCP-compatible client works — just point it at the skillgraph-mcp binary.

Advanced example: GitHub MCP Server

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."
    }
  }
}

Configuration

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.

Common options

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.

STDIO server

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}"
      }
    }
  }
}
HTTP server

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}"
      }
    }
  }
}
SSE server

Connects via Server-Sent Events.

Field Required Description
type yes Must be "sse"
url yes SSE endpoint URL
headers no HTTP headers
Skill graph (manual overrides)

The optional skillGraph section in mcp.json lets you manually define descriptions and relationships that the agent uses for routing and planning. The LLM refinement loops also write here — you can seed it by hand or let it auto-populate.

{
  "mcpServers": { "...": {} },
  "skillGraph": {
    "descriptions": {
      "github-issues": "GitHub issue management — create, search, update, and triage issues.",
      "github_create_issue": "Opens a new issue. Requires a repository name and title."
    },
    "relations": [
      {
        "source": "github_search_issues",
        "target": "github_create_issue",
        "type": "COMMON_NEXT_STEP",
        "description": "After searching, you may want to open a related issue"
      },
      {
        "source": "github_create_issue",
        "target": "github_add_label",
        "type": "PREREQUISITE_FOR",
        "description": "Label the issue after creating it"
      }
    ]
  }
}

Descriptions override the tool/server text shown by list_skills, use_skill, and plan_workflow. Both server names and individual tool names are valid keys.

Relations define typed edges in the capability graph. Valid type values:

Type Meaning
PREREQUISITE_FOR source must run before target
PRODUCES source output is consumed by target
REQUIRES source needs target's output as input
COMMON_NEXT_STEP target is commonly called after source
Automatic relation inference

skillgraph-mcp also infers PREREQUISITE_FOR edges automatically at startup. If a tool has a parameter ending in _id or _number, it looks for other tools whose names contain create_<prefix>, get_<prefix>, or search_<prefix> and adds a prerequisite edge from them. For example, comment_on_issue(issue_number: int) will automatically get a PREREQUISITE_FOR edge from create_issue and search_issues — no config needed.

Manual relations entries are merged on top of inferred ones.

Flags
Flag Default Description
--config ./mcp.json Path to the config file
--lattice-dir ./.mcp_lattice Directory for traces, READMEs, and lattice docs
--transport stdio Upstream transport: stdio or http
--host localhost HTTP listen host
--port 8080 HTTP listen port
--version Print version and exit

Gotchas

STDIO child processes get a blank environment. When command servers are launched, they inherit only the variables explicitly listed in the env block — not the parent shell's environment. If a child process needs PATH, HOME, or any other system variable, you must pass it explicitly:

"env": {
  "PATH": "${PATH}",
  "HOME": "${HOME}",
  "MY_API_KEY": "${MY_API_KEY}"
}

.mcp_lattice/ is relative to CWD by default. Traces, READMEs, and lattice files are written to .mcp_lattice/ in the working directory where skillgraph-mcp is launched — not next to the config file. Use --lattice-dir /absolute/path to pin it to a fixed location.

mcp.json is mutated at runtime. The skillGraph section (descriptions and relations) is auto-populated and updated by the background refinement loops. Keep a copy if you want to preserve a known-good baseline, or use git to track changes.

register_server over --transport http is RCE-by-design. The register_server gateway tool lets any MCP client write a new entry to mcp.json — including a command field that the gateway will then exec. Combined with --transport http (which exposes the MCP protocol over an unauthenticated HTTP endpoint), anyone who can reach the port can register and run arbitrary processes as the gateway's user. Stick to --transport stdio unless you have an authenticating reverse proxy in front, and prefer to keep register_server off the allow-list for HTTP deployments.

--transport http has no authentication. The HTTP transport speaks raw MCP JSON-RPC with no token, header, or origin check. Treat it as 127.0.0.1-only by default; if you need to expose it, put it behind an auth proxy (caddy, nginx, oauth2-proxy, Cloudflare Access, etc.).

Troubleshooting

SkillOpt loop never runs — Check that an LLM env var is set (LLM_BASE_URL, OPENAI_API_KEY, DEEPSEEK_API_KEY, or GEMINI_API_KEY). Without one, both background loops are skipped silently on startup.

Child server won't start — Run skillgraph-mcp with --transport http and check logs. The most common cause is a missing env var (remember: child processes get a blank env, not the parent shell's env).

Tool name conflicts — If two skills define a tool with the same name, skillgraph-mcp prefixes both with their skill name (e.g. github_search, docs_search). The prefixed names are what use_skill returns and what execute_code expects.

Graph not updating after config edit — The graph is loaded once at startup. Restart skillgraph-mcp to pick up manual edits to mcp.json's skillGraph section.

MCP client reports "connection timed out after 30000ms" — Cold start of the gateway scales with the slowest downstream. The gateway connects all of them in parallel at startup, but if a single server hangs on initialize it can push past your client's timeout. Run skillgraph-mcp validate --config mcp.json --timeout 25 to see exactly which one is slow; if it's a npx server doing a first-time download, run it once standalone to warm the npm cache.

One downstream fails, can I still use the rest? — Yes. As of v0.1.0+, a failed NewServer call is logged as a warning and that server is skipped; the gateway comes up with whatever connected. The MCP-level error only surfaces when every configured server fails. Use validate to see per-server status.

Hyphenated tool names not callable from execute_code — Tool names from downstream MCPs may contain hyphens (e.g. context7's resolve-library-id). Python identifiers can't contain hyphens, so the gateway sanitises them to underscores (resolve_library_id) when registering into the sandbox. use_skill shows the sanitised name; call it that way in execute_code. The wire call to the downstream server still uses the original hyphenated name.

Stability and versioning

skillgraph-mcp follows semantic versioning from v1.0 onwards. The public API surface is fixed and documented in ADR-0003:

  • The mcp.json schema (server entries, skillGraph overrides, ${VAR} interpolation).
  • The CLI flags and subcommands listed under Flags and Diagnostics & pre-flight.
  • The eight gateway tool names and their JSON input schemas.
  • The file formats inside .mcp_lattice/ (traces/*.json, skills.md, relations.md, history/*.json).

Breaking changes to any of the above require a major version bump. Adding tools, flags, or fields is a minor bump. Bug fixes and internal refactors are patches.

The following are not part of the public API and may change in any release: anything under internal/, the LLM prompt text used by SkillOpt and the bootstrap loops, log format and slog field names, the directory layout inside ~/.cache/skillgraph-mcp/ beyond the documented filenames, and the Docker image's internal filesystem layout.

Development

Makefile targets
Target Purpose
make build Compile the binary in the project root
make test Run go test ./... with a 120 s timeout
make lint Run golangci-lint
make install Build and install to ~/.local/bin/skillgraph-mcp
make inspect Launch the official MCP Inspector against the installed gateway with CONFIG=...
make inspect-downstream CMD="..." Same, but against a single downstream MCP in isolation — useful when the gateway is masking a downstream issue
make ci / make ci-release Run the GitHub Actions workflows locally via act
Local CI via act

The GitHub Actions workflows in .github/workflows/ can be run locally before pushing using nektos/act. This catches workflow regressions that would otherwise only surface after git push + waiting for CI.

Requires Docker. First run pulls a ~600 MB runner image; subsequent runs are cached. GITHUB_TOKEN must be exported (act uses it to clone actions from uses:).

go install github.com/nektos/act@latest    # one-time
make ci                                    # runs .github/workflows/test.yml
make ci-release TAG=v0.1.1                 # simulates release dispatch

Runner image and arch are pinned in .actrc. Use this before pushing changes that touch workflows or build scripts.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal
app

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL