使用说明
MCP 服务器(即插即用)
这个仓库提供了一个最小化、适合共享主机的MCP服务器,该服务器通过JSON-RPC向大型语言模型(LLM)客户端暴露工具。它设计为即插即用:只需将工具类放入指定位置即可 src/Tools/并且它们会自动被发现并暴露出来。
快速入门
- 要求:
PHP >= 8.0,ComposerApache 或 Nginx(Apache.htaccess(包含在共享托管中)。 - 安装依赖项:
composer install - 无需服务器配置文件。工具每次调用时接受凭据。
- 部署
public/到您的网站根目录。请求将被路由到public/index.php。 - 通过HTTP使用JSON-RPC进行调用。请参考示例
curl以下命令。
即插即用设计
- 自动发现:以下包中的所有类
MCP\Tools命名空间 insrc/Tools/被发现并登记。 - 灵活实例化:可以使用以下任意一种支持的模式来构建工具:
public static function create(MCP\Framework\ToolContext $context): self__construct(MCP\Framework\ToolContext $context)__construct(GuzzleHttp\Client $client, string $apiKey)(向后兼容的)__construct()(无参构造函数)
- 背景:
ToolContext为服务提供配置和HTTP客户端工厂。
编写一个工具
-
在(某个位置)创建一个PHP文件
src/Tools/并在其下定义一个或多个工具类MCP\Tools命名空间。 -
扩展
MCP\Framework\Tool并实施:getName(): stringgetDescription(): stringgetInputSchema(): array(用于JSON Schema的tools/call(参数)execute(array $arguments): array
-
将结果作为文本对象返回给MCP:
return ['type' => 'text', 'text' => 'Your result text'];
- 选择一个实例化模式:
选项A:带有(某功能/特性)的静态工厂 ToolContext
use MCP\Framework\ToolContext;
use MCP\Framework\Tool;
class WeatherTool extends Tool {
private \GuzzleHttp\Client $client;
public static function create(ToolContext $ctx): self {
$inst = new self();
$inst->client = $ctx->makeServiceClient('weather', ['timeout' => 8.0]);
return $inst;
}
public function getName(): string { return 'weather_current'; }
public function getDescription(): string { return 'Get current weather by city.'; }
public function getInputSchema(): array {
return {
'type': 'object',
'properties': {
'city': { 'type': 'string' },
// If API requires a key, declare and pass per-call:
// 'api_key': { 'type': 'string' }
},
'required': ['city']
};
}
public function execute(array $args): array {
$resp = $this->client->get('current', [
'query' => [ 'q' => $args['city'] /*, 'key' => $args['api_key'] */ ]
]);
return ['type' => 'text', 'text' => $resp->getBody()->getContents()];
}
}
选项B:带(参数)的构造函数 ToolContext
use MCP\Framework\ToolContext;
use MCP\Framework\Tool;
class GithubRepoTool extends Tool {
public function __construct(private ToolContext $ctx) {}
public function getName(): string { return 'github_repo_info'; }
public function getDescription(): string { return 'Fetch GitHub repo details.'; }
public function getInputSchema(): array { return ['type' => 'object', 'properties' => ['token' => ['type' => 'string'], 'owner' => ['type' => 'string'], 'repo' => ['type' => 'string']], 'required' => ['token','owner','repo']]; }
public function execute(array $args): array {
$client = $this->ctx->makeServiceClient('github');
$resp = $client->get("repos/{$args['owner']}/{$args['repo']}", [ 'headers' => ['Authorization' => 'Bearer ' . $args['token']] ]);
return ['type' => 'text', 'text' => $resp->getBody()->getContents()];
}
}
工具上下文引用
get(string $key, $default = null)从服务器提供的上下文设置中读取(无配置文件)。getAll(): array整个上下文设置数组。makeClient(array $options = []): Client使用提供的选项创建一个默认的HTTP客户端。makeServiceClient(string $service, array $options = []): Client使用服务特定选项创建客户端(例如base_uri,headers)。
选项合并:已提供 $options 覆盖默认值;头部信息将被合并。
服务器终端点(或服务器端点)
POST /index.phpJSON-RPC 方法:initializetools/listtools/call与;带有;使用{ name, arguments }
GET /index.php?health=1回报;收益{ "status": "ok" }。
CORS(跨源资源共享)
- 浏览器客户端可以直接调用服务器。入口点设置为:
Access-Control-Allow-Origin: *Access-Control-Allow-Methods: POST, OPTIONSAccess-Control-Allow-Headers: Content-Type
- 预飞行检查(Preflight)
OPTIONS) 请求返回204附上上述标题。
样品请求
初始化
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"initialize","params":{}}' \
https://your-host/index.php
列出工具
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"2","method":"tools/list","params":{}}' \
https://your-host/index.php
调用工具(VBOUT 示例包括每调用一次 api_key)
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"3","method":"tools/call","params":{"name":"vbout_get_contacts","arguments":{"api_key":"$VBOUT_KEY","limit":10}}}' \
https://your-host/index.php
GitHub(需要令牌)
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":"4",
"method":"tools/call",
"params":{
"name":"github_repo_info",
"arguments":{ "token":"$GITHUB_TOKEN", "owner":"octocat", "repo":"Hello-World" }
}
}' \
https://your-host/index.php
配置
不 config.php 需要满足此要求。在构建客户端时,工具应明确设置服务基础URI,并且每次调用都必须传递凭据(例如 api_key)。
共享主机使用技巧
.htaccess包含:禁用目录列表并路由到index.php。- 日志记录:错误信息被写入
storage/error.log。 - 已启用速率限制和有效载荷大小保护
public/index.php。 - 避免在代码仓库中存储真实密钥;每次调用时传递凭据。
开发说明
- 通过配置自动加载
composer.json(MCP\->src/)。 - 工具发现结果会被缓存,以减少文件系统和反射操作的开销。
- 响应有效载荷遵循JSON-RPC 2.0标准并返回
result.content作为适合大型语言模型(LLM)输出的文本。 - 当工具响应为JSON格式时,工具现在会解析请求体并返回结构化数据
{"type":"json","data":{...}}在可用时。如果解析失败,工具将回退到{"type":"text","text":"..."}。 - VBOUT 工具:
- 按每次通话使用
api_key传入的凭据arguments. - 具有明确配置的加固HTTP客户端
base_uri,超时设置,以及JSON头部信息。 - 标准化错误处理:当存在时,HTTP 4xx/5xx 响应会从 JSON 中显示消息;否则,显示原始的 HTTP 状态码或响应体。
- 按每次通话使用
连接一个大型语言模型(LLM)
- 终端点:
POST https://your-host/index.php使用 JSON-RPC 2.0。 - 握手:通话
initialize,然后tools/list发现可用的工具。 - 呼叫:使用
tools/call与……一起;带着;具有{ name, arguments }传递每次调用的凭据,如api_key或者token在arguments在需要时。 - 结果:服务器返回
result.content作为一系列对象,如{"type":"text","text":"..."}或者{"type":"json","data":{...}}。 - 错误:使用标准的JSON-RPC代码;服务器可能会返回
-32000对于工具执行失败,以及用于防护的HTTP 4xx状态码(401/405/413/429)。
Node.js 示例
// Minimal JSON-RPC client for MCP server
const fetch = globalThis.fetch || (await import('node-fetch')).default;
const HOST = 'https://your-host/index.php';
async function rpc(method, params) {
const body = { jsonrpc: '2.0', id: String(Date.now()), method, params };
const res = await fetch(HOST, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const json = await res.json();
if (json.error) throw new Error(`${json.error.code}: ${json.error.message}`);
return json.result;
}
// Initialize and list tools
const init = await rpc('initialize', {});
const tools = await rpc('tools/list', {});
console.log('Tools:', tools.tools.map(t => t.name));
// Call VBOUT contacts (per-call api_key)
const contacts = await rpc('tools/call', {
name: 'vbout_get_contacts',
arguments: { api_key: process.env.VBOUT_KEY, limit: 10 },
});
console.log('Contacts result:', contacts.content[0]);
// Call GitHub repo info (per-call token)
const gh = await rpc('tools/call', {
name: 'github_repo_info',
arguments: { token: process.env.GITHUB_TOKEN, owner: 'octocat', repo: 'Hello-World' },
});
console.log('GitHub result:', gh.content[0]);
Python 示例
import os, json, requests, time
HOST = 'https://your-host/index.php'
def rpc(method, params):
body = { 'jsonrpc': '2.0', 'id': str(int(time.time()*1000)), 'method': method, 'params': params }
r = requests.post(HOST, headers={
'Content-Type': 'application/json',
}, data=json.dumps(body))
j = r.json()
if 'error' in j:
raise RuntimeError(f"{j['error']['code']}: {j['error']['message']}")
return j['result']
init = rpc('initialize', {})
tools = rpc('tools/list', {})
print('Tools:', [t['name'] for t in tools['tools']])
contacts = rpc('tools/call', {
'name': 'vbout_get_contacts',
'arguments': { 'api_key': os.environ['VBOUT_KEY'], 'limit': 10 }
})
print('Contacts result:', contacts['content'][0])
gh = rpc('tools/call', {
'name': 'github_repo_info',
'arguments': { 'token': os.environ['GITHUB_TOKEN'], 'owner': 'octocat', 'repo': 'Hello-World' }
})
print('GitHub result:', gh['content'][0])
最佳实践
- 每次调用时传递服务凭据
api_key,token) 在arguments而不是存储在服务器上。 - 不要在服务器上存储服务API密钥。VBOUT工具需要
arguments.api_key并且将会返回Missing api_key如果省略,则不使用基于配置的回退。 - 遵守速率限制;默认60次请求/分钟,可调整
public/index.php。 - 根据每种工具的要求验证工具输入
inputSchema来自tools/list.# php-mcp-server(可译为:“# PHP-MCP 服务器”或根据上下文具体含义调整,但直译即为上述)