Skip to content

Commit ae75823

Browse files
committed
✨ Add mcp and gpt commands to connect Anyquery to LLMs
1 parent d2a22ae commit ae75823

38 files changed

+3256
-146
lines changed

.goreleaser.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ chocolateys:
153153
Anyquery is a query engine that allows you to query anything over SQL. It's built on top of SQLite.
154154
155155
icon_url: https://anyquery.dev/favicon.png
156-
copyright: 2024 Julien CAGNIART
156+
copyright: 2025 Julien CAGNIART
157157
tags: "anyquery sql sqlite mysql query database api"
158158
package_source_url: https://github.com/julien040/anyquery
159159
release_notes: "See release notes at https://github.com/julien040/anyquery/releases"
@@ -240,7 +240,7 @@ winget:
240240
homepage: https://anyquery.dev
241241
license_url: https://github.com/julien040/anyquery/blob/main/LICENSE.md
242242
skip_upload: false
243-
copyright: 2024 Julien CAGNIART
243+
copyright: 2025 Julien CAGNIART
244244
copyright_url: https://github.com/julien040/anyquery/blob/main/LICENSE.md
245245
tags:
246246
- anyquery

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
{
2-
"go.buildTags": "vtable fts5 sqlite_json sqlite_math_functions"
2+
"go.buildTags": "vtable fts5 sqlite_json sqlite_math_functions",
3+
"yaml.schemas": {
4+
"https://spec.openapis.org/oas/3.1/schema/2022-10-07": "file:///Users/julien/Code/anyquery/other/llm/openapi-actions.yaml"
5+
}
36
}

cmd/llm.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package cmd
2+
3+
import (
4+
"github.com/julien040/anyquery/controller"
5+
"github.com/spf13/cobra"
6+
)
7+
8+
var gptCmd = &cobra.Command{
9+
Use: "gpt",
10+
Aliases: []string{"chat", "chatgpt"},
11+
Short: "Open an HTTP server so that ChatGPT can do function calls",
12+
Long: `Open an HTTP server so that ChatGPT can do function calls. By default, it will expose a tunnel to the internet.
13+
By setting the --host or --port flags, you can disable the tunnel and bind to a specific host and port. In this case, you will need to configure your LLM to connect to this host and port.`,
14+
15+
RunE: controller.Gpt,
16+
}
17+
18+
var mcpCmd = &cobra.Command{
19+
Use: "mcp",
20+
Short: "Start the Model Context Protocol (MCP) server",
21+
Long: `Start the Model Context Protocol (MCP) server. It is used to provide context for LLM that supports it.
22+
Pass the --stdio flag to use standard input/output for communication. By default, it will bind locally to localhost:8070 (modify it with the --host, --port and --domain flags).
23+
You can also expose the tunnel to the internet by using the --tunnel flag (useful when the LLM is on a remote server).
24+
`,
25+
RunE: controller.Mcp,
26+
}
27+
28+
func init() {
29+
// GPT command
30+
rootCmd.AddCommand(gptCmd)
31+
addFlag_commandModifiesConfiguration(gptCmd)
32+
gptCmd.Flags().StringP("database", "d", "", "Database to connect to (a path or :memory:)")
33+
gptCmd.Flags().Bool("in-memory", false, "Use an in-memory database")
34+
gptCmd.Flags().Bool("readonly", false, "Open the SQLite database in read-only mode")
35+
gptCmd.Flags().Bool("read-only", false, "Open the SQLite database in read-only mode")
36+
gptCmd.Flags().StringSlice("extension", []string{}, "Load one or more extensions by specifying their path. Separate multiple extensions with a comma.")
37+
gptCmd.Flags().String("log-file", "", "Log file")
38+
gptCmd.Flags().String("log-level", "info", "Log level (trace, debug, info, warn, error, off)")
39+
gptCmd.Flags().String("log-format", "text", "Log format (text, json)")
40+
gptCmd.Flags().String("host", "", "Host to bind to. If not empty, the tunnel will be disabled")
41+
gptCmd.Flags().Int("port", 0, "Port to bind to. If not empty, the tunnel will be disabled")
42+
43+
// MCP command
44+
rootCmd.AddCommand(mcpCmd)
45+
addFlag_commandModifiesConfiguration(mcpCmd)
46+
mcpCmd.Flags().String("host", "127.0.0.1", "Host to bind to")
47+
mcpCmd.Flags().String("domain", "", "Domain to use for the HTTP tunnel (empty to use the host)")
48+
mcpCmd.Flags().Int("port", 8070, "Port to bind to")
49+
mcpCmd.Flags().Bool("stdio", false, "Use standard input/output for communication")
50+
mcpCmd.Flags().Bool("tunnel", false, "Use an HTTP tunnel, and expose the server to the internet (when used, --host, --domain and --port are ignored)")
51+
mcpCmd.Flags().StringP("database", "d", "", "Database to connect to (a path or :memory:)")
52+
mcpCmd.Flags().Bool("in-memory", false, "Use an in-memory database")
53+
mcpCmd.Flags().Bool("readonly", false, "Open the SQLite database in read-only mode")
54+
mcpCmd.Flags().Bool("read-only", false, "Open the SQLite database in read-only mode")
55+
mcpCmd.Flags().StringSlice("extension", []string{}, "Load one or more extensions by specifying their path. Separate multiple extensions with a comma.")
56+
mcpCmd.Flags().String("log-file", "", "Log file")
57+
mcpCmd.Flags().String("log-level", "info", "Log level (trace, debug, info, warn, error, off)")
58+
mcpCmd.Flags().String("log-format", "text", "Log format (text, json)")
59+
60+
}

controller/config/database.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package config
33

44
import (
55
"database/sql"
6+
"fmt"
67
"os"
78

89
_ "embed"
@@ -61,6 +62,12 @@ func OpenDatabaseConnection(path string, readOnly bool) (*sql.DB, *model.Queries
6162
return nil, nil, err
6263
}
6364

65+
// Apply the migrations in migrations.go
66+
err = applyMigrations(db)
67+
if err != nil {
68+
return nil, nil, fmt.Errorf("failed to apply migrations: %w", err)
69+
}
70+
6471
// We create the querier
6572
querier := model.New(db)
6673

controller/config/migrations.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package config
2+
3+
import (
4+
"database/sql"
5+
"fmt"
6+
"slices"
7+
)
8+
9+
type migration struct {
10+
// The version of the migration
11+
Version int
12+
// The description of the migration
13+
Description string
14+
// The SQL queries to run for the migration
15+
Queries []string
16+
17+
// A check to see if the migration has already been applied
18+
// This is used to skip migrations that have already been applied
19+
// If this is nil, the migration will always be applied
20+
Check func(db *sql.DB) (bool, error)
21+
}
22+
23+
var migrations = []migration{
24+
{
25+
Version: 1,
26+
Description: "Add column tableMetadata to plugin_installed",
27+
Queries: []string{
28+
`ALTER TABLE plugin_installed ADD COLUMN tableMetadata TEXT DEFAULT '{}' NOT NULL`,
29+
},
30+
Check: func(db *sql.DB) (bool, error) {
31+
var count int
32+
err := db.QueryRow("SELECT COUNT(*) FROM pragma_table_info('plugin_installed') WHERE name = 'tableMetadata'").Scan(&count)
33+
if err != nil {
34+
return false, err
35+
}
36+
37+
return count > 0, nil
38+
},
39+
},
40+
}
41+
42+
// Apply migrations to the database
43+
// unless they have already been applied
44+
func applyMigrations(db *sql.DB) error {
45+
// Sort the migrations by semver
46+
slices.SortStableFunc(migrations, func(i, j migration) int {
47+
return i.Version - j.Version
48+
})
49+
50+
// Get the current version
51+
var version int
52+
err := db.QueryRow("PRAGMA user_version").Scan(&version)
53+
if err != nil {
54+
return fmt.Errorf("failed to get current database version: %w", err)
55+
}
56+
57+
// Apply the migrations
58+
for _, m := range migrations {
59+
if version >= m.Version {
60+
continue
61+
}
62+
63+
if m.Check != nil {
64+
alreadyApplied, err := m.Check(db)
65+
if err != nil {
66+
return fmt.Errorf("failed to check if migration %d has already been applied: %w", m.Version, err)
67+
}
68+
69+
if alreadyApplied {
70+
continue
71+
}
72+
}
73+
74+
tx, err := db.Begin()
75+
if err != nil {
76+
return fmt.Errorf("failed to start transaction for migration %d: %w", m.Version, err)
77+
}
78+
79+
for _, q := range m.Queries {
80+
_, err := tx.Exec(q)
81+
if err != nil {
82+
tx.Rollback()
83+
return fmt.Errorf("failed to apply migration %d: %w", m.Version, err)
84+
}
85+
}
86+
87+
_, err = tx.Exec(fmt.Sprintf("PRAGMA user_version = %d", m.Version))
88+
if err != nil {
89+
tx.Rollback()
90+
return fmt.Errorf("failed to update database version: %w", err)
91+
}
92+
93+
err = tx.Commit()
94+
if err != nil {
95+
return fmt.Errorf("failed to commit transaction for migration %d: %w", m.Version, err)
96+
}
97+
98+
version = m.Version
99+
}
100+
101+
return nil
102+
}

controller/config/model/models.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)