🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度
最近在尝试将大模型接入本地工具时,发现不同模型、不同框架的集成方式五花八门,配置起来相当繁琐。尤其是在处理流式输出、工具调用和上下文管理时,经常需要为每个项目重复编写大量胶水代码。如果你也遇到过类似问题,那么Model Context Protocol (MCP)可能就是那个你一直在寻找的标准化解决方案。本文将深入解析 MCP 的核心概念,并手把手带你从零开始,基于MCP Server Boot Starters快速构建一个支持流式输出的服务器,让你能轻松地将任何本地工具或数据源暴露给大模型使用。
1. MCP 核心概念:为什么需要它?
在深入代码之前,我们首先要理解 MCP 解决了什么问题。简单来说,MCP 是一个开放协议,旨在为大模型(LLM)与外部工具、数据源之间提供标准化的通信桥梁。
想象一下这个场景:你开发了一个股票查询工具、一个数据库客户端和一个文件搜索工具。现在,你想让 ChatGPT、Claude 或本地部署的 Llama 模型能够使用这些工具。在没有 MCP 之前,你可能需要:
- 为每个模型(OpenAI API, Anthropic API, Ollama 等)分别编写适配器。
- 处理不同的工具调用格式(如 OpenAI 的
function calling, Anthropic 的tools)。 - 自己管理上下文、状态和错误处理。
- 实现复杂的流式响应机制以提供更好的用户体验。
这个过程不仅重复劳动,而且难以维护和扩展。MCP 的出现,正是为了将工具/数据提供方(Server)与模型/客户端(Client)解耦。它定义了一套标准的 JSON-RPC over STDIO/HTTP 接口,使得:
- 工具开发者只需实现一次 MCP Server,其工具就能被任何兼容 MCP 的客户端(如 Claude Desktop, Cursor 等)使用。
- 客户端/模型侧只需实现一次 MCP Client,就能接入无数个 MCP Server 提供的工具和资源。
MCP 的核心组件:
- MCP Server: 工具或数据的提供方。它向客户端宣告自己提供了哪些“工具”(Tools,即可调用的函数)和“资源”(Resources,即可读取的上下文数据)。
- MCP Client: 通常是模型运行时环境(如 AI 应用)。它发现并调用 Server 提供的工具和资源,将结果整合到模型上下文中。
- 传输层: 默认为 JSON-RPC over STDIO(标准输入输出),也支持 HTTP 和 SSE(Server-Sent Events)。这保证了跨语言、跨进程的通用性。
Streamable-HT 是什么?在 MCP 的上下文中,“Streamable” 指的是支持流式传输(Streaming)的能力。这对于需要长时间运行或逐步产生结果的工具至关重要(例如,执行一个长时间脚本、监控日志文件、生成大量文本)。HT可能指代某种特定的处理类型或传输方式(在后续实践中我们会看到其具体表现)。简单理解,一个 Streamable 的 MCP Server 能够将工具执行结果以“数据块”的形式实时推送给客户端,而不是等待全部执行完毕再一次性返回。
接下来,我们将聚焦于如何快速构建一个这样的 MCP Server。
2. 环境准备与项目初始化
我们将使用TypeScript/JavaScript生态来构建 MCP Server,这是目前 MCP 社区最活跃、工具链最成熟的选择。确保你的开发环境满足以下要求:
基础环境:
- Node.js: 版本 18 或更高(推荐 LTS 版本,如 20.x)。这是运行 JavaScript 代码的引擎。
- npm或yarn或pnpm: 包管理工具。本文示例使用
npm。 - 代码编辑器: VS Code 或其他你熟悉的 IDE。
初始化项目:首先,创建一个新的项目目录并初始化一个 Node.js 项目。
# 1. 创建项目目录并进入 mkdir my-mcp-streaming-server cd my-mcp-streaming-server # 2. 初始化 package.json,一路回车或按需填写 npm init -y # 3. 安装 TypeScript 及相关类型定义(我们将用 TypeScript 获得更好的开发体验) npm install typescript @types/node --save-dev # 4. 初始化 TypeScript 配置 npx tsc --init生成的tsconfig.json文件需要调整以适配现代 Node.js 开发。一个推荐的配置如下:
{ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "lib": ["ES2022"], "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "declaration": true, "declarationMap": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] }安装 MCP 核心依赖:MCP 的核心 SDK 由 Anthropic 官方维护。我们还需要安装@modelcontextprotocol/sdk。
npm install @modelcontextprotocol/sdk现在,项目的基础骨架已经搭建完成。接下来,我们将引入能极大提升开发效率的利器——MCP Server Boot Starters。
3. 认识 MCP Server Boot Starters
手动从零开始实现一个 MCP Server 需要处理 JSON-RPC 通信、生命周期管理、工具注册等底层细节,虽然可行但效率不高。MCP Server Boot Starters 可以理解为 MCP Server 的“脚手架”或“快速启动模板”,它封装了这些通用逻辑,让开发者能专注于实现工具(Tools)和资源(Resources)的业务逻辑。
目前,社区已有一些优秀的 Boot Starters 项目。一个典型的例子是@modelcontextprotocol/server-boot(请注意,这是一个假设的包名,用于说明概念。实际开发中,你可能需要寻找或参考类似mcp-server-starter这样的开源模板)。其核心价值在于:
- 标准化入口: 提供了统一的 Server 类,继承后只需重写
initialize等方法。 - 生命周期管理: 自动处理 Server 的启动、停止、与客户端的握手协议。
- 工具/资源注册简化: 提供清晰的 API 来声明工具和资源。
- 传输层抽象: 简化了 STDIO、HTTP 等传输方式的配置。
- 类型安全: 配合 TypeScript,提供完整的类型提示,减少错误。
为什么使用 Starter?对于新手,它能帮你跳过复杂的协议实现,直接进入业务开发。对于有经验的开发者,它能保证项目结构符合最佳实践,便于维护和协作。在本文中,我们将基于一个类似 Starter 的理念,构建一个结构清晰、支持流式输出的 MCP Server。
4. 构建支持流式输出的 MCP Server
我们将创建一个模拟的“系统监控”服务器,它提供两个工具:
get_cpu_usage: 获取当前 CPU 使用率(一次性调用)。tail_logs: “尾随”查看一个模拟日志文件的内容(流式输出)。
4.1 项目结构设计
首先,创建以下目录和文件结构:
my-mcp-streaming-server/ ├── package.json ├── tsconfig.json ├── src/ │ ├── index.ts # 服务器主入口 │ ├── server/ # 服务器核心逻辑 │ │ ├── index.ts │ │ ├── StreamingMonitorServer.ts # 我们的 MCP 服务器类 │ │ └── tools/ # 工具实现 │ │ ├── index.ts │ │ ├── CpuTool.ts │ │ └── LogTailTool.ts │ └── types/ # 类型定义 │ └── index.ts └── dist/ # TypeScript 编译输出目录4.2 定义工具参数类型
在src/types/index.ts中,我们定义工具调用时需要的参数类型,这有助于 TypeScript 进行类型检查。
// src/types/index.ts /** * tail_logs 工具的参数类型 */ export interface TailLogsArgs { /** * 要监控的日志文件路径(模拟) */ filePath: string; /** * 要输出的日志行数 */ lines?: number; } /** * get_cpu_usage 工具的参数类型(此工具无需参数,但保留结构) */ export interface GetCpuUsageArgs { // 目前没有参数,但保持接口定义以备扩展 [key: string]: never; } /** * 所有工具参数的联合类型 */ export type ToolArgs = TailLogsArgs | GetCpuUsageArgs;4.3 实现流式日志工具
这是本文的核心,我们将展示如何实现一个支持流式输出的工具。关键在于使用 MCP SDK 提供的ToolResult和Content类型,并返回一个Promise,该 Promise 解析为一个包含isDelta标志的结果。
// src/server/tools/LogTailTool.ts import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { CallToolRequest } from '@modelcontextprotocol/sdk/types.js'; import { TailLogsArgs } from '../../types/index.js'; /** * 模拟尾随日志文件的工具 * 这是一个支持流式输出的工具示例。 */ export class LogTailTool { // 工具的名称,客户端通过此名称调用 static toolName = 'tail_logs'; // 工具的描述,帮助模型理解其用途 static toolDescription = 'Tail (follow) a log file and stream its new contents in real time.'; // 工具的参数模式定义(JSON Schema) static inputSchema = { type: 'object', properties: { filePath: { type: 'string', description: 'Path to the log file to tail (simulated).', }, lines: { type: 'number', description: 'Number of historical lines to output first (default: 10).', default: 10, }, }, required: ['filePath'], }; /** * 处理工具调用 * @param server MCP Server 实例 * @param request 工具调用请求 * @returns 返回一个 Promise,解析为流式结果 */ static async handler( server: Server, request: CallToolRequest ) { const args = request.params.arguments as unknown as TailLogsArgs; const filePath = args.filePath; const lines = args.lines || 10; // 模拟读取文件并流式返回 // 在实际应用中,这里可能是 fs.createReadStream, 子进程 stdout 等。 return new Promise(async (resolve) => { // 1. 首先,发送一个初始消息,包含文件信息和开始提示 resolve({ content: [ { type: 'text', text: `开始监控日志文件: ${filePath}\n先显示最后 ${lines} 行:\n${'-'.repeat(50)}\n`, }, ], // `isDelta: true` 表明这是一个流式响应的开始,后续还会有更多数据块。 isDelta: true, }); // 2. 模拟流式发送历史日志行(快速发送) const historicalLogs = this.generateLogs(lines, 'HISTORICAL'); for (const log of historicalLogs) { // 使用 server 的 sendNotification 或其他机制发送增量数据。 // 注意:标准 MCP 工具调用结果返回后,通常通过独立的 `notifications` 或后续调用来发送流数据。 // 这里为了简化演示,我们模拟在 handler 返回后异步发送数据。 // 更精确的实现可能需要使用 Server-Sent Events (SSE) 或 WebSocket,具体取决于传输层。 // 以下代码演示了概念,实际流式传输需要客户端和服务器端共同支持。 setTimeout(() => { // 在实际流式传输中,你会通过一个持久的连接发送这些数据块。 // 例如:response.write(`data: ${JSON.stringify({ delta: log })}\n\n`); console.log('[Stream Delta]', log); // 此处模拟发送数据块 }, Math.random() * 100); // 随机延迟模拟网络波动 } // 3. 模拟持续产生新的日志行(间隔发送) let counter = 0; const intervalId = setInterval(() => { counter++; const newLog = this.generateLogLine('STREAMING', counter); console.log('[Stream Delta]', newLog); // 模拟发送新的数据块 // 模拟流式传输 5 条新日志后停止 if (counter >= 5) { clearInterval(intervalId); // 发送流结束标记(在实际协议中可能有特定消息) console.log('[Stream End]', `\n${'-'.repeat(50)}\n日志流监控结束。`); } }, 1000); // 每秒一条新日志 // 注意:实际的 MCP 流式传输需要更精细的控制,例如使用 `ReadableStream` 或类似的抽象。 // 上述 `setTimeout` 和 `setInterval` 仅用于演示流式数据生成的逻辑。 }); } /** * 生成模拟的历史日志 */ private static generateLogs(count: number, type: string): string[] { const logs: string[] = []; for (let i = 1; i <= count; i++) { logs.push(this.generateLogLine(type, i)); } return logs; } /** * 生成单条模拟日志行 */ private static generateLogLine(type: string, index: number): string { const timestamps = ['2023-10-27 08:00:01', '2023-10-27 08:00:05', '2023-10-27 08:00:12']; const messages = [ 'User login from IP 192.168.1.100', 'Database query executed successfully.', 'Cache miss for key: user_profile_12345', 'Scheduled task `cleanup_temp` started.', 'API `/api/v1/data` responded in 156ms.', ]; const randomTime = timestamps[Math.floor(Math.random() * timestamps.length)]; const randomMsg = messages[Math.floor(Math.random() * messages.length)]; return `[${randomTime}] [INFO] (${type}_${index}) ${randomMsg}`; } }关键点解析:
isDelta: true: 在返回的ToolResult中设置此标志,是向客户端表明此工具的响应是流式的,初始返回的内容只是第一部分。- 异步数据推送: 真正的流式数据需要在工具调用返回后,通过其他通道(如 Server-Sent Events, WebSocket,或 MCP 协议中定义的特定通知)持续推送给客户端。上面的
console.log模拟了这种推送。在实际的 HTTP+SSE 传输层中,你会直接向响应流写入数据。 - 模式定义 (
inputSchema): 使用 JSON Schema 清晰地定义工具参数,这对客户端(尤其是大模型)理解如何调用工具至关重要。
4.4 实现普通 CPU 工具
作为对比,我们实现一个普通的、一次性返回结果的工具。
// src/server/tools/CpuTool.ts import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { CallToolRequest } from '@modelcontextprotocol/sdk/types.js'; import { GetCpuUsageArgs } from '../../types/index.js'; /** * 获取 CPU 使用率的工具 * 这是一个普通(非流式)工具示例。 */ export class CpuTool { static toolName = 'get_cpu_usage'; static toolDescription = 'Get the current system CPU usage percentage (simulated).'; static inputSchema = { type: 'object', properties: { // 这个工具目前不需要参数,但 schema 仍需是一个对象 }, required: [], }; static async handler( server: Server, request: CallToolRequest ) { // 模拟获取 CPU 使用率 const usage = Math.floor(Math.random() * 30) + 10; // 10% 到 40% const timestamp = new Date().toISOString(); return { content: [ { type: 'text', text: `CPU Usage Report (Simulated):\nTimestamp: ${timestamp}\nUsage: ${usage}%\nStatus: ${usage > 70 ? 'High Load' : 'Normal'}`, }, ], // 对于非流式工具,不设置 isDelta 或设置为 false(默认) }; } }4.5 创建主服务器类
现在,我们将工具整合到一个 MCP Server 类中,并处理服务器的初始化。
// src/server/StreamingMonitorServer.ts import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { ListToolsRequestSchema, CallToolRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { CpuTool } from './tools/CpuTool.js'; import { LogTailTool } from './tools/LogTailTool.js'; /** * 我们的自定义 MCP 服务器,支持流式工具。 */ export class StreamingMonitorServer { private server: Server; constructor() { // 1. 创建 MCP Server 实例 this.server = new Server( { name: 'streaming-monitor-server', version: '0.1.0', }, { capabilities: { // 声明服务器支持的工具 tools: {}, }, } ); // 2. 设置请求处理器 this.setupHandlers(); // 3. 连接传输层(这里使用 STDIO,适用于 Claude Desktop 等客户端) this.setupTransport(); } /** * 设置 JSON-RPC 请求处理器 */ private setupHandlers() { // 处理客户端查询可用工具的请求 this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: CpuTool.toolName, description: CpuTool.toolDescription, inputSchema: CpuTool.inputSchema, }, { name: LogTailTool.toolName, description: LogTailTool.toolDescription, inputSchema: LogTailTool.inputSchema, }, ], }; }); // 处理客户端调用工具的请求 this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; console.error(`[Server] Tool called: ${name}`, args); // 日志输出到 stderr,避免干扰 stdio 协议 switch (name) { case CpuTool.toolName: return await CpuTool.handler(this.server, request); case LogTailTool.toolName: return await LogTailTool.handler(this.server, request); default: throw new Error(`Unknown tool: ${name}`); } }); } /** * 设置传输层(使用标准输入输出) */ private setupTransport() { const transport = new StdioServerTransport(); this.server.connect(transport).catch((error) => { console.error('[Server] Failed to connect transport:', error); process.exit(1); }); } /** * 启动服务器 */ async run() { // 服务器在 connect 后会自动开始监听 STDIO console.error('[Server] Streaming Monitor MCP Server is running on STDIO...'); // 保持进程运行 process.on('SIGINT', () => { this.server.close(); process.exit(0); }); } }4.6 创建程序入口
最后,在src/index.ts中启动我们的服务器。
// src/index.ts import { StreamingMonitorServer } from './server/StreamingMonitorServer.js'; // 创建并运行服务器 const server = new StreamingMonitorServer(); server.run().catch((error) => { console.error('[Server] Fatal error:', error); process.exit(1); });5. 构建、运行与测试
5.1 构建 TypeScript 项目
首先,我们需要将 TypeScript 代码编译成 JavaScript。
# 在项目根目录执行 npx tsc如果编译成功,你会在dist/目录下看到生成的.js文件。
5.2 通过 Node.js 直接运行(测试)
为了快速测试服务器逻辑(不涉及完整的 MCP 客户端),我们可以创建一个简单的测试脚本。但请注意,真正的 MCP 交互需要通过 STDIO 协议。
一个更直接的测试方法是使用MCP 客户端调试工具,例如@modelcontextprotocol/sdk包中可能提供的工具,或者第三方 CLI 工具如mcp-cli。这里我们演示如何通过修改入口文件,让服务器在启动时模拟一个工具调用(仅用于开发调试)。
创建src/test.ts(记得在tsconfig.json的include中添加"src/test.ts"):
// src/test.ts - 开发环境下的自检 import { StreamingMonitorServer } from './server/StreamingMonitorServer.js'; const server = new StreamingMonitorServer(); console.log('服务器初始化完成。'); console.log('请使用兼容 MCP 的客户端(如 Claude Desktop)连接到此服务器。'); console.log('确保在客户端配置中指定服务器命令为:node /path/to/dist/index.js'); // 保持进程运行 process.stdin.resume();编译并运行它,确保没有启动错误:
npx tsc node dist/test.js你应该看到服务器等待连接的提示。
5.3 与 MCP 客户端集成(以 Claude Desktop 为例)
这是 MCP Server 的真正用途。以 Claude Desktop 为例:
找到 Claude Desktop 的配置目录:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
- macOS:
编辑配置文件,添加你的 MCP Server:
{ "mcpServers": { "my-monitor": { "command": "node", "args": [ "/ABSOLUTE/PATH/TO/YOUR/PROJECT/dist/index.js" ], "env": { "NODE_ENV": "production" } } } }重要:将
/ABSOLUTE/PATH/TO/YOUR/PROJECT替换为你项目的绝对路径。重启 Claude Desktop。
在 Claude 的聊天界面,你应该能直接使用定义的工具,例如:“请帮我查看一下 CPU 使用率”或“尾随一下
/var/log/system.log这个文件”。
6. 常见问题与排查思路
在开发和运行 MCP Server 时,你可能会遇到以下问题:
| 问题现象 | 可能原因 | 排查思路与解决方案 |
|---|---|---|
| Claude Desktop 无法连接服务器 | 1. 配置文件路径错误。 2. Node.js 命令路径错误。 3. 服务器脚本存在语法错误,启动即崩溃。 4. 端口或 STDIO 冲突。 | 1. 检查claude_desktop_config.json格式和路径。2. 在终端中直接运行 node /path/to/dist/index.js,看是否有错误输出。3. 查看 Claude Desktop 的日志文件(位置因系统而异)。 4. 确保服务器代码正确处理了 STDIO 传输。 |
| 工具列表不显示或调用失败 | 1.ListToolsRequest处理器未正确设置或返回格式错误。2. 工具的名称、描述、参数 Schema 不符合规范。 3. CallToolRequest处理器中未正确匹配工具名。 | 1. 在setupHandlers中检查ListToolsRequestSchema的处理逻辑。2. 使用简单的 JSON 验证工具检查 inputSchema。3. 在 CallToolRequest的switch语句中添加默认的console.error日志。 |
| 流式输出不工作 | 1. 客户端不支持流式传输。 2. 服务器返回的 ToolResult中isDelta设置不正确。3. 流式数据推送的机制未实现或实现有误(如未使用正确的传输通道)。 | 1. 确认你的客户端(如 Claude Desktop)版本是否支持流式工具。 2. 确保流式工具的 handler 返回 { content: [...], isDelta: true }。3.深入研究 MCP 协议对于流式传输的具体定义。当前示例中的 setInterval是模拟,真正的实现可能需要基于 SSE 或 WebSocket 的传输层。查阅官方 SDK 和示例。 |
| 服务器进程意外退出 | 1. 未捕获的异常。 2. 传输层连接断开。 3. 进程收到终止信号。 | 1. 在server.run()和connect()外包裹try-catch。2. 监听 process的uncaughtException和unhandledRejection事件。3. 实现 server.close()的优雅关闭逻辑。 |
| TypeScript 编译错误 | 1. 类型不匹配。 2. tsconfig.json配置问题。3. 模块导入路径错误。 | 1. 仔细阅读错误信息,修正类型。 2. 确保使用 NodeNext的模块解析策略。3. 检查 import语句中的文件扩展名(.js或.ts)。 |
7. 最佳实践与工程建议
构建用于生产环境的 MCP Server 时,请考虑以下建议:
清晰的工具定义:
- 名称:使用
snake_case,清晰表达动作和对象,如search_web,execute_sql_query。 - 描述:一句话准确描述功能,让 LLM 能准确判断何时调用。
- 参数 Schema:务必详细。使用
description字段解释每个参数的用途和示例。对于枚举值,使用enum列出所有选项。
- 名称:使用
健壮的错误处理:
- 在工具
handler内部使用try-catch,返回结构化的错误信息,而不是让异常冒泡导致整个请求失败。 - 错误信息应对用户友好,同时包含可供调试的细节(可记录在服务器日志中)。
try { // ... 业务逻辑 } catch (error: any) { return { content: [{ type: 'text', text: `操作失败: ${error.message}` }], isError: true, // MCP 协议中可能用于标识错误结果 }; }- 在工具
资源管理:
- 对于文件操作、数据库连接、网络请求等,确保在工具执行完毕后正确关闭资源,避免泄漏。
- 考虑为长时间运行的工具(如
tail_logs)实现超时和取消机制。
安全性:
- 输入验证:永远不要信任客户端传入的参数。即使有 Schema,也要在 handler 中再次进行边界检查和清理,防止路径遍历(
../../../)、命令注入等攻击。 - 权限控制:如果服务器提供敏感操作,应实现认证和授权机制。MCP 协议本身不强制要求,但可以在 Server 层面或通过传输层(如 HTTPS)添加。
- 环境隔离:考虑在沙箱或容器中运行工具,尤其是执行任意代码或命令时。
- 输入验证:永远不要信任客户端传入的参数。即使有 Schema,也要在 handler 中再次进行边界检查和清理,防止路径遍历(
性能与可观测性:
- 日志记录:记录工具调用、参数、耗时和结果(注意脱敏)。将日志输出到
stderr或文件,避免污染与客户端的协议通信(stdio)。 - 指标监控:对于高频工具,可以考虑添加简单的指标,如调用次数、平均延迟。
- 流式传输优化:对于真正的流式工具,注意缓冲区管理,避免内存溢出。考虑使用
StreamAPI 或类似的异步迭代器。
- 日志记录:记录工具调用、参数、耗时和结果(注意脱敏)。将日志输出到
配置化:
- 将工具列表、服务器元数据(名称、版本)等提取到配置文件(如
config.json)中,便于不同环境部署。
- 将工具列表、服务器元数据(名称、版本)等提取到配置文件(如
测试:
- 为每个工具编写单元测试,模拟
CallToolRequest。 - 进行集成测试,使用一个简单的 MCP 客户端脚本实际连接你的服务器并调用工具。
- 为每个工具编写单元测试,模拟
通过遵循以上步骤和最佳实践,你不仅可以构建出演示中的监控服务器,更能将其模式应用到任何你想暴露给大模型的本地能力上,例如代码库搜索、内部 API 调用、数据分析脚本等。MCP 的生态正在快速发展,掌握其 Server 端开发,意味着你能够为自己和团队打造出强大、可复用的 AI 原生工具链。
🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度