lowdown-proxy 0.1.0 → 0.1.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/mcp-server.js +85 -0
- package/package.json +15 -5
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
const BASE_URL = process.env.LOWDOWN_API_URL ?? "https://lowdown-proxy.vercel.app";
|
|
5
|
+
const server = new McpServer({
|
|
6
|
+
name: "lowdown",
|
|
7
|
+
version: "0.1.0",
|
|
8
|
+
});
|
|
9
|
+
// ── 1. get_lowdown ──────────────────────────────────────────────
|
|
10
|
+
server.tool("get_lowdown", "Get the interaction history and reputation of an agent, tool, or service. " +
|
|
11
|
+
"Call this before choosing between providers to make an informed decision.", {
|
|
12
|
+
target: z.string().describe("Identifier of the agent/tool/service. e.g. 'mcp:vendor/tool-name' or 'agent:some-agent-id'"),
|
|
13
|
+
}, async ({ target }) => {
|
|
14
|
+
const res = await fetch(`${BASE_URL}/api/reputation/${encodeURIComponent(target)}`, { headers: { "x-lowdown-source": "organic" } });
|
|
15
|
+
const data = await res.json();
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
// ── 2. record_interaction ───────────────────────────────────────
|
|
21
|
+
server.tool("record_interaction", "Record an interaction with an agent, tool, or service after using it. " +
|
|
22
|
+
"Helps build the shared reputation dataset for the community.", {
|
|
23
|
+
actor: z.string().describe("Your agent/system identifier"),
|
|
24
|
+
target: z.string().describe("Identifier of what you used"),
|
|
25
|
+
target_type: z.enum(["agent", "tool", "service", "human"]),
|
|
26
|
+
task_type: z.string().describe("What kind of task was performed. e.g. 'web_search', 'code_gen'"),
|
|
27
|
+
outcome: z.enum(["success", "partial", "failure"]),
|
|
28
|
+
latency_ms: z.number().optional().describe("How long it took in milliseconds"),
|
|
29
|
+
source: z.enum(["organic", "seeded", "synthetic"]).optional(),
|
|
30
|
+
}, async ({ actor, target, target_type, task_type, outcome, latency_ms, source }) => {
|
|
31
|
+
const res = await fetch(`${BASE_URL}/api/interactions`, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: { "Content-Type": "application/json" },
|
|
34
|
+
body: JSON.stringify({ actor, target, target_type, task_type, outcome, latency_ms, source }),
|
|
35
|
+
});
|
|
36
|
+
const data = await res.json();
|
|
37
|
+
return {
|
|
38
|
+
content: [{ type: "text", text: data.ok ? "Recorded." : `Error: ${data.error}` }],
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
// ── 3. compare_tools ────────────────────────────────────────────
|
|
42
|
+
server.tool("compare_tools", "Compare multiple tools or agents by their recorded interaction data. " +
|
|
43
|
+
"Returns a ranked list with success rates and confidence levels.", {
|
|
44
|
+
candidates: z.array(z.string()).min(2).max(5).describe("List of tool/agent identifiers to compare"),
|
|
45
|
+
task_type: z.string().optional().describe("Filter by task type if you want task-specific comparison"),
|
|
46
|
+
}, async ({ candidates }) => {
|
|
47
|
+
// 후보들을 병렬로 조회
|
|
48
|
+
const results = await Promise.all(candidates.map(async (target) => {
|
|
49
|
+
try {
|
|
50
|
+
const res = await fetch(`${BASE_URL}/api/reputation/${encodeURIComponent(target)}`, { headers: { "x-lowdown-source": "organic" } });
|
|
51
|
+
return await res.json();
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return { target, interactions: 0, success_rate: null, confidence: "none" };
|
|
55
|
+
}
|
|
56
|
+
}));
|
|
57
|
+
// 규칙 기반 정렬: success_rate 내림차순, 데이터 없으면 맨 뒤
|
|
58
|
+
const ranked = [...results].sort((a, b) => {
|
|
59
|
+
if (a.success_rate === null && b.success_rate === null)
|
|
60
|
+
return 0;
|
|
61
|
+
if (a.success_rate === null)
|
|
62
|
+
return 1;
|
|
63
|
+
if (b.success_rate === null)
|
|
64
|
+
return -1;
|
|
65
|
+
return Number(b.success_rate) - Number(a.success_rate);
|
|
66
|
+
});
|
|
67
|
+
const summary = ranked
|
|
68
|
+
.map((r, i) => {
|
|
69
|
+
const rate = r.success_rate !== null ? `${(Number(r.success_rate) * 100).toFixed(0)}%` : "no data";
|
|
70
|
+
return `${i + 1}. ${r.target} — success: ${rate}, interactions: ${r.interactions}, confidence: ${r.confidence}`;
|
|
71
|
+
})
|
|
72
|
+
.join("\n");
|
|
73
|
+
const recommendation = ranked[0]?.success_rate !== null
|
|
74
|
+
? `Recommended: ${ranked[0].target}`
|
|
75
|
+
: "Not enough data to recommend.";
|
|
76
|
+
return {
|
|
77
|
+
content: [{
|
|
78
|
+
type: "text",
|
|
79
|
+
text: `${summary}\n\n${recommendation}`,
|
|
80
|
+
}],
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
// ── 서버 시작 ───────────────────────────────────────────────────
|
|
84
|
+
const transport = new StdioServerTransport();
|
|
85
|
+
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lowdown-proxy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Transparent stdio proxy that records MCP tool call interactions for the Lowdown reputation experiment.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"lowdown-proxy": "./dist/cli.js"
|
|
7
|
+
"lowdown-proxy": "./dist/cli.js",
|
|
8
|
+
"lowdown-mcp": "./dist/mcp-server.js"
|
|
8
9
|
},
|
|
9
10
|
"main": "./dist/cli.js",
|
|
10
11
|
"files": [
|
|
@@ -14,13 +15,22 @@
|
|
|
14
15
|
"build": "tsc",
|
|
15
16
|
"dev": "tsx src/cli.ts"
|
|
16
17
|
},
|
|
17
|
-
"keywords": [
|
|
18
|
+
"keywords": [
|
|
19
|
+
"mcp",
|
|
20
|
+
"agent",
|
|
21
|
+
"reputation",
|
|
22
|
+
"interaction",
|
|
23
|
+
"lowdown"
|
|
24
|
+
],
|
|
18
25
|
"license": "MIT",
|
|
19
26
|
"dependencies": {
|
|
20
|
-
"@
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
28
|
+
"@supabase/supabase-js": "^2.45.0",
|
|
29
|
+
"@vercel/node": "^3.0.0",
|
|
30
|
+
"zod": "^3.25.76"
|
|
21
31
|
},
|
|
22
32
|
"devDependencies": {
|
|
23
|
-
"@types/node": "^22.
|
|
33
|
+
"@types/node": "^22.20.4",
|
|
24
34
|
"tsx": "^4.16.0",
|
|
25
35
|
"typescript": "^5.5.0"
|
|
26
36
|
},
|