@skilljack/mcp 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/github-config.d.ts +83 -0
- package/dist/github-config.js +191 -0
- package/dist/github-polling.d.ts +35 -0
- package/dist/github-polling.js +108 -0
- package/dist/github-sync.d.ts +49 -0
- package/dist/github-sync.js +259 -0
- package/dist/index.d.ts +12 -3
- package/dist/index.js +309 -53
- package/dist/skill-config-tool.d.ts +22 -0
- package/dist/skill-config-tool.js +495 -0
- package/dist/skill-config.d.ts +163 -0
- package/dist/skill-config.js +450 -0
- package/dist/skill-discovery.d.ts +52 -1
- package/dist/skill-discovery.js +70 -1
- package/dist/skill-display-tool.d.ts +22 -0
- package/dist/skill-display-tool.js +302 -0
- package/dist/skill-prompts.d.ts +4 -0
- package/dist/skill-prompts.js +62 -10
- package/dist/skill-resources.d.ts +6 -3
- package/dist/skill-resources.js +10 -94
- package/dist/skill-tool.js +4 -3
- package/dist/subscriptions.d.ts +1 -1
- package/dist/subscriptions.js +1 -1
- package/dist/ui/mcp-app.d.ts +1 -0
- package/dist/ui/mcp-app.html +278 -0
- package/dist/ui/mcp-app.js +484 -0
- package/dist/ui/skill-display.d.ts +1 -0
- package/dist/ui/skill-display.html +188 -0
- package/dist/ui/skill-display.js +269 -0
- package/package.json +1 -1
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Display MCP App - Vanilla JS implementation
|
|
3
|
+
*/
|
|
4
|
+
import { App, applyDocumentTheme, applyHostStyleVariables, applyHostFonts, } from "@modelcontextprotocol/ext-apps";
|
|
5
|
+
// State
|
|
6
|
+
let skills = [];
|
|
7
|
+
let searchQuery = "";
|
|
8
|
+
let app = null;
|
|
9
|
+
// DOM Elements
|
|
10
|
+
const skillList = document.getElementById("skill-list");
|
|
11
|
+
const stats = document.getElementById("stats");
|
|
12
|
+
const searchInput = document.getElementById("search-input");
|
|
13
|
+
const toast = document.getElementById("toast");
|
|
14
|
+
// Handle host context changes
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
+
function handleHostContextChanged(ctx) {
|
|
17
|
+
if (ctx.theme) {
|
|
18
|
+
applyDocumentTheme(ctx.theme);
|
|
19
|
+
}
|
|
20
|
+
if (ctx.styles?.variables) {
|
|
21
|
+
applyHostStyleVariables(ctx.styles.variables);
|
|
22
|
+
}
|
|
23
|
+
if (ctx.styles?.css?.fonts) {
|
|
24
|
+
applyHostFonts(ctx.styles.css.fonts);
|
|
25
|
+
}
|
|
26
|
+
// Handle safe area insets for mobile/notched devices
|
|
27
|
+
if (ctx.safeAreaInsets) {
|
|
28
|
+
const { top, right, bottom, left } = ctx.safeAreaInsets;
|
|
29
|
+
document.body.style.paddingTop = `${top + 16}px`;
|
|
30
|
+
document.body.style.paddingRight = `${right + 16}px`;
|
|
31
|
+
document.body.style.paddingBottom = `${bottom + 16}px`;
|
|
32
|
+
document.body.style.paddingLeft = `${left + 16}px`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Update state from tool result
|
|
36
|
+
function updateState(data) {
|
|
37
|
+
if (data.skills) {
|
|
38
|
+
skills = data.skills;
|
|
39
|
+
}
|
|
40
|
+
render();
|
|
41
|
+
}
|
|
42
|
+
// Get filtered skills based on search query
|
|
43
|
+
function getFilteredSkills() {
|
|
44
|
+
if (!searchQuery) {
|
|
45
|
+
return skills;
|
|
46
|
+
}
|
|
47
|
+
const query = searchQuery.toLowerCase();
|
|
48
|
+
return skills.filter((skill) => skill.name.toLowerCase().includes(query) ||
|
|
49
|
+
skill.description.toLowerCase().includes(query));
|
|
50
|
+
}
|
|
51
|
+
// Render the UI
|
|
52
|
+
function render() {
|
|
53
|
+
renderStats();
|
|
54
|
+
renderSkills();
|
|
55
|
+
}
|
|
56
|
+
function renderStats() {
|
|
57
|
+
const filtered = getFilteredSkills();
|
|
58
|
+
if (searchQuery) {
|
|
59
|
+
stats.textContent = `${filtered.length} of ${skills.length} skills`;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
stats.textContent = `${skills.length} skill${skills.length !== 1 ? "s" : ""} available`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function renderSkills() {
|
|
66
|
+
const filtered = getFilteredSkills();
|
|
67
|
+
if (skills.length === 0) {
|
|
68
|
+
skillList.innerHTML = `
|
|
69
|
+
<div class="empty-state">
|
|
70
|
+
<p>No skills available.</p>
|
|
71
|
+
<p>Configure skill directories using the skill-config tool.</p>
|
|
72
|
+
</div>
|
|
73
|
+
`;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (filtered.length === 0) {
|
|
77
|
+
skillList.innerHTML = `
|
|
78
|
+
<div class="empty-state">
|
|
79
|
+
<p>No skills match your search.</p>
|
|
80
|
+
</div>
|
|
81
|
+
`;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
skillList.innerHTML = filtered
|
|
85
|
+
.map((skill) => {
|
|
86
|
+
const isCustomized = skill.isAssistantOverridden || skill.isUserOverridden;
|
|
87
|
+
// Build source badge based on type
|
|
88
|
+
let sourceBadge;
|
|
89
|
+
if (skill.sourceType === "github") {
|
|
90
|
+
sourceBadge = `<span class="source-badge github" title="From GitHub: ${escapeHtml(skill.sourceDisplayName)}">
|
|
91
|
+
<svg class="source-icon" viewBox="0 0 16 16" width="12" height="12" aria-hidden="true">
|
|
92
|
+
<path fill="currentColor" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
|
|
93
|
+
</svg>
|
|
94
|
+
${escapeHtml(skill.sourceDisplayName)}
|
|
95
|
+
</span>`;
|
|
96
|
+
}
|
|
97
|
+
else if (skill.sourceType === "bundled") {
|
|
98
|
+
sourceBadge = `<span class="source-badge bundled" title="Bundled with server">
|
|
99
|
+
<svg class="source-icon" viewBox="0 0 16 16" width="12" height="12" aria-hidden="true">
|
|
100
|
+
<path fill="currentColor" d="M8.878.392a1.75 1.75 0 0 0-1.756 0l-5.25 3.045A1.75 1.75 0 0 0 1 4.951v6.098c0 .624.332 1.2.872 1.514l5.25 3.045a1.75 1.75 0 0 0 1.756 0l5.25-3.045c.54-.313.872-.89.872-1.514V4.951c0-.624-.332-1.2-.872-1.514L8.878.392ZM8 3.5a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V4.25A.75.75 0 0 1 8 3.5Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"/>
|
|
101
|
+
</svg>
|
|
102
|
+
Bundled
|
|
103
|
+
</span>`;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
sourceBadge = `<span class="source-badge local" title="Local skill directory">
|
|
107
|
+
<svg class="source-icon" viewBox="0 0 16 16" width="12" height="12" aria-hidden="true">
|
|
108
|
+
<path fill="currentColor" d="M1.75 1A1.75 1.75 0 000 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0016 13.25v-8.5A1.75 1.75 0 0014.25 3H7.5a.25.25 0 01-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75z"/>
|
|
109
|
+
</svg>
|
|
110
|
+
Local
|
|
111
|
+
</span>`;
|
|
112
|
+
}
|
|
113
|
+
return `
|
|
114
|
+
<div class="skill-card" data-skill="${escapeHtml(skill.name)}">
|
|
115
|
+
<div class="skill-header">
|
|
116
|
+
<span class="skill-name">${escapeHtml(skill.name)}</span>
|
|
117
|
+
<div class="skill-badges">
|
|
118
|
+
${sourceBadge}
|
|
119
|
+
${isCustomized ? '<span class="customized-badge">Customized</span>' : ""}
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
<p class="skill-description">${escapeHtml(skill.description)}</p>
|
|
123
|
+
${skill.sourceType !== "bundled" ? `<div class="skill-path">${escapeHtml(skill.path)}</div>` : ""}
|
|
124
|
+
<div class="skill-controls">
|
|
125
|
+
<div class="toggle-group">
|
|
126
|
+
<span class="toggle-label ${skill.isAssistantOverridden ? "overridden" : ""}">Assistant</span>
|
|
127
|
+
<div
|
|
128
|
+
class="toggle-switch ${skill.assistantInvocable ? "active" : ""}"
|
|
129
|
+
data-skill="${escapeHtml(skill.name)}"
|
|
130
|
+
data-setting="assistant"
|
|
131
|
+
data-value="${skill.assistantInvocable}"
|
|
132
|
+
title="${skill.assistantInvocable ? "Model can auto-invoke this skill" : "Model cannot auto-invoke this skill"}"
|
|
133
|
+
></div>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="toggle-group">
|
|
136
|
+
<span class="toggle-label ${skill.isUserOverridden ? "overridden" : ""}">User</span>
|
|
137
|
+
<div
|
|
138
|
+
class="toggle-switch ${skill.userInvocable ? "active" : ""}"
|
|
139
|
+
data-skill="${escapeHtml(skill.name)}"
|
|
140
|
+
data-setting="user"
|
|
141
|
+
data-value="${skill.userInvocable}"
|
|
142
|
+
title="${skill.userInvocable ? "Appears in prompts menu" : "Hidden from prompts menu"}"
|
|
143
|
+
></div>
|
|
144
|
+
</div>
|
|
145
|
+
<button
|
|
146
|
+
class="reset-btn"
|
|
147
|
+
data-skill="${escapeHtml(skill.name)}"
|
|
148
|
+
${!isCustomized ? "disabled" : ""}
|
|
149
|
+
title="Reset to frontmatter defaults"
|
|
150
|
+
>Reset</button>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
`;
|
|
154
|
+
})
|
|
155
|
+
.join("");
|
|
156
|
+
// Add click handlers for toggle switches
|
|
157
|
+
skillList.querySelectorAll(".toggle-switch").forEach((toggle) => {
|
|
158
|
+
toggle.addEventListener("click", () => {
|
|
159
|
+
const skillName = toggle.dataset.skill;
|
|
160
|
+
const setting = toggle.dataset.setting;
|
|
161
|
+
const currentValue = toggle.dataset.value === "true";
|
|
162
|
+
if (skillName && setting) {
|
|
163
|
+
updateInvocation(skillName, setting, !currentValue);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
// Add click handlers for reset buttons
|
|
168
|
+
skillList.querySelectorAll(".reset-btn").forEach((btn) => {
|
|
169
|
+
btn.addEventListener("click", () => {
|
|
170
|
+
const skillName = btn.dataset.skill;
|
|
171
|
+
if (skillName) {
|
|
172
|
+
resetOverride(skillName);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
// Update invocation setting
|
|
178
|
+
async function updateInvocation(skillName, setting, value) {
|
|
179
|
+
try {
|
|
180
|
+
const result = await app.callServerTool({
|
|
181
|
+
name: "skill-display-update-invocation",
|
|
182
|
+
arguments: { skillName, setting, value },
|
|
183
|
+
});
|
|
184
|
+
console.log("Update result:", result);
|
|
185
|
+
const structured = result.structuredContent;
|
|
186
|
+
if (structured?.success) {
|
|
187
|
+
updateState(structured);
|
|
188
|
+
showToast(`${skillName}: ${setting} = ${value ? "on" : "off"}`, "success");
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
showToast(structured?.error || "Failed to update", "error");
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
console.error("Update invocation error:", error);
|
|
196
|
+
showToast(error.message || "Failed to update", "error");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Reset override
|
|
200
|
+
async function resetOverride(skillName) {
|
|
201
|
+
try {
|
|
202
|
+
const result = await app.callServerTool({
|
|
203
|
+
name: "skill-display-reset-override",
|
|
204
|
+
arguments: { skillName },
|
|
205
|
+
});
|
|
206
|
+
console.log("Reset result:", result);
|
|
207
|
+
const structured = result.structuredContent;
|
|
208
|
+
if (structured?.success) {
|
|
209
|
+
updateState(structured);
|
|
210
|
+
showToast(`${skillName} reset to defaults`, "success");
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
showToast(structured?.error || "Failed to reset", "error");
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
console.error("Reset override error:", error);
|
|
218
|
+
showToast(error.message || "Failed to reset", "error");
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Toast
|
|
222
|
+
function showToast(message, type = "success") {
|
|
223
|
+
toast.textContent = message;
|
|
224
|
+
toast.className = `toast ${type} visible`;
|
|
225
|
+
setTimeout(() => {
|
|
226
|
+
toast.classList.remove("visible");
|
|
227
|
+
}, 3000);
|
|
228
|
+
}
|
|
229
|
+
// Utilities
|
|
230
|
+
function escapeHtml(str) {
|
|
231
|
+
if (!str)
|
|
232
|
+
return "";
|
|
233
|
+
return String(str).replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c] || c);
|
|
234
|
+
}
|
|
235
|
+
// Set up event listeners
|
|
236
|
+
searchInput.addEventListener("input", () => {
|
|
237
|
+
searchQuery = searchInput.value.trim();
|
|
238
|
+
render();
|
|
239
|
+
});
|
|
240
|
+
// 1. Create app instance
|
|
241
|
+
app = new App({ name: "Skill Display", version: "1.0.0" });
|
|
242
|
+
// 2. Register handlers BEFORE connecting
|
|
243
|
+
app.onteardown = async () => {
|
|
244
|
+
console.info("App is being torn down");
|
|
245
|
+
return {};
|
|
246
|
+
};
|
|
247
|
+
app.ontoolinput = (params) => {
|
|
248
|
+
console.info("Received tool input:", params);
|
|
249
|
+
};
|
|
250
|
+
app.ontoolresult = (result) => {
|
|
251
|
+
console.info("Received tool result:", result);
|
|
252
|
+
if (result.structuredContent) {
|
|
253
|
+
updateState(result.structuredContent);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
app.ontoolcancelled = (params) => {
|
|
257
|
+
console.info("Tool call cancelled:", params.reason);
|
|
258
|
+
};
|
|
259
|
+
app.onerror = console.error;
|
|
260
|
+
app.onhostcontextchanged = handleHostContextChanged;
|
|
261
|
+
// 3. Connect to host
|
|
262
|
+
app.connect().then(() => {
|
|
263
|
+
console.info("Connected to host");
|
|
264
|
+
// Apply initial host context
|
|
265
|
+
const ctx = app.getHostContext();
|
|
266
|
+
if (ctx) {
|
|
267
|
+
handleHostContextChanged(ctx);
|
|
268
|
+
}
|
|
269
|
+
});
|