Skip to content

Implement new SDK APIs#10

Open
bettercallsaulj wants to merge 18 commits into
mainfrom
iml_new_apis
Open

Implement new SDK APIs#10
bettercallsaulj wants to merge 18 commits into
mainfrom
iml_new_apis

Conversation

@bettercallsaulj

Copy link
Copy Markdown
Collaborator

No description provided.

RahulHere added 18 commits June 17, 2026 22:25
Switch the gopher-orch submodule from main to br_release to match the
JS SDK's release pipeline, and advance the recorded pointer from v0.1.13
to the br_release tip (Release 0.1.23, 05667fdf).
Wire up the five gopher_orch_agent_create_by_* C entry points that
landed in gopher-orch 0.1.23 (PR #116) into the Python SDK's ctypes
binding layer, matching the JS koffi side commit ce787c99.

Added in gopher_mcp_python/ffi/library.py:

- argtypes / restype declarations for:
    gopher_orch_agent_create_by_server_id   (4 char*)
    gopher_orch_agent_create_by_server_name (4 char*)
    gopher_orch_agent_create_by_gateway_id  (4 char*)
    gopher_orch_agent_create_by_gateway_name (4 char*)
    gopher_orch_agent_create_by_url         (3 char*)
  Wrapped in try / except AttributeError so the SDK keeps loading
  against older libgopher-orch builds (< 0.1.23) that lack these
  symbols; the matching set_log_level binding above uses the same
  pattern for forward-compatibility.

- Five Python wrapper methods on GopherOrchLibrary:
    agent_create_by_server_id   (provider, model, api_key, server_id)
    agent_create_by_server_name (provider, model, api_key, server_name)
    agent_create_by_gateway_id  (provider, model, api_key, gateway_id)
    agent_create_by_gateway_name(provider, model, api_key, gateway_name)
    agent_create_by_url         (provider, model, url)
  Each uses getattr(self._lib, symbol, None) so a missing C symbol
  yields a None handle instead of an AttributeError; the higher-level
  factory layer (A2) will translate the None into AgentError using
  the same last_error / clear_error pump the existing create() path
  uses.

Verified with the existing tests/test_ffi.py suite (11 passed) and a
local smoke test against the vendored 0.1.1 dylib confirming the
new wrappers return None for missing symbols rather than crashing
at import.
Surface the five routing factories landed in gopher-orch 0.1.23 on
the public GopherAgent class so Python SDK consumers can scope an
agent to a single MCP server, MCP gateway, or a known MCP URL
without hand-rolling a JSON server config.

Added in gopher_mcp_python/agent.py:

- Five @staticmethod factories mirroring create_with_api_key /
  create_with_server_config:
    create_with_server_id   (provider, model, api_key, server_id)
    create_with_server_name (provider, model, api_key, server_name)
    create_with_gateway_id  (provider, model, api_key, gateway_id)
    create_with_gateway_name(provider, model, api_key, gateway_name)
    create_with_url         (provider, model, url)
  Each delegates to the matching FFI wrapper from the A1 binding
  layer via a small lambda passed to the new helper below.

- _create_from_ffi(create_handle) helper that centralises the null
  handle pump shared by the five new factories: lazy library init,
  AgentError translation, and last_error / clear_error draining --
  same pattern as the existing create() inline pump, extracted so
  the five new factories each stay around ten lines instead of
  duplicating the diagnostic plumbing.

- Callable added to the typing imports for the helper signature.

Note: the existing GopherAgent.create(config) path is unchanged --
its inline pump still flows through GopherAgentConfig, which only
models the api_key / server_config XOR. The new factories take a
fourth input (server_id / gateway_name / url) that does not fit
that XOR and so bypass the builder; the next commit documents this
decision on GopherAgentConfig itself.

Verified with the full pytest suite (198 passed, 1 skipped) and a
local smoke test confirming that calling create_with_url against
the vendored 0.1.1 dylib (which lacks the new C symbols) raises
AgentError via the null-handle pump rather than crashing.
Add a class-level docstring note to GopherAgentConfig explaining
that the five new GopherAgent.create_with_* routing factories
deliberately bypass the builder.

Context:

GopherAgentConfig has always modelled an api_key XOR server_config
shape that maps onto the two original C entry points
gopher_orch_agent_create_by_api_key and
gopher_orch_agent_create_by_json. The five routing factories that
landed in gopher-orch 0.1.23 -- create_with_server_id /
create_with_server_name / create_with_gateway_id /
create_with_gateway_name / create_with_url -- each take a fourth
input (server / gateway identifier, or URL) that does not fit the
XOR, so the previous commit added them as static methods on
GopherAgent that dispatch into GopherOrchLibrary via the new
_create_from_ffi helper rather than threading an extra optional
field through GopherAgentConfig.

This commit only updates the docstring -- runtime behaviour is
unchanged. The note exists so a future reader looking at
GopherAgentConfig is not surprised that four of the factory
methods on GopherAgent never construct one.

Mirrors the equivalent docstring landed on the JS-side
GopherAgentConfig (gopher-mcp-js src/config.ts), keeping the two
SDKs aligned on this architectural decision.

Verified with the full pytest suite (198 passed, 1 skipped).
Verify the package-level export surface still covers the new
GopherAgent.create_with_* routing factories, and close the only
parity gap discovered against the JS SDK index.ts.

Findings:

- GopherAgent is already in gopher_mcp_python.__all__, and the
  five new @staticmethod factories ride along on it -- no new
  top-level export needed for the A2 surface.

- The native FFI subpackage exports both GopherOrchLibrary and
  GopherOrchHandle (gopher_mcp_python/ffi/__init__.py), but only
  GopherOrchLibrary was being re-exported at the package root.
  The JS SDK index.ts exposes both, with GopherOrchHandle reserved
  for advanced FFI consumers that need to hand-craft the opaque
  pointer type.

Change in gopher_mcp_python/__init__.py:

- Import GopherOrchHandle next to GopherOrchLibrary
- Add "GopherOrchHandle" to __all__ under the "# FFI" section

No runtime behaviour change for existing consumers; this only
widens the public re-export surface so advanced users no longer
need to reach into gopher_mcp_python.ffi for the handle type.

Verified by importing the new symbol at the top level and walking
the five new GopherAgent factories from the package root.
Add tests/test_agent_create_by.py covering the failure-path contract
for the five GopherAgent.create_with_* factories that the previous
commits added. Mirrors the JS-side suite at
gopher-mcp-js/tests/agent-create-by.test.ts and the C++ failure-path
cases in gopher-orch/tests/gopher/orch/agent_create_by_test.cc.

Cases:

- Empty api_key (one test per routing variant):
    create_with_server_id   raises AgentError
    create_with_server_name raises AgentError
    create_with_gateway_id  raises AgentError
    create_with_gateway_name raises AgentError
  Locks down the nullptr-on-failure contract that fetch_mcp_servers
  surfaces through the wrapper layer.

- Empty url:
    create_with_url with url="" raises AgentError.
  Mirrors the CreateByUrlRejectsEmptyUrl C++ case -- validation
  fires before any FFI work happens.

- Unknown provider:
    create_with_url(BAD_PROVIDER, MODEL, URL) raises AgentError.
  create_with_url synthesises a local http_sse config and reaches
  create_by_json, which rejects an unknown provider name; the
  factory must surface that as AgentError.

- AgentError carries a non-empty message:
  A regression sentinel for the last_error / clear_error pump --
  if a future change to _create_from_ffi swallows the C-side
  diagnostic, this case catches it immediately.

Skip pattern:

  Module-level pytestmark uses GopherOrchLibrary.is_available() to
  skip the whole module when the native library is not loadable,
  matching the existing tests/test_ffi.py decorator approach. CI
  runs build.sh first so the suite executes; the contract tests
  also pass against the vendored 0.1.1 dylib because the FFI
  wrappers' getattr(_lib, symbol, None) pump returns None for
  missing symbols and the helper translates None into AgentError.

Happy-path coverage for the four routing variants needs a stubbed
HTTP listener capturing the /v1/mcp-servers query string with the
new camelCase keys (serverId / serverName / gatewayId /
gatewayName); that infrastructure is tracked separately, matching
the same TODO comment on the C++ side.

Verified locally: 7 tests pass, runs in ~0.05s.
Land examples/api/create_by_api_key.py, the smallest of the seven
create_by_* examples and the first sanity check that the toolchain
(pip install -e ., the right native lib, env vars) is wired
correctly on the Python side.

Direct port of gopher-mcp-js/examples/api/create_by_api_key.ts,
which itself ports gopher-orch/examples/sdk/api/create_by_api_key.cc.

Behaviour:

- Uses a Gopher API key to fetch the caller's full MCP server
  inventory via GET /v1/mcp-servers and constructs an agent with
  every server the api key owns -- no routing parameter.

- Provider defaults to AnthropicProvider; model is taken from the
  LLM_MODEL env var. Refuses to call the FFI when either the API
  key or the model is still the placeholder value, printing a
  clear error to stderr and exiting 1. This matches the env-var-
  required path the JS side picked, so the example never surfaces
  a stale or fictional model identifier even if the toolchain
  would otherwise auto-pick one.

- Accepts queries as positional argv with a single canned fallback
  ("What time is it in Tokyo?") matching the JS sibling.

- try / finally with agent.dispose() so the native handle is
  always released, even if a query fails partway through.

This is the first file in examples/api/, a new self-contained
subdirectory following the same convention as the existing
examples/auth/ and examples/pip/ subdirs. The README, run scripts,
and remaining six variants follow in subsequent commits.

Verified locally: placeholder refusal path exits 1 with a clear
diagnostic; with GOPHER_API_KEY / LLM_MODEL / ANTHROPIC_API_KEY
set, the example completes end-to-end against a real provider.
Land examples/api/create_by_json.py, second of the seven create_by_*
examples. Direct port of
gopher-mcp-js/examples/api/create_by_json.ts, which itself ports
gopher-orch/examples/sdk/api/create_by_json.cc.

Behaviour:

- Builds a GopherAgent from an inline server JSON document, skipping
  the remote /v1/mcp-servers fetch that create_with_api_key performs.

- The inline SERVER_CONFIG follows the
  { succeeded, code, message, data: { servers: [...] } } envelope
  that ConfigLoader on the C++ side accepts. Default servers entry
  is a single http_sse stub pointing at 127.0.0.1:3001/rpc; edit the
  literal to point at a real MCP server before running.

- Built with json.dumps(...) rather than a string literal so the
  inline payload stays valid JSON under any future edit and matches
  the JS sibling's JSON.stringify({...}) shape.

- Provider defaults to AnthropicProvider; model is taken from the
  LLM_MODEL env var. Refuses to call the FFI when LLM_MODEL is still
  the placeholder, printing a clear error to stderr and exiting 1.
  Matches the env-var-required path the JS side picked so the
  example never surfaces a stale or fictional model identifier.

- Accepts queries as positional argv with the canned "What time is
  it in Tokyo?" fallback. try / finally with agent.dispose() so the
  native handle is always released.

Lands without depending on any of the five new routing factories,
so this and create_by_api_key.py are the two variants that work
today against either the vendored 0.1.1 dylib or a fresh
gopher-orch 0.1.23 build.

Verified locally: placeholder refusal path exits 1 with a clear
diagnostic; syntax check clean; no forbidden tokens.
Land examples/api/create_by_server_id.py, third of the seven
create_by_* examples and the first of the four routing variants
added in gopher-orch 0.1.23. Direct port of
gopher-mcp-js/examples/api/create_by_server_id.ts, which itself
ports gopher-orch/examples/sdk/api/create_by_server_id.cc.

Behaviour:

- Scopes the agent to a single MCP server in the caller's workspace
  by id. Internally hits the same GET /v1/mcp-servers endpoint as
  create_with_api_key under the Bearer api key, but adds the
  "?serverId={id}" routing query so the response carries only the
  matching server entry. Use this when the api key owns several
  MCP servers but the agent should bind to exactly one.

- Provider defaults to AnthropicProvider; the model comes from the
  LLM_MODEL env var. Refuses to call the FFI when any of the three
  required env vars (LLM_MODEL, GOPHER_API_KEY, GOPHER_MCP_SERVER_ID)
  is still the placeholder, printing a clear error to stderr and
  exiting 1.

- Reads the routing parameter from GOPHER_MCP_SERVER_ID,
  matching the JS sibling's env-var name so a downstream user can
  share env across the JS and Python toolchains. Same pattern for
  the next three routing variants (server_name, gateway_id,
  gateway_name).

- Accepts queries as positional argv with the canned "What time is
  it in Tokyo?" fallback. try / finally with agent.dispose() so the
  native handle is always released.

Requires the create_with_server_id factory landed earlier in this
PR series; against the vendored 0.1.1 dylib the example exits with
an AgentError from the null-handle pump because the C symbol is
missing. Against a fresh gopher-orch 0.1.23 build the example
completes end-to-end.

Verified locally: placeholder refusal path exits 1 with a clear
diagnostic; syntax check clean; no forbidden tokens.
Land examples/api/create_by_server_name.py, fourth of the seven
create_by_* examples and the second routing variant added in
gopher-orch 0.1.23. Direct port of
gopher-mcp-js/examples/api/create_by_server_name.ts, which itself
ports gopher-orch/examples/sdk/api/create_by_server_name.cc.

Behaviour:

- Scopes the agent to a single MCP server in the caller's workspace
  by human-readable name. Internally hits the same GET
  /v1/mcp-servers endpoint as create_with_api_key under the Bearer
  api key, but adds the "?serverName={name}" routing query so the
  response carries only the matching server entry. Use this when
  the api key owns several MCP servers and the agent should bind to
  exactly one identified by name rather than id.

- Provider defaults to AnthropicProvider; the model comes from the
  LLM_MODEL env var. Refuses to call the FFI when any of the three
  required env vars (LLM_MODEL, GOPHER_API_KEY,
  GOPHER_MCP_SERVER_NAME) is still the placeholder, printing a
  clear error to stderr and exiting 1.

- Reads the routing parameter from GOPHER_MCP_SERVER_NAME,
  matching the JS sibling env-var name. The sibling create_by_
  server_id.py example uses GOPHER_MCP_SERVER_ID; the two coexist
  so a downstream user can flip between routing variants by
  swapping the example file without changing the env block.

- Accepts queries as positional argv with the canned "What time is
  it in Tokyo?" fallback. try / finally with agent.dispose() so the
  native handle is always released.

Requires the create_with_server_name factory landed earlier in this
PR series.

Verified locally: placeholder refusal path exits 1 with a clear
diagnostic; syntax check clean; no forbidden tokens.
Land examples/api/create_by_gateway_id.py, fifth of the seven
create_by_* examples and the third routing variant added in
gopher-orch 0.1.23. Direct port of
gopher-mcp-js/examples/api/create_by_gateway_id.ts, which itself
ports gopher-orch/examples/sdk/api/create_by_gateway_id.cc.

Behaviour:

- Scopes the agent to a single MCP gateway in the caller's
  workspace. Internally hits the same GET /v1/mcp-servers endpoint
  as create_with_api_key under the Bearer api key, but adds the
  "?gatewayId={id}" routing query so the response carries the
  backing MCP servers for that gateway. Use this when the api key
  owns several gateways and the agent should bind to exactly one.

- Gateway routing is the natural choice when the workspace fronts
  several MCP servers behind a single mcp_gateway process; binding
  by gateway id reaches every backing server in one shot rather
  than picking one by server id / name. The variant landed at the
  same time as the per-server routing factories in gopher-orch PR
  #116 so the examples come in matched pairs.

- Provider defaults to AnthropicProvider; model from LLM_MODEL.
  Refuses to call the FFI when LLM_MODEL, GOPHER_API_KEY, or
  GOPHER_MCP_GATEWAY_ID is still the placeholder, printing a clear
  error to stderr and exiting 1. Matches the env-var-required path
  the JS side picked so no specific model identifier is baked
  into source.

- Accepts queries as positional argv with the canned "What time is
  it in Tokyo?" fallback. try / finally with agent.dispose() so the
  native handle is always released.

Requires the create_with_gateway_id factory landed earlier in this
PR series.

Verified locally: placeholder refusal path exits 1; full pytest
suite still passes (205 tests, including the seven contract tests
from earlier in the series).
Land examples/api/create_by_gateway_name.py, sixth of the seven
create_by_* examples and the fourth (last) routing variant added
in gopher-orch 0.1.23. Direct port of
gopher-mcp-js/examples/api/create_by_gateway_name.ts, which itself
ports gopher-orch/examples/sdk/api/create_by_gateway_name.cc.

Behaviour:

- Scopes the agent to a single MCP gateway in the caller's
  workspace by human-readable name. Internally hits the same GET
  /v1/mcp-servers endpoint as create_with_api_key under the Bearer
  api key, but adds the "?gatewayName={name}" routing query so the
  response carries the backing MCP servers for that gateway. Use
  this when the api key owns several gateways and the agent should
  bind to exactly one identified by name rather than id.

- Matches the by-name / by-id pair pattern established earlier for
  server routing: create_by_server_name + create_by_server_id,
  create_by_gateway_name + create_by_gateway_id. The next commit
  (B7) lands the seventh example, create_by_url.py, which is the
  outlier in the set because it skips the remote fetch entirely.

- Provider defaults to AnthropicProvider; model from LLM_MODEL.
  Refuses to call the FFI when LLM_MODEL, GOPHER_API_KEY, or
  GOPHER_MCP_GATEWAY_NAME is still the placeholder, printing a
  clear error to stderr and exiting 1.

- Accepts queries as positional argv with the canned "What time is
  it in Tokyo?" fallback. try / finally with agent.dispose() so the
  native handle is always released.

Requires the create_with_gateway_name factory landed earlier in
this PR series.

Verified locally: placeholder refusal path exits 1 with a clear
diagnostic; syntax check clean; no forbidden tokens.
Land examples/api/create_by_url.py, the seventh and final
create_by_* example. Direct port of
gopher-mcp-js/examples/api/create_by_url.ts, which itself ports
gopher-orch/examples/sdk/api/create_by_url.cc.

Behaviour:

- Builds a GopherAgent from a single MCP server URL, skipping the
  remote /v1/mcp-servers fetch that create_with_api_key performs
  and the inline JSON shape that create_with_server_config
  requires. Internally the factory synthesises an http_sse server
  entry around the URL and delegates to create_by_json. Use this
  for local development or one-off endpoints where the operator
  already knows the URL.

- The outlier of the seven examples: needs no Gopher API key
  because there is no remote config fetch. Only LLM_MODEL and
  GOPHER_MCP_URL are required.

- Provider defaults to AnthropicProvider; model from LLM_MODEL.
  Refuses to call the FFI when LLM_MODEL or GOPHER_MCP_URL is
  still the placeholder, printing a clear error to stderr and
  exiting 1.

- Accepts queries as positional argv with the canned "What time
  is it in Tokyo?" fallback. try / finally with agent.dispose()
  so the native handle is always released.

Closes the matched set: this is the seventh and last variant
needed before the C1 run-script wrappers and C2 README can cover
the full set in a single file-to-factory mapping.

Verified locally: placeholder refusal path exits 1 with a clear
diagnostic; syntax check clean; no forbidden tokens.
…#9)

Land the seven shell wrappers that pair with the .py examples
added earlier in this PR series. Single commit per the JS-side
precedent at gopher-mcp-js commit 53e3f44a.

Files:

    examples/api/create_by_api_key_run.sh
    examples/api/create_by_json_run.sh
    examples/api/create_by_server_id_run.sh
    examples/api/create_by_server_name_run.sh
    examples/api/create_by_gateway_id_run.sh
    examples/api/create_by_gateway_name_run.sh
    examples/api/create_by_url_run.sh

Each wrapper:

- Resolves PROJECT_DIR with two dirname() walks because the
  scripts sit at examples/api/ rather than the existing
  examples/ root.

- Bails out with a pointer at ./build.sh when
  $PROJECT_DIR/native/lib is missing -- this is where the
  build.sh CMake output lands and where the loader resolves
  libgopher-orch.dylib / libgopher-orch.so in dev mode.

- Pre-flight checks that gopher_mcp_python is importable via
    PYTHONPATH="$PROJECT_DIR" python3 -c "import gopher_mcp_python"
  and prints a clear "pip install -e ." hint on failure. The
  .ts side did not need this because tsx + relative import work
  out of the box; Python requires the package to be on sys.path
  or installed.

- Warns (but does not fail) when the per-variant routing env
  vars, GOPHER_API_KEY, LLM_MODEL, or ANTHROPIC_API_KEY are
  unset. The .py example itself enforces the hard failure on
  placeholder values; the wrapper just surfaces the warnings
  early so a user does not get to the Python-side error after a
  longer execution path.

- Exports DYLD_LIBRARY_PATH (macOS) and LD_LIBRARY_PATH (Linux)
  pointing at "$PROJECT_DIR/native/lib", plus PYTHONPATH so the
  example runs even when the package has not been pip installed.

- Runs python3 examples/api/<file>.py "$@" so positional argv
  passes through as queries, matching the .py behaviour.

Differences from the existing examples/client_example_json_run.sh:
  - No lsof / kill-port helper: the new wrappers do not bring up
    any local server, they just call the SDK factory. Matches the
    JS-side wrappers at commit 6769a068 which dropped the helper
    for the same reason.
  - Two dirname() walks instead of one because the new wrappers
    are at examples/api/ rather than examples/.

Verified locally: chmod +x set on all seven; bash -n syntax check
clean on each; smoke run of create_by_server_id_run.sh without env
exits 1 with the expected env-var warnings followed by the
Python-side placeholder rejection; no forbidden tokens.
Land examples/api/README.md, the documentation index for the seven
.py / *_run.sh example pairs added earlier in this PR series.
Direct port of gopher-mcp-js/examples/api/README.md
(commit 26b4662d on the JS side) with Python idiom translations.

Sections:

- File-to-factory mapping: three-column table that ties each
  Python example back to both the C++ canonical reference and
  the TypeScript sibling, plus the GopherAgent factory each
  exercises. Lists all seven create_by_* variants so a reader
  can navigate from any one of the three SDKs to the other two.

- Quick start: build.sh first to drop the native dylib into
  native/lib/, then pip install -e . for editable-mode imports,
  then invoke a wrapper. Mirrors the JS-side three-step intro
  with the pip step substituted for the npm install one.

- Environment-variable matrix: required vs optional env vars
  per example. Includes the four routing variants (server_id,
  server_name, gateway_id, gateway_name), plus a note that
  LLM_MODEL has no default and each example refuses to start
  until it is set -- the same env-var-required path the JS
  side picked.

- Picking the right factory: ties each factory to its routing
  query string and whether it triggers a /v1/mcp-servers
  fetch. Mirrors the gopher-orch/docs/Agent.md "Simple creation
  factories" section so the Python docs stay aligned with the
  upstream C++ docs.

- SDK-resolution note: the .py files import `from
  gopher_mcp_python import GopherAgent`, which resolves against
  whatever pip install -e . pointed at. Downstream consumers
  who pip install gopher-mcp-python from PyPI and copy an
  example do not need to edit the import path. Calls out the
  intentional difference from the JS sibling, which uses a
  relative `'../../src'` import that has to be rewritten on
  copy-out.

- Native-library resolution note: documents the DYLD_LIBRARY_PATH
  / LD_LIBRARY_PATH story for the wrappers, the pre-flight
  `python3 -c "import gopher_mcp_python"` check (which the
  TypeScript wrappers don't need because tsx handles the
  relative import for them), and the GOPHER_MCP_PYTHON_LIBRARY_PATH
  env var as an explicit override checked first by
  gopher_mcp_python/ffi/library.py.

- Cross-reference block: pointers at the C++ examples and docs,
  the TypeScript siblings, the FFI binding layer
  (gopher_mcp_python/ffi/library.py), the high-level wrappers
  (gopher_mcp_python/agent.py), and the contract tests
  (tests/test_agent_create_by.py).

Closes the examples/api/ landing series for the seven
create_by_* variants on the Python side. C3 (pyproject.toml /
setup.py adjustments) was tagged OPTIONAL in the design note
and skipped because no new dependency was needed -- the
examples use only stdlib (os, sys, json, traceback) plus the
SDK itself.

Verified locally: no forbidden tokens; all seven .py and *_run.sh
files referenced in the file-to-factory mapping table exist.
Replace the "warn / interactive prompt if [Unreleased] is empty"
check with an auto-population step that builds release notes from
the Python repo git log since the previous tag and from the
gopher-orch GitHub release notes for the new version. Mirrors the
same improvement landed on the JS-side dump-version.sh so the two
SDKs share a release workflow.

Verified by dry-running Step 4 against the current repo state
(v0.1.21 -> v0.1.23): 15 Python commits in range, gopher-orch
notes fetched and embedded, original CHANGELOG.md restored
afterwards. No interactive input required.
Move all seven examples/api/ wrappers from the local-source +
local-build resolution model to the PyPI-based model already used
by examples/pip/. Each wrapper now bootstraps its own venv,
installs gopher-mcp-python plus the matching platform native
package from PyPI, and runs the .py against the just-installed
package -- not against the in-tree source.

.gitignore
- Add examples/api/test-project-*/ so the per-variant venv
  work dirs the wrappers create do not pollute the working
  tree. The existing test-project-api/ rule covered only the
  examples/pip/ workdir and not the new examples/api/ ones.
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