@heretek-ai/epistemic-swarm 0.2.2 → 0.3.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/.agents/skills/brainstorming/SKILL.md +13 -0
- package/.agents/skills/code_audit/SKILL.md +13 -0
- package/.agents/skills/epistemic_search/SKILL.md +13 -0
- package/.agents/skills/grilling/SKILL.md +13 -0
- package/.agents/skills/oss_scout/SKILL.md +13 -0
- package/.agents/skills/research_cache/SKILL.md +13 -0
- package/.agents/skills/swarm_config/SKILL.md +13 -0
- package/.claude-plugin/plugin.json +15 -5
- package/.omp/README.md +39 -0
- package/.omp/SYSTEM.md +12 -0
- package/.omp/commands/audit.md +10 -0
- package/.omp/commands/brainstorming.md +12 -0
- package/.omp/commands/grill.md +9 -0
- package/.omp/commands/scout.md +10 -0
- package/.omp/commands/swarm-config.md +10 -0
- package/.omp/commands/swarm.md +9 -0
- package/.omp/hooks/post/epistemic-audit.ts +16 -0
- package/.omp/hooks/pre/epistemic-redirect.ts +21 -0
- package/.omp/prompts/brainstorming.md +8 -0
- package/.omp/prompts/swarm.md +8 -0
- package/MARKETPLACE.md +8 -0
- package/README.md +53 -8
- package/bin/cli.js +27 -3
- package/config/domain_packs/biopharma.json +23 -0
- package/config/domain_packs/legal.json +19 -0
- package/config/domain_packs/quant.json +19 -0
- package/config/mcp-research-servers.json +7 -0
- package/config/mcp_launcher.py +48 -137
- package/config/opencode-snippet.json +58 -3
- package/config/searxng_mcp.py +42 -83
- package/extensions/pi/index.js +196 -28
- package/install.sh +20 -4
- package/package.json +40 -5
- package/plugins/antigravity/README.md +28 -0
- package/plugins/antigravity/agents/alpha-thesis.md +6 -0
- package/plugins/antigravity/agents/beta-antithesis.md +7 -0
- package/plugins/antigravity/agents/brainstormer.md +7 -0
- package/plugins/antigravity/agents/epistemic-auditor.md +5 -0
- package/plugins/antigravity/hooks.json +23 -0
- package/plugins/antigravity/mcp_config.json +33 -0
- package/plugins/antigravity/plugin.json +21 -0
- package/plugins/antigravity/rules/epistemic-integrity.md +6 -0
- package/plugins/antigravity/skills/brainstorming/SKILL.md +13 -0
- package/plugins/antigravity/skills/code_audit/SKILL.md +13 -0
- package/plugins/antigravity/skills/epistemic_search/SKILL.md +13 -0
- package/plugins/antigravity/skills/grilling/SKILL.md +13 -0
- package/plugins/antigravity/skills/oss_scout/SKILL.md +13 -0
- package/plugins/antigravity/skills/research_cache/SKILL.md +13 -0
- package/plugins/antigravity/skills/swarm_config/SKILL.md +13 -0
- package/plugins/codex/AGENTS.md.snippet +10 -0
- package/plugins/codex/README.md +37 -0
- package/plugins/codex/config.toml.snippet +28 -0
- package/plugins/codex/openai.yaml +24 -0
- package/plugins/codex/skills/brainstorming/SKILL.md +13 -0
- package/plugins/codex/skills/code_audit/SKILL.md +13 -0
- package/plugins/codex/skills/epistemic_search/SKILL.md +13 -0
- package/plugins/codex/skills/grilling/SKILL.md +13 -0
- package/plugins/codex/skills/oss_scout/SKILL.md +13 -0
- package/plugins/codex/skills/research_cache/SKILL.md +13 -0
- package/plugins/codex/skills/swarm_config/SKILL.md +13 -0
- package/plugins/gemini/GEMINI.md +15 -0
- package/plugins/gemini/README.md +19 -0
- package/plugins/gemini/commands/audit.toml +6 -0
- package/plugins/gemini/commands/brainstorming.toml +10 -0
- package/plugins/gemini/commands/grill.toml +6 -0
- package/plugins/gemini/commands/scout.toml +7 -0
- package/plugins/gemini/commands/swarm-config.toml +7 -0
- package/plugins/gemini/commands/swarm.toml +8 -0
- package/plugins/gemini/gemini-extension.json +38 -0
- package/plugins/gemini/hooks/hooks.json +11 -0
- package/plugins/gemini/skills/brainstorming/SKILL.md +13 -0
- package/plugins/gemini/skills/code_audit/SKILL.md +13 -0
- package/plugins/gemini/skills/epistemic_search/SKILL.md +13 -0
- package/plugins/gemini/skills/grilling/SKILL.md +13 -0
- package/plugins/gemini/skills/oss_scout/SKILL.md +13 -0
- package/plugins/gemini/skills/research_cache/SKILL.md +13 -0
- package/plugins/gemini/skills/swarm_config/SKILL.md +13 -0
- package/plugins/opencode/index.js +335 -118
- package/prompts/agent_brainstormer.md +97 -0
- package/runner/__pycache__/__init__.cpython-311.pyc +0 -0
- package/runner/__pycache__/auctioneer.cpython-311.pyc +0 -0
- package/runner/__pycache__/auditor_engine.cpython-311.pyc +0 -0
- package/runner/__pycache__/claim_store.cpython-311.pyc +0 -0
- package/runner/__pycache__/claim_witness.cpython-311.pyc +0 -0
- package/runner/__pycache__/living_dossiers.cpython-311.pyc +0 -0
- package/runner/__pycache__/mcp_protocol.cpython-311.pyc +0 -0
- package/runner/__pycache__/mcp_server.cpython-311.pyc +0 -0
- package/runner/__pycache__/pcrb.cpython-311.pyc +0 -0
- package/runner/__pycache__/pcrb_verify.cpython-311.pyc +0 -0
- package/runner/__pycache__/refinement.cpython-311.pyc +0 -0
- package/runner/__pycache__/research_swarm.cpython-311.pyc +0 -0
- package/runner/__pycache__/state_machine.cpython-311.pyc +0 -0
- package/runner/auctioneer.py +169 -0
- package/runner/auditor_engine.py +127 -12
- package/runner/claim_store.py +361 -0
- package/runner/claim_witness.py +183 -0
- package/runner/living_dossiers.py +355 -0
- package/runner/mcp_protocol.py +188 -0
- package/runner/mcp_server.py +567 -0
- package/runner/pcrb.py +212 -0
- package/runner/pcrb_verify.py +237 -0
- package/runner/refinement.py +335 -0
- package/runner/research_swarm.py +378 -94
- package/runner/tests/__pycache__/test_auction_order.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_claim_store.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_claim_witness.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_domain_packs.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_fleet_seam.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_living_dossiers.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_mcp_server.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_pcrb.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_refinement.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_swarm.cpython-311.pyc +0 -0
- package/runner/tests/fixtures/auction_objective.json +35 -0
- package/runner/tests/fixtures/borderline_claims.json +27 -0
- package/runner/tests/fixtures/divergence_objectives.json +31 -0
- package/runner/tests/test_auction_order.py +173 -0
- package/runner/tests/test_claim_store.py +162 -0
- package/runner/tests/test_claim_witness.py +186 -0
- package/runner/tests/test_domain_packs.py +217 -0
- package/runner/tests/test_fleet_seam.py +113 -0
- package/runner/tests/test_living_dossiers.py +204 -0
- package/runner/tests/test_mcp_server.py +212 -0
- package/runner/tests/test_pcrb.py +240 -0
- package/runner/tests/test_refinement.py +255 -0
- package/runner/tests/test_swarm.py +173 -15
- package/scripts/auction_experiment.py +180 -0
- package/scripts/build_adapters.py +183 -0
- package/scripts/divergence_experiment.py +184 -0
- package/skills/brainstorming/SKILL.md +106 -0
- package/skills/brainstorming/__init__.py +1 -0
- package/skills/brainstorming/scripts/brainstorm.py +200 -0
- package/skills/research_cache/__pycache__/__init__.cpython-311.pyc +0 -0
- package/skills/research_cache/__pycache__/hasher.cpython-311.pyc +0 -0
- package/skills/swarm_config/SKILL.md +1 -1
- package/skills/swarm_config/__pycache__/__init__.cpython-311.pyc +0 -0
- package/skills/swarm_config/__pycache__/configure.cpython-311.pyc +0 -0
- package/skills/swarm_config/configure.py +45 -11
package/config/mcp_launcher.py
CHANGED
|
@@ -36,144 +36,55 @@ def run_zero_key_mcp_server(service: str):
|
|
|
36
36
|
"""Run lightweight JSON-RPC MCP server handling search and fetch without API keys."""
|
|
37
37
|
from skills.epistemic_search.scripts.search import search_duckduckgo
|
|
38
38
|
from skills.epistemic_search.scripts.fetch import fetch_and_cache
|
|
39
|
+
from runner.mcp_protocol import StdioJsonRpcServer, ToolSpec
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
elif method == "tools/list":
|
|
91
|
-
if service == "brave-search":
|
|
92
|
-
tools = [
|
|
93
|
-
{
|
|
94
|
-
"name": "brave_web_search",
|
|
95
|
-
"description": "Web search with DuckDuckGo fallback (zero API key required). Returns verifiable search results with titles, URLs, and snippets.",
|
|
96
|
-
"inputSchema": {
|
|
97
|
-
"type": "object",
|
|
98
|
-
"properties": {
|
|
99
|
-
"query": {"type": "string", "description": "The search query terms"},
|
|
100
|
-
"count": {"type": "number", "description": "Number of results (1-20)", "default": 10}
|
|
101
|
-
},
|
|
102
|
-
"required": ["query"]
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
]
|
|
106
|
-
elif service == "firecrawl":
|
|
107
|
-
tools = [
|
|
108
|
-
{
|
|
109
|
-
"name": "firecrawl_scrape",
|
|
110
|
-
"description": "Scrape and extract clean markdown with automatic content-addressed SHA-256 caching into .research/sources/.",
|
|
111
|
-
"inputSchema": {
|
|
112
|
-
"type": "object",
|
|
113
|
-
"properties": {
|
|
114
|
-
"url": {"type": "string", "description": "URL to scrape and cache"}
|
|
115
|
-
},
|
|
116
|
-
"required": ["url"]
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
]
|
|
120
|
-
else:
|
|
121
|
-
tools = []
|
|
122
|
-
|
|
123
|
-
res = {
|
|
124
|
-
"jsonrpc": "2.0",
|
|
125
|
-
"id": req_id,
|
|
126
|
-
"result": {"tools": tools}
|
|
127
|
-
}
|
|
128
|
-
elif method == "tools/call":
|
|
129
|
-
tool_name = params.get("name")
|
|
130
|
-
args = params.get("arguments", {})
|
|
131
|
-
|
|
132
|
-
if tool_name in ("brave_web_search", "search"):
|
|
133
|
-
q = args.get("query", "")
|
|
134
|
-
count = args.get("count", 10)
|
|
135
|
-
results = search_duckduckgo(query=q, max_results=count)
|
|
136
|
-
text_out = json.dumps(results, indent=2)
|
|
137
|
-
res = {
|
|
138
|
-
"jsonrpc": "2.0",
|
|
139
|
-
"id": req_id,
|
|
140
|
-
"result": {
|
|
141
|
-
"content": [{"type": "text", "text": text_out}]
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
elif tool_name in ("firecrawl_scrape", "scrape", "fetch"):
|
|
145
|
-
target_url = args.get("url", "")
|
|
146
|
-
content = fetch_and_cache(url=target_url)
|
|
147
|
-
res = {
|
|
148
|
-
"jsonrpc": "2.0",
|
|
149
|
-
"id": req_id,
|
|
150
|
-
"result": {
|
|
151
|
-
"content": [{"type": "text", "text": content}]
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
else:
|
|
155
|
-
res = {
|
|
156
|
-
"jsonrpc": "2.0",
|
|
157
|
-
"id": req_id,
|
|
158
|
-
"error": {"code": -32601, "message": f"Tool {tool_name} not found"}
|
|
159
|
-
}
|
|
160
|
-
elif method == "notifications/initialized":
|
|
161
|
-
continue
|
|
162
|
-
else:
|
|
163
|
-
res = {
|
|
164
|
-
"jsonrpc": "2.0",
|
|
165
|
-
"id": req_id,
|
|
166
|
-
"error": {"code": -32601, "message": f"Method {method} not handled"}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if use_headers:
|
|
170
|
-
send_response(res)
|
|
171
|
-
else:
|
|
172
|
-
send_json(res)
|
|
173
|
-
|
|
174
|
-
except Exception as e:
|
|
175
|
-
sys.stderr.write(f"[mcp-launcher] Error handling request: {e}\n")
|
|
176
|
-
break
|
|
41
|
+
tools = []
|
|
42
|
+
if service == "brave-search":
|
|
43
|
+
def handle_brave_search(args: Dict[str, Any]) -> str:
|
|
44
|
+
results = search_duckduckgo(
|
|
45
|
+
query=args.get("query", ""), max_results=args.get("count", 10)
|
|
46
|
+
)
|
|
47
|
+
return json.dumps(results, indent=2)
|
|
48
|
+
|
|
49
|
+
tools.append(ToolSpec(
|
|
50
|
+
name="brave_web_search",
|
|
51
|
+
description="Web search with DuckDuckGo fallback (zero API key required). Returns verifiable search results with titles, URLs, and snippets.",
|
|
52
|
+
input_schema={
|
|
53
|
+
"type": "object",
|
|
54
|
+
"properties": {
|
|
55
|
+
"query": {"type": "string", "description": "The search query terms"},
|
|
56
|
+
"count": {"type": "number", "description": "Number of results (1-20)", "default": 10},
|
|
57
|
+
},
|
|
58
|
+
"required": ["query"],
|
|
59
|
+
},
|
|
60
|
+
handler=handle_brave_search,
|
|
61
|
+
aliases=("search",),
|
|
62
|
+
))
|
|
63
|
+
elif service == "firecrawl":
|
|
64
|
+
def handle_firecrawl_scrape(args: Dict[str, Any]) -> str:
|
|
65
|
+
return fetch_and_cache(url=args.get("url", ""))
|
|
66
|
+
|
|
67
|
+
tools.append(ToolSpec(
|
|
68
|
+
name="firecrawl_scrape",
|
|
69
|
+
description="Scrape and extract clean markdown with automatic content-addressed SHA-256 caching into .research/sources/.",
|
|
70
|
+
input_schema={
|
|
71
|
+
"type": "object",
|
|
72
|
+
"properties": {
|
|
73
|
+
"url": {"type": "string", "description": "URL to scrape and cache"},
|
|
74
|
+
},
|
|
75
|
+
"required": ["url"],
|
|
76
|
+
},
|
|
77
|
+
handler=handle_firecrawl_scrape,
|
|
78
|
+
aliases=("scrape", "fetch"),
|
|
79
|
+
))
|
|
80
|
+
|
|
81
|
+
server = StdioJsonRpcServer(
|
|
82
|
+
server_name=f"epistemic-{service}-fallback",
|
|
83
|
+
version="0.2.1",
|
|
84
|
+
tools=tools,
|
|
85
|
+
log_prefix="[mcp-launcher]",
|
|
86
|
+
)
|
|
87
|
+
server.serve_forever()
|
|
177
88
|
|
|
178
89
|
def main():
|
|
179
90
|
service = sys.argv[1] if len(sys.argv) > 1 else "brave-search"
|
|
@@ -1,9 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://opencode.ai/config.json",
|
|
3
|
+
"mcp": {
|
|
4
|
+
"iumbtems": {
|
|
5
|
+
"type": "local",
|
|
6
|
+
"command": ["python3", "<IUMBTEMS_ROOT>/runner/mcp_server.py"],
|
|
7
|
+
"enabled": true
|
|
8
|
+
}
|
|
9
|
+
},
|
|
3
10
|
"plugin": [
|
|
4
|
-
|
|
11
|
+
[
|
|
12
|
+
"@heretek-ai/epistemic-swarm",
|
|
13
|
+
{
|
|
14
|
+
"search_engine": "duckduckgo",
|
|
15
|
+
"max_iterations": 2,
|
|
16
|
+
"mode": "research"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
5
19
|
],
|
|
6
20
|
"agent": {
|
|
21
|
+
"code-auditor": {
|
|
22
|
+
"description": "IUMBTEMS Codebase Auditor: Dialectic architecture mapping and vulnerability red-teaming with line-level proof.",
|
|
23
|
+
"tools": {
|
|
24
|
+
"read": true,
|
|
25
|
+
"write": true,
|
|
26
|
+
"grep": true,
|
|
27
|
+
"glob": true,
|
|
28
|
+
"iumbtems_code_audit": true,
|
|
29
|
+
"iumbtems_config": true
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"oss-scout": {
|
|
33
|
+
"description": "IUMBTEMS Open Source Scout: Discovers mature OSS packages, audits copyleft licensing, and generates clean-room blueprints.",
|
|
34
|
+
"tools": {
|
|
35
|
+
"read": true,
|
|
36
|
+
"write": true,
|
|
37
|
+
"grep": true,
|
|
38
|
+
"glob": true,
|
|
39
|
+
"iumbtems_oss_scout": true,
|
|
40
|
+
"iumbtems_config": true
|
|
41
|
+
}
|
|
42
|
+
},
|
|
7
43
|
"alpha-thesis": {
|
|
8
44
|
"description": "IUMBTEMS Proponent Agent: Gathers corroborating primary sources, benchmark results, and empirical proofs.",
|
|
9
45
|
"tools": {
|
|
@@ -13,7 +49,9 @@
|
|
|
13
49
|
"grep": true,
|
|
14
50
|
"glob": true,
|
|
15
51
|
"task": true,
|
|
16
|
-
"
|
|
52
|
+
"iumbtems_swarm_research": true,
|
|
53
|
+
"iumbtems_verify_quote": true,
|
|
54
|
+
"iumbtems_config": true
|
|
17
55
|
}
|
|
18
56
|
},
|
|
19
57
|
"beta-redteam": {
|
|
@@ -25,6 +63,7 @@
|
|
|
25
63
|
"grep": true,
|
|
26
64
|
"glob": true,
|
|
27
65
|
"task": true,
|
|
66
|
+
"iumbtems_swarm_research": true,
|
|
28
67
|
"iumbtems_verify_quote": true
|
|
29
68
|
}
|
|
30
69
|
},
|
|
@@ -37,12 +76,28 @@
|
|
|
37
76
|
"glob": true,
|
|
38
77
|
"iumbtems_verify_quote": true
|
|
39
78
|
}
|
|
79
|
+
},
|
|
80
|
+
"brainstormer": {
|
|
81
|
+
"description": "IUMBTEMS Lateral Brainstormer: divergent what-if features, paradigm inversions, falsifiable spikes (never bug fixes).",
|
|
82
|
+
"tools": {
|
|
83
|
+
"read": true,
|
|
84
|
+
"write": true,
|
|
85
|
+
"grep": true,
|
|
86
|
+
"glob": true,
|
|
87
|
+
"iumbtems_brainstorm": true,
|
|
88
|
+
"iumbtems_config": true
|
|
89
|
+
}
|
|
40
90
|
}
|
|
41
91
|
},
|
|
42
92
|
"skills": {
|
|
43
93
|
"paths": [
|
|
44
94
|
"./skills/grilling",
|
|
45
|
-
"./skills/
|
|
95
|
+
"./skills/research_cache",
|
|
96
|
+
"./skills/epistemic_search",
|
|
97
|
+
"./skills/swarm_config",
|
|
98
|
+
"./skills/code_audit",
|
|
99
|
+
"./skills/oss_scout",
|
|
100
|
+
"./skills/brainstorming"
|
|
46
101
|
]
|
|
47
102
|
}
|
|
48
103
|
}
|
package/config/searxng_mcp.py
CHANGED
|
@@ -11,6 +11,15 @@ import urllib.request
|
|
|
11
11
|
import urllib.parse
|
|
12
12
|
from typing import Dict, Any, List
|
|
13
13
|
|
|
14
|
+
# Add parent dir to path so the shared protocol module resolves when this
|
|
15
|
+
# script is launched directly as `python3 config/searxng_mcp.py`.
|
|
16
|
+
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
17
|
+
PROJECT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, ".."))
|
|
18
|
+
if PROJECT_ROOT not in sys.path:
|
|
19
|
+
sys.path.insert(0, PROJECT_ROOT)
|
|
20
|
+
|
|
21
|
+
from runner.mcp_protocol import StdioJsonRpcServer, ToolSpec # noqa: E402
|
|
22
|
+
|
|
14
23
|
SEARXNG_URL = os.environ.get("SEARXNG_URL", "http://localhost:8080")
|
|
15
24
|
|
|
16
25
|
def search_searxng(query: str, categories: str = "general", max_results: int = 10) -> List[Dict[str, Any]]:
|
|
@@ -23,7 +32,7 @@ def search_searxng(query: str, categories: str = "general", max_results: int = 1
|
|
|
23
32
|
}
|
|
24
33
|
url = f"{SEARXNG_URL.rstrip('/')}/search?{urllib.parse.urlencode(params)}"
|
|
25
34
|
req = urllib.request.Request(url, headers={"User-Agent": "EpistemicSwarm/1.0"})
|
|
26
|
-
|
|
35
|
+
|
|
27
36
|
try:
|
|
28
37
|
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
29
38
|
data = json.loads(resp.read().decode("utf-8"))
|
|
@@ -41,93 +50,43 @@ def search_searxng(query: str, categories: str = "general", max_results: int = 1
|
|
|
41
50
|
return [{"error": f"Failed to query SearXNG at {SEARXNG_URL}: {str(e)}"}]
|
|
42
51
|
|
|
43
52
|
|
|
44
|
-
def
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
method = msg.get("method")
|
|
53
|
+
def build_server() -> StdioJsonRpcServer:
|
|
54
|
+
def handle_searxng_search(args: Dict[str, Any]) -> str:
|
|
55
|
+
res = search_searxng(
|
|
56
|
+
query=args.get("query", ""),
|
|
57
|
+
categories=args.get("categories", "general"),
|
|
58
|
+
max_results=args.get("max_results", 10),
|
|
59
|
+
)
|
|
60
|
+
return json.dumps(res, indent=2)
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
+
return StdioJsonRpcServer(
|
|
63
|
+
server_name="searxng-mcp",
|
|
64
|
+
version="1.0.0",
|
|
65
|
+
tools=[
|
|
66
|
+
ToolSpec(
|
|
67
|
+
name="searxng_search",
|
|
68
|
+
description="Execute an unbiased multi-engine metasearch via SearXNG.",
|
|
69
|
+
input_schema={
|
|
70
|
+
"type": "object",
|
|
71
|
+
"properties": {
|
|
72
|
+
"query": {"type": "string", "description": "Search query"},
|
|
73
|
+
"categories": {
|
|
74
|
+
"type": "string",
|
|
75
|
+
"description": "Categories (general, science, it)",
|
|
76
|
+
"default": "general"
|
|
77
|
+
},
|
|
78
|
+
"max_results": {"type": "integer", "default": 10}
|
|
79
|
+
},
|
|
80
|
+
"required": ["query"]
|
|
62
81
|
},
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
sys.stdout.write(json.dumps(response) + "\n")
|
|
70
|
-
sys.stdout.flush()
|
|
71
|
-
|
|
72
|
-
elif method == "tools/list":
|
|
73
|
-
response = {
|
|
74
|
-
"jsonrpc": "2.0",
|
|
75
|
-
"id": msg_id,
|
|
76
|
-
"result": {
|
|
77
|
-
"tools": [
|
|
78
|
-
{
|
|
79
|
-
"name": "searxng_search",
|
|
80
|
-
"description": "Execute an unbiased multi-engine metasearch via SearXNG.",
|
|
81
|
-
"inputSchema": {
|
|
82
|
-
"type": "object",
|
|
83
|
-
"properties": {
|
|
84
|
-
"query": {"type": "string", "description": "Search query"},
|
|
85
|
-
"categories": {
|
|
86
|
-
"type": "string",
|
|
87
|
-
"description": "Categories (general, science, it)",
|
|
88
|
-
"default": "general"
|
|
89
|
-
},
|
|
90
|
-
"max_results": {"type": "integer", "default": 10}
|
|
91
|
-
},
|
|
92
|
-
"required": ["query"]
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
]
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
sys.stdout.write(json.dumps(response) + "\n")
|
|
99
|
-
sys.stdout.flush()
|
|
100
|
-
|
|
101
|
-
elif method == "tools/call":
|
|
102
|
-
params = msg.get("params", {})
|
|
103
|
-
tool_name = params.get("name")
|
|
104
|
-
args = params.get("arguments", {})
|
|
105
|
-
|
|
106
|
-
if tool_name == "searxng_search":
|
|
107
|
-
query = args.get("query", "")
|
|
108
|
-
cat = args.get("categories", "general")
|
|
109
|
-
limit = args.get("max_results", 10)
|
|
110
|
-
res = search_searxng(query, categories=cat, max_results=limit)
|
|
111
|
-
response = {
|
|
112
|
-
"jsonrpc": "2.0",
|
|
113
|
-
"id": msg_id,
|
|
114
|
-
"result": {
|
|
115
|
-
"content": [
|
|
116
|
-
{
|
|
117
|
-
"type": "text",
|
|
118
|
-
"text": json.dumps(res, indent=2)
|
|
119
|
-
}
|
|
120
|
-
]
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
sys.stdout.write(json.dumps(response) + "\n")
|
|
124
|
-
sys.stdout.flush()
|
|
82
|
+
handler=handle_searxng_search,
|
|
83
|
+
)
|
|
84
|
+
],
|
|
85
|
+
)
|
|
125
86
|
|
|
126
87
|
|
|
127
88
|
def main():
|
|
128
|
-
|
|
129
|
-
if line.strip():
|
|
130
|
-
handle_json_rpc(line.strip())
|
|
89
|
+
build_server().serve_forever()
|
|
131
90
|
|
|
132
91
|
if __name__ == "__main__":
|
|
133
92
|
main()
|