# Package app fetch `packageAppFetch` invokes a published package app over MCP — the fetch handler `kody.app.entry` exports. It is a **platform-marked real-surface** `app_fetch` run: same package runtime, `packageStorage()`, and secret mounts as production traffic. **Side effects are real.** `path` is mount-relative (`/notes`); the handler sees that stripped path, exactly like a browser request after the host strips the mount. Use it to verify `kody.app` wiring and JSON/API responses immediately after publish — without opening the hosted URL in a browser or performing session handoff. The platform strips caller-supplied `Kody-Synthetic` from MCP input (same as public app ingress) and sets `Kody-Synthetic: true` before the handler runs. Handlers treat synthetic fetches identically to production unless a deliberately visible irreversible-side-effect guard says otherwise. Hosted-URL checks remain useful for UI, OAuth redirects, and websocket facets. ## When to use it - After `packagePublishExternalPush` when the package declares `package.json#kody.app` - When `test_hints.app` is present on the publish response (copy-paste starting point) - To prove a fetch handler returns expected status/body before sharing `hosted_app_url` with a user Prefer a static `kody:@scope/package/export` import from `execute` for export smoke tests. Prefer [Inbound webhooks](./webhooks.md) ingress for provider POST deliveries. ## Call shape Search the `packages` domain, then call `packageAppFetch`: ```json { "package_id": "550e8400-e29b-41d4-a716-446655440000" } ``` Optional request fields: | Field | Default | Meaning | | --------- | ------- | ---------------------------------------------------- | | `path` | `/` | Path **after** the app mount (what the handler sees) | | `method` | `GET` | HTTP method | | `headers` | `{}` | Extra request headers (safe subset) | | `body` | omitted | Raw request body string for `POST` / `PUT` / `PATCH` | Example POST with JSON: ```json { "package_id": "550e8400-e29b-41d4-a716-446655440000", "path": "/api/items", "method": "POST", "headers": { "content-type": "application/json" }, "body": "{\"name\":\"smoke\"}" } ``` Look up the package with the scoped `@owner/leaf` name (or `package_id` when the name is not known). The lookup runs in the org the connection is bound to. Websocket upgrade requests (`Upgrade: websocket`, `Connection: Upgrade`, or equivalent) are rejected. ## Response The capability returns exactly: | Field | Meaning | | ----------- | ---------------------------------------------------------------------- | | `status` | HTTP status code | | `headers` | Response headers (safe allowlisted subset) | | `body` | Response body text, or base64 when the handler returns binary content | | `truncated` | `true` when the body was truncated to fit the MCP response size budget | Failures surface as structured MCP errors with the handler's thrown message or non-2xx body when applicable. A host-setup 500 (`Package app could not be prepared`) includes `cause` with the platform error so the failure is not an opaque smoke-test dead end. Check [Activity](./activity.md) (`app_fetch` surface) for run records; the run record carries the same `Kody-Synthetic` marker the handler saw. Host-setup failures happen before the isolate starts, so they do not appear as `app_fetch` run errors. ## Semantics - Resolves the signed-in user's saved package at its current published commit. - Strips credential headers (`Cookie`, `Authorization`, and internal `X-Kody-*` headers) before the handler runs — same rule as public package-app ingress. - Populates `packageContext` (`hostedUrl`, `appBasePath`) from the serving username and package name leaf. - Does **not** count toward package activation milestones. ## Platform assets under `/_assets/` `packageAppFetch` goes through the same serve path as the browser, so on a host that serves platform client assets, `path: "/_assets/__version.json"` returns the current `clientModuleUrl` and `path: "/_assets/"` returns files from `kody.app.assets` — the fetch handler is never invoked for those paths. A 404 on `/_assets/__version.json` means the host does not serve client assets yet, not that the handler is broken; see [Troubleshooting](../guides/package-apps.md#troubleshooting). ## Related - [Packages](./packages.md) — package apps and `hosted_app_url` - [Synthetic event dispatch](./synthetic-event-dispatch.md) — interactive MCP post-publish smoke test for one subscription handler - [Package apps guide](../guides/package-apps.md) — session handoff, `packageAppFetch` smoke, asset URLs, lean forks - [Package authoring guide](../guides/package-authoring.md#verify-your-publish) - Decision: [Synthetic package requests](../contributing/decisions/0013-synthetic-package-requests.md)