feat: sync

This commit is contained in:
Gab
2026-03-24 13:51:14 +11:00
parent 39bd38040c
commit 4596310485
20 changed files with 1356 additions and 101 deletions

View File

@@ -13,6 +13,7 @@ from toothfairyai.errors import ToothFairyError
class Region(str, Enum):
DEV = "dev"
AU = "au"
EU = "eu"
US = "us"
@@ -38,12 +39,19 @@ class FunctionRequestType(str, Enum):
# Region-specific URL configurations
REGION_URLS = {
Region.DEV: {
"base_url": "https://api.toothfairylab.link",
"ai_url": "https://ai.toothfairylab.link",
"ai_stream_url": "https://ais.toothfairylab.link",
"mcp_url": "https://mcp.toothfairylab.link/sse",
"mcp_proxy_url": "https://mcp-proxy.toothfairylab.link",
},
Region.AU: {
"base_url": "https://api.au.toothfairyai.com",
"ai_url": "https://ai.au.toothfairyai.com",
"ai_stream_url": "https://ais.au.toothfairyai.com",
"mcp_url": "https://mcp.au.toothfairyai.com/sse",
"mcp_proxy_url": "https://mcp-proxy.au.toothfairyai.com",
"base_url": "https://api.toothfairyai.com",
"ai_url": "https://ai.toothfairyai.com",
"ai_stream_url": "https://ais.toothfairyai.com",
"mcp_url": "https://mcp.toothfairyai.com/sse",
"mcp_proxy_url": "https://mcp-proxy.toothfairyai.com",
},
Region.EU: {
"base_url": "https://api.eu.toothfairyai.com",

View File

@@ -1,12 +1,17 @@
"""
MCP server sync module for tfcode.
Uses the official ToothFairyAI Python SDK.
NOTE: MCP servers are not currently exposed via the ToothFairyAI SDK.
This module is reserved for future implementation when MCP server
discovery is added to the SDK.
For now, MCP servers should be configured manually via tfcode.json.
"""
from pydantic import BaseModel
from tf_sync.config import TFConfig
from tf_sync.tools import SyncedTool, sync_tools, ToolType
from tf_sync.tools import SyncedTool, ToolType
class MCPServerSyncResult(BaseModel):
@@ -21,34 +26,16 @@ def sync_mcp_servers(config: TFConfig) -> MCPServerSyncResult:
"""
Sync MCP servers from ToothFairyAI workspace.
MCP servers are tools with isMCPServer=true.
Credentials stay in TF and are accessed via tf_proxy.
NOTE: Currently not supported. MCP servers are not exposed via the SDK.
Configure MCP servers manually in tfcode.json instead.
Args:
config: TFConfig instance
Returns:
MCPServerSyncResult with synced MCP servers
MCPServerSyncResult with error message
"""
result = sync_tools_by_type(config, [ToolType.MCP_SERVER])
if not result.success:
return MCPServerSyncResult(
success=False,
error=result.error,
)
# Get MCP servers from tools with isMCPServer
mcp_servers = [
t for t in result.tools
if t.is_mcp_server
]
return MCPServerSyncResult(
success=True,
servers=mcp_servers,
)
# Import from tools module
from tf_sync.tools import sync_tools_by_type
success=False,
error="MCP server sync not available via SDK. Configure MCP servers in tfcode.json.",
)

View File

@@ -1,7 +1,11 @@
"""
Tool sync module for tfcode.
Syncs MCP servers, Agent Skills, Database Scripts, and API Functions from ToothFairyAI workspace.
Uses the official ToothFairyAI Python SDK.
Syncs tools from ToothFairyAI workspace using the official SDK.
SDK Structure:
- agent_functions: API Functions (with request_type)
- connections: Provider connections (openai, anthropic, etc.)
- agents: TF workspace agents
"""
from typing import Any, Optional
@@ -20,13 +24,10 @@ class SyncedTool(BaseModel):
description: Optional[str] = None
tool_type: ToolType
is_mcp_server: bool = False
is_agent_skill: bool = False
is_database_script: bool = False
request_type: Optional[FunctionRequestType] = None
url: Optional[str] = None
tools: list[str] = []
authorisation_type: Optional[str] = None
auth_via: str = "tf_proxy"
@@ -40,60 +41,57 @@ class ToolSyncResult(BaseModel):
error: Optional[str] = None
def classify_tool(tool: AgentFunction) -> ToolType:
def classify_tool(func: AgentFunction) -> ToolType:
"""
Classify a tool based on its flags and fields.
Classify a tool based on its properties.
Currently the SDK exposes:
- agent_functions: API functions with request_type
Args:
tool: AgentFunction from TF SDK
func: AgentFunction from TF SDK
Returns:
ToolType enum value
"""
if tool.is_mcp_server:
return ToolType.MCP_SERVER
if tool.is_agent_skill:
return ToolType.AGENT_SKILL
if tool.is_database_script:
return ToolType.DATABASE_SCRIPT
if tool.request_type:
# All agent_functions with request_type are API Functions
if func.request_type:
return ToolType.API_FUNCTION
return ToolType.API_FUNCTION
def parse_tool(tool: AgentFunction) -> SyncedTool:
def parse_function(func: AgentFunction) -> SyncedTool:
"""
Parse AgentFunction from SDK into SyncedTool.
Args:
tool: AgentFunction from TF SDK
func: AgentFunction from TF SDK
Returns:
SyncedTool instance
"""
tool_type = classify_tool(tool)
tool_type = classify_tool(func)
request_type_enum = None
if tool.request_type:
if func.request_type:
try:
request_type_enum = FunctionRequestType(tool.request_type)
request_type_enum = FunctionRequestType(func.request_type)
except ValueError:
pass
auth_via = "user_provided" if tool_type == ToolType.API_FUNCTION else "tf_proxy"
# API Functions may have user-provided auth (authorisation_type)
# or may use TF proxy
auth_via = "user_provided" if func.authorisation_type == "api_key" else "tf_proxy"
return SyncedTool(
id=tool.id,
name=tool.name,
description=tool.description,
id=func.id,
name=func.name,
description=func.description,
tool_type=tool_type,
is_mcp_server=tool.is_mcp_server or False,
is_agent_skill=tool.is_agent_skill or False,
is_database_script=tool.is_database_script or False,
request_type=request_type_enum,
url=tool.url,
tools=[],
url=func.url,
authorisation_type=func.authorisation_type,
auth_via=auth_via,
)
@@ -112,7 +110,7 @@ def sync_tools(config: TFConfig) -> ToolSyncResult:
client = config.get_client()
result = client.agent_functions.list()
tools = [parse_tool(f) for f in result.items]
tools = [parse_function(f) for f in result.items]
by_type = {}
for tool in tools:
@@ -165,21 +163,6 @@ def sync_tools_by_type(
)
def sync_mcp_servers_only(config: TFConfig) -> ToolSyncResult:
"""Sync only MCP servers (isMCPServer=true)."""
return sync_tools_by_type(config, [ToolType.MCP_SERVER])
def sync_agent_skills_only(config: TFConfig) -> ToolSyncResult:
"""Sync only Agent Skills (isAgentSkill=true)."""
return sync_tools_by_type(config, [ToolType.AGENT_SKILL])
def sync_database_scripts_only(config: TFConfig) -> ToolSyncResult:
"""Sync only Database Scripts (isDatabaseScript=true)."""
return sync_tools_by_type(config, [ToolType.DATABASE_SCRIPT])
def sync_api_functions_only(config: TFConfig) -> ToolSyncResult:
"""Sync only API Functions (has requestType)."""
return sync_tools_by_type(config, [ToolType.API_FUNCTION])