@letta-ai/letta-code 0.14.8 → 0.14.9
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/letta.js +123 -12
- package/package.json +1 -1
- package/skills/converting-mcps-to-skills/SKILL.md +171 -0
- package/skills/converting-mcps-to-skills/references/skill-templates.md +141 -0
- package/skills/converting-mcps-to-skills/scripts/mcp-http.ts +429 -0
- package/skills/converting-mcps-to-skills/scripts/mcp-stdio.ts +359 -0
- package/skills/converting-mcps-to-skills/scripts/package.json +13 -0
- package/vendor/ink/build/hooks/use-input.js +23 -11
package/letta.js
CHANGED
|
@@ -3127,7 +3127,7 @@ var package_default;
|
|
|
3127
3127
|
var init_package = __esm(() => {
|
|
3128
3128
|
package_default = {
|
|
3129
3129
|
name: "@letta-ai/letta-code",
|
|
3130
|
-
version: "0.14.
|
|
3130
|
+
version: "0.14.9",
|
|
3131
3131
|
description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
|
|
3132
3132
|
type: "module",
|
|
3133
3133
|
bin: {
|
|
@@ -3589,7 +3589,7 @@ class SettingsManager {
|
|
|
3589
3589
|
}
|
|
3590
3590
|
this.settings = updatedSettings;
|
|
3591
3591
|
await this.persistSettings();
|
|
3592
|
-
|
|
3592
|
+
debugWarn("settings", "Successfully migrated tokens to secrets");
|
|
3593
3593
|
} catch (error) {
|
|
3594
3594
|
console.warn("Failed to migrate tokens to secrets:", error);
|
|
3595
3595
|
console.warn("Tokens will remain in settings file for persistence");
|
|
@@ -30640,11 +30640,22 @@ var import_react16, useInput = (inputHandler, options = {}) => {
|
|
|
30640
30640
|
}
|
|
30641
30641
|
let keypress = parse_keypress_default(data);
|
|
30642
30642
|
if (!keypress.name && typeof data === "string") {
|
|
30643
|
+
let keycode = null;
|
|
30644
|
+
let modifier = 0;
|
|
30645
|
+
let event = 1;
|
|
30643
30646
|
const csiUMatch = data.match(/^\x1b\[(\d+)(?:;(\d+))?(?::(\d+))?u$/);
|
|
30644
30647
|
if (csiUMatch) {
|
|
30645
|
-
|
|
30646
|
-
|
|
30647
|
-
|
|
30648
|
+
keycode = parseInt(csiUMatch[1], 10);
|
|
30649
|
+
modifier = parseInt(csiUMatch[2] || "1", 10) - 1;
|
|
30650
|
+
event = csiUMatch[3] ? parseInt(csiUMatch[3], 10) : 1;
|
|
30651
|
+
} else {
|
|
30652
|
+
const modifyOtherKeysMatch = data.match(/^\x1b\[27;(\d+);(\d+)~$/);
|
|
30653
|
+
if (modifyOtherKeysMatch) {
|
|
30654
|
+
modifier = parseInt(modifyOtherKeysMatch[1], 10) - 1;
|
|
30655
|
+
keycode = parseInt(modifyOtherKeysMatch[2], 10);
|
|
30656
|
+
}
|
|
30657
|
+
}
|
|
30658
|
+
if (keycode !== null) {
|
|
30648
30659
|
if (event === 3) {
|
|
30649
30660
|
return;
|
|
30650
30661
|
}
|
|
@@ -30680,7 +30691,7 @@ var import_react16, useInput = (inputHandler, options = {}) => {
|
|
|
30680
30691
|
rightArrow: keypress.name === "right",
|
|
30681
30692
|
pageDown: keypress.name === "pagedown",
|
|
30682
30693
|
pageUp: keypress.name === "pageup",
|
|
30683
|
-
return: keypress.name === "return",
|
|
30694
|
+
return: keypress.name === "return" || keypress.name === "enter",
|
|
30684
30695
|
escape: keypress.name === "escape",
|
|
30685
30696
|
ctrl: keypress.ctrl,
|
|
30686
30697
|
shift: keypress.shift,
|
|
@@ -64856,6 +64867,8 @@ In headless mode, use:
|
|
|
64856
64867
|
settingsManager.setMemfsEnabled(agent.id, false);
|
|
64857
64868
|
} else if (isNewlyCreatedAgent && !isSubagent) {
|
|
64858
64869
|
settingsManager.setMemfsEnabled(agent.id, true);
|
|
64870
|
+
} else if (specifiedAgentId && !isSubagent) {
|
|
64871
|
+
settingsManager.setMemfsEnabled(agent.id, true);
|
|
64859
64872
|
}
|
|
64860
64873
|
if (settingsManager.isMemfsEnabled(agent.id)) {
|
|
64861
64874
|
try {
|
|
@@ -65699,6 +65712,8 @@ async function runBidirectionalMode(agent, conversationId, _client, _outputForma
|
|
|
65699
65712
|
const buffers = createBuffers(agent.id);
|
|
65700
65713
|
const startTime = performance.now();
|
|
65701
65714
|
let numTurns = 0;
|
|
65715
|
+
let lastStopReason = null;
|
|
65716
|
+
let sawStreamError = false;
|
|
65702
65717
|
let currentInput = [
|
|
65703
65718
|
{ role: "user", content: userContent }
|
|
65704
65719
|
];
|
|
@@ -65710,7 +65725,33 @@ async function runBidirectionalMode(agent, conversationId, _client, _outputForma
|
|
|
65710
65725
|
const stream2 = await sendMessageStream(conversationId, currentInput, {
|
|
65711
65726
|
agentId: agent.id
|
|
65712
65727
|
});
|
|
65713
|
-
const streamJsonHook = ({
|
|
65728
|
+
const streamJsonHook = ({
|
|
65729
|
+
chunk,
|
|
65730
|
+
shouldOutput,
|
|
65731
|
+
errorInfo
|
|
65732
|
+
}) => {
|
|
65733
|
+
if (errorInfo && shouldOutput) {
|
|
65734
|
+
sawStreamError = true;
|
|
65735
|
+
const errorEvent = {
|
|
65736
|
+
type: "error",
|
|
65737
|
+
message: errorInfo.message,
|
|
65738
|
+
stop_reason: "error",
|
|
65739
|
+
run_id: errorInfo.run_id,
|
|
65740
|
+
session_id: sessionId,
|
|
65741
|
+
uuid: crypto.randomUUID(),
|
|
65742
|
+
...errorInfo.error_type && errorInfo.run_id && {
|
|
65743
|
+
api_error: {
|
|
65744
|
+
message_type: "error_message",
|
|
65745
|
+
message: errorInfo.message,
|
|
65746
|
+
error_type: errorInfo.error_type,
|
|
65747
|
+
detail: errorInfo.detail,
|
|
65748
|
+
run_id: errorInfo.run_id
|
|
65749
|
+
}
|
|
65750
|
+
}
|
|
65751
|
+
};
|
|
65752
|
+
console.log(JSON.stringify(errorEvent));
|
|
65753
|
+
return { shouldAccumulate: true };
|
|
65754
|
+
}
|
|
65714
65755
|
if (!shouldOutput) {
|
|
65715
65756
|
return { shouldAccumulate: true };
|
|
65716
65757
|
}
|
|
@@ -65737,6 +65778,7 @@ async function runBidirectionalMode(agent, conversationId, _client, _outputForma
|
|
|
65737
65778
|
};
|
|
65738
65779
|
const result = await drainStreamWithResume(stream2, buffers, () => {}, currentAbortController?.signal, undefined, streamJsonHook);
|
|
65739
65780
|
const stopReason = result.stopReason;
|
|
65781
|
+
lastStopReason = stopReason;
|
|
65740
65782
|
const approvals = result.approvals || [];
|
|
65741
65783
|
if (stopReason === "end_turn") {
|
|
65742
65784
|
break;
|
|
@@ -65746,6 +65788,7 @@ async function runBidirectionalMode(agent, conversationId, _client, _outputForma
|
|
|
65746
65788
|
}
|
|
65747
65789
|
if (stopReason === "requires_approval") {
|
|
65748
65790
|
if (approvals.length === 0) {
|
|
65791
|
+
lastStopReason = "error";
|
|
65749
65792
|
break;
|
|
65750
65793
|
}
|
|
65751
65794
|
const { autoAllowed, autoDenied, needsUserInput } = await classifyApprovals(approvals, {
|
|
@@ -65835,9 +65878,12 @@ async function runBidirectionalMode(agent, conversationId, _client, _outputForma
|
|
|
65835
65878
|
const lastReasoning = reversed.find((line2) => line2.kind === "reasoning" && ("text" in line2) && typeof line2.text === "string" && line2.text.trim().length > 0);
|
|
65836
65879
|
const lastToolResult = reversed.find((line2) => line2.kind === "tool_call" && ("resultText" in line2) && typeof line2.resultText === "string" && (line2.resultText ?? "").trim().length > 0);
|
|
65837
65880
|
const resultText = lastAssistant?.text || lastReasoning?.text || lastToolResult?.resultText || "";
|
|
65881
|
+
const isAborted = currentAbortController?.signal.aborted;
|
|
65882
|
+
const isError = sawStreamError || lastStopReason && lastStopReason !== "end_turn" && lastStopReason !== "requires_approval";
|
|
65883
|
+
const subtype = isAborted ? "interrupted" : isError ? "error" : "success";
|
|
65838
65884
|
const resultMsg = {
|
|
65839
65885
|
type: "result",
|
|
65840
|
-
subtype
|
|
65886
|
+
subtype,
|
|
65841
65887
|
session_id: sessionId,
|
|
65842
65888
|
duration_ms: Math.round(durationMs),
|
|
65843
65889
|
duration_api_ms: 0,
|
|
@@ -65847,18 +65893,38 @@ async function runBidirectionalMode(agent, conversationId, _client, _outputForma
|
|
|
65847
65893
|
conversation_id: conversationId,
|
|
65848
65894
|
run_ids: [],
|
|
65849
65895
|
usage: null,
|
|
65850
|
-
uuid: `result-${agent.id}-${Date.now()}
|
|
65896
|
+
uuid: `result-${agent.id}-${Date.now()}`,
|
|
65897
|
+
...subtype === "error" && {
|
|
65898
|
+
stop_reason: lastStopReason && lastStopReason !== "end_turn" ? lastStopReason : "error"
|
|
65899
|
+
}
|
|
65851
65900
|
};
|
|
65852
65901
|
console.log(JSON.stringify(resultMsg));
|
|
65853
65902
|
} catch (error) {
|
|
65903
|
+
const errorDetails = formatErrorDetails(error, agent.id);
|
|
65854
65904
|
const errorMsg2 = {
|
|
65855
65905
|
type: "error",
|
|
65856
|
-
message:
|
|
65906
|
+
message: errorDetails,
|
|
65857
65907
|
stop_reason: "error",
|
|
65858
65908
|
session_id: sessionId,
|
|
65859
65909
|
uuid: crypto.randomUUID()
|
|
65860
65910
|
};
|
|
65861
65911
|
console.log(JSON.stringify(errorMsg2));
|
|
65912
|
+
const errorResultMsg = {
|
|
65913
|
+
type: "result",
|
|
65914
|
+
subtype: "error",
|
|
65915
|
+
session_id: sessionId,
|
|
65916
|
+
duration_ms: 0,
|
|
65917
|
+
duration_api_ms: 0,
|
|
65918
|
+
num_turns: 0,
|
|
65919
|
+
result: null,
|
|
65920
|
+
agent_id: agent.id,
|
|
65921
|
+
conversation_id: conversationId,
|
|
65922
|
+
run_ids: [],
|
|
65923
|
+
usage: null,
|
|
65924
|
+
uuid: `result-error-${agent.id}-${Date.now()}`,
|
|
65925
|
+
stop_reason: "error"
|
|
65926
|
+
};
|
|
65927
|
+
console.log(JSON.stringify(errorResultMsg));
|
|
65862
65928
|
} finally {
|
|
65863
65929
|
currentAbortController = null;
|
|
65864
65930
|
}
|
|
@@ -69964,6 +70030,15 @@ var init_InlineBashApproval = __esm(async () => {
|
|
|
69964
70030
|
}
|
|
69965
70031
|
if (key.escape) {
|
|
69966
70032
|
onCancel?.();
|
|
70033
|
+
return;
|
|
70034
|
+
}
|
|
70035
|
+
if (input === "1") {
|
|
70036
|
+
onApprove();
|
|
70037
|
+
return;
|
|
70038
|
+
}
|
|
70039
|
+
if (input === "2" && allowPersistence) {
|
|
70040
|
+
onApproveAlways("project");
|
|
70041
|
+
return;
|
|
69967
70042
|
}
|
|
69968
70043
|
}, { isActive: isFocused });
|
|
69969
70044
|
const solidLine = SOLID_LINE6.repeat(Math.max(columns, 10));
|
|
@@ -70415,6 +70490,15 @@ var init_InlineFileEditApproval = __esm(async () => {
|
|
|
70415
70490
|
}
|
|
70416
70491
|
if (key.escape) {
|
|
70417
70492
|
onCancel?.();
|
|
70493
|
+
return;
|
|
70494
|
+
}
|
|
70495
|
+
if (input === "1") {
|
|
70496
|
+
onApprove(diffsToPass.size > 0 ? diffsToPass : undefined);
|
|
70497
|
+
return;
|
|
70498
|
+
}
|
|
70499
|
+
if (input === "2" && allowPersistence) {
|
|
70500
|
+
onApproveAlways("project", diffsToPass.size > 0 ? diffsToPass : undefined);
|
|
70501
|
+
return;
|
|
70418
70502
|
}
|
|
70419
70503
|
}, { isActive: isFocused });
|
|
70420
70504
|
const solidLine = SOLID_LINE8.repeat(Math.max(columns, 10));
|
|
@@ -70757,6 +70841,15 @@ var init_InlineGenericApproval = __esm(async () => {
|
|
|
70757
70841
|
}
|
|
70758
70842
|
if (key.escape) {
|
|
70759
70843
|
onCancel?.();
|
|
70844
|
+
return;
|
|
70845
|
+
}
|
|
70846
|
+
if (input === "1") {
|
|
70847
|
+
onApprove();
|
|
70848
|
+
return;
|
|
70849
|
+
}
|
|
70850
|
+
if (input === "2" && allowPersistence) {
|
|
70851
|
+
onApproveAlways("project");
|
|
70852
|
+
return;
|
|
70760
70853
|
}
|
|
70761
70854
|
}, { isActive: isFocused });
|
|
70762
70855
|
const solidLine = SOLID_LINE9.repeat(Math.max(columns, 10));
|
|
@@ -71350,6 +71443,15 @@ var init_InlineTaskApproval = __esm(async () => {
|
|
|
71350
71443
|
}
|
|
71351
71444
|
if (key.escape) {
|
|
71352
71445
|
onCancel?.();
|
|
71446
|
+
return;
|
|
71447
|
+
}
|
|
71448
|
+
if (input === "1") {
|
|
71449
|
+
onApprove();
|
|
71450
|
+
return;
|
|
71451
|
+
}
|
|
71452
|
+
if (input === "2" && allowPersistence) {
|
|
71453
|
+
onApproveAlways("session");
|
|
71454
|
+
return;
|
|
71353
71455
|
}
|
|
71354
71456
|
}, { isActive: isFocused });
|
|
71355
71457
|
const solidLine = SOLID_LINE11.repeat(Math.max(columns, 10));
|
|
@@ -71654,6 +71756,15 @@ var init_StaticPlanApproval = __esm(async () => {
|
|
|
71654
71756
|
}
|
|
71655
71757
|
if (key.escape) {
|
|
71656
71758
|
onKeepPlanning("User cancelled");
|
|
71759
|
+
return;
|
|
71760
|
+
}
|
|
71761
|
+
if (input === "1") {
|
|
71762
|
+
onApproveAndAcceptEdits();
|
|
71763
|
+
return;
|
|
71764
|
+
}
|
|
71765
|
+
if (input === "2") {
|
|
71766
|
+
onApprove();
|
|
71767
|
+
return;
|
|
71657
71768
|
}
|
|
71658
71769
|
}, { isActive: isFocused });
|
|
71659
71770
|
const hintText = isOnCustomOption ? customReason ? "Enter to submit · Esc to clear" : "Type feedback · Esc to cancel" : "Enter to select · Esc to cancel";
|
|
@@ -101358,7 +101469,7 @@ class SettingsManager2 {
|
|
|
101358
101469
|
}
|
|
101359
101470
|
this.settings = updatedSettings;
|
|
101360
101471
|
await this.persistSettings();
|
|
101361
|
-
|
|
101472
|
+
debugWarn("settings", "Successfully migrated tokens to secrets");
|
|
101362
101473
|
} catch (error) {
|
|
101363
101474
|
console.warn("Failed to migrate tokens to secrets:", error);
|
|
101364
101475
|
console.warn("Tokens will remain in settings file for persistence");
|
|
@@ -103860,4 +103971,4 @@ Error during initialization: ${message}`);
|
|
|
103860
103971
|
}
|
|
103861
103972
|
main();
|
|
103862
103973
|
|
|
103863
|
-
//# debugId=
|
|
103974
|
+
//# debugId=E6B9011D5A418C2E64756E2164756E21
|
package/package.json
CHANGED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: converting-mcps-to-skills
|
|
3
|
+
description: Connect to MCP (Model Context Protocol) servers and create skills for repeated use. Load when a user wants to use an MCP server, connect to external tools via MCP, or when they mention MCP, model context protocol, or specific MCP servers.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Converting MCP Servers to Skills
|
|
7
|
+
|
|
8
|
+
Letta Code is not itself an MCP client, but as a general computer-use agent, you can easily connect to any MCP server using the scripts in this skill.
|
|
9
|
+
|
|
10
|
+
## What is MCP?
|
|
11
|
+
|
|
12
|
+
MCP (Model Context Protocol) is a standard for exposing tools to AI agents. MCP servers provide tools via JSON-RPC, either over:
|
|
13
|
+
- **HTTP** - Server running at a URL (e.g., `http://localhost:3001/mcp`)
|
|
14
|
+
- **stdio** - Server runs as a subprocess, communicating via stdin/stdout
|
|
15
|
+
|
|
16
|
+
## Quick Start: Connecting to an MCP Server
|
|
17
|
+
|
|
18
|
+
### Step 1: Determine the transport type
|
|
19
|
+
|
|
20
|
+
Ask the user:
|
|
21
|
+
- Is it an HTTP server (has a URL)?
|
|
22
|
+
- Is it a stdio server (runs via command like `npx`, `node`, `python`)?
|
|
23
|
+
|
|
24
|
+
### Step 2: Test the connection
|
|
25
|
+
|
|
26
|
+
**For HTTP servers:**
|
|
27
|
+
```bash
|
|
28
|
+
npx tsx <skill-path>/scripts/mcp-http.ts <url> list-tools
|
|
29
|
+
|
|
30
|
+
# With auth header
|
|
31
|
+
npx tsx <skill-path>/scripts/mcp-http.ts <url> --header "Authorization: Bearer KEY" list-tools
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**For stdio servers:**
|
|
35
|
+
```bash
|
|
36
|
+
# First, install dependencies (one time)
|
|
37
|
+
cd <skill-path>/scripts && npm install
|
|
38
|
+
|
|
39
|
+
# Then connect
|
|
40
|
+
npx tsx <skill-path>/scripts/mcp-stdio.ts "<command>" list-tools
|
|
41
|
+
|
|
42
|
+
# Examples
|
|
43
|
+
npx tsx <skill-path>/scripts/mcp-stdio.ts "npx -y @modelcontextprotocol/server-filesystem ." list-tools
|
|
44
|
+
npx tsx <skill-path>/scripts/mcp-stdio.ts "python server.py" list-tools
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Step 3: Explore available tools
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# List all tools
|
|
51
|
+
... list-tools
|
|
52
|
+
|
|
53
|
+
# Get schema for a specific tool
|
|
54
|
+
... info <tool-name>
|
|
55
|
+
|
|
56
|
+
# Test calling a tool
|
|
57
|
+
... call <tool-name> '{"arg": "value"}'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Creating a Dedicated Skill
|
|
61
|
+
|
|
62
|
+
When an MCP server will be used repeatedly, create a dedicated skill for it. This makes future use easier and documents the server's capabilities.
|
|
63
|
+
|
|
64
|
+
### Decision: Simple vs Rich Skill
|
|
65
|
+
|
|
66
|
+
**Simple skill** (just SKILL.md):
|
|
67
|
+
- Good for straightforward servers
|
|
68
|
+
- Documents how to use the parent skill's scripts with this specific server
|
|
69
|
+
- No additional scripts needed
|
|
70
|
+
|
|
71
|
+
**Rich skill** (SKILL.md + scripts/):
|
|
72
|
+
- Good for frequently-used servers
|
|
73
|
+
- Includes convenience wrapper scripts with defaults baked in
|
|
74
|
+
- Provides a simpler interface than the generic scripts
|
|
75
|
+
|
|
76
|
+
See `references/skill-templates.md` for templates.
|
|
77
|
+
|
|
78
|
+
## Built-in Scripts Reference
|
|
79
|
+
|
|
80
|
+
### mcp-http.ts - HTTP Transport
|
|
81
|
+
|
|
82
|
+
Connects to MCP servers over HTTP. No dependencies required.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx tsx mcp-http.ts <url> [options] <command> [args]
|
|
86
|
+
|
|
87
|
+
Commands:
|
|
88
|
+
list-tools List available tools
|
|
89
|
+
list-resources List available resources
|
|
90
|
+
info <tool> Show tool schema
|
|
91
|
+
call <tool> '<json>' Call a tool
|
|
92
|
+
|
|
93
|
+
Options:
|
|
94
|
+
--header "K: V" Add HTTP header (repeatable)
|
|
95
|
+
--timeout <ms> Request timeout (default: 30000)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Examples:**
|
|
99
|
+
```bash
|
|
100
|
+
# Basic usage
|
|
101
|
+
npx tsx mcp-http.ts http://localhost:3001/mcp list-tools
|
|
102
|
+
|
|
103
|
+
# With authentication
|
|
104
|
+
npx tsx mcp-http.ts http://localhost:3001/mcp --header "Authorization: Bearer KEY" list-tools
|
|
105
|
+
|
|
106
|
+
# Call a tool
|
|
107
|
+
npx tsx mcp-http.ts http://localhost:3001/mcp call vault '{"action":"search","query":"notes"}'
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### mcp-stdio.ts - stdio Transport
|
|
111
|
+
|
|
112
|
+
Connects to MCP servers that run as subprocesses. Requires npm install first.
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# One-time setup
|
|
116
|
+
cd <skill-path>/scripts && npm install
|
|
117
|
+
|
|
118
|
+
npx tsx mcp-stdio.ts "<command>" [options] <action> [args]
|
|
119
|
+
|
|
120
|
+
Actions:
|
|
121
|
+
list-tools List available tools
|
|
122
|
+
list-resources List available resources
|
|
123
|
+
info <tool> Show tool schema
|
|
124
|
+
call <tool> '<json>' Call a tool
|
|
125
|
+
|
|
126
|
+
Options:
|
|
127
|
+
--env "KEY=VALUE" Set environment variable (repeatable)
|
|
128
|
+
--cwd <path> Set working directory
|
|
129
|
+
--timeout <ms> Request timeout (default: 30000)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Examples:**
|
|
133
|
+
```bash
|
|
134
|
+
# Filesystem server
|
|
135
|
+
npx tsx mcp-stdio.ts "npx -y @modelcontextprotocol/server-filesystem ." list-tools
|
|
136
|
+
|
|
137
|
+
# With environment variable
|
|
138
|
+
npx tsx mcp-stdio.ts "node server.js" --env "API_KEY=xxx" list-tools
|
|
139
|
+
|
|
140
|
+
# Call a tool
|
|
141
|
+
npx tsx mcp-stdio.ts "python server.py" call read_file '{"path":"./README.md"}'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Common MCP Servers
|
|
145
|
+
|
|
146
|
+
Here are some well-known MCP servers:
|
|
147
|
+
|
|
148
|
+
| Server | Transport | Command/URL |
|
|
149
|
+
|--------|-----------|-------------|
|
|
150
|
+
| Filesystem | stdio | `npx -y @modelcontextprotocol/server-filesystem <path>` |
|
|
151
|
+
| GitHub | stdio | `npx -y @modelcontextprotocol/server-github` |
|
|
152
|
+
| Brave Search | stdio | `npx -y @modelcontextprotocol/server-brave-search` |
|
|
153
|
+
| obsidian-mcp-plugin | HTTP | `http://localhost:3001/mcp` |
|
|
154
|
+
|
|
155
|
+
## Troubleshooting
|
|
156
|
+
|
|
157
|
+
**"Cannot connect" error:**
|
|
158
|
+
- For HTTP: Check the URL is correct and server is running
|
|
159
|
+
- For stdio: Check the command works when run directly in terminal
|
|
160
|
+
|
|
161
|
+
**"Authentication required" error:**
|
|
162
|
+
- Add `--header "Authorization: Bearer YOUR_KEY"` for HTTP
|
|
163
|
+
- Or `--env "API_KEY=xxx"` for stdio servers that need env vars
|
|
164
|
+
|
|
165
|
+
**stdio "npm install" error:**
|
|
166
|
+
- Run `cd <skill-path>/scripts && npm install` first
|
|
167
|
+
- The stdio client requires the MCP SDK
|
|
168
|
+
|
|
169
|
+
**Tool call fails:**
|
|
170
|
+
- Use `info <tool>` to see the expected input schema
|
|
171
|
+
- Ensure JSON arguments match the schema
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Skill Templates for MCP Servers
|
|
2
|
+
|
|
3
|
+
When to create a dedicated skill:
|
|
4
|
+
- **One-off use**: No skill needed - just use `converting-mcps-to-skills` scripts directly
|
|
5
|
+
- **Repeated use**: Create a self-contained skill with customized scripts
|
|
6
|
+
|
|
7
|
+
Skills should be self-contained per the [Agent Skills spec](https://agentskills.io/specification).
|
|
8
|
+
|
|
9
|
+
## Naming Rules (from Agent Skills spec)
|
|
10
|
+
|
|
11
|
+
The `name` field must:
|
|
12
|
+
- Be lowercase letters, numbers, and hyphens only (`a-z`, `0-9`, `-`)
|
|
13
|
+
- Be 1-64 characters
|
|
14
|
+
- Not start or end with a hyphen
|
|
15
|
+
- Not contain consecutive hyphens (`--`)
|
|
16
|
+
- Match the parent directory name exactly
|
|
17
|
+
|
|
18
|
+
Examples: `using-github-mcp`, `mcp-filesystem`, `slack-mcp`
|
|
19
|
+
|
|
20
|
+
## Skill Template
|
|
21
|
+
|
|
22
|
+
Use this template when creating a self-contained skill for an MCP server.
|
|
23
|
+
|
|
24
|
+
### Directory Structure
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
using-<server-name>/
|
|
28
|
+
├── SKILL.md
|
|
29
|
+
└── scripts/
|
|
30
|
+
└── <server>.ts # Customized client (copied from converting-mcps-to-skills)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### SKILL.md Template
|
|
34
|
+
|
|
35
|
+
```markdown
|
|
36
|
+
---
|
|
37
|
+
name: using-<server-name>
|
|
38
|
+
description: <What the server does>. Use when <trigger conditions>.
|
|
39
|
+
# Optional fields:
|
|
40
|
+
# license: MIT
|
|
41
|
+
# compatibility: Requires network access to <service>
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# Using <Server Name>
|
|
45
|
+
|
|
46
|
+
<Brief description>
|
|
47
|
+
|
|
48
|
+
## Prerequisites
|
|
49
|
+
|
|
50
|
+
- <Requirements>
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Set API key (if needed)
|
|
56
|
+
export <SERVER>_API_KEY="your-key"
|
|
57
|
+
|
|
58
|
+
# List tools
|
|
59
|
+
npx tsx <skill-path>/scripts/<server>.ts list-tools
|
|
60
|
+
|
|
61
|
+
# Call a tool
|
|
62
|
+
npx tsx <skill-path>/scripts/<server>.ts call <tool> '{"arg":"value"}'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Available Tools
|
|
66
|
+
|
|
67
|
+
<Document tools with examples>
|
|
68
|
+
|
|
69
|
+
## Environment Variables
|
|
70
|
+
|
|
71
|
+
- `<SERVER>_API_KEY` - API key for authentication
|
|
72
|
+
- `<SERVER>_URL` - Override server URL (default: <default-url>)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Script Template (scripts/<server>.ts)
|
|
76
|
+
|
|
77
|
+
Copy the HTTP client from `converting-mcps-to-skills/scripts/mcp-http.ts` (or `mcp-stdio.ts` for stdio servers) and customize:
|
|
78
|
+
|
|
79
|
+
1. Set `DEFAULT_URL` to this server's URL
|
|
80
|
+
2. Rename the API key env var (e.g., `GITHUB_MCP_KEY` instead of generic)
|
|
81
|
+
3. Optionally simplify the CLI for common operations
|
|
82
|
+
|
|
83
|
+
The copied code is self-contained - no external dependencies for HTTP transport.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
#!/usr/bin/env npx tsx
|
|
87
|
+
/**
|
|
88
|
+
* <Server Name> CLI - Self-contained MCP client
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
// Customize these for your server
|
|
92
|
+
const DEFAULT_URL = "<server-url>";
|
|
93
|
+
const API_KEY = process.env.<SERVER>_API_KEY;
|
|
94
|
+
|
|
95
|
+
// Copy the rest of mcp-http.ts here and adjust as needed
|
|
96
|
+
// ...
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Example: Self-Contained Filesystem Skill
|
|
100
|
+
|
|
101
|
+
A complete example of a self-contained skill for the MCP filesystem server:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
using-mcp-filesystem/
|
|
105
|
+
├── SKILL.md
|
|
106
|
+
└── scripts/
|
|
107
|
+
└── filesystem.ts # Copied and customized from mcp-stdio.ts
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**SKILL.md:**
|
|
111
|
+
```markdown
|
|
112
|
+
---
|
|
113
|
+
name: using-mcp-filesystem
|
|
114
|
+
description: Access local filesystem via MCP. Use when user wants to read, write, or search files via MCP protocol.
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
# Using MCP Filesystem Server
|
|
118
|
+
|
|
119
|
+
Access local files via the official MCP filesystem server.
|
|
120
|
+
|
|
121
|
+
## Quick Start
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npx tsx <skill-path>/scripts/filesystem.ts list-tools
|
|
125
|
+
npx tsx <skill-path>/scripts/filesystem.ts call read_file '{"path":"./README.md"}'
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Available Tools
|
|
129
|
+
|
|
130
|
+
- `read_file` - Read file contents
|
|
131
|
+
- `write_file` - Write content to file
|
|
132
|
+
- `list_directory` - List directory contents
|
|
133
|
+
- `search_files` - Search for files by pattern
|
|
134
|
+
- `get_file_info` - Get file metadata
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**scripts/filesystem.ts:**
|
|
138
|
+
Copy `converting-mcps-to-skills/scripts/mcp-stdio.ts` and set the default command to:
|
|
139
|
+
```typescript
|
|
140
|
+
const DEFAULT_COMMAND = "npx -y @modelcontextprotocol/server-filesystem .";
|
|
141
|
+
```
|