> ## Documentation Index
> Fetch the complete documentation index at: https://villagesql.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP로 AI 에이전트를 MySQL에 연결하기

> vsql_mcp 확장을 사용해 Model Context Protocol로 VillageSQL 데이터베이스를 AI 에이전트에 노출하는 방법 — 설치, 최소 권한 데이터베이스 계정, 가드레일, 그리고 실제 클라이언트 연결입니다.

> [card] [VillageSQL is a drop-in replacement for MySQL with extensions.](https://villagesql.com/docs/en/8.4/stable/install-server.md)
>
> All examples on this page work on VillageSQL. Install Now →

데이터베이스를 쿼리해야 하는 AI 에이전트는 보통 직접 작성한 도구나 사이드카 프로세스를 통해 데이터베이스와 대화합니다. `vsql_mcp` 확장은 그 계층을 없앱니다: VillageSQL 프로세스 자체 안에서 Model Context Protocol 서버를 실행하므로, 어떤 MCP 클라이언트든 스키마를 발견하고 쿼리를 직접 실행할 수 있습니다.

## vsql\_mcp가 하는 일

`vsql_mcp`는 서버 프로세스 안의 백그라운드 워커에서 Streamable HTTP 전송(사양 리비전 `2025-06-18`)으로 MCP를 제공하는 Rust 확장입니다. 여섯 개의 도구를 노출합니다: `list_schemas`, `list_tables`, `describe_table`, `query`, `explain`, `write`. `write` 도구는 활성화한 뒤에만 나타나며, 기본적으로 비활성화되어 있습니다.

`vsql_mcp`는 프리뷰 확장입니다. VEF 프리뷰 기능 `thread_worker`, `sys_var`, `status_var`, `sql_query`를 사용하므로, 서버는 프리뷰 확장이 허용된 상태로 시작되어야 합니다:

```bash theme={null}
mysqld --vsql_allow_preview_extensions=ON ...
```

설치하기 전에 플래그를 확인하세요:

```sql theme={null}
SHOW VARIABLES LIKE 'vsql_allow_preview_extensions';
```

```
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| vsql_allow_preview_extensions  | ON    |
+--------------------------------+-------+
```

## 샘플 데이터 만들기

이 가이드는 에이전트가 쿼리할 실제 대상이 있도록 전용 데이터베이스와 작은 `posts` 테이블을 사용합니다.

```sql theme={null}
CREATE DATABASE mcp_guide;

CREATE TABLE mcp_guide.posts (
  id INT PRIMARY KEY AUTO_INCREMENT,
  title VARCHAR(200) NOT NULL,
  author VARCHAR(100) NOT NULL,
  views INT NOT NULL DEFAULT 0
);

INSERT INTO mcp_guide.posts (title, author, views) VALUES
  ('Getting started with VillageSQL', 'Adam', 1520),
  ('Custom types in VEF', 'Priya', 890),
  ('Why we built the MCP extension', 'Tomas', 430);
```

## 확장 설치하기

```sql theme={null}
INSTALL EXTENSION vsql_mcp;
```

설치하면 구성 변수와 상태 변수가 등록됩니다. 다음 섹션에서 켜기 전까지는 어떤 포트에서도 수신 대기하지 않습니다.

## 최소 권한 계정 만들기

`vsql_mcp`는 `query`, `write`, `explain`, 테이블 DDL 리소스를 포함한 대부분의 도구 호출을 여러분이 구성한 계정을 사용해 서버에 대한 루프백 연결로 실행합니다. `list_schemas`, `list_tables`, `describe_table`만 그 계정 없이 프로세스 안에서 실행됩니다. 이 가이드가 사용하는 하나의 데이터베이스로 범위를 제한한, `SELECT`만 가진 전용 계정을 만드세요:

```sql theme={null}
CREATE USER 'mcp_user'@'127.0.0.1' IDENTIFIED BY 'ChangeMe-guide-2026!';
GRANT SELECT ON mcp_guide.* TO 'mcp_user'@'127.0.0.1';
```

계정의 권한이 실제 접근 경계입니다. 아래에서 다루는 `vsql_mcp` 자체의 가드레일은 이 권한을 대신하는 것이 아니라 그 위에서 적용됩니다.

## 서버 구성과 활성화

모든 `vsql_mcp` 설정은 `SET GLOBAL vsql_mcp.<name>` 변수입니다. `db_url`을 위에서 만든 계정으로 향하게 하고, 서버 범위를 하나의 스키마로 제한하고, 베어러 토큰을 요구하고, 켜세요:

```sql theme={null}
SET GLOBAL vsql_mcp.db_url = 'mysql://mcp_user:ChangeMe-guide-2026!@127.0.0.1:3306/mcp_guide';
SET GLOBAL vsql_mcp.schema = 'mcp_guide';
SET GLOBAL vsql_mcp.require_auth = ON;
SET GLOBAL vsql_mcp.bearer_token = 'a-long-random-token';
SET GLOBAL vsql_mcp.vsql_mcp_enabled = ON;
```

수신 대기 중인지 확인하세요:

```sql theme={null}
SHOW STATUS LIKE 'vsql_mcp%';
```

```
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| vsql_mcp.http_port         | 3100  |
| vsql_mcp.https_port        | 0     |
| vsql_mcp.rows_returned_total | 0   |
| vsql_mcp.sessions_active   | 0     |
| vsql_mcp.tool_calls_total  | 0     |
| vsql_mcp.tool_errors_total | 0     |
+----------------------------+-------+
```

`http_port`는 기본값인 `3100`을 보여줍니다. 서버는 `http://127.0.0.1:3100/mcp`에서 수신 대기하며 `127.0.0.1`에만 바인딩합니다. 원격 연결을 직접 받아들이는 일은 절대 없습니다. 그것이 필요하다면 리버스 프록시 뒤에 두는 것은 여러분의 책임입니다.

## 서버가 MCP로 말하는지 검증하기

실제 클라이언트를 연결하기 전에, MCP 핸드셰이크로 HTTP 엔드포인트를 직접 검증하세요. 베어러 토큰이 없는 요청은 거부됩니다:

```bash theme={null}
curl -i -X POST http://127.0.0.1:3100/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl-test","version":"1.0"}}}'
```

```
HTTP/1.1 401 Unauthorized
```

토큰과 함께라면 `initialize`가 성공하고 `Mcp-Session-Id` 헤더를 반환합니다:

```bash theme={null}
curl -i -X POST http://127.0.0.1:3100/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer a-long-random-token" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl-test","version":"1.0"}}}'
```

```
HTTP/1.1 200 OK
Mcp-Session-Id: f5330e8b88f9c5ffed95fdfdc2b42606

{"id":1,"jsonrpc":"2.0","result":{"capabilities":{"resources":{},"tools":{}},"protocolVersion":"2025-06-18","serverInfo":{"name":"vsql_mcp","version":"0.0.7"}}}
```

이 연결의 이후 모든 요청에는 그 세션 ID가 필요합니다. 생략하면 조용한 기본값이 아니라 JSON-RPC 오류로 실패합니다:

```bash theme={null}
curl -i -X POST http://127.0.0.1:3100/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer a-long-random-token" \
  -d '{"jsonrpc":"2.0","id":99,"method":"tools/list"}'
```

```
HTTP/1.1 400 Bad Request

{"error":{"code":-32600,"message":"missing Mcp-Session-Id header"},"id":99,"jsonrpc":"2.0"}
```

`notifications/initialized`를 보낸 다음, `initialize` 응답의 세션 ID를 사용해 사용 가능한 도구를 나열하세요:

```bash theme={null}
curl -X POST http://127.0.0.1:3100/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer a-long-random-token" \
  -H "Mcp-Session-Id: f5330e8b88f9c5ffed95fdfdc2b42606" \
  -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'

curl -X POST http://127.0.0.1:3100/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer a-long-random-token" \
  -H "Mcp-Session-Id: f5330e8b88f9c5ffed95fdfdc2b42606" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
```

```json theme={null}
{"id":2,"jsonrpc":"2.0","result":{"tools":[
  {"name":"list_schemas","description":"List schemas visible to the extension.","inputSchema":{"properties":{},"type":"object"}},
  {"name":"list_tables","description":"List tables and views in a schema, with row estimates.","inputSchema":{"properties":{"schema":{"type":"string"}},"type":"object"}},
  {"name":"describe_table","description":"Columns, types, and keys for a table.","inputSchema":{"properties":{"schema":{"type":"string"},"table":{"type":"string"}},"required":["table"],"type":"object"}},
  {"name":"query","description":"Run a single read-only SELECT and return JSON rows.","inputSchema":{"properties":{"sql":{"type":"string"}},"required":["sql"],"type":"object"}},
  {"name":"explain","description":"Return EXPLAIN FORMAT=JSON for a candidate query.","inputSchema":{"properties":{"sql":{"type":"string"}},"required":["sql"],"type":"object"}}
]}}
```

여섯 개가 아니라 다섯 개의 도구가 나열됩니다. `allow_write`가 아직 OFF이므로 `write`가 빠져 있고, 그래서 지금 연결하는 에이전트는 사용할 수 없는 도구를 중심으로 계획을 세우는 일이 없습니다.

## MCP로 도구 호출하기

`list_tables`는 노출된 스키마에 있는 하나의 테이블을 보여줍니다:

```bash theme={null}
curl -X POST http://127.0.0.1:3100/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer a-long-random-token" \
  -H "Mcp-Session-Id: f5330e8b88f9c5ffed95fdfdc2b42606" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"list_tables","arguments":{"schema":"mcp_guide"}}}'
```

```json theme={null}
{"id":3,"jsonrpc":"2.0","result":{"content":[{"text":"{\"schema\":\"mcp_guide\",\"tables\":[{\"name\":\"posts\",\"row_estimate\":3,\"type\":\"BASE TABLE\"}]}","type":"text"}],"isError":false}}
```

`query`는 `SELECT`를 실행하고 실제 행을 반환합니다:

```bash theme={null}
curl -X POST http://127.0.0.1:3100/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer a-long-random-token" \
  -H "Mcp-Session-Id: f5330e8b88f9c5ffed95fdfdc2b42606" \
  -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"query","arguments":{"sql":"SELECT id, title, author, views FROM mcp_guide.posts ORDER BY views DESC"}}}'
```

```json theme={null}
{"id":4,"jsonrpc":"2.0","result":{"content":[{"text":"{\"columns\":[\"id\",\"title\",\"author\",\"views\"],\"row_count\":3,\"rows\":[{\"author\":\"Adam\",\"id\":1,\"title\":\"Getting started with VillageSQL\",\"views\":1520},{\"author\":\"Priya\",\"id\":2,\"title\":\"Custom types in VEF\",\"views\":890},{\"author\":\"Tomas\",\"id\":3,\"title\":\"Why we built the MCP extension\",\"views\":430}],\"truncated\":false}","type":"text"}],"isError":false}}
```

이것은 직접 SQL 연결이 아니라 MCP 프로토콜을 통해 반환된 `posts` 테이블의 실제 샘플 데이터입니다.

`query`는 단일 읽기 전용 문장만 받아들입니다. 그 외의 것은 데이터베이스에 도달하기 전에 거부됩니다:

```bash theme={null}
curl -X POST http://127.0.0.1:3100/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer a-long-random-token" \
  -H "Mcp-Session-Id: f5330e8b88f9c5ffed95fdfdc2b42606" \
  -d '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"query","arguments":{"sql":"DELETE FROM mcp_guide.posts"}}}'
```

```json theme={null}
{"id":5,"jsonrpc":"2.0","result":{"content":[{"text":"only a single read-only statement is allowed by the query tool","type":"text"}],"isError":true}}
```

## 실제 클라이언트 연결하기

위의 단계는 서버가 원시 HTTP 클라이언트에 올바르게 응답한다는 것을 증명합니다. 에이전트가 실제로 사용할 수 있다는 것을 증명하려면, Claude Code에 서버를 등록하고 세션 관리를 맡기세요:

```bash theme={null}
claude mcp add --transport http vsql http://127.0.0.1:3100/mcp \
  --header "Authorization: Bearer a-long-random-token"
```

```
Added HTTP MCP server vsql with URL: http://127.0.0.1:3100/mcp to local config
Headers: {
  "Authorization": "[REDACTED]"
}
File modified: /Users/villageadam/.claude.json [project: /path/to/your/project]
```

`local config`는 서버의 범위를 이 하나의 프로젝트로 제한합니다. 모든 프로젝트에서 사용할 수 있게 하려면 대신 `--scope user`를 전달하세요. `claude mcp list`가 연결 상태를 보여줍니다:

```
vsql: http://127.0.0.1:3100/mcp (HTTP) - ✔ Connected
```

Claude Code는 자체 `initialize` 핸드셰이크를 수행하고, 세션을 협상하고, 서버가 MCP로 응답한다는 것을 확인했습니다. 이 가이드가 `SET GLOBAL` 문으로 구성하고 위에서 직접 테스트한 바로 그 서버입니다. 여기서부터 에이전트에게 `mcp_guide` 데이터베이스에 관해 질문하면, curl로 보여준 것과 정확히 같이 `list_tables`, `describe_table`, `query`를 거쳐 처리됩니다.

`vsql_mcp`에는 stdio 전송이 없습니다. 서버가 데이터베이스 프로세스 안에서 실행되므로, 클라이언트가 띄울 자식 프로세스가 없습니다. stdio만 말하는 클라이언트나, 벤더 클라우드를 통해 프록시하여 `127.0.0.1`에 도달할 수 없는 원격 클라이언트에는 엔드포인트 앞에 stdio-HTTP 브리지나 터널이 필요합니다.

## write 도구

`allow_write`는 기본적으로 OFF이며, 꺼져 있는 동안 `write`를 호출하면 데이터베이스를 건드리지 않고 즉시 실패합니다:

```bash theme={null}
curl -X POST http://127.0.0.1:3100/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer a-long-random-token" \
  -H "Mcp-Session-Id: f5330e8b88f9c5ffed95fdfdc2b42606" \
  -d '{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"write","arguments":{"sql":"UPDATE mcp_guide.posts SET views = 9999 WHERE id = 1"}}}'
```

```json theme={null}
{"id":6,"jsonrpc":"2.0","result":{"content":[{"text":"the write tool is disabled (set vsql_mcp.allow_write = ON)","type":"text"}],"isError":true}}
```

`allow_write`를 ON으로 켜면 도구가 `tools/list`에 나타나지만, 계정 자체의 권한은 여전히 적용됩니다. `mcp_user`가 아직 `SELECT`만 가지고 있으면, 같은 호출은 이제 대신 데이터베이스 계층에서 실패합니다:

```sql theme={null}
SET GLOBAL vsql_mcp.allow_write = ON;
```

```json theme={null}
{"id":8,"jsonrpc":"2.0","result":{"content":[{"text":"ERROR 1142 (42000): UPDATE command denied to user 'mcp_user'@'localhost' for table 'posts'","type":"text"}],"isError":true}}
```

데이터베이스에 `UPDATE`를 부여하면 같은 호출이 통과하고, 실제로 행을 변경합니다:

```sql theme={null}
GRANT UPDATE ON mcp_guide.* TO 'mcp_user'@'127.0.0.1';
```

```json theme={null}
{"id":9,"jsonrpc":"2.0","result":{"content":[{"text":"{\"affected_rows\":1}","type":"text"}],"isError":false}}
```

```sql theme={null}
SELECT id, title, views FROM mcp_guide.posts WHERE id = 1;
```

```
+----+----------------------------------+-------+
| id | title                            | views |
+----+----------------------------------+-------+
|  1 | Getting started with VillageSQL | 9999  |
+----+----------------------------------+-------+
```

`allow_write`와 계정의 권한은 두 개의 별개 관문이며, 실제 쓰기에는 둘 다 열려 있어야 합니다. `max_rows`가 읽기를 제한하는 것처럼 쓰기에 행 상한은 없습니다: 조건 없는 `DELETE`는 다른 어떤 설정과 관계없이 테이블을 비웁니다. 프로덕션 데이터베이스에 쓰기 접근 권한을 가진 에이전트는 넓은 쓰기 접근 권한을 가진 다른 어떤 자율 프로세스와 같은 위험을 지닙니다: 에이전트에게는 말이 되지만 여러분에게는 그렇지 않은 계획을, 사람의 검토 단계가 잡아내는 것보다 빠르게 실행할 수 있습니다. `db_url`에는 권한이 여러분이 받아들일 수 있는 피해 범위와 정확히 일치하는 계정을 주고, `allow_write`는 데모에 필요했다고 켜 두는 기본값이 아니라 데이터베이스별로 내리는 결정으로 취급하세요.

이 가이드는 계속하기 전에 `allow_write`를 다시 끄고 `UPDATE`를 회수합니다. 이것은 에이전트가 상시 접근 권한을 가진 데이터베이스에 더 안전한 기본 상태이기도 합니다.

```sql theme={null}
REVOKE UPDATE ON mcp_guide.* FROM 'mcp_user'@'127.0.0.1';
SET GLOBAL vsql_mcp.allow_write = OFF;
```

## 에이전트가 닿을 수 있는 범위 좁히기

두 가지 설정이 계정의 권한만으로 하는 것보다 에이전트의 범위를 더 좁히며, 하나의 계정이 `vsql_mcp` 외의 용도로도 쓰일 때 유용합니다.

`max_rows`는 단일 `query` 호출이 반환하는 행 수를 제한하고 결과에 `truncated` 표시를 합니다:

```sql theme={null}
SET GLOBAL vsql_mcp.max_rows = 2;
```

```json theme={null}
{"id":10,"jsonrpc":"2.0","result":{"content":[{"text":"{\"columns\":[\"id\",\"title\"],\"row_count\":2,\"rows\":[{\"id\":1,\"title\":\"Getting started with VillageSQL\"},{\"id\":2,\"title\":\"Custom types in VEF\"}],\"truncated\":true}","type":"text"}],"isError":false}}
```

`allowed_tables`는 문장이 건드릴 수 있는 테이블을 제한하며, `EXPLAIN FORMAT=JSON`으로 문장을 계획해서 검사합니다. 적용되는 것을 보려면 두 번째 테이블을 추가하세요:

```sql theme={null}
CREATE TABLE mcp_guide.secrets (id INT PRIMARY KEY AUTO_INCREMENT, api_key VARCHAR(200) NOT NULL);
INSERT INTO mcp_guide.secrets (api_key) VALUES ('sk-do-not-expose-this');
GRANT SELECT ON mcp_guide.* TO 'mcp_user'@'127.0.0.1';
```

같은 스키마에 `secrets`가 있고 `allowed_tables`가 `posts`만으로 설정되어 있으면, `posts`는 여전히 동작하고 `secrets`는 거부됩니다:

```sql theme={null}
SET GLOBAL vsql_mcp.allowed_tables = 'posts';
```

```json theme={null}
{"id":12,"jsonrpc":"2.0","result":{"content":[{"text":"table 'secrets' is not in vsql_mcp.allowed_tables","type":"text"}],"isError":true}}
```

`list_tables`도 제외된 테이블을 더 이상 나열하지 않으므로, `list_tables`만으로 작업하는 에이전트는 `secrets`가 존재한다는 것을 결코 알지 못합니다:

```json theme={null}
{"id":13,"jsonrpc":"2.0","result":{"content":[{"text":"{\"schema\":\"mcp_guide\",\"tables\":[{\"name\":\"posts\",\"row_estimate\":3,\"type\":\"BASE TABLE\"}]}","type":"text"}],"isError":false}}
```

허용 목록은 계정의 권한에서 읽는 것이 아니라 문장의 테이블에서 계획되므로, 저장 함수를 꿰뚫어 보지 못하고, 읽기 도구가 `db_url` 계정이 호출할 수 있는 어떤 함수든 호출하는 것을 막지 못합니다. `allowed_tables`와 `max_rows`는 권한 자체의 범위를 정하는 것의 대체물이 아니라, 계정의 권한 위에 겹쳐진 가드레일로 취급하세요.

## 자주 묻는 질문

#### 에이전트에게 데이터베이스 자격 증명에 대한 직접 접근이 필요한가요?

아니요. 에이전트는 `vsql_mcp.bearer_token`에 설정된 베어러 토큰으로 `vsql_mcp`에 인증합니다. `vsql_mcp.db_url`이 지정하는 데이터베이스 계정은 확장이 도구 쿼리를 실행하는 데 내부적으로만 사용됩니다. 에이전트는 그 계정의 비밀번호를 결코 보지 못합니다.

#### 스키마를 둘 이상 노출할 수 있나요?

예, 하지만 `vsql_mcp.schema`를 비워 두면 예상보다 많은 것이 노출됩니다. 그러면 `list_schemas`는 `mysql`, `sys`, `performance_schema`를 포함해 서버의 모든 스키마를 반환하며, `db_url` 계정이 닿을 수 있는 것으로 필터링하지 않습니다: 목록은 계정의 연결이 아니라 프로세스 안에서 `information_schema.SCHEMATA`를 대상으로 실행됩니다. 발견 범위를 제한하는 것은 `vsql_mcp.schema`를 하나의 스키마로 좁히는 것입니다. `query`와 `write`가 실제로 읽을 수 있는 것은 여전히 계정의 권한이 결정하므로, 나열된 스키마가 반드시 읽을 수 있는 스키마는 아닙니다.

#### vsql\_mcp는 TLS를 지원하나요?

예. `vsql_mcp.ssl_cert`와 `vsql_mcp.ssl_key`를 모두 PEM 파일 경로로 설정하면 `vsql_mcp.ssl_port`(기본값 `3143`)에서 HTTPS를 제공합니다. 둘 중 하나라도 비워 두면 포트 설정과 관계없이 TLS가 꺼집니다.

#### 포트를 바꿔도 왜 즉시 적용되지 않나요?

포트와 TLS 설정은 다음에 서버가 활성화될 때 적용됩니다. 바꾼 뒤에 `vsql_mcp.vsql_mcp_enabled`를 OFF로 했다가 ON으로 전환하세요.

## 문제 해결

| 문제 | 해결 방법 |
| :- | :- |
| `INSTALL EXTENSION vsql_mcp`가 프리뷰 기능 오류로 실패 | 서버를 `--vsql_allow_preview_extensions=ON`으로 시작하거나, `SET PERSIST vsql_allow_preview_extensions = ON`을 실행. 이것은 즉시 적용되며 재시작이 필요하지 않음. |
| `SHOW STATUS LIKE 'vsql_mcp.http_port'`가 `0`을 반환 | 서버가 수신 대기하지 않음. `vsql_mcp.vsql_mcp_enabled = ON`을 확인하고, 구성된 포트에 다른 것이 이미 바인딩되어 있지 않은지 확인. |
| 모든 요청이 `401 Unauthorized`를 반환 | `require_auth`가 ON이고 요청에 `Authorization: Bearer <token>`이 없거나, 토큰이 `vsql_mcp.bearer_token`과 일치하지 않음. |
| `tools/call`이 `missing Mcp-Session-Id header`와 함께 `400`을 반환 | `initialize` 이후의 모든 요청은 `initialize` 응답이 반환한 `Mcp-Session-Id` 값을 담아야 함. |
| `query`나 `write`가 MySQL 권한 오류로 실패 | `vsql_mcp.db_url`에 지정된 계정에 문장에 필요한 권한이 없음. `allowed_tables`만이 아니라 거기에 권한을 부여. |
| `tools/list`에 `write`가 없음 | `vsql_mcp.allow_write`가 OFF임. 도구를 활성화하려면 `ON`으로 설정. |
| 클라이언트가 전혀 연결하지 못함 | 클라이언트가 `127.0.0.1`과 구성된 포트에 도달하는지 확인. `vsql_mcp`는 다른 어떤 인터페이스에도 바인딩하지 않음. |

## 함께 보기

* [MySQL 사용자 관리](https://villagesql.com/docs/ko/guides/user-management.md) — `CREATE USER`, `GRANT`, 최소 권한 계정 설계
* [MySQL 보안 강화](https://villagesql.com/docs/ko/guides/security-hardening.md) — 단일 확장의 가드레일을 넘어선 더 넓은 서버 강화
* [MySQL을 AI API에 연결하기](https://villagesql.com/docs/ko/guides/ai-api-setup.md) — 반대 방향: `vsql_ai`로 MySQL 안에서 AI API 호출하기


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.