111 lines
6.5 KiB
Python
111 lines
6.5 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
使用飞书 MCP 创建云文档
|
||
"""
|
||
import requests
|
||
import json
|
||
import sys
|
||
import time
|
||
|
||
# 飞书应用凭证
|
||
APP_ID = "cli_a93815b250b9dcb5"
|
||
# 用户访问令牌(从解密获取)
|
||
USER_ACCESS_TOKEN = "eyJhbGciOiJFUzI1NiIsImZlYXR1cmVfY29kZSI6IkZlYXR1cmVPQXV0aEpXVFNpZ25fQ04iLCJraWQiOiI3NjE2ODk0MzA1NzE3OTE0NTczIiwidHlwIjoiSldUIn0.eyJqdGkiOiI3NjE4Mjk3MjkyNDM2NTczMTE1IiwiaWF0IjoxNzczNzczMDYyLCJleHAiOjE3NzM3ODAyNjIsInZlciI6InYxIiwidHlwIjoiYWNjZXNzX3Rva2VuIiwiY2xpZW50X2lkIjoiY2xpX2E5MzgxNWIyNTBiOWRjYjUiLCJzY29wZSI6ImF1dGg6dXNlci5pZDpyZWFkIGJhc2U6YXBwOmNvcHkgYmFzZTphcHA6Y3JlYXRlIGJhc2U6YXBwOnJlYWQgYmFzZTphcHA6dXBkYXRlIGJhc2U6ZmllbGQ6Y3JlYXRlIGJhc2U6ZmllbGQ6ZGVsZXRlIGJhc2U6ZmllbGQ6cmVhZCBiYXNlOmZpZWxkOnVwZGF0ZSBiYXNlOnJlY29yZDpjcmVhdGUgYmFzZTpyZWNvcmQ6ZGVsZXRlIGJhc2U6cmVjb3JkOnJldHJpZXZlIGJhc2U6cmVjb3JkOnVwZGF0ZSBiYXNlOnRhYmxlOmNyZWF0ZSBiYXNlOnRhYmxlOmRlbGV0ZSBiYXNlOnRhYmxlOnJlYWQgYmFzZTp0YWJsZTp1cGRhdGUgYmFzZTp2aWV3OnJlYWQgYmFzZTp2aWV3OndyaXRlX29ubHkgYm9hcmQ6d2hpdGVib2FyZDpub2RlOmNyZWF0ZSBib2FyZDp3aGl0ZWJvYXJkOm5vZGU6cmVhZCBjYWxlbmRhcjpjYWxlbmRhci5ldmVudDpjcmVhdGUgY2FsZW5kYXI6Y2FsZW5kYXIuZXZlbnQ6ZGVsZXRlIGNhbGVuZGFyOmNhbGVuZGFyLmV2ZW50OnJlYWQgY2FsZW5kYXI6Y2FsZW5kYXIuZXZlbnQ6cmVwbHkgY2FsZW5kYXI6Y2FsZW5kYXIuZXZlbnQ6dXBkYXRlIGNhbGVuZGFyOmNhbGVuZGFyLmZyZWVfYnVzeTpyZWFkIGNhbGVuZGFyOmNhbGVuZGFyOnJlYWQgY29udGFjdDpjb250YWN0LmJhc2U6cmVhZG9ubHkgY29udGFjdDp1c2VyLmJhc2U6cmVhZG9ubHkgY29udGFjdDp1c2VyLmJhc2ljX3Byb2ZpbGU6cmVhZG9ubHkgY29udGFjdDp1c2VyLmVtcGxveWVlX2lkOnJlYWRvbmx5IGNvbnRhY3Q6dXNlcjpzZWFyY2ggZG9jczpkb2N1bWVudC5jb21tZW50OmNyZWF0ZSBkb2NzOmRvY3VtZW50LmNvbW1lbnQ6cmVhZCBkb2NzOmRvY3VtZW50LmNvbW1lbnQ6dXBkYXRlIGRvY3M6ZG9jdW1lbnQubWVkaWE6ZG93bmxvYWQgZG9jczpkb2N1bWVudC5tZWRpYTp1cGxvYWQgZG9jczpkb2N1bWVudDpjb3B5IGRvY3M6ZG9jdW1lbnQ6ZXhwb3J0IGRvY3g6ZG9jdW1lbnQ6Y3JlYXRlIGRvY3g6ZG9jdW1lbnQ6cmVhZG9ubHkgZG9jeDpkb2N1bWVudDp3cml0ZV9vbmx5IGRyaXZlOmRyaXZlLm1ldGFkYXRhOnJlYWRvbmx5IGRyaXZlOmZpbGU6ZG93bmxvYWQgZHJpdmU6ZmlsZTp1cGxvYWQgaW06Y2hhdC5tZW1iZXJzOnJlYWQgaW06Y2hhdDpyZWFkIGltOm1lc3NhZ2UgaW06bWVzc2FnZS5ncm91cF9tc2c6Z2V0X2FzX3VzZXIgaW06bWVzc2FnZS5wMnBfbXNnOmdldF9hc191c2VyIGltOm1lc3NhZ2U6cmVhZG9ubHkgc2VhcmNoOmRvY3M6cmVhZCBzZWFyY2g6bWVzc2FnZSBzaGVldHM6c3ByZWFkc2hlZXQubWV0YTpyZWFkIHNoZWV0czpzcHJlYWRzaGVldDpjcmVhdGUgc2hlZXRzOnNwcmVhZHNoZWV0OnJlYWQgc2hlZXRzOnNwcmVhZHNoZWV0OndyaXRlX29ubHkgc3BhY2U6ZG9jdW1lbnQ6ZGVsZXRlIHNwYWNlOmRvY3VtZW50Om1vdmUgc3BhY2U6ZG9jdW1lbnQ6cmV0cmlldmUgdGFzazpjb21tZW50OnJlYWQgdGFzazpjb21tZW50OndyaXRlIHRhc2s6dGFzazpyZWFkIHRhc2s6dGFzazp3cml0ZSB0YXNrOnRhc2s6d3JpdGVvbmx5IHRhc2s6dGFza2xpc3Q6cmVhZCB0YXNrOnRhc2tsaXN0OndyaXRlIHdpa2k6bm9kZTpjb3B5IHdpa2k6bm9kZTpjcmVhdGUgd2lraTpub2RlOm1vdmUgd2lraTpub2RlOnJlYWQgd2lraTpub2RlOnJldHJpZXZlIHdpa2k6c3BhY2U6cmVhZCB3aWtpOnNwYWNlOnJldHJpZXZlIHdpa2k6c3BhY2U6d3JpdGVfb25seSBvZmZsaW5lX2FjY2VzcyIsImF1dGhfaWQiOiI3NjE4Mjk3MjY2ODA5MzU5NTUzIiwiYXV0aF90aW1lIjoxNzczNzczMDYyLCJhdXRoX2V4cCI6MTgwNTMwOTA2MiwidW5pdCI6ImV1X25jIiwidGVuYW50X3VuaXQiOiJldV9uYyIsIm9wYXF1ZSI6dHJ1ZSwiZW5jIjoiQWlRa0FRRUNBTUlEQUFFQkF3QUNBUTBBQXdzTEFBQUFBd0FBQUFkR1pXRjBkWEpsQUFBQUVHOWhkWFJvWDI5d1lYRjFaVjlxZDNRQUFBQUlWR1Z1WVc1MFNXUUFBQUFCTUFBQUFBUlVhVzFsQUFBQUNqRTNOek0yTVRreU1EQVBBQVFNQUFBQUFRb0FBV0xGSWxtdmdBQWlDd0FDQUFBQURFNkdsUjZPN0VpWjJQeGh0UXNBQXdBQUFEQmFqSDRQSmx6d2lISDZybXlCZVg4TWRjSHAxc2t0U3R6c3h2U1F5NUtSL2JEYlJhMnhoeVA5RHVYWDFvUTRvaFVBQ3dBRkFBQUFCV1YxWDI1akFIakhLdlpTb3B4bGs2REJYb0xocVlKc1NoMVczQnJvVUZIL2kvd0hqUHpHalE1VjJRaVJlUXdxYzBHOE9wRTRjdXFkNk02QmluUUozdlpaK0JYdWo0SzlwOWdBMDRwZVF3TW5vSjlEY0l4SENlSGFCRmZSdmx5Wk5iZCtBY2RGZUxHRUdLZWNicTM4cUVGNE40bzlLYmRrWWZNUThFYU91bEhrcjh5WkFXWmdwczycllLVFBiNzFzTjVwS09mcU9aRmRvdz09IiwiZW5jX3ZlciI6InYxIn0.x-4ybLikzQ6WQEATSVbffwJGDKrTNYMydBp8RZtSAMuaCL1SUa8Wec7v6OBx0rLN3S4jEXMp9htJRsYZB09L5Q"
|
||
|
||
# MCP 端点
|
||
MCP_ENDPOINT = "https://mcp.feishu.cn/mcp"
|
||
|
||
def call_mcp_create_doc(title, markdown):
|
||
"""调用 MCP create-doc 工具"""
|
||
tool_call_id = f"call_{int(time.time() * 1000)}"
|
||
|
||
# JSON-RPC 2.0 请求
|
||
payload = {
|
||
"jsonrpc": "2.0",
|
||
"id": tool_call_id,
|
||
"method": "tools/call",
|
||
"params": {
|
||
"name": "create-doc",
|
||
"arguments": {
|
||
"title": title,
|
||
"markdown": markdown
|
||
}
|
||
}
|
||
}
|
||
|
||
headers = {
|
||
"Content-Type": "application/json",
|
||
"X-Lark-MCP-UAT": USER_ACCESS_TOKEN,
|
||
"X-Lark-MCP-Allowed-Tools": "create-doc",
|
||
"User-Agent": "OpenClaw/2026.3.13"
|
||
}
|
||
|
||
print(f"正在调用 MCP create-doc 工具...")
|
||
print(f"MCP 端点:{MCP_ENDPOINT}")
|
||
print(f"工具调用 ID: {tool_call_id}")
|
||
|
||
resp = requests.post(MCP_ENDPOINT, json=payload, headers=headers, timeout=30)
|
||
print(f"HTTP 状态码:{resp.status_code}")
|
||
|
||
if not resp.ok:
|
||
print(f"响应内容:{resp.text[:500]}")
|
||
raise Exception(f"MCP HTTP {resp.status_code} {resp.reason}")
|
||
|
||
data = resp.json()
|
||
print(f"MCP 响应:{json.dumps(data, indent=2, ensure_ascii=False)[:1000]}")
|
||
|
||
if "error" in data:
|
||
raise Exception(f"MCP error {data['error'].get('code')}: {data['error'].get('message')}")
|
||
|
||
# 解包结果
|
||
result = data.get("result", {})
|
||
# 某些实现可能再包一层 result
|
||
if "result" in result:
|
||
result = result["result"]
|
||
|
||
content = result.get("content", [])
|
||
if content:
|
||
# 解析返回的 JSON
|
||
text_content = content[0].get("text", "")
|
||
try:
|
||
details = json.loads(text_content)
|
||
doc_token = details.get("doc_id") or details.get("doc_token")
|
||
doc_url = details.get("doc_url")
|
||
return doc_token, doc_url, details
|
||
except:
|
||
return None, None, {"raw": text_content}
|
||
|
||
return None, None, result
|
||
|
||
def main():
|
||
if len(sys.argv) < 3:
|
||
print("用法:python3 create_feishu_doc_mcp.py <title> <content_file>")
|
||
sys.exit(1)
|
||
|
||
title = sys.argv[1]
|
||
content_file = sys.argv[2]
|
||
|
||
# 读取文档内容
|
||
with open(content_file, 'r', encoding='utf-8') as f:
|
||
markdown = f.read()
|
||
|
||
print(f"创建文档:{title}")
|
||
print(f"内容来源:{content_file}")
|
||
print(f"内容长度:{len(markdown)} 字符")
|
||
|
||
# 调用 MCP 创建文档
|
||
doc_token, doc_url, details = call_mcp_create_doc(title, markdown)
|
||
|
||
if doc_url:
|
||
print(f"\n✓ 文档创建成功!")
|
||
print(f"文档 Token: {doc_token}")
|
||
print(f"文档 URL: {doc_url}")
|
||
return doc_url
|
||
else:
|
||
print(f"\n✗ 文档创建失败")
|
||
print(f"详细信息:{details}")
|
||
return None
|
||
|
||
if __name__ == "__main__":
|
||
main()
|