Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Lark AgentCore Gateway Interceptor Server

БесплатноНе проверен

Enables Bedrock agents to act as a Lark user, performing document operations (list, create, edit, delete) and identity verification via Lark's REST API, with pe

GitHubEmbed

Описание

Enables Bedrock agents to act as a Lark user, performing document operations (list, create, edit, delete) and identity verification via Lark's REST API, with per-user credential injection through a Gateway Interceptor.

README

A reference implementation of enterprise identity on Amazon Bedrock AgentCore, using Lark (Feishu) as the identity provider. A simple agent is reachable from two Lark entrypoints — chat messages and a desktop-client-embedded web UI — that both resolve to the same lark:{open_id} identity. That identity is forwarded to downstream MCP tools through an AgentCore Gateway Request Interceptor (the agent never holds a downstream credential), and the tools then act as the user against Lark with the user's own token, so they reach only what that user can — Lark itself decides. In short, the agent inherits both who you are and what you're allowed to do, adding nothing of its own.

This is the Gateway Interceptor variant: downstream tools are Lambda targets, and a custom Gateway Request Interceptor forwards identity and injects the per-user credential (self-managed token store). The sibling repo lark-identity-on-agentcore-native achieves the same guarantees with the AgentCore Identity Token Vault (OAuth 3LO, driven agent-side) instead; the two differ only in how the downstream hop resolves per-user credentials.

Architecture

                   ┌─────────────────────┐      Lark desktop web UI ──▶ SPA (S3/CloudFront)
  Lark message ──▶ │    Router Lambda    │         h5sdk requestAccess ──▶ login code
  (webhook)        │   verify/decrypt    │                                  │
                   │   resolve user      │                                  ▼
                   └──────────┬──────────┘                        web_api Lambda
                              │                POST /api/lark/auth  code ─▶ Cognito JWT (Lark is IdP)
                              │                POST /api/session    JWT  ─▶ presigned WSS URL
                              │                                            │
   InvokeAgentRuntime (SigV4) │                                            │ browser opens WSS
   payload carries actorId    ▼                                            ▼ (platform bridges to /ws)
                             ┌─────────────────────────────────────────────────────┐
                             │  Agent container (ARM64, AgentCore Runtime)         │
                             │    :8080  /ping  /invocations(POST) /ws(WebSocket)  │
                             │    Strands Agent + AgentCore Memory (per-user STM)  │
                             └───────────────────────┬─────────────────────────────┘
                                       │ MCP call, Bearer = user's Cognito ACCESS token
                                       ▼
                             ┌───────────────────┐
                             │ AgentCore Gateway │  MCP server; customJWTAuthorizer (Cognito)
                             │  + Interceptor λ  │  passRequestHeaders=true; injects end-user id
                             └─────────┬─────────┘  (Gateway invokes its Lambda target, not Lark)
                                       ▼
                             ┌───────────────────┐  whoami — identity proof
                             │   Tool Lambda     │  list_my_docs / create / edit / delete — act AS the
                             └─────────┬─────────┘  user with THIS user's Lark user_access_token (by open_id)
                                       │ HTTPS, Bearer = user_access_token
                                       ▼
                             ┌─────────────────────────────────────────────────────┐
                             │ Lark REST API → returns only what THIS user can see │
                             └─────────────────────────────────────────────────────┘

  Identity: both entrypoints resolve to  lark:{open_id}  (shared session/memory).
  Lark is NOT standard OIDC → web_api exchanges the login code for a Cognito JWT.
  Pass-through (whoami) proves WHO; inheritance (list_my_docs, user_access_token)
  means the agent can only reach what the user can — Lark adjudicates.

See docs/architecture.md for the full layered design, per-hop auth matrix, and sequence diagrams.

Layout

Path What
app.py, cdk.json CDK app (uv-managed deps) — 6 stacks
stacks/ security, agentcore, router, webui, gateway, observability
agent/ Strands agent container (HTTP contract + WS + AgentCore Memory + MCP Gateway client)
lambda/router/ Lark webhook: verify/decrypt/tenant-token/send
lambda/web_api/ Lark login exchange + session bootstrap (presigned WSS)
lambda/interceptor/ Gateway Request Interceptor (per-user credential injection)
lambda/tools/ MCP tool targets: whoami (identity proof) + list_my_docs/create_doc/edit_doc/delete_doc (all act as the user against Lark)
web-ui/ Lark-embedded SPA (no build step; renders agent replies as Markdown via marked + DOMPurify)
scripts/ deploy / setup-lark / manage-allowlist / test
docs/architecture.md Full architecture: layers, per-hop auth, sequence diagrams

Deploy

Prereqs: uv, Docker, the AgentCore CLI (npm i -g @aws/agentcore), and AWS credentials. Scripts default to the default profile / us-west-2; override with PROFILE=... REGION=.... The AgentCore Runtime and Gateway have no CloudFormation resources in this region: the Gateway is created by deploy.sh via the control-plane CLI (IDs fed back into cdk.json), the Runtime image is built (ARM64, CodeBuild) and deployed with the AgentCore CLI.

cp .env.example .env          # fill in Lark appId/appSecret/encryptKey/token + your open_id
scripts/deploy.sh --base      # CDK base stacks (security, agentcore, router, gateway, observability)
scripts/deploy.sh --gateway   # create the MCP Gateway + interceptor + demo target
scripts/deploy.sh --runtime   # build ARM64 image (CodeBuild) + deploy the Runtime (AgentCore CLI)
scripts/setup-lark.sh         # read .env → Secrets Manager; print webhook/SPA URLs; allowlist you
scripts/deploy.sh --frontend  # deploy WebUI stack, inject SPA config, upload SPA
# or: scripts/deploy.sh       # run every step in order

Config in cdk.json: default_model_id (global.anthropic.claude-sonnet-5), lark_api_domain (https://open.larksuite.com international / open.feishu.cn), registration_open (false = allowlist only), presigned_url_expires.

Tear down

scripts/destroy.sh            # delete everything deploy.sh created (asks for confirmation)
# or: scripts/destroy.sh --yes  # skip the prompt

Deletes in dependency order — Gateway targets → Gateway → Runtime (all CLI-created, so cdk destroy alone can't remove them) → the six CDK stacks → the dynamic per-user token secrets ({prefix}/user-tokens/*, created at runtime and not owned by any stack). Idempotent: re-running skips already-gone resources. Your Lark console app config is not touched; re-seed credentials from .env via scripts/setup-lark.sh on the next deploy.

Lark console setup (do this once, in order)

The webhook/SPA URLs come from deploy.sh output. In the Lark developer console:

  1. Add features: enable Bot + Web app.

  2. Permissions & Scopes — add all of these, then note that subscribing to the message event requires the p2p scope or single-chat messages are never pushed:

    • im:message, im:message:readonly
    • im:message.p2p_msg:readonly ← required for single-chat messages
    • im:message:send_as_bot (reply), im:resource (images)
    • contact:user.base:readonly (login → open_id)

    For permission inheritance (the doc tools act as the user), also add these under the User Token Scopes tab — they are user-identity scopes and usually need admin approval before they take effect; the web app's tt.requestAccess scopeList must match them exactly or it fails with 20027:

    • drive:drive (list/create/manage the user's Drive files), docx:document (create/edit docx), offline_access (refresh_token — the user_access_token lives only ~2h)
  3. Events & Callbacks: subscription mode = Send to developer's server; set Request URL to the webhook URL; enable Encryption (note the Encrypt Key); add event im.message.receive_v1.

  4. Security Settings: add the SPA URL to Redirect URLs and H5 trusted domains. Leave IP allowlist empty (Lambda egress IPs are dynamic).

  5. Web app: set Desktop + Mobile homepage to the SPA URL.

  6. Version Management & Release: create a version and publish. Any change to scopes/events requires a re-publish to take effect.

Then scripts/setup-lark.sh stores credentials and allowlists your open_id. New users: they message the bot, get a rejection with their lark:ou_... id, and an admin runs scripts/manage-allowlist.sh add lark:ou_... (or set registration_open: true to let anyone in).

Test

scripts/test.sh              # agent (8) + router (7) + web_api (4)

Cost

This deploys billable AWS resources. All the always-on pieces are consumption- or per-unit-priced (no fixed reservation), so an idle single-user demo in us-west-2 is on the order of a couple USD/month before model usage; the variable cost is dominated by the agent's Bedrock calls. Verify current rates on the AWS pricing pages — figures below are as researched, not a quote.

  • Bedrock model invocations — the main usage-sensitive line; priced per input/output token on the model in default_model_id. A chatty demo is cents-to-dollars; a load test is not.
  • AgentCore Runtime — metered per-second, not a reserved instance: CPU (~$0.0895/vCPU-hour) is billed only during active processing (free while waiting on the model/tools), memory (~$0.00945/GB-hour) accrues continuously while the microVM is alive. An idle deployment still costs memory-time until the session's microVM is torn down.
  • AgentCore Gateway — per-invocation (~$0.005 per 1,000 tool/list calls); negligible at demo volume.
  • AgentCore Memory (STM) — billed per event written (~$0.25 per 1,000 create-event calls), not for retention duration; the 30-day window itself adds no storage fee.
  • Lambda + API Gateway (HTTP + WSS) — router, web_api, interceptor, tools; effectively free at demo volume.
  • Secrets Manager$0.40/secret/month each: the Lark-creds and tool-key secrets, plus one per-user token secret created on first authorization (so ~$1.20/month at one user, growing with users).
  • Cognito, DynamoDB (on-demand) — the identity plane; negligible at demo volume.
  • S3 + CloudFront — SPA hosting; pennies at demo volume.

scripts/destroy.sh removes everything, including the dynamic per-user token secrets, so you aren't left paying for orphaned resources. Costs are usage-driven — an idle deployment is cheap, but leaving it up still accrues the per-secret charges and the Runtime's memory-time until its microVM shuts down.

Security considerations

This is a reference implementation, not production-ready as-is. Before any real use:

  • IAM is scoped but a sample. Secret access is path-scoped to {prefix}/*; two grants use resources=["*"] only because the AWS actions (ecr:GetAuthorizationToken, secretsmanager:CreateSecret) cannot be resource-scoped — writes are still constrained to {prefix}/user-tokens/*. Re-review least-privilege for your account.
  • CORS is wide open (allow_origins=["*"] on the HTTP API and CloudFront). Lock it to your SPA origin for production.
  • Per-user Lark tokens live in Secrets Manager, one secret per user ({prefix}/user-tokens/{open_id}), refreshed on expiry. The agent never holds them — the tool Lambda loads them at call time. Treat the account hosting these as sensitive.
  • The agent's output is untrusted — the web UI sanitizes it with DOMPurify before rendering, and the marked/DOMPurify CDN scripts are pinned with SRI. Keep it that way if you touch the render path.
  • Webhook verification is fail-closed — missing/invalid signature or a timestamp outside the replay window is rejected. Don't relax this.
  • No secrets in this repo — Lark credentials come from .env → Secrets Manager via scripts/setup-lark.sh; .env is git-ignored.

Deployment status

All four goals verified end-to-end on an AWS account:

  • Lark chat: a real single-chat message → Router (verify + AES decrypt) → resolve lark:{open_id} → AgentCore Runtime → model reply back in Lark. /whoami in Lark reports the caller's identity.
  • Desktop embed: opening the Web app inside the Lark client runs h5sdk 免登 → POST /api/lark/auth (code → Cognito JWT) → POST /api/session (presigned WSS) → the AgentCore platform bridges the browser's WSS to the agent's /ws on port 8080 → streaming reply. Shows the user's display name. The minted JWT is cached in sessionStorage, so a refresh reuses it and skips requestAccess — otherwise Lark re-prompts the consent popup on every load (it has no silent login-state reuse; the SPA must do it).
  • Unified identity: both entrypoints resolve to the same lark:{open_id} (session/workspace shared).
  • MCP identity pass-through: the agent mints the user's Cognito access token → AgentCore Gateway (customJWTAuthorizer) → Request Interceptor reads the identity and injects the per-tenant downstream key → the whoami tool reports the real end-user id and that a credential was injected, while the agent never holds the key.
  • Conversation memory: the agent is a Strands agent with an AgentCore Memory (STM) session manager keyed by (actor_id, session). Verified across two different runtime sessions for the same user: it recalls a fact stated earlier — so memory persists across reconnects and both entrypoints (30-day event retention).
  • Permission inheritance: after the user authorizes in the web app, the doc tools act as that user — each loads the user's Lark user_access_token (stored by open_id, refreshed on expiry) and calls the Lark API, so access is scoped to what that user can see/do in Lark, adjudicated by Lark, and the agent never holds the token. Verified end-to-end: list_my_docs returned the user's real folder and, given a folder_token, descended into it to list the nested docs; create_doc/edit_doc/delete_doc created a doc (real document_id), appended content, and trashed it — all as the user.
  • Markdown rendering: agent replies (bold, lists, tables, code) render as sanitized HTML in the web chat — marked parses, DOMPurify strips anything unsafe (agent output is untrusted); streaming re-renders each delta; plain-text fallback if the CDN scripts fail.

Build & deploy notes (learned the hard way)

  • Runtime image must be ARM64 and built via CodeBuild. A QEMU cross-build on an x86 host produces an image that fails to start on Graviton (500 with no logs). The runtime is built + deployed with the AgentCore CLI (agentcore configure / agentcore deploy), which runs CodeBuild in the cloud. The agent image includes aws-opentelemetry-distro and runs under opentelemetry-instrument (required for AgentCore log/trace collection).
  • Runtime container logs: aws logs tail /aws/bedrock-agentcore/runtimes/<runtime_id>-DEFAULT --since 15m.
  • WebSocket endpoint /ws on port 8080 (same app as /ping + /invocations), matching the AgentCore SDK contract — not a separate port.
  • Gateway auth: send the Cognito access token (has the client_id claim that allowedClients validates); an ID token 403s with insufficient_scope.
  • Errors from the agent are returned as HTTP 200 {error: ...} — AgentCore wraps non-2xx as RuntimeClientError and drops the body.

Notes & limitations

  • Session granularity: one long conversation thread per user (the Memory session_id is derived from actor_id). All of a user's turns — webhook, web UI, and reconnects — share one context. There is no "new chat" affordance yet.
  • Memory is STM-only: the Memory resource stores raw turns (30-day retention), no long-term extraction/summarization strategies are configured.
  • whoami reports the lark:{open_id}, not a display name — the tool only sees what the Gateway interceptor injects (identity from the JWT), and the JWT carries no name claim.

from github.com/aws-samples/sample-lark-identity-on-agentcore-interceptor

Установка Lark AgentCore Gateway Interceptor Server

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/aws-samples/sample-lark-identity-on-agentcore-interceptor

FAQ

Lark AgentCore Gateway Interceptor Server MCP бесплатный?

Да, Lark AgentCore Gateway Interceptor Server MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Lark AgentCore Gateway Interceptor Server?

Нет, Lark AgentCore Gateway Interceptor Server работает без API-ключей и переменных окружения.

Lark AgentCore Gateway Interceptor Server — hosted или self-hosted?

Доступен hosted-вариант: Unyly запускает сервер в облаке, локальная установка не обязательна.

Как установить Lark AgentCore Gateway Interceptor Server в Claude Desktop, Claude Code или Cursor?

Открой Lark AgentCore Gateway Interceptor Server на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Lark AgentCore Gateway Interceptor Server with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории development