GPT MCP Server Design - Phase 1 Rev3
GPT MCP Server Design — Phase 1 Rev3
Date: 2026-05-13 Author: Opus (Claude) Status: PENDING GPT REVIEW Revision: Rev3 (sửa 8 điểm Rev1 + 6 điểm Rev2)
1. Tổng quan
Xây GPT-specific FastMCP server trên VPS để ChatGPT kết nối qua Developer Mode custom connector. Thay thế GPT Actions/OpenAPI direct đã chứng minh không ổn định (ClientResponseError, request không tới VPS).
Quyết định đã thống nhất:
- Build trên VPS, container riêng
- FastMCP / Python 3.11+
- KHÔNG đụng Claude MCP hiện tại
- KHÔNG dùng GPT Actions cũ
- ChatGPT kết nối qua Developer Mode → No Auth → secret path URL
- Full CRUD code, write bật/tắt qua env var
- Phase 1: validation + log + soft-delete approach
- Không expose shell / raw SQL / hard-delete / arbitrary filesystem
2. Kiến trúc
ChatGPT (Developer Mode, No Auth)
↓ HTTPS
https://vps.incomexsaigoncorp.vn/gpt-mcp/<SECRET>/mcp
↓ nginx reverse proxy (strip secret path)
http://127.0.0.1:8100/mcp/
↓ FastMCP Streamable HTTP
GPT MCP Server container (gpt-mcp)
↓ REST HTTP (internal Docker network)
http://agent-data:8888/api/...
↓
AgentData container (existing, unchanged)
Chốt: GPT MCP server gọi AgentData bằng REST HTTP nội bộ. Không dùng MCP client nội bộ. Lý do: đơn giản, debug bằng curl được, không MCP-over-MCP.
3. Auth — Secret path (temporary Phase 1)
- ChatGPT UI chọn No Auth
- Secret nhúng trong URL path segment
- Nginx filter: chỉ match đúng secret path → proxy tới FastMCP
- URL không match → 404 tự nhiên
- Đây là temporary capability URL cho Phase 1 nội bộ, KHÔNG phải production auth
- Roadmap Phase 3/4: đánh giá chuyển OAuth 2.1 / Mixed Auth
Secret management
- Secret lấy từ
.envfile trên VPS (không commit) - Nginx config render từ template bởi
deploy.sh - Rendered config file: permission 600, owner root:root
- Không commit rendered config
- Không in secret thật ra report/chat
- Rotate: đổi env → chạy deploy.sh → cập nhật ChatGPT connector URL
4. Tool set
Read tools (luôn bật)
| Tool | AgentData REST | Method | Params |
|---|---|---|---|
| healthCheck() | /api/health | GET | — |
| searchKnowledge(query) | /api/chat | POST | {"query": str} |
| listDocuments(path) | /api/kb/list?path=... | GET | path: str = "knowledge/" |
| getDocument(document_id) | /api/documents/{path} | GET | document_id: str |
| getDocumentFull(path) | /api/documents/{path}?full=true | GET | path: str |
| batchRead(paths, full) | /api/documents/batch | POST | {"paths": list, "full": bool} |
Write tools (chỉ register khi ENABLE_WRITE=true)
| Tool | AgentData REST | Method | Params |
|---|---|---|---|
| createDocument(path, content, title?, tags?) | /api/documents | POST | {"path": str, "content": str, "title": str|None, "tags": list|None} |
| updateDocument(path, content, title?, tags?) | /api/documents/{path} | PUT | {"content": str, "title": str|None, "tags": list|None} |
| patchDocument(path, old_str, new_str) | /api/documents/{path} | PATCH | {"old_str": str, "new_str": str} |
Không expose Phase 1
- deleteDocument — chưa verify soft/hard
- shell, raw SQL, filesystem, qdrant/postgres admin
Conditional registration
if os.environ.get("ENABLE_WRITE", "false").lower() == "true":
# register write tools
Khi ENABLE_WRITE=false: write tools KHÔNG xuất hiện trong tools/list.
5. Mapping evidence
Source: AgentData MCP tool definitions verified qua tool_search trong Claude chat 2026-05-13.
search_knowledge
- Input:
{"query": "string" (required), "session_id": "string" (optional)} - Test:
search_knowledge("test connection health check") - Output: text response + "Sources:" với document paths
- Không có top_k — GPT prompt gốc sai, đã sửa
list_documents
- Input:
{"path": "string" (optional)} - Test:
list_documents("knowledge/current-state/reports/") - Output: "Documents in '...' (50 items):" + list "- path [tags]"
- Không có limit/offset — GPT prompt gốc sai, đã sửa
get_document
- Input:
{"document_id": "string" (required)} - Test:
get_document("knowledge/current-state/reports/agentdata-gpt-action-401-auth-fix-2026-05-13.md") - Output: truncated content (500 chars) + "[Truncated: showing 500/2631 chars]"
upload_document (= createDocument)
- Input:
{"content": "string", "path": "string", "tags": ["string"] optional, "title": "string" optional} - Mapping: POST /api/documents
update_document
- Input:
{"content": "string", "path": "string", "tags": ["string"] optional, "title": "string" optional} - Mapping: PUT /api/documents/{path}
patch_document
- Input:
{"path": "string", "old_str": "string", "new_str": "string"} - Output: 404 if not found, 409 if old_str ambiguous
- Mapping: PATCH /api/documents/{path}
batch_read
- Input:
{"paths": ["string"] (max 20), "full": boolean (default false)} - Mapping: POST /api/documents/batch
Lưu ý: REST endpoint/method/body mapping dựa trên tool definitions + GPT prompt gốc. Phase 2 bước đầu sẽ curl test từng endpoint nội bộ để xác nhận 100%.
6. Nginx config
Template (nginx-gpt-mcp.conf.template)
# GPT MCP Server route — DO NOT commit rendered version
# Log format: NO path/query logged (secret protection)
log_format gpt_mcp_log '$remote_addr - [$time_local] "GPT_MCP $request_method" '
'$status $body_bytes_sent "$http_user_agent" '
'$request_id $upstream_response_time';
location /gpt-mcp/__GPT_MCP_SECRET__/mcp {
proxy_pass http://127.0.0.1:8100/mcp;
proxy_http_version 1.1;
proxy_buffering off;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Request-ID $request_id;
proxy_set_header Connection '';
access_log /var/log/nginx/gpt-mcp-access.log gpt_mcp_log;
}
Route matching
- Prefix location (không có
=): match/mcp,/mcp/,/mcp/anything - proxy_pass có URI component
/mcp: nginx thay thế location prefix - Phase 2 sẽ test cả
/mcpvà/mcp/với curl, chốt kết quả thực tế
Deploy script (deploy.sh)
#!/bin/bash
set -euo pipefail
source /opt/incomex/gpt-mcp/.env
sed "s|__GPT_MCP_SECRET__|${GPT_MCP_SECRET}|g" \
/opt/incomex/gpt-mcp/nginx-gpt-mcp.conf.template \
> /etc/nginx/conf.d/gpt-mcp.conf
chmod 600 /etc/nginx/conf.d/gpt-mcp.conf
chown root:root /etc/nginx/conf.d/gpt-mcp.conf
nginx -t && nginx -s reload
echo "GPT MCP nginx route deployed (secret redacted)"
7. Docker
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY server.py agentdata_client.py validation.py ./
CMD ["python", "server.py"]
# requirements.txt
fastmcp>=2.0
httpx>=0.27
python-dotenv>=1.0
Port: 8100 internal. Không expose external — chỉ nginx proxy.
8. Env vars
# .env.example (commit được)
GPT_MCP_SECRET=change_me_64_random_chars
AGENTDATA_BASE_URL=http://agent-data:8888
ENABLE_WRITE=false
LOG_LEVEL=INFO
REQUEST_TIMEOUT_SECONDS=30
9. Validation
pathphải bắt đầu bằngknowledge/— no path traversal (..)contentmax 100KBtagsmax 20 items, mỗi tag max 100 chars (type:list[str] | None = None)pathstrong batchRead max 20 itemsquerymax 500 chars- All write tool params use
| None = Nonefor optional fields
10. Logging (app level)
Mỗi tool call log:
- timestamp, request_id, tool_name, status, duration_ms
- upstream_status, upstream_duration_ms
- auth_present (true/false), payload_size, response_size
- Không log: secret/token, full document content nếu dài
11. Error layers (3 cho Phase 1)
auth_error— nginx 404 (secret path không match)validation_error— input không hợp lệ (trả 400 trong MCP response)upstream_error— AgentData timeout/error (trả error trong MCP response kèm upstream status)
12. Files
/opt/incomex/gpt-mcp/
├── server.py # FastMCP server + tool definitions
├── agentdata_client.py # httpx REST client for AgentData
├── validation.py # Input validation
├── Dockerfile
├── requirements.txt
├── .env.example # template (commit OK)
├── .env # secret thật (VPS only, NO commit)
├── nginx-gpt-mcp.conf.template # nginx template (commit OK)
├── deploy.sh # render nginx config + reload
└── test_mcp_client.py # MCP client test script
13. Test plan (Phase 2)
Step 1: Verify AgentData REST endpoints nội bộ
docker exec gpt-mcp curl -s http://agent-data:8888/api/health
docker exec gpt-mcp curl -s http://agent-data:8888/api/kb/list?path=knowledge/
Nếu endpoint khác → sửa mapping, báo cáo.
Step 2: Test nginx route
curl -v https://vps.incomexsaigoncorp.vn/gpt-mcp/<redacted>/mcp \
-X POST -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{...},"id":1}'
Test cả /mcp và /mcp/.
Step 3: MCP client test
# test_mcp_client.py — version check first:
# python --version && pip show fastmcp
import asyncio
from fastmcp import Client
from fastmcp.client.transports import StreamableHTTPTransport
async def test():
url = "https://vps.../gpt-mcp/<redacted>/mcp"
transport = StreamableHTTPTransport(url=url)
async with Client(transport=transport) as client:
tools = await client.list_tools()
print(f"Tools: {[t.name for t in tools]}")
result = await client.call_tool("healthCheck", {})
print(f"Health: {result}")
asyncio.run(test())
Fallback: curl với MCP initialize + tools/list nếu FastMCP Client không hỗ trợ remote.
Step 4: Test từng tool (read)
healthCheck → listDocuments → searchKnowledge → getDocument → batchRead
Step 5: Test write (chỉ sau khi read ổn VÀ được duyệt bật ENABLE_WRITE)
createDocument vào knowledge/test/gpt-mcp-write-test.md
14. Rollback
docker stop gpt-mcp && docker rm gpt-mcp- Comment nginx location block →
nginx -s reload - Claude MCP: KHÔNG BỊ ẢNH HƯỞNG
15. Auth roadmap
- Phase 1: No Auth + capability URL secret (temporary)
- Phase 3/4 nếu ổn định: đánh giá OAuth 2.1 / Mixed Auth theo OpenAI Apps SDK