/**
* Feishu MCP Server - Constants
*/
// ============================================================================
// API Endpoints
// ============================================================================
export const FEISHU_API_BASE = 'https://open.feishu.cn/open-apis'
export const API_ENDPOINTS = {
// Auth
TENANT_ACCESS_TOKEN: '/auth/v3/tenant_access_token/internal',
// Document
CREATE_DOCUMENT: '/docx/v1/documents',
GET_DOCUMENT: (documentId: string) => `/docx/v1/documents/${documentId}`,
GET_RAW_CONTENT: (documentId: string) =>
`/docx/v1/documents/${documentId}/raw_content`,
GET_BLOCKS: (documentId: string) => `/docx/v1/documents/${documentId}/blocks`,
CREATE_BLOCK: (documentId: string, blockId: string) =>
`/docx/v1/documents/${documentId}/blocks/${blockId}/children`,
UPDATE_BLOCK: (documentId: string, blockId: string) =>
`/docx/v1/documents/${documentId}/blocks/${blockId}`,
// 删除块:DELETE /docx/v1/documents/:document_id/blocks/:block_id/children/batch_delete
DELETE_BLOCK_CHILDREN: (documentId: string, blockId: string) =>
`/docx/v1/documents/${documentId}/blocks/${blockId}/children/batch_delete`,
BATCH_UPDATE_BLOCKS: (documentId: string) =>
`/docx/v1/documents/${documentId}/blocks/batch_update`,
} as const
// ============================================================================
// Token Management
// ============================================================================
/** Token refresh buffer - refresh 5 minutes before expiry */
export const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000
/** Default token expiry (2 hours) */
export const DEFAULT_TOKEN_EXPIRY_MS = 2 * 60 * 60 * 1000
// ============================================================================
// Code Language Mapping
// ============================================================================
/** Map language names to Feishu language codes */
export const LANGUAGE_MAP: Record<string, number> = {
plaintext: 1,
abap: 2,
ada: 3,
apache: 4,
apex: 5,
assembly: 6,
bash: 7,
shell: 7,
csharp: 8,
'c#': 8,
cpp: 9,
'c++': 9,
c: 10,
cobol: 11,
css: 12,
coffeescript: 13,
d: 14,
dart: 15,
delphi: 16,
django: 17,
dockerfile: 18,
erlang: 19,
fortran: 20,
foxpro: 21,
go: 22,
golang: 22,
groovy: 23,
html: 24,
htmlbars: 25,
http: 26,
haskell: 27,
json: 28,
java: 29,
javascript: 30,
js: 30,
julia: 31,
kotlin: 32,
latex: 33,
lisp: 34,
lua: 36,
matlab: 37,
makefile: 38,
markdown: 39,
nginx: 40,
objectivec: 41,
'objective-c': 41,
openedgeabl: 42,
php: 43,
perl: 44,
postscript: 45,
powershell: 46,
prolog: 47,
protobuf: 48,
python: 49,
py: 49,
r: 50,
rpm: 51,
ruby: 52,
rust: 53,
sas: 54,
scss: 55,
sql: 56,
scala: 57,
scheme: 58,
scratch: 59,
solidity: 60,
swift: 61,
thrift: 62,
typescript: 63,
ts: 63,
vbscript: 64,
visual_basic: 65,
vb: 65,
xml: 66,
yaml: 67,
yml: 67,
cmake: 68,
diff: 69,
gherkin: 70,
graphql: 71,
openglsl: 72,
properties: 73,
solr: 74,
toml: 75,
// Note: Mermaid is not a supported language code in Feishu API (max is 75)
// Mermaid code blocks will be created as plaintext
mermaid: 1, // Use plaintext for mermaid since Feishu doesn't support it as a language
}
/** Get language code from name, defaults to plaintext */
export function getLanguageCode(language: string): number {
const normalized = language.toLowerCase().trim()
return LANGUAGE_MAP[normalized] ?? 1 // Default to plaintext
}
// ============================================================================
// Block Type Helpers
// ============================================================================
import { BlockType } from './types.js'
/** Map markdown heading level to block type */
export function getHeadingBlockType(level: number): BlockType {
const headingMap: Record<number, BlockType> = {
1: BlockType.Heading1,
2: BlockType.Heading2,
3: BlockType.Heading3,
4: BlockType.Heading4,
5: BlockType.Heading5,
6: BlockType.Heading6,
7: BlockType.Heading7,
8: BlockType.Heading8,
9: BlockType.Heading9,
}
return headingMap[level] ?? BlockType.Heading1
}
/** Get heading property name from level */
export function getHeadingPropertyName(
level: number
): `heading${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}` {
const validLevel = Math.min(Math.max(level, 1), 9) as 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
return `heading${validLevel}`
}
// ============================================================================
// URL Parsing
// ============================================================================
/** Extract document ID from Feishu URL or return as-is if already an ID */
export function extractDocumentId(input: string): string {
// If it's a URL, extract the document ID
const urlPatterns = [
// Wiki URL: https://xxx.feishu.cn/wiki/ABC123
/feishu\.cn\/wiki\/([a-zA-Z0-9]+)/,
// Docx URL: https://xxx.feishu.cn/docx/ABC123
/feishu\.cn\/docx\/([a-zA-Z0-9]+)/,
// Doc URL: https://xxx.feishu.cn/docs/ABC123
/feishu\.cn\/docs\/([a-zA-Z0-9]+)/,
]
for (const pattern of urlPatterns) {
const match = input.match(pattern)
if (match) {
return match[1]
}
}
// Return as-is if not a URL (assume it's already an ID)
return input.trim()
}
// ============================================================================
// Error Messages
// ============================================================================
export const ERROR_MESSAGES = {
MISSING_CREDENTIALS:
'Missing Feishu credentials. Set FEISHU_APP_ID and FEISHU_APP_SECRET environment variables.',
TOKEN_FETCH_FAILED: 'Failed to fetch tenant access token',
DOCUMENT_NOT_FOUND: 'Document not found',
PERMISSION_DENIED: 'Permission denied. Check app permissions.',
RATE_LIMITED: 'Rate limited. Please wait and try again.',
NETWORK_ERROR: 'Network error. Check your connection.',
INVALID_DOCUMENT_ID: 'Invalid document ID or URL',
} as const
// ============================================================================
// API Error Codes
// ============================================================================
export const ERROR_CODES = {
SUCCESS: 0,
INVALID_TOKEN: 99991663,
TOKEN_EXPIRED: 99991661,
PERMISSION_DENIED: 99991668,
DOCUMENT_NOT_FOUND: 1254002,
RATE_LIMITED: 99991400,
} as const