# Directory Structure ``` ├── .github │ ├── actions │ │ └── setup-python-env │ │ └── action.yml │ └── workflows │ ├── main.yml │ ├── on-release-main.yml │ └── validate-codecov-config.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .python-version ├── .vscode │ └── settings.json ├── assets │ ├── config-cursor.png │ └── showcase.jpeg ├── codecov.yaml ├── CONTRIBUTING.md ├── Dockerfile ├── examples │ └── index.html ├── LICENSE ├── Makefile ├── pyproject.toml ├── pytest.ini ├── README.md ├── tests │ └── test_hello.py ├── tox.ini ├── uv.lock └── yourware_mcp ├── __init__.py ├── app.py ├── cli.py ├── client.py ├── credentials.py ├── log.py └── utils.py ``` # Files -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- ``` 3.12 ``` -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- ```yaml repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: "v5.0.0" hooks: - id: check-case-conflict - id: check-merge-conflict - id: check-toml - id: check-yaml - id: check-json exclude: ^.devcontainer/devcontainer.json - id: pretty-format-json exclude: ^.devcontainer/devcontainer.json args: [--autofix] - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/executablebooks/mdformat rev: 0.7.22 hooks: - id: mdformat additional_dependencies: [mdformat-gfm, mdformat-frontmatter, mdformat-footnote] - repo: https://github.com/astral-sh/ruff-pre-commit rev: "v0.11.9" hooks: - id: ruff args: [--exit-non-zero-on-fix] - id: ruff-format ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` docs/source # From https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ # Vscode config files # .vscode/ # PyCharm # JetBrains specific template is maintained in a separate JetBrains.gitignore that can # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown [](https://img.shields.io/github/v/release/ai-zerolab/yourware-mcp) [](https://github.com/ai-zerolab/yourware-mcp/actions/workflows/main.yml?query=branch%3Amain) [](https://img.shields.io/github/commit-activity/m/ai-zerolab/yourware-mcp) [](https://img.shields.io/github/license/ai-zerolab/yourware-mcp) <!-- [](https://codecov.io/gh/ai-zerolab/yourware-mcp) --> # Yourware MCP MCP server to upload your project to [yourware](https://www.yourware.so). Support single file or directory. ## Showcase Visit on [yourware](https://v9gfmmif5s.app.yourware.so/): https://v9gfmmif5s.app.yourware.so/  ## Pre-requisites 1. You need to login to [yourware](https://www.yourware.so) 1. Then you can create a new API key, and set the `YOURWARE_API_KEY` environment variable. Don't worry, you chat with LLM to create and store the API key. ## Configuration ### General configuration You can use the following configuration for cline/cursor/windsurf... ```json { "mcpServers": { "yourware-mcp": { "command": "uvx", "args": ["yourware-mcp@latest", "stdio"], "env": {} } } } ``` ### Cursor config guide In cursor settings -> features -> MCP Servers, Add a new MCP Server, name it `yourware-mcp` and set the command to `uvx yourware-mcp@latest stdio`  ### Config claude code ```bash claude mcp add yourware-mcp -s user -- uvx yourware-mcp@latest stdio ``` ## Available environments variables `YOURWARE_API_KEY` for the API key, you can also let llm config it for you. ``` -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- ```markdown # Contributing to `yourware-mcp` Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. You can contribute in many ways: # Types of Contributions ## Report Bugs Report bugs at https://github.com/ai-zerolab/yourware-mcp/issues If you are reporting a bug, please include: - Your operating system name and version. - Any details about your local setup that might be helpful in troubleshooting. - Detailed steps to reproduce the bug. ## Fix Bugs Look through the GitHub issues for bugs. Anything tagged with "bug" and "help wanted" is open to whoever wants to implement a fix for it. ## Implement Features Look through the GitHub issues for features. Anything tagged with "enhancement" and "help wanted" is open to whoever wants to implement it. ## Write Documentation yourware-mcp could always use more documentation, whether as part of the official docs, in docstrings, or even on the web in blog posts, articles, and such. ## Submit Feedback The best way to send feedback is to file an issue at https://github.com/ai-zerolab/yourware-mcp/issues. If you are proposing a new feature: - Explain in detail how it would work. - Keep the scope as narrow as possible, to make it easier to implement. - Remember that this is a volunteer-driven project, and that contributions are welcome :) # Get Started! Ready to contribute? Here's how to set up `yourware-mcp` for local development. Please note this documentation assumes you already have `uv` and `Git` installed and ready to go. 1. Fork the `yourware-mcp` repo on GitHub. 1. Clone your fork locally: ```bash cd <directory_in_which_repo_should_be_created> git clone [email protected]:YOUR_NAME/yourware-mcp.git ``` 3. Now we need to install the environment. Navigate into the directory ```bash cd yourware-mcp ``` Then, install and activate the environment with: ```bash uv sync ``` 4. Install pre-commit to run linters/formatters at commit time: ```bash uv run pre-commit install ``` 5. Create a branch for local development: ```bash git checkout -b name-of-your-bugfix-or-feature ``` Now you can make your changes locally. 6. Don't forget to add test cases for your added functionality to the `tests` directory. 1. When you're done making changes, check that your changes pass the formatting tests. ```bash make check ``` Now, validate that all unit tests are passing: ```bash make test ``` 9. Before raising a pull request you should also run tox. This will run the tests across different versions of Python: ```bash tox ``` This requires you to have multiple versions of python installed. This step is also triggered in the CI/CD pipeline, so you could also choose to skip this step locally. 10. Commit your changes and push your branch to GitHub: ```bash git add . git commit -m "Your detailed description of your changes." git push origin name-of-your-bugfix-or-feature ``` 11. Submit a pull request through the GitHub website. # Pull Request Guidelines Before you submit a pull request, check that it meets these guidelines: 1. The pull request should include tests. 1. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in `README.md`. ``` -------------------------------------------------------------------------------- /yourware_mcp/__init__.py: -------------------------------------------------------------------------------- ```python ``` -------------------------------------------------------------------------------- /tests/test_hello.py: -------------------------------------------------------------------------------- ```python def test_hello(): assert 1 == 1 ``` -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- ``` # pytest.ini [pytest] asyncio_mode = auto ``` -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- ```json { "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python" } ``` -------------------------------------------------------------------------------- /yourware_mcp/utils.py: -------------------------------------------------------------------------------- ```python def urljoin(base: str, url: str) -> str: return "/".join([base.rstrip("/"), url.lstrip("/")]) ``` -------------------------------------------------------------------------------- /yourware_mcp/log.py: -------------------------------------------------------------------------------- ```python import os USER_DEFINED_LOG_LEVEL = os.getenv("YOURWARE_MCP_LOG_LEVEL", "INFO") os.environ["LOGURU_LEVEL"] = USER_DEFINED_LOG_LEVEL from loguru import logger # noqa: E402 __all__ = ["logger"] ``` -------------------------------------------------------------------------------- /codecov.yaml: -------------------------------------------------------------------------------- ```yaml coverage: range: 70..100 round: down precision: 1 status: project: default: target: 90% threshold: 0.5% patch: default: target: auto threshold: 0% informational: true codecov: token: f927bff4-d404-4986-8c11-624eadda8431 ``` -------------------------------------------------------------------------------- /yourware_mcp/cli.py: -------------------------------------------------------------------------------- ```python import typer from yourware_mcp.app import mcp app = typer.Typer() @app.command() def stdio(): mcp.run(transport="stdio") @app.command() def sse( host: str = "localhost", port: int = 9123, ): mcp.settings.host = host mcp.settings.port = port mcp.run(transport="sse") if __name__ == "__main__": app(["stdio"]) ``` -------------------------------------------------------------------------------- /.github/workflows/validate-codecov-config.yml: -------------------------------------------------------------------------------- ```yaml name: validate-codecov-config on: pull_request: paths: [codecov.yaml] push: branches: [main] jobs: validate-codecov-config: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: Validate codecov configuration run: curl -sSL --fail-with-body --data-binary @codecov.yaml https://codecov.io/validate ``` -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- ``` [tox] skipsdist = true envlist = py39, py310, py311, py312, py313 [gh-actions] python = 3.10: py310 3.11: py311 3.12: py312 3.13: py313 [testenv] passenv = PYTHON_VERSION allowlist_externals = uv commands = uv sync --python {envpython} uv run python -m pytest --doctest-modules tests --cov --cov-config=pyproject.toml --cov-report=xml ``` -------------------------------------------------------------------------------- /yourware_mcp/client.py: -------------------------------------------------------------------------------- ```python from __future__ import annotations from functools import cache from typing import TYPE_CHECKING import httpx if TYPE_CHECKING: from yourware_mcp.credentials import Credentials @cache def get_client(credentials: Credentials) -> httpx.AsyncClient: return httpx.AsyncClient(base_url=credentials.base_url, headers={"Authorization": f"Bearer {credentials.api_key}"}) ``` -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- ```dockerfile # Install uv FROM python:3.12-slim COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv # Change the working directory to the `app` directory WORKDIR /app # Copy the lockfile and `pyproject.toml` into the image COPY uv.lock /app/uv.lock COPY pyproject.toml /app/pyproject.toml # Install dependencies RUN uv sync --frozen --no-install-project # Copy the project into the image COPY . /app # Sync the project RUN uv sync --frozen CMD [ "python", "yourware_mcp/foo.py" ] ``` -------------------------------------------------------------------------------- /.github/actions/setup-python-env/action.yml: -------------------------------------------------------------------------------- ```yaml name: "Setup Python Environment" description: "Set up Python environment for the given Python version" inputs: python-version: description: "Python version to use" required: true default: "3.12" uv-version: description: "uv version to use" required: true default: "0.6.2" runs: using: "composite" steps: - uses: actions/setup-python@v5 with: python-version: ${{ inputs.python-version }} - name: Install uv uses: astral-sh/setup-uv@v5 with: version: ${{ inputs.uv-version }} enable-cache: 'true' cache-suffix: ${{ matrix.python-version }} - name: Install Python dependencies run: uv sync --frozen shell: bash ``` -------------------------------------------------------------------------------- /yourware_mcp/credentials.py: -------------------------------------------------------------------------------- ```python from __future__ import annotations import os from pathlib import Path from pydantic import BaseModel, ConfigDict from yourware_mcp.client import get_client CREDENTIALS_PATH = Path("~/.yourware/credentials.json").expanduser().resolve() API_BASE_URL = os.getenv("YOURWARE_ENDPOINT", "https://www.yourware.so") class Credentials(BaseModel): api_key: str base_url: str = API_BASE_URL model_config = ConfigDict(frozen=True) def store_credentials(self) -> None: CREDENTIALS_PATH.parent.mkdir(parents=True, exist_ok=True) CREDENTIALS_PATH.write_text(self.model_dump_json(include=["api_key"])) @classmethod def load(cls) -> Credentials: api_key_from_env = os.getenv("YOURWARE_API_KEY") if api_key_from_env: return cls(api_key=api_key_from_env) if not CREDENTIALS_PATH.exists(): raise FileNotFoundError(f"Credentials not found at {CREDENTIALS_PATH}") # noqa: TRY003 return cls.model_validate_json(CREDENTIALS_PATH.read_text()) async def check_credentials(self) -> bool: api_key_list_path = "/api/v1/api-keys/list" client = get_client(self) response = await client.get(api_key_list_path) return response.status_code == 200 ``` -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- ```yaml name: Main on: push: branches: - main pull_request: types: [opened, synchronize, reopened, ready_for_review] jobs: quality: runs-on: ubuntu-latest steps: - name: Check out uses: actions/checkout@v4 - uses: actions/cache@v4 with: path: ~/.cache/pre-commit key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} - name: Set up the environment uses: ./.github/actions/setup-python-env - name: Run checks run: make check tests-and-type-check: runs-on: ubuntu-latest strategy: matrix: python-version: ["3.10", "3.11", "3.12", "3.13"] fail-fast: false env: UV_PYTHON: ${{ inputs.python-version }} defaults: run: shell: bash steps: - name: Check out uses: actions/checkout@v4 - name: Set up the environment uses: ./.github/actions/setup-python-env with: python-version: ${{ matrix.python-version }} - name: Run tests run: uv run python -m pytest tests --cov --cov-config=pyproject.toml --cov-report=xml - name: Upload coverage reports to Codecov with GitHub Action on Python 3.11 uses: codecov/codecov-action@v4 if: ${{ matrix.python-version == '3.11' }} ``` -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- ```toml [project] name = "yourware-mcp" version = "0.0.1" description = "yourware mcp server" authors = [{ name = "Wh1isper", email = "[email protected]" }] readme = "README.md" keywords = ['python'] requires-python = ">=3.10,<4.0" classifiers = [ "Intended Audience :: Developers", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Software Development :: Libraries :: Python Modules", ] dependencies = [ "httpx>=0.28.1", "loguru>=0.7.3", "mcp[cli]>=1.6.0", "pydantic>=2.11.1", "typer>=0.15.2", ] [project.urls] Homepage = "https://ai-zerolab.github.io/yourware-mcp/" Repository = "https://github.com/ai-zerolab/yourware-mcp" Documentation = "https://ai-zerolab.github.io/yourware-mcp/" [dependency-groups] dev = [ "pytest>=7.2.0", "pre-commit>=2.20.0", "pytest-asyncio>=0.25.3", "tox-uv>=1.11.3", "deptry>=0.22.0", "pytest-cov>=4.0.0", "ruff>=0.9.2", ] [project.scripts] yourware-mcp = "yourware_mcp.cli:app" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.setuptools] py-modules = ["yourware_mcp"] [tool.pytest.ini_options] testpaths = ["tests"] [tool.ruff] target-version = "py39" line-length = 120 fix = true [tool.ruff.lint] select = [ # flake8-2020 "YTT", # flake8-bandit "S", # flake8-bugbear "B", # flake8-builtins "A", # flake8-comprehensions "C4", # flake8-debugger "T10", # flake8-simplify "SIM", # isort "I", # mccabe "C90", # pycodestyle "E", "W", # pyflakes "F", # pygrep-hooks "PGH", # pyupgrade "UP", # ruff "RUF", # tryceratops "TRY", ] ignore = [ # LineTooLong "E501", # DoNotAssignLambda "E731", ] [tool.ruff.lint.per-file-ignores] "tests/*" = ["S101"] [tool.ruff.format] preview = true [tool.coverage.report] skip_empty = true [tool.coverage.run] branch = true source = ["yourware_mcp"] ``` -------------------------------------------------------------------------------- /.github/workflows/on-release-main.yml: -------------------------------------------------------------------------------- ```yaml name: release-main permissions: contents: write packages: write on: release: types: [published] jobs: set-version: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Export tag id: vars run: echo tag=${GITHUB_REF#refs/*/} >> $GITHUB_OUTPUT if: ${{ github.event_name == 'release' }} - name: Update project version run: | sed -i "s/^version = \".*\"/version = \"$RELEASE_VERSION\"/" pyproject.toml env: RELEASE_VERSION: ${{ steps.vars.outputs.tag }} if: ${{ github.event_name == 'release' }} - name: Upload updated pyproject.toml uses: actions/upload-artifact@v4 with: name: pyproject-toml path: pyproject.toml publish: runs-on: ubuntu-latest needs: [set-version] steps: - name: Check out uses: actions/checkout@v4 - name: Set up the environment uses: ./.github/actions/setup-python-env - name: Download updated pyproject.toml uses: actions/download-artifact@v4 with: name: pyproject-toml - name: Build package run: uv build - name: Publish package run: uv publish env: UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }} - name: Upload dists to release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: dist/* file_glob: true tag: ${{ github.ref }} overwrite: true push-image: runs-on: ubuntu-latest needs: [set-version] steps: - uses: actions/checkout@v4 - name: Export tag id: vars run: echo tag=${GITHUB_REF#refs/*/} >> $GITHUB_OUTPUT if: ${{ github.event_name == 'release' }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Github Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ai-zerolab password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push image id: docker_build_publish uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64,linux/arm64/v8 cache-from: type=gha cache-to: type=gha,mode=max file: ./Dockerfile push: true tags: | ghcr.io/ai-zerolab/yourware-mcp:${{ steps.vars.outputs.tag }} ghcr.io/ai-zerolab/yourware-mcp:latest ``` -------------------------------------------------------------------------------- /yourware_mcp/app.py: -------------------------------------------------------------------------------- ```python import io import os import zipfile from pathlib import Path from typing import Annotated import httpx from mcp.server.fastmcp import FastMCP from yourware_mcp.client import get_client from yourware_mcp.credentials import API_BASE_URL, CREDENTIALS_PATH, Credentials from yourware_mcp.utils import urljoin mcp = FastMCP("yourware-mcp") @mcp.tool(description="Check your yourware credentials exists and are valid.") async def check_credentials(): try: credentials = Credentials.load() except FileNotFoundError: return { "success": False, "message": "Credentials not found", "help": "Run `create_api_key` to create one", } if not await credentials.check_credentials(): return { "success": False, "message": "Credentials are invalid", "help": "Call `create_api_key` to create one", } return { "success": True, "message": "Credentials are valid", } @mcp.tool( description=f"Create a new yourware API key. This will automatically be stored in {CREDENTIALS_PATH.as_posix()}. Use this tool if current credentials are invalid" ) async def create_api_key(api_key: Annotated[str | None, "The API key to store"] = None): if not api_key: quick_create_address = urljoin(API_BASE_URL, "/api/v1/api-keys/quick-create") login_address = urljoin(API_BASE_URL, "/login") return { "success": False, "message": "API key is required, please guide the user to create one. Let the user tell you what the page shows and guide them through the login process if needed.", "help": f"Click this link to create one: {quick_create_address}\n\nClick this link to login if needed: {login_address}", } Credentials(api_key=api_key).store_credentials() return { "success": True, "message": "API key created", } @mcp.tool( description="Upload a file or directory to yourware, might be a dist/out directory or a single html file. Use absolute path if possible. " "For multiple files, you should move them to a directory first, then use this tool to upload the directory" ) async def upload_project( # noqa: C901 file_path: Annotated[ str, "The path to the dist/out directory or single file. If ends with /, it will be treated as a directory", ], cwd: Annotated[ str | None, "The current working directory to resolve relative paths from, should be a absolute path", ] = None, ): if cwd: cwd_path = Path(cwd).expanduser().resolve() file_path = cwd_path / file_path else: file_path = Path(file_path) file_path = file_path.expanduser().resolve() try: credentials = Credentials.load() except FileNotFoundError: return { "success": False, "message": "Credentials not found", "help": "Run `create_api_key` to create one", } if not await credentials.check_credentials(): return { "success": False, "message": "Credentials are invalid", "help": "Call `create_api_key` to create one", } client = get_client(credentials) # 1. Create a zip in memory zip_buffer = io.BytesIO() with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file: if file_path.is_dir(): # 2. Zip the directory into it for root, dirs, files in os.walk(file_path): # Skip .git directories if ".git" in dirs: dirs.remove(".git") # This modifies dirs in-place to prevent os.walk from traversing .git for file in files: file_full_path = Path(root) / file arc_name = file_full_path.relative_to(file_path) zip_file.write(file_full_path, arcname=arc_name) else: # Zip the single file zip_file.write(file_path, arcname=file_path.name) # Get the zip content zip_buffer.seek(0) zip_content = zip_buffer.getvalue() zip_size = len(zip_content) # 3. Call /api/v1/files/upload for upload infos upload_response = await client.post( "/api/v1/files/upload", json={ "files": [ { "file_name": "source_code.zip", "file_size": zip_size, "mime_type": "application/zip", } ], "event_type": "source_code", "is_public": False, }, ) if upload_response.status_code != 200: return { "success": False, "message": f"Failed to get upload info: {upload_response.text}", } upload_data = upload_response.json() upload_info = upload_data["data"]["upload_infos"][0] file_id = upload_info["file_id"] upload_url = upload_info["upload_url"] fields = upload_info["fields"] # 4. Upload the zip to the upload url files = {"file": ("source_code.zip", zip_content, "application/zip")} form_data = {**fields} async with httpx.AsyncClient() as upload_client: upload_result = await upload_client.post(upload_url, data=form_data, files=files) if upload_result.status_code not in (200, 201, 204): return { "success": False, "message": f"Failed to upload file: {upload_result.text}", } # 5. Call /api/v1/projects/deploy with the file_id deploy_response = await client.post("/api/v1/projects/deploy", json={"file_id": file_id}) if deploy_response.status_code != 200: return { "success": False, "message": f"Failed to deploy project: {deploy_response.text}", } deploy_data = deploy_response.json() project_data = deploy_data["data"] return { "success": True, "message": "Project uploaded successfully", "project_url": project_data["project_url"], "iframe_url": project_data["iframe_url"], } ``` -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Yourware MCP - Upload Your Projects to Yourware</title> <style> :root { --primary-color: #4f46e5; --secondary-color: #818cf8; --text-color: #1f2937; --bg-color: #ffffff; --card-bg: #f9fafb; --border-color: #e5e7eb; --code-bg: #f3f4f6; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: var(--text-color); background-color: var(--bg-color); margin: 0; padding: 0; } .container { max-width: 1200px; margin: 0 auto; padding: 0 20px; } header { background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); color: white; padding: 60px 0; text-align: center; } header h1 { margin: 0; font-size: 3rem; letter-spacing: -0.5px; } header p { font-size: 1.2rem; margin: 15px 0 0; opacity: 0.9; } section { padding: 60px 0; border-bottom: 1px solid var(--border-color); } h2 { font-size: 2rem; margin-top: 0; color: var(--primary-color); } h3 { font-size: 1.5rem; margin-top: 30px; color: var(--primary-color); } .features { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 30px; margin-top: 40px; } .feature-card { background-color: var(--card-bg); border-radius: 8px; padding: 25px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); transition: transform 0.3s ease; } .feature-card:hover { transform: translateY(-5px); } .feature-card h3 { margin-top: 0; font-size: 1.3rem; } code { background-color: var(--code-bg); padding: 2px 5px; border-radius: 4px; font-family: 'Courier New', Courier, monospace; font-size: 0.9em; } pre { background-color: var(--code-bg); padding: 15px; border-radius: 8px; overflow-x: auto; font-family: 'Courier New', Courier, monospace; font-size: 0.9em; line-height: 1.4; } .btn { display: inline-block; background-color: var(--primary-color); color: white; padding: 12px 24px; border-radius: 6px; text-decoration: none; font-weight: 600; transition: background-color 0.3s ease; } .btn:hover { background-color: var(--secondary-color); } .installation-steps { counter-reset: step; list-style-type: none; padding-left: 0; } .installation-steps li { position: relative; margin-bottom: 20px; padding-left: 50px; } .installation-steps li::before { counter-increment: step; content: counter(step); position: absolute; left: 0; top: 0; background-color: var(--primary-color); color: white; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; } .resources { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; } .resource-card { background-color: var(--card-bg); border-radius: 8px; padding: 20px; text-align: center; } footer { text-align: center; padding: 30px 0; color: #6b7280; font-size: 0.9rem; } @media (max-width: 768px) { header h1 { font-size: 2.2rem; } .features { grid-template-columns: 1fr; } } </style> </head> <body> <header> <div class="container"> <h1>Yourware MCP</h1> <p>An MCP server to upload your projects to <a href="https://www.yourware.so" style="color: white; text-decoration: underline;">yourware.so</a></p> </div> </header> <section id="overview"> <div class="container"> <h2>Project Overview</h2> <p> Yourware MCP is a Model Context Protocol (MCP) server that enables AI assistants like Claude to upload your projects directly to <a href="https://www.yourware.so">yourware.so</a>. This integration allows for seamless deployment of web projects, making it easy to showcase your work online without leaving your development environment. </p> <p> Whether you're building a simple HTML page, a complex web application, or any other web-based project, Yourware MCP streamlines the process of getting your work online and shareable. </p> </div> </section> <section id="features"> <div class="container"> <h2>Features & Capabilities</h2> <div class="features"> <div class="feature-card"> <h3>Easy Authentication</h3> <p>Simple API key-based authentication system that securely stores your credentials.</p> </div> <div class="feature-card"> <h3>Flexible Uploads</h3> <p>Upload single files or entire directories with automatic compression and organization.</p> </div> <div class="feature-card"> <h3>Seamless Deployment</h3> <p>One-step deployment process that makes your projects instantly available online.</p> </div> <div class="feature-card"> <h3>AI Assistant Integration</h3> <p>Works with AI assistants like Claude through Cline, Cursor, and Windsurf for a smooth workflow.</p> </div> </div> </div> </section> <section id="installation"> <div class="container"> <h2>Installation & Setup</h2> <p>Getting started with Yourware MCP is straightforward:</p> <ol class="installation-steps"> <li> <strong>Install the package</strong> <pre>pip install yourware-mcp</pre> <p>Or use <code>uvx</code> directly as shown in the configuration examples below.</p> </li> <li> <strong>Configure your AI assistant</strong> <p>Add the Yourware MCP server to your AI assistant's configuration:</p> <h3>General Configuration</h3> <pre>{ "mcpServers": { "yourware-mcp": { "command": "uvx", "args": ["yourware-mcp@latest", "stdio"], "env": {} } } }</pre> <h3>Cursor Configuration</h3> <p>In Cursor settings → features → MCP Servers, add a new MCP Server named <code>yourware-mcp</code> and set the command to <code>uvx yourware-mcp@latest stdio</code></p> <img src="../assets/config-cursor.png" alt="Config cursor screenshot" style="max-width: 100%; border-radius: 8px; margin-top: 10px;"> </li> <li> <strong>Create an API key</strong> <p>You can either:</p> <ul> <li>Let your AI assistant guide you through creating an API key</li> <li>Visit <a href="https://www.yourware.so/api/v1/api-keys/quick-create">https://www.yourware.so/api/v1/api-keys/quick-create</a> to create one manually</li> <li>Set the <code>YOURWARE_API_KEY</code> environment variable</li> </ul> </li> </ol> </div> </section> <section id="usage"> <div class="container"> <h2>Usage Examples</h2> <p>Once configured, you can use Yourware MCP through your AI assistant with the following tools:</p> <h3>Check Credentials</h3> <pre> // Check if your credentials exist and are valid await use_mcp_tool({ server_name: "yourware-mcp", tool_name: "check_credentials", arguments: {} });</pre> <h3>Create API Key</h3> <pre> // Create a new API key await use_mcp_tool({ server_name: "yourware-mcp", tool_name: "create_api_key", arguments: { api_key: "your-api-key-here" // Optional, if not provided, will guide you to create one } });</pre> <h3>Upload Project</h3> <pre> // Upload a file or directory to yourware await use_mcp_tool({ server_name: "yourware-mcp", tool_name: "upload_project", arguments: { file_path: "path/to/your/project" // Can be a directory or single file } });</pre> <h3>Example Workflow</h3> <p>A typical workflow might look like this:</p> <ol> <li>Build your web project</li> <li>Check if your credentials are valid</li> <li>If not, create a new API key</li> <li>Upload your project to Yourware</li> <li>Get the project URL and share it with others</li> </ol> </div> </section> <section id="resources"> <div class="container"> <h2>Resources & Links</h2> <div class="resources"> <div class="resource-card"> <h3>GitHub Repository</h3> <p>View the source code and contribute to the project.</p> <a href="https://github.com/ai-zerolab/yourware-mcp" class="btn">Visit GitHub</a> </div> <div class="resource-card"> <h3>Yourware Platform</h3> <p>Explore the Yourware platform and see examples.</p> <a href="https://www.yourware.so" class="btn">Visit Yourware</a> </div> <div class="resource-card"> <h3>Documentation</h3> <p>Read the full documentation for detailed information.</p> <a href="https://www.yourware.so/docs" class="btn">Read Docs</a> </div> </div> </div> </section> <footer> <div class="container"> <p>© 2025 Yourware MCP. All rights reserved.</p> </div> </footer> </body> </html> ```