lowdown-proxy 0.1.13 → 0.1.15
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 +51 -0
- package/dist/cli.js +14 -4
- package/dist/mcp-server.js +27 -8
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -39,6 +39,27 @@ Wrap any MCP server in your client config:
|
|
|
39
39
|
}
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
## Lookup tools for agents (MCP server)
|
|
43
|
+
|
|
44
|
+
The same package ships an MCP server (stdio) that lets an agent query Lowdown directly:
|
|
45
|
+
|
|
46
|
+
```jsonc
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"lowdown": { "command": "npx", "args": ["-y", "-p", "lowdown-proxy", "lowdown-mcp"] }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
| Tool | What it returns |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `get_lowdown` | Interactions reported for a target and, for MCP Registry servers, the Scanner observations (see [Scanner observations](#scanner-observations)) |
|
|
57
|
+
| `compare_tools` | 2 to 5 targets side by side, ordered by reported success rate; Scanner data is not used for the ordering |
|
|
58
|
+
| `record_interaction` | Records the outcome of using a tool or service |
|
|
59
|
+
| `get_node_stats` | Contribution stats of your node |
|
|
60
|
+
|
|
61
|
+
The same data is available over HTTP without installing anything: see [Public API](#public-api), the [OpenAPI document](https://lowdown-proxy.vercel.app/openapi.json), and [`llms.txt`](https://lowdown-proxy.vercel.app/llms.txt). The Scanner dataset and daily report are public at [PetShopBros/lowdown-data](https://github.com/PetShopBros/lowdown-data) (CC BY 4.0).
|
|
62
|
+
|
|
42
63
|
## Options
|
|
43
64
|
|
|
44
65
|
| Flag | Description | Default |
|
|
@@ -103,6 +124,32 @@ Contributor response (with node):
|
|
|
103
124
|
|
|
104
125
|
Fuzzy matching supported — short names like `brave-search`, `fetch`, `github` resolve automatically.
|
|
105
126
|
|
|
127
|
+
### Scanner observations
|
|
128
|
+
|
|
129
|
+
For MCP servers listed in the official MCP Registry, the response also carries a separate `scanner` block: what Lowdown's Scanner observed when it connected to the server (`initialize` and `tools/list` only — it never calls a tool). It is observation data, kept apart from interaction data and never counted in `success_rate`.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
curl https://lowdown-proxy.vercel.app/api/reputation/mcp:io.github.user/server
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"target": "mcp:io.github.user/server",
|
|
138
|
+
"interactions": 0,
|
|
139
|
+
"success_rate": null,
|
|
140
|
+
"scanner": {
|
|
141
|
+
"target": "mcp:io.github.user/server",
|
|
142
|
+
"last_observed_at": "2026-10-07T19:17:00+00:00",
|
|
143
|
+
"observations_30d": 28,
|
|
144
|
+
"latest": { "reached_level": 2, "http_status": 200, "latency_ms": 312, "tool_count": 7 },
|
|
145
|
+
"schema_versions_30d": 1,
|
|
146
|
+
"status_distribution_30d": { "200": 28 }
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
HTTP statuses are reported as observed — a `401` is a `401`, not "dead".
|
|
152
|
+
|
|
106
153
|
## Why Lowdown
|
|
107
154
|
|
|
108
155
|
Agents often start with no shared history of a tool's past behavior. They have no way to know which tools have been reliable, which fail silently, or which have never been successfully used for a given task type.
|
|
@@ -175,6 +222,10 @@ MCP 서버를 감싸는 stdio 프록시가 첫 번째 진입점입니다.
|
|
|
175
222
|
|
|
176
223
|
프록시를 실행하는 것 자체가 노드 참여입니다. 기여할수록 더 상세한 데이터를 조회할 수 있습니다.
|
|
177
224
|
|
|
225
|
+
### Scanner 관찰 데이터
|
|
226
|
+
|
|
227
|
+
공식 MCP Registry에 등록된 원격 MCP 서버는 응답에 `scanner` 블록이 함께 옵니다. Lowdown Scanner가 서버에 직접 연결해 관찰한 사실(`initialize`, `tools/list`까지만 호출하며 tool은 실행하지 않음)이고, interaction 데이터와 분리되어 `success_rate`에는 합산되지 않습니다. HTTP 상태는 해석 없이 관찰된 값 그대로 보여줍니다.
|
|
228
|
+
|
|
178
229
|
### 설계 원칙 (요약)
|
|
179
230
|
|
|
180
231
|
- **설정 없음**: 프록시를 실행하면 모든 것이 자동으로 처리됩니다.
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { runProxy } from "./proxy.js";
|
|
3
|
-
// 사용법:
|
|
3
|
+
// 사용법:
|
|
4
|
+
// npx lowdown-proxy → 인자 없음: Lowdown 조회용 MCP 서버(stdio) 시작 (Registry 설치 경로)
|
|
5
|
+
// npx lowdown-proxy [--actor <id>] [--target <label>] [--source organic|seeded|synthetic] -- <command> [args...]
|
|
6
|
+
// → MCP 서버를 감싸는 기록 프록시
|
|
4
7
|
function parseArgs(argv) {
|
|
5
8
|
const dashIndex = argv.indexOf("--");
|
|
6
9
|
if (dashIndex === -1) {
|
|
7
10
|
console.error("사용법: lowdown-proxy [--actor <id>] [--target <label>] [--source organic|seeded|synthetic] -- <command> [args...]\n" +
|
|
8
|
-
"예시: lowdown-proxy -- npx some-mcp-server"
|
|
11
|
+
"예시: lowdown-proxy -- npx some-mcp-server\n" +
|
|
12
|
+
"인자 없이 실행하면 Lowdown 조회용 MCP 서버가 시작됩니다.");
|
|
9
13
|
process.exit(1);
|
|
10
14
|
}
|
|
11
15
|
const flags = argv.slice(0, dashIndex);
|
|
@@ -26,5 +30,11 @@ function parseArgs(argv) {
|
|
|
26
30
|
args: commandParts.slice(1),
|
|
27
31
|
};
|
|
28
32
|
}
|
|
29
|
-
const
|
|
30
|
-
|
|
33
|
+
const argv = process.argv.slice(2);
|
|
34
|
+
if (argv.length === 0) {
|
|
35
|
+
// MCP Registry 는 이 패키지를 인자 없이 실행한다 → 조회 도구(get_lowdown 등)를 제공하는 MCP 서버로 동작
|
|
36
|
+
await import("./mcp-server.js");
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
runProxy(parseArgs(argv));
|
|
40
|
+
}
|
package/dist/mcp-server.js
CHANGED
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { getNodeId } from "./node-identity.js";
|
|
5
6
|
const BASE_URL = process.env.LOWDOWN_API_URL ?? "https://lowdown-proxy.vercel.app";
|
|
6
7
|
const NODE_ID = getNodeId();
|
|
8
|
+
// dist/mcp-server.js 기준 ../package.json (npm 패키지에 항상 포함됨)
|
|
9
|
+
const PKG_VERSION = (() => {
|
|
10
|
+
try {
|
|
11
|
+
return createRequire(import.meta.url)("../package.json").version;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return "0.0.0";
|
|
15
|
+
}
|
|
16
|
+
})();
|
|
7
17
|
const server = new McpServer({
|
|
8
18
|
name: "lowdown",
|
|
9
|
-
version:
|
|
19
|
+
version: PKG_VERSION,
|
|
10
20
|
});
|
|
11
21
|
// ── 1. get_lowdown ──────────────────────────────────────────────
|
|
12
|
-
server.tool("get_lowdown", "
|
|
13
|
-
"
|
|
14
|
-
|
|
22
|
+
server.tool("get_lowdown", "Look up what Lowdown has recorded about an agent, tool, or service. " +
|
|
23
|
+
"Returns interactions reported by agents (count, outcomes, success rate) and, for MCP Registry servers, " +
|
|
24
|
+
"a separate `scanner` block: daily Lowdown Scanner observations of initialize and tools/list reachability, " +
|
|
25
|
+
"HTTP status, tool count, and tool-schema changes over the last 30 days. " +
|
|
26
|
+
"Scanner data is observation, not a score. A target outside the observed sample returns `observed: false`, " +
|
|
27
|
+
"which says nothing about its status.", {
|
|
28
|
+
target: z.string().describe("MCP Registry name with the mcp: prefix (e.g. 'mcp:io.github.user/server'), a short name, or an id such as 'agent:some-agent-id'"),
|
|
15
29
|
}, async ({ target }) => {
|
|
16
30
|
const res = await fetch(`${BASE_URL}/api/reputation/${encodeURIComponent(target)}`, { headers: { "x-lowdown-source": "organic", "x-lowdown-node-id": NODE_ID } });
|
|
17
31
|
const data = await res.json();
|
|
@@ -27,13 +41,16 @@ server.tool("record_interaction", "Record an interaction with an agent, tool, or
|
|
|
27
41
|
target_type: z.enum(["agent", "tool", "service", "human"]),
|
|
28
42
|
task_type: z.string().describe("What kind of task was performed. e.g. 'web_search', 'code_gen'"),
|
|
29
43
|
outcome: z.enum(["success", "partial", "failure"]),
|
|
44
|
+
failure_type: z.enum(["invalid_arguments", "timeout", "server_error", "tool_error", "unknown"])
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("Required when outcome is failure. Distinguish tool failure vs argument error."),
|
|
30
47
|
latency_ms: z.number().optional().describe("How long it took in milliseconds"),
|
|
31
48
|
source: z.enum(["organic", "seeded", "synthetic"]).optional(),
|
|
32
|
-
}, async ({ actor, target, target_type, task_type, outcome, latency_ms, source }) => {
|
|
49
|
+
}, async ({ actor, target, target_type, task_type, outcome, failure_type, latency_ms, source }) => {
|
|
33
50
|
const res = await fetch(`${BASE_URL}/api/interactions`, {
|
|
34
51
|
method: "POST",
|
|
35
52
|
headers: { "Content-Type": "application/json" },
|
|
36
|
-
body: JSON.stringify({ actor, target, target_type, task_type, outcome, latency_ms, source }),
|
|
53
|
+
body: JSON.stringify({ actor, target, target_type, task_type, outcome, failure_type, latency_ms, source }),
|
|
37
54
|
});
|
|
38
55
|
const data = await res.json();
|
|
39
56
|
return {
|
|
@@ -41,8 +58,10 @@ server.tool("record_interaction", "Record an interaction with an agent, tool, or
|
|
|
41
58
|
};
|
|
42
59
|
});
|
|
43
60
|
// ── 3. compare_tools ────────────────────────────────────────────
|
|
44
|
-
server.tool("compare_tools", "Compare
|
|
45
|
-
"
|
|
61
|
+
server.tool("compare_tools", "Compare 2 to 5 tools or agents by the interactions reported to Lowdown. " +
|
|
62
|
+
"Candidates are ordered by reported success rate (candidates without data last), with interaction count and confidence. " +
|
|
63
|
+
"Scanner observations are not used for the ordering. " +
|
|
64
|
+
"A suggestion is included only when the first candidate has at least 5 reported interactions.", {
|
|
46
65
|
candidates: z.array(z.string()).min(2).max(5).describe("List of tool/agent identifiers to compare"),
|
|
47
66
|
task_type: z.string().optional().describe("Filter by task type if you want task-specific comparison"),
|
|
48
67
|
}, async ({ candidates, task_type }) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lowdown-proxy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"mcpName": "io.github.PetShopBros/lowdown-proxy",
|
|
5
5
|
"description": "Record and query AI agent/tool/service interactions. Shared interaction history for the multi-agent ecosystem.",
|
|
6
6
|
"type": "module",
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "tsc",
|
|
17
|
-
"dev": "tsx src/cli.ts"
|
|
17
|
+
"dev": "tsx src/cli.ts",
|
|
18
|
+
"scanner:typecheck": "tsc -p scanner",
|
|
19
|
+
"scanner:test": "node --test scanner/lib.test.ts scanner/reputation.test.ts scanner/report.test.ts",
|
|
20
|
+
"scanner:dry": "node scanner/scan.ts --dry-run --limit=30"
|
|
18
21
|
},
|
|
19
22
|
"keywords": [
|
|
20
23
|
"mcp",
|