Twisp MCP桥接器

twisp-mcp 是一个本地 stdio 桥接器,用于连接 Twisp 已认证的云 MCP,并可通过 AWS 凭证启动 Codex,适用于云工具调用和本地 GraphQL 开发。

  • by parsnips
  • Go
  • 本地部署
  • 更新于
在 GitHub 查看 parsnips/twisp-mcp

使用说明

twisp-mcp

A local stdio bridge to Twisp's authenticated cloud MCP, with an optional direct GraphQL connection for local development. It can launch Codex with the bridge configured for that session and obtain renewable Twisp tokens from AWS credentials.

Mode graphql_execute Search, documentation, and other tools
local (default) Direct to TWISP_ENDPOINT Cloud MCP
cloud Cloud MCP Cloud MCP
flowchart LR
    Launcher[twisp-mcp launcher] --> Agent[Codex]
    Agent -->|stdio| Bridge[twisp-mcp bridge]
    Bridge -->|AWS identity proof| Auth[Twisp token exchange]
    Auth -->|short-lived JWT| Bridge
    Bridge -->|JWT + tenant| Cloud[Hosted Twisp MCP]
    Bridge -->|local mode GraphQL only| Local[Local Twisp]

Installation

The commands below use a macOS or Linux shell. Install these prerequisites:

  • Go 1.25.5 or newer, with go on PATH.
  • Codex CLI, with codex on PATH, if you want to launch Codex. Start codex once to complete its sign-in.
  • AWS CLI v2 if you use the AWS SSO setup and login commands below. The bridge itself uses the AWS SDK and does not require the AWS CLI for other credential sources.

Install or update from main

Run this from any directory; no repository checkout or GitHub SSH key is needed:

GOBIN="$HOME/bin" GOWORK=off go install github.com/parsnips/twisp-mcp@main
~/bin/twisp-mcp --version

This builds the current main branch and installs ~/bin/twisp-mcp. Run the same command whenever you want to update. GOWORK=off keeps installation independent of any Go workspace configured by your current project.

You can use ~/bin/twisp-mcp directly in every command below. To also run it as twisp-mcp, add this line to your shell startup file (~/.zshrc for zsh or ~/.bashrc for interactive bash), then open a new terminal:

export PATH="$HOME/bin:$PATH"

Build from a checkout

If you want to modify the source, install Git and Make as well, then run:

git clone https://github.com/parsnips/twisp-mcp.git
cd twisp-mcp
GOWORK=off make install
~/bin/twisp-mcp --version

This builds ./twisp-mcp and copies it to ~/bin/twisp-mcp. To update an existing checkout on main:

git pull --ff-only origin main
GOWORK=off make install

Launch Codex against the cloud

You need an existing Twisp tenant/account, a deployed MCP endpoint, and an AWS identity registered as a client in that tenant. The AWS profile and the Twisp account are separate: choosing an AWS profile does not select a Twisp tenant. A profile named dev-ro is read-only only if its Twisp client policies enforce that access. This bridge does not grant additional permissions.

With Twisp's aws/env wrapper

Run from your Twisp core checkout:

./aws/env us-east-1 dev-ro \
  ~/bin/twisp-mcp --mode cloud --account '<tenant>' codex

The bridge infers dev from ZONE and us-east-1 from AWS_REGION, both set by that wrapper. It uses the AWS session supplied by aws-vault and restores Codex to your original working directory after Bazel enters its runfiles tree. Relative credential-file paths are preserved. To avoid repeating the account, set TWISP_MCP_ACCOUNT_ID before launching.

With a standard AWS profile

Configure a profile using your organization's normal AWS sign-in process. For an IAM Identity Center / SSO profile, the initial setup and login are:

aws configure sso --profile twisp-dev
aws sso login --profile twisp-dev

Then launch:

AWS_PROFILE=twisp-dev ~/bin/twisp-mcp \
  --mode cloud --env dev --region us-east-1 --account '<tenant>' codex

Existing shared-credentials, assume-role, and credential_process profiles also use AWS_PROFILE; they do not require the SSO commands above. No aws/env wrapper is needed. Ambient environment credentials and AWS container/instance role credentials work through the AWS SDK credential chain as well.

You can also use aws-vault directly:

aws-vault exec twisp-dev -- ~/bin/twisp-mcp \
  --mode cloud --env dev --region us-east-1 --account '<tenant>' codex

Flags before codex configure the bridge. Everything after codex is passed unchanged to Codex, including subcommands and prompts:

AWS_PROFILE=twisp-dev ~/bin/twisp-mcp \
  --mode cloud --env dev --account '<tenant>' \
  codex exec 'Use the Twisp tools to describe the schema.'

The default target is https://api.us-east-1.dev.twisp.com/mcp. Other environments or regions work only when MCP is deployed there; selecting a target does not deploy it. The core branch that introduced hosted MCP enables dev / us-east-1.

Verify cloud access

Replace <tenant> with your Twisp account ID or alias. From the directory where you want to work, run a preflight without opening an interactive Codex session:

AWS_PROFILE=twisp-dev ~/bin/twisp-mcp \
  --mode cloud --env dev --region us-east-1 --account '<tenant>' codex --version

With the Twisp wrapper, run this from the core checkout instead:

./aws/env us-east-1 dev-ro \
  ~/bin/twisp-mcp --mode cloud --account '<tenant>' codex --version

Success prints the Codex version after authenticating and listing the cloud tools. Remove --version to start a session, then use /mcp to check that the Twisp bridge is connected. If preflight fails, see Troubleshooting preflight.

What the launcher does

The launcher verifies cloud credentials and lists tools before starting Codex. It injects invocation-only MCP settings and replaces itself with Codex on Unix, so terminal input, signals, and the exit code behave normally. Codex starts a second twisp-mcp process for the stdio bridge. That process keeps its own token in memory and refreshes it before expiry.

Use /mcp in Codex to find twisp_bridge_<session-id>. A unique name prevents Codex's configuration merging from picking up stale settings from an older MCP entry. Other configured MCP servers remain enabled; disable an older Twisp entry in your own Codex configuration if you do not want duplicate Twisp tools.

The launcher does not write ~/.codex/config.toml. It passes credential variable names in MCP configuration and inherits their values through the process environment; JWTs and AWS credential values are not placed in command arguments or token files. Use the launcher again when resuming a session so its bridge configuration and AWS environment are supplied again.

Local GraphQL development

Local mode preserves the existing routing and credential separation. With your local Twisp running:

TWISP_ENDPOINT=http://localhost:8080/financial/v1/graphql \
TWISP_ACCOUNT_ID=000000000000 \
AWS_PROFILE=twisp-dev \
~/bin/twisp-mcp --mode local --env dev --account '<cloud-tenant>' codex

Here --account is the cloud tenant for documentation/search billing; TWISP_ACCOUNT_ID is the direct GraphQL account. Local GraphQL never uses the cloud token or passes its requests through hosted MCP.

The Codex launcher checks cloud access in either mode. For offline local GraphQL, configure the stdio server directly in an MCP client rather than using the launcher; plain twisp-mcp starts local mode without requiring cloud access.

Other MCP clients

No launcher is required. Configure your client to run the executable over stdio:

{
  "mcpServers": {
    "twisp": {
      "command": "/absolute/path/to/twisp-mcp",
      "args": ["--mode", "cloud", "--env", "dev", "--region", "us-east-1", "--account", "<tenant>", "serve"],
      "env": { "AWS_PROFILE": "twisp-dev" }
    }
  }
}

Perform any interactive AWS login first. Allow at least 45 seconds for startup and 130 seconds for tool calls. The bridge uses the AWS configuration and cached login of the OS user running that MCP process. A GUI client may not inherit your shell environment; set its profile and bridge options explicitly.

Configuration

Usage: twisp-mcp [options] [serve | codex [codex arguments...]]. Options must precede the command. No command means serve. --help lists flags; --version prints the version. Logs go to stderr; stdio server stdout is MCP only.

Variable Purpose Default / precedence
TWISP_MCP_MODE Tool routing --mode, then variable, then local
TWISP_MCP_ENV Cloud deployment environment --env, variable, ZONE, dev
TWISP_MCP_REGION Cloud deployment region --region, variable, AWS_REGION, AWS_DEFAULT_REGION, us-east-1
TWISP_MCP_URL Explicit cloud MCP URL --url, variable, URL built from environment/region
TWISP_MCP_ACCOUNT_ID Cloud tenant/account --account, then variable; required for cloud requests
TWISP_MCP_TOKEN_FILE File containing cloud OIDC token Read on every request; takes precedence over other sources
TWISP_MCP_BEARER_TOKEN Explicit cloud OIDC token Used when no token file is configured
AWS_PROFILE AWS credential profile AWS SDK default credential chain
TWISP_ENDPOINT Direct GraphQL endpoint in local mode http://localhost:8080/financial/v1/graphql
TWISP_ACCOUNT_ID Direct GraphQL account in local mode 000000000000
TWISP_BEARER_TOKEN Direct GraphQL bearer token None
TWISP_API_KEY Direct GraphQL API key Used when no direct bearer token is set

Credentials and refresh

Without an explicit cloud token or file, the bridge signs a regional AWS STS GetCallerIdentity request and exchanges it at https://auth.<region>.<env>.twisp.com/token/iam. The proof is bound to that Twisp issuer. The returned JWT is cached in memory and renewed on the next request when it is within one minute of expiry. Concurrent requests share the cached token. Commercial AWS regions are supported for this exchange.

AWS credential providers that can refresh credentials continue to do so through the SDK. Fixed credentials injected into the environment by a wrapper cannot renew themselves: when that AWS session expires, obtain a new session and relaunch. Expired interactive SSO logins likewise need aws sso login again. JWT refresh does not extend the underlying AWS login.

An explicit bearer token is static. An explicit token file is re-read on each request, so another process may atomically replace it with a renewed token. Empty, unreadable, or invalid explicit credentials never fall back to AWS. Do not check credentials into the repository.

The bridge mints AWS-derived tokens only for the exact environment/region MCP URL it selects. Custom URLs require an explicit token or token file. All cloud URLs require HTTPS except loopback HTTP for development. Neither GraphQL nor cloud requests follow redirects. Tool arguments cannot change endpoints or credential configuration.

Troubleshooting preflight

An HTTP 401 with a plain unauthorized response can come from the load balancer's fixed default action, before the request reaches Twisp authentication. Check that /mcp has a forwarding rule and that the MCP-enabled API service has been deployed. A running AgentCore runtime alone is not sufficient. If the same identity and account can query GraphQL but MCP gets this response, check deployment/routing before changing credentials.

Other 401 responses suggest a rejected bearer token. A 403 indicates an access rejection: check the account and the identity's Twisp client registration. Pass literal account IDs as-is; use alias/<name> when selecting a tenant alias. HTTP errors include their status and a troubleshooting hint without printing response bodies or credentials.

Tool behavior

Cloud tool definitions, descriptions, schemas, annotations, results, errors, and metadata are forwarded through the MCP SDK, including JSON and SSE responses. Tools are discovered rather than hardcoded. In local mode, cloud graphql_execute is excluded and blocked by the forwarding client; in cloud mode, that same tool is forwarded with all other cloud tools. The bridge does not advertise cloud prompts or resources.

The catalog refreshes on tools/list and picks up added, changed, or removed tools. During an outage, the last successful catalog remains available and cloud calls return tool errors. Local stdio startup can expose only local GraphQL when the cloud is unavailable. Pure cloud startup fails if discovery fails. Discovery has a five-second deadline after startup; initial cloud startup has a 30-second budget and tool calls have a two-minute deadline. The application does not automatically replay failed tool calls, including mutations.

Search embeddings and write-unit billing stay in the cloud. In local mode, cloud docs and schema may describe a newer Twisp release than your local instance.

Development

make build
go test -race ./...
go vet ./...

Set GOWORK=off for these commands if your shell points at another Go workspace. Builds and tests need no AWS credentials, live Twisp service, Bedrock access, or docs corpus. Tests use local HTTP fixtures and a subprocess launcher fixture. The MCP SDK is pinned to v1.0.0.

  • main.go, launcher*.go: options, cloud preflight, Codex invocation.
  • internal/server: stdio MCP and mode-specific tool routing.
  • internal/cloud: authenticated HTTP client, AWS exchange/refresh, token files.
  • internal/graphql, internal/tools: direct GraphQL client and local tool.