Skip to content

Commit e87d13e

Browse files
- Got it working locally.
1 parent 3006ab4 commit e87d13e

File tree

8 files changed

+316
-256
lines changed

8 files changed

+316
-256
lines changed

.vscode/launch.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,28 @@
445445
"${input:queryString}"
446446
],
447447
},
448+
{
449+
"name": "run MCP standalone server",
450+
"type": "go",
451+
"request": "launch",
452+
"envFile": "${workspaceFolder}/.vscode/.env",
453+
"mode": "debug",
454+
"program": "${workspaceFolder}/stackql",
455+
"args": [
456+
"mcp",
457+
"--pgsrv.port=6555",
458+
"--tls.allowInsecure",
459+
"--auth=${input:authString}",
460+
"--session=${input:sessionString}",
461+
"--gc=${input:gcString}",
462+
"--registry=${input:registryString}",
463+
"--namespaces=${input:namespaceString}",
464+
"--sqlBackend=${input:sqlBackendString}",
465+
"--dbInternal=${input:dbInternalString}",
466+
"--export.alias=${input:exportAliasString}",
467+
"--pgsrv.debug.enable=${input:serverDebugPublish}",
468+
],
469+
},
448470
{
449471
"name": "run server",
450472
"type": "go",

AGENTS.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Repository Guidelines
2+
3+
These guidelines help contributors work effectively on the PostgreSQL MCP server in this repo.
4+
5+
## StackQL Resource Key Encoding Quirk
6+
7+
### Hierarchical Keys and URL Encoding
8+
9+
StackQL resource methods (especially for GitHub refs/tags) require exact matches for hierarchical keys, such as `refs/tags/x1.3`. However, due to dependency library behavior (e.g., gorilla/mux in Go), forward slashes in parameters may be interpreted as path delimiters, causing query failures or incorrect resource matching.
10+
11+
**Workaround:** Always URL encode forward slashes in resource keys when constructing queries. For example:
12+
13+
- **Incorrect:**
14+
```sql
15+
SELECT ref FROM github.git.refs WHERE owner = 'stackql' AND repo = 'mcp-stackql' AND ref = 'refs/tags/x2.0';
16+
```
17+
- **Correct:**
18+
```sql
19+
SELECT ref FROM github.git.refs WHERE owner = 'stackql' AND repo = 'mcp-stackql' AND ref = 'tags%2Fx2.0';
20+
```
21+
22+
This ensures the backend treats the parameter as a literal string, not a path.
23+
24+
### Guidance for LLMs and Contributors
25+
26+
- Always encode slashes in hierarchical resource keys for StackQL queries.
27+
- Do not use wildcards or inequalities for `WHERE` clauses that map to parameters (eg: HTTP path parameters); in such cases, only exact matches are supported.
28+
- Document this quirk in scripts, tests, and code comments to avoid confusion.
29+
- Example for LLM prompt:
30+
> "When querying StackQL resources with keys containing slashes, always URL encode the slash."
31+
32+
### Why This Is Necessary
33+
34+
Many RESTful routing libraries (like gorilla/mux) treat slashes as path separators. Encoding slashes prevents misinterpretation and ensures correct resource access.
35+
36+
Refer to this section whenever you encounter issues with resource keys containing slashes or hierarchical identifiers.
37+
38+
39+
## Project Structure & Module Organization
40+
- Root module: `postgres_server.py` — FastMCP server exposing PostgreSQL tools.
41+
- Config: `.env` (optional), `smithery.yaml` (publishing metadata).
42+
- Packaging/infra: `requirements.txt`, `Dockerfile`.
43+
- Docs: `README.md`, this `AGENTS.md`.
44+
- No dedicated `src/` or `tests/` directories yet; keep server logic cohesive and small, or start a `src/` layout if adding modules.
45+
46+
## Build, Test, and Development Commands
47+
- Create env: `python -m venv .venv && source .venv/bin/activate`
48+
- Install deps: `pip install -r requirements.txt`
49+
- Run server (no DB): `python postgres_server.py`
50+
- Run with DB: `POSTGRES_CONNECTION_STRING="postgresql://user:pass@host:5432/db" python postgres_server.py`
51+
- Docker build/run: `docker build -t mcp-postgres .` then `docker run -e POSTGRES_CONNECTION_STRING=... -p 8000:8000 mcp-postgres`
52+
53+
## Coding Style & Naming Conventions
54+
- Python 3.10+, 4-space indentation, PEP 8.
55+
- Use type hints (as in current code) and concise docstrings.
56+
- Functions/variables: `snake_case`; classes: `PascalCase`; MCP tool names: short `snake_case`.
57+
- Logging: use the existing `logger` instance; prefer informative, non-PII messages.
58+
- Optional formatting/linting: `black` and `ruff` (not enforced in repo). Example: `pip install black ruff && ruff check . && black .`.
59+
60+
## Testing Guidelines
61+
- There is no test suite yet. Prefer adding `pytest` with tests under `tests/` named `test_*.py`.
62+
- For DB behaviors, use a disposable PostgreSQL instance or mock `psycopg2` connections.
63+
- Minimum smoke test: start server without DSN, verify each tool returns the friendly “connection string is not set” message.
64+
65+
## Typed Tools & Resources
66+
- Preferred tools: `run_query(QueryInput)` and `run_query_json(QueryJSONInput)` with validated inputs (via Pydantic) and `row_limit` safeguards.
67+
- Legacy tools `query`/`query_json` remain for backward compatibility.
68+
- Table resources: `table://{schema}/{table}` (best-effort registration), with fallback tools `list_table_resources` and `read_table_resource`.
69+
- Prompts available as MCP prompts and tools: `write_safe_select`, `explain_plan_tips`.
70+
71+
## Tests
72+
- Test deps: `dev-requirements.txt` (`pytest`, `pytest-cov`).
73+
- Layout: `tests/test_server_tools.py` includes no-DSN smoke tests and prompt checks.
74+
- Run: `pytest -q`. Ensure runtime deps installed from `requirements.txt`.
75+
76+
## Commit & Pull Request Guidelines
77+
- Commit style: conventional commits preferred (`feat:`, `fix:`, `chore:`, `docs:`). Keep subjects imperative and concise.
78+
- PRs should include: purpose & scope, before/after behavior, example commands/queries, and any config changes (`POSTGRES_CONNECTION_STRING`, Docker, `mcp.json`).
79+
- When adding tools, document them in `README.md` (name, args, example) and ensure safe output formatting.
80+
- Never commit secrets. `.env`, `.venv`, and credentials are ignored by `.gitignore`.
81+
82+
## Security & Configuration Tips
83+
- Pass DB credentials via `POSTGRES_CONNECTION_STRING` env var; avoid hardcoding.
84+
- Prefer least-privilege DB users and SSL options (e.g., add `?sslmode=require`).
85+
- The server runs without a DSN for inspection; database-backed tools should fail gracefully (maintain this behavior).

internal/stackql/cmd/mcp.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
Copyright © 2019 stackql [email protected]
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package cmd
17+
18+
import (
19+
"context"
20+
21+
"github.com/spf13/cobra"
22+
23+
"github.com/stackql/any-sdk/pkg/logging"
24+
"github.com/stackql/stackql/internal/stackql/entryutil"
25+
"github.com/stackql/stackql/internal/stackql/iqlerror"
26+
"github.com/stackql/stackql/pkg/mcp_server"
27+
)
28+
29+
//nolint:gochecknoglobals // cobra pattern
30+
var mcpSrvCmd = &cobra.Command{
31+
Use: "mcp",
32+
Short: "run mcp server",
33+
Long: `
34+
Run a MCP protocol server.
35+
Supports MCP client connections from all manner or libs.
36+
`,
37+
//nolint:revive // acceptable for now
38+
Run: func(cmd *cobra.Command, args []string) {
39+
flagErr := dependentFlagHandler(&runtimeCtx)
40+
iqlerror.PrintErrorAndExitOneIfError(flagErr)
41+
inputBundle, err := entryutil.BuildInputBundle(runtimeCtx)
42+
iqlerror.PrintErrorAndExitOneIfError(err)
43+
handlerCtx, err := entryutil.BuildHandlerContext(runtimeCtx, nil, queryCache, inputBundle, false)
44+
iqlerror.PrintErrorAndExitOneIfError(err)
45+
iqlerror.PrintErrorAndExitOneIfNil(handlerCtx, "handler context is unexpectedly nil")
46+
server, serverErr := mcp_server.NewExampleBackendServer(
47+
nil,
48+
logging.GetLogger(),
49+
)
50+
// server, serverErr := mcp_server.NewExampleHTTPBackendServer(
51+
// logging.GetLogger(),
52+
// )
53+
iqlerror.PrintErrorAndExitOneIfError(serverErr)
54+
server.Start(context.Background()) //nolint:errcheck // TODO: investigate
55+
},
56+
}

internal/stackql/cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ func init() {
197197
rootCmd.AddCommand(shellCmd)
198198
rootCmd.AddCommand(registryCmd)
199199
rootCmd.AddCommand(srvCmd)
200+
rootCmd.AddCommand(mcpSrvCmd)
200201
}
201202

202203
func mergeConfigFromFile(runtimeCtx *dto.RuntimeCtx, flagSet pflag.FlagSet) {

0 commit comments

Comments
 (0)