Skip to content

Latest commit

 

History

History
260 lines (206 loc) · 7.07 KB

File metadata and controls

260 lines (206 loc) · 7.07 KB

Contributing

Prerequisites

  • Node.js 24+
  • pnpm via Corepack:
    corepack enable
    Corepack reads the packageManager field in package.json and ensures the correct pnpm version is used automatically.

How to Set Up the Development Environment

  1. Clone and install dependencies:

    git clone https://github.com/davegomez/fizzy-mcp.git
    cd fizzy-mcp
    pnpm install
  2. Verify setup:

    pnpm check

    You should see Checked N files with no errors.

  3. Run tests:

    pnpm test:run

    All 373 tests should pass.

How to Add a New Tool

  1. Create the tool file src/tools/yourfeature.ts:

    import { UserError } from "fastmcp";
    import { z } from "zod";
    import { getFizzyClient, toUserError } from "../client/index.js";
    import { getDefaultAccount } from "../state/session.js";
    import { isErr } from "../types/result.js";
    
    function resolveAccount(accountSlug?: string): string {
      const slug = (accountSlug || getDefaultAccount())?.replace(/^\//, "");
      if (!slug) {
        throw new UserError(
          "No account specified and no default set. Use fizzy_account first.",
        );
      }
      return slug;
    }
    
    export const yourFeatureTool = {
      name: "fizzy_your_feature",
      description: `One-line summary.
    
    **When to use:**
    - Scenario 1
    - Scenario 2
    
    **Arguments:**
    - \`account_slug\` (optional): Uses session default if omitted
    - \`required_param\` (required): Description
    
    **Returns:** JSON with fields.`,
      parameters: z.object({
        account_slug: z.string().optional(),
        required_param: z.string(),
      }),
      execute: async (args: { account_slug?: string; required_param: string }) => {
        const slug = resolveAccount(args.account_slug);
        const client = getFizzyClient();
        // Implementation
      },
    };
  2. Export from src/tools/index.ts:

    export { yourFeatureTool } from "./yourfeature.js";
  3. Register in src/server.ts:

    import { yourFeatureTool } from "./tools/index.js";
    // ...
    server.addTool(yourFeatureTool);
  4. Add tests in src/tools/yourfeature.test.ts:

    import { http, HttpResponse } from "msw";
    import { describe, expect, it } from "vitest";
    import { server } from "../test/mocks/server.js";
    import { yourFeatureTool } from "./yourfeature.js";
    
    describe("fizzy_your_feature", () => {
      it("does expected behavior", async () => {
        server.use(
          http.get("https://app.fizzy.do/test-account/endpoint", () =>
            HttpResponse.json({ /* mock */ })
          )
        );
    
        const result = await yourFeatureTool.execute({
          account_slug: "test-account",
          required_param: "value",
        });
    
        expect(JSON.parse(result)).toMatchObject({ /* expected */ });
      });
    });
  5. Verify:

    pnpm check && pnpm test:run

How to Submit a Pull Request

  1. Create a feature branch:

    git checkout -b feature/your-feature
  2. Make atomic commits using Conventional Commits format:

    git commit -m "feat(tools): add fizzy_your_feature tool"
  3. Run all checks before pushing:

    pnpm check && pnpm test:run
  4. Push and create PR:

    git push -u origin feature/your-feature
    gh pr create

How to Release a New Version

Releases are automated via changelogen and GitHub Actions. The version bump is determined by commit types since the last tag.

  1. Preview the release locally (commits and tags locally, does not push):

    pnpm release:dry
  2. If everything looks good, reset the local changes and run the actual release:

    git reset --hard HEAD~1 && git tag -d v<version>
    pnpm release

    This bumps the version in package.json, updates CHANGELOG.md, commits, tags, and pushes to the remote.

  3. The tag push triggers the Release workflow, which runs CI, publishes to npm, and creates a GitHub Release.


Architecture Reference

Directory Structure

Directory Purpose
src/client/ HTTP client returning Result<T, FizzyApiError>
src/schemas/ Zod schemas for API types
src/tools/ MCP tool definitions
src/types/ Shared utilities (Result ADT)
src/state/ Session state (default account)

Client Layer

File Purpose
fizzy.ts HTTP client with all API methods. Singleton via getFizzyClient().
errors.ts Error types (AuthenticationError, NotFoundError, etc.) and toUserError() conversion.
markdown.ts markdownToHtml() and htmlToMarkdown() for content conversion.
pagination.ts encodeCursor() / decodeCursor() for opaque pagination.
upload.ts Direct upload handling for attachments.

Schemas Layer

Each domain follows this pattern:

Schema Purpose
EntitySchema Full entity shape from API responses
CreateEntityInputSchema Payload for creation endpoints
UpdateEntityInputSchema Partial payload for update endpoints

Tool Structure

{
  name: string;           // "fizzy_" prefix, snake_case
  description: string;    // Includes when-to-use, arguments, returns
  parameters: ZodObject;  // Input validation schema
  execute: (args) => Promise<string>;  // Returns JSON string
}

Result ADT

Function Signature
ok(value) <T>(value: T) => Result<T, never>
err(error) <E>(error: E) => Result<never, E>
isOk(result) <T, E>(result: Result<T, E>) => result is Ok<T>
isErr(result) <T, E>(result: Result<T, E>) => result is Err<E>

Error Types

Error HTTP Status Description
AuthenticationError 401 Invalid or missing token
ForbiddenError 403 Insufficient permissions
NotFoundError 404 Resource not found
ValidationError 422 Invalid request data
RateLimitError 429 Too many requests

Commands Reference

Command Purpose
pnpm dev Run with tsx (live reload)
pnpm build Compile TypeScript to dist/
pnpm test Vitest watch mode
pnpm test:run Run tests once
pnpm lint Check with Biome
pnpm lint:fix Auto-fix lint issues
pnpm check Lint + typecheck (CI-ready)
pnpm release Bump version, update changelog, commit, tag, and push
pnpm release:dry Same as release but without pushing (local preview)

Running Specific Tests

pnpm test -- src/client/fizzy.test.ts     # Single file
pnpm test -- -t "creates a card"          # By test name pattern

Code Style Reference

Rule Value
Formatter Biome
Indentation Tabs
Quotes Double
TypeScript Strict mode
Error handling Result<T, E> over exceptions
Tool naming fizzy_ prefix, snake_case