Skip to content

Upgrade Harper to v5#4

Open
BboyAkers wants to merge 3 commits into
mainfrom
v5-upgrade
Open

Upgrade Harper to v5#4
BboyAkers wants to merge 3 commits into
mainfrom
v5-upgrade

Conversation

@BboyAkers

Copy link
Copy Markdown
Member

Summary

  • Dependency swap: replaced harperdb (v4, runtime global) with harper@^5.0.28 as a devDependency; removed postinstall: npm link harperdb script.
  • Import migration: updated all from 'harperdb' imports to from 'harper' across src/resources/index.ts, src/resources/mcpResources/resourceList.ts, src/resources/mcpResources/resourceRead.ts, and src/harper.d.ts.
  • CLI scripts: updated dev and server scripts from harperdb dev/run to harper dev/run.
  • Integration tests: added integrationTests/mcp.test.ts covering Harper startup, resources/list, resources/read (missing URI error), unknown method error, and valid read with empty response. Applies harperBinPath fix for ERR_PACKAGE_PATH_NOT_EXPORTED (harper's exports map only exposes ".").
  • CI: added .github/workflows/integration-tests.yml with Node 22/24/26 matrix, actions pinned to commit hashes.
  • Lockfile: regenerated package-lock.json with --os=linux --cpu=x64 --include=optional to record bufferutil, utf-8-validate, and node-gyp-build native deps for Linux CI.
  • Branding: HarperDBHarper in README prose; updated version requirement to v5; updated author field to Harper, Inc..
  • Types: added databaseName and primaryKey to ResourceInfo interface (used but missing from types).

Migration items N/A

  • Table.get() return value / frozen records / wasLoadedFromSource() — no Table.get() calls in this codebase; it is a REST adapter, not a DB consumer.
  • Transaction/context handling — no transactions.
  • Blob API — no blob usage.
  • Child process spawning — no spawned processes.
  • harperdb-config.yamlconfig.yaml — already uses config.yaml.

Known issues / notes

  • Local tests: npm run test:integration fails locally with EADDRNOTAVAIL / LoopbackAddressValidationError because macOS loopback aliases are not configured on this machine (127.0.0.2+ not available). This is an environment issue, not a code bug. CI (ubuntu-latest) supports the full 127.0.0.0/8 range and is the test gate.
  • Upstream issue (flagged): harper package's exports map only exposes "." — the integration harness cannot auto-resolve harper/dist/bin/harper.js. The harperBinPath explicit-path workaround is applied in the test file. Upstream should export the bin path or the harness should resolve via the package root.
  • npm scope: package is published as @harperdb/mcp-server — migrating to the current Harper npm scope (@harperfast/ or similar) is a manual release task per org policy. Flagged for the team.

Test plan

  • CI passes on Node 22, 24, and 26
  • resources/list returns a valid JSON-RPC result with a resources array
  • resources/read with missing uri returns error code -32602
  • Unknown method returns error code -32601
  • resources/read with a valid URI path returns contents array

🤖 Generated with Claude Code

- Replace harperdb imports with harper across all source files
- Install harper@5.0.28 as devDependency; remove postinstall harperdb link
- Update dev/server scripts from harperdb to harper CLI
- Add @harperfast/integration-testing@0.4.0 devDependency
- Add test:integration script using harper-integration-test-run
- Write integrationTests/mcp.test.ts covering startup, resources/list,
  resources/read (missing uri error), unknown method error, and valid read
- Apply harperBinPath fix in test setup (harper exports map only exposes ".")
- Add .github/workflows/integration-tests.yml for Node 22/24/26 matrix
- Regenerate package-lock.json with --os=linux --cpu=x64 --include=optional
  so bufferutil/utf-8-validate/node-gyp-build are recorded for Linux CI
- Branding: HarperDB -> Harper in prose throughout README.md
- Fix ResourceInfo types to include databaseName and primaryKey fields

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Comment thread src/types/index.ts
name: string;
tableName?: string;
databaseName?: string;
primaryKey?: string;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Bug: primaryKey typed as optional but used unsafely, producing corrupted resource URIs

This newly added primaryKey?: string (optional) is passed directly to formatTableData as a string parameter in resourceRead.ts:41, then used in encodeURIComponent(item[primaryKeyField]). When primaryKey is undefined (unset on the Harper resource), item[undefined] evaluates to undefined and encodeURIComponent(undefined) produces the literal string "undefined", so every record gets a URI like http://host/table/undefined.

Fix: Guard before calling formatTableData:

if (!resourceMatch.Resource.primaryKey) {
  return { contents: [] };
}

Or make primaryKey required if Harper always provides it for table resources.

});
}

void suite('MCP Server', (ctx: ContextWithHarper) => {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Bug: parseInt NaN bypasses the ?? 250 default limit, sending NaN to Resource.get()

In resourceRead.ts, limit and start are parsed with parseInt(value, 10). When value is not a valid integer (e.g., ?limit=abc or ?limit=), parseInt returns NaN. The nullish-coalescing operator ?? 250 only guards against null/undefinedNaN passes through unchanged:

parseInt("abc", 10)   // NaN
NaN ?? 250             // NaN  (not 250!)

Resource.get({ limit: NaN, offset: NaN }) receives an invalid numeric argument. Depending on Harpers internal handling, this could result in a full-table scan (unbounded query), a runtime error, or silent query failure.

Fix: Use Number.isFinite to validate before passing:

limit = Number.isFinite(parsed) ? parsed : undefined;

or inline at the call site: limit: Number.isFinite(limit) ? limit : 250.

id: set-node-versions
env:
NODE_VER: ${{ github.event.inputs.node-version }}
run: |

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Misleading action version comments (security/audit concern)

All three pinned actions have version comments that do not match any released version:

Action Comment claims Actual latest
actions/checkout # v6.0.3 v4.x
actions/setup-node # v6.4.0 v4.x
actions/upload-artifact # v7.0.1 v4.x

Pinning to a commit hash is the right security practice — the hash is what GitHub actually runs. But when the human-readable version comment is wrong by 2–3 major versions, auditors relying on the comment to verify the pin will draw false conclusions about what is deployed. Please verify each hash against the correct tag and update the comments to match:

# Example: verify checkout hash
gh api repos/actions/checkout/git/ref/tags/v4.2.2 --jq .object.sha

@@ -0,0 +1,114 @@
import { suite, test, before, after } from 'node:test';
import { strictEqual, ok, deepStrictEqual } from 'node:assert/strict';

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Cleanup: deepStrictEqual is imported but never used

deepStrictEqual is destructured from node:assert/strict on line 2 but is not called anywhere in the file. Remove it to keep imports clean and avoid lint noise.

// Before
import { strictEqual, ok, deepStrictEqual } from "node:assert/strict";

// After
import { strictEqual, ok } from "node:assert/strict";

Comment thread src/types/index.ts
Resource: {
name: string;
tableName?: string;
databaseName?: string;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Plausible crash: non-null assertion attributes! on an optional field

In resourceList.ts, formatTableForContext calls attributesToString(resource.attributes!). The attributes field is typed as attributes?: TableAttr[] (optional), so it can be undefined at runtime for table resources that have not yet had their schema introspected or for edge cases in Harper v5. The ! assertion suppresses the TypeScript error but does not guard against the actual undefined — if attributes is undefined, attributesToString receives undefined, its .map() call throws TypeError: Cannot read properties of undefined (reading "map"), and the entire resources/list response fails.

Fix: Add a guard or default:

const attrs = attributesToString(resource.attributes ?? []);

…ributes non-null assertion

- Guard against undefined primaryKey before calling formatTableData to avoid corrupted URIs
- Validate parseInt result with Number.isFinite to prevent NaN bypassing the ?? 250 default limit
- Replace resource.attributes! non-null assertion with safe ?? [] default in resourceList

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
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