Documentation
Use Kicklace from Claude
What connecting Claude to a workspace gives you, in the words the screen uses: what to ask it, the two ways to connect, what it can and cannot do, and where to read back everything it did. It never sends an email, never deletes a record, and never puts anybody on a list without the words they agreed to.
Settings → Claude is the screen a customer connects their own AI from (#145, #166). Since #228
it opens on what you could ask it — the eight asks in §3 — because that is the only frame on the
screen that answers "why would I?", and an address answers "how?", which is a question nobody has
until they want the thing above. Then the two ways in (sign in from Claude, or take a key), then what
Claude can do, then what it did. This file is where those words are agreed and reviewed; the protocol
behind the screen — the JSON-RPC shapes, the OAuth endpoints, the errors, the two kinds of key — is
docs/mcp.md.
The screen is src/app/app/[slug]/settings/claude/page.tsx with
src/components/settings/claude-recipes.tsx, claude-connect.tsx, claude-tools.tsx and
claude-log.tsx, in that order. Its tab sits in Get set up, right after Your website, because
connecting Claude is hookup work of the same kind — and Your website's own "Let your AI do it" frame
links here, since a founder who has connected Claude can ask it to set the site up rather than paste
anything.
1. Use Kicklace from Claude
Claude can look people up, read a person's whole story, run a report, add notes and follow-ups, put someone on a list with the words they agreed to, and draft an email for you to send. It never sends anything and never deletes anything, and everything it does is written down below, in your name.
The frame's own line is "Add Kicklace to Claude and sign in. Nothing is copied, and you can disconnect it here at any time."
Sign in from Claude (#166)
First inside the frame, because it is what most people want and it copies nothing:
Open Claude, go to Settings → Connectors → Add custom connector, and paste this address. Claude sends you back here to sign in, you pick this workspace, and that is the whole of it.
"Settings → Connectors → Add custom connector" is the name of a menu inside Claude, which is why the word connector survives here and nowhere else on the screen (#228).
The address is the same <url> as below: appBaseUrl() + "/api/mcp". The connector registers
itself, sends the person to Kicklace's own sign-in, and shows them the consent screen at
/connect/authorize:
Let Claude use Kicklace as you? · Signed in as michael@example.com · It will work in WoodWright, as you. — with a workspace picker when they are in more than one, "You are not in any workspace yet" when they are in none, the four things it will be able to do, the two it will not, and Allow / Deny.
What comes back is that member's, in that one workspace: an access token that lasts an hour and a refresh token that rotates. Connected apps on the settings screen lists one row per sign-in — the app's name, the workspace, the day, when it was last used — with a two-step Disconnect that revokes the whole sign-in, so the app stops at its next call rather than at the end of the hour.
Or connect with a key
Claude Code, and some other apps, ask for a key instead of sending you here to sign in. One button makes one of your own; it is shown once.
Any member may press it: the key carries that member's user id, the door acts as them, and Claude can do exactly what they can do with their own hands and nothing more. That is also why the keys listed under it are the signed-in member's own and not the workspace's, and why the two-step Disconnect refuses a key that is not theirs.
The key is sk_live_… with the purpose claude, stored as a SHA-256 hash the moment it is made, so
the panel below is the only time it exists in readable form. The screen says so: "This key is shown
once. If you lose it, disconnect and connect again."
What each connection knows about (#195)
Every row in both lists — a Connected app and a key — carries one quiet line saying what that
client believes Kicklace can do, because a client reads tools/list once and keeps it, and the
door cannot push it a new one. It is drawn from tools_listed on the key or on the grant, against
the registry the "What Claude can do" frame below reads, so a row can never name a tool that is not
there.
Claude read the tool list 2 h ago and has 17 of the 62 tools. It does not know about
find_records,describe_website,create_recordand 10 more. In the Claude desktop app, switch this connector off and on in the Code tab's list of connected MCP servers; in claude.ai, remove the connector and add it again. A key-based client re-reads the list when it starts.
That is the real sentence for a connection made before the setup round of 2026-09-08, when the door held seventeen tools.
At most three tools are named and the rest are counted; a name the client still holds that Kicklace
has since taken away is said as well ("It still lists old_tool, which Kicklace no longer has").
A client with everything reads, in the quiet grey the "last used" line is drawn in:
Has all 62 tools; read 2 h ago.
A connection that has never read the list — a key minted before this landed, or one nothing has
used yet — says nothing at all. docs/mcp.md has why listChanged is false and when each
client reads the list.
The three snippets
<url> is appBaseUrl() + "/api/mcp" — the screen prints the deployment's own address, so a local
one says http://localhost:3111/api/mcp and production says https://www.kicklace.com/api/mcp.
<key> is the key just minted.
Claude Code. One line, no shell continuation in it: a backslash and a newline are not the same thing in every shell, and this is meant to be pasted, not read.
claude mcp add --transport http kicklace <url> --header "Authorization: Bearer <key>"
Claude Desktop. Put this in claude_desktop_config.json and start Claude Desktop again.
{
"mcpServers": {
"kicklace": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"<url>",
"--header",
"Authorization: Bearer <key>"
]
}
}
}
On macOS that file is ~/Library/Application Support/Claude/claude_desktop_config.json; on Windows
it is %APPDATA%\Claude\claude_desktop_config.json. mcp-remote is a small bridge, for a client
that still wants a key on disk; it runs on the customer's own machine and passes the key straight to
Kicklace. Claude Desktop's own connector list does not need it — it takes the address and signs in,
which is the frame above this one.
Any other app that takes a key. An address and one line of authorization. Nothing else to set up. (Streamable HTTP, one header, for whoever is wiring it.)
<url>
Authorization: Bearer <key>
2. What Claude can do
The tools are read from the registry itself (TOOLS in src/lib/mcp/tools/index.ts), so the screen
can never promise a tool that is not there; before the tools land it says "The tools arrive with the
next deploy." Reads come first, under Looking things up ("Changes nothing. A key made for one of
your own servers may do this much and no more"), then Making changes ("Each one is written on the
record in your name"). Each row is the tool's name in words, its real name in mono, and the first
sentence of the description the model is given. toolWords in claude-tools.tsx holds the words:
| Tool | On the screen | Tool | On the screen |
|---|---|---|---|
list_approvals |
See what is waiting for you | send_broadcast |
Ask to send an email to a list |
describe_workspace |
Look around your workspace | send_email |
Ask to email one person |
find_people |
Find people | delete_record |
Ask to delete a record |
get_person |
Open a person | delete_list |
Ask to remove a list |
search |
Search everything | delete_template |
Ask to delete a template |
search_activity |
Search the activity | delete_object |
Ask to delete an object |
list_followups |
List follow-ups | remove_field |
Ask to remove a field |
run_report |
Run a report | remove_member |
Ask to remove a member |
find_records |
Find organizations and deals | revoke_key |
Ask to revoke a key |
describe_website |
Check your website hookup | disconnect_integration |
Ask to disconnect an integration |
export_record |
Get a record's export link | merge_records |
Ask to merge two records |
create_list |
Make a list | ||
update_list |
Change a list | ||
update_automation |
Change an automation | ||
activate_automation |
Switch an automation on | ||
pause_automation |
Pause an automation | ||
rename_stage |
Rename a stage | ||
reorder_stages |
Reorder a pipeline's stages | ||
remove_stage |
Remove a stage | ||
rename_field |
Rename a field | ||
rename_object |
Rename an object | ||
remove_chart |
Take a chart off a dashboard | ||
resize_chart |
Resize a chart | ||
create_dashboard |
Make a dashboard | ||
rename_view |
Rename a view | ||
create_record |
Add an organization or a deal | ||
save_report |
Keep a report as a chart | ||
create_view |
Save a view | ||
add_stage |
Add a stage | ||
create_pipeline |
Make a pipeline | ||
add_field |
Add a field | ||
create_template |
Write an email template | ||
update_template |
Change an email template | ||
allow_site |
Connect a website address | ||
set_timezone |
Set the timezone | ||
set_workspace |
Change the workspace's settings | ||
invite_member |
Invite somebody | ||
change_role |
Change what a member may do | ||
add_many_to_list |
Put people on a list | ||
remove_many_from_list |
Take people off a list | ||
import_rows |
Import rows | ||
add_note |
Add a note | ||
create_followup |
Make a follow-up | ||
complete_followup |
Complete a follow-up | ||
create_person |
Add a person | ||
update_person |
Update a person | ||
add_to_list |
Put someone on a list | ||
remove_from_list |
Take someone off a list | ||
move_stage |
Move a record's stage | ||
draft_broadcast |
Draft an email | ||
propose_automation |
Propose an automation | ||
install_blueprint |
Install a starter |
Then the four rules, which are the reason the screen exists at all:
- It never sends email. It writes a draft and hands you the link; a person presses send.
- It never deletes. No record, no note, no consent record goes because Claude asked.
- It never puts anyone on a list without the words they agreed to. The sentence is stored on the subscription, as the proof it always is.
- Everything is in your name. A note, a stage move, a follow-up — the timeline says "<member> via Claude", and the call is on the log below.
Setting the whole thing up in one call (#222)
install_blueprint is the same button Settings → Setup has: one of Kicklace's starters — the
SaaS starter, the Waitlist starter, Release notes — written into the workspace as a list with the
words people agree to, the emails, and the automations, all paused. "Set this workspace up like
a SaaS product" is one call rather than a dozen, and every automation still waits for a person to
read it and switch it on. Anything it could not write comes back as a sentence saying what to
connect first; installing again afterwards adds only what is missing. docs/blueprints.md says
what each starter holds.
3. Things to ask Claude
The first frame on the screen (#168, moved to the top by #228;
src/components/settings/claude-recipes.tsx, whose RECIPES is the same list): eight one-line asks
with a Copy beside each, and under each the one
clause that says what happens in Kicklace — and, where the ask leans on something outside Kicklace,
what has to be connected in Claude for it to work. The frame's meta says to swap the list and stage
names for the workspace's own — and, while nothing is connected yet, that this is "what Claude can do
with your workspace once you connect it, below". None of them sends anything.
| Ask | What it does |
|---|---|
| "Who joined the waitlist this week, and what did they say?" | Reads the people and their notes. Nothing changes. |
| "Add everyone who emailed me this week to Kicklace, with a note on what they wanted" | Adds a person and writes one note for each, in your name. Needs Gmail connected in Claude. |
| "Put the people in this CSV on Product updates with the words they agreed to" | Puts each one on the list with that sentence stored word for word. Without it, Claude is refused. |
| "Draft a welcome to the macOS waitlist for me to send" | Writes the draft and gives you the link. Claude never sends. |
| "Read this LinkedIn profile and add them as a person" | Adds one person with what the page says, and a note saying where it came from. Needs Claude's browser. |
| "Every Monday, brief me on the waitlist" | Reads and sums up. Nothing changes. Needs Claude's scheduled tasks. |
| "Who downloaded but never activated?" | Runs a report over your own stages and answers with the count. |
| "Write an automation that welcomes anyone who joins a list" | Claude writes it against your own stages, lists and templates, and saves it as a draft. You read it and turn it on yourself. |
| "Put visits by week on my dashboard, and save a view of everyone still at Subscriber" | Adds the chart to your dashboard and the view to Contacts, both live. Nothing is removed. |
Each one is answered by tools that already exist — find_people and get_person, create_person
and add_note, add_to_list, draft_broadcast, run_report, propose_automation, save_report
and create_view — so the frame
| "Set up a Product updates list and write the welcome email for it" | Makes the list with the sentence people will agree to, stored word for word, and saves the email as a template. Nothing is sent. |
Each one is answered by tools that already exist — find_people and get_person, create_person
and add_note, add_to_list, draft_broadcast, run_report, propose_automation, create_list
and create_template — so the frame
never asks for something the door cannot do. The two that name a Claude connector and the one that
names Claude's browser say so on the line, because that part is Claude's side of the hookup and not
Kicklace's: Kicklace connects to Claude, and Claude connects to the rest. That is also the
framing the site's own "Connect what you already use" scene keeps (#168): a tool is shown as
reachable through Claude, never as a Kicklace integration.
4. What Claude did
The last twenty rows of agent_calls (src/lib/data/agent-calls.ts), newest first: when, who — the
member whose key it was, or the label on a server key, or "No longer connected" for a key that has
since been revoked — the tool in the same words, the tool's own one-line summary, and the outcome as
Ok, Refused beside its code, or Error. A write carries a small "change" tag. Under the
table, one sentence: "34 calls in 30 days, 6 of them changes, 2 refused. Lines older than 30 days are
deleted." Empty, it says: "Nothing yet. Connect Claude and ask it something."
A row holds no argument, no address and no words of a note — only the summary the tool wrote — so the whole workspace can read it.
Since #212 a row also says which door the call came through. One made by Ask Kicklace inside the app carries in the app under the member's name; everything else came through a connector or a key and says nothing extra, because that is what every row was until the box existed.
Your questions (#212)
Above the log, and yours only: what you asked Ask Kicklace inside the app, the answer, and how many calls it made, for thirty days.
"who joined the waitlist this week?" — Four people joined since Monday… · 2 h ago · 2 calls: look around your workspace, find people
Empty, it says "Nothing yet. Press ⌘J anywhere, or Ask on a record." A question is a person's own,
so nobody else's are listed here; the calls those questions made are in the table below, where the
whole workspace can read them. docs/ask.md is the whole of it — the shortcut, the rules, what it
costs, and what happens when it wants to send something.
5. Approvals: what Claude asks for (#211)
Claude can look people up, write things down and set a workspace up on its own. Sending an email, deleting something, removing a member, revoking a key and disconnecting an integration are the acts it has to ask for — a sent email cannot be recalled, a deleted record cannot be restored, and a revoked key breaks somebody's server. Those are proposals, and they land on Approvals in the sidebar, under Tasks, with a count of what is waiting.
The two frames on the page:
Waiting for you — "Approve one and it runs now, in your name. Reject one and nothing happens at all." Every row is the one sentence a person decides on ("Send “Still on the waitlist” to 33 people on Waitlist"), a line saying who asked and through what and when it runs out, and the two buttons. Approve on a send or a delete asks a second time, in a dialog that repeats the sentence and says what it does; everything else is one click. A member who is not an owner or an admin reads the row and is told "Only owners and admins can decide a proposal." Empty, the frame says:
Nothing waiting. Claude can look people up, write things down and set this workspace up on its own. Sending an email, deleting something, removing a member, revoking a key and disconnecting an integration are the acts it has to ask for — they land here.
Decided — what was approved, rejected, or left until it ran out, newest first, with what happened ("Sent to 33 people.") or the sentence the app refused it with, and a link to whatever the act touched.
A proposal's own page (/app/<workspace>/approvals/<id>) is where the notification lands: the
sentence as the page's title, What happens if you approve in plain words, the same two
buttons, and the proposal's facts beside it — asked, through, by, expires, and its reference.
The notification. Every owner and admin hears about it once, in the bell and in the Slack channel if one is connected: "Claude wants to send “Still on the waitlist” to 33 people on Waitlist", with "Nothing has happened yet. Approve or reject it by Sep 15." under it. There is no second notification when it is decided — the page says.
Seven days. A proposal nobody decides runs out, and the worker marks it. Nothing expired can be approved.
Whose name it is in. The act runs through the app's own data layer, exactly as the screen would run it, and where it lands on a timeline the row says "Michael Fall, proposed by Claude" — the member who approved it first, and where the idea came from second.