Welcome back! π
In Chapter 3: Skill Sources & Scoping, we learned how the application loads skills from files in your user or project folders.
But what if a skill is too complex to be a simple text file? What if a skill needs to query a database, execute Python code, or search the internet? These capabilities usually live on external "servers."
In this chapter, we explore MCP (Model Context Protocol) Integration.
Imagine you want your AI to have tools from many different sources:
The Problem: Writing custom code to connect to every single one of these services inside your app is messy and hard to maintain.
The Solution: The Model Context Protocol (MCP). Think of MCP as a "Universal Translator" or a USB port for AI tools. The application doesn't need to know how Google Drive works. It just connects to an "MCP Server," and that server hands over a list of tools.
Our job in the skills project is to take those external tools and make them look and feel exactly like the local skills we studied in previous chapters.
To integrate these external tools into our menu, we need to understand three things:
source: 'mcp' (instead of userSettings or projectSettings).server-name:tool-name.Here is how an external tool travels from a remote server to your terminal menu.
Let's look at how the SkillsMenu component handles these special skills.
Just like we filtered for local files in previous chapters, we explicitly look for the string 'mcp'.
// SkillsMenu.tsx
// Filter commands to find skills
const isMcpSkill = (cmd) => {
return cmd.type === 'prompt' && cmd.loadedFrom === 'mcp';
};
Explanation: This ensures that tools coming from the Model Context Protocol are recognized as valid skills, satisfying the "ID Card" contract we discussed in Chapter 2: Skill Command Structure.
When listing MCP skills, it is helpful to tell the user which servers are currently connected. We do this by looking at the skill names.
If you have skills named github:read and postgres:query, we want to show a subtitle like: (github, postgres).
// SkillsMenu.tsx
function getSourceSubtitle(source, skills) {
if (source === 'mcp') {
// 1. Extract names before the colon (e.g., "github" from "github:read")
const serverNames = skills.map(s => {
const idx = s.name.indexOf(':');
return idx > 0 ? s.name.slice(0, idx) : null;
});
// 2. Remove duplicates and join them
const uniqueServers = [...new Set(serverNames)];
return uniqueServers.join(', ');
}
}
Explanation:
:).Set to remove duplicates (so we don't list "github" 50 times).Finally, we display these skills in their own dedicated section in the menu.
// SkillsMenu.tsx
const renderMcpGroup = () => {
// We utilize the helper we wrote above
const subtitle = getSourceSubtitle('mcp', mcpSkills);
return (
<Box flexDirection="column">
<Text bold dimColor>MCP skills</Text>
{/* Show the connected servers in parentheses */}
{subtitle && <Text dimColor> ({subtitle})</Text>}
{/* List the actual skills */}
{mcpSkills.map(skill => renderSkill(skill))}
</Box>
);
}
Explanation:
This creates a distinct visual block. The user sees MCP skills in bold, followed by the list of servers like (github, filesystem, postgres), and then the individual tools below.
Without this integration:
read_file and not know if it reads from your local computer or a remote Google Drive.
By utilizing the mcp source and the server:tool naming convention, the menu provides clarity and safety.
In this chapter, we learned:
loadedFrom === 'mcp'.Now that we have all our skills loadedβfrom files, projects, and remote serversβwe need to present one last crucial piece of information to the user: Cost. How "heavy" is a skill?
Next Chapter: Token Estimation & Metadata
Generated by Code IQ