@spekoai/mcp 1.0.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 +59 -0
- package/dist/data/benchmarks.d.ts +124 -0
- package/dist/data/benchmarks.d.ts.map +1 -0
- package/dist/data/benchmarks.js +176 -0
- package/dist/data/benchmarks.js.map +1 -0
- package/dist/data/templates.d.ts +39 -0
- package/dist/data/templates.d.ts.map +1 -0
- package/dist/data/templates.js +385 -0
- package/dist/data/templates.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api-client.d.ts +71 -0
- package/dist/lib/api-client.d.ts.map +1 -0
- package/dist/lib/api-client.js +282 -0
- package/dist/lib/api-client.js.map +1 -0
- package/dist/lib/benchmark-utils.d.ts +45 -0
- package/dist/lib/benchmark-utils.d.ts.map +1 -0
- package/dist/lib/benchmark-utils.js +244 -0
- package/dist/lib/benchmark-utils.js.map +1 -0
- package/dist/lib/project-scanner.d.ts +16 -0
- package/dist/lib/project-scanner.d.ts.map +1 -0
- package/dist/lib/project-scanner.js +154 -0
- package/dist/lib/project-scanner.js.map +1 -0
- package/dist/lib/tier-gate.d.ts +14 -0
- package/dist/lib/tier-gate.d.ts.map +1 -0
- package/dist/lib/tier-gate.js +27 -0
- package/dist/lib/tier-gate.js.map +1 -0
- package/dist/tools/agent-crud.d.ts +3 -0
- package/dist/tools/agent-crud.d.ts.map +1 -0
- package/dist/tools/agent-crud.js +114 -0
- package/dist/tools/agent-crud.js.map +1 -0
- package/dist/tools/legacy.d.ts +3 -0
- package/dist/tools/legacy.d.ts.map +1 -0
- package/dist/tools/legacy.js +354 -0
- package/dist/tools/legacy.js.map +1 -0
- package/dist/tools/migrate.d.ts +3 -0
- package/dist/tools/migrate.d.ts.map +1 -0
- package/dist/tools/migrate.js +145 -0
- package/dist/tools/migrate.js.map +1 -0
- package/dist/tools/recommend.d.ts +3 -0
- package/dist/tools/recommend.d.ts.map +1 -0
- package/dist/tools/recommend.js +163 -0
- package/dist/tools/recommend.js.map +1 -0
- package/dist/tools/setup.d.ts +3 -0
- package/dist/tools/setup.d.ts.map +1 -0
- package/dist/tools/setup.js +104 -0
- package/dist/tools/setup.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { BENCHMARK_DATA, S2S_OPTIONS } from "../data/benchmarks.js";
|
|
3
|
+
import { clampMaxResults, formatToolResponse, getLanguageSuggestions, getObjectiveWeights, rankBenchmarks, resolveLanguage, resolveUseCase, } from "../lib/benchmark-utils.js";
|
|
4
|
+
function rankS2SOptions(entries, weights) {
|
|
5
|
+
return entries
|
|
6
|
+
.map((entry) => {
|
|
7
|
+
const latencyScore = Math.max(0, 100 - (entry.latencyMs - 90) * 0.7);
|
|
8
|
+
const qualityScore = (entry.quality / 5) * 100;
|
|
9
|
+
const costScore = Math.max(0, 100 - entry.costPerHour * 35);
|
|
10
|
+
const weighted = (latencyScore * weights.latency + qualityScore * weights.quality + costScore * weights.cost) /
|
|
11
|
+
(weights.latency + weights.quality + weights.cost);
|
|
12
|
+
return {
|
|
13
|
+
...entry,
|
|
14
|
+
score: Math.round(weighted * 10) / 10,
|
|
15
|
+
};
|
|
16
|
+
})
|
|
17
|
+
.sort((a, b) => {
|
|
18
|
+
if (b.score !== a.score)
|
|
19
|
+
return b.score - a.score;
|
|
20
|
+
if (a.latencyMs !== b.latencyMs)
|
|
21
|
+
return a.latencyMs - b.latencyMs;
|
|
22
|
+
if (b.quality !== a.quality)
|
|
23
|
+
return b.quality - a.quality;
|
|
24
|
+
return a.costPerHour - b.costPerHour;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function isLanguageSupported(targetLanguage, optionLanguages) {
|
|
28
|
+
return optionLanguages.some((language) => language.toLowerCase() === targetLanguage.toLowerCase());
|
|
29
|
+
}
|
|
30
|
+
export function registerRecommendTool(server) {
|
|
31
|
+
server.registerTool("speko_recommend", {
|
|
32
|
+
description: "Recommend Speko runtime options with S2S (OpenAI Realtime, Gemini Live) and cascaded STT+LLM+TTS stacks, including trade-offs and cost breakdown.",
|
|
33
|
+
inputSchema: z.object({
|
|
34
|
+
language: z.string().describe("Target language, e.g. English, Thai"),
|
|
35
|
+
use_case: z.string().describe("Use case, e.g. customer-support, sales, scheduling"),
|
|
36
|
+
optimize_for: z
|
|
37
|
+
.enum(["balanced", "latency", "quality", "cost"])
|
|
38
|
+
.default("balanced")
|
|
39
|
+
.describe("Optimization objective"),
|
|
40
|
+
max_results: z.number().default(3).describe("Rows to return per mode"),
|
|
41
|
+
output_format: z
|
|
42
|
+
.enum(["markdown", "json"])
|
|
43
|
+
.default("markdown")
|
|
44
|
+
.describe("Set to json for machine-readable output"),
|
|
45
|
+
}),
|
|
46
|
+
}, async ({ language, use_case, optimize_for, max_results, output_format }) => {
|
|
47
|
+
const format = output_format;
|
|
48
|
+
const resolvedLanguage = resolveLanguage(language);
|
|
49
|
+
const resolvedUseCase = resolveUseCase(use_case);
|
|
50
|
+
if (!resolvedLanguage) {
|
|
51
|
+
const suggestions = getLanguageSuggestions(language);
|
|
52
|
+
return formatToolResponse(format, `Language "${language}" is not recognized. Try one of: ${suggestions.join(", ")}.`, {
|
|
53
|
+
status: "error",
|
|
54
|
+
reason: "unsupported-language",
|
|
55
|
+
requested_language: language,
|
|
56
|
+
suggestions,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const resultLimit = clampMaxResults(max_results);
|
|
60
|
+
const weights = getObjectiveWeights(optimize_for, resolvedUseCase);
|
|
61
|
+
const cascadedMatches = BENCHMARK_DATA.filter((entry) => entry.languages.some((entryLanguage) => entryLanguage.toLowerCase() === resolvedLanguage.toLowerCase()));
|
|
62
|
+
const rankedCascaded = rankBenchmarks(cascadedMatches, weights).slice(0, resultLimit);
|
|
63
|
+
const s2sMatches = S2S_OPTIONS.filter((option) => isLanguageSupported(resolvedLanguage, option.languages));
|
|
64
|
+
const rankedS2S = rankS2SOptions(s2sMatches, weights).slice(0, resultLimit);
|
|
65
|
+
if (rankedCascaded.length === 0 && rankedS2S.length === 0) {
|
|
66
|
+
return formatToolResponse(format, `No recommendation data available for ${resolvedLanguage}.`, {
|
|
67
|
+
status: "error",
|
|
68
|
+
reason: "no-matches",
|
|
69
|
+
language: resolvedLanguage,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const topS2S = rankedS2S[0];
|
|
73
|
+
const topCascaded = rankedCascaded[0];
|
|
74
|
+
const isEnglish = resolvedLanguage.toLowerCase() === "english";
|
|
75
|
+
const modeRecommendation = !topCascaded
|
|
76
|
+
? "s2s"
|
|
77
|
+
: !topS2S
|
|
78
|
+
? "cascaded"
|
|
79
|
+
: optimize_for === "cost"
|
|
80
|
+
? "cascaded"
|
|
81
|
+
: isEnglish
|
|
82
|
+
? topS2S.score >= topCascaded.score
|
|
83
|
+
? "s2s"
|
|
84
|
+
: "cascaded"
|
|
85
|
+
: "cascaded";
|
|
86
|
+
const recommendationReason = modeRecommendation === "s2s"
|
|
87
|
+
? "S2S gives lower end-to-end latency for this target profile and keeps Speko out of the audio path."
|
|
88
|
+
: optimize_for === "cost"
|
|
89
|
+
? "Cascaded pipelines are cheaper per minute and are preferred for cost optimization."
|
|
90
|
+
: !isEnglish
|
|
91
|
+
? "Cascaded pipelines are preferred for non-English coverage and routing flexibility."
|
|
92
|
+
: "Cascaded stack scores higher for your requested optimization profile.";
|
|
93
|
+
const lines = [
|
|
94
|
+
"## Speko Recommendation",
|
|
95
|
+
`**Language:** ${resolvedLanguage} | **Use case:** ${resolvedUseCase} | **Optimize:** ${optimize_for}`,
|
|
96
|
+
`**Recommended mode:** ${modeRecommendation.toUpperCase()}`,
|
|
97
|
+
`- ${recommendationReason}`,
|
|
98
|
+
"",
|
|
99
|
+
"### S2S Options",
|
|
100
|
+
"| Rank | Provider | Model | Latency | Quality | Cost/hour | Score |",
|
|
101
|
+
"|------|----------|-------|---------|---------|-----------|-------|",
|
|
102
|
+
];
|
|
103
|
+
if (rankedS2S.length === 0) {
|
|
104
|
+
lines.push("| - | - | - | - | - | - | - |");
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
rankedS2S.forEach((entry, index) => {
|
|
108
|
+
lines.push(`| ${index + 1} | ${entry.provider} | ${entry.model} | ${entry.latencyMs}ms | ${entry.quality}/5 | $${entry.costPerHour.toFixed(2)} | ${entry.score} |`);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
lines.push("", "### Cascaded Options", "| Rank | STT | LLM | TTS | Latency | Quality | Cost/min | Cost/hour | Score |", "|------|-----|-----|-----|---------|---------|----------|-----------|-------|");
|
|
112
|
+
if (rankedCascaded.length === 0) {
|
|
113
|
+
lines.push("| - | - | - | - | - | - | - | - | - |");
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
rankedCascaded.forEach((entry, index) => {
|
|
117
|
+
lines.push(`| ${index + 1} | ${entry.stt} ${entry.sttModel} | ${entry.llm} ${entry.llmModel} | ${entry.tts} ${entry.ttsModel} | ${entry.latencyMs}ms | ${entry.quality}/5 | $${entry.costPerMin.toFixed(3)} | $${(entry.costPerMin * 60).toFixed(2)} | ${entry.score} |`);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
lines.push("", "### S2S vs Cascaded Trade-offs", "- S2S: direct provider WebSocket, lowest latency, simpler path, billed as provider runtime ($/hour).", "- Cascaded: Speko edge orchestration, better multilingual control and failover, billed per-minute stack cost.");
|
|
121
|
+
if (topS2S) {
|
|
122
|
+
lines.push(`- Top S2S note: ${topS2S.notes}`);
|
|
123
|
+
}
|
|
124
|
+
if (topCascaded?.notes) {
|
|
125
|
+
lines.push(`- Top cascaded note: ${topCascaded.notes}`);
|
|
126
|
+
}
|
|
127
|
+
return formatToolResponse(format, lines.join("\n"), {
|
|
128
|
+
status: "ok",
|
|
129
|
+
language: resolvedLanguage,
|
|
130
|
+
use_case: resolvedUseCase,
|
|
131
|
+
optimize_for,
|
|
132
|
+
recommendation: {
|
|
133
|
+
mode: modeRecommendation,
|
|
134
|
+
reason: recommendationReason,
|
|
135
|
+
},
|
|
136
|
+
s2s_options: rankedS2S,
|
|
137
|
+
cascaded_options: rankedCascaded,
|
|
138
|
+
top_pick: modeRecommendation === "s2s"
|
|
139
|
+
? {
|
|
140
|
+
mode: "s2s",
|
|
141
|
+
provider: topS2S?.provider,
|
|
142
|
+
model: topS2S?.model,
|
|
143
|
+
latency_ms: topS2S?.latencyMs,
|
|
144
|
+
quality: topS2S?.quality,
|
|
145
|
+
cost_per_hour: topS2S?.costPerHour,
|
|
146
|
+
}
|
|
147
|
+
: {
|
|
148
|
+
mode: "cascaded",
|
|
149
|
+
stt: topCascaded?.stt,
|
|
150
|
+
stt_model: topCascaded?.sttModel,
|
|
151
|
+
llm: topCascaded?.llm,
|
|
152
|
+
llm_model: topCascaded?.llmModel,
|
|
153
|
+
tts: topCascaded?.tts,
|
|
154
|
+
tts_model: topCascaded?.ttsModel,
|
|
155
|
+
latency_ms: topCascaded?.latencyMs,
|
|
156
|
+
quality: topCascaded?.quality,
|
|
157
|
+
cost_per_min: topCascaded?.costPerMin,
|
|
158
|
+
cost_per_hour: topCascaded ? Number((topCascaded.costPerMin * 60).toFixed(2)) : undefined,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=recommend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recommend.js","sourceRoot":"","sources":["../../src/tools/recommend.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/tools/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAiDpE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6EzD"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { getSpekoComponentTemplate } from "../data/templates.js";
|
|
3
|
+
import { SpekoApiClient } from "../lib/api-client.js";
|
|
4
|
+
import { hasSpekoApiKey, tier2GateResponse } from "../lib/tier-gate.js";
|
|
5
|
+
const DEFAULT_SYSTEM_PROMPTS = {
|
|
6
|
+
"customer-support": "You are a customer support voice agent. Resolve issues quickly, verify user context, and keep responses concise.",
|
|
7
|
+
sales: "You are a sales voice agent. Qualify leads, ask discovery questions, and guide users to a clear next action.",
|
|
8
|
+
scheduling: "You are a scheduling voice agent. Collect availability, confirm timezone, and book meetings with minimal friction.",
|
|
9
|
+
};
|
|
10
|
+
function sanitizeTextForPrompt(value) {
|
|
11
|
+
return value.replace(/\s+/g, " ").trim();
|
|
12
|
+
}
|
|
13
|
+
function defaultSystemPrompt(type, context) {
|
|
14
|
+
const cleanContext = sanitizeTextForPrompt(context);
|
|
15
|
+
if (type === "custom") {
|
|
16
|
+
return `You are a voice agent for this context: ${cleanContext}. Be accurate, concise, and helpful.`;
|
|
17
|
+
}
|
|
18
|
+
return `${DEFAULT_SYSTEM_PROMPTS[type]} Context: ${cleanContext}`;
|
|
19
|
+
}
|
|
20
|
+
function defaultTargetFile(framework) {
|
|
21
|
+
if (framework === "nextjs")
|
|
22
|
+
return "src/app/page.tsx";
|
|
23
|
+
if (framework === "react")
|
|
24
|
+
return "src/App.tsx";
|
|
25
|
+
return "src/main.ts";
|
|
26
|
+
}
|
|
27
|
+
function buildAgentName(type, context) {
|
|
28
|
+
const shortContext = sanitizeTextForPrompt(context)
|
|
29
|
+
.toLowerCase()
|
|
30
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
31
|
+
.replace(/^-|-$/g, "")
|
|
32
|
+
.slice(0, 36);
|
|
33
|
+
const prefix = type === "custom" ? "custom" : type;
|
|
34
|
+
return `${prefix}-${shortContext || "voice-agent"}`;
|
|
35
|
+
}
|
|
36
|
+
export function registerSetupTool(server) {
|
|
37
|
+
server.registerTool("speko_setup", {
|
|
38
|
+
description: "Set up Speko in Next.js/React/vanilla projects: install SDK, create agent, and generate the voice agent component snippet.",
|
|
39
|
+
inputSchema: z.object({
|
|
40
|
+
framework: z.enum(["nextjs", "react", "vanilla"]),
|
|
41
|
+
type: z.enum(["customer-support", "sales", "scheduling", "custom"]),
|
|
42
|
+
context: z.string().min(1).describe("What the agent should know about your business/product"),
|
|
43
|
+
systemPrompt: z.string().optional().describe("Optional override for the default system prompt"),
|
|
44
|
+
targetFile: z.string().optional().describe("Target file where the VoiceAgent snippet should be added"),
|
|
45
|
+
language: z.string().default("auto").describe("Agent language hint, default auto"),
|
|
46
|
+
priority: z.enum(["speed", "quality", "cost"]).default("quality"),
|
|
47
|
+
}),
|
|
48
|
+
}, async ({ framework, type, context, systemPrompt, targetFile, language, priority }) => {
|
|
49
|
+
if (!hasSpekoApiKey())
|
|
50
|
+
return tier2GateResponse();
|
|
51
|
+
const resolvedPrompt = sanitizeTextForPrompt(systemPrompt ?? defaultSystemPrompt(type, context));
|
|
52
|
+
const resolvedTargetFile = targetFile ?? defaultTargetFile(framework);
|
|
53
|
+
const api = new SpekoApiClient();
|
|
54
|
+
const createResult = await api.createAgent({
|
|
55
|
+
name: buildAgentName(type, context),
|
|
56
|
+
context,
|
|
57
|
+
systemPrompt: resolvedPrompt,
|
|
58
|
+
language,
|
|
59
|
+
priority: priority,
|
|
60
|
+
});
|
|
61
|
+
const componentCode = getSpekoComponentTemplate({
|
|
62
|
+
framework: framework,
|
|
63
|
+
agentId: createResult.data.id,
|
|
64
|
+
language,
|
|
65
|
+
priority: priority,
|
|
66
|
+
context,
|
|
67
|
+
systemPrompt: resolvedPrompt,
|
|
68
|
+
});
|
|
69
|
+
const response = {
|
|
70
|
+
status: "ok",
|
|
71
|
+
framework,
|
|
72
|
+
type,
|
|
73
|
+
agentId: createResult.data.id,
|
|
74
|
+
dashboardUrl: api.getDashboardUrl(createResult.data.id),
|
|
75
|
+
installCommand: "npm install @speko/js",
|
|
76
|
+
env: {
|
|
77
|
+
file: framework === "nextjs" ? ".env.local" : ".env",
|
|
78
|
+
snippet: framework === "react"
|
|
79
|
+
? "VITE_SPEKO_KEY=sk_your_api_key"
|
|
80
|
+
: framework === "vanilla"
|
|
81
|
+
? "SPEKO_KEY=sk_your_api_key"
|
|
82
|
+
: "NEXT_PUBLIC_SPEKO_KEY=sk_your_api_key",
|
|
83
|
+
},
|
|
84
|
+
targetFile: resolvedTargetFile,
|
|
85
|
+
componentCode,
|
|
86
|
+
api: createResult.meta,
|
|
87
|
+
nextSteps: [
|
|
88
|
+
"Run the install command.",
|
|
89
|
+
`Add the env variable to ${framework === "nextjs" ? ".env.local" : ".env"}.`,
|
|
90
|
+
`Paste the component code into ${resolvedTargetFile}.`,
|
|
91
|
+
"Run your app and verify the widget can start a session.",
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
content: [
|
|
96
|
+
{
|
|
97
|
+
type: "text",
|
|
98
|
+
text: JSON.stringify(response, null, 2),
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/tools/setup.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@spekoai/mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for voice AI development \u2014 recommend, benchmark, and scaffold STT+LLM+TTS stacks with Speko",
|
|
5
|
+
"author": "Beknazar Abdikamalov <bek@speko.ai>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"speko-mcp": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"main": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"dev": "tsc --watch",
|
|
19
|
+
"start": "node dist/index.js",
|
|
20
|
+
"prepare": "npm run build",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"lint": "echo 'No lint configured for @speko/mcp'",
|
|
23
|
+
"test": "bun run build && bun run vitest run",
|
|
24
|
+
"test:unit": "vitest run tests/*.test.ts",
|
|
25
|
+
"test:e2e": "bun run build && bun run vitest run tests/mcp-e2e.test.ts"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
29
|
+
"zod": "^3.24.1"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^22.15.21",
|
|
33
|
+
"typescript": "^5.8.3",
|
|
34
|
+
"vitest": "^4.0.18"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"mcp",
|
|
38
|
+
"voice-ai",
|
|
39
|
+
"stt",
|
|
40
|
+
"tts",
|
|
41
|
+
"llm",
|
|
42
|
+
"elevenlabs",
|
|
43
|
+
"deepgram",
|
|
44
|
+
"cartesia",
|
|
45
|
+
"livekit",
|
|
46
|
+
"speko",
|
|
47
|
+
"claude-code",
|
|
48
|
+
"voice-agents"
|
|
49
|
+
],
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "https://github.com/spekoAI/speko-mcp"
|
|
53
|
+
}
|
|
54
|
+
}
|