@drumcode/runner 0.1.0
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/README.md +139 -0
- package/dist/chunk-3CUTJWRW.mjs +358 -0
- package/dist/chunk-4ITFFQXW.mjs +716 -0
- package/dist/chunk-5NEEOHT4.mjs +632 -0
- package/dist/chunk-6BC7SCK5.mjs +720 -0
- package/dist/chunk-E3F3IKO5.mjs +359 -0
- package/dist/chunk-JN5YMFB7.mjs +1061 -0
- package/dist/chunk-T7I5AI3A.mjs +558 -0
- package/dist/chunk-VDJVZU3Q.mjs +908 -0
- package/dist/chunk-VYYR5EI4.mjs +1061 -0
- package/dist/chunk-ZOZS5KN7.mjs +713 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1432 -0
- package/dist/cli.mjs +360 -0
- package/dist/index.d.mts +192 -0
- package/dist/index.d.ts +192 -0
- package/dist/index.js +1085 -0
- package/dist/index.mjs +8 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# @drumcode/runner
|
|
2
|
+
|
|
3
|
+
The Drumcode MCP Runner - A local execution engine for AI-Ready SaaS infrastructure.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Using npm
|
|
9
|
+
npm install -g @drumcode/runner
|
|
10
|
+
|
|
11
|
+
# Using pnpm
|
|
12
|
+
pnpm add -g @drumcode/runner
|
|
13
|
+
|
|
14
|
+
# Or run directly with npx
|
|
15
|
+
npx @drumcode/runner start
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
### 1. Configure Claude Desktop
|
|
21
|
+
|
|
22
|
+
Add Drumcode to your Claude Desktop configuration:
|
|
23
|
+
|
|
24
|
+
**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
25
|
+
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"drumcode": {
|
|
31
|
+
"command": "npx",
|
|
32
|
+
"args": ["@drumcode/runner", "start"],
|
|
33
|
+
"env": {
|
|
34
|
+
"DRUMCODE_TOKEN": "your-token-here"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 2. For Local Development
|
|
42
|
+
|
|
43
|
+
If you're running from the monorepo:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"mcpServers": {
|
|
48
|
+
"drumcode": {
|
|
49
|
+
"command": "node",
|
|
50
|
+
"args": ["/path/to/drumcode/packages/runner/dist/cli.js", "start"],
|
|
51
|
+
"env": {
|
|
52
|
+
"DRUMCODE_TOKEN": "your-token-here"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 3. Restart Claude Desktop
|
|
60
|
+
|
|
61
|
+
After updating the config, restart Claude Desktop. You should see "drumcode" in the MCP servers list.
|
|
62
|
+
|
|
63
|
+
## Available Tools
|
|
64
|
+
|
|
65
|
+
### Core Tools (Always Available)
|
|
66
|
+
|
|
67
|
+
| Tool | Description |
|
|
68
|
+
|------|-------------|
|
|
69
|
+
| `drumcode_search_tools` | Search for available tools by natural language query |
|
|
70
|
+
| `drumcode_get_tool_schema` | Get detailed schema for a specific tool |
|
|
71
|
+
|
|
72
|
+
### Full Mode Tools (Only in --mode full)
|
|
73
|
+
|
|
74
|
+
| Tool | Description |
|
|
75
|
+
|------|-------------|
|
|
76
|
+
| `drumcode_echo` | Echo back input (for testing) |
|
|
77
|
+
| `drumcode_http_get` | Make HTTP GET requests |
|
|
78
|
+
| `arxiv_*` | arXiv search tools (hardcoded) |
|
|
79
|
+
|
|
80
|
+
### Integration Tools (Via Registry)
|
|
81
|
+
|
|
82
|
+
When connected to the Drumcode Registry, you can search for and use tools from integrated SaaS platforms like Spotify, Stripe, and more.
|
|
83
|
+
|
|
84
|
+
## CLI Options
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
drumcode start [options]
|
|
88
|
+
|
|
89
|
+
Options:
|
|
90
|
+
-t, --token <token> Drumcode API token
|
|
91
|
+
-p, --project <project> Project name
|
|
92
|
+
-r, --registry <url> Registry URL (default: https://api.drumcode.ai)
|
|
93
|
+
--no-cache Disable caching
|
|
94
|
+
-l, --log-level <level> Log level: debug, info, warn, error (default: info)
|
|
95
|
+
--transport <type> Transport type: stdio, sse (default: stdio)
|
|
96
|
+
-h, --help Display help
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Environment Variables
|
|
100
|
+
|
|
101
|
+
| Variable | Description |
|
|
102
|
+
|----------|-------------|
|
|
103
|
+
| `DRUMCODE_TOKEN` | API token for Registry authentication |
|
|
104
|
+
| `DRUMCODE_PROJECT` | Project name |
|
|
105
|
+
| `DRUMCODE_REGISTRY_URL` | Custom Registry URL |
|
|
106
|
+
| `DRUMCODE_INTEGRATION_BASE_URLS` | JSON map of integration -> base URL for generic execution |
|
|
107
|
+
| `DRUMCODE_INTEGRATION_HEADERS` | JSON map of integration -> headers for generic execution |
|
|
108
|
+
|
|
109
|
+
## Security
|
|
110
|
+
|
|
111
|
+
- **API keys are never sent to the cloud.** All SaaS API keys (Spotify, Stripe, etc.) are loaded from local environment variables and execution happens locally.
|
|
112
|
+
- The Registry only stores tool definitions and metadata, not credentials.
|
|
113
|
+
- Telemetry data is sanitized before transmission (secrets are redacted).
|
|
114
|
+
|
|
115
|
+
## Development
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Build the runner
|
|
119
|
+
pnpm build
|
|
120
|
+
|
|
121
|
+
# Run in development mode
|
|
122
|
+
pnpm dev
|
|
123
|
+
|
|
124
|
+
# Test locally
|
|
125
|
+
node dist/cli.js start
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Architecture
|
|
129
|
+
|
|
130
|
+
The Runner implements the MCP (Model Context Protocol) and acts as a "smart wrapper" that:
|
|
131
|
+
|
|
132
|
+
1. **Discovers tools** via semantic search against the Drumcode Registry
|
|
133
|
+
2. **Hot-loads schemas** on demand when the LLM needs them
|
|
134
|
+
3. **Executes locally** with zero-trust (your API keys never leave your machine)
|
|
135
|
+
4. **Logs and audits** all actions for compliance
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
MIT
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
// src/runner.ts
|
|
2
|
+
import { CORE_TOOLSET, redactSecrets } from "@drumcode/core";
|
|
3
|
+
var DrumcodeRunner = class {
|
|
4
|
+
options;
|
|
5
|
+
toolCache = /* @__PURE__ */ new Map();
|
|
6
|
+
manifestCache = null;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
this.log("info", "Drumcode Runner initialized", {
|
|
10
|
+
registryUrl: options.registryUrl,
|
|
11
|
+
cacheEnabled: options.cacheEnabled
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Tool Discovery
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
/**
|
|
18
|
+
* Get the list of tools to expose to the client
|
|
19
|
+
* Uses Polyfill strategy for non-Anthropic clients
|
|
20
|
+
*/
|
|
21
|
+
async getToolList(capabilities) {
|
|
22
|
+
if (capabilities.supportsAdvancedToolUse && capabilities.supportsDeferredLoading) {
|
|
23
|
+
this.log("debug", "Using Strategy A: Full manifest with deferred loading");
|
|
24
|
+
return this.getFullManifest();
|
|
25
|
+
}
|
|
26
|
+
this.log("debug", "Using Strategy B: Polyfill mode with core toolset");
|
|
27
|
+
return CORE_TOOLSET;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Fetch the full tool manifest from the Registry
|
|
31
|
+
*/
|
|
32
|
+
async getFullManifest() {
|
|
33
|
+
if (this.manifestCache && this.options.cacheEnabled) {
|
|
34
|
+
return this.manifestCache;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const response = await fetch(`${this.options.registryUrl}/v1/manifest`, {
|
|
38
|
+
headers: this.getAuthHeaders()
|
|
39
|
+
});
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(`Registry returned ${response.status}`);
|
|
42
|
+
}
|
|
43
|
+
const data = await response.json();
|
|
44
|
+
this.manifestCache = data.tools;
|
|
45
|
+
return this.manifestCache;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
this.log("warn", "Failed to fetch manifest, using cached or fallback", { error });
|
|
48
|
+
return this.manifestCache || CORE_TOOLSET;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Tool Execution
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
/**
|
|
55
|
+
* Execute a tool call
|
|
56
|
+
*/
|
|
57
|
+
async executeTool(request) {
|
|
58
|
+
const startTime = Date.now();
|
|
59
|
+
try {
|
|
60
|
+
if (request.name === "drumcode_search_tools") {
|
|
61
|
+
return this.handleSearchTools(request.arguments);
|
|
62
|
+
}
|
|
63
|
+
if (request.name === "drumcode_get_tool_schema") {
|
|
64
|
+
return this.handleGetToolSchema(request.arguments);
|
|
65
|
+
}
|
|
66
|
+
return this.executeRegularTool(request);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
const latency = Date.now() - startTime;
|
|
69
|
+
this.log("error", "Tool execution failed", {
|
|
70
|
+
tool: request.name,
|
|
71
|
+
error,
|
|
72
|
+
latencyMs: latency
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
content: [{
|
|
76
|
+
type: "text",
|
|
77
|
+
text: `Error executing tool: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
78
|
+
}],
|
|
79
|
+
isError: true
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Handle drumcode_search_tools meta-tool
|
|
85
|
+
*/
|
|
86
|
+
async handleSearchTools(args) {
|
|
87
|
+
this.log("debug", "Searching tools", { query: args.query });
|
|
88
|
+
try {
|
|
89
|
+
const response = await fetch(`${this.options.registryUrl}/v1/tools/search`, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers: {
|
|
92
|
+
...this.getAuthHeaders(),
|
|
93
|
+
"Content-Type": "application/json"
|
|
94
|
+
},
|
|
95
|
+
body: JSON.stringify({
|
|
96
|
+
query: args.query,
|
|
97
|
+
project_token: this.options.token,
|
|
98
|
+
limit: 5
|
|
99
|
+
})
|
|
100
|
+
});
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
throw new Error(`Search failed with status ${response.status}`);
|
|
103
|
+
}
|
|
104
|
+
const data = await response.json();
|
|
105
|
+
const resultText = data.matches.length > 0 ? data.matches.map(
|
|
106
|
+
(m, i) => `${i + 1}. **${m.name}** (relevance: ${(m.relevance * 100).toFixed(0)}%)
|
|
107
|
+
${m.description}`
|
|
108
|
+
).join("\n\n") : "No matching tools found. Try a different search query.";
|
|
109
|
+
return {
|
|
110
|
+
content: [{
|
|
111
|
+
type: "text",
|
|
112
|
+
text: `Found ${data.matches.length} matching tools:
|
|
113
|
+
|
|
114
|
+
${resultText}
|
|
115
|
+
|
|
116
|
+
Use \`drumcode_get_tool_schema\` to get detailed arguments for any of these tools.`
|
|
117
|
+
}]
|
|
118
|
+
};
|
|
119
|
+
} catch (error) {
|
|
120
|
+
this.log("error", "Search failed", { error });
|
|
121
|
+
return {
|
|
122
|
+
content: [{
|
|
123
|
+
type: "text",
|
|
124
|
+
text: `Search failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
125
|
+
}],
|
|
126
|
+
isError: true
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Handle drumcode_get_tool_schema meta-tool
|
|
132
|
+
*/
|
|
133
|
+
async handleGetToolSchema(args) {
|
|
134
|
+
this.log("debug", "Fetching tool schema", { toolName: args.tool_name });
|
|
135
|
+
if (this.toolCache.has(args.tool_name)) {
|
|
136
|
+
const cached = this.toolCache.get(args.tool_name);
|
|
137
|
+
return {
|
|
138
|
+
content: [{
|
|
139
|
+
type: "text",
|
|
140
|
+
text: this.formatToolSchema(cached)
|
|
141
|
+
}]
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const response = await fetch(
|
|
146
|
+
`${this.options.registryUrl}/v1/tools/${encodeURIComponent(args.tool_name)}`,
|
|
147
|
+
{ headers: this.getAuthHeaders() }
|
|
148
|
+
);
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
if (response.status === 404) {
|
|
151
|
+
return {
|
|
152
|
+
content: [{
|
|
153
|
+
type: "text",
|
|
154
|
+
text: `Tool "${args.tool_name}" not found. Use drumcode_search_tools to find available tools.`
|
|
155
|
+
}],
|
|
156
|
+
isError: true
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
throw new Error(`Failed to fetch schema: ${response.status}`);
|
|
160
|
+
}
|
|
161
|
+
const schema = await response.json();
|
|
162
|
+
const tool = {
|
|
163
|
+
name: schema.name,
|
|
164
|
+
description: schema.description,
|
|
165
|
+
input_schema: schema.input_schema
|
|
166
|
+
};
|
|
167
|
+
this.toolCache.set(args.tool_name, tool);
|
|
168
|
+
return {
|
|
169
|
+
content: [{
|
|
170
|
+
type: "text",
|
|
171
|
+
text: this.formatToolSchema(tool)
|
|
172
|
+
}]
|
|
173
|
+
};
|
|
174
|
+
} catch (error) {
|
|
175
|
+
this.log("error", "Schema fetch failed", { error });
|
|
176
|
+
return {
|
|
177
|
+
content: [{
|
|
178
|
+
type: "text",
|
|
179
|
+
text: `Failed to fetch schema: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
180
|
+
}],
|
|
181
|
+
isError: true
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Execute a regular (non-meta) tool
|
|
187
|
+
*/
|
|
188
|
+
async executeRegularTool(request) {
|
|
189
|
+
if (request.name === "drumcode_echo") {
|
|
190
|
+
return {
|
|
191
|
+
content: [{
|
|
192
|
+
type: "text",
|
|
193
|
+
text: JSON.stringify(request.arguments, null, 2)
|
|
194
|
+
}]
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (request.name === "drumcode_http_get") {
|
|
198
|
+
const url = request.arguments.url;
|
|
199
|
+
try {
|
|
200
|
+
const response = await fetch(url);
|
|
201
|
+
const text = await response.text();
|
|
202
|
+
return {
|
|
203
|
+
content: [{
|
|
204
|
+
type: "text",
|
|
205
|
+
text: `Status: ${response.status}
|
|
206
|
+
|
|
207
|
+
${text.slice(0, 5e3)}${text.length > 5e3 ? "...(truncated)" : ""}`
|
|
208
|
+
}]
|
|
209
|
+
};
|
|
210
|
+
} catch (error) {
|
|
211
|
+
return {
|
|
212
|
+
content: [{
|
|
213
|
+
type: "text",
|
|
214
|
+
text: `HTTP request failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
215
|
+
}],
|
|
216
|
+
isError: true
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
content: [{
|
|
222
|
+
type: "text",
|
|
223
|
+
text: `Tool "${request.name}" is not yet implemented. This tool needs to be fetched and executed via the Registry.`
|
|
224
|
+
}],
|
|
225
|
+
isError: true
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
// ---------------------------------------------------------------------------
|
|
229
|
+
// Helpers
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
231
|
+
getAuthHeaders() {
|
|
232
|
+
const headers = {
|
|
233
|
+
"User-Agent": "drumcode-runner/0.1.0"
|
|
234
|
+
};
|
|
235
|
+
if (this.options.token) {
|
|
236
|
+
headers["Authorization"] = `Bearer ${this.options.token}`;
|
|
237
|
+
}
|
|
238
|
+
return headers;
|
|
239
|
+
}
|
|
240
|
+
formatToolSchema(tool) {
|
|
241
|
+
const props = tool.input_schema.properties;
|
|
242
|
+
const required = tool.input_schema.required || [];
|
|
243
|
+
const argsDescription = Object.entries(props).map(([name, prop]) => {
|
|
244
|
+
const req = required.includes(name) ? " (required)" : " (optional)";
|
|
245
|
+
return ` - ${name}${req}: ${prop.type}${prop.description ? ` - ${prop.description}` : ""}`;
|
|
246
|
+
}).join("\n");
|
|
247
|
+
return `## ${tool.name}
|
|
248
|
+
|
|
249
|
+
${tool.description}
|
|
250
|
+
|
|
251
|
+
### Arguments:
|
|
252
|
+
${argsDescription || " No arguments required."}
|
|
253
|
+
|
|
254
|
+
### Usage:
|
|
255
|
+
Call this tool with the arguments above to execute it.`;
|
|
256
|
+
}
|
|
257
|
+
log(level, message, data) {
|
|
258
|
+
const levels = ["debug", "info", "warn", "error"];
|
|
259
|
+
const currentLevel = levels.indexOf(this.options.logLevel);
|
|
260
|
+
const messageLevel = levels.indexOf(level);
|
|
261
|
+
if (messageLevel >= currentLevel) {
|
|
262
|
+
const safeData = data ? JSON.parse(redactSecrets(JSON.stringify(data))) : void 0;
|
|
263
|
+
console.error(`[drumcode] ${message}`, safeData ? safeData : "");
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// src/server.ts
|
|
269
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
270
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
271
|
+
import {
|
|
272
|
+
CallToolRequestSchema,
|
|
273
|
+
ListToolsRequestSchema
|
|
274
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
275
|
+
async function createServer(options) {
|
|
276
|
+
const runner = new DrumcodeRunner(options);
|
|
277
|
+
const server = new Server(
|
|
278
|
+
{
|
|
279
|
+
name: "drumcode",
|
|
280
|
+
version: "0.1.0"
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
capabilities: {
|
|
284
|
+
tools: {}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
server.setRequestHandler(ListToolsRequestSchema, async (request) => {
|
|
289
|
+
const capabilities = detectClientCapabilities(request);
|
|
290
|
+
const tools = await runner.getToolList(capabilities);
|
|
291
|
+
const demoTools = [
|
|
292
|
+
{
|
|
293
|
+
name: "drumcode_echo",
|
|
294
|
+
description: "Echo back the input arguments. Useful for testing.",
|
|
295
|
+
input_schema: {
|
|
296
|
+
type: "object",
|
|
297
|
+
properties: {
|
|
298
|
+
message: {
|
|
299
|
+
type: "string",
|
|
300
|
+
description: "The message to echo back"
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
required: ["message"]
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: "drumcode_http_get",
|
|
308
|
+
description: "Make an HTTP GET request to a URL and return the response.",
|
|
309
|
+
input_schema: {
|
|
310
|
+
type: "object",
|
|
311
|
+
properties: {
|
|
312
|
+
url: {
|
|
313
|
+
type: "string",
|
|
314
|
+
description: "The URL to fetch"
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
required: ["url"]
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
];
|
|
321
|
+
const allTools = [...tools, ...demoTools].map((t) => ({
|
|
322
|
+
name: t.name,
|
|
323
|
+
description: t.description,
|
|
324
|
+
inputSchema: {
|
|
325
|
+
type: "object",
|
|
326
|
+
properties: t.input_schema.properties,
|
|
327
|
+
required: t.input_schema.required
|
|
328
|
+
}
|
|
329
|
+
}));
|
|
330
|
+
return { tools: allTools };
|
|
331
|
+
});
|
|
332
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
333
|
+
const { name, arguments: args } = request.params;
|
|
334
|
+
const result = await runner.executeTool({
|
|
335
|
+
name,
|
|
336
|
+
arguments: args ?? {}
|
|
337
|
+
});
|
|
338
|
+
return {
|
|
339
|
+
content: result.content,
|
|
340
|
+
isError: result.isError
|
|
341
|
+
};
|
|
342
|
+
});
|
|
343
|
+
const transport = new StdioServerTransport();
|
|
344
|
+
await server.connect(transport);
|
|
345
|
+
console.error("[drumcode] MCP Server started");
|
|
346
|
+
}
|
|
347
|
+
function detectClientCapabilities(_request) {
|
|
348
|
+
return {
|
|
349
|
+
type: "unknown",
|
|
350
|
+
supportsAdvancedToolUse: false,
|
|
351
|
+
supportsDeferredLoading: false
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export {
|
|
356
|
+
DrumcodeRunner,
|
|
357
|
+
createServer
|
|
358
|
+
};
|