Metadata-Version: 2.4
Name: speak-global-mcp
Version: 0.1.1
Summary: MCP server for the SpeakGlobal Developer API (/v1).
Project-URL: Repository, https://github.com/Design-Aura/speak-global-MCP
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: mcp>=1.8
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic-settings>=2.0
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"

# speak-global-mcp

Model Context Protocol server for the [SpeakGlobal](https://speakglobal.ai) Developer API (`/v1`). Tools call the same JSON endpoints as API keys in the dashboard (**Account → Developer**).

Export endpoints are not implemented: `GET /v1/tts/export-status`, `POST /v1/tts/export`, `POST /v1/recordings/export`.

## Requirements

- Python 3.10+
- `httpx`, `mcp`, `pydantic-settings` (see `pyproject.toml`)

## Install

```bash
cd speak-global-mcp
python -m venv .venv
.venv\Scripts\activate
pip install -e .
```

### End users (Cursor, Claude Desktop, any stdio MCP client)

Cursor runs MCP over **stdio**: it starts a **local** process on your machine. There is no need to host a public MCP URL for normal use—each person installs the package once and puts their own [Developer API key](https://speakglobal.ai) in config.

**1. Install (pick one)**

- **pipx** (isolates dependencies; recommended if you have Python 3.10+):

  ```bash
  pipx install git+https://github.com/Design-Aura/speak-global-MCP.git
  ```

  Ensure pipx’s binary directory is on your `PATH` (see `pipx ensurepath`).

- **venv + pip** from a clone or Git URL:

  ```bash
  python -m venv .venv
  .venv\Scripts\activate   # Windows
  # source .venv/bin/activate   # macOS / Linux
  pip install git+https://github.com/Design-Aura/speak-global-MCP.git
  ```

**2. Cursor `mcp.json`**

User-level file (example paths): Windows `%USERPROFILE%\.cursor\mcp.json`, macOS/Linux `~/.cursor/mcp.json`. Use the `speakglobal-mcp` executable if it is on `PATH` (after pipx); otherwise set `command` to your venv’s `python` and `args` to `["-m", "speak_global_mcp.server"]`.

```json
{
  "mcpServers": {
    "speakglobal": {
      "command": "speakglobal-mcp",
      "env": {
        "SPEAKGLOBAL_ENVIRONMENT": "production",
        "SPEAKGLOBAL_API_BASE": "https://api.speakglobal.ai",
        "SPEAKGLOBAL_API_KEY": "sg_live_REPLACE_ME"
      }
    }
  }
}
```

Use **`production`** when calling the real API: it enforces `https://` on `SPEAKGLOBAL_API_BASE` and requires a non-empty key. Restart Cursor after editing.

**3. Optional: PyPI install**

After the package is published to PyPI, end users can run `pipx install speak-global-mcp` (or `pip install speak-global-mcp`) instead of the Git URL.

### Hosted MCP over HTTP (teams / automation)

The Docker image can expose **SSE** or **streamable-http** on a port (see [Docker](#docker)). Use that when a client connects over the network (automation, or products that support remote MCP). **Do not** expose it on the public internet without TLS and an access policy: the process still holds one `SPEAKGLOBAL_API_KEY`. Confirm whether your Cursor build supports remote MCP URLs; many setups use **local stdio only**.

Development (tests):

```bash
pip install -e ".[dev]"
pytest
```

## Package layout

| Module | Role |
|--------|------|
| `speak_global_mcp.server` | Entrypoint: loads tools + observability, exports `mcp` and `main`. |
| `speak_global_mcp.mcp_instance` | `FastMCP` singleton. |
| `speak_global_mcp.tools` | `@mcp.tool()` handlers. |
| `speak_global_mcp.observability` | `GET /health`, `GET /ready` (HTTP transports only). |
| `speak_global_mcp.http_client` | Backend requests, errors, path encoding. |
| `speak_global_mcp.config` | `SPEAKGLOBAL_*` settings. |
| `speak_global_mcp.runtime` | Logging and `main()`. |

## Configuration

### `SPEAKGLOBAL_*`

| Variable | Default | Notes |
|----------|---------|--------|
| `ENVIRONMENT` | `development` | `production` enforces `https` on `API_BASE` and non-empty `API_KEY`. |
| `API_BASE` | `http://127.0.0.1:8000` | Origin only; trailing `/` stripped. |
| `API_KEY` | *(empty)* | Bearer token (`sg_live_…`). |
| `MCP_TRANSPORT` | `stdio` | `stdio` \| `sse` \| `streamable-http`. |
| `LOG_LEVEL` | `INFO` | `DEBUG` \| `INFO` \| `WARNING` \| `ERROR`. |
| `HTTP_TIMEOUT_TOTAL` | `300` | Seconds. |
| `HTTP_TIMEOUT_CONNECT` | `30` | Seconds. |
| `HTTP_VERIFY_SSL` | `true` | Set `false` only for local TLS testing. |
| `HTTP_TRUST_ENV` | `false` | If `true`, `httpx` uses proxy env vars. |
| `HTTP_MAX_CONNECTIONS` | `100` | Pool size. |
| `HTTP_MAX_KEEPALIVE_CONNECTIONS` | `20` | Keepalive cap. |
| `READY_DEEP_PROBE` | `false` | If `true`, `GET /ready` issues `GET /v1/me`. |

Prefix: all of the above are read as `SPEAKGLOBAL_<NAME>` (e.g. `SPEAKGLOBAL_API_BASE`).

### `FASTMCP_*` (HTTP transports)

Used by FastMCP/uvicorn when `MCP_TRANSPORT` is `sse` or `streamable-http`:

| Variable | Default |
|----------|---------|
| `FASTMCP_HOST` | `127.0.0.1` |
| `FASTMCP_PORT` | `8000` |
| `FASTMCP_LOG_LEVEL` | `INFO` |

## Run

Stdio (e.g. Cursor / Claude Desktop):

```bash
speakglobal-mcp
```

Or:

```bash
python -m speak_global_mcp.server
```

Example `mcp.json` env block:

```json
{
  "SPEAKGLOBAL_API_BASE": "https://api.example.com",
  "SPEAKGLOBAL_API_KEY": "sg_live_…"
}
```

HTTP: set `SPEAKGLOBAL_MCP_TRANSPORT`, bind with `FASTMCP_HOST` / `FASTMCP_PORT`, terminate TLS at the load balancer or reverse proxy.

## Tools

| Tool | HTTP |
|------|------|
| `account_summary` | `GET /v1/me` |
| `list_voice_profiles` | `GET /v1/voice-profiles` |
| `get_voice_profile` | `GET /v1/voice-profiles/{session_id}` |
| `list_recordings` | `GET /v1/recordings` |
| `get_recording` | `GET /v1/recordings/{session_id}` |
| `generate_tts` | `POST /v1/tts/generate` |
| `presign_audio_upload` | `GET /v1/recordings/presign` |
| `register_recording_from_url` | `POST /v1/recordings/upload/with-url` |
| `start_transcription` | `POST /v1/processing/transcribe` |
| `start_enhance` | `POST /v1/processing/enhance` |
| `edit_transcript` | `POST /v1/processing/edit-transcript` |
| `start_re_enhance` | `POST /v1/processing/re-enhance` |
| `get_job_status` | `GET /v1/jobs/{job_id}` |
| `list_jobs_for_session` | `GET /v1/jobs/session/{session_id}` |

Scopes are enforced on the API; use a key with the minimum scopes required.

## HTTP observability

On `sse` / `streamable-http`:

- `GET /health` — process up, no upstream call.
- `GET /ready` — configuration and API key present; optional deep probe via `READY_DEEP_PROBE`.

## Docker

```bash
docker build -t speak-global-mcp .
docker run --rm -p 8080:8080 \
  -e SPEAKGLOBAL_API_BASE=https://api.example.com \
  -e SPEAKGLOBAL_API_KEY=sg_live_… \
  speak-global-mcp
```

Image defaults: non-root user, `streamable-http`, `FASTMCP_HOST=0.0.0.0`, `FASTMCP_PORT=8080`. Adjust env as needed.

## CI/CD (GitHub Actions)

| Workflow | Trigger | Purpose |
|----------|---------|---------|
| [ci.yml](.github/workflows/ci.yml) | Push / PR to `main` | `pytest` on Python 3.10–3.13 |
| [publish-pypi.yml](.github/workflows/publish-pypi.yml) | [Release](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository) **published** | Build wheel + sdist, upload to PyPI via [trusted publishing](https://docs.pypi.org/trusted-publishers/) (OIDC; no `PYPI_API_TOKEN` secret) |

**Release checklist:** PyPI rejects duplicate versions — each release needs a new semver in `pyproject.toml`.

**Automated (recommended):** from repo root, on branch `main`, with a clean working tree and [GitHub CLI](https://cli.github.com/) on your `PATH`, logged in via `gh auth login`. On Windows: `winget install --id GitHub.cli`, then restart the terminal.

```bash
python scripts/bump_and_release.py patch    # or minor | major
python scripts/bump_and_release.py --set 1.2.0 -y   # exact version; -y skips confirm
python scripts/bump_and_release.py minor --dry-run  # print steps only
```

This updates `pyproject.toml`, commits `chore: release vX.Y.Z`, pushes `main`, and runs `gh release create` (published release → triggers PyPI workflow).

**Manual:** edit `version` in `pyproject.toml`, push `main`, then create a GitHub Release from a new tag.

**One-time PyPI:** [Add a pending publisher](https://pypi.org/manage/account/publishing/) — GitHub → owner/repo that hosts this code → workflow file name `publish-pypi.yml`. The first project upload may require a manual `twine upload` once if PyPI has not seen `speak-global-mcp` yet; after that, trusted publishing can own releases.

## Publishing to PyPI (maintainers)

PyPI is the index behind `pip install speak-global-mcp`. You upload **wheels + sdist** built from this repo; each **version** can only be uploaded once (bump `version` in `pyproject.toml` for every release).

### One-time setup

1. Create accounts on [pypi.org](https://pypi.org/account/register/) and (recommended) [test.pypi.org](https://test.pypi.org/account/register/).
2. Create an **API token** under *Account settings → API tokens* (scope: entire account or single project after the first upload).
3. Configure credentials locally. With a token, username is always `__token__` and the password is the token value (`pypi-...`). Example `~/.pypirc` (Unix) or `%USERPROFILE%\.pypirc` (Windows):

   ```ini
   [distutils]
   index-servers =
       pypi
       testpypi

   [pypi]
   username = __token__
   password = pypi-YOUR_PRODUCTION_TOKEN

   [testpypi]
   repository = https://test.pypi.org/legacy/
   username = __token__
   password = pypi-YOUR_TEST_TOKEN
   ```

   Alternatively pass the token once:  
   `twine upload -u __token__ -p pypi-... dist/*`  
   Do not commit tokens or `.pypirc` into Git.

4. Optional: [Trusted publishing](https://docs.pypi.org/trusted-publishers/) (OpenID Connect from GitHub Actions) avoids long-lived tokens on your laptop.

### Release workflow

From the `speak-global-mcp` directory (with a clean Git tree):

```bash
pip install build twine
# Remove old artifacts: dist/, build/, and any *.egg-info under src/
python -m build
twine check dist/*
```

**Dry run on TestPyPI** (same version string must not already exist on TestPyPI either):

```bash
twine upload --repository testpypi dist/*
```

Install from TestPyPI to verify:

```bash
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ speak-global-mcp
```

**Production upload:**

```bash
twine upload dist/*
```

After a successful upload, end users can run:

```bash
pipx install speak-global-mcp
# or: pip install speak-global-mcp
```

Tag the Git release (e.g. `v0.1.0`) to match `pyproject.toml` if you track versions in Git.

## Security notes

- One `API_KEY` per process maps to one developer account; scope keys narrowly.
- Do not commit keys; inject via secret manager or env in orchestration.
- For public network exposure, use TLS in front of the MCP HTTP listener and restrict source IPs or add an authentication layer appropriate to your deployment.
- `register_recording_from_url` forwards URLs to your API; SSRF policy is defined server-side.

## Tests

`tests/` covers HTTP helpers, tool request shapes, response contracts, multi-step flows, config validation, and health routes. Run `pytest` from the package root.
