agent-farm 0.1.1__tar.gz

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.
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.3
2
+ Name: agent-farm
3
+ Version: 0.1.1
4
+ Summary: DuckDB-powered MCP Server with SQL macros for LLM agents
5
+ Keywords: duckdb,mcp,llm,ollama,sql,agent
6
+ Author: Björn Bethge
7
+ Author-email: Björn Bethge <bjoern.bethge@gmail.com>
8
+ License: MIT
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Database
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Requires-Dist: duckdb>=1.1.0
18
+ Requires-Dist: pytest>=8.0 ; extra == 'dev'
19
+ Requires-Dist: ruff>=0.4 ; extra == 'dev'
20
+ Requires-Python: >=3.11
21
+ Project-URL: Homepage, https://github.com/bjoernbethge/agent-farm
22
+ Project-URL: Repository, https://github.com/bjoernbethge/agent-farm
23
+ Provides-Extra: dev
24
+ Description-Content-Type: text/markdown
25
+
26
+ <div align="center">
27
+ <img src="assets/farm.png" alt="Agent Farm" width="100%%" />
28
+ </div>
29
+
30
+ # Agent Farm
31
+
32
+ [![Python](https://img.shields.io/badge/Python-3.11+-blue.svg)](https://www.python.org)
33
+ [![DuckDB](https://img.shields.io/badge/DuckDB-1.1.0+-yellow.svg)](https://duckdb.org)
34
+ [![Ollama](https://img.shields.io/badge/Ollama-Run%%20Locally-white.svg)](https://ollama.com)
35
+ [![Docker](https://img.shields.io/badge/Docker-Enabled-blue.svg)](https://www.docker.com)
36
+ [![MCP](https://img.shields.io/badge/MCP-Protocol-green.svg)](https://modelcontextprotocol.io)
37
+ [![Query Farm](https://img.shields.io/badge/Powered%%20By-Query%%20Farm-orange.svg)](https://query.farm)
38
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
39
+
40
+ **DuckDB-powered MCP Server with SQL macros for LLM agents - Web Search, Python execution, RAG, and more.**
41
+
42
+ [DuckDB](https://duckdb.org) - [Ollama](https://ollama.com) - [Docker](https://www.docker.com) - [Query Farm](https://query.farm)
43
+
44
+ ## Features
45
+
46
+ - **MCP Server**: Exposes DuckDB as an MCP server for Claude and other LLM clients
47
+ - **Auto-Discovery**: Automatically discovers MCP configurations from standard locations
48
+ - **LLM Integration**: SQL macros for calling Ollama models (local and cloud)
49
+ - **Tool Calling**: Full function calling support for agentic workflows
50
+ - **Web Search**: DuckDuckGo and Brave Search integration
51
+ - **Shell Execution**: Run shell commands and Python code via UV
52
+ - **Web Scraping**: Fetch and extract text from web pages
53
+ - **RAG Support**: Embeddings and vector similarity search
54
+ - **Rich Extensions**: Pre-configured with useful DuckDB community extensions
55
+
56
+ ## Installation
57
+
58
+ Using uv (recommended):
59
+ uv sync --dev
60
+
61
+ Or with pip:
62
+ pip install -e .
63
+
64
+ ## Quick Start
65
+
66
+ Run the MCP server:
67
+ agent-farm
68
+
69
+ Or as a module:
70
+ python -m agent_farm
71
+
72
+ ## SQL Macros
73
+
74
+ ### Cloud LLM Models (via Ollama)
75
+
76
+ SELECT deepseek('Explain quantum computing');
77
+ SELECT kimi_think('Solve this step by step: ...');
78
+ SELECT qwen3_coder('Write a Python function for...');
79
+
80
+ ### Web Search
81
+
82
+ SELECT ddg_instant('Python programming');
83
+ SELECT ddg_abstract('machine learning');
84
+ SELECT brave_search('DuckDB tutorial');
85
+
86
+ ### Shell and Python Execution
87
+
88
+ SELECT shell('ls -la');
89
+ SELECT py('print(2+2)');
90
+ SELECT py_with('requests', 'import requests; print(requests.__version__)');
91
+
92
+ ### Web Scraping
93
+
94
+ SELECT fetch('https://example.com');
95
+ SELECT fetch_text('https://example.com');
96
+ SELECT fetch_json('https://api.example.com/data');
97
+
98
+ ### File and Git Operations
99
+
100
+ SELECT read_file('path/to/file.txt');
101
+ SELECT git_status();
102
+ SELECT git_log(10);
103
+
104
+ ### RAG and Embeddings
105
+
106
+ SELECT rag_query('What is the price?', 'Product: Widget, Price: 49.99');
107
+ SELECT embed('Hello world');
108
+ SELECT semantic_score('query', 'document');
109
+
110
+ ### Combined Power Macros
111
+
112
+ SELECT search_and_summarize('What is DuckDB?');
113
+ SELECT analyze_page('https://example.com', 'What is this page about?');
114
+ SELECT review_code('src/main.py');
115
+
116
+ ## Docker
117
+
118
+ docker build -t agent-farm .
119
+ docker run -it agent-farm
120
+
121
+ ## Requirements
122
+
123
+ - Python >= 3.11
124
+ - DuckDB >= 1.1.0
125
+ - Ollama (for LLM features)
126
+
127
+ ## License
128
+
129
+ MIT
@@ -0,0 +1,104 @@
1
+ <div align="center">
2
+ <img src="assets/farm.png" alt="Agent Farm" width="100%%" />
3
+ </div>
4
+
5
+ # Agent Farm
6
+
7
+ [![Python](https://img.shields.io/badge/Python-3.11+-blue.svg)](https://www.python.org)
8
+ [![DuckDB](https://img.shields.io/badge/DuckDB-1.1.0+-yellow.svg)](https://duckdb.org)
9
+ [![Ollama](https://img.shields.io/badge/Ollama-Run%%20Locally-white.svg)](https://ollama.com)
10
+ [![Docker](https://img.shields.io/badge/Docker-Enabled-blue.svg)](https://www.docker.com)
11
+ [![MCP](https://img.shields.io/badge/MCP-Protocol-green.svg)](https://modelcontextprotocol.io)
12
+ [![Query Farm](https://img.shields.io/badge/Powered%%20By-Query%%20Farm-orange.svg)](https://query.farm)
13
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
14
+
15
+ **DuckDB-powered MCP Server with SQL macros for LLM agents - Web Search, Python execution, RAG, and more.**
16
+
17
+ [DuckDB](https://duckdb.org) - [Ollama](https://ollama.com) - [Docker](https://www.docker.com) - [Query Farm](https://query.farm)
18
+
19
+ ## Features
20
+
21
+ - **MCP Server**: Exposes DuckDB as an MCP server for Claude and other LLM clients
22
+ - **Auto-Discovery**: Automatically discovers MCP configurations from standard locations
23
+ - **LLM Integration**: SQL macros for calling Ollama models (local and cloud)
24
+ - **Tool Calling**: Full function calling support for agentic workflows
25
+ - **Web Search**: DuckDuckGo and Brave Search integration
26
+ - **Shell Execution**: Run shell commands and Python code via UV
27
+ - **Web Scraping**: Fetch and extract text from web pages
28
+ - **RAG Support**: Embeddings and vector similarity search
29
+ - **Rich Extensions**: Pre-configured with useful DuckDB community extensions
30
+
31
+ ## Installation
32
+
33
+ Using uv (recommended):
34
+ uv sync --dev
35
+
36
+ Or with pip:
37
+ pip install -e .
38
+
39
+ ## Quick Start
40
+
41
+ Run the MCP server:
42
+ agent-farm
43
+
44
+ Or as a module:
45
+ python -m agent_farm
46
+
47
+ ## SQL Macros
48
+
49
+ ### Cloud LLM Models (via Ollama)
50
+
51
+ SELECT deepseek('Explain quantum computing');
52
+ SELECT kimi_think('Solve this step by step: ...');
53
+ SELECT qwen3_coder('Write a Python function for...');
54
+
55
+ ### Web Search
56
+
57
+ SELECT ddg_instant('Python programming');
58
+ SELECT ddg_abstract('machine learning');
59
+ SELECT brave_search('DuckDB tutorial');
60
+
61
+ ### Shell and Python Execution
62
+
63
+ SELECT shell('ls -la');
64
+ SELECT py('print(2+2)');
65
+ SELECT py_with('requests', 'import requests; print(requests.__version__)');
66
+
67
+ ### Web Scraping
68
+
69
+ SELECT fetch('https://example.com');
70
+ SELECT fetch_text('https://example.com');
71
+ SELECT fetch_json('https://api.example.com/data');
72
+
73
+ ### File and Git Operations
74
+
75
+ SELECT read_file('path/to/file.txt');
76
+ SELECT git_status();
77
+ SELECT git_log(10);
78
+
79
+ ### RAG and Embeddings
80
+
81
+ SELECT rag_query('What is the price?', 'Product: Widget, Price: 49.99');
82
+ SELECT embed('Hello world');
83
+ SELECT semantic_score('query', 'document');
84
+
85
+ ### Combined Power Macros
86
+
87
+ SELECT search_and_summarize('What is DuckDB?');
88
+ SELECT analyze_page('https://example.com', 'What is this page about?');
89
+ SELECT review_code('src/main.py');
90
+
91
+ ## Docker
92
+
93
+ docker build -t agent-farm .
94
+ docker run -it agent-farm
95
+
96
+ ## Requirements
97
+
98
+ - Python >= 3.11
99
+ - DuckDB >= 1.1.0
100
+ - Ollama (for LLM features)
101
+
102
+ ## License
103
+
104
+ MIT
@@ -0,0 +1,49 @@
1
+ [project]
2
+ name = "agent-farm"
3
+ version = "0.1.1"
4
+ description = "DuckDB-powered MCP Server with SQL macros for LLM agents"
5
+ readme = "README.md"
6
+ license = { text = "MIT" }
7
+ authors = [
8
+ { name = "Björn Bethge", email = "bjoern.bethge@gmail.com" }
9
+ ]
10
+ requires-python = ">=3.11"
11
+ keywords = ["duckdb", "mcp", "llm", "ollama", "sql", "agent"]
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Topic :: Database",
20
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
21
+ ]
22
+
23
+ dependencies = [
24
+ "duckdb>=1.1.0",
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ dev = [
29
+ "pytest>=8.0",
30
+ "ruff>=0.4",
31
+ ]
32
+
33
+ [project.scripts]
34
+ agent-farm = "agent_farm.main:main"
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/bjoernbethge/agent-farm"
38
+ Repository = "https://github.com/bjoernbethge/agent-farm"
39
+
40
+ [build-system]
41
+ requires = ["uv_build>=0.8.22,<0.9.0"]
42
+ build-backend = "uv_build"
43
+
44
+ [tool.ruff]
45
+ line-length = 100
46
+ target-version = "py311"
47
+
48
+ [tool.ruff.lint]
49
+ select = ["E", "F", "I", "W"]
@@ -0,0 +1,2 @@
1
+ def hello() -> str:
2
+ return "Hello from farmer-agent!"
@@ -0,0 +1,551 @@
1
+ -- macros.sql
2
+
3
+ -- Mock get_secret for now if not native:
4
+ CREATE OR REPLACE MACRO get_secret(name) AS 'mock_secret_value';
5
+
6
+ -- =============================================================================
7
+ -- OLLAMA BASE
8
+ -- =============================================================================
9
+
10
+ -- Base Ollama API endpoint
11
+ CREATE OR REPLACE MACRO ollama_base() AS 'http://localhost:11434';
12
+
13
+ -- Generic Ollama chat completion (simple)
14
+ CREATE OR REPLACE MACRO ollama_chat(model_name, prompt) AS (
15
+ SELECT json_extract_string(
16
+ http_post(
17
+ ollama_base() || '/api/generate',
18
+ headers := MAP {'Content-Type': 'application/json'},
19
+ body := json_object(
20
+ 'model', model_name,
21
+ 'prompt', prompt,
22
+ 'stream', false
23
+ )
24
+ ).body,
25
+ '$.response'
26
+ )
27
+ );
28
+
29
+ -- Ollama chat with messages format (for tool calling)
30
+ CREATE OR REPLACE MACRO ollama_chat_messages(model_name, messages_json) AS (
31
+ SELECT http_post(
32
+ ollama_base() || '/api/chat',
33
+ headers := MAP {'Content-Type': 'application/json'},
34
+ body := json_object(
35
+ 'model', model_name,
36
+ 'messages', json(messages_json),
37
+ 'stream', false
38
+ )
39
+ ).body
40
+ );
41
+
42
+ -- Ollama chat WITH tools (function calling)
43
+ CREATE OR REPLACE MACRO ollama_chat_with_tools(model_name, messages_json, tools_json) AS (
44
+ SELECT http_post(
45
+ ollama_base() || '/api/chat',
46
+ headers := MAP {'Content-Type': 'application/json'},
47
+ body := json_object(
48
+ 'model', model_name,
49
+ 'messages', json(messages_json),
50
+ 'tools', json(tools_json),
51
+ 'stream', false
52
+ )
53
+ ).body
54
+ );
55
+
56
+ -- Extract tool calls from Ollama response
57
+ CREATE OR REPLACE MACRO extract_tool_calls(response_body) AS (
58
+ SELECT json_extract(response_body, '$.message.tool_calls')
59
+ );
60
+
61
+ -- Extract text response from Ollama response
62
+ CREATE OR REPLACE MACRO extract_response(response_body) AS (
63
+ SELECT json_extract_string(response_body, '$.message.content')
64
+ );
65
+
66
+ -- Ollama embeddings
67
+ CREATE OR REPLACE MACRO ollama_embed(model_name, text_input) AS (
68
+ SELECT json_extract(
69
+ http_post(
70
+ ollama_base() || '/api/embeddings',
71
+ headers := MAP {'Content-Type': 'application/json'},
72
+ body := json_object(
73
+ 'model', model_name,
74
+ 'prompt', text_input
75
+ )
76
+ ).body,
77
+ '$.embedding'
78
+ )::FLOAT[]
79
+ );
80
+
81
+ -- =============================================================================
82
+ -- CLOUD MODELLE (via Ollama Gateway)
83
+ -- =============================================================================
84
+
85
+ -- DeepSeek V3.1 (671B Cloud)
86
+ CREATE OR REPLACE MACRO deepseek(prompt) AS ollama_chat('deepseek-v3.1:671b-cloud', prompt);
87
+
88
+ -- Kimi K2 (1T Cloud, mit Thinking-Variante)
89
+ CREATE OR REPLACE MACRO kimi(prompt) AS ollama_chat('kimi-k2:1t-cloud', prompt);
90
+ CREATE OR REPLACE MACRO kimi_think(prompt) AS ollama_chat('kimi-k2-thinking:cloud', prompt);
91
+
92
+ -- Gemini 3 Pro (Cloud)
93
+ CREATE OR REPLACE MACRO gemini(prompt) AS ollama_chat('gemini-3-pro-preview:latest', prompt);
94
+
95
+ -- Qwen3 Coder 480B (Cloud)
96
+ CREATE OR REPLACE MACRO qwen3_coder(prompt) AS ollama_chat('qwen3-coder:480b-cloud', prompt);
97
+
98
+ -- Qwen3 VL 235B (Vision, Cloud)
99
+ CREATE OR REPLACE MACRO qwen3_vl(prompt) AS ollama_chat('qwen3-vl:235b-cloud', prompt);
100
+
101
+ -- GLM 4.6 (Cloud)
102
+ CREATE OR REPLACE MACRO glm(prompt) AS ollama_chat('glm-4.6:cloud', prompt);
103
+
104
+ -- MiniMax M2 (Cloud)
105
+ CREATE OR REPLACE MACRO minimax(prompt) AS ollama_chat('minimax-m2:cloud', prompt);
106
+
107
+ -- GPT-OSS (Cloud, 120B und 20B)
108
+ CREATE OR REPLACE MACRO gpt_oss(prompt) AS ollama_chat('gpt-oss:120b-cloud', prompt);
109
+ CREATE OR REPLACE MACRO gpt_oss_small(prompt) AS ollama_chat('gpt-oss:20b-cloud', prompt);
110
+
111
+ -- =============================================================================
112
+ -- CLOUD MODELLE MIT TOOL CALLING
113
+ -- =============================================================================
114
+
115
+ -- DeepSeek with tools
116
+ CREATE OR REPLACE MACRO deepseek_tools(prompt, tools_json) AS (
117
+ SELECT ollama_chat_with_tools(
118
+ 'deepseek-v3.1:671b-cloud',
119
+ json_array(json_object('role', 'user', 'content', prompt)),
120
+ tools_json
121
+ )
122
+ );
123
+
124
+ -- Kimi with tools
125
+ CREATE OR REPLACE MACRO kimi_tools(prompt, tools_json) AS (
126
+ SELECT ollama_chat_with_tools(
127
+ 'kimi-k2:1t-cloud',
128
+ json_array(json_object('role', 'user', 'content', prompt)),
129
+ tools_json
130
+ )
131
+ );
132
+
133
+ -- Gemini with tools
134
+ CREATE OR REPLACE MACRO gemini_tools(prompt, tools_json) AS (
135
+ SELECT ollama_chat_with_tools(
136
+ 'gemini-3-pro-preview:latest',
137
+ json_array(json_object('role', 'user', 'content', prompt)),
138
+ tools_json
139
+ )
140
+ );
141
+
142
+ -- Qwen3 Coder with tools
143
+ CREATE OR REPLACE MACRO qwen3_coder_tools(prompt, tools_json) AS (
144
+ SELECT ollama_chat_with_tools(
145
+ 'qwen3-coder:480b-cloud',
146
+ json_array(json_object('role', 'user', 'content', prompt)),
147
+ tools_json
148
+ )
149
+ );
150
+
151
+ -- =============================================================================
152
+ -- MCP TOOL HELPERS
153
+ -- =============================================================================
154
+
155
+ -- Convert MCP tool schema to Ollama tool format
156
+ -- Usage: SELECT mcp_to_ollama_tool('tool_name', 'description', '{"type":"object","properties":{...}}')
157
+ CREATE OR REPLACE MACRO mcp_to_ollama_tool(tool_name, description, input_schema_json) AS (
158
+ SELECT json_object(
159
+ 'type', 'function',
160
+ 'function', json_object(
161
+ 'name', tool_name,
162
+ 'description', description,
163
+ 'parameters', json(input_schema_json)
164
+ )
165
+ )
166
+ );
167
+
168
+ -- Build tools array from multiple tool definitions
169
+ CREATE OR REPLACE MACRO build_tools_array(tools_list) AS (
170
+ SELECT json_group_array(json(tool)) FROM (SELECT unnest(tools_list) as tool)
171
+ );
172
+
173
+ -- =============================================================================
174
+ -- RAG HELPERS
175
+ -- =============================================================================
176
+
177
+ -- Standard RAG mit DeepSeek (beste Qualität)
178
+ CREATE OR REPLACE MACRO rag_query(question, context) AS
179
+ deepseek('Beantworte basierend auf folgendem Kontext:\n\n' || context || '\n\nFrage: ' || question);
180
+
181
+ -- RAG mit Kimi Thinking (für komplexe Reasoning-Aufgaben)
182
+ CREATE OR REPLACE MACRO rag_think(question, context) AS
183
+ kimi_think('Analysiere sorgfältig den Kontext und beantworte die Frage:\n\nKontext:\n' || context || '\n\nFrage: ' || question);
184
+
185
+ -- =============================================================================
186
+ -- AGENTIC HELPERS
187
+ -- =============================================================================
188
+
189
+ -- Agent loop: Send prompt with tools, get response
190
+ -- Returns full response including potential tool_calls
191
+ CREATE OR REPLACE MACRO agent_call(model_name, system_prompt, user_prompt, tools_json) AS (
192
+ SELECT ollama_chat_with_tools(
193
+ model_name,
194
+ json_array(
195
+ json_object('role', 'system', 'content', system_prompt),
196
+ json_object('role', 'user', 'content', user_prompt)
197
+ ),
198
+ tools_json
199
+ )
200
+ );
201
+
202
+ -- Check if response contains tool calls
203
+ CREATE OR REPLACE MACRO has_tool_calls(response_body) AS (
204
+ SELECT json_extract(response_body, '$.message.tool_calls') IS NOT NULL
205
+ AND json_array_length(json_extract(response_body, '$.message.tool_calls')) > 0
206
+ );
207
+
208
+ -- =============================================================================
209
+ -- EXTERNAL APIS
210
+ -- =============================================================================
211
+
212
+ CREATE OR REPLACE MACRO elevenlabs_tts(text_input) AS TABLE
213
+ SELECT
214
+ http_post(
215
+ 'https://api.elevenlabs.io/v1/tts/voice_id',
216
+ headers := MAP {'xi-api-key': get_secret('elevenlabs_key')},
217
+ body := json_object('text', text_input)
218
+ ) AS audio_file_bytes;
219
+
220
+ -- =============================================================================
221
+ -- UTILITY MACROS (must be defined before use)
222
+ -- =============================================================================
223
+
224
+ -- URL encode helper
225
+ CREATE OR REPLACE MACRO url_encode(str) AS (
226
+ replace(replace(replace(replace(replace(replace(
227
+ str,
228
+ '%', '%25'),
229
+ ' ', '%20'),
230
+ '&', '%26'),
231
+ '=', '%3D'),
232
+ '?', '%3F'),
233
+ '#', '%23')
234
+ );
235
+
236
+ -- Timestamp helpers
237
+ CREATE OR REPLACE MACRO now_iso() AS (
238
+ strftime(now(), '%Y-%m-%dT%H:%M:%SZ')
239
+ );
240
+
241
+ CREATE OR REPLACE MACRO now_unix() AS (
242
+ epoch(now())
243
+ );
244
+
245
+ -- =============================================================================
246
+ -- WEB SEARCH
247
+ -- =============================================================================
248
+
249
+ -- DuckDuckGo Instant Answer API (kostenlos, kein API Key)
250
+ -- HINWEIS: Das ist KEIN vollstaendiger Suchergebnis-API, nur Instant Answers!
251
+ CREATE OR REPLACE MACRO ddg_instant(query) AS (
252
+ http_get(
253
+ 'https://api.duckduckgo.com/?q=' || url_encode(query) || '&format=json&no_html=1'
254
+ ).body::JSON
255
+ );
256
+
257
+ -- Extrahiere Abstract aus DuckDuckGo Response
258
+ CREATE OR REPLACE MACRO ddg_abstract(query) AS (
259
+ json_extract_string(ddg_instant(query), '$.Abstract')
260
+ );
261
+
262
+ -- Extrahiere Related Topics aus DuckDuckGo
263
+ CREATE OR REPLACE MACRO ddg_related(query) AS (
264
+ json_extract(ddg_instant(query), '$.RelatedTopics')
265
+ );
266
+
267
+ -- DuckDuckGo Definition
268
+ CREATE OR REPLACE MACRO ddg_definition(query) AS (
269
+ json_extract_string(ddg_instant(query), '$.Definition')
270
+ );
271
+
272
+ -- Brave Search API (2000 queries/Monat kostenlos)
273
+ -- Benoetigt API Key in get_secret('brave_api_key')
274
+ CREATE OR REPLACE MACRO brave_search(query) AS (
275
+ http_get(
276
+ 'https://api.search.brave.com/res/v1/web/search?q=' || url_encode(query),
277
+ headers := MAP {'X-Subscription-Token': get_secret('brave_api_key')}
278
+ ).body::JSON
279
+ );
280
+
281
+ -- Brave Search - nur Web Results
282
+ CREATE OR REPLACE MACRO brave_results(query) AS (
283
+ json_extract(brave_search(query), '$.web.results')
284
+ );
285
+
286
+ -- Brave News
287
+ CREATE OR REPLACE MACRO brave_news(query) AS (
288
+ http_get(
289
+ 'https://api.search.brave.com/res/v1/news/search?q=' || url_encode(query),
290
+ headers := MAP {'X-Subscription-Token': get_secret('brave_api_key')}
291
+ ).body::JSON
292
+ );
293
+
294
+ -- =============================================================================
295
+ -- SHELL / COMMAND EXECUTION (via shellfs)
296
+ -- Syntax: Pipe-Zeichen am Ende '|' = lese von diesem Befehl
297
+ -- =============================================================================
298
+
299
+ -- Shell-Befehl ausfuehren und Output als Text
300
+ CREATE OR REPLACE MACRO shell(cmd) AS (
301
+ (SELECT content FROM read_text(cmd || ' |'))
302
+ );
303
+
304
+ -- Shell als CSV Tabelle
305
+ CREATE OR REPLACE MACRO shell_csv(cmd) AS TABLE
306
+ SELECT * FROM read_csv(cmd || ' |', auto_detect=true);
307
+
308
+ -- Shell als JSON Tabelle
309
+ CREATE OR REPLACE MACRO shell_json(cmd) AS TABLE
310
+ SELECT * FROM read_json(cmd || ' |', auto_detect=true);
311
+
312
+ -- Windows cmd.exe
313
+ CREATE OR REPLACE MACRO cmd(command) AS (
314
+ (SELECT content FROM read_text('cmd /c ' || command || ' |'))
315
+ );
316
+
317
+ -- PowerShell
318
+ CREATE OR REPLACE MACRO pwsh(command) AS (
319
+ (SELECT content FROM read_text('pwsh -NoProfile -Command "' || replace(command, '"', '`"') || '" |'))
320
+ );
321
+
322
+ -- =============================================================================
323
+ -- PYTHON / UV EXECUTION
324
+ -- =============================================================================
325
+
326
+ -- Python Code via uv run (inline)
327
+ CREATE OR REPLACE MACRO py(code) AS (
328
+ (SELECT content FROM read_text('uv run python -c "' || replace(code, '"', chr(92) || '"') || '" |'))
329
+ );
330
+
331
+ -- Python mit Dependencies (uv run --with)
332
+ CREATE OR REPLACE MACRO py_with(deps, code) AS (
333
+ (SELECT content FROM read_text('uv run --with ' || deps || ' python -c "' || replace(code, '"', chr(92) || '"') || '" |'))
334
+ );
335
+
336
+ -- Python Script ausfuehren
337
+ CREATE OR REPLACE MACRO py_script(script_path) AS (
338
+ (SELECT content FROM read_text('uv run python ' || script_path || ' |'))
339
+ );
340
+
341
+ -- Python Script mit Args
342
+ CREATE OR REPLACE MACRO py_script_args(script_path, args) AS (
343
+ (SELECT content FROM read_text('uv run python ' || script_path || ' ' || args || ' |'))
344
+ );
345
+
346
+ -- Python Expression evaluieren (print automatisch)
347
+ CREATE OR REPLACE MACRO py_eval(expr) AS (
348
+ (SELECT content FROM read_text('uv run python -c "print(' || replace(expr, '"', chr(92) || '"') || ')" |'))
349
+ );
350
+
351
+ -- =============================================================================
352
+ -- WEB SCRAPING / FETCH
353
+ -- =============================================================================
354
+
355
+ -- Fetch URL und gib rohen Content zurueck
356
+ CREATE OR REPLACE MACRO fetch(url) AS (
357
+ http_get(url).body
358
+ );
359
+
360
+ -- Fetch URL und konvertiere HTML zu plain text (benoetigt htmlstringify)
361
+ CREATE OR REPLACE MACRO fetch_text(url) AS (
362
+ htmlstringify(http_get(url).body)
363
+ );
364
+
365
+ -- Fetch JSON API
366
+ CREATE OR REPLACE MACRO fetch_json(url) AS (
367
+ http_get(url).body::JSON
368
+ );
369
+
370
+ -- Fetch mit Custom Headers
371
+ CREATE OR REPLACE MACRO fetch_headers(url, headers_map) AS (
372
+ http_get(url, headers := headers_map).body
373
+ );
374
+
375
+ -- Fetch mit User-Agent (fuer Sites die Bots blocken)
376
+ CREATE OR REPLACE MACRO fetch_ua(url) AS (
377
+ http_get(url, headers := MAP {'User-Agent': 'Mozilla/5.0 AppleWebKit/537.36'}).body
378
+ );
379
+
380
+ -- POST Request mit JSON Body
381
+ CREATE OR REPLACE MACRO post_json(url, body_json) AS (
382
+ http_post(
383
+ url,
384
+ headers := MAP {'Content-Type': 'application/json'},
385
+ body := body_json
386
+ ).body::JSON
387
+ );
388
+
389
+ -- POST mit Form Data
390
+ CREATE OR REPLACE MACRO post_form(url, form_data) AS (
391
+ http_post(
392
+ url,
393
+ headers := MAP {'Content-Type': 'application/x-www-form-urlencoded'},
394
+ body := form_data
395
+ ).body
396
+ );
397
+
398
+ -- =============================================================================
399
+ -- FILE OPERATIONS
400
+ -- =============================================================================
401
+
402
+ -- Lese Datei (native DuckDB)
403
+ CREATE OR REPLACE MACRO read_file(path) AS (
404
+ (SELECT content FROM read_text(path))
405
+ );
406
+
407
+ -- Liste Verzeichnis (Unix)
408
+ CREATE OR REPLACE MACRO ls(path) AS (
409
+ (SELECT content FROM read_text('ls -la ' || path || ' |'))
410
+ );
411
+
412
+ -- Liste Verzeichnis (Windows)
413
+ CREATE OR REPLACE MACRO dir_list(path) AS (
414
+ (SELECT content FROM read_text('dir "' || path || '" |'))
415
+ );
416
+
417
+ -- Find files (Unix)
418
+ CREATE OR REPLACE MACRO find_files(path, pattern) AS (
419
+ (SELECT content FROM read_text('find ' || path || ' -name "' || pattern || '" |'))
420
+ );
421
+
422
+ -- Find files (Windows)
423
+ CREATE OR REPLACE MACRO find_win(path, pattern) AS (
424
+ (SELECT content FROM read_text('dir /s /b "' || path || chr(92) || pattern || '" |'))
425
+ );
426
+
427
+ -- Cat multiple files
428
+ CREATE OR REPLACE MACRO cat_files(pattern) AS TABLE
429
+ SELECT * FROM read_text(pattern);
430
+
431
+ -- =============================================================================
432
+ -- GIT OPERATIONS
433
+ -- =============================================================================
434
+
435
+ CREATE OR REPLACE MACRO git_status() AS (
436
+ (SELECT content FROM read_text('git status |'))
437
+ );
438
+
439
+ CREATE OR REPLACE MACRO git_log(n) AS (
440
+ (SELECT content FROM read_text('git log -' || n::VARCHAR || ' --oneline |'))
441
+ );
442
+
443
+ CREATE OR REPLACE MACRO git_diff() AS (
444
+ (SELECT content FROM read_text('git diff |'))
445
+ );
446
+
447
+ CREATE OR REPLACE MACRO git_branch() AS (
448
+ (SELECT content FROM read_text('git branch -a |'))
449
+ );
450
+
451
+ -- =============================================================================
452
+ -- SYSTEM INFO
453
+ -- =============================================================================
454
+
455
+ -- System info (cross-platform via Python)
456
+ CREATE OR REPLACE MACRO sys_info() AS (
457
+ (SELECT content FROM read_text('uv run python -c "import platform,json;print(json.dumps(dict(system=platform.system(),release=platform.release(),machine=platform.machine(),python=platform.python_version())))" |'))
458
+ );
459
+
460
+ -- Environment variable (Unix)
461
+ CREATE OR REPLACE MACRO env_var(name) AS (
462
+ (SELECT content FROM read_text('printenv ' || name || ' |'))
463
+ );
464
+
465
+ -- Current working directory (Unix)
466
+ CREATE OR REPLACE MACRO cwd() AS (
467
+ (SELECT content FROM read_text('pwd |'))
468
+ );
469
+
470
+ -- Environment variable (Windows)
471
+ CREATE OR REPLACE MACRO env_var_win(name) AS (
472
+ (SELECT content FROM read_text('cmd /c echo %' || name || '% |'))
473
+ );
474
+
475
+ -- Current working directory (Windows)
476
+ CREATE OR REPLACE MACRO cwd_win() AS (
477
+ (SELECT content FROM read_text('cmd /c cd |'))
478
+ );
479
+
480
+ -- =============================================================================
481
+ -- KOMBINIERTE POWER-MAKROS
482
+ -- =============================================================================
483
+
484
+ -- Web Search + LLM Summary
485
+ CREATE OR REPLACE MACRO search_and_summarize(query) AS (
486
+ deepseek(
487
+ 'Fasse die Suchergebnisse zusammen und beantworte: ' || query ||
488
+ chr(10) || chr(10) || 'Suchergebnisse: ' || COALESCE(ddg_abstract(query), 'Keine Ergebnisse')
489
+ )
490
+ );
491
+
492
+ -- Fetch Page + LLM Analysis
493
+ CREATE OR REPLACE MACRO analyze_page(url, question) AS (
494
+ deepseek(
495
+ 'Analysiere den Webseiten-Inhalt und beantworte: ' || question ||
496
+ chr(10) || chr(10) || 'Inhalt: ' || fetch_text(url)
497
+ )
498
+ );
499
+
500
+ -- Code Review via LLM
501
+ CREATE OR REPLACE MACRO review_code(file_path) AS (
502
+ deepseek(
503
+ 'Code Review - finde Bugs, Verbesserungen und Security Issues:' ||
504
+ chr(10) || chr(10) || read_file(file_path)
505
+ )
506
+ );
507
+
508
+ -- Explain Code
509
+ CREATE OR REPLACE MACRO explain_code(file_path) AS (
510
+ deepseek('Erklaere diesen Code Schritt fuer Schritt:' || chr(10) || read_file(file_path))
511
+ );
512
+
513
+ -- Generate Python Code
514
+ CREATE OR REPLACE MACRO generate_py(task) AS (
515
+ deepseek('Schreibe Python-Code fuer: ' || task || ' - Gib NUR Code zurueck, kein Markdown.')
516
+ );
517
+
518
+ -- =============================================================================
519
+ -- DATA PROCESSING HELPERS
520
+ -- =============================================================================
521
+
522
+ -- CSV von URL laden
523
+ CREATE OR REPLACE MACRO load_csv_url(url) AS TABLE
524
+ SELECT * FROM read_csv(url, auto_detect=true);
525
+
526
+ -- JSON von URL laden
527
+ CREATE OR REPLACE MACRO load_json_url(url) AS TABLE
528
+ SELECT * FROM read_json(url, auto_detect=true);
529
+
530
+ -- Parquet von URL laden
531
+ CREATE OR REPLACE MACRO load_parquet_url(url) AS TABLE
532
+ SELECT * FROM read_parquet(url);
533
+
534
+ -- =============================================================================
535
+ -- VECTOR / EMBEDDING HELPERS (requires vss extension)
536
+ -- =============================================================================
537
+
538
+ -- Cosine similarity zwischen zwei Vektoren
539
+ CREATE OR REPLACE MACRO cosine_sim(vec1, vec2) AS (
540
+ list_cosine_similarity(vec1, vec2)
541
+ );
542
+
543
+ -- Text zu Embedding (via Ollama)
544
+ CREATE OR REPLACE MACRO embed(text_input) AS (
545
+ ollama_embed('nomic-embed-text', text_input)
546
+ );
547
+
548
+ -- Semantic search helper - gibt Similarity Score zurueck
549
+ CREATE OR REPLACE MACRO semantic_score(query_text, doc_text) AS (
550
+ cosine_sim(embed(query_text), embed(doc_text))
551
+ );
@@ -0,0 +1,194 @@
1
+ import duckdb
2
+ import sys
3
+ import os
4
+ import json
5
+ from pathlib import Path
6
+
7
+
8
+ def find_mcp_config():
9
+ """
10
+ Discover MCP configuration files in standard locations.
11
+ Returns list of (config_path, config_data) tuples.
12
+ """
13
+ config_locations = [
14
+ # Project-local
15
+ Path.cwd() / "mcp.json",
16
+ Path.cwd() / ".mcp.json",
17
+ Path.cwd() / "mcp_config.json",
18
+ # Claude Desktop standard locations
19
+ Path.home() / ".config" / "claude" / "claude_desktop_config.json",
20
+ Path.home() / "AppData" / "Roaming" / "Claude" / "claude_desktop_config.json", # Windows
21
+ Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json", # macOS
22
+ # Generic MCP config
23
+ Path.home() / ".mcp" / "config.json",
24
+ ]
25
+
26
+ found_configs = []
27
+ for config_path in config_locations:
28
+ if config_path.exists():
29
+ try:
30
+ with open(config_path, 'r', encoding='utf-8') as f:
31
+ config_data = json.load(f)
32
+ found_configs.append((str(config_path), config_data))
33
+ print(f"Found MCP config: {config_path}", file=sys.stderr)
34
+ except Exception as e:
35
+ print(f"Error reading {config_path}: {e}", file=sys.stderr)
36
+
37
+ return found_configs
38
+
39
+
40
+ def extract_mcp_servers(configs):
41
+ """
42
+ Extract MCP server definitions from config files.
43
+ Returns dict of server_name -> server_config
44
+ """
45
+ servers = {}
46
+ for config_path, config_data in configs:
47
+ # Handle claude_desktop_config.json format
48
+ if "mcpServers" in config_data:
49
+ for name, server_config in config_data["mcpServers"].items():
50
+ servers[name] = {
51
+ "source": config_path,
52
+ **server_config
53
+ }
54
+ # Handle simple mcp.json format
55
+ elif "servers" in config_data:
56
+ for name, server_config in config_data["servers"].items():
57
+ servers[name] = {
58
+ "source": config_path,
59
+ **server_config
60
+ }
61
+ return servers
62
+
63
+
64
+ def setup_mcp_tables(con, servers):
65
+ """
66
+ Create tables with discovered MCP server info for SQL access.
67
+ """
68
+ # Create table for MCP servers
69
+ con.sql("""
70
+ CREATE OR REPLACE TABLE mcp_servers (
71
+ name VARCHAR,
72
+ command VARCHAR,
73
+ args VARCHAR[],
74
+ env JSON,
75
+ source_config VARCHAR
76
+ )
77
+ """)
78
+
79
+ for name, config in servers.items():
80
+ command = config.get("command", "")
81
+ args = config.get("args", [])
82
+ env = json.dumps(config.get("env", {}))
83
+ source = config.get("source", "")
84
+
85
+ con.execute("""
86
+ INSERT INTO mcp_servers VALUES (?, ?, ?, ?, ?)
87
+ """, [name, command, args, env, source])
88
+
89
+ print(f"Registered {len(servers)} MCP servers in mcp_servers table", file=sys.stderr)
90
+
91
+
92
+ def main():
93
+ # Initialize DuckDB connection
94
+ con = duckdb.connect(database=":memory:")
95
+
96
+ print("initializing queries...", file=sys.stderr)
97
+
98
+ # 1. Install & Load Extensions
99
+ extensions = [
100
+ # Core: HTTP & Data Formats
101
+ "httpfs",
102
+ "http_client",
103
+ "json",
104
+ "icu",
105
+ "duckdb_mcp",
106
+
107
+ # Advanced Data Structs & Logic
108
+ "jsonata",
109
+ "duckpgq",
110
+ "bitfilters",
111
+ "lindel",
112
+
113
+ # AI/LLM Stack
114
+ "vss", # Vector Similarity Search (native)
115
+
116
+ # Text Processing
117
+ "htmlstringify", # HTML to plain text
118
+ "lsh", # Locality Sensitive Hashing
119
+
120
+ # Extended Data Sources
121
+ "shellfs", # Shell commands as tables
122
+ "zipfs", # Read ZIP archives
123
+
124
+ # Real-time (optional, may fail on some platforms)
125
+ "radio", # WebSocket & Redis PubSub
126
+ ]
127
+
128
+ loaded_extensions = []
129
+ for ext in extensions:
130
+ try:
131
+ con.sql(f"INSTALL {ext};")
132
+ con.sql(f"LOAD {ext};")
133
+ loaded_extensions.append(ext)
134
+ print(f"Loaded extension: {ext}", file=sys.stderr)
135
+ except Exception as e:
136
+ print(f"Failed to load {ext}: {e}", file=sys.stderr)
137
+ try:
138
+ con.sql(f"INSTALL {ext} FROM community;")
139
+ con.sql(f"LOAD {ext};")
140
+ loaded_extensions.append(ext)
141
+ print(f"Loaded extension {ext} from community", file=sys.stderr)
142
+ except Exception as e2:
143
+ print(f"Skipping {ext}: {e2}", file=sys.stderr)
144
+
145
+ # 2. MCP Config Discovery
146
+ print("Discovering MCP configurations...", file=sys.stderr)
147
+ mcp_configs = find_mcp_config()
148
+ mcp_servers = extract_mcp_servers(mcp_configs)
149
+
150
+ if mcp_servers:
151
+ setup_mcp_tables(con, mcp_servers)
152
+ else:
153
+ print("No MCP configurations found", file=sys.stderr)
154
+ # Create empty table for consistency
155
+ con.sql("""
156
+ CREATE OR REPLACE TABLE mcp_servers (
157
+ name VARCHAR,
158
+ command VARCHAR,
159
+ args VARCHAR[],
160
+ env JSON,
161
+ source_config VARCHAR
162
+ )
163
+ """)
164
+
165
+ # 3. Load Macros
166
+ macros_path = os.path.join(os.path.dirname(__file__), "macros.sql")
167
+ if os.path.exists(macros_path):
168
+ with open(macros_path, "r") as f:
169
+ sql_script = f.read()
170
+ for statement in sql_script.split(';'):
171
+ if statement.strip():
172
+ try:
173
+ con.sql(statement)
174
+ except Exception as e:
175
+ print(f"Error executing macro SQL: {e}", file=sys.stderr)
176
+ print("Loaded macros.", file=sys.stderr)
177
+
178
+ # 4. Create extension info table
179
+ con.sql(f"""
180
+ CREATE OR REPLACE TABLE loaded_extensions AS
181
+ SELECT unnest({loaded_extensions!r}::VARCHAR[]) as extension_name
182
+ """)
183
+
184
+ # 5. Start MCP Server
185
+ print("Starting MCP Server...", file=sys.stderr)
186
+ try:
187
+ con.sql("SELECT mcp_server_start('stdio', 'localhost', 0, '{}')")
188
+ except Exception as e:
189
+ print(f"Error starting MCP Server: {e}", file=sys.stderr)
190
+ sys.exit(1)
191
+
192
+
193
+ if __name__ == "__main__":
194
+ main()
File without changes