A community-driven registry for Claude, Cursor, Windsurf, Cline & more. Not affiliated with Anthropic.
Are you the author? Sign in to claim
电子发票MCP 数电发票MCP
让 AI 大模型成为您的开票助手 — 通过 MCP (Model Context Protocol) 协议,将电子发票/数电发票 API 接入 Claude、GPT、Cursor、Cherry Studio 等大模型平台,实现 AI 智能开票。
MCP(Model Context Protocol)是由 Anthropic 推出的开放标准协议,定义了 AI 大模型如何安全、标准化地调用外部工具和 API。
fa-piao.com 率先实现了 MCP Server (Streamable HTTP),让任何支持 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 开关,完整请求/响应日志 |
访问 open.fa-piao.com 注册并登录,申请 MCP Token。
在大模型客户端的配置文件中添加以下 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。
配置完成后,即可通过自然语言与大模型交互,例如:
| 配置项 | 值 | 说明 |
|---|---|---|
| 协议类型 | 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 协议,支持流式和非流式两种调用模式。
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"}
})
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))
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<string | null> {
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<string, string> = {
'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);
}
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<string> {
const currentId = nextRequestId();
const body = JSON.stringify({
jsonrpc: '2.0',
id: currentId,
method: method,
params: params ? JSON.parse(params) : {}
});
const headers: Record<string, string> = {
'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);
}
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<String> 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<InputStream> 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\"}}");
}
}
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<String> 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<String> 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\"}}");
}
}
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"}}`)
}
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"}}`)
}
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<string> 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<string> 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\"}}");
}
}
}
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<string> 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
class McpStreamClient
{
const DEBUG = false;
private static $MCP_URL = "https://mcp.fa-piao.com";
private static $MCP_TOKEN = "YOUR_MCP_TOKEN";
private static $mcpSessionId = null;
private static $requestId = 0;
private static function nextRequestId()
{
return ++self::$requestId;
}
public static function mcpCallStream($method, $params = null)
{
$currentId = self::nextRequestId();
$paramsJson = $params !== null ? $params : '{}';
$body = json_encode([
'jsonrpc' => '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
class McpClient
{
const DEBUG = true;
private static $MCP_URL = "https://mcp.fa-piao.com";
private static $MCP_TOKEN = "YOUR_MCP_TOKEN";
private static $mcpSessionId = null;
private static $requestId = 0;
private static function nextRequestId()
{
return ++self::$requestId;
}
public static function mcpCall($method, $params = null)
{
$currentId = self::nextRequestId();
$paramsJson = $params !== null ? $params : '{}';
$body = json_encode([
'jsonrpc' => '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"
]);
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<Option<String>> = Mutex::new(None);
}
static REQUEST_ID: AtomicI32 = AtomicI32::new(0);
fn get_session_id() -> Option<String> {
MCP_SESSION_ID.lock().unwrap().clone()
}
fn set_session_id(session_id: Option<String>) {
*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<String, Box<dyn std::error::Error>> {
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<String, Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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(())
}
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<Option<String>> = Mutex::new(None);
}
static REQUEST_ID: AtomicI32 = AtomicI32::new(0);
fn get_session_id() -> Option<String> {
MCP_SESSION_ID.lock().unwrap().clone()
}
fn set_session_id(session_id: Option<String>) {
*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<String, Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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(())
}
YOUR_MCP_TOKEN 为实际 Token,点击确定mcp.json 中填入配置YOUR_MCP_TOKEN 为实际 Token,保存CTRL/CMD + L 打开 Agent 对话框,开始使用YOUR_MCP_TOKEN 为实际 Token,点击确认参考:TRAE MCP 文档
| 场景 | 说明 |
|---|---|
| 💬 对话式开票 | 对大模型说"帮我开一张发票",AI 自动完成全流程 |
| 📊 批量智能处理 | 上传 Excel 订单列表,AI 逐条解析并批量开票 |
| 🔍 智能发票查验 | AI 自动识别发票信息并调用查验接口 |
| 🔄 自动化财税流程 | 结合 RPA 和 AI,实现订单→开票→入账全流程自动化 |
| 🏗️ 企业 AI 中台集成 | 将发票 API 作为企业 AI 中台的标准工具 |
| 📱 AI 客服自动开票 | 智能客服机器人直接为用户开具发票 |
MCP 是由 Anthropic 推出的开放标准协议,用于规范 AI 大模型与外部工具/API 之间的通信。fa-piao.com 的 MCP Server 实现了 Streamable HTTP 传输方式,支持实时流式响应。
支持所有兼容 MCP 协议的大模型平台,包括 Claude Desktop、Cursor、Cherry Studio、TRAE、Windsurf 等。只要平台支持 MCP 协议,即可直接使用。
MCP Server 让您通过自然语言与大模型交互来完成开票,无需编写代码。两者底层调用的是同一套发票 API,功能和性能完全一致。MCP Server 更适合非技术用户和追求效率的场景。
是的,MCP Server 功能包含在所有定价方案中,包括 1 分钱试用版。
| 资源 | 链接 |
|---|---|
| 🌐 官网 | fa-piao.com |
| 📖 接口文档 | fa-piao.com/doc.html |
| 💰 价格方案 | fa-piao.com/pricing.html |
| 📦 SDK 资源 | fa-piao.com/sdk.html |
| 🔑 申请 Token | open.fa-piao.com |
| 💬 企业微信客服 | work.weixin.qq.com |
© 2025 fa-piao.com | 数电发票 API · 电子发票接口 · AI 开票 · MCP Server
mcp-language-server gives MCP enabled clients access semantic tools like get definition, references, rename, and diagnos
MCP server integration for DaVinci Resolve Studio
Run Claude Code as an MCP server so any agent can delegate coding tasks to it