{"slug": "mcp-servers-interfaced-through-skills-thoughts", "title": "MCP servers interfaced through skills – thoughts?", "summary": "Keen now integrates Model Context Protocol (MCP) servers through its existing Skills system, generating an ordinary skill directory under `~/.keen/skills` for each connected server so the LLM discovers MCP tools before calling the shared `call_mcp_tool` tool. For a server named `github`, Keen writes `SKILL.md`, a `.keen-generated-mcp.json` metadata file, and one schema file per tool under `schemas/`, capping the generated tool table at 1000 rows and rejecting tool names that attempt path traversal; the system prompt lists only a concise skill entry, with detailed tool lists and schemas loaded on demand.", "body_md": "# MCP Servers Driven by Skills[¶](#mcp-servers-driven-by-skills)\n\nKeen integrates MCP servers through the existing Skills system. Connected MCP servers generate ordinary skill directories under `~/.keen/skills`, and the LLM uses those skill files to discover MCP tools before calling the shared `call_mcp_tool` tool.\n\n## Overview[¶](#overview)\n\nThe flow is:\n\n1. User configures MCP servers in `~/.keen/mcp/configs.json` , or a file selected with`--mcp-config <file_path>` (see[configuration location](mcp-servers.html#configuration-location) ).\n2. Keen starts the MCP manager and connects to configured servers.\n3. For each connected server, Keen lists MCP tools.\n4. Keen generates a skill named `mcp:<server>` .\n5. The generated skill appears in the skills catalog sent in the system prompt.\n6. When the LLM needs that server, it reads the generated `SKILL.md` and relevant schema file.\n7. The LLM calls the built-in `call_mcp_tool` tool with the configured server name, tool name, and schema-shaped arguments.\n\nThis makes MCP discovery prompt-efficient: the system prompt only lists a concise skill entry, while detailed tool lists and schemas are loaded on demand.\n\n## Generated skill layout[¶](#generated-skill-layout)\n\nFor a configured server named `github`, Keen generates:\n\n```\n~/.keen/skills/mcp:github/\n├── SKILL.md\n├── .keen-generated-mcp.json\n└── schemas/\n    ├── create_issue.json\n    └── list_issues.json\n```\n\nThe skill name is always:\n\n```\nmcp:<server>\n```\n\nFor example:\n\n| MCP server | Generated skill | \n|---|---|\n| `github` | `mcp:github` | \n| `context7` | `mcp:context7` | \n| `posthog` | `mcp:posthog` | \n\n## How MCP skills are generated[¶](#how-mcp-skills-are-generated)\n\nKeen generates MCP skills from connected server tool metadata:\n\n- `SKILL.md` contains YAML frontmatter and a markdown table of available tools.\n- One schema file is written for each tool's MCP input schema.\n- Tool schemas are written to `schemas/<tool-name>.json` .\n- If a tool has no input schema, Keen writes an empty JSON object.\n- Generation is atomic: Keen writes to a temporary directory, then replaces the target skill directory.\n- Tool names that attempt path traversal are rejected.\n- The generated tool table is capped at 1000 rows.\n- Long descriptions are truncated.\n\nExample generated `SKILL.md`:\n\n```\n---\nname: mcp:github\ndescription: Use this skill to interact with the `github` MCP server.\n---\n## Available tools\n| Tool | Description |\n|------|-------------|\n| create_issue | Create a GitHub issue |\n| list_issues | List issues |\n```\n\nThe generated metadata file looks like:\n\n```\n{\n  \"managed_by\": \"keen-mcp\",\n  \"server\": \"github\",\n  \"status\": \"connected\",\n  \"tool_count\": 2,\n  \"last_successful_refresh\": \"2026-05-21T12:00:00Z\",\n  \"last_error\": \"\"\n}\n```\n\n## Schema generation[¶](#schema-generation)\n\nEach MCP tool's `InputSchema` becomes a JSON file in the generated skill directory.\n\nFor a tool named `create_issue`, the schema path is:\n\n```\n~/.keen/skills/mcp:github/schemas/create_issue.json\n```\n\nFor nested tool names, directories may be created under `schemas/` as long as the name does not escape the schemas directory.\n\nThe LLM is instructed by `call_mcp_tool` to read:\n\n1. `~/.keen/skills/mcp:<server>/SKILL.md`\n2. `~/.keen/skills/mcp:<server>/schemas/<tool>.json`\n\nThen it must pass arguments matching the schema exactly.\n\n## How the LLM uses MCP skills[¶](#how-the-llm-uses-mcp-skills)\n\nKeen includes enabled skills in the system prompt as a catalog entry like:\n\n```\n- mcp:github: Use this skill to interact with the `github` MCP server. → read ~/.keen/skills/mcp:github/SKILL.md\n```\n\nWhen the user asks for something that needs the MCP server, the LLM follows the same skill activation process as regular skills:\n\n1. Read the generated `SKILL.md` to discover available tools.\n2. Pick the relevant MCP tool.\n3. Read that tool's schema file.\n4. Call `call_mcp_tool` .\n\n⚠️ There is a persistence difference between explicit slash activation and LLM-driven skill reading:\n\n**Slash activation** (`/mcp:<server> ...`) is handled before the LLM turn. Keen reads `SKILL.md`, applies slash-command argument substitution, and injects the processed skill body as an `[Activate skill: ...]` conversation message. Because it is a message, it remains in retained conversation history after the turn finishes.\n**LLM-driven activation** happens when the model reads `~/.keen/skills/mcp:<server>/SKILL.md` itself after seeing the skill catalog entry. No slash-command argument substitution is applied. The skill body arrives as a tool result for the active turn, and raw tool traffic is compacted when the turn finishes, so a later turn may need to read the skill file again.\n**For context management**, prefer letting the LLM read `SKILL.md` on demand instead of explicitly injecting MCP skills with a slash command. This keeps detailed skill instructions out of retained conversation history unless they are needed again.\n\nExample `call_mcp_tool` input:\n\n```\n{\n  \"server\": \"github\",\n  \"tool\": \"create_issue\",\n  \"arguments\": {\n    \"title\": \"Fix flaky test\",\n    \"body\": \"The race test fails intermittently.\"\n  }\n}\n```\n\nThe `call_mcp_tool` tool is generic. It does not expose one separate LLM tool per MCP server tool. The generated skill and schema files provide the discovery and argument contract.\n\n## Benefits of the skill-driven approach[¶](#benefits-of-the-skill-driven-approach)\n\n| Benefit | Why it matters | \n|---|---|\n| Smaller system prompt | The prompt includes only a skill catalog entry, not every MCP tool schema. | \n| On-demand context | The LLM reads only the MCP server and schema it needs. | \n| Works with existing skill UX | MCP servers appear in `/skills list` , can be enabled/disabled, and are activated like other skills. | \n| Stable tool surface | Keen only needs one built-in LLM tool: `call_mcp_tool` . | \n| Dynamic server discovery | Generated skill files reflect the server's live tool list after successful startup or refresh. | \n| Clear audit point | User approval happens for each `call_mcp_tool` execution. | \n| Failure isolation in prompt | Failed MCP servers are disabled as skills, keeping unavailable tools out of the skill catalog. | \n\n## Startup synchronization[¶](#startup-synchronization)\n\nAfter MCP startup completes, the REPL syncs MCP statuses with skills:\n\n| MCP state | Skill behavior | \n|---|---|\n| `connected` | Generate/refresh `mcp:<server>` skill, mark it enabled, then reload skills. | \n| `disconnected` | Mark `mcp:<server>` disabled, then reload skills. | \n| `auth_required` | Mark `mcp:<server>` disabled, show a connect hint. | \n| `auth_failed` | Mark `mcp:<server>` disabled, show a connect hint. | \n| Removed from MCP config | If a previously enabled `mcp:<server>` skill is not in the configured server set, mark it disabled. | \n\nGenerated files for failed or removed servers are not necessarily deleted. Instead, Keen disables the skill so it is hidden from the enabled skills catalog.\n\n## Manual refresh and authentication[¶](#manual-refresh-and-authentication)\n\nUse:\n\n```\n/mcp connect <server>\n```\n\nThis calls MCP `Refresh` for the server.\n\nFor OAuth servers, `/mcp connect` also:\n\n- uses the browser OAuth code fetcher,\n- uses `http://localhost:1456/auth/mcp/callback` as the redirect URL,\n- forces re-authentication by clearing the stored token first,\n- waits up to 5 minutes for connection/authentication.\n\nOn success:\n\n1. Keen refreshes the server's MCP tools.\n2. Keen regenerates the `mcp:<server>` skill.\n3. Keen enables the skill.\n4. Keen reloads skills so the LLM sees the skill catalog entry.\n\nOn failure:\n\n1. Keen reports the connection error.\n2. Keen disables `mcp:<server>` .\n3. Keen reloads skills so the unavailable server is removed from the enabled skills catalog.\n\n## Enabling and disabling MCP server skills[¶](#enabling-and-disabling-mcp-server-skills)\n\nMCP skills use the normal skills config file:\n\n```\n~/.keen/skills/config.json\n```\n\nExample:\n\n```\n{\n  \"is_enabled\": {\n    \"mcp:github\": true,\n    \"mcp:posthog\": false\n  }\n}\n```\n\nYou can manage them with normal skill commands:\n\n```\n/skills list\n/skills disable mcp:github\n/skills enable mcp:github\n/skills reload\n```\n\nImportant behavior:\n\n- Skills default to enabled if they are not present in `config.json` .\n- A connected MCP server refreshes its generated skill files during startup MCP sync, but it does not override an explicitly disabled skill.\n- Running `/mcp connect <server>` successfully refreshes the generated skill files and enables the skill.\n- A failed MCP server is automatically disabled during MCP sync.\n- A stale MCP skill whose server was removed from MCP config is removed from skills config and its generated skill directory is deleted during MCP sync.\n- Disabling a generated MCP skill hides it from the skills catalog, but it does not remove the MCP server from the selected MCP configuration file and does not stop an already-running MCP session.\n\nTo fully remove a server from Keen's MCP runtime, remove it from the selected MCP configuration file (`~/.keen/mcp/configs.json` by default, or the file passed to `--mcp-config`) and restart Keen.\n\n## What happens when MCP fails[¶](#what-happens-when-mcp-fails)\n\n| Scenario | Skill outcome | LLM outcome | \n|---|---|---|\n| Server connects and tools list succeeds on startup | Skill files are generated or refreshed; existing enabled/disabled preference is preserved. | LLM can discover the skill only when it is enabled, but connected MCP tools remain callable through `call_mcp_tool` . | \n| Server fails to connect | Skill is disabled. | LLM does not see the skill in the enabled catalog. | \n| OAuth is required | Skill is disabled and Keen suggests `/mcp connect <server>` . | LLM does not see the skill until auth succeeds. | \n| OAuth/API key auth fails | Skill is disabled. | LLM does not see the skill until the server reconnects. | \n| Tool listing fails | Skill generation is skipped; server state becomes failed. | Existing generated files may remain, but sync disables the skill for failure states. | \n| `/mcp connect <server>` succeeds | Skill files are generated or refreshed, and the skill is enabled. | LLM can discover the skill in future turns. | \n| Server is removed from config | Any explicit skill status is removed from skills config and generated skill files are deleted on next startup sync. | LLM no longer sees the generated skill. | \n| MCP manager cannot start due invalid config | No MCP runtime is registered and `call_mcp_tool` is unavailable. | LLM cannot call MCP tools. Previously generated skills may still exist on disk, but they are only useful if enabled and the tool exists; fix config and restart. | \n\n## Security and permissions[¶](#security-and-permissions)\n\nMCP tools can perform arbitrary actions depending on the remote server. Keen adds these guardrails:\n\n- Every `call_mcp_tool` request asks the user for approval.\n- The approval prompt includes the server, tool, and JSON arguments.\n- API keys are redacted from MCP errors/logs when possible.\n- Stdio credentials should be provided via environment variables in MCP config or the parent environment.\n- Generated schemas are local files under `~/.keen/skills/mcp:<server>/schemas` .\n\n## Implementation references[¶](#implementation-references)\n\n| Concern | Code | \n|---|---|\n| MCP config format and validation | `internal/mcp/config.go` | \n| MCP runtime, connection, tool listing, tool calls | `internal/mcp/manager.go` | \n| OAuth handling and token persistence | `internal/mcp/oauth.go` | \n| Generated MCP skills and schemas | `internal/mcpskills/mcpskills.go` | \n| Startup MCP/skill synchronization | `internal/cli/repl/repl_helpers.go` | \n| `/mcp` commands | `internal/cli/repl/command_handlers.go` | \n| Generic LLM MCP tool | `internal/tools/call_mcp_tool.go` | \n| Skills config and catalog | `internal/skills/config.go` ,`internal/skills/discover.go` |", "url": "https://wpnews.pro/news/mcp-servers-interfaced-through-skills-thoughts", "canonical_source": "https://mochow13.github.io/keen-code/docs/mcp-skills.html", "published_at": "2026-10-17 22:57:49+00:00", "updated_at": "2026-10-17 23:07:00.469282+00:00", "lang": "en", "topics": ["ai-agents", "agent-protocols", "ai-tools", "developer-tools"], "entities": ["Keen", "Model Context Protocol", "call_mcp_tool", "GitHub"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/mcp-servers-interfaced-through-skills-thoughts", "markdown": "https://wpnews.pro/news/mcp-servers-interfaced-through-skills-thoughts.md", "text": "https://wpnews.pro/news/mcp-servers-interfaced-through-skills-thoughts.txt", "jsonld": "https://wpnews.pro/news/mcp-servers-interfaced-through-skills-thoughts.jsonld"}}