skyvern run server, skyvern run mcp (#2072)

This commit is contained in:
Shuchang Zheng 2025-04-02 13:09:28 -04:00 committed by GitHub
parent d168da4653
commit c7e62f45d3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 85 additions and 33 deletions

4
skyvern/__main__.py Normal file
View file

@ -0,0 +1,4 @@
from skyvern.cli.commands import app
if __name__ == "__main__":
app()

View file

@ -1,3 +1,4 @@
import asyncio
import json import json
import os import os
import shutil import shutil
@ -6,11 +7,35 @@ import time
from typing import Optional from typing import Optional
import typer import typer
import uvicorn
from click import Choice from click import Choice
from dotenv import load_dotenv from dotenv import load_dotenv
from mcp.server.fastmcp import FastMCP
from skyvern.agent import SkyvernAgent
from skyvern.config import settings
from skyvern.schemas.runs import RunEngine
from skyvern.utils import detect_os, get_windows_appdata_roaming, migrate_db from skyvern.utils import detect_os, get_windows_appdata_roaming, migrate_db
mcp = FastMCP("Skyvern")
skyvern_agent = SkyvernAgent(
base_url=settings.SKYVERN_BASE_URL,
api_key=settings.SKYVERN_API_KEY,
)
@mcp.tool()
async def skyvern_run_task(prompt: str, url: str) -> str:
"""Browse the internet using a browser to achieve a user goal.
Args:
prompt: brief description of what the user wants to accomplish
url: the target website for the user goal
"""
res = await skyvern_agent.run_task(prompt=prompt, url=url, engine=RunEngine.skyvern_v1)
return res.model_dump()["output"]
load_dotenv() load_dotenv()
app = typer.Typer() app = typer.Typer()
@ -115,18 +140,54 @@ def setup_postgresql() -> None:
print("Database and user created successfully.") print("Database and user created successfully.")
async def _setup_local_organization() -> str:
"""
Returns the API key for the local organization generated
"""
from skyvern.forge import app
from skyvern.forge.sdk.core import security
from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType
from skyvern.forge.sdk.services.org_auth_token_service import API_KEY_LIFETIME
organization = await app.DATABASE.get_organization_by_domain("skyvern.local")
if not organization:
organization = await app.DATABASE.create_organization(
organization_name="Skyvern-local",
domain="skyvern.local",
max_steps_per_run=10,
max_retries_per_step=3,
)
api_key = security.create_access_token(
organization.organization_id,
expires_delta=API_KEY_LIFETIME,
)
# generate OrganizationAutoToken
await app.DATABASE.create_org_auth_token(
organization_id=organization.organization_id,
token=api_key,
token_type=OrganizationAuthTokenType.api,
)
org_auth_token = await app.DATABASE.get_valid_org_auth_token(
organization_id=organization.organization_id,
token_type=OrganizationAuthTokenType.api,
)
return org_auth_token.token
@app.command(name="init") @app.command(name="init")
def init( def init(
openai_api_key: str = typer.Option(..., help="The OpenAI API key"), openai_api_key: str = typer.Option(..., help="The OpenAI API key"),
log_level: str = typer.Option("CRITICAL", help="The log level"), log_level: str = typer.Option("INFO", help="The log level"),
) -> None: ) -> None:
setup_postgresql() setup_postgresql()
api_key = asyncio.run(_setup_local_organization())
# Generate .env file # Generate .env file
with open(".env", "w") as env_file: with open(".env", "w") as env_file:
env_file.write("ENABLE_OPENAI=true\n") env_file.write("ENABLE_OPENAI=true\n")
env_file.write(f"OPENAI_API_KEY={openai_api_key}\n") env_file.write(f"OPENAI_API_KEY={openai_api_key}\n")
env_file.write(f"LOG_LEVEL={log_level}\n") env_file.write(f"LOG_LEVEL={log_level}\n")
env_file.write("ARTIFACT_STORAGE_PATH=./artifacts\n") env_file.write("ARTIFACT_STORAGE_PATH=./artifacts\n")
env_file.write(f"SKYVERN_API_KEY={api_key}\n")
print(".env file created with the parameters provided.") print(".env file created with the parameters provided.")
@ -332,8 +393,8 @@ def get_command_config(host_system: str, command: str, target: str, env_vars: st
raise Exception(f"Unsupported host system: {host_system}") raise Exception(f"Unsupported host system: {host_system}")
@run_app.command(name="mcp") @run_app.command(name="setupmcp")
def run_mcp() -> None: def setup_mcp() -> None:
"""Configure MCP for different Skyvern deployments.""" """Configure MCP for different Skyvern deployments."""
host_system = detect_os() host_system = detect_os()
@ -432,3 +493,20 @@ def setup_cursor_config(host_system: str, command: str, target: str, env_vars: s
except Exception as e: except Exception as e:
print(f"Error configuring Cursor: {e}") print(f"Error configuring Cursor: {e}")
return False return False
@run_app.command(name="server")
def run_server() -> None:
port = int(os.environ.get("PORT", 8000))
uvicorn.run(
"skyvern.forge.api_app:app",
host="0.0.0.0",
port=port,
log_level="info",
)
@run_app.command(name="mcp")
def run_mcp() -> None:
"""Run the MCP server."""
mcp.run(transport="stdio")

View file

@ -1,30 +0,0 @@
import os
from mcp.server.fastmcp import FastMCP
from skyvern.agent import SkyvernAgent
mcp = FastMCP("Skyvern")
if "SKYVERN_MCP_CLOUD_URL" in os.environ and "SKYVERN_MCP_API_KEY" in os.environ:
skyvern_agent = SkyvernAgent(
base_url=os.environ.get("SKYVERN_MCP_CLOUD_URL"), api_key=os.environ.get("SKYVERN_MCP_API_KEY")
)
else:
skyvern_agent = SkyvernAgent()
@mcp.tool()
async def skyvern_run_task(prompt: str, url: str) -> str:
"""Browse the internet using a browser to achieve a user goal.
Args:
prompt: brief description of what the user wants to accomplish
url: the target website for the user goal
"""
res = await skyvern_agent.run_task(prompt=prompt, url=url)
return res.model_dump()["output"]
if __name__ == "__main__":
mcp.run(transport="stdio")