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.
- agent_farm-0.1.1/PKG-INFO +129 -0
- agent_farm-0.1.1/README.md +104 -0
- agent_farm-0.1.1/pyproject.toml +49 -0
- agent_farm-0.1.1/src/agent_farm/__init__.py +2 -0
- agent_farm-0.1.1/src/agent_farm/macros.sql +551 -0
- agent_farm-0.1.1/src/agent_farm/main.py +194 -0
- agent_farm-0.1.1/src/agent_farm/py.typed +0 -0
|
@@ -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
|
+
[](https://www.python.org)
|
|
33
|
+
[](https://duckdb.org)
|
|
34
|
+
[](https://ollama.com)
|
|
35
|
+
[](https://www.docker.com)
|
|
36
|
+
[](https://modelcontextprotocol.io)
|
|
37
|
+
[](https://query.farm)
|
|
38
|
+
[](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
|
+
[](https://www.python.org)
|
|
8
|
+
[](https://duckdb.org)
|
|
9
|
+
[](https://ollama.com)
|
|
10
|
+
[](https://www.docker.com)
|
|
11
|
+
[](https://modelcontextprotocol.io)
|
|
12
|
+
[](https://query.farm)
|
|
13
|
+
[](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,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
|