2025-06-12 把「Mac 作為中介(MCP server)、同時連 ChatGPT(OpenAI)與 DeepSeek」的角色

2025-06-12 把「Mac 作為中介(MCP server)、同時連 ChatGPT(OpenAI)與 DeepSeek」的角色

好 — 我來把「Mac 作為中介(MCP server)、同時連 ChatGPT(OpenAI)與 DeepSeek」的角色、資料流、最小可運作範例與測試步驟,一次清楚且分步說明給你。你要一步步做就行,我把每步拆得很小、可驗收。

先說重點(短)

  • Mac(MCP server)=中介 / 多供應商 Gateway:前端或其他機器把使用者請求丟給 Mac(MCP),MCP 決定要呼叫哪一家(OpenAI 或 DeepSeek),呼叫後把結果回給前端,並可同步存到 DB(放在 Windows 或雲端)。
  • 你的 Mac 並不做大量推理;它只做路由、adapter、轉格式、審查、logging 與 failover。這樣可把重資源工作放在雲端供應商,舊 Mac 也能跑得順。

高階資料流(簡圖)

Frontend ←→ Mac(MCP gateway) ←→ { OpenAI (ChatGPT API), DeepSeek API }

DB(在 Windows / 雲端)

最小可運作步驟(3 個小步驟,先做第一個)

我把整個工作分成三小步,你先只做第 1。

Step 1(先做) — 在 Mac 建最簡 Gateway 並測試能呼叫兩個 provider(用 mock 或實 API)

目的:讓 Mac 能接受前端請求並呼叫不同 provider(可先 mock DeepSeek / OpenAI 回應做測試)

  1. 在 Mac 建資料夾並安裝 node(若已安裝可跳過)
1
2
3
4
5
mkdir ~/mcp-gateway
cd ~/mcp-gateway
npm init -y
npm i express dotenv node-fetch
# node-fetch for fetch in Node <18 ; 若 Node18+ 可直接用全域 fetch
  1. 新增 .env(請放在 Mac,不要放前端)
1
2
3
OPENAI_API_KEY=sk-xxxx
DEEPSEEK_API_KEY=ds-xxxx
PORT=4000
  1. gateway.mjs(最小版,多 provider adapter + failover) — 先貼這份到 Mac:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// gateway.mjs (最小可運作範例)
import express from 'express';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
app.use(express.json());

const OPENAI_KEY = process.env.OPENAI_API_KEY || '';
const DEEPSEEK_KEY = process.env.DEEPSEEK_API_KEY || '';

// provider adapters
const providers = {
openai: async ({ messages, opts }) => {
// real OpenAI call (示意) - 如果沒 key, 回 mock
if (!OPENAI_KEY) return { text: '[mock openai] 回傳範例' };
const resp = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${OPENAI_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: opts?.model || 'gpt-3.5-turbo',
messages
})
});
const data = await resp.json();
return { text: data?.choices?.[0]?.message?.content ?? '', raw: data };
},

deepseek: async ({ messages, opts }) => {
// real DeepSeek call (示意) - 若沒 key,回 mock
if (!DEEPSEEK_KEY) return { text: '[mock deepseek] 回傳範例' };
const resp = await fetch('https://api.deepseek.com/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${DEEPSEEK_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: opts?.model || 'deepseek-v1',
messages
})
});
const data = await resp.json();
return { text: data?.choices?.[0]?.message?.content ?? '', raw: data };
}
};

// health
app.get('/health', (req, res) => res.json({ status: 'ok', role: 'mcp-gateway' }));

// main endpoint
app.post('/api/chat', async (req, res) => {
const { provider = 'openai', messages = [{ role:'user', content: 'hello' }], opts } = req.body;
const adapter = providers[provider];
if (!adapter) return res.status(400).json({ error: 'unknown provider' });

try {
const { text, raw } = await adapter({ messages, opts });
// 這裡可加入:存 DB / 審查 / 計費
return res.json({ provider, text, raw });
} catch (err) {
console.error('provider error:', err);
// 簡單 failover:若 openai 失敗則嘗試 deepseek(或反之)
const fallback = provider === 'openai' ? providers['deepseek'] : providers['openai'];
try {
const fallbackResp = await fallback({ messages, opts });
return res.json({ provider: provider + '_fallback', text: fallbackResp.text, raw: fallbackResp.raw });
} catch (err2) {
console.error('fallback failed:', err2);
return res.status(500).json({ error: 'all providers failed' });
}
}
});

const port = process.env.PORT || 4000;
app.listen(port, () => console.log(`MCP gateway listening on ${port}`));
  1. 啟動並測試(本地)
1
2
3
4
5
node gateway.mjs
# 在同一台 MAC 測試
curl -X POST http://localhost:4000/api/chat -H "Content-Type: application/json" -d '{"provider":"openai","messages":[{"role":"user","content":"哈囉"}] }'
# 或 test deepseek
curl -X POST http://localhost:4000/api/chat -H "Content-Type: application/json" -d '{"provider":"deepseek","messages":[{"role":"user","content":"哈囉"}] }'
  • 如果你沒放 API keys,會看到 mock 回覆([mock ...]),這對測試路由很方便。

驗收條件(Step 1 完成)

  • 在 Mac 上能啟動 gateway.mjs 並成功回應 /health
  • POST /api/chat 能對 provider=openaiprovider=deepseek 回應(mock 或真實回覆皆可)。
  • 若你在 Windows(或前端機)用 curl http://<mac-ip>:4000/health 能拿到回應(檢查 LAN 能通)。

Step 2(完成 Step1 再做) — 把 DB 放到 Windows,並讓 Mac 把每次對話儲存到 DB

(我會給你 MySQL SQL 與如何在 Mac 用 mysql2 存入的最小範例。這一步我會再拆成小動作給你,等你說 Step1 OK 我再貼 Step2)

Step 3(之後) — 加入 moderation / stream / UI 與 provider 選單(再拆細)


實務配置建議(快速清單)

  • API keys 放在 Mac 的 .env(不要放前端)。
  • Mac 設為固定 LAN IP(router DHCP 保留),方便 Windows 或手機前端呼叫。
  • Mac 用 pm2 常駐 gateway:pm2 start gateway.mjs --name mcp-gateway
  • 日誌:在 gateway 實作簡單 request logger(winston 或檔案),以利除錯與計費。
  • 安全:若要在 Internet 上測試,先用 ngrok 做測試隧道再做正式 port-forward;正式上線再放 WAF / auth。
  • Failover 策略:優先 provider 與 fallback provider 明確寫在 config(例如 default=openai, fallback=deepseek)。

小提醒(你可能會問)

  • 「Mac 是中介」表示它不需要跑大型模型;請把 heavy compute 留給雲端(OpenAI/DeepSeek)。
  • 若 DeepSeek API 路徑或參數變了,請以該服務官方文件為準;我可以幫你把 adapter 調整成最新格式(你貼官方 doc 或告訴我有哪個錯誤即可)。

想先做哪一步?

  1. 你說「我現在要先做 Step1(在 Mac 上啟動 gateway.mjs 並測試)」,我就給你要執行的確切命令與排錯方式。
  2. 或直接把你現有的 gateway 程式碼貼上,我直接幫你修改成同時支援 OpenAI + DeepSeek 的 adapter(可直接貼上執行)。

你選一個,我立刻把下一個可直接貼上執行的內容給你。