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

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.

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.

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.

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).

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.

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.

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.

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).

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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).

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.

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.

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).

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.

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.

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).

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.

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).

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.

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.

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.

Rules the server enforces

More