Skip to content

Latest commit

 

History

History
100 lines (82 loc) · 3.39 KB

File metadata and controls

100 lines (82 loc) · 3.39 KB

Contributing

Adding a New MCP Tool

  1. Identify which packages/server/src/tools/ file it belongs in (or create a new one)
  2. Import { getAuthenticatedRh, text } from ./_helpers.js
  3. Register with server.tool(name, description, zodSchema, handler)
  4. Define the input schema with Zod — MCP uses these for the tool schema
  5. Wrap the handler body in try/catch, return text({ error: String(e) }) on failure
  6. If a new file, import and call its register*Tools(server) in server.ts
  7. Add tests in packages/server/__tests__/tools.test.ts

Example:

import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { getAuthenticatedRh, text } from "./_helpers.js";

export function registerNewTools(server: McpServer): void {
  server.tool(
    "robinhood_new_tool",
    "Tool description shown to agents.",
    {
      param: z.string().describe("Parameter description."),
    },
    async ({ param }) => {
      try {
        const rh = await getAuthenticatedRh();
        const result = await rh.someMethod(param);
        return text({ data: result });
      } catch (e) {
        return text({ error: String(e) });
      }
    },
  );
}

Creating a New Skill

  1. Create .claude/skills/robinhood-<name>/SKILL.md with:
    • YAML frontmatter (name, description)
    • Trigger phrases
    • Step-by-step instructions for Claude
    • Code patterns to follow
  2. Create .claude/skills/robinhood-<name>/reference.md with API details
  3. Keep SKILL.md under 500 lines

Adding Client Methods

  1. Define a Zod schema in packages/client/src/types.ts (use .passthrough())
  2. Add a URL builder in packages/client/src/urls.ts if needed
  3. Implement the method in packages/client/src/client.ts:
    • Use parseOne(Schema, data) or parseArray(Schema, data) for return values
    • Use typed return signatures (e.g. Promise<Quote[]>, not Promise<unknown[]>)
  4. Export the new type from packages/client/src/index.ts
  5. Add tests in packages/client/__tests__/ using vi.mock("../src/http.js")

Testing

npx vitest run          # All tests
npx vitest run --watch  # Watch mode

All tests mock the HTTP layer via vi.mock() — no real API calls. Use vitest (not bun test) for correct module isolation.

When mocking http.js, use importOriginal to preserve parseOne/parseArray:

vi.mock("../src/http.js", async (importOriginal) => {
  const actual = await importOriginal<typeof import("../src/http.js")>();
  return {
    ...actual,
    requestGet: vi.fn(),
    requestPost: vi.fn(),
  };
});

Git Workflow

  1. Fork the repo and create a branch from main
  2. Branch naming: feat/description, fix/description, or docs/description
  3. Make your changes and ensure all checks pass locally:
    bun run check && bun run typecheck && npx vitest run
  4. Write clear commit messages: feat: add new tool, fix: handle null margin, docs: update README
  5. Open a pull request against main
  6. Fill out the PR template (safety checklist + testing)

Safety Checklist

Before adding any new tool or skill:

  • Does it expose fund transfer or bank operations? (If yes, BLOCK it)
  • Does it place orders? (If yes, require explicit parameters, add to high-risk tier)
  • Could it cause bulk operations? (If yes, consider blocking or adding safeguards)
  • Update ACCESS_CONTROLS.md with the new operation