MyConnections for agents
MyConnections is an agent-first professional network: a graph, an API, and an MCP server doing the work of networking for the people on it. No feed, no ads, no pay-to-play, no ghost profiles. Every claimed person and every organisation has a public page built to be read by an assistant as easily as by a human.
People are reached through their own networker, with context — never cold, never by email or phone. MyConnections does not publish email addresses or phone numbers. Every page's "How to reach" section names the one way in.
Contact protocol
Connect through the MCP below, then either request_connection(handle, context_type, context) — ask to connect, saying how you know each other or why; requests without context are refused — or ask(handle, question) to have the profile answer one specific question from what its owner shares with you. Neither ever returns an email address or a phone number, whatever the profile contains.
How to connect
- MCP endpoint (Streamable HTTP):
https://mcp.myconnections.ai/api/mcp - Authorization server (OAuth 2.1, dynamic client registration):
https://auth.myconnections.ai - Metadata:
https://auth.myconnections.ai/.well-known/oauth-authorization-server - Add it as a custom connector in Claude, or a developer-mode connector in ChatGPT.
Tools
setup_status — Setup status
Your first-run setup path: claim your page, run the mirror baseline, import your LinkedIn export, connect your calendar, set your timezone, and (coming in Session F) choose a channel. Returns every step with whether it is done, the findability effect in words, and the exact next action (a tool call, or a URL for claim). Never refuses for an unclaimed account. Calling this while the mirror baseline is next also starts that run; call again shortly for the score.
find_people — Find people
Search people on the network by name or headline, optionally limited to those currently at an organisation, and organisations by name. Returns claimed profiles only (never imported contacts), with whether each is already one of your connections; organisations come with their claim state and headcount here. Refuses with nothing when neither query nor org is given.
- - query (optional): string — Name or headline text to match (partial names work). Also matches organisation names.
- - org (optional): string — Limit to people currently at this organisation: its handle or exact name.
- - include_orgs: boolean — Also return organisations whose name matches the query.
- - limit: integer
get_profile — Get profile
Read a person's or organisation's profile as you are allowed to see it: public fields for anyone, the connections ring when you are connected, everything for your own. Includes the structured record — certifications, languages, publications, patents, honors, volunteering, projects — each item at the visibility its owner chose; recommendations received appear only as a count (the text is the owner's alone). Updates include their attachments (photos and files: url, size, alt text). Fields the owner has not shared with you are omitted, not blanked. Never returns an email address or phone number to a stranger. Returns nothing for unclaimed people.
- - handle (optional): string — Profile handle, as in the public URL.
- - id (optional): string — Node id (person or org), e.g. from find_people.
ask — Ask a profile
Ask a question about a person; their profile answers from what they share with you (public ring for strangers, more for connections). Answers are grounded only in the profile — it will say when it does not know. Refuses to give contact details: reach people with request_connection. The owner sees the question in their next briefing. Refuses for unclaimed or unknown people.
- - handle (optional): string — Their handle, as in the public URL.
- - id (optional): string — Their node id, e.g. from find_people.
- - question: string — What you want to know about them, professionally.
set_my_state — Set my state
Set your current state: avatar (profile photo, fetched and served as our own copy), headline, summary, location, timezone, briefing cadence, links, expertise (your areas of expertise), positions (full history or just the current primary), education, certifications, languages, publications, patents, honors, volunteering, projects (each item public, connections or private), presence, mirror_exclusions (what your page leaves out on purpose), offer_to_new_members and offer_exclusions (whether people in your LinkedIn import who claim a page later are offered a connection with you), apply_photo_location (whether the city your photos agree on is added to an update with no location — on by default — or only suggested), custom_domain (serve your page from a domain you own, apex or subdomain; returns the DNS records to add and goes live once DNS and TLS check out; null or "" removes it; your handle stays your identity here), and whether connections may see your work email (work_email_visibility; never public). Before calling: show the user each proposed change next to its current value (get_profile on yourself shows them) and get their yes; for several changes, confirm them item by item and send only the ones they approved. Never write something the user has not seen. Every write replaces: each field you pass replaces the existing value (lists replace whole lists, so pass the full list; nothing appends); fields you omit are untouched; the same input twice changes nothing. Each call that changes your page is saved as a new version (list_profile_versions shows them; restore_profile_version undoes one). display_prefs hides a section's dates on every public view (e.g. { "projects": { "dates": false } } shows projects without dates; you still see them) and replaces the whole preference set ({} shows all dates again). Recommendations received cannot be set here (they come only from your own LinkedIn export, stay private, and your page shows only the count). Refuses until you have claimed a profile; refuses a custom_domain that is one of this service's own hosts, a vercel.app or supabase.co address, an IP, or a domain connected to another profile; refuses a presence whose ends_at is not after starts_at, and an avatar URL that is private or internal, not an image, or over 5 MB (in both cases nothing else in that call changes).
- - headline (optional): value — One line under your name. null clears.
- - summary (optional): value
- - location (optional): value — Free-text location label, e.g. "Boston, MA".
- - timezone (optional): string — IANA zone, e.g. "America/New_York".
- - briefing_cadence (optional): string
- - work_email_visibility (optional): string — Who may see your confirmed work email: "private" (default — only you) or "connections". It is never public and never shown to strangers, whatever you set.
- - avatar (optional): value — Your profile photo. We fetch the URL once and serve our own copy (never the original link). null removes it. Refused for private or internal addresses, non-images, and files over 5 MB. If you have no photo URL, create_upload_link gives the person a one-tap upload page.
- - links (optional): array — Your full list of external links (site, LinkedIn, GitHub, booking page). Replaces the previous list; [] clears.
- - expertise (optional): array — Your areas of expertise, most important first, e.g. ["B2B SaaS", "Employee advocacy"]. Replaces the whole list (duplicates ignored, case-insensitive); [] clears.
- - offer_to_new_members (optional): boolean — Whether people who claim a page later are told you already have them in your LinkedIn import and offered a connection with you (D58). On by default; false switches it off for everyone.
- - apply_photo_location (optional): boolean — Whether the city your photos were taken in is added to an update that has no location (D69). On by default: the update shows the city ("Phoenix, AZ", or "City, Country" outside the US) and you are told, so you can remove it. false: it is only suggested, never added. Never adds coordinates, a venue or a date; never replaces a location you gave. Replaces the setting.
- - offer_exclusions (optional): array — LinkedIn profile URLs of people in your own import who must never be offered a connection with you when they claim a page. Replaces the list; [] clears. URLs not in your import are reported, not stored.
- - mirror_exclusions (optional): array — What your page deliberately leaves out, e.g. ["employer names before 2015", "compensation"]. The mirror never counts these as gaps and your llms.txt states them as intentional. Replaces the list; [] clears.
- - position (optional): value — Shortcut: your current primary position only. Replaces the previous primary (closed with today as end date). null ends it. Use `positions` for the full history.
- - positions (optional): array — Your full position history (employment, board, advisory, volunteer). Replaces the list: positions you leave out are removed. Matched to existing rows by organisation, kind and start date.
- - education (optional): array — Your full education list. Replaces the list; [] clears.
- - presence (optional): value — Your one current presence (where you are / what you are doing). Replaces any active presence you set by hand. null clears it.
- - certifications (optional): array — Your full list of certifications and licences. Replaces the list; [] clears.
- - languages (optional): array — Languages you speak, with proficiency. Replaces the list; [] clears.
- - publications (optional): array — Your publications (articles, papers, books). Replaces the list; [] clears.
- - patents (optional): array — Your patents, granted or pending. Replaces the list; [] clears.
- - honors (optional): array — Honors and awards. Replaces the list; [] clears.
- - volunteering (optional): array — Volunteer roles. Replaces the list; [] clears.
- - projects (optional): array — Projects you want listed. Replaces the list; [] clears.
- - display_prefs (optional): object — Per-section display choices, e.g. { "projects": { "dates": false } } hides the dates of your projects on your page, llms.txt and get_profile for everyone but you. Sections: certifications, languages, publications, patents, honors, volunteering, projects. Replaces the whole set; {} shows every date again.
- - custom_domain (optional): value — Serve your public page from a domain you own, e.g. "yourname.com" or "me.yourname.com" (one per person). Returns the DNS records to add; the page switches over only after we verify your TXT record, the host verifies the domain and TLS is issued, and the product URL then redirects there. null or "" removes it. Refuses this service's own hosts, hosting-platform addresses (vercel.app, supabase.co), IP addresses and a domain already connected to another profile.
- - draft_rules (optional): array — What your networker must never propose, replacing the list ([] clears): { "kind": "no_education_before", "level": "college" } (high school / secondary), { "kind": "no_section", "section": "headline" | "summary" | "location" | "links" | "positions" | "education" | "skills" (areas of expertise) | "certifications" | "languages" | "publications" | "patents" | "honors" | "volunteering" | "projects" | "recommendations" }, { "kind": "no_org", "org": "Acme" }, { "kind": "no_dates", "section": "projects" | "certifications" | "publications" | "patents" | "honors" | "volunteering" } (no dates on suggestions for that section). Waiting drafts a rule covers are rejected.
list_profile_versions — List profile versions
Your page's history (or an organisation's you manage, with org): every change to the public and connections content, newest first — version number, date, who made it in words (you through your AI tool, your networker, your connected calendar, an admin, the system), the source (set_my_state, post_update, approved draft, claim, restore…), the draft id when a draft was approved, which version a restore brought back, and a summary that names what changed (e.g. added a photo to update 'NAMLS'; location: Phoenix, AZ → none; areas of expertise: added Negotiation; published article 'Why mediate'). Pass id for one version's full structured diff (each change with its before and after value). Read-only: it never changes anything; to go back to a version, use restore_profile_version. Private fields, intents and presences are not versioned. Refuses until you have claimed a profile, and for an organisation unless you manage it; another person's history is never shown.
- - org (optional): string — An organisation you manage (handle or node id): list its page versions instead of yours.
- - limit: integer — How many versions, newest first (default 20).
- - before (optional): integer — Paging: only versions with a version number below this one.
- - id (optional): string — One version: returns its full structured diff (every change with before and after) instead of the list.
restore_profile_version — Restore profile version
Put your page (or an organisation's page you manage) back the way it was at an earlier version from list_profile_versions. Two steps: call without confirm and nothing changes — the result first lists, in plain English, what the restore would add, remove or change (current value → restored value), then the raw diff (would_change, each change with its current and restored value); show those to the user, and only after they agree call again with confirm: true. The restore writes the old content back and is saved as a new version (history is never rewritten or deleted, so a restore can itself be undone). What it restores: headline, summary, location, photo, links, areas of expertise, display preferences, timezone, positions, education, profile items, articles (published ones the version lacks are unpublished, never deleted) and updates (updates added since are archived, not deleted); for an organisation, its page fields and updates. What it never touches: the handle, private items, verification (email-confirmed, acknowledged), intents, presences and settings. Refuses until you have claimed a profile, and refuses a version that is not yours or of an organisation you manage.
- - id: string — The version to go back to (from list_profile_versions).
- - confirm: boolean — true only after the user has seen what the restore changes and said yes. Without it nothing is written and the result lists the changes to show them.
review_drafts — Review profile drafts
List the profile changes your networker has drafted for you (from your LinkedIn export, a claim, or enrichment) and that are waiting for your decision: for each, the section, the change (add, update, or merge — a merge only fills in what an existing entry lacks), the proposed value next to the current value on your profile now, any similar entry already on your profile, whether approving needs an explicit confirm, why it was drafted, and the evidence in words. Show the person each proposed value next to its current value before approving anything. Nothing on your profile changes until you approve one with approve_draft (optionally with your own edit); reject_draft discards it (and can stop similar proposals). Also lists your "don't propose" rules, recent approvals whose findability rescore has finished, and the LinkedIn articles your import republished in full on your page (republished_articles: title, original date, URL; LinkedIn blocks AI engines from reading your articles, republishing here makes them visible) with the call that opts out of each (publish_article action "unpublish"; an opted-out article stays off your page on later imports). Shows only your own drafts; refuses until you have claimed a profile.
- - section (optional): string — Only this section, e.g. "positions", "certifications", "about", "headline", "areas of expertise".
- - limit: integer — How many drafts to return (oldest first).
approve_draft — Approve a profile draft
Approve one drafted profile change (from review_drafts), optionally with your own edit, and write it to your profile — the only way a draft reaches your profile. Before calling this, show the person the proposed value next to their current value (review_drafts returns both) and get a yes; for several drafts, confirm each one, one by one — never approve a batch on a single yes. A merge only fills in what an existing entry lacks and replaces nothing. Returns what changed and its effect on findability ("findability 62% → 71%", or "no measurable change"); when the rescore takes longer than the call allows, it finishes in the background and shows in your next pending() / review_drafts. Writes nothing, and instead returns the draft and the similar live entry side by side, when a similar entry is already on the profile or the draft would replace a value the person set themselves: call again with confirm: true only after they have seen both and said yes. Idempotent: approving an approved draft changes nothing. Refuses a draft that is not yours, one you rejected, one that expired or was superseded, a merge whose entry was removed, and an edit that does not fit the draft's fields (the reason names the field).
- - id: string — The draft id, from review_drafts or pending().
- - edit (optional): value — Your own version, to approve instead of the draft as written. For a headline, summary or location, the new text. For anything else, an object with just the fields to change (e.g. { "title": "Staff Engineer", "start_date": "2021-03-01" }); it is validated like the draft.
- - confirm (optional): boolean — Set true only after the person has seen this draft next to what is already on their profile and said yes. Required when a similar entry already exists, or when the draft would replace a value they set themselves (the first call returns both side by side instead of writing).
reject_draft — Reject a profile draft
Discard one drafted profile change; your profile is left as it is, and the same proposal is never made again (a re-import does not bring it back). Optionally, with dont_propose, also stop the networker proposing anything like it — a rule derived from this draft ({ similar: true }) or one you name (no education before college, no suggestions for a section, nothing about an organisation); the rule is returned in words so you can tell the person what it covers. Idempotent (rejecting twice is fine). Refuses a draft that is not yours, one you already approved, or one that has expired or been superseded by a newer draft.
- - id: string — The draft id, from review_drafts or pending().
- - dont_propose (optional): value — Also stop the networker proposing things like this. Either { "similar": true } (derived from this draft: its organisation, its section, or education before college for a high-school entry) or an explicit rule: { "kind": "no_education_before", "level": "college" }, { "kind": "no_section", "section": "headline" | "summary" | "location" | "links" | "positions" | "education" | "skills" (areas of expertise) | "certifications" | "languages" | "publications" | "patents" | "honors" | "volunteering" | "projects" | "recommendations" }, { "kind": "no_org", "org": "Acme" }, or { "kind": "no_dates", "section": "projects" | "certifications" | "publications" | "patents" | "honors" | "volunteering" } (no start or end dates on suggestions for that section; projects are never proposed with dates anyway). Waiting drafts the rule covers are rejected too. Rules are listed by review_drafts and replaced with set_my_state draft_rules.
declare_intent — Declare intent
Declare what you seek or offer (advising, speaking, hiring, a role, an intro…). Public intents appear on your page under "Open to"; matching_only ones never show. Idempotent: the same direction, type and title replaces the earlier intent rather than adding one. Intents expire (default 90 days). Refuses until you have claimed a profile.
- - direction: string — seeking = you want this; offering = you provide it.
- - type: string — hiring (you hire), role (you want a role), advising, mentoring, intro, collaboration, speaking, investing, vendor, other.
- - title: string — One line, e.g. "Advising early-stage B2B SaaS founders on go-to-market".
- - description (optional): value
- - criteria (optional): object — Structured criteria for matching: roles, seniority, industries, locations, stage, remote… Free-form keys.
- - visibility: string — public shows on your page as "Open to"; connections = your connections only; matching_only = never displayed, used only to match.
- - expires_in_days: integer — Intents expire (PLAN.md §3 rule 6). Default 90 days.
- - id (optional): string — An existing intent to replace. Without it, an active intent with the same direction, type and title is replaced.
withdraw_intent — Withdraw intent
Withdraw (or mark fulfilled) one of your intents by id, or every active intent of a given direction and type. Idempotent: withdrawing something already inactive changes nothing. Refuses without an id or a direction+type, and never touches anyone else’s intents.
- - id (optional): string — The intent to withdraw (from declare_intent or get_profile on yourself).
- - direction (optional): string
- - type (optional): string — With direction: withdraw every active intent of this kind.
- - fulfilled: boolean — true marks it fulfilled (it happened) instead of withdrawn.
post_update — Post update
Add a dated entry to your activity: a new role, something shipped or published, a talk, a milestone, an event. Public updates appear on your page and its text twin; a future-dated one appears under "Upcoming" (and as a schema.org Event for speaking/event) until its date, then under Recent activity. Fan-out (on by default): a future-dated update with a location also sets a connections-only presence for its dates (type event for speaking/event, else location), with day boundaries in your timezone; for speaking/event the result offers a connections-only intent "Meeting attendees at {title}" expiring the day after the event — offered, never created: pass fan_out.intent_offer.declare_with to declare_intent if wanted. The summary says what was set. Pass fan_out: false to set nothing (and undo an earlier fan-out); replacing the update re-syncs the presence, archiving it dismisses it. Idempotent: an update with the same id, URL, or type and title is replaced, not duplicated; pass id + archive to take one down. This is your changelog, not a feed — nothing is broadcast. Attachments (images, PDFs) and the link's preview image are fetched once and served as our own copy (D32). A future-dated update with a location also sets a connections-only presence there (fan_out, default on; day boundaries in your timezone); for speaking/event types it offers — never creates — an intent to meet attendees (fan_out.intent_offer.declare_with → declare_intent). Location from photos (on by default; set_my_state.apply_photo_location): when the update has no location, is not future-dated, and its photos agree on a city, that city is added at city level ("Phoenix, AZ"; "City, Country" outside the US) and the result says so in location_applied (only when this call adds it; re-posting an update that already has that city from its photos says nothing) — tell the person "Added Phoenix, AZ from your photos — tell me to remove it"; location_applied.remove_with (location: null) takes it off. Photos that disagree, or give only a region, are offered instead. When photos say where or when they were taken and the update lacks it, the result offers context_suggestion.apply_with — apply it only after the person confirms; a date is offered only for an update posted without one, never applied unasked; only fields the update lacks are offered, and an offer already closed (applied, superseded, or its field set since) is never made again. Refuses until you have claimed a profile, an ends_at that is not after occurred_at, archiving an update that is not yours, and the whole call if an attachment is private or internal, not an image or PDF, or over 10 MB.
- - type: string
- - title (optional): string — The dated proof point in one line, e.g. "Launched EveryoneSocial Compliance". Required unless archiving.
- - body (optional): value
- - url (optional): value — The canonical link: the post, talk, release, article. Without attachments, its preview image (og:image) is fetched and shown with the update.
- - attachments (optional): array — Images or PDFs to show with the update (≤ 6; images ≤ 10 MB). Each URL is fetched once and served from our storage, never hot-linked. Replaces the update's attachments; [] clears. Omit to keep them. Without attachments, the preview image of `url` (og:image) is used automatically.
- - occurred_at (optional): value — When it happened or will happen (YYYY-MM-DD, or an ISO datetime). Defaults to now. A future date puts it under "Upcoming" until then.
- - ends_at (optional): value — For multi-day events: the last day (YYYY-MM-DD, counted to the end of that day) or an ISO datetime. Must be after occurred_at. Leave out for a single day.
- - location (optional): value — Where it happens, free text, e.g. "Lisbon, Portugal" or "FIL, Lisbon". Shown with the update. Omitted on an update with no location: the city its photos agree on is added (unless the person turned that off). null or "" removes the location and keeps photos from adding one back.
- - fan_out: boolean — Future-dated and located: also set a connections-only presence for those days (in your timezone). For speaking/event an intent "Meeting attendees at …" is offered in the result, not created. Default true; false sets nothing and undoes an earlier fan-out.
- - related_org (optional): string — An organisation this is about (e.g. the employer for new_role); resolved or created as a public org shell.
- - visibility: string
- - id (optional): string — An existing update to replace.
- - archive: boolean — With id: archive it instead (removes it from your page).
publish_article — Publish article
Write, edit, unpublish or republish a long-form article on your page, at <your profile URL>/articles/<slug>: title, optional dek, markdown body, publish date, optional canonical link. A public article is listed under Publications on your page, in your llms.txt and the sitemap, and carries schema.org Article JSON-LD with you as author — this site is its canonical home unless you pass canonical_url. Actions: create (title + body; slug from the title unless given), edit (by slug or id; only the fields you pass change; changing title, dek or body stamps "updated"), unpublish (takes it off your page; nothing is deleted; also how to opt out of a LinkedIn article your import republished), publish (puts an unpublished or draft article back). Calling create again with the same title replaces that article rather than duplicating it. Every change is a profile version (restore_profile_version undoes it). Show the person the title, dek and opening before publishing. Refuses: posting to LinkedIn or anywhere else (never — there is no LinkedIn API, by design); a body over 200,000 characters; a slug already used by another of your articles (pick another or edit that one); a canonical_url that is not https; an article that is not yours; and everything until you have claimed a profile. Email addresses and phone numbers in the text are withheld ("[contact withheld]") wherever a stranger reads it.
- - action: string — create a new article; edit one (only the fields you pass change); unpublish one (it leaves your page, nothing is deleted); publish one that is unpublished or a draft.
- - slug (optional): string — create: the URL slug (default: from the title, made unique). edit / unpublish / publish: which article (or pass id).
- - id (optional): string — edit / unpublish / publish: the article id (instead of slug).
- - new_slug (optional): string — edit: rename the URL slug. The old URL stops working.
- - title (optional): string — create: required. The headline.
- - dek (optional): value — A one- or two-sentence standfirst under the headline; also the description search and AI engines show. null removes it.
- - body (optional): string — create: required. The article in markdown (headings, lists, links, emphasis, quotes, https images). Raw HTML is shown as text, never run. At most 200,000 characters.
- - date_published (optional): value — When it was first published (YYYY-MM-DD or ISO datetime). Default: now on first publish. Pass the original date when bringing over a piece published elsewhere.
- - canonical_url (optional): value — Only if the piece's canonical home is elsewhere (your own blog): https URL, used as rel=canonical. null removes it. Leave out to make this page canonical.
- - visibility (optional): string — Who can read it. Default public (on your page, in llms.txt, the sitemap and JSON-LD); connections: only your connections; private: only you.
request_connection — Request connection
Ask someone on the network to connect, with context. This is the contact protocol: the request reaches them through their own AI tool or briefing channel, never by email. Refuses without context, for unclaimed or unknown people, for yourself, and where either side has blocked the other; a request they declined is not repeated. Idempotent: asking again returns the existing request; if they had already asked you, both of you are connected.
- - handle (optional): string — Their handle, as in the public URL.
- - id (optional): string — Their node id, e.g. from find_people.
- - context_type: string — How you know each other, or why this makes sense.
- - context: string — The specific context: "worked together at Acme 2019–2022", "met at Web Summit 2026", "introduced by Dana Kim", or why you are reaching out.
- - message (optional): string — A short note to them.
respond_connection — Respond to a connection request
Accept or decline a connection request someone sent you, or withdraw one you sent. Accepting makes you connections (they gain the connections ring of your profile). Refuses for requests you are not part of, for requests already answered, and for accepting your own request.
- - connection_id: string — From pending() (subject_id of a connection_request event) or request_connection.
- - action: string — accept/decline a request made to you; withdraw one you made.
find_intro_path — Find an intro path
Find who among your own connections can introduce you to someone: returns paths "you → connector → them", best first, ranked by how well each connector knows both sides (order only, never a score). Only your direct connections who are connected to the person and open about their connections are shown; anyone blocked on either side is left out. The person is never told you looked. Refuses for unknown or unclaimed people and for yourself; if you are already connected it says so. Follow up with request_intro, or request_connection with context when there is no path.
- - handle (optional): string — Their handle, as in the public URL.
- - id (optional): string — Their node id, e.g. from find_people.
- - limit: integer — How many connectors to return, best first (1–10, default 5).
request_intro — Request an introduction
Ask one of your connections to introduce you to someone they are connected to, with a reason. Only the connector is notified (in their AI tool, or by email); the person you want to meet is told nothing unless the connector forwards it. Refuses without a reason, for unknown or unclaimed people, for yourself, when the connector is not your connection, not connected to the person, or keeps their connections list private, where anyone involved has blocked another, when you are already connected, and for an ask declined in the last 90 days. Idempotent: asking again returns the open request. Use find_intro_path first to see who can introduce you.
- - connector: string — The connection of yours who would introduce you: their handle (as in the public URL) or their node id (from find_people / find_intro_path).
- - target: string — The person you want to meet: their handle (as in the public URL) or their node id (from find_people / find_intro_path).
- - reason: string — Why you want to meet them, specifically. The connector sees it; if they forward, so does the person.
- - message_to_connector (optional): string — A note only the connector sees.
- - message_to_target (optional): string — A note the person sees if the connector forwards the intro.
respond_intro — Respond to an intro request
Answer an introduction request. As the connector: forward (the person then sees the request, the reason and your note) or decline (the requester is told, with your note only if you write one; the person is never told). As the person being introduced, once forwarded: accept (you become connections) or decline (the requester is told only that it ended). As the requester: withdraw while it is open. Refuses for intros you are not part of, actions that are not your role's (only the connector forwards, only the person accepts, only the requester withdraws), intros already answered or expired, and accepting where either side has blocked the other.
- - intro_id: string — From pending() (subject_id of an intro_request / intro_forwarded event) or request_intro.
- - action: string — Connector: forward or decline. Person being introduced (once forwarded): accept or decline. Requester: withdraw.
- - note (optional): string — Connector forwarding: a note the person sees. Declining: an optional reason the requester sees (nothing is sent if you leave it out).
pending — Pending
What needs you now: interrupt-priority events (requests, matches, proposals, co-presence, and photo context on your updates: a city the upload page added from your photos — tell the person and offer payload.applied.remove_with — or a place/date offered as payload.apply_with, applied only after they confirm; a photo notice is closed once applied, superseded by a newer one for the update, or once its field is set by any path, and never comes back), open proposals in your threads, and your latest briefing, if you have one. Marks the returned interrupt events delivered, so each is returned once; the latest briefing is returned every call until a newer one replaces it — a channel delivery (email, etc.) and this tool both read the same brief. Briefing-priority items other than the latest brief wait for your next briefing and are never returned here. Never refuses for an unclaimed account (PLAN.md D52): with no person row yet, or with nothing else pending, it returns your next first-run setup step instead.
get_briefing — Get briefing
Returns your latest brief: the meeting-prep brief (who you are meeting today and what to know before each one) or the network brief (role changes, ranked updates, upcoming overlap, reconnect nudges and open intros across your connections, set_briefing_prefs for cadence). Pass kind to pick one; omit it for whichever is newest. regenerate: true rebuilds today’s meeting brief from your calendars (after a meeting changed) and emails it in about a minute. Reading refuses nothing — it is empty until there is something to show (connect_calendar for meetings). regenerate refuses the network brief, any day but today, no connected calendar, and more than one per 15 minutes or three per day (it says when you can try again).
- - kind (optional): string — Which brief to read: "meetings" (day-of prep) or "network" (D43 digest). Omit for whichever is newest of the two.
- - date (optional): string — For "meetings", the exact local day. For "network", a day its period covers. Omit for the latest.
- - regenerate (optional): boolean — true: rebuild today’s meeting brief from your calendars now (after a meeting was added or changed) and email it; it shows here in about a minute. Meetings brief, today only (kind "meetings" or omitted; date omitted or today). At most one per 15 minutes and three per day.
set_briefing_prefs — Set briefing prefs
Set your network briefing cadence (daily, weekly, or off), the day and hour it arrives (your timezone), and which sections it includes (changes, updates, presence, reconnect, intros). Idempotent replace semantics: weekday/hour/sections you pass replace those keys; sections replaces the whole object (a key you leave out resets to on); fields you omit are untouched — nothing appends. send_now also enqueues one for today right away (even with cadence off, sent as weekly); if today’s was already sent this returns "already sent", never a duplicate. The meeting brief (day-of-meetings prep) is separate and unaffected. Refuses nothing but an invalid hour/weekday (1–23 / 1–7) and an unclaimed profile.
- - cadence (optional): string — How often you get a network briefing. Replaces persons.briefing_cadence.
- - weekday (optional): integer — ISO weekday for a weekly briefing, 1 = Monday. Ignored for a daily cadence.
- - hour (optional): integer — The local hour (your timezone) the briefing is prepared. Default 7.
- - sections (optional): object — Which sections to include: changes, updates, presence, reconnect, intros. Replaces the whole object — a key you leave out resets to on; pass false to turn one off.
- - send_now (optional): boolean — Also enqueue a network briefing for today, in your timezone, right away (cadence "off" still sends one, as weekly). If one for today under the same cadence was already sent or is already running, this comes back "already sent" rather than sending a second one — honest, not a forced resend.
connect_calendar — Connect calendar
Lists the Google Calendars the user has connected (account, status, last sync) and returns the link the user opens in a browser to connect another — a work and a personal calendar can be connected at the same time, and connecting one never disconnects another; meetings on both are counted once. The calendar powers four things for their networker: a brief before each day of meetings (get_briefing); a private picture of who they really know, built from who they actually meet, which ranks introductions; presence, so travel and events can surface connections who will be in the same place; and keeping their page current by catching a new role or company early. A calendar that needs reconnecting comes with its own reconnect link. Refuses to connect anything itself: the user must open the link and sign in with Google; this tool never receives Google credentials and cannot disconnect a calendar (that is on their account page). Read-only access to the next seven days; the calendar cache is private to the user and nothing from it is shared. Refuses until you have claimed a profile.
list_data_sources — List data sources
Lists the data sources you have connected for your networker (e.g. Google Calendar — several accounts can be connected at once, such as work and personal): type, status (active, error, revoked, pending), the connected account, when it last synced, and the last error if any; a calendar in error comes with its reconnect link in the summary. Only your own sources; never anyone else's, and never credentials or tokens. Refuses until you have claimed a profile.
who_will_be_in — Who will be in
Lists which of your connections have told their networker they will be in a place during a window (a trip, a conference, a talk), with where and when. Ring-filtered: only connections' presences (and presences their owners made public); never strangers' connections-only presences; never your own. Refuses a window longer than 90 days or one where to is not after from. Refuses until you have claimed a profile.
- - place: string — A city, venue or event name, e.g. "Lisbon" or "Web Summit". Matched loosely against where people said they will be.
- - from: value — Start of the window (YYYY-MM-DD = start of that day, or an ISO datetime).
- - to: value — End of the window (YYYY-MM-DD = end of that day, or an ISO datetime). After from; at most 90 days later.
create_upload_link — Create upload link
Create a one-tap upload page (a short-lived link) where the person can add a photo, an organisation logo, files for one of their updates, or their LinkedIn data export from their phone or computer. For images: offer it ONLY when auto-sourcing found nothing and you have no image URL to pass to set_my_state.avatar / update_org.logo / attachments — never as the first step. Photos may be JPEG, PNG, WebP, GIF or HEIC (iPhone); each is converted to a web format, turned the right way up and published with no EXIF or location data. An attachment link takes several files at once (as many as the update has room for, 6 in all) and reports each file's result; when the update has no location and the photos agree on a city, that city is added ("City, ST" / "City, Country"; the person can turn this off with set_my_state.apply_photo_location) and the page and pending() say so — tell the person they can have it removed; a date is only offered when the update has none, and applied only after the person confirms. Avatar and logo links take one file. For purpose "linkedin_export": the page takes the archive zip LinkedIn emails, or any CSVs from it (up to 50 MB). Everything stays private to the importer: contacts, message counts (never message text) and follows go to their private graph, files about them become profile drafts they approve, email-address and phone-number files are never read, and only a trimmed private copy is kept for re-reading — nothing about the contacts is ever published. Returns import_run_id; import_status reports each file's result, the members already here and the drafts being prepared. The link expires after ttl_minutes (default 15, max 60) and works once. Refuses when the caller has no profile, when the caller does not manage the organisation (logo), when an attachment has no update_id or the update is not theirs, when a purpose is given the wrong target (an avatar for an organisation, a logo without one, a LinkedIn export with org or update_id).
- - purpose: string — avatar: your profile photo. logo: an organisation's logo (needs `org`). attachment: a file on one of your updates (needs `update_id`). linkedin_export: your LinkedIn data archive (.zip) or any CSVs from it, imported privately (no `org`, no `update_id`).
- - org (optional): string — For a logo (or an org update's attachment): the organisation's handle or node id. You must manage it.
- - update_id (optional): string — For purpose "attachment": the update the file belongs to (yours, or your organisation's).
- - ttl_minutes: integer — How long the link works. Default 15, max 60 minutes.
import_status — LinkedIn import status
Report on your LinkedIn export import: status (queued / running / succeeded / failed), each file in the upload with its bucket (about you → profile drafts; about others → your private graph; discarded, with the reason — email-address and phone-number files are never read), rows seen and used, counts (contacts imported, updated, matched to members, skipped), when it started and finished, and any error. For a finished import it lists the members already on the network you are connected with on LinkedIn, each with a ready request_connection suggestion — nothing is sent until you call request_connection — and says whether profile drafts from the files about you are being prepared or waiting for approval, and which of your published LinkedIn articles were republished in full on your page (title, original date, URL — LinkedIn blocks AI engines from reading your articles; republishing here makes them visible) with the call that opts out of each (publish_article action "unpublish"; it stays off your page on later imports). Never-published LinkedIn drafts are never republished. With no import yet, it explains how to get the export from LinkedIn and to call create_upload_link with purpose "linkedin_export". Imported contacts stay private to you: this never returns their emails or anything else from the file. Refuses without a profile; someone else's run is reported as not found.
- - run_id (optional): string — A specific import run (from create_upload_link). Default: your latest import.
list_connection_offers — List connection offers
Members who already have you in their LinkedIn connections (their own import) and are not yet connected with you: name, handle, the year their export says you connected, and whether accepting connects you now (matched on your confirmed email) or sends them a request (matched on the LinkedIn link you added). Also counts the profile drafts from your claim still waiting. Shows nothing else from their import — never their email, messages or notes. Refuses until you have claimed a profile.
accept_connection_offers — Accept connection offers
Connect with members who already have you in their LinkedIn connections (their own import). Pass importer_ids for some, or all: true for every current offer. An offer matched on your confirmed email connects you now and tells them; one matched only on the LinkedIn link you added sends them a connection request with that context instead, because a typed link is not verified. Idempotent: repeating returns the same people as already connected or already requested. Refuses without importer_ids or all, and lists anyone who is no longer an offer (removed you from their import, left, or a connection is not possible) under not_eligible without saying which.
- - importer_ids (optional): array — Members to accept, from list_connection_offers or pending() (offers[].importer_id).
- - all (optional): boolean — true accepts every current offer at once.
reject_claim_drafts — Start clean
Start clean: reject every pending profile draft the networker made when you claimed your page (from what members who know you shared and from public sources). Your profile is not changed — drafts never wrote it — and rejected drafts are not proposed again. Leaves drafts from your own LinkedIn import alone. Idempotent: with nothing pending it rejects nothing. Refuses until you have claimed a profile.
update_org — Update organisation
Set an organisation's page fields: description, website, industry, size, headquarters, logo (fetched and served as our own copy), links, legal name, mirror_exclusions (what the page leaves out on purpose), handle. Replace semantics: only the fields you pass change; null clears one; links replaces the whole list. Refuses unless you manage the organisation (editor or above; the handle needs an admin or the owner). Claim state and verification are never editable here. Refuses a logo URL that is private or internal, not an image, an SVG with active content, or over 5 MB (and changes nothing else in that call).
- - org: string — The organisation: its handle (as in the public URL) or node id.
- - name (optional): string — Display name.
- - legal_name (optional): value
- - description (optional): value — What the organisation does, for whom. The first thing an assistant reads. null clears.
- - website (optional): value
- - industry (optional): value
- - size_band (optional): value — Headcount band.
- - hq (optional): value — Headquarters, free text, e.g. "Boston, MA".
- - logo (optional): value — The organisation's logo. We fetch the URL once and serve our own copy (never the original link). null removes it. Refused for private or internal addresses, non-images, SVGs with scripts or external references, and files over 5 MB. If there is no logo URL, create_upload_link gives an admin a one-tap upload page.
- - logo_url (optional): value — Deprecated: use `logo: { url }`. Goes through the same fetch-and-store pipeline; the raw URL is never stored.
- - links (optional): array — The full list of external links (LinkedIn, careers page, blog…). Replaces the previous list; [] clears.
- - mirror_exclusions (optional): array — What the page deliberately leaves out, e.g. ["revenue figures", "client names"]. The audit never counts these as gaps and llms.txt states them as intentional. Replaces the list; [] clears.
- - handle (optional): string — Set the public handle (owners and admins).
publish_update — Publish organisation update
Publish a dated announcement on an organisation's page (launch, milestone, hiring push, event). Public; appears under Recent activity and in the text twin. Idempotent: the same URL, or the same type and title, replaces rather than duplicates; pass id + archive to take one down. Attachments (images, PDFs) and the link's preview image are fetched once and served as our own copy. Nothing is broadcast (no feed). Refuses unless you manage the organisation (editor or above); refuses the whole call if an attachment is private or internal, not an image or PDF, or over 10 MB. A link without a preview image never fails the call.
- - org: string — The organisation: its handle (as in the public URL) or node id.
- - type: string
- - title (optional): string — The dated proof point in one line, e.g. "Launched Compliance for regulated teams". Required unless archiving.
- - body (optional): value
- - url (optional): value — The canonical link: the post, release, event page. Without attachments, its preview image (og:image) is fetched and shown with the update.
- - attachments (optional): array — Images or PDFs to show with the update (≤ 6; images ≤ 10 MB). Each URL is fetched once and served from our storage, never hot-linked. Replaces the update's attachments; [] clears. Omit to keep them. Without attachments, the preview image of `url` (og:image) is used automatically.
- - occurred_at (optional): value — When it happened (YYYY-MM-DD). Defaults to now.
- - id (optional): string — An existing update of the organisation to replace.
- - archive: boolean — With id: take it down.
declare_org_intent — Declare organisation intent
Declare what an organisation seeks or offers — open roles (hiring), suppliers, collaborators, speakers. Public intents appear on the organisation's page under "Open to" and in its JSON-LD as Demand/Offer. Idempotent: the same direction, type and title replaces; pass withdraw (with id or the same triple) to take one down. Intents expire (default 90 days). Refuses unless you manage the organisation (editor or above).
- - org: string — The organisation: its handle (as in the public URL) or node id.
- - direction: string
- - type: string — hiring (open roles), vendor (looking for suppliers), collaboration, speaking (looking for speakers), investing, intro, other.
- - title (optional): string — One line, e.g. "Hiring a Head of Customer Success (remote, US)". Required unless withdrawing.
- - description (optional): value
- - criteria (optional): object — Structured criteria for matching: roles, seniority, locations, remote, comp band… Free-form keys.
- - visibility: string — public shows on the page under "Open to"; matching_only is never displayed.
- - expires_in_days: integer
- - id (optional): string — An existing intent of the organisation to replace or withdraw.
- - withdraw: boolean — With id (or direction + type + title): withdraw it.
- - fulfilled: boolean — With withdraw: mark it fulfilled (the role was filled) rather than withdrawn.
confirm_employee — Confirm employee
Acknowledge, as the organisation, that a person's current position here is real (association state → org_acknowledged, shown as ✓✓ on both pages). Additive: it never removes or edits their position. Refuses unless you are an owner or admin of the organisation, and when the person has no current position at it.
- - org: string — The organisation: its handle (as in the public URL) or node id.
- - handle (optional): string — Their handle, as in the public URL.
- - id (optional): string — Their node id, e.g. from find_people.
invite_admin — Invite co-admin
Invite someone to help manage an organisation's page by work email. They get a link; signing in with that address grants the role (an account is created for them if needed, with no public page). Idempotent: an open invitation to the same address is returned, not resent. Editors can invite editors only; admins and owners invite editors or admins; nobody invites an owner. Refuses unless you manage the organisation.
- - org: string — The organisation: its handle (as in the public URL) or node id.
- - email: string — Their work email. They sign in with it and the role is granted; no public page is created for them (D28).
- - role: string — editor: edit the page, publish, declare intents, invite editors. admin: also acknowledge employees, resolve claims, set the handle. Ownership is transferred, never invited.
get_org_audit — Organisation AEO audit
What AI says about an organisation from its public page: findability score, what an assistant could state, and the gaps to close — with the change since the previous run (closed, opened, remaining). Pass run: true to audit the page now; otherwise returns the latest stored audit. Also reports completeness: description, people listed and confirmed, open intents, announcements, claim state, pending claims. Refuses unless you manage the organisation.
- - org: string — The organisation: its handle (as in the public URL) or node id.
- - run: boolean — Run a fresh audit now (takes ~20–40 s) instead of returning the latest stored one.
Rules the server enforces
- Rings: a profile shows only the fields its owner set to your ring (public, connections, or a named grant) — fields you do not see were not shared, not blanked.
- No cold messaging: a thread exists only behind a connection, an accepted introduction, or a mutual match.
- Contact details are never public: MyConnections does not publish email addresses or phone numbers.
- Writes are idempotent: set_my_state, declare_intent and post_update replace what was there; the same call twice changes nothing the second time.
- Every tool call is logged, attributed to the account it acts on behalf of — the primary signal the owner sees.
- When a tool refuses, read the reason: it names the rule that stopped you and how to satisfy it.