# tax-invoice-mcp **Repository Path**: fapiaoapi/tax-invoice-mcp ## Basic Information - **Project Name**: tax-invoice-mcp - **Description**: 电子发票MCP 数电发票MCP - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: https://fa-piao.com - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-16 - **Last Updated**: 2026-05-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: MCP, 电子发票, 数电发票, 电子发票MCP, 数电发票MCP ## README # fa-piao.com MCP Server [![MCP](https://img.shields.io/badge/MCP-Streamable%20HTTP-8b5cf6)](https://modelcontextprotocol.io) [![API](https://img.shields.io/badge/API-fa--piao.com-1a56db)](https://fa-piao.com) **让 AI 大模型成为您的开票助手** — 通过 MCP (Model Context Protocol) 协议,将电子发票/数电发票 API 接入 Claude、GPT、Cursor、Cherry Studio 等大模型平台,实现 AI 智能开票。 --- ## 目录 - [什么是 MCP Server?](#什么是-mcp-server) - [核心特性](#核心特性) - [快速开始](#快速开始) - [MCP Server 配置](#mcp-server-配置) - [多语言接入示例](#多语言接入示例) - [Python](#python) - [Node.js](#nodejs) - [Java](#java) - [Go](#go) - [C#](#csharp) - [PHP](#php) - [Rust](#rust) - [平台接入教程](#平台接入教程) - [Cherry Studio](#cherry-studio) - [Cursor](#cursor) - [TRAE](#trae) - [典型应用场景](#典型应用场景) - [常见问题](#常见问题) - [相关链接](#相关链接) --- ## 什么是 MCP Server? MCP(Model Context Protocol)是由 Anthropic 推出的开放标准协议,定义了 AI 大模型如何安全、标准化地调用外部工具和 API。 fa-piao.com 率先实现了 **MCP Server (Streamable HTTP)**,让任何支持 MCP 的大模型都能直接操作发票系统: - **Streamable HTTP** 传输,实时流式响应 - 完整的 **Tools 定义**:开票 / 查询 / 红冲 / 查验 - **自然语言交互**,无需编写代码 - 企业级 **API Key 权限隔离** - 支持所有 MCP 兼容的大模型客户端 --- ## 核心特性 | 特性 | 说明 | |------|------| | 🔌 Streamable HTTP | 基于 HTTP 的流式传输协议,实时推送响应 | | 🛠️ 完整 Tools 定义 | 开票、查询、红冲、查验等全部发票操作 | | 🔐 安全认证 | Bearer Token 认证 + TLS 1.3 加密传输 | | 🌍 多语言支持 | Python / Node.js / Java / Go / C# / PHP / Rust | | 🤖 大模型兼容 | Claude / GPT / Cursor / Cherry Studio / TRAE | | 📊 调试模式 | 内置 DEBUG 开关,完整请求/响应日志 | --- ## 快速开始 ### 1. 获取 MCP Token 访问 [open.fa-piao.com](https://open.fa-piao.com) 注册并登录,申请 MCP Token。 ### 2. 配置 MCP Server 在大模型客户端的配置文件中添加以下 JSON: ```json { "mcpServers": { "tax-invoice-mcp": { "type": "streamableHttp", "url": "https://mcp.fa-piao.com", "headers": { "Authorization": "Bearer YOUR_MCP_TOKEN", "X-Client-Version": "1.0.1" } } } } ``` > ⚠️ **重要**:请将 `YOUR_MCP_TOKEN` 替换为实际的 MCP Token。 ### 3. 开始使用 配置完成后,即可通过自然语言与大模型交互,例如: - "查询纳税人 91500112MAXXXXX 的开票状态" - "查验这张发票的真伪" --- ## MCP Server 配置 | 配置项 | 值 | 说明 | |--------|-----|------| | 协议类型 | `streamableHttp` | Streamable HTTP 传输协议 | | 端点地址 | `https://mcp.fa-piao.com` | MCP Server 服务地址 | | 认证方式 | `Bearer Token` | 在 Header 中携带 Authorization | | 客户端版本 | `1.0.1` | X-Client-Version 请求头 | ### 请求头说明 | Header | 必填 | 说明 | |--------|------|------| | `Authorization` | ✅ | Bearer Token 认证 | | `Content-Type` | ✅ | `application/json` | | `X-Client-Version` | ✅ | 客户端版本号 | | `Accept` | 流式必填 | `text/event-stream, application/json` | | `mcp-session-id` | 自动 | 服务端返回的会话 ID | --- ## 多语言接入示例 所有示例均基于 **JSON-RPC 2.0** 协议,支持流式和非流式两种调用模式。 ### Python
流式调用 ```python import requests import json import time from datetime import datetime MCP_URL = "https://mcp.fa-piao.com" MCP_TOKEN = "YOUR_MCP_TOKEN" DEBUG = True mcp_session_id = None request_id_counter = 0 def get_next_request_id(): global request_id_counter request_id_counter += 1 return request_id_counter def mcp_call_stream(method, params=None): global mcp_session_id start_time = time.time() current_id = get_next_request_id() payload = { "jsonrpc": "2.0", "id": current_id, "method": method, "params": params if params is not None else {} } body = json.dumps(payload, ensure_ascii=False) headers = { "Authorization": f"Bearer {MCP_TOKEN}", "Content-Type": "application/json", "X-Client-Version": "1.0.1", "Accept": "text/event-stream, application/json" } if mcp_session_id: headers["mcp-session-id"] = mcp_session_id try: resp = requests.post(MCP_URL, data=body, headers=headers, timeout=120) duration_ms = (time.time() - start_time) * 1000 response_session_id = resp.headers.get("mcp-session-id") if response_session_id: mcp_session_id = response_session_id content_type = resp.headers.get("Content-Type", "") is_sse = "text/event-stream" in content_type if is_sse: response_data = [] for line in resp.iter_lines(decode_unicode=True): if not line: continue if line.startswith("data:"): data = line[5:].strip() if data == "[DONE]": break response_data.append(data) return "\n".join(response_data) else: return resp.text.strip() except Exception as e: print(f"[异常] {method}: {e}") return None if __name__ == "__main__": mcp_call_stream("initialize", {"protocolVersion": "2025-11-25"}) mcp_call_stream("notifications/initialized", {}) mcp_call_stream("tools/list", {}) mcp_call_stream("tools/call", { "name": "enterprise_query_state", "arguments": {"nsrsbh": "91500112MAXXXXX", "username": "1325580xxxx"} }) ```
非流式调用 ```python import requests import json MCP_URL = "https://mcp.fa-piao.com" MCP_TOKEN = "YOUR_MCP_TOKEN" mcp_session_id = None request_id_counter = 0 def get_next_request_id(): global request_id_counter request_id_counter += 1 return request_id_counter def mcp_call(method, params=None): global mcp_session_id current_id = get_next_request_id() payload = { "jsonrpc": "2.0", "id": current_id, "method": method, "params": params if params is not None else {} } headers = { "Authorization": f"Bearer {MCP_TOKEN}", "X-Client-Version": "1.0.1", "Content-Type": "application/json" } if mcp_session_id: headers["mcp-session-id"] = mcp_session_id resp = requests.post(MCP_URL, data=json.dumps(payload, ensure_ascii=False), headers=headers, timeout=120) response_session_id = resp.headers.get("mcp-session-id") if response_session_id: mcp_session_id = response_session_id return resp.text.strip() if __name__ == "__main__": mcp_call("initialize", {"protocolVersion": "2025-11-25"}) result = mcp_call("tools/call", { "name": "enterprise_query_state", "arguments": {"nsrsbh": "91500112MAXXXX", "username": "1325580xxxx"} }) print(json.dumps(result, indent=2, ensure_ascii=False)) ```
### Node.js
流式调用 ```typescript import axios, { AxiosResponse } from 'axios'; const MCP_URL = 'https://mcp.fa-piao.com'; const MCP_TOKEN = 'YOUR_MCP_TOKEN'; const DEBUG = true; let mcpSessionId: string | null = null; let requestIdCounter = 0; function getNextRequestId(): number { requestIdCounter += 1; return requestIdCounter; } function parseSseResponse(responseText: string): string { const lines = responseText.split(/\r?\n/); const responseData: string[] = []; for (const line of lines) { if (!line.trim()) continue; if (line.startsWith('data:')) { const data = line.substring(5).trim(); if (data === '[DONE]') break; responseData.push(data); } } return responseData.join('\n'); } export async function mcpCallStream(method: string, params?: any): Promise { const startTime = Date.now(); const currentId = getNextRequestId(); const payload = { jsonrpc: '2.0', id: currentId, method: method, params: params !== undefined && params !== null ? params : {} }; const headers: Record = { 'Authorization': `Bearer ${MCP_TOKEN}`, 'Content-Type': 'application/json', 'X-Client-Version': '1.0.1', 'Accept': 'text/event-stream, application/json' }; if (mcpSessionId) { headers['mcp-session-id'] = mcpSessionId; } try { const response: AxiosResponse = await axios.post(MCP_URL, JSON.stringify(payload), { headers: headers, timeout: 120000, validateStatus: () => true, responseType: 'text', transformResponse: [(data) => data] }); const responseSessionId = response.headers['mcp-session-id']; if (responseSessionId && !Array.isArray(responseSessionId)) { mcpSessionId = responseSessionId; } const contentType = response.headers['content-type'] || ''; const isSse = contentType.includes('text/event-stream'); if (isSse) { return parseSseResponse(response.data); } return response.data.trim(); } catch (error) { console.error(`[异常] ${method}:`, error); return null; } } async function main() { await mcpCallStream('initialize', { protocolVersion: '2025-11-25' }); await mcpCallStream('notifications/initialized', {}); await mcpCallStream('tools/list', {}); await mcpCallStream('tools/call', { name: 'enterprise_query_state', arguments: { nsrsbh: '91500112MAXXXXX', username: '1325580xxxx' } }); } if (require.main === module) { main().catch(console.error); } ```
非流式调用 ```typescript import axios, { AxiosResponse } from 'axios'; const MCP_URL = 'https://mcp.fa-piao.com'; const MCP_TOKEN = 'YOUR_MCP_TOKEN'; let mcpSessionId: string | null = null; let requestId = 0; function nextRequestId(): number { return ++requestId; } export async function mcpCall(method: string, params?: string): Promise { const currentId = nextRequestId(); const body = JSON.stringify({ jsonrpc: '2.0', id: currentId, method: method, params: params ? JSON.parse(params) : {} }); const headers: Record = { 'Authorization': `Bearer ${MCP_TOKEN}`, 'X-Client-Version': '1.0.1', 'Content-Type': 'application/json' }; if (mcpSessionId) { headers['mcp-session-id'] = mcpSessionId; } const response: AxiosResponse = await axios.post(MCP_URL, body, { headers: headers, timeout: 30000, validateStatus: () => true, transformResponse: [] }); const responseSessionId = response.headers['mcp-session-id']; if (responseSessionId && !Array.isArray(responseSessionId)) { mcpSessionId = responseSessionId; } return typeof response.data === 'string' ? response.data : JSON.stringify(response.data); } async function main() { await mcpCall('initialize', '{"protocolVersion": "2025-11-25"}'); const result = await mcpCall('tools/call', '{"name":"enterprise_query_state","arguments":{"nsrsbh":"91500112MAXXXX","username":"1325580xxxx"}}'); console.log(result); } if (require.main === module) { main().catch(console.error); } ```
### Java
流式调用 ```java import java.net.URI; import java.net.http.*; import java.io.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; public class McpStreamClient { public static final boolean DEBUG = false; private static final String MCP_URL = "https://mcp.fa-piao.com"; private static final String MCP_TOKEN = "YOUR_MCP_TOKEN"; private static final AtomicReference mcpSessionId = new AtomicReference<>(null); private static final AtomicInteger requestId = new AtomicInteger(0); private static int nextRequestId() { return requestId.incrementAndGet(); } public static String mcpCallStream(String method, String params) throws Exception { int currentId = nextRequestId(); String body = String.format( "{\"jsonrpc\":\"2.0\",\"id\":%d,\"method\":\"%s\",\"params\":%s}", currentId, method, params != null ? params : "{}" ); HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() .uri(URI.create(MCP_URL)) .header("Authorization", "Bearer " + MCP_TOKEN) .header("X-Client-Version", "1.0.1") .header("Content-Type", "application/json") .header("Accept", "text/event-stream, application/json") .POST(HttpRequest.BodyPublishers.ofString(body)); String sessionId = mcpSessionId.get(); if (sessionId != null && !sessionId.isEmpty()) { requestBuilder.header("mcp-session-id", sessionId); } HttpClient client = HttpClient.newHttpClient(); HttpResponse resp = client.send( requestBuilder.build(), HttpResponse.BodyHandlers.ofInputStream()); String responseSessionId = resp.headers().firstValue("mcp-session-id").orElse(null); if (responseSessionId != null) { mcpSessionId.set(responseSessionId); } return readStreamResponse(resp.body(), resp.headers()); } private static String readStreamResponse(InputStream inputStream, HttpHeaders respHeaders) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder responseBody = new StringBuilder(); String line; String contentType = respHeaders.firstValue("Content-Type").orElse(""); boolean isSSE = contentType.contains("text/event-stream"); if (isSSE) { while ((line = reader.readLine()) != null) { if (line.startsWith("data:")) { String data = line.substring(5).trim(); if ("[DONE]".equals(data)) break; responseBody.append(data); } } } else { int ch; while ((ch = reader.read()) != -1) { responseBody.append((char) ch); } } reader.close(); return responseBody.toString().trim(); } public static void main(String[] args) throws Exception { mcpCallStream("initialize", "{\"protocolVersion\": \"2025-11-25\"}"); mcpCallStream("notifications/initialized", "{}"); mcpCallStream("tools/list", "{}"); mcpCallStream("tools/call", "{\"name\":\"enterprise_query_state\",\"arguments\":{\"nsrsbh\":\"91500112MAXXXXX\",\"username\":\"1325580xxxx\"}}"); } } ```
非流式调用 ```java import java.net.URI; import java.net.http.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; public class McpClient { private static final String MCP_URL = "https://mcp.fa-piao.com"; private static final String MCP_TOKEN = "YOUR_MCP_TOKEN"; private static final AtomicReference mcpSessionId = new AtomicReference<>(null); private static final AtomicInteger requestId = new AtomicInteger(0); private static int nextRequestId() { return requestId.incrementAndGet(); } public static String mcpCall(String method, String params) throws Exception { int currentId = nextRequestId(); String body = String.format( "{\"jsonrpc\":\"2.0\",\"id\":%d,\"method\":\"%s\",\"params\":%s}", currentId, method, params != null ? params : "{}" ); HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() .uri(URI.create(MCP_URL)) .header("Authorization", "Bearer " + MCP_TOKEN) .header("X-Client-Version", "1.0.1") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(body)); String sessionId = mcpSessionId.get(); if (sessionId != null && !sessionId.isEmpty()) { requestBuilder.header("mcp-session-id", sessionId); } HttpClient client = HttpClient.newHttpClient(); HttpResponse resp = client.send( requestBuilder.build(), HttpResponse.BodyHandlers.ofString()); String responseSessionId = resp.headers().firstValue("mcp-session-id").orElse(null); if (responseSessionId != null) { mcpSessionId.set(responseSessionId); } return resp.body(); } public static void main(String[] args) throws Exception { mcpCall("initialize", "{\"protocolVersion\": \"2025-11-25\"}"); mcpCall("tools/call", "{\"name\":\"enterprise_query_state\",\"arguments\":{\"nsrsbh\":\"91500112MAXXXX\",\"username\":\"1325580xxxx\"}}"); } } ```
### Go
流式调用 ```go package main import ( "bufio" "bytes" "fmt" "io" "net/http" "strings" "sync/atomic" ) const ( MCP_URL = "https://mcp.fa-piao.com" MCP_TOKEN = "YOUR_MCP_TOKEN" DEBUG = true ) var ( mcpSessionId atomic.Value requestId atomic.Int32 ) func init() { mcpSessionId.Store("") } func nextRequestId() int32 { return requestId.Add(1) } func mcpCallStream(method string, params string) (string, error) { currentId := nextRequestId() body := fmt.Sprintf(`{"jsonrpc":"2.0","id":%d,"method":"%s","params":%s}`, currentId, method, params) req, err := http.NewRequest("POST", MCP_URL, bytes.NewBufferString(body)) if err != nil { return "", fmt.Errorf("创建请求失败: %v", err) } req.Header.Set("Authorization", "Bearer "+MCP_TOKEN) req.Header.Set("X-Client-Version", "1.0.1") req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "text/event-stream, application/json") sessionId := mcpSessionId.Load().(string) if sessionId != "" { req.Header.Set("mcp-session-id", sessionId) } client := &http.Client{} resp, err := client.Do(req) if err != nil { return "", fmt.Errorf("发送请求失败: %v", err) } defer resp.Body.Close() responseSessionId := resp.Header.Get("mcp-session-id") if responseSessionId != "" { mcpSessionId.Store(responseSessionId) } return readStreamResponse(resp.Body, resp.Header.Get("Content-Type")) } func readStreamResponse(body io.Reader, contentType string) (string, error) { reader := bufio.NewReader(body) var responseBody strings.Builder isSSE := strings.Contains(contentType, "text/event-stream") if isSSE { for { line, err := reader.ReadString('\n') if err != nil { if err == io.EOF { break } return "", err } line = strings.TrimSpace(line) if strings.HasPrefix(line, "data:") { data := strings.TrimSpace(line[5:]) if data == "[DONE]" { break } responseBody.WriteString(data) } } } else { all, err := io.ReadAll(reader) if err != nil { return "", err } responseBody.Write(all) } return strings.TrimSpace(responseBody.String()), nil } func main() { mcpCallStream("initialize", `{"protocolVersion": "2025-11-25"}`) mcpCallStream("notifications/initialized", "{}") mcpCallStream("tools/list", "{}") mcpCallStream("tools/call", `{"name":"enterprise_query_state","arguments":{"nsrsbh":"91500112MAXXXXX","username":"1325580xxxx"}}`) } ```
非流式调用 ```go package main import ( "bytes" "fmt" "io" "net/http" "sync/atomic" ) const ( MCP_URL = "https://mcp.fa-piao.com" MCP_TOKEN = "YOUR_MCP_TOKEN" ) var ( mcpSessionId atomic.Value requestId atomic.Int32 ) func init() { mcpSessionId.Store("") } func nextRequestId() int32 { return requestId.Add(1) } func mcpCall(method string, params string) (string, error) { currentId := nextRequestId() body := fmt.Sprintf(`{"jsonrpc":"2.0","id":%d,"method":"%s","params":%s}`, currentId, method, params) req, err := http.NewRequest("POST", MCP_URL, bytes.NewBufferString(body)) if err != nil { return "", fmt.Errorf("创建请求失败: %v", err) } req.Header.Set("Authorization", "Bearer "+MCP_TOKEN) req.Header.Set("X-Client-Version", "1.0.1") req.Header.Set("Content-Type", "application/json") sessionId := mcpSessionId.Load().(string) if sessionId != "" { req.Header.Set("mcp-session-id", sessionId) } client := &http.Client{} resp, err := client.Do(req) if err != nil { return "", fmt.Errorf("发送请求失败: %v", err) } defer resp.Body.Close() responseSessionId := resp.Header.Get("mcp-session-id") if responseSessionId != "" { mcpSessionId.Store(responseSessionId) } respBody, err := io.ReadAll(resp.Body) if err != nil { return "", fmt.Errorf("读取响应失败: %v", err) } return string(respBody), nil } func main() { mcpCall("initialize", `{"protocolVersion": "2025-11-25"}`) mcpCall("tools/call", `{"name":"enterprise_query_state","arguments":{"nsrsbh":"91500112MAXXXX","username":"1325580xxxx"}}`) } ```
### C#
流式调用 ```csharp using System; using System.IO; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace Tax.Invoice.Example { public class McpStreamClient { public static readonly bool DEBUG = true; private const string MCP_URL = "https://mcp.fa-piao.com"; private const string MCP_TOKEN = "YOUR_MCP_TOKEN"; private static string? mcpSessionId = null; private static int requestId = 0; private static readonly object lockObj = new object(); private static int NextRequestId() { lock (lockObj) { return ++requestId; } } public static async Task McpCallStream(string method, string? paramsJson = null) { int currentId = NextRequestId(); string body = string.Format( "{{\"jsonrpc\":\"2.0\",\"id\":{0},\"method\":\"{1}\",\"params\":{2}}}", currentId, method, !string.IsNullOrEmpty(paramsJson) ? paramsJson : "{}" ); using var requestBuilder = new HttpRequestMessage(HttpMethod.Post, MCP_URL); requestBuilder.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", MCP_TOKEN); requestBuilder.Headers.TryAddWithoutValidation("X-Client-Version", "1.0.1"); requestBuilder.Headers.TryAddWithoutValidation("Accept", "text/event-stream, application/json"); if (!string.IsNullOrEmpty(mcpSessionId)) requestBuilder.Headers.TryAddWithoutValidation("mcp-session-id", mcpSessionId); requestBuilder.Content = new StringContent(body, Encoding.UTF8, "application/json"); using var client = new HttpClient(); var response = await client.SendAsync(requestBuilder); var responseSessionId = response.Headers.GetValues("mcp-session-id").FirstOrDefault(); if (!string.IsNullOrEmpty(responseSessionId)) mcpSessionId = responseSessionId; return await ReadStreamResponse( await response.Content.ReadAsStreamAsync(), response.Content.Headers.ContentType?.MediaType ?? ""); } private static async Task ReadStreamResponse(Stream stream, string contentType) { using var reader = new StreamReader(stream, Encoding.UTF8); var responseBody = new StringBuilder(); bool isSSE = contentType.Contains("text/event-stream"); if (isSSE) { string? line; while ((line = await reader.ReadLineAsync()) != null) { if (line.StartsWith("data:")) { string data = line.Substring(5).Trim(); if ("[DONE]".Equals(data)) break; responseBody.Append(data); } } } else { responseBody.Append(await reader.ReadToEndAsync()); } return responseBody.ToString().Trim(); } public static async Task Main(string[] args) { await McpCallStream("initialize", "{\"protocolVersion\": \"2025-11-25\"}"); await McpCallStream("notifications/initialized", "{}"); await McpCallStream("tools/list", "{}"); await McpCallStream("tools/call", "{\"name\":\"enterprise_query_state\",\"arguments\":{\"nsrsbh\":\"91500112MAXXXXX\",\"username\":\"1325580xxxx\"}}"); } } } ```
非流式调用 ```csharp using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace Tax.Invoice.Example { public class McpClient { private const string MCP_URL = "https://mcp.fa-piao.com"; private const string MCP_TOKEN = "YOUR_MCP_TOKEN"; private static string? mcpSessionId = null; private static int requestId = 0; private static readonly object lockObj = new object(); private static int NextRequestId() { lock (lockObj) { return ++requestId; } } public static async Task McpCall(string method, string? paramsJson = null) { int currentId = NextRequestId(); string body = string.Format( "{{\"jsonrpc\":\"2.0\",\"id\":{0},\"method\":\"{1}\",\"params\":{2}}}", currentId, method, !string.IsNullOrEmpty(paramsJson) ? paramsJson : "{}" ); using var requestBuilder = new HttpRequestMessage(HttpMethod.Post, MCP_URL); requestBuilder.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", MCP_TOKEN); requestBuilder.Headers.TryAddWithoutValidation("X-Client-Version", "1.0.1"); if (!string.IsNullOrEmpty(mcpSessionId)) requestBuilder.Headers.TryAddWithoutValidation("mcp-session-id", mcpSessionId); requestBuilder.Content = new StringContent(body, Encoding.UTF8, "application/json"); using var client = new HttpClient(); var response = await client.SendAsync(requestBuilder); var responseSessionId = response.Headers.GetValues("mcp-session-id").FirstOrDefault(); if (!string.IsNullOrEmpty(responseSessionId)) mcpSessionId = responseSessionId; return await response.Content.ReadAsStringAsync(); } public static async Task Main(string[] args) { await McpCall("initialize", "{\"protocolVersion\": \"2025-11-25\"}"); await McpCall("tools/call", "{\"name\":\"enterprise_query_state\",\"arguments\":{\"nsrsbh\":\"91500112MAXXXX\",\"username\":\"1325580xxxx\"}}"); } } } ```
### PHP
流式调用 ```php '2.0', 'id' => $currentId, 'method' => $method, 'params' => json_decode($paramsJson, true) ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $headers = [ 'Authorization: Bearer ' . self::$MCP_TOKEN, 'X-Client-Version: 1.0.1', 'Content-Type: application/json', 'Accept: text/event-stream, application/json' ]; if (self::$mcpSessionId !== null && self::$mcpSessionId !== '') { $headers[] = 'mcp-session-id: ' . self::$mcpSessionId; } $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => self::$MCP_URL, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body, CURLOPT_HTTPHEADER => $headers, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_TIMEOUT => 30, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); if (curl_errno($ch)) { curl_close($ch); throw new Exception("cURL Error: " . curl_error($ch)); } curl_close($ch); $responseHeaders = substr($response, 0, $headerSize); $responseBody = trim(substr($response, $headerSize)); preg_match_all('/^mcp-session-id:\s*(.+)$/im', $responseHeaders, $matches); if (!empty($matches[1])) { self::$mcpSessionId = trim($matches[1][0]); } return $responseBody; } public static function initialize() { return self::mcpCallStream("initialize", '{"protocolVersion": "2025-11-25"}'); } public static function toolsCall($toolName, $arguments) { $params = json_encode(['name' => $toolName, 'arguments' => $arguments], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); return self::mcpCallStream("tools/call", $params); } } // 使用示例 McpStreamClient::initialize(); McpStreamClient::toolsCall("enterprise_query_state", [ "nsrsbh" => "91500112MAXXXXX", "username" => "1325580xxxx" ]); ```
非流式调用 ```php '2.0', 'id' => $currentId, 'method' => $method, 'params' => json_decode($paramsJson, true) ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $headers = [ 'Authorization: Bearer ' . self::$MCP_TOKEN, 'X-Client-Version: 1.0.1', 'Content-Type: application/json' ]; if (self::$mcpSessionId !== null && self::$mcpSessionId !== '') { $headers[] = 'mcp-session-id: ' . self::$mcpSessionId; } $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => self::$MCP_URL, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body, CURLOPT_HTTPHEADER => $headers, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_TIMEOUT => 30, ]); $response = curl_exec($ch); $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); if (curl_errno($ch)) { curl_close($ch); throw new Exception("cURL Error: " . curl_error($ch)); } curl_close($ch); $responseHeaders = substr($response, 0, $headerSize); $responseBody = trim(substr($response, $headerSize)); preg_match_all('/^mcp-session-id:\s*(.+)$/im', $responseHeaders, $matches); if (!empty($matches[1])) { self::$mcpSessionId = trim($matches[1][0]); } return $responseBody; } public static function initialize() { return self::mcpCall("initialize", '{"protocolVersion": "2025-11-25"}'); } public static function toolsCall($toolName, $arguments) { $params = json_encode(['name' => $toolName, 'arguments' => $arguments], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); return self::mcpCall("tools/call", $params); } } // 使用示例 McpClient::initialize(); McpClient::toolsCall("enterprise_query_state", [ "nsrsbh" => "91500112MAXXXX", "username" => "1325580xxxx" ]); ```
### Rust
流式调用 ```rust use std::io::{self, BufRead}; use std::sync::atomic::{AtomicI32, Ordering}; use std::sync::Mutex; use reqwest::header::{HeaderMap, HeaderName, HeaderValue, ACCEPT, AUTHORIZATION, CONTENT_TYPE}; const MCP_URL: &str = "https://mcp.fa-piao.com"; const MCP_TOKEN: &str = "YOUR_MCP_TOKEN"; const CLIENT_VERSION: &str = "1.0.1"; lazy_static::lazy_static! { static ref MCP_SESSION_ID: Mutex> = Mutex::new(None); } static REQUEST_ID: AtomicI32 = AtomicI32::new(0); fn get_session_id() -> Option { MCP_SESSION_ID.lock().unwrap().clone() } fn set_session_id(session_id: Option) { *MCP_SESSION_ID.lock().unwrap() = session_id; } fn next_request_id() -> i32 { REQUEST_ID.fetch_add(1, Ordering::SeqCst) + 1 } async fn read_stream_response( body: reqwest::Response, content_type: &str, ) -> Result> { let is_sse = content_type.contains("text/event-stream"); if is_sse { let mut response_body = String::new(); let reader = io::BufReader::new(body.bytes().await?); for line in reader.lines() { let line = line?; if line.starts_with("data:") { let data = line[5..].trim().to_string(); if data == "[DONE]" { break; } response_body.push_str(&data); } } Ok(response_body.trim().to_string()) } else { let bytes = body.bytes().await?; Ok(String::from_utf8(bytes.to_vec())?.trim().to_string()) } } pub async fn mcp_call_stream( method: &str, params: Option<&str>, ) -> Result> { let current_id = next_request_id(); let params_json = params.unwrap_or("{}"); let body = format!( r#"{{"jsonrpc":"2.0","id":{},"method":"{}","params":{}}}"#, current_id, method, params_json ); let mut headers = HeaderMap::new(); headers.insert(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {}", MCP_TOKEN))?); headers.insert(HeaderName::from_static("x-client-version"), HeaderValue::from_static(CLIENT_VERSION)); headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); headers.insert(ACCEPT, HeaderValue::from_static("text/event-stream, application/json")); if let Some(session_id) = get_session_id() { headers.insert(HeaderName::from_static("mcp-session-id"), HeaderValue::from_str(&session_id)?); } let client = reqwest::Client::new(); let response = client.post(MCP_URL).headers(headers).body(body).send().await?; let response_session_id = response.headers() .get(HeaderName::from_static("mcp-session-id")) .and_then(|v| v.to_str().ok()) .map(|s| s.to_string()); set_session_id(response_session_id); let content_type = response.headers() .get(CONTENT_TYPE) .and_then(|v| v.to_str().ok()) .unwrap_or(""); read_stream_response(response, content_type).await } #[tokio::main] async fn main() -> Result<(), Box> { mcp_call_stream("initialize", Some(r#"{"protocolVersion": "2025-11-25"}"#)).await?; mcp_call_stream("notifications/initialized", Some("{}")).await?; mcp_call_stream("tools/list", Some("{}")).await?; mcp_call_stream("tools/call", Some(r#"{"name":"enterprise_query_state","arguments":{"nsrsbh":"91500112MAXXXXX","username":"1325580xxxx"}}"#)).await?; Ok(()) } ```
非流式调用 ```rust use std::sync::atomic::{AtomicI32, Ordering}; use std::sync::Mutex; use reqwest::header::{HeaderMap, HeaderName, HeaderValue, AUTHORIZATION, CONTENT_TYPE}; const MCP_URL: &str = "https://mcp.fa-piao.com"; const MCP_TOKEN: &str = "YOUR_MCP_TOKEN"; const CLIENT_VERSION: &str = "1.0.1"; lazy_static::lazy_static! { static ref MCP_SESSION_ID: Mutex> = Mutex::new(None); } static REQUEST_ID: AtomicI32 = AtomicI32::new(0); fn get_session_id() -> Option { MCP_SESSION_ID.lock().unwrap().clone() } fn set_session_id(session_id: Option) { *MCP_SESSION_ID.lock().unwrap() = session_id; } fn next_request_id() -> i32 { REQUEST_ID.fetch_add(1, Ordering::SeqCst) + 1 } pub async fn mcp_call( method: &str, params: Option<&str>, ) -> Result> { let current_id = next_request_id(); let params_json = params.unwrap_or("{}"); let body = format!( r#"{{"jsonrpc":"2.0","id":{},"method":"{}","params":{}}}"#, current_id, method, params_json ); let mut headers = HeaderMap::new(); headers.insert(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {}", MCP_TOKEN))?); headers.insert(HeaderName::from_static("x-client-version"), HeaderValue::from_static(CLIENT_VERSION)); headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); if let Some(session_id) = get_session_id() { headers.insert(HeaderName::from_static("mcp-session-id"), HeaderValue::from_str(&session_id)?); } let client = reqwest::Client::new(); let response = client.post(MCP_URL).headers(headers).body(body).send().await?; let response_session_id = response.headers() .get(HeaderName::from_static("mcp-session-id")) .and_then(|v| v.to_str().ok()) .map(|s| s.to_string()); set_session_id(response_session_id); Ok(response.text().await?.trim().to_string()) } #[tokio::main] async fn main() -> Result<(), Box> { mcp_call("initialize", Some(r#"{"protocolVersion": "2025-11-25"}"#)).await?; mcp_call("tools/call", Some(r#"{"name":"enterprise_query_state","arguments":{"nsrsbh":"91500112MAXXXX","username":"1325580xxxx"}}"#)).await?; Ok(()) } ```
--- ## 平台接入教程 ### Cherry Studio 1. 打开 Cherry Studio,进入设置页面 2. 选择 **「MCP」选项卡** 3. 点击 **「添加」按钮** 4. 选择 **「从 JSON 导入」**,粘贴 MCP Server 配置 JSON 5. **替换 `YOUR_MCP_TOKEN`** 为实际 Token,点击确定 6. 打开 **启用开关**,即可在聊天窗口中使用 > 参考:[Cherry Studio MCP 文档](https://docs.cherry-ai.com/advanced-basic/mcp) ### Cursor 1. 打开 Cursor,点击 **【设置】→【Tools & MCP】** 2. 点击 **【Add Custom MCP】**,在 `mcp.json` 中填入配置 3. **替换 `YOUR_MCP_TOKEN`** 为实际 Token,保存 4. 确认服务状态显示 **【已连接】** 5. 按 `CTRL/CMD + L` 打开 Agent 对话框,开始使用 > 参考:[Cursor MCP 文档](https://cursor.com/cn/docs/context/mcp) ### TRAE 1. 打开 Trae,点击 **【设置】→【MCP】→【手动添加】** 2. 填入 MCP Server 配置 JSON 3. **替换 `YOUR_MCP_TOKEN`** 为实际 Token,点击确认 4. 确认服务状态显示 **【已连接】** 5. 选择 **【Builder with MCP】** 模式,开始使用 > 参考:[TRAE MCP 文档](https://docs.trae.cn/ide/model-context-protocol) --- ## 典型应用场景 | 场景 | 说明 | |------|------| | 💬 **对话式开票** | 对大模型说"帮我开一张发票",AI 自动完成全流程 | | 📊 **批量智能处理** | 上传 Excel 订单列表,AI 逐条解析并批量开票 | | 🔍 **智能发票查验** | AI 自动识别发票信息并调用查验接口 | | 🔄 **自动化财税流程** | 结合 RPA 和 AI,实现订单→开票→入账全流程自动化 | | 🏗️ **企业 AI 中台集成** | 将发票 API 作为企业 AI 中台的标准工具 | | 📱 **AI 客服自动开票** | 智能客服机器人直接为用户开具发票 | --- ## 常见问题
什么是 MCP (Model Context Protocol)? MCP 是由 Anthropic 推出的开放标准协议,用于规范 AI 大模型与外部工具/API 之间的通信。fa-piao.com 的 MCP Server 实现了 Streamable HTTP 传输方式,支持实时流式响应。
支持哪些大模型平台? 支持所有兼容 MCP 协议的大模型平台,包括 Claude Desktop、Cursor、Cherry Studio、TRAE、Windsurf 等。只要平台支持 MCP 协议,即可直接使用。
MCP Server 的安全性如何保障? - API Key 认证,每个请求需携带有效 Token - 权限隔离,不同 API Key 可配置不同操作权限 - TLS 1.3 加密传输 - 请求频率限制,防止滥用 - 完整的操作审计日志
使用 MCP Server 和直接调用 API 有什么区别? MCP Server 让您通过**自然语言**与大模型交互来完成开票,无需编写代码。两者底层调用的是同一套发票 API,功能和性能完全一致。MCP Server 更适合非技术用户和追求效率的场景。
MCP Server 是否包含在所有定价方案中? 是的,MCP Server 功能**包含在所有定价方案中**,包括 1 分钱试用版。
--- ## 相关链接 | 资源 | 链接 | |------|------| | 🌐 官网 | [fa-piao.com](https://fa-piao.com) | | 📖 接口文档 | [fa-piao.com/doc.html](https://fa-piao.com/doc.html) | | 💰 价格方案 | [fa-piao.com/pricing.html](https://fa-piao.com/pricing.html) | | 📦 SDK 资源 | [fa-piao.com/sdk.html](https://fa-piao.com/sdk.html) | | 🔑 申请 Token | [open.fa-piao.com](https://open.fa-piao.com) | | 💬 企业微信客服 | [work.weixin.qq.com](https://work.weixin.qq.com/kfid/kfc6f5234eb39061e09) | ---

© 2025 fa-piao.com | 数电发票 API · 电子发票接口 · AI 开票 · MCP Server