Docs · Integrate

Connect Google, a terminal, an iPhone and a browser

Everything below is one call with a scoped token. The token decides which tools run, how many times, until when, and whose keys they use. Every call and every answer is a row on the Ledger.

1. The one call

Every tool of this build runs through one address. Send its name and its arguments with a token:

{
  "method": "POST",
  "url": "https://miscsubjects.com/g/run",
  "headers": {
    "authorization": "Bearer $MISC_TOKEN",
    "content-type": "application/json"
  },
  "body": {
    "tool": "NOW",
    "args": {}
  }
}

As a command line:

curl -sS -X POST https://miscsubjects.com/g/run \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"tool":"NOW","args":{}}'

The answer is JSON: ok, result (the tool's answer), error when it failed, call_id and receipt (the Ledger record of the call). A refused call says which limit refused it.

WayThe call
RESTPOST https://miscsubjects.com/g/run, header Authorization: Bearer <token>, body {"tool","args"}
LinkGET https://miscsubjects.com/g/run/<TOOL>?args=<JSON>&t=<token>: a browser, a Shortcut, a bookmark
MCPPOST https://miscsubjects.com/g/mcp with the token as Bearer, or https://miscsubjects.com/g/mcp?t=<token> for clients that take only an address. It lists only the tools the token allows.
Every way for one toolGET https://miscsubjects.com/g/contracts/<TOOL>?t=<token>: each way written out whole with that token in it
What a token allowsGET https://miscsubjects.com/g?t=<token>: its limits in plain words

2. Get a token

The owner makes tokens in the Console (Scoped access): pick the tools, the ways, the uses, the expiry, a task, and whose keys it runs with. The same thing as a call, on the owner's Mac (the build key is read from its file, never written down):

curl -sS -X POST https://miscsubjects.com/api/grants \
  -H "x-sheets-key: $(cat ~/.misc-sheets-key)" \
  -H 'content-type: application/json' \
  --data '{"label":"Alice","tools":["GOOGLE_AS","IMSG_SEND_AS","MAC_EXEC_AS","CHROME_OPEN_AS","SEARCH","QUERY"],"ways":["rest","link","mcp"],"keys":"p_alice","ttl_s":2592000}'

The answer holds token (starts g1.). Give it to the person; they keep it as MISC_TOKEN. A holder can make a narrower copy without asking the build (POST https://miscsubjects.com/g/narrow), never a wider one.

3. Their own keys

Whose keys (keys = p_k2x9qa): Every tool runs with this person’s own keys (their Gmail key, their iMessage line), never the build’s: a key they have not stored is a refusal that names it. It can only be set when the token is made; a narrower copy cannot change it. The holder adds and removes that person’s keys at /g/keys.

The person stores a key with their token. Values are sealed in storage and no address ever answers one back; only names are listed.

curl -sS -X POST https://miscsubjects.com/g/keys \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"name":"GOOGLE_REFRESH_TOKEN","value":"1//0g…"}'
curl -sS https://miscsubjects.com/g/keys -H "authorization: Bearer $MISC_TOKEN"                 # the names they hold
curl -sS -X DELETE "https://miscsubjects.com/g/keys?name=GOOGLE_REFRESH_TOKEN" -H "authorization: Bearer $MISC_TOKEN"

The rules a run for one person follows:

The owner can read and set a person's key names too: GET|POST|DELETE https://miscsubjects.com/api/people/<person id>/keys with the build key.

4. Google services

GOOGLE_AS calls any Google API (Gmail, Calendar, Drive, Sheets, Docs, YouTube, Search Console…) as the person's own Google account. It needs three keys from their own Google Cloud project:

  1. At console.cloud.google.com, make a project and turn on the APIs they will use (APIs & Services, Library: Gmail API, Google Calendar API, Google Drive API, Google Sheets API).
  2. OAuth consent screen: External, add their own address as a test user.
  3. Credentials, Create credentials, OAuth client ID, type Web application, with the redirect address https://developers.google.com/oauthplayground. Keep the client ID and the client secret.
  4. At developers.google.com/oauthplayground: the gear, "Use your own OAuth credentials", paste the two. Pick the scopes (for example https://mail.google.com/, https://www.googleapis.com/auth/calendar, https://www.googleapis.com/auth/drive, https://www.googleapis.com/auth/spreadsheets), Authorize, then "Exchange authorization code for tokens". Keep the refresh token.
  5. Store the three with their token:
curl -sS -X POST https://miscsubjects.com/g/keys \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"name":"GOOGLE_CLIENT_ID","value":"123…apps.googleusercontent.com"}'

curl -sS -X POST https://miscsubjects.com/g/keys \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"name":"GOOGLE_CLIENT_SECRET","value":"GOCSPX-…"}'

curl -sS -X POST https://miscsubjects.com/g/keys \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"name":"GOOGLE_REFRESH_TOKEN","value":"1//0g…"}'

Then:

Send a mail from their Gmail

curl -sS -X POST https://miscsubjects.com/g/run \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"tool":"GOOGLE_AS","args":{"gmail_send":{"to":"someone@example.com","subject":"Hello","text":"Sent from my own Gmail through the build."}}}'

Search their mail

curl -sS -X POST https://miscsubjects.com/g/run \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"tool":"GOOGLE_AS","args":{"url":"https://gmail.googleapis.com/gmail/v1/users/me/messages","query":{"q":"from:stripe newer_than:7d","maxResults":10}}}'

Their next ten calendar events

curl -sS -X POST https://miscsubjects.com/g/run \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"tool":"GOOGLE_AS","args":{"url":"https://www.googleapis.com/calendar/v3/calendars/primary/events","query":{"singleEvents":true,"orderBy":"startTime","timeMin":"2026-10-05T00:00:00Z","maxResults":10}}}'

Read a sheet

curl -sS -X POST https://miscsubjects.com/g/run \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"tool":"GOOGLE_AS","args":{"url":"https://sheets.googleapis.com/v4/spreadsheets/SPREADSHEET_ID/values/Sheet1!A1:F50"}}'

Their newest Drive files

curl -sS -X POST https://miscsubjects.com/g/run \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"tool":"GOOGLE_AS","args":{"url":"https://www.googleapis.com/drive/v3/files","query":{"orderBy":"modifiedTime desc","pageSize":10,"fields":"files(id,name,mimeType,modifiedTime)"}}}'

Args: url (only addresses on googleapis.com), method (default GET), query, body, headers; or gmail_send: {to, subject, text, cc, bcc}. The access token is made from the refresh token for each call and never kept.

5. Terminal

curl

Every example on this page is a command line. Keep the token in the shell:

export MISC_TOKEN='g1.…'
curl -sS -X POST https://miscsubjects.com/g/run \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"tool":"SEARCH","args":{"q":"gmail"}}'

Node, Bun, Deno, a script

starter/call.mjs in the build is the whole client:

import { call } from './call.mjs';          // MISC_TOKEN in the environment
const r = await call('GOOGLE_AS', { gmail_send: { to: 'a@b.com', subject: 'Hi', text: 'Hello' } });
console.log(r.result);

Claude Code, Codex, Cursor (MCP)

claude mcp add --transport http scoped-now 'https://miscsubjects.com/g/mcp' --header 'Authorization: Bearer $MISC_TOKEN'
# ~/.codex/config.toml
[mcp_servers.misc]
url = "https://miscsubjects.com/g/mcp"
http_headers = { Authorization = "Bearer g1.…" }

The model sees only the tools the token allows, and every call it makes is on the Ledger under that token.

Python

import os, requests
r = requests.post("https://miscsubjects.com/g/run", headers={"authorization": "Bearer " + os.environ["MISC_TOKEN"]}, json={"tool": "NOW", "args": {}})
print(r.json())

6. Their own Mac: terminal, iMessage, Chrome, Shortcuts

A person's Mac becomes reachable with one file, starter/mac-bridge/bridge.mjs (Node 18 or later, no packages), and a Cloudflare tunnel:

export MAC_BRIDGE_KEY=$(openssl rand -hex 24)
node bridge.mjs                                   # listens on 127.0.0.1:3737
brew install cloudflared
printf 'ingress:\n  - service: http://127.0.0.1:3737\n' > bridge-tunnel.yml   # its own file: a ~/.cloudflared/config.yml with other rules answers 404
cloudflared tunnel --config bridge-tunnel.yml --url http://127.0.0.1:3737
# prints https://<words>.trycloudflare.com

They store the address and the key with their token:

curl -sS -X POST https://miscsubjects.com/g/keys \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"name":"MAC_BRIDGE_URL","value":"https://<words>.trycloudflare.com"}'

curl -sS -X POST https://miscsubjects.com/g/keys \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"name":"MAC_BRIDGE_KEY","value":"<the same value as MAC_BRIDGE_KEY>"}'
ToolArgsWhat it does on their Mac
MAC_EXEC_AS{"cmd","args":[],"cwd","timeout"}Runs a program and answers code, stdout and stderr
IMSG_SEND_AS{"to","text"}Sends an iMessage from the account signed in to Messages
CHROME_OPEN_AS{"url"}Opens a page in their Chrome
CHROME_TABS_AS{}Every open tab: window, index, title, address
CHROME_JS_AS{"js"}Runs JavaScript in the front tab (Chrome: View, Developer, Allow JavaScript from Apple Events)
SHORTCUT_RUN_AS{"name","input"}Runs one of their Shortcuts
curl -sS -X POST https://miscsubjects.com/g/run \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"tool":"IMSG_SEND_AS","args":{"to":"+14155551212","text":"On my way"}}'

The first iMessage, Chrome or Shortcuts call asks the Mac for permission (System Settings, Privacy & Security, Automation); they allow it once. A quick tunnel's address changes when it restarts; a named tunnel (cloudflared tunnel create) keeps one.

7. iPhone

A Shortcut that runs a tool

  1. New Shortcut, add Get Contents of URL. URL: https://miscsubjects.com/g/run.
  2. Method POST. Headers: Authorization = Bearer g1.…, Content-Type = application/json.
  3. Request Body: JSON, with tool (Text) = GOOGLE_AS and args (Dictionary) holding the tool's arguments.
  4. Add Get Dictionary Value for result, then Show Result or Speak Text.

Run it from the home screen, Siri, the Action button, or an automation (arriving somewhere, a time of day, a message from someone).

A link on the home screen

The link way needs no Shortcut: Safari, Share, Add to Home Screen.

https://miscsubjects.com/g/run/NOW?args=%7B%7D&t=%24MISC_TOKEN

Make that token narrow (one tool, a few uses): whoever has the link can run it.

Text from their own number

Their iPhone and Mac share one iMessage account, so IMSG_SEND_AS on their Mac (section 6) sends from their own number. SHORTCUT_RUN_AS runs a Shortcut synced from the iPhone.

8. Browser

From any page

fetch('https://miscsubjects.com/g/run', {
  method: 'POST',
  headers: { authorization: 'Bearer ' + token, 'content-type': 'application/json' },
  body: JSON.stringify({ tool: 'SEARCH', args: { q: 'invoice' } }),
}).then((r) => r.json()).then(console.log);

The doors answer any origin (CORS is open); the token is the only credential.

A model in the browser (claude.ai, ChatGPT connectors)

Add a custom connector with the address https://miscsubjects.com/g/mcp?t=g1.…. The model sees only the token's tools.

A model that can only open links

Give it the link way: https://miscsubjects.com/g/run/<TOOL>?args=<JSON>&t=<token>. It answers JSON.

Their own Chrome

CHROME_OPEN_AS, CHROME_TABS_AS and CHROME_JS_AS drive the Chrome on their Mac through the bridge (section 6), with their sign-ins.

9. Workers on any host

A worker needs only the address and a token: no database password, no other key. The build's starter/ folder has one file per host, each a few lines around call.mjs:

HostFileRun it
Cloudflare Workersstarter/cloudflare/worker.jsnpx wrangler deploy -c starter/cloudflare/wrangler.jsonc, then npx wrangler secret put MISC_TOKEN -c starter/cloudflare/wrangler.jsonc
Google Cloud Run, Fly, Render, a VPSstarter/node-server/server.mjs + Dockerfilegcloud run deploy misc-starter --source starter --set-env-vars MISC_TOKEN=g1.…
AWS Lambdastarter/lambda/index.mjsNode 22, a function URL, MISC_TOKEN in its environment; zip it with call.mjs beside the folder
Deno Deploystarter/deno/main.tsdeployctl deploy starter/deno/main.ts, MISC_TOKEN in its settings

The Cloudflare file also binds one shared D1 database, misc-shared: every copy of it (change name in the wrangler file) reads and writes the same tables. A worker elsewhere reaches data through the build's tools (QUERY, D1_QUERY) with its token, never with a Cloudflare key. Each worker gets its own token, so turning one token off stops one worker and no other.

SEARCH looks for every word across the Directory, the Ledger, articles, tasks, stored objects and this build's routes. Each hit carries the QUERY that opens it.

curl -sS -X POST https://miscsubjects.com/g/run \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"tool":"SEARCH","args":{"q":"gmail refresh","in":["directory","ledger","tasks"],"limit":10}}'

QUERY answers rows of one source with only the fields named in select. Dot paths reach inside a field.

curl -sS -X POST https://miscsubjects.com/g/run \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"tool":"QUERY","args":{"from":"ledger","where":{"tool":"GOOGLE_AS","from":"2026-10-01T00:00:00Z"},"select":["seq","ts","status","agent"],"limit":20}}'
curl -sS -X POST https://miscsubjects.com/g/run \
  -H "authorization: Bearer $MISC_TOKEN" \
  -H 'content-type: application/json' \
  --data '{"tool":"QUERY","args":{"from":"directory","where":{"type":"tool","description":{"contains":"imessage"}},"select":["name","description"],"limit":50}}'
fromWhat it reads
directoryEvery row of the Directory. Asking for a field beyond name, type and description reads each tool whole (its contracts too).
ledgerThe Ledger in its own database: where takes its fields (tool, agent, session, task, status, kind…) and from/to times (30 days by default); q is its word search.
articles, tasks, apiEvery article (no bodies), every task, every route of this build
objects, objects:<kind>Stored objects, all kinds or one

where is {field: value} for equal, or {field: {"contains"|"in"|"gte"|"lte"|"ne": …}}. With the build key the same two are GET https://miscsubjects.com/api/search?q=… and POST https://miscsubjects.com/api/query.

This page is written by the build from the code it describes, each time it is opened. The scoped tokens themselves: /a/scoped-access.