Skip to content

feat(mcp): add Rippling integration as a shimmed MCP preset#5

Open
aidenybai wants to merge 3 commits into
mainfrom
feat/rippling-mcp-shim
Open

feat(mcp): add Rippling integration as a shimmed MCP preset#5
aidenybai wants to merge 3 commits into
mainfrom
feat/rippling-mcp-shim

Conversation

@aidenybai

@aidenybai aidenybai commented May 7, 2026

Copy link
Copy Markdown
Member

Summary

  • New rippling MCP preset that the user adds with /mcp-add rippling <api-key> (same UX as Linear PAT, GitHub PAT, etc.) — Pookie validates the key against GET /platform/api/companies/current and reports the connected tool count.
  • The integration is exposed to the agent as mcp_rippling_* tools that are indistinguishable from real MCP tools, but Pookie hosts them locally and calls Rippling's REST API directly — no MCP transport, no proxy server. 14 tools cover companies, employees, leave types/balances/requests.
  • Generalizes the mechanism: presets gain an optional shim field, and tryRegister / openMcpTools short-circuit to a local toolset builder for shim configs. Future built-in HR/finance/etc. integrations drop into apps/api/server/mcp/shims/ with zero changes to the agent or MCP wiring.

What's where

  • apps/api/server/mcp/presets.tsMcpPreset.shim?: string, resolveShimName(...), plus the rippling preset entry (token auth, search-tool list, regen URL).
  • apps/api/server/mcp/shims/rippling-client.ts — bearer-auth REST client with timeout + structured RipplingApiError + probeRipplingToken.
  • apps/api/server/mcp/shims/rippling.tsbuildRipplingTools(token) and validateRipplingShim(token). 401/403 map to a structured toolErr with regen instructions instead of leaking a stack.
  • apps/api/server/mcp/shims/index.tsresolveShim(name) registry.
  • apps/api/server/mcp/client.ts — short-circuits in tryRegister and openMcpTools when a config resolves to a shim.

Tools exposed (mcp_rippling_*)

get_current_company, get_current_user, get_departments, get_work_locations, get_teams, get_levels, get_company_leave_types, get_custom_fields, list_employees, list_employees_including_terminated, get_employee, get_leave_balance, list_leave_balances, list_leave_requests.

Test plan

  • pnpm typecheck clean
  • pnpm lint clean (0 warnings, 0 errors across 172 files)
  • pnpm test — 229/229 pass (was 221 before; +8 new shim tests covering preset wiring, full toolset shape, URL/auth/pagination, 401 → tool-error mapping with regen instructions, query-param forwarding without undefined strings, and validateRipplingShim happy-path + 401 path)
  • Manual: /mcp-add rippling <key> in a real workspace, verify tool count appears in /mcp-list
  • Manual: ask the agent "who's out next week according to rippling?" and confirm it routes to mcp_rippling_list_leave_requests
  • Manual: revoke the API key in Rippling, send a follow-up, verify the agent surfaces the regen instructions instead of crashing

Note

Medium Risk
Adds a new token-based integration that calls an external HR API and changes MCP tool loading/registration flow to support shimmed servers, so errors or token handling could impact tool availability and data exposure (especially at global scope).

Overview
Adds a shim framework for MCP presets so some integrations can expose mcp_<server>_* tools without opening an MCP transport: tryRegister now validates shim tokens and openMcpTools partitions configs into shim vs transport, registering shim-built tools locally.

Introduces a new Rippling preset as a token-based shim, including a REST client with timeout/error mapping and a 14-tool HR/leave toolset (with model-output compaction for large list responses) plus token probing/validation.

Updates UX: system reminders now show the exact /mcp-add invocation per preset (including <api-key> for token presets), /mcp-add warns when adding a shim preset at global scope, and new tests cover shim partitioning/registration and Rippling tool behavior.

Reviewed by Cursor Bugbot for commit bd26645. Bugbot is set up for automated code reviews on this repo. Configure here.

Lets users add Rippling with `/mcp-add rippling <api-key>` and have it appear
to the agent as `mcp_rippling_*` tools, indistinguishable from a real MCP
server. Under the hood Pookie hosts the tools locally and calls Rippling's
REST API directly — no MCP transport, no proxy server.

The shim mechanism is generic: presets gain an optional `shim` field, and
`tryRegister` / `openMcpTools` short-circuit to a local toolset builder for
shim configs. Future built-in HR/finance/etc. integrations can drop into
`server/mcp/shims/` without changing the agent or MCP wiring.

Rippling tools cover companies, employees, leave types, leave balances, and
leave requests (14 tools total). 401/403 from Rippling map to a structured
tool error with regen instructions instead of leaking a stack trace.
@vercel

vercel Bot commented May 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pookie Ready Ready Preview, Comment May 7, 2026 5:26am

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c2be9bc. Configure here.

Comment thread apps/api/server/mcp/shims/rippling-client.ts Outdated
Comment thread apps/api/server/mcp/shims/rippling.ts Outdated
Hardens the shim path against context-window blowups, makes the agent
guidance accurate for token-only presets, and adds the integration-glue
tests the original PR was missing.

- Project list endpoints (list_employees, list_employees_including_terminated,
  list_leave_requests) before handing the JSON to the model. Drops photo
  blobs, workSchedule, customFields, per-day breakdowns, and timestamp
  metadata — fields the agent rarely needs at list level and that can
  push a single call past 50k characters.
- Trim the search subagent's view of the Rippling toolset: drop
  list_employees_including_terminated, list_leave_balances, and
  get_leave_balance from `searchTools`. The main agent can still call
  them when the user is explicit.
- Map AbortController-driven timeouts to a structured tool error
  (`code: rippling_timeout`) instead of leaking "This operation was
  aborted" to the model.
- System reminder now flags `authType: "token"` presets with a
  "(requires API key)" tag and the correct invocation
  (`/mcp-add rippling <api-key>`), so the agent stops telling users to
  run a command that immediately rejects them.
- Replace the hard-coded `app.rippling.com/developer/apiKeys` URL with a
  stable Rippling developer docs link, captured in a single constant
  used by both the preset registry and the shim's error messages.
- Warn admins when adding any shim preset at `--global` scope that the
  shared API key gives every workspace member visibility limited only by
  the key's own data scopes.
- Extract `partitionShimConfigs` and `tryRegisterShim` into pure
  exported helpers, and cover them with tests for: alias resolution
  (rippling_finance), missing-token error path, fall-through for
  non-shim configs, and 401-on-validate.
- Cleanup: hoist shared `RIPPLING_RESULT_SCHEMA`, drop unused
  `RipplingRequestError` interface, tighten `summarizeArrayLength` into
  `formatArrayOutput`.
Bugbot flagged that `RipplingShimValidationResult` (in rippling.ts) was
structurally identical to `McpShimValidation` (in shims/index.ts). Two
copies of the same shape risk drifting apart as the contract evolves.

Extract `McpShim` and `McpShimValidation` into `shims/types.ts` so both
the registry (`index.ts`) and individual shims (`rippling.ts`) import
the single canonical definition. `index.ts` re-exports them so
existing import sites keep working unchanged.
aidenybai added a commit that referenced this pull request May 7, 2026
Closes the four actionable findings from the review pass:

#1 (medium) -- detection-source mismatch between auto-react and system
reminder. The auto-react in handleSlackMessage previously only inspected
[currentMessage, ...skippedMessages], so a uwu/owo/meow that arrived as
a Redis-drained follow-up mid-turn (round 2+) flipped pet-mode on in
the system reminder but never got its cat reaction. Auto-react
bookkeeping now lives at the turn scope in handleSlackMessage with a
single \`petAutoReacted\` flag and a \`tryPetAutoReact(candidates)\`
helper called both on round 0 input AND after each Redis drain. To
keep the messageId attached, drainFollowUps now returns the full
QueuedFollowUp[] (text + messageId) instead of \`string[]\`; callers
that only need text \`.map((f) => f.text)\`. The downstream
followUpMessages -> system reminder pipeline is unchanged.

#2 (low) -- belt-and-suspenders: findUwuTriggerMessage now filters out
candidates with empty \`id\` so a malformed Slack message with text but
no ts can't trigger a 400 from reactions.add.

#5 (low) -- test coverage: resolveSlackEmojiShortcode now has cases
for the bare cat emojis (🐱 → cat, 🐈 → cat2), skin-tone modifiers
(👍🏻 / 👍🏿 → +1), and the bare-codepoint heart without its variation
selector (\\u2764 → heart).

#6 (low) -- tracing observability: stamp \`pookiebot.uwu_mode: true\`
when the per-round detection lights up, plus
\`pookiebot.uwu_trigger_message_id\` when the auto-react fires.
Consistent with existing \`pookiebot.dropped_card\` etc. attributes.

#7 (doc nit) -- the \`already_reacted\` comment now reflects Slack's
exact-emoji semantics: that branch only fires when the SAME shortcode
is already on the message, so reporting success there is genuinely
correct (the user-facing reaction state matches what the model wanted).

Tests:
- thread-lock tests rewritten for the QueuedFollowUp[] return shape
- agent-follow-ups mocks updated to mock objects with messageId
- four new resolveSlackEmojiShortcode cases for #5
- 281 total tests passing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant