
C# 與 Windows API 交互的“秘密武器”:結(jié)構(gòu)體和聯(lián)合體
我舉一個(gè)非常簡(jiǎn)單的例子,假設(shè)我有一個(gè)電商后臺(tái)系統(tǒng),除了有一個(gè)前端的控制臺(tái)之外,還有有一套現(xiàn)成的統(tǒng)一的后端 API,那如果現(xiàn)在我想將我的后臺(tái)系統(tǒng)接入大模型,通過自然語言來進(jìn)行一些操作,而不是手動(dòng)在控制臺(tái)上點(diǎn)來點(diǎn)去,我們只需要簡(jiǎn)單的寫一個(gè) MCP 服務(wù),大概的樣子:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
// 定義 MCP 服務(wù)
server.tool('create-user', '用于創(chuàng)建用戶', (params) => {
// 調(diào)用后端創(chuàng)建用戶的 API
return axios.post('/api/users', params);
});
server.tool('create-order', '用于創(chuàng)建訂單', (params) => {
// 調(diào)用后端創(chuàng)建訂單的 API
return axios.post('/api/orders', params);
});
server.tool('get-order-status', '用于查詢訂單狀態(tài)', (orderId) => {
// 調(diào)用后端查詢訂單狀態(tài)的 API
return axios.get(/api/orders/${orderId}/status
);
});
server.tool('update-inventory', '用于更新庫存', (productId, quantity) => {
// 調(diào)用后端更新庫存的 API
return axios.put(/api/inventory/${productId}
, { quantity });
});
實(shí)現(xiàn)了這么一個(gè)服務(wù)后,我們可以將這個(gè)服務(wù)通過 cursor,cline,claude app 等 AI 客戶端進(jìn)行接入,直接通過自然語言描述自己想要進(jìn)行的操作,大模型就會(huì)解析你的語意以及 MCP 服務(wù)中我們自己寫的描述,去組合調(diào)用不同的 API,例如用戶輸入?請(qǐng)為用戶 "john_doe" 創(chuàng)建一個(gè)新訂單,商品是 2 件商品 ID 為 "123" 的商品,配送地址是 "上海市浦東新區(qū)"。?大模型解析用戶的自然語言輸入,提取出以下信息:
用戶ID:
john_doe
商品:
[{ productId: "123", quantity: 2 }]
配送地址: 上海市浦東新區(qū) 大模型調(diào)用
create-order
工具,傳入解析后的參數(shù):
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
{
"userId": "john_doe",
"products": [
{ "productId": "123", "quantity": 2 }
],
"shippingAddress": "上海市浦東新區(qū)"
}
MCP 服務(wù)將請(qǐng)求轉(zhuǎn)發(fā)給后端 API,完成訂單創(chuàng)建,并返回結(jié)果給用戶:
ounter(lineounter(lineounter(line
訂單創(chuàng)建成功,訂單ID為 "ORDER_123456"。
目前已有許多成熟的 MCP 服務(wù),例如 GitHub 的 MCP 支持創(chuàng)建倉庫、提交 PR 等操作;Figma 的 MCP 可以直接生成 UI 設(shè)計(jì)圖;瀏覽器和操作系統(tǒng)的 MCP 則能增強(qiáng) cursor 的能力,甚至可以讓 cursor 基于瀏覽器中的錯(cuò)誤信息自動(dòng)調(diào)試代碼。這些服務(wù)極大地?cái)U(kuò)展了 LLM 的應(yīng)用場(chǎng)景,使其能夠更智能地完成復(fù)雜任務(wù)。下圖是 Cline 中可以直接快速集成的一些 MCP 服務(wù):
具體如何實(shí)現(xiàn)一個(gè) MCP 服務(wù)上面簡(jiǎn)單提了一下 MCP 服務(wù)的大致實(shí)現(xiàn)原理,接下來我們根據(jù)官方文檔[1]中介紹,從零開始實(shí)現(xiàn)一個(gè) MCP 服務(wù)。
首先我們需要有一套現(xiàn)有的后端服務(wù) API,可以是我們?cè)?jīng)開發(fā)過的小項(xiàng)目,或者是一些開源的帶有開放 API 的庫,這里我選擇了開源的 APISIX[2] 網(wǎng)關(guān),因?yàn)樗幸惶赚F(xiàn)成的 Admin API[3],并且還有 API 文檔,這可以提供給 cursor 作為參考幫助我們快速將所有 API 轉(zhuǎn)換成 MCP 服務(wù)中的操作。
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
# 創(chuàng)建一個(gè)新的項(xiàng)目目錄
mkdir apisix-mcp
cd apisix-mcp
# 初始化一個(gè)新的 npm 項(xiàng)目
npm init -y
# 安裝依賴
npm install @modelcontextprotocol/sdk zod
npm install -D @types/node typescript
# 創(chuàng)建項(xiàng)目文件
mkdir src
touch src/index.ts
更新
package.json
文件,添加
type: "module"
和一個(gè)構(gòu)建腳本:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
{
"type": "module",
"bin": {
"apisix-mcp": "./build/index.js"
},
"scripts": {
"build": "tsc && chmod 755 build/index.js"
},
"files": [
"build"
]
}
在項(xiàng)目根目錄下創(chuàng)建一個(gè)
tsconfig.json
文件:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
在
src/index.ts
文件的頂部添加以下內(nèi)容:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Configuration for APISIX MCP server
const APISIX_API_BASE = "http://127.0.0.1:9180/apisix/admin";
const APISIX_API_KEY = "edd1c9f034335f136f87ad84b625c8f1";
// Create server instance
const server = new McpServer({
name: "apisix-mcp",
version: "1.0.0",
config: {
apiKey: {
type: "string",
description: "API key for APISIX Admin API authentication",
default: APISIX_API_KEY
},
apiBase: {
type: "string",
description: "Base URL for APISIX Admin API",
default: APISIX_API_BASE
}
}
});
這段代碼用于創(chuàng)建一個(gè) APISIX MCP 服務(wù)實(shí)例:「導(dǎo)入依賴」:
McpServer
和
StdioServerTransport
是用于創(chuàng)建 MCP 服務(wù)的工具。
z
是 Zod 庫,用于數(shù)據(jù)驗(yàn)證。「配置 APISIX MCP 服務(wù)」:
APISIX_API_BASE
是 APISIX Admin API 的基礎(chǔ) URL。
APISIX_API_KEY
是用于身份驗(yàn)證的 API 密鑰。「創(chuàng)建服務(wù)實(shí)例」:使用
McpServer
創(chuàng)建一個(gè)名為
apisix-mcp
的服務(wù)實(shí)例,版本為
1.0.0
。配置了
apiKey
和
apiBase
兩個(gè)參數(shù),分別用于指定 API 密鑰和基礎(chǔ) URL,并提供了默認(rèn)值。這段代碼用于初始化一個(gè) MCP 服務(wù),為后續(xù)與 APISIX Admin API 的交互做準(zhǔn)備,大家可以根據(jù)自己的業(yè)務(wù)進(jìn)行簡(jiǎn)單的調(diào)整。
然后我們添加一個(gè)單獨(dú)的文件用于發(fā)送請(qǐng)求,官方的文檔中使用的是
fetch
,但我覺得用
axios
在執(zhí)行不同的請(qǐng)求方法時(shí)會(huì)更加方便一點(diǎn),所以就改用
axios
發(fā)送請(qǐng)求 :
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
/**
* Define the common data structure returned by APISIX Admin API
*/
import axios from 'axios';
interface ApiResponse {
value?: any;
modifiedIndex?: number;
createdIndex?: number;
error?: string;
message?: string;
}
/**
* 使用axios發(fā)送請(qǐng)求到APISIX管理API
* @param url 請(qǐng)求的完整URL
* @param method HTTP方法,默認(rèn)為'GET'
* @param data 請(qǐng)求數(shù)據(jù),用于PUT/POST/PATCH請(qǐng)求
* @param apiKey API密鑰,用于認(rèn)證,默認(rèn)使用環(huán)境變量或硬編碼值
* @returns 返回API響應(yīng)或null(如果發(fā)生錯(cuò)誤)
*/
export async function makeAdminAPIRequest(
url: string,
method: string = 'GET',
data?: any,
apiKey: string = process.env.APISIX_API_KEY || "edd1c9f034335f136f87ad84b625c8f1"
): Promise {
try {
// 配置axios請(qǐng)求
const response = await axios({
method,
url,
data,
headers: {
'X-API-KEY': apiKey,
'Content-Type': 'application/json'
}
});
console.log(Axios請(qǐng)求成功: ${method} ${url}
);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
console.error(請(qǐng)求失敗: ${method} ${url}
);
console.error(狀態(tài)碼: ${error.response?.status}, 錯(cuò)誤信息: ${error.message}
);
console.error(響應(yīng)數(shù)據(jù):
, error.response?.data);
} else {
console.error('發(fā)送API請(qǐng)求時(shí)出錯(cuò):', error);
}
return null;
}
}
export default makeAdminAPIRequest;
創(chuàng)建好了服務(wù)實(shí)例,并且實(shí)現(xiàn)了一個(gè)請(qǐng)求方法后,接下來到最核心的一步,也就是實(shí)現(xiàn)執(zhí)行工具:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
const idSchema = z.object({
id: z.string().describe("資源 id")
});
server.tool('get-routes',
'列出所有路由',
{},
async (args, extra) => {
const response = await makeAdminAPIRequest(${APISIX_API_BASE}/routes
);
return {
content: [
{
type: "text",
text: JSON.stringify(response, null, 2),
},
],
};
}
);
server.tool('create-route',
'創(chuàng)建一個(gè)新的路由',
routeSchema.shape,
async (args, extra) => {
try {
const routeId = args.id || Date.now().toString();
console.log(Starting route creation, ID: ${routeId}, path: ${args.route.uri}
);
const response = await makeAdminAPIRequest(
${APISIX_API_BASE}/routes/${routeId}
,
'PUT',
args.route
);
if (!response) {
console.error(Failed to create route, ID: ${routeId}
);
return {
content: [
{
type: "text",
text: JSON.stringify({ error: "Failed to create route, please check the APISIX connection and logs" }, null, 2),
},
],
};
}
console.log(Route created successfully, ID: ${routeId}
);
return {
content: [
{
type: "text",
text: JSON.stringify(response, null, 2),
},
],
};
} catch (error: unknown) {
console.error(Exception occurred:
, error);
const err = error instanceof Error ? error : new Error(String(error));
return {
content: [
{
type: "text",
text: JSON.stringify({ error: Failed to create route: ${err.message}
}, null, 2),
},
],
};
}
}
);
用
server.tool
函數(shù)定義了兩個(gè)個(gè)工具,分別用于獲取路由數(shù)據(jù)和新增路由,我們?cè)诘诙€(gè)參數(shù)中用中文描述了我們這個(gè)工具的作用,這個(gè)描述就是用來給大模型調(diào)用時(shí)進(jìn)行語意分析后進(jìn)行觸發(fā)的,所以描述我們可以寫的更加詳盡一些,讓沒那么聰明的模型也號(hào)理解一點(diǎn),這里只是一個(gè)簡(jiǎn)單示例,兩個(gè)工具都通過
makeAdminAPIRequest
函數(shù)與 APISIX Admin API 交互。
接下來我們執(zhí)行
npm build
將項(xiàng)目構(gòu)建一下,可以看到
build
目錄也就是構(gòu)建成功了
在我們的 MCP 服務(wù)基礎(chǔ)框架完成后,就到了調(diào)試環(huán)節(jié)。 MCP 服務(wù)的接入需要用到一些 AI 客戶端,例如 cursor,vscode 中的 cline 插件,或者是 Claude 的官方應(yīng)用,考慮到我們開發(fā)用的就是 curosr,所以這里我們直接使用 cursor 作為客戶端進(jìn)行調(diào)試。
第一步我們需要在
.cursor
目錄中創(chuàng)建
mcp.json
文件用于聲明 MCP 服務(wù),在我的 windows 電腦上路徑如下圖:
創(chuàng)建完成后,我們?cè)谖募袑懭肱渲茫?/p>
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
{
"mcpServers": {
"apisix-mcp": {
"command": "node",
"args": [
"E:\projects\apisix-mcp\build\index.js"
]
}
}
}
mcpServers
字段代表我們聲明的 MCP 服務(wù),值的類型是對(duì)象,其中鍵名為服務(wù)名稱,這里我們叫
apisix-mcp
,可以根據(jù)自己的業(yè)務(wù)修改,
apisix-mcp
的值也是一個(gè)對(duì)象,其中包含了
command
和
args
兩個(gè)字段。
command
代表啟動(dòng) MCP 服務(wù)要執(zhí)行的命令,因?yàn)槲覀兊姆?wù)是用 node.js 寫的,那么這里我們的
command
就是
node
args
是要傳入的參數(shù),也就是拼接在
command
后面的內(nèi)容,這里我們寫的是上一步構(gòu)建出來的產(chǎn)物的路徑
當(dāng)配置完成后,我們打開 cursor 設(shè)置中 MCP 菜單項(xiàng),如果沒有看到這個(gè)菜單需要升級(jí)一下 cursor 的版本,然后我們會(huì)看到一個(gè) apisix-mcp 的服務(wù),而且還亮著綠燈,說明已經(jīng)連接成功了,如果沒有連接成功我們可以點(diǎn)一下刷新按鈕。MCP 服務(wù)中還會(huì)展示我們的代碼里定義了哪些工具和資源,(這里截圖中的工具比較多是因?yàn)槲液罄m(xù)實(shí)現(xiàn)了比較多個(gè)工具了,正常應(yīng)該只展示我們實(shí)現(xiàn)的那兩個(gè)工具):
要注意的是 cursor 在執(zhí)行腳本時(shí)會(huì)彈出一個(gè)黑色的終端,代表我們 MCP 服務(wù)的進(jìn)程是執(zhí)行在這個(gè)終端的,在后臺(tái)掛著就好了,不要關(guān)掉它。
接下來,我們?cè)?cursor 的輸入框中,選擇
agent
模式,然后選擇一個(gè)比較聰明的模型,例如
claude 3.5/3.7
或者
gpt4oo3-mini
,不要選擇
cursor-small
或
gpt-4o-mini
這種,因?yàn)槲易约簩?shí)測(cè)這兩個(gè)模型似乎不具備
agent
能力,可能是上下文太小了,所以不會(huì)調(diào)用 MCP 服務(wù)。這里我們選擇
claude-3.7-sonnet
,然后輸入如下圖的操作:
然后我們會(huì)看到 cursor 執(zhí)行了
called MCP tool
的操作,一共執(zhí)行了四次,對(duì)應(yīng)調(diào)用的
tool
我們都能直接看到:
雖然大模型說創(chuàng)建成功了,但是我們還是自己檢查一下,看看是不是真的成功了,這里我在終端輸入:
ounter(lineounter(lineounter(line
curl http://127.0.0.1:9180/apisix/admin/routes -H "X-API-KEY: $admin_key" | jq
可以看到控制臺(tái)打印出了我們創(chuàng)建的路由,并且插件也按照我們的要求配置了,說明真的創(chuàng)建成功了。
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
{
"key": "/apisix/routes/1742721614502",
"modifiedIndex": 98,
"createdIndex": 98,
"value": {
"id": "1742721614502",
"uri": "/api/httpbin/*",
"upstream": {
"pass_host": "pass",
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin",
"scheme": "http",
"hash_on": "vars"
},
"status": 1,
"create_time": 1742721614,
"update_time": 1742721614,
"priority": 0,
"plugins": {
"limit-req": {
"key_type": "var",
"allow_degradation": false,
"rate": 2,
"nodelay": false,
"key": "remote_addr",
"burst": 4,
"rejected_code": 429,
"policy": "local"
},
"cors": {
"allow_headers": "*",
"allow_origins": "*",
"max_age": 3600,
"expose_headers": "Content-Length,Content-Type",
"allow_methods": "GET,POST,PUT,DELETE,PATCH,OPTIONS",
"allow_credential": false
},
"limit-count": {
"show_limit_quota_header": true,
"count": 5,
"key_type": "var",
"time_window": 60,
"key": "remote_addr",
"allow_degradation": false,
"rejected_code": 429,
"policy": "local"
}
}
}
}
至此為止,我們花了不用兩個(gè)小時(shí)就能把一個(gè)現(xiàn)有服務(wù)接入大模型,通過自然語言進(jìn)行交互。
后續(xù)我們要做的就是將所有的 API 轉(zhuǎn)換為 MCP 中的工具,讓大模型按需調(diào)用即可,這一步也很簡(jiǎn)單,直接把我們的 OpenAPI 文檔提供給 cursor 就能批量生成了,我用一輪對(duì)話就全部生成好了,也就是上面截圖中展示的所有 MCP 工具:
而且這里我非常推薦大家直接是用 curosr 去調(diào)試 MCP 服務(wù),因?yàn)樵谖覀儗?shí)現(xiàn) MCP 服務(wù)的項(xiàng)目中,如果發(fā)現(xiàn) MCP 服務(wù)沒法正常調(diào)用,我們可以直接讓 cursor 自己調(diào)試,下面貼一個(gè)我調(diào)試過程中 cursor 自己的執(zhí)行記錄:
cursor 會(huì)一邊調(diào)用 MCP 服務(wù),根據(jù)服務(wù)的響應(yīng)結(jié)果去調(diào)整我們的源碼,然后自己重新構(gòu)建新的產(chǎn)物再重新嘗試調(diào)用 MCP 服務(wù),直到服務(wù)正常工作,從開發(fā)到測(cè)試的流程自己閉環(huán),真的很強(qiáng)!
MCP 中的概念不僅僅有
tool
這一種,但是目前我實(shí)現(xiàn)的功能只用到了這個(gè)工具,后續(xù)我將繼續(xù)完善這個(gè) MCP 服務(wù)的項(xiàng)目,等其他功能都摸清楚了再給大家完整的介紹 MCP 中的各種功能的實(shí)現(xiàn)。
我花了幾個(gè)小時(shí)就把這個(gè)服務(wù)構(gòu)建出來,而且最終的效果讓我感覺蠻驚訝的,用自然語言去和系統(tǒng)交互的感覺很奇妙,如果再開發(fā)一個(gè) MCP 的客戶端,那都不需要寫一個(gè)功能健全的控制臺(tái)了,可玩性很強(qiáng),上手又很簡(jiǎn)單,非常推薦大家去嘗試寫一下。
目前唯一的問題就是依賴能力比較強(qiáng)的模型,因此調(diào)用成本會(huì)比較高,但是未來隨著大模型的能力越來越強(qiáng),調(diào)用成本越來越低,后續(xù) MCP 服務(wù)可能替代現(xiàn)有傳統(tǒng)前端控制臺(tái)成為主流。
如果文章對(duì)你有幫助,歡迎點(diǎn)個(gè)贊??,respect~點(diǎn)擊關(guān)注公眾號(hào),“技術(shù)干貨” 及時(shí)達(dá)!
原文轉(zhuǎn)載自:https://mp.weixin.qq.com/s/YaQuGQeWhwBWU-aMTle6-A
C# 與 Windows API 交互的“秘密武器”:結(jié)構(gòu)體和聯(lián)合體
免費(fèi)強(qiáng)大的API開發(fā)和調(diào)試工具——Reqable
SpringBoot中6種API版本控制策略
更智能的Kubernetes AI推理路由:Gateway API推理擴(kuò)展
超越 API:MCP 如何成為 AI 時(shí)代的“萬能適配器”?
從零開始的機(jī)器學(xué)習(xí)實(shí)踐指南
2025年最佳語音轉(zhuǎn)文字API比較:一個(gè)報(bào)表31項(xiàng)指標(biāo)近200條數(shù)據(jù)
使用DeepSeek和Claude繪制出高質(zhì)量的SVG 圖片
18種最佳 RAG 技術(shù)
對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力
一鍵對(duì)比試用API 限時(shí)免費(fèi)