Porchlight for AI assistants
The API and MCP server
What it's for
Let an AI assistant (like Claude) or a program of your own see your gatherings and, if you allow it, say whether you're coming and sign you up to bring something or help out. Ask it "what's on this week?", "am I bringing anything Sunday?" or "tell them I'm coming to the retreat, just me". If you lead a group, it can run your gatherings too: "move Thursday's study to Friday at 7" or "cancel the 19th and tell everyone why".
It works as you, with your key: it sees what you see in the app, and nothing more. Home addresses follow the same rules as in the app. Group chats, prayer requests and notes are end-to-end encrypted on your phone, so they aren't available here: Porchlight's servers can't read them either.
Get a key
In Porchlight, tap your picture (top right), then AI assistants and API. Name the key after what will use it, and turn on Can answer and sign up for me if it should do more than look. If you lead a group, you can also turn on Can plan and change gatherings I run, so it can put gatherings on the calendar, change or move them, and cancel them. Then tap Make key.
Keys start with plk_. Copy yours straight away: it's shown only once (we keep only a scrambled copy that can check it, not the key itself). Treat it like a password. To change what it can do, tap what it says beside its name (like Can answer): the same key works the new way at once. Remove it on the same screen and it stops working at once.
Connect an assistant (MCP)
Porchlight is an MCP server, for assistants that speak the Model Context Protocol. It's at https://porchlight.tech/mcp (Streamable HTTP), and takes your key in a header: Authorization: Bearer plk_…. The tools are the operations below; a key gets only the ones it's allowed to do (one that can only look gets only the ones that look).
With Claude Code:
claude mcp add --transport http porchlight https://porchlight.tech/mcp --header "Authorization: Bearer plk_your-key"
Other assistants and tools that can connect to a remote MCP server with a header work the same way.
Connect by signing in (claude.ai, ChatGPT and others)
Assistants that add an MCP server as a connector, and sign you in themselves, don't need a key. Add a custom connector with the address https://porchlight.tech/mcp. It sends you to Porchlight, where you sign in if you need to and choose whether it can only look or also Can answer and sign up for me (and, for a group's leader, Can plan and change gatherings I run). Tap Allow, and you're back where you started, connected.
It's listed under AI assistants and API with your keys, marked Connected. Remove it there and it's disconnected at once. Tap what it says beside its name to change what it can do, without connecting again.
For developers: this is OAuth 2.1 as the MCP specification describes it. A 401 from /mcp points to /.well-known/oauth-protected-resource, which names Porchlight as the authorization server (/.well-known/oauth-authorization-server). Clients register themselves (dynamic client registration, at /oauth/register), send people to /oauth/authorize with PKCE (S256 only), and swap the code at /oauth/token. Scopes are read, write and manage (each includes the one before); the person picks, so a token may get less than was asked for, and its scope says what it got. Access tokens last an hour; refresh tokens last 90 days and are replaced each time they're used.
Use the API
The same operations, as a plain web API: https://porchlight.tech/api/v1, with your key in the Authorization header, JSON in and out. Dates are YYYY-MM-DD and times HH:MM, in the group's own time zone.
curl https://porchlight.tech/api/v1/gatherings?days=7 \ -H "Authorization: Bearer plk_your-key"
When something doesn't work, you get an HTTP error and { "error": "…" } saying why, in words you can pass on: 400 (something's missing or wrong), 401 (no key, or it's been removed), 403 (not allowed: a key that can only look, say, or a gathering you don't run), 404 (no such gathering, or not one you can see) or 409 (it's taken already). There's an OpenAPI description at https://porchlight.tech/api/v1/openapi.json.
What it can do
Who you are
Any keyGET /api/v1/me
MCP tool: get_profile
The person this key belongs to: their name, email, and their household (the people one answer covers).
Returns: Your name and email, and your household's name and people (or null).
Your groups
Any keyGET /api/v1/groups
MCP tool: list_groups
Every group you're in: its name, what kind of group it is, how many people, whether you lead it, and when it next meets.
Returns: A list of groups, each with its id, name, kind, member count, your role and its next gathering's date.
Coming up
Any keyGET /api/v1/gatherings
MCP tool: list_gatherings
Gatherings coming up in your groups, soonest first: when and where, your answer, how many are coming, and what's still needed. Use it to answer "what's on this week?" or to find a gathering's id.
group_id(query, text): Only this group's (from list_groups). Leave out for all your groups.days(query, number): How far ahead to look, in days (1 to 120). Leave out for everything scheduled.
Returns: A list of gatherings (see Gathering).
One gathering
Any keyGET /api/v1/gatherings/{gathering_id}
MCP tool: get_gathering
Everything about one gathering that you can see in the app: when and where, the details and agenda, the menu (what's needed, who's bringing what), jobs, who's coming, and your answer. Ids of menu items and jobs are what sign_up takes.
gathering_id(in the path, text, required): The gathering's id, from list_gatherings.
Returns: The gathering, with its agenda, menu, jobs, who's coming and the people your answer covers.
What you signed up for
Any keyGET /api/v1/commitments
MCP tool: list_commitments
Everything you've said you'll do, across your groups: hosting, jobs, dishes, meal crews and rides, soonest first.
Returns: A list of commitments, each with the gathering, its date and what you're doing.
Your notifications
Any keyGET /api/v1/notifications
MCP tool: list_notifications
Your recent notifications in Porchlight, newest first: reminders, changes to gatherings, invitations and the like.
unread_only(query, true or false): Only the ones you haven't read yet.
Returns: A list of notifications, each with its title, text, when, and whether it's been read.
Your Secret Santa person
Any keyGET /api/v1/secret-santa
MCP tool: get_secret_santa
Every Secret Santa you're in, with the person you're shopping for once you've revealed them in the app: their name, their wishlist (each question with their answer, links included), your anonymous Secret Santa messages with them (reading them here doesn't mark them read), and what you've said you're getting them. Also the party's date and budget. Their email, phone and address aren't included. If you haven't revealed your person yet, open Secret Santa in the app first: the reveal is yours to do.
Returns: A list of Secret Santas, each with the party and your person (or why there isn't one yet).
Say what you're getting them
Needs a key that can answerPUT /api/v1/secret-santa/{gathering_id}/gift
MCP tool: set_secret_santa_gift
Note what you're getting your Secret Santa person, as you can in the app. Only you (and someone running it who isn't playing) can see it, so the same gift isn't bought twice. Send an empty gift to clear it.
gathering_id(in the path, text, required): The Christmas party's id, from get_secret_santa.gift(JSON body, text, required): What you're getting them ("A board game and hot sauce"), up to 500 characters.
Returns: What you're getting them, as saved.
Say if you're coming
Needs a key that can answerPUT /api/v1/gatherings/{gathering_id}/rsvp
MCP tool: answer_gathering
Answer for a gathering: going, maybe, or not going. In a household, one answer covers everyone in it; to say only some of them are going, list their keys (from get_gathering's my_household). If it's full, going puts you on the waitlist.
gathering_id(in the path, text, required): The gathering's id, from list_gatherings.status(JSON body, text, required): Your answer. One ofgoing,maybe,not_going.who(JSON body, list of text): In a household: the keys of who's going (from my_household). Leave out for everyone.note(JSON body, text): A short note with your answer ("Running 10 minutes late").
Returns: The gathering, with your answer as it now stands.
Bring something, or take a job
Needs a key that can answerPOST /api/v1/gatherings/{gathering_id}/signups
MCP tool: sign_up
Sign yourself up for something on a gathering: a dish on the menu, a job (childcare, setup), a meal crew or shared gear. Use the item's id from get_gathering. For an open-ended dish ("an entree"), say what you'll bring in note.
gathering_id(in the path, text, required): The gathering's id, from list_gatherings.item_id(JSON body, text, required): The menu item's or job's id, from get_gathering.note(JSON body, text): For a dish: what you're bringing ("Grandma's lasagna").
Returns: The gathering, with you signed up.
Let something go
Needs a key that can answerDELETE /api/v1/gatherings/{gathering_id}/signups/{item_id}
MCP tool: drop_sign_up
Take yourself off something you signed up for on a gathering (a dish, a job, a crew), so someone else can.
gathering_id(in the path, text, required): The gathering's id, from list_gatherings.item_id(in the path, text, required): The menu item's or job's id.
Returns: The gathering, without you on it.
A gathering's food plan
Any keyGET /api/v1/gatherings/{gathering_id}/food-plan
MCP tool: get_food_plan
For a gathering whose group plans its meals with a food plan (with meal crews): each meal (its day, name, its crew's note such as "Pick up the pizzas", and who's on its crew), its dishes (what's good to know, prep notes, and each ingredient as planned and as worked out for how many are coming), how many it's for (grown-ups, kids at half a portion, and anyone more the planner added), and the shopping list for every meal together, by section of the store. Whoever plans the food also sees what's been ticked off.
gathering_id(in the path, text, required): The gathering's id, from list_gatherings.
Returns: The meals with their dishes and ingredients, how many it's for, and the shopping list.
Plan a dish for a meal
Needs a key that can plan gatheringsPOST /api/v1/gatherings/{gathering_id}/food-plan/dishes
MCP tool: set_food_plan_dish
Add a dish to one of a gathering's meals, or change one (with dish_id): its name, what's good to know, prep notes for whoever cooks it, and its ingredients. Give each ingredient's amount per person ("4" oz of pasta a person), per so many people (1 jar of parmesan per 12), or as a set amount for the whole meal; the app works out how much to buy from who's coming. For something bought as it is (chips, 3 bags in total), give it one ingredient with the dish's own name. Only the group's leaders, the gathering's organizer and its meal planner can. Replacing a dish replaces all its ingredients.
gathering_id(in the path, text, required): The gathering's id, from list_gatherings.meal_id(JSON body, text, required): The meal (its crew's id), from get_food_plan.dish_id(JSON body, text): To change a dish already planned: its id, from get_food_plan.name(JSON body, text, required): What it is ("Spaghetti and meat sauce").tags(JSON body, list of text): What's good to know about it: any of Vegetarian, Vegan, Gluten-free, Dairy-free, Contains nuts, Spicy, or "contains:" and an allergen of its own ("contains:Peanuts").prep_notes(JSON body, text): For whoever's cooking it: how to make it, when to start, what's where.ingredients(JSON body, list of text): Everything it takes.
Returns: The food plan, with the dish as saved.
Take a dish off a meal
Needs a key that can plan gatheringsDELETE /api/v1/gatherings/{gathering_id}/food-plan/dishes/{dish_id}
MCP tool: remove_food_plan_dish
Take a dish off a gathering's food plan. Only the group's leaders, the gathering's organizer and its meal planner can.
gathering_id(in the path, text, required): The gathering's id, from list_gatherings.dish_id(in the path, text, required): The dish's id, from get_food_plan.
Returns: The food plan, without it.
Count more people in
Needs a key that can plan gatheringsPUT /api/v1/gatherings/{gathering_id}/food-plan/extra
MCP tool: set_food_plan_extra
How many more people to cook for than have said they're coming (people you know are coming who haven't answered yet). Only the group's leaders, the gathering's organizer and its meal planner can.
gathering_id(in the path, text, required): The gathering's id, from list_gatherings.extra(JSON body, number, required): How many more (0 for none).
Returns: The food plan, with the new numbers.
Plan a gathering
Needs a key that can plan gatheringsPOST /api/v1/groups/{group_id}/gatherings
MCP tool: create_gathering
Put a new one-time gathering on a group's calendar, in a group you lead. Everyone in the group hears about it unless notify is false. Confirm the details with the person first.
group_id(in the path, text, required): The group's id, from list_groups (one where your role is leader).title(JSON body, text, required): What it's called ("Sunday Dinner").date(JSON body, text, required): The day it's on (or starts), as YYYY-MM-DD.end_date(JSON body, text): For one that runs over several days (a retreat or trip): its last day, as YYYY-MM-DD.start_time(JSON body, text): When it starts, on a 24-hour clock ("18:30").end_time(JSON body, text): When it ends, on a 24-hour clock.location(JSON body, text): Where, as people would say it ("Grace Church", "The Parks' house").address(JSON body, text): The street address, shown only to the people the app shows addresses to.details(JSON body, text): What people should know (up to 2,000 characters).capacity(JSON body, number): How many people can come. Past that, people go on a waitlist.sign_up_by(JSON body, text): The last day people can say they're coming, as YYYY-MM-DD.cost(JSON body, text): What it costs, in words ("$20 per person").notify(JSON body, true or false): Tell everyone in the group it's on the calendar. On unless you send false.
Returns: The new gathering (see One gathering).
Change a gathering
Needs a key that can plan gatheringsPATCH /api/v1/gatherings/{gathering_id}
MCP tool: update_gathering
Change a gathering you run (as a group leader or its organizer): its name, when, where, details, spots, sign-up deadline or cost. Send only what changes. For a repeating gathering, this changes just that date. Once it's over, only its date and time (to put it off) and its cost can change.
gathering_id(in the path, text, required): The gathering's id, from list_gatherings.title(JSON body, text): What it's called ("Sunday Dinner").date(JSON body, text): The day it's on (or starts), as YYYY-MM-DD. Moving it starts its reminders over.end_date(JSON body, text): For one that runs over several days (a retreat or trip): its last day, as YYYY-MM-DD. Send an empty string to clear it.start_time(JSON body, text): When it starts, on a 24-hour clock ("18:30"). Send an empty string to clear it.end_time(JSON body, text): When it ends, on a 24-hour clock. Send an empty string to clear it.location(JSON body, text): Where, as people would say it ("Grace Church", "The Parks' house"). Send an empty string to clear it.address(JSON body, text): The street address, shown only to the people the app shows addresses to. Send an empty string to clear it.details(JSON body, text): What people should know (up to 2,000 characters). Send an empty string to clear it.capacity(JSON body, number): How many people can come. Past that, people go on a waitlist. Send 0 for no limit.sign_up_by(JSON body, text): The last day people can say they're coming, as YYYY-MM-DD. Send an empty string to clear it.cost(JSON body, text): What it costs, in words ("$20 per person"). Send an empty string to clear it.notify(JSON body, true or false): Tell everyone it has a new time or place (only sent when the date, start time or place changed). Leave out to change it quietly.
Returns: The gathering as it now stands.
Cancel a gathering
Needs a key that can plan gatheringsPOST /api/v1/gatherings/{gathering_id}/cancel
MCP tool: cancel_gathering
Call off a gathering you run. Everyone coming hears it's cancelled, with your reason if you give one. It stays on the calendar marked cancelled, and restore_gathering puts it back on.
gathering_id(in the path, text, required): The gathering's id, from list_gatherings.reason(JSON body, text): Why, in a sentence, passed on to everyone ("Snow's too deep, let's try next week").
Returns: The gathering, cancelled.
Put a cancelled gathering back on
Needs a key that can plan gatheringsPOST /api/v1/gatherings/{gathering_id}/restore
MCP tool: restore_gathering
Undo cancel_gathering: the gathering's back on, and everyone hears the good news.
gathering_id(in the path, text, required): The gathering's id, from list_gatherings.
Returns: The gathering, back on.
A gathering
Gatherings come back with:
id,group(its id and name),title,date,end_date(for one over several days),start_time,end_time,status(scheduledorcancelled),invite_only(just for the people invited, so don't mention it to anyone else) andurl(its page in Porchlight).where(the host's place by its name, or the location),address(only when the app would show it to you),hostandneeds_host.my_answer(going,maybe,not_going,waitlist, or null if you haven't said),coming(how many),spots(when seats are limited),sign_up_byandcost.still_needed(what nobody's signed up for yet),my_jobsandmy_dishes.- From get_gathering, also:
details,agenda,menu(each item'sid,open_spots, who'sbringingit and whether it'smine),jobs(jobs, meal crews and shared gear, the same way),who_is_comingandmy_household(thekeyof everyone your answer covers, for answering for some of them), andyou_run_it(you lead its group or organize it, so you can change or cancel it with a key that can plan gatherings).
Good to know
An assistant acting for you does exactly what you could: answering tells the same people the app would, and signing up shows on the gathering for everyone. Planning or cancelling a gathering tells everyone just as the app does, and changing one tells them when you send notify. Please don't hammer it: it's a small, free app.
Questions, or something you'd like it to do? Tap your picture, then Help & support, then Contact support.