The connector
Give an agent this connector and it holds one MCP endpoint for one wawesome workspace. Through it the agent deploys a JavaScript Function and the files served beside it, runs it, attaches a custom domain and reads back the invocations that followed. Every call acts inside the workspace the presented credential belongs to, so no tool here takes a workspace as an argument.
MCP walks through adding it to Claude or to ChatGPT. This page is what the endpoint declares.
Transport
Section titled “Transport”POST https://api.wawesome.io/v1/mcp
Streamable HTTP over HTTPS, and stateless. GET and DELETE on the endpoint answer 405. There is
no stream to open and no session to delete, because a credential is presented on every request.
The endpoint declares 35 tools and shows every caller all of them. A tool the presented credential cannot run refuses and names the capability words it wanted, rather than disappearing from the list.
Each one declares a title, a readOnlyHint, a destructiveHint and an idempotentHint.
openWorldHint is false on all 35. Nothing here reaches outside the workspace the credential
names.
| Tool | What it does | Capability | readOnlyHint |
destructiveHint |
|---|---|---|---|---|
whoami |
The workspace the credential acts inside, the words it carries and the Apps it reaches | none | true | false |
list_apps |
The Apps in the workspace, with the address each answers at | read:apps |
true | false |
list_templates |
Every template the platform publishes | none | true | false |
get_template |
One template’s source and what it declares | none | true | false |
invoke_function |
Runs a deployed Function once | write:runs |
false | true |
fetch_function |
Fetches one path from a Function’s live version, a long body in parts | write:runs |
true | false |
rollback_function |
Puts an earlier version back on the public address | write:functions |
false | true |
set_env_var |
Sets an App’s environment variable | write:env |
false | true |
list_env_vars |
An App’s environment variables: each key, its kind and when it was set, and a variable’s value | read:env |
true | false |
attach_domain |
Attaches a domain to an App and answers the DNS records to add | write:domains |
false | true |
cancel_domain_claim |
Gives up a claim on a domain nobody has proved control of yet | write:domains |
false | true |
check_domain |
Where an App’s domain got to, asked of the certificate vendor | read:domains |
true | false |
list_functions |
The Functions in one App | read:functions |
true | false |
list_versions |
One Function’s versions, newest first | read:functions |
true | false |
get_function_source |
The code a version is running, and the files it carries | read:functions |
true | false |
read_function_file |
One file a version or a draft carries, by path, a long file in parts | read:functions |
true | false |
open_draft |
Opens a draft of a Function: a private copy of its files that nothing serves | write:functions |
false | false |
list_drafts |
The open drafts of a Function, with who owns each one | read:functions |
true | false |
get_draft_changes |
The paths a draft added, changed and deleted against its version, and its revision | read:functions |
true | false |
edit_draft |
Adds, writes, changes and deletes a draft’s files in one step, all of it or none | write:functions |
false | false |
preview_draft |
A link that shows a draft as it stands on every request, until the draft ends | write:functions |
false | false |
discard_draft |
Ends a draft and every change in it | write:functions |
false | true |
promote_draft |
Makes the draft revision the person approved live as a new version, and ends the draft | write:functions |
false | true |
list_invocations |
One Function’s invocations, newest first | read:invocations |
true | false |
get_invocation |
One invocation by id | read:invocations |
true | false |
read_invocation_logs |
One invocation’s captured log body, a page at a time | read:invocations |
true | false |
get_usage |
What this workspace spent this period against what its plan grants | read:tenant |
true | false |
get_function_health |
Each Function’s requests, failures, 5xx answers and p95, for one App or every App, most failures first | read:apps |
true | false |
get_export |
Whether the export is ready, and a download link once it is | write:exports |
true | false |
diagnose_latest_failure |
The latest failed invocation, its log body and the version that ran it | read:invocations, read:functions |
true | false |
deploy_function |
Deploys code, the files served beside it, private files, or all of them, and promotes what it made | write:functions |
false | true |
request_upload |
A link a person opens to hand the agent files it cannot produce | write:functions |
false | false |
list_uploads |
What a person sent against one upload session, with their instructions for the set and each image’s size | read:functions |
true | false |
read_upload |
The contents of one uploaded text or JSON file, a page at a time | read:functions |
true | false |
deploy_status |
How the deploy behind a handle went | read:functions |
true | false |
Two rows want reading twice. fetch_function is read-only because it answers what a path served and
changes nothing, and it asks for write:runs because serving that path runs the Function.
request_upload is neither read-only nor destructive. It mints a link and then waits for a person to
open it.
list_uploads answers instructions on the session: what the person wrote about the files as a
set, or null. Each upload carries width and height in pixels. The server reads them from the
stored bytes of a png, jpeg, gif, webp or avif, and they are null for any other type or for an
image whose header does not parse. The upload page accepts every type the platform serves,
including .csv, .tsv, .md and .ics. It refuses markup (.html, .htm, .svg, .xml) and
unknown extensions. request_upload refuses a note that names a markup file, such as index.html,
and tells the agent to send the file itself in the deploy or with edit_draft.
get_export runs only for the workspace’s owner, signed in as themselves. write:exports is never
granted to a credential, so the connector sees the tool and is refused by it. No tool starts an
export. Only a person starts one, from the dashboard or the CLI.
No tool reads a secret back. list_env_vars answers a variable’s value, but a secret only as its key,
its kind and the date it was set. None of its value is returned, not even the last characters.
Authorization
Section titled “Authorization”The endpoint answers a request carrying no token with 401 and an RFC 6750 challenge naming where
to look next:
WWW-Authenticate: Bearer resource_metadata="https://api.wawesome.io/.well-known/oauth-protected-resource/v1/mcp", scope="..."
Discovery metadata sits at both standard locations. RFC 9728’s resource metadata answers on the API
hostname, at /.well-known/oauth-protected-resource/v1/mcp, and names
https://auth.wawesome.io as the authorization server. RFC 8414’s authorization server metadata
answers there, at /.well-known/oauth-authorization-server. A client that predates RFC 9728 and
asks for that document on the resource’s own origin, at
https://api.wawesome.io/.well-known/oauth-authorization-server/v1/mcp, is redirected to it.
A client identifies itself one of two ways. Neither one takes a client secret, and neither takes a configuration step.
- A Client ID Metadata Document. The client’s
client_idis an HTTPS URL, and the server fetches its metadata from that address. Every redirect URI the document declares must be on that same origin, or plain HTTP on loopback for a native client. The authorization server metadata declares"client_id_metadata_document_supported": true. - Dynamic client registration per RFC 7591, at
https://auth.wawesome.io/oauth/register, named in the metadata asregistration_endpoint.
A redirect URI is matched against the ones the client registered, byte for byte. The one exception
is a native client listening on loopback. A client that registered http://localhost/callback or
http://127.0.0.1/callback may be answered on any port at that same host, which is what RFC 8252
asks for: the operating system hands it the port, so it cannot register it in advance. The scheme,
the host, the path and the query all still have to match exactly, and the port it authorized on is
the port it has to present when it exchanges the code.
Proof of possession is PKCE. code_challenge_methods_supported is ["S256"] and nothing else.
plain is neither offered nor negotiated down to, and an authorize request carrying no challenge is
refused.
Codes become tokens at POST https://auth.wawesome.io/oauth/token, as
application/x-www-form-urlencoded, over both the authorization_code and refresh_token grants.
The answer is Cache-Control: no-store. An authorization code is worth five minutes and an access
token an hour.
Refreshing rotates the refresh token. The rotation replaces the grant’s secret where it stands rather than minting a second credential, so a connector running daily for a year stays one row in the workspace with its last-used date moving. A grant nobody has presented for 90 days is over, counted from its last call rather than from the day it was issued.
The token a rotation replaced still works for 60 seconds, so two of a connector’s requests crossing the expiry at the same moment both refresh instead of one of them ending the install. Nothing older than that one token is ever accepted, and neither is that one a minute later: a refresh token from further back is a copy of it in somebody else’s hands, so presenting it ends the grant rather than refreshing it, and the connector has to be installed again.
The metadata’s scopes_supported is our own capability words, read off the same table a credential
is cut from. What a connector ends up holding is what one person approved on one screen, and they
can hold it to named Apps.
What it reads and writes
Section titled “What it reads and writes”It reads the Apps in one workspace and the addresses they answer at, their Functions, their versions and the code a version is running. It reads invocations with their outcome and what they spent, and the log bodies captured with them. It reads where a custom domain claim got to, and what the workspace has spent against its plan.
It writes new versions of a Function and the files served beside them, environment variable values, custom domain claims, and runs of a Function.
It reaches no workspace but the one the credential belongs to.
What is kept, and for how long
Section titled “What is kept, and for how long”An invocation’s captured log body is kept 14 days. The stored objects expire at that age.
The invocation’s own row, carrying its status, timing, fuel, peak memory and egress bytes, is kept 35 days, and a housekeeping pass then deletes it. That row records no request method, no path and no caller IP.
Deployed code and files last as long as a rollback can reach them. Ten versions behind the live one keep their files, and the bytes nothing references any more are reclaimed a day later.
The grant is one credential row, and it lasts until somebody revokes it or 90 days pass with no call.
A person’s name and email address come from the identity provider they signed in with. The connector neither collects them nor reads them back.
Deleting it
Section titled “Deleting it”Revoke the connector on the dashboard, under Settings and then Credentials, on the row
marked Connector. Its very next call is refused.
Delete an App from its own page on the dashboard. Its Functions, their versions and their files go with it.
The privacy policy says what else you may ask for, including a copy of what we hold about you and its erasure, and that we answer within a month.
Policies and contact
Section titled “Policies and contact”- Privacy policy: https://wawesome.io/legal/privacy-policy
- Terms of service: https://wawesome.io/legal/terms-of-service
- Contact: account@wawesome.io