AI 资讯
I Almost Hand-Rolled JSON-RPC for an MCP Server. Eight Tools Later I'm Glad I Didn't.
When I built the MCP server for this project — it combines GitHub and DEV.to into a set of tools an agent can call — I had a decision to make before writing a single tool: talk to the low-level MCP protocol directly, or use FastMCP 's decorator API. I've seen a few "your first MCP server" writeups lately walk through the low-level path because it's more "honest" about what MCP actually is under the hood — JSON-RPC over stdio, a capabilities handshake, typed request/response schemas. That's true, and it's a reasonable thing to want to understand. But I want to write about the other side: what it actually costs you in practice once you have more than one or two tools, because I went through both and the difference showed up fast. what the low-level path actually asks you to write Strip away the decorator and MCP is a JSON-RPC server. For every tool you add, you're responsible for: Registering the tool's name, description, and a JSON Schema for its inputs in a list_tools handler Writing a call_tool dispatcher that matches on tool name and unpacks arguments by hand Serializing the return value into the TextContent / ImageContent wrapper types MCP expects Keeping the schema you wrote in step 1 in sync with the arguments you actually read in step 2, by hand, forever None of that is hard in isolation. The problem is it's boilerplate that scales linearly with tool count and has zero connection to the actual logic of the tool. My server has 8 tools. Hand-rolled, that's 8 schema blocks plus a dispatcher if/elif chain plus 8 response-wrapping calls, all of which exist purely to satisfy the protocol, not to do anything a GitHub or DEV.to API call needs. what it looks like with FastMCP Here's an actual tool from server.py , unedited: @mcp.tool () def get_repo_stats ( repo : str ) -> dict : """ Get stars, forks, watchers, open issues for enjoykumawat/<repo>. """ r = _gh ( f " /repos/ { GITHUB_USERNAME } / { repo } " ) return { " name " : r [ " name " ], " stars " : r [ " stargaze
AI 资讯
variant-confidence v0.1.0: a calibrated confidence layer for variant-effect pathogenicity scores
variant-confidence v0.1.0: a calibrated confidence layer for variant-effect pathogenicity scores State-of-the-art variant-effect models are accurate in cross-validation but their scores are poorly calibrated on temporal data. variant-confidence adds an auditable calibration layer on top of existing predictors — it does not train a new model. The problem: accuracy is not trust Protein variant-effect predictors (AlphaMissense, ESM-1v, EVE) report pathogenicity scores, but a clinician or researcher needs to know how much to trust the number , not just its rank. The gap is calibration, not accuracy: AnnotateMissense (2026) reports MCC 0.94 in cross-validation, dropping to 0.76 on temporal ClinVar, accuracy 0.8798. A raw score near 0.9 may not mean 90% probability. Acting on an uncalibrated score is a risk. What it does variant-confidence wraps an existing predictor's score and produces a calibrated, uncertainty-aware output: Probability calibration (AC1): Platt scaling or isotonic regression over a separate holdout. Selectable, not hardcoded. Conformal prediction (AC1b): coverage 1−α intervals, split or Mondrian by gene. ECE (AC2, AC9): Expected Calibration Error reported before/after calibration, with bootstrap CI and per-bin counts. Bins with too few samples are flagged as low-reliability. Leakage-free split (AC3): temporal split by ClinVar release date with gene isolation — the same gene never appears in both train and test. This is unit-tested. Missing-score handling (AC4): works with AlphaMissense or ESM-1v alone; emits an explicit warning instead of failing silently. Non-deceptive reporting (AC7): every result includes interval/ECE + method + threshold, never a bare calibrated score. Verification (clean clone, no network) Built under a three-party governance loop: implement → independent audit in a clean clone → merge approval. ruff check . → All checks passed. pytest tests/ → 28 passed in 8.90s (offline fixture). An honest bug we caught in audit The first ECE tes
AI 资讯
How We Built 非标准文本翻译与含义确认: A Context-Aware Book Translation Pipeline with Python and LLMs
Tackling idioms, cultural references, and ambiguous phrases in AI-powered book translation. At LectuLibre, we’ve been working on an AI-powered book translation service. One of the toughest challenges we ran into wasn’t the straightforward sentences — it was the non-standard text: idioms, metaphors, cultural references, and ambiguous phrases that machine translation consistently butchers. We needed a way to not only translate these correctly but also let users verify and edit the translations, because in literary works, getting them wrong breaks the entire reading experience. That’s how we built our 非标准文本翻译与含义确认 (non‑standard text translation and meaning confirmation) feature. It’s a pipeline that detects tricky sentences, proposes a contextual translation with a full meaning explanation, and gives users a final say. Here’s the engineering story, warts and all. The Problem Standard LLM translation does an impressive job on factual, literal text. But when a book says “it’s raining cats and dogs” it could be rendered as “raining animals” in the target language, which is either brilliant or absurd depending on context. Idioms often carry cultural weight that a simple word‑for‑word translation misplaces. Additionally, metaphors and ambiguous phrases can have multiple valid interpretations. For a translator, understanding the intent behind the phrase is half the work. We wanted a system that: Automatically identifies sentences containing non‑standard language. Generates a translation that preserves the original meaning rather than just the literal words. Provides a plain‑language explanation of what the phrase actually means (e.g., “This is an English idiom meaning it’s raining heavily”), so the user can judge the translation’s accuracy. Allows the user to confirm, edit, or retranslate those segments. A book can easily run to hundreds of thousands of words, so cost and speed were critical. We couldn’t just throw everything at a single high‑end LLM and call it a day. Our A
AI 资讯
An unofficial dated ledger of pricing & usage-limit changes for AI coding tools
I put together a small, unofficial public ledger that records dated snapshots of the public pricing, usage limits, and rate limits of a few AI coding tools — currently Cursor, GitHub Copilot, and Claude Code — and keeps the history so you can see what changed over time. Why: pricing and quota pages change in place. The old value disappears from the live page and there is usually no public diff. If you are budgeting a team or comparing tools, the history is the useful part, and it is the part the live pages do not keep. What it is / isn't: - It records only factual public values (a price, a numeric limit) plus a short source attribution and capture date. It does not reproduce vendor pages. - "silent" / "quiet" here means only that a change was not paired with a prominent announcement when it was recorded. It makes no claim about any vendor's intent. - Unofficial. Not affiliated with any vendor. The vendor's official page is always authoritative. No affiliate links, no "best tool" ranking. - Early stage: this is an initial snapshot; a daily automated capture is planned but not yet running. Repo: https://github.com/pricing-ledger-lab/ai-coding-tool-pricing-ledger Corrections welcome — open an issue with a source URL and the date you observed the value.
AI 资讯
I Built 31 Developer Tools Into a Single 133 KB HTML File — No Dependencies, No Backend
As a developer, I regularly find myself searching for small online utilities. Format some JSON. Decode a JWT. Test a regex. Generate a UUID. Convert a timestamp. Format an SQL query. Calculate a CIDR subnet. None of these tasks individually justify installing another application. But over time, I ended up with a collection of bookmarks to different websites, each solving one small problem. There was also another issue: privacy. Sometimes the data I'm working with isn't something I necessarily want to paste into an unknown third-party website. So I decided to build an alternative. The constraint: one HTML file I wanted the entire application to exist as a single file. No backend. No npm install. No CDN dependencies. No external API calls. No account. No telemetry. You download the HTML file, open it in a modern browser, and everything runs locally. The final file ended up at approximately 133 KB and contains 31 developer tools. What's inside? The tools are organized into six categories. Encode / Decode JSON Formatter & Validator Base64 Encode / Decode URL Encode / Decode HTML Escape / Unescape JWT Decoder Number Base Converter Generators UUID Generator Password Generator with entropy estimation Hash Generator Slug Generator Lorem Ipsum Generator Random Data Generator Converters Timestamp Converter CSV ↔ JSON JSON ↔ YAML CSS Unit Converter Case Converter Cron Expression Parser CSS Tools Color Picker & Converter Gradient Generator Box Shadow Generator Border Radius Generator CSS Filter Generator Text Tools Regex Tester Markdown Preview Text Diff Character Counter Text Sort & Dedupe String Inspector SQL Formatter Network Tools IPv4 Address and CIDR/Subnet Calculator Everything happens inside the browser. Making a single HTML file feel like an application I didn't want the result to feel like 31 unrelated forms dumped onto one page. So I added some application-level functionality. There's a Ctrl+K / Cmd+K command palette for quickly jumping between tools. The sidebar org
AI 资讯
The Start of My RAGgedy Journey
Big Howdy, I'm so tired of hearing all the hype about AI. Don't get me wrong, I think AI is pretty cool and I use it all the time for various tasks throughout the day, but I hate AI marketing. What do I mean by that? Whenever there is any hype, advertising, or corporate shilling about AI, it's always vague. Talks about agents, multi-agent systems, RAG pipelines, etc. are everywhere, and yet the specifics are always just out of reach. Even when I try to dig into deeper technical articles, the handwaving usually begins almost immediately. Several architecture diagrams and acronyms later, I still do not have a much better idea of what is actually happening (although I'm not really looking hard enough). I’m going to fix that. For myself, at least. First Stop: RAG My first target is RAG. It's been out for quite a while now and I've been hearing about it for forever. It also sounds like enough progress has been made that the problem has effectively been solved. Best practices have been established and it's pretty commonplace now, so it's a great place to start. But what is RAG? RAG stands for Retrieval-Augmented Generation , which is... not a helpful name if you are just learning about this for the first time like I am. However, if you use AI at all, you've already been using a form of it without even knowing it. RAG is simply a pattern: Retrieve relevant external information. Add that information to the AI model’s working context. Generate an answer grounded in it. That's it. When you use an AI assistant and it goes and searches the internet for relevant information to give you an answer, that's a form of RAG. That doesn't mean your AI assistant is a RAG system, but it can perform RAG functions. But this STILL sounds a bit hand-wavy to me. How does this work in real life? The Part I Kept Missing Context windows are massive these days, but I have seen technical documents that are literally more than 3,000 pages long and full of dense technical information. Even if one tec
AI 资讯
How I Built a Block Puzzle Game with React Native and Expo
How I Built a Block Puzzle Game with React Native and Expo A few weeks ago, I launched my first mobile game — a block puzzle called Blockbeam. It's an 8x8 grid where you drag colorful blocks, fill rows and columns, and chase high scores. Simple concept, but building it taught me a lot about React Native's capabilities beyond typical CRUD apps. Here's what I learned. Why React Native for Games? Most mobile games are built with Unity or native code. But for a 2D puzzle game, React Native is surprisingly capable. The game doesn't need 60fps 3D rendering — it needs gesture handling, state management, and smooth animations. React Native handles all three well. The key stack: Expo SDK 54 — managed workflow, over-the-air updates, zero native config react-native-reanimated — 60fps drag animations react-native-gesture-handler — PanResponder for drag-and-drop react-native-svg — block rendering AsyncStorage — game state persistence The Puzzle Engine The core of any block puzzle is the board state. I used a flat 64-element array for the 8x8 grid: const BOARD = 8 ; const CELLS = BOARD * BOARD ; // 64 type Board = number []; // 0 = empty, 1-7 = block colors Piece placement is straightforward: check if all target cells are empty, fill them, then scan for complete rows and columns to clear. The interesting part was the "greedy solver" I built for the auto-play bot. It tries every piece in every position and picks the move that clears the most lines: for ( const piece of tray ) { for ( let r = 0 ; r <= BOARD - piece . h ; r ++ ) { for ( let c = 0 ; c <= BOARD - piece . w ; c ++ ) { if ( ! canPlace ( board , piece , r , c )) continue ; const score = simulateClear ( board , piece , r , c ); if ( score > bestScore ) { /* pick this move */ } } } } Drag and Drop with PanResponder The trickiest part was the drag mechanic. Each tray piece has a PanResponder that tracks touch position and renders a floating ghost. On release, it calculates the nearest cell position: const anchor = { col : M
开发者
Swift Classes — Everything You Knew About Structs Just Got More Complicated
Okay so. We need to talk about classes. If you've been following along with this series, you're...
AI 资讯
Every AI-built site looks the same, so I built a skill that locks taste before any code is written
I build a lot of side projects with AI coding tools. Mostly Claude Code. A few months ago I noticed something that kept bugging me. Every site I shipped looked the same. Same indigo gradient. Same default font. Same rounded cards with the same soft shadow. Then I started noticing it on other people's projects too. Demo days, launch posts, screenshots on social media. The indigo gradient is everywhere. It is basically a uniform at this point. This is not really the AI's fault. When you do not give a model a design direction, it picks the average of the internet. And the average of the internet is a Tailwind starter template with an indigo gradient and Inter. The model is doing exactly what it was trained to do. The problem is that nobody told it what you actually want. So I built tastemaker. It is a skill for Claude Code, and it also works with Cursor, Windsurf, and Codex. The idea is simple: lock the design system before the AI writes a single line of UI. Full disclosure before we go further: I built this. I am a solo builder. It is free and open source under the MIT license. I am posting it here because I want honest feedback, not because I have anything to sell you. There is nothing to buy. Taste first, code second A skill is just a folder of instructions the AI reads before it starts working. tastemaker forces a design decision step at the beginning, with real constraints, instead of letting the model improvise the look as it goes. When you start a UI project with it installed, this is what gets locked before any code exists: A palette. 5 presets, each matched to a mood. Not random hex codes. Combinations that were picked to work together. Fonts. 24 curated Google Font pairings. A display font and a body font that actually belong on the same page. Real assets. Illustrator-grade illustrations, recolored to your palette. No gray placeholder boxes. No generic stock photo energy. A logo. A constructed geometric mark, plus a full favicon set, so the tab icon is not th
AI 资讯
We made our security auditor buyable by AI agents (x402, one serverless function)
Last night we made our Supabase security auditor buyable by AI agents. One HTTP request, a USDC payment attached to a header, and the product comes back in the response body. No checkout page, no account, no human. Here is why we did it, how the whole thing is about 80 lines of code, and an honest accounting of what it will and will not do for us. The 30-second history of HTTP 402 The HTTP spec reserved status code 402 Payment Required in 1997 and it sat unused for nearly three decades. In 2025 Coinbase published x402, an open protocol that finally gives it a job: a server answers a request with 402 plus machine-readable payment requirements, the client attaches a signed stablecoin payment to a header, retries, and gets the resource. Settlement happens on-chain (USDC on Base) in one round trip. Visa's Intelligent Commerce integrated it this spring. It is not a concept; it is running infrastructure. What we shipped Our RLS Security Pack is a zip: a read-only SQL auditor that finds the five common row-level-security holes in AI-built Supabase apps, fix recipes for every finding class, and a Claude Code skill. Humans buy it on Gumroad. Now an agent can buy it like this: # ask for the product curl -i https://ticassociation.com/api/agent/rls-pack # the server answers 402 with the exact terms: # {"x402Version":1,"accepts":[{"scheme":"exact","network":"base", # "asset":"...USDC...","payTo":"0x...","maxAmountRequired":"...", ...}]} # an x402-capable client attaches the signed payment and retries: curl -H "X-PAYMENT: <signed>" \ https://ticassociation.com/api/agent/rls-pack -o pack.zip The server side is one serverless function: return 402 with the requirements when there is no payment header, verify and settle through the public facilitator when there is one, then stream the zip. The product file ships inside the function bundle, so there is no public URL to leak. The whole thing took an evening, and most of that was reading the spec. Why a tiny company bothered Three hones
AI 资讯
The Economics of Self-Hosting vs. Managed Monitoring
The "Obvious" Math That's Wrong Engineer A: "Datadog is $15K/month. Prometheus is free. We should self-host." Engineer B: "But we'd need to pay an SRE to run it. That's $150K/year." Engineer A: "Prometheus doesn't need a full SRE. It's easy." Engineer B: "Famous last words." This conversation happens at every company. Both sides have points. The real math is more complex. The Total Cost Breakdown Managed (Datadog, New Relic, Dynatrace) : Licensing: $X/month (scales with hosts, events, logs) Integration time: 1-2 weeks per service Training: 1 day per new hire Ongoing: minimal Self-hosted (Prometheus + Grafana + Loki + Alertmanager) : Infrastructure: hosting costs (~$500-$5000/month depending on scale) Initial setup: 2-4 weeks of engineering time Ongoing maintenance: 10-20% of 1 FTE Upgrade costs: quarterly, each upgrade ~1 week Storage growth: ~20% per year Expertise: junior → senior SRE hire required The honest answer: managed is cheaper for teams under 50 engineers. Self-hosted becomes cheaper around 200+ engineers if you can run it well . The Real Variables It's not just licensing cost vs. hosting cost. These factors matter more: 1. Data volume growth Managed tools charge per GB ingested or per metric. If your logs 10x, your bill 10x's. Self-hosted scales linearly with compute. You control the growth. 2. Retention requirements Managed tools often charge extra for long retention. Self-hosted you store as much as your disk allows. 3. Cardinality Prometheus dies at high cardinality. Datadog handles it but charges more. High-cardinality metrics are where self-hosted breaks. 4. Incident rate Heavy incident load means heavy query load on your monitoring tools. Self-hosted needs bigger compute for this. 5. Team expertise If your team has never run Prometheus, you'll spend 6 months in the pit learning cardinality mistakes, retention tuning, and HA setups. That's not free. The Break-Even Calculation Rough calculation for a 50-engineer startup: Managed (Datadog) : - Licensi
AI 资讯
Code Review, Part 2: The Reviewer That Learned To Lie Better
Several posts ago, I wrote about setting up a multi-agent adversarial code review process as part of my development pipeline. The premise came from a podcast: if one frontier model is writing the code, you want a different lineage model doing the review. I'd already been running an informal version of this: just Claude Code reviewing Claude Code with an adversarial prompt. It had shockingly good luck catching real problems. Good enough that I stopped trusting the vibe and decided to go get actual data. So here's what I set up. Claude Code wrote the PRs. Every PR got reviewed automatically by 2 reviewers running in parallel through GitHub Actions: Claude Code with an adversarial prompt and Gemini with an adversarial prompt. I read everything myself. Then the same Claude Code agent that had written most of the PRs pulled both reviewers' feedback locally and distilled it into a scored ledger, PR by PR, for 6 weeks. Wiring Claude Code to review PRs through a GitHub Action was trivial. Wiring Gemini up the same way was not. Claude Code could not figure out how to get the Gemini CLI working inside a GitHub Action, and I ended up installing the Gemini CLI locally and having it perform the wiring. A couple of weeks into collecting data, I noticed Gemini's reviews were shallow. Not wrong, exactly. Thin. I started wondering whether Gemini actually had read access to the repository or whether it was only ever seeing the diff it was handed. I checked. It was the diff. Just the diff. Nothing but the diff. No file reads, no git history, nothing. And the thing that configured the GitHub Action in the first place was Gemini. It set up its own blindfold. I fixed it and Gemini's reviews got worse. Not louder or more frequent. Worse in a specific way: more confident. Before the fix, a blind Gemini would correctly tell you that it couldn't verify something and to check manually. That's an honest failure mode. After the fix, once it could actually read the code, it started fabricating.
AI 资讯
I Got Tired of AI Quiz Tools Making Up Facts That Weren't In My Notes, So I Built One That Can't
Two nights before a chemistry final, I pasted my notes into an AI quiz generator to test myself. One question asked about a reaction I never studied. I got it wrong, looked it up afterward, and it wasn't in my notes at all. The tool had just made it up. I went looking for a better one and hit the same wall three more times with three different tools. Paste your notes, get a quiz, and somewhere in the output is a question built on a fact your source material never mentioned. Nobody flags it. You just find out when you're wrong about something you were sure you'd studied. The failure mode makes sense once you think about how these tools are built. Most of them prompt an LLM with something like "generate 10 quiz questions from this text" and print whatever comes back. The model is good at sounding right. It is not naturally good at staying inside the boundary of what you actually gave it, and a prompt that says "don't hallucinate" is a request, not a constraint. The model can ignore it and you'd never know from the output alone. So I built QuizPaste around a different idea: don't ask the model to be honest, check it. When it generates a question, it has to also point at the sentence in your source text the question came from. Before that question ever gets shown to you, the code tries to actually locate that sentence in your original text. If it can't find it (wrong wording, a made-up detail, or the line just isn't there), the question gets thrown out silently and never reaches you. You only ever see questions the tool can prove came from your own material. Open any question and you can see the exact line highlighted. Using it is the boring part, on purpose. Paste lecture notes or a block of text, or grab a YouTube video's transcript (open the video, three dots, "Show transcript," copy the panel text, paste it in). There's no scraping involved, so it doesn't break when YouTube changes something on their end. You get a practice quiz plus flashcards in about five seconds
AI 资讯
When Green Browser Tests Lie: Environment Drift, CI Noise, and Hidden Runtime Failures
A browser test can be green and still be wrong. It can pass because a mock returned an outdated response. It can fail because staging enabled a feature flag that no one documented. It can become flaky after a React upgrade even though the user-facing behavior looks unchanged. And when the same failure appears only in a minified build, the stack trace may be so unhelpful that the team blames the test before investigating the application. These problems look unrelated, but they usually share one root cause: the test is running against a different system than the one the team thinks it is testing . The difference may be configuration, data, rendering behavior, build output, infrastructure, or timing. Reliable browser testing therefore requires more than stable selectors. It requires evidence that the environment, application state, and execution path are what you expect. Feature flags create multiple versions of the same application Feature flags are useful because they let teams release functionality gradually. They are also one of the easiest ways to create staging-only failures. A test written against the default interface may encounter a completely different component tree when a flag is enabled. A button can move into a menu, a form can become a wizard, or an API request can be delayed until the user completes an additional step. The difficult part is that the URL may remain identical. From the test runner's perspective, it is visiting the same page. From the application's perspective, it is executing a different product variant. A useful starting point is this breakdown of why browser tests fail only in staging when feature flags change runtime UI state . For important workflows, record the active flag state with every run. Do not limit the log to a generic environment name such as staging . Capture the actual configuration that influenced the UI. A failed run should answer questions such as: Which flags were active? Which account or cohort received them? Did the
AI 资讯
How to Test AI-Powered Web Apps Without Treating the Model Like a Normal API
AI-powered web applications look familiar on the surface. They have text boxes, buttons, menus, loading indicators, and API calls. That makes it tempting to test them like any other web application: submit an input, wait for a response, and compare the output with an expected string. That approach breaks quickly. Model output is variable. Safety behavior depends on context. A response can be semantically correct but displayed in the wrong conversation. An agent can produce a convincing final message after calling the wrong tool. A prompt-injection defense can block obvious attacks while failing when malicious instructions arrive through a webpage, document, image, or previous message. Testing these applications requires two kinds of evidence at the same time: Deterministic product evidence: the UI, state, permissions, tool calls, and workflow behaved correctly. Probabilistic model evidence: the output stayed within an acceptable range across repeated and adversarial inputs. Prompt injection is a workflow problem Prompt injection testing is often reduced to pasting “ignore previous instructions” into a chat box. That is a useful smoke test, but it does not represent how browser-based agents encounter untrusted content. An agent may read instructions from: A webpage. A support ticket. A PDF. A hidden DOM node. An email. A retrieved knowledge-base entry. A tool response. A previous conversation turn. The guide on testing prompt injection defenses in AI-powered browser workflows provides a good foundation. The test should verify more than the final sentence. It should inspect whether the agent: Treated external content as data rather than authority. Attempted a prohibited tool call. Exposed secrets in an intermediate step. Navigated to an unapproved domain. Changed its goal after reading untrusted content. Requested confirmation before a sensitive action. Preserved the original user instruction. A safe final answer does not prove that the workflow was safe. The agent ma
AI 资讯
Build a webhook-driven email pipeline for your AI agent
Most "AI email" tutorials end with a while True loop that polls an inbox every thirty seconds, runs the new messages through a model, and sends a reply. It demos fine. Then you put it in front of real traffic and the cracks show up immediately: you're burning API calls to fetch nothing 99% of the time, your reaction latency is bounded by your poll interval, and the moment you scale to more than one inbox the polling cost multiplies. Polling an agent's inbox is wasteful. Webhooks are the right primitive for this. The mailbox already knows the instant a message lands — there's no reason to keep asking. What you actually want is a pipeline: inbound mail fans out to a verified ingest endpoint, lands on a queue, and gets picked up by workers that drive your agent runtime and send the reply. This post is about that architecture end to end — not a single feature, but the whole flow, with the parts that bite you in production (idempotency, retries, ordering, backpressure) called out honestly. I work on the Nylas CLI, so the terminal commands below are the exact ones I reach for when I'm wiring this up. I'll show the curl HTTP call and the CLI equivalent for every concrete step, because you'll use both: curl in your provisioning scripts, the CLI when you're poking at a live account. The mental model: an Agent Account is just a grant Before any of the pipeline matters, here's the one abstraction that makes the whole thing simple. An Agent Account is a Nylas grant — it has a grant_id , an inbox, an email address on a domain you own, and it speaks every grant-scoped endpoint you already know: Messages, Threads, Folders, Drafts, Attachments, Calendars, Events, Contacts, Webhooks. There's no OAuth token to refresh, no provider-specific quirks, no separate SDK. If you've built against a connected Gmail or Microsoft grant before, the data plane is identical. Nothing new to learn there. What's different is that the agent is a participant. It has its own address — support@yourcompany
AI 资讯
Make your email agent idempotent against duplicate webhooks
Most posts about "AI email agents" stop at the happy path: webhook fires, model drafts a reply, agent sends it. Demo works, screenshot looks great, ship it. Then it goes to production and the agent replies to the same customer twice ninety seconds apart, and now your "intelligent assistant" looks like a broken cron job. That second reply isn't a bug in your model. It's a property of the delivery system, and it's guaranteed to happen eventually. Nylas webhooks are at-least-once: the same event can arrive up to three times. If your handler treats every POST as a fresh event, every retry is a second action. For a logging pipeline that's harmless. For an agent that sends email on your behalf , a duplicate delivery is a duplicate reply, and a double-reply embarrasses the agent in front of the exact person you built it to impress. So this post is about the engineering of idempotency itself, applied to an Agent Account. Not "remember to dedupe" hand-waving — the actual moving parts: which field is the real dedup key, how to persist processed ids atomically, why you ack before you work, how to make the send path itself idempotent, and where a per-thread lock catches the race that dedup alone can't. I work on the Nylas CLI, so every terminal command below is one I've actually run, verified against nylas v3.1.27. What an Agent Account changes (and what it doesn't) An Agent Account is just a grant. It has a grant_id and works with every grant-scoped endpoint — Messages, Drafts, Threads, Folders — exactly like a connected Gmail or Microsoft account. The difference is it's an inbox the agent owns : support@yourcompany.com is the agent, not a human whose inbox the agent borrows. Inbound mail to that address fires the standard message.created webhook, the agent reads it, and the agent replies from its own address. Nothing new to learn on the data plane. That's the whole point of the grant abstraction — the idempotency work below is plain webhook-handling discipline, and it transfe
AI 资讯
Connect legacy tools to an agent mailbox over IMAP/SMTP
Most "AI email" integrations assume everything on the other side speaks REST. You wire up a webhook, you call POST /messages/send , and you move on. That works right up until you remember how much of your stack doesn't speak REST and never will: the ticketing system that ingests mail over IMAP, the backup script your predecessor wrote in 2014, the monitoring tool that only knows how to send SMTP, the compliance archiver that polls a mailbox every five minutes. None of those are getting rewritten to call an HTTP API for your demo. So here's the trick that makes a Nylas Agent Account genuinely useful in a real environment: it's not API-only. You can expose the same mailbox over IMAP and SMTP submission , hand the host, port, and credentials to one of those legacy tools, and let it read and send like it's talking to any old mail server. Meanwhile your agent drives that identical mailbox over the v3 API. Both surfaces hit one storage layer. A flag, move, or delete on either side shows up on the other within seconds. I work on the Nylas CLI, so the terminal commands below are the exact ones I reach for. As usual I'll show both angles for every operation — the raw curl against the API and the nylas command — because half the point of an Agent Account is that you can mix them freely. What you actually get An Agent Account is just a grant . It has a grant_id , and that grant_id works with every grant-scoped endpoint you already know — Messages, Drafts, Threads, Folders, Attachments, Contacts, Calendars, Events. There's nothing new to learn on the data plane. The IMAP/SMTP layer doesn't change that model. It adds a second door into the same room: One mailbox, two protocols. The API and the IMAP/SMTP server are two front-ends over the same backend. There is no sync job, no eventual-consistency window worth worrying about, no "API mailbox" versus "client mailbox." It's one mailbox. Legacy tools just work. Anything that can authenticate to an IMAP server with a username and pas
AI 资讯
Installing Apache Kafka 4.2 on Ubuntu (WSL2): A Complete KRaft Step-by-Step Guide
Installing Apache Kafka 4.2 on Ubuntu 24.04 (WSL2) Using KRaft Mode: A Complete Step-by-Step Guide Learn how to install Apache Kafka 4.2 in KRaft mode, understand its architecture, create topics, produce and consume messages, and troubleshoot common configuration issues—all without ZooKeeper. 🚀 Introduction Apache Kafka has become the de facto standard for building event-driven , real-time , and high-throughput applications. Whether you're processing millions of financial transactions, collecting application logs, streaming IoT sensor data, or connecting microservices, Kafka provides a scalable and reliable messaging platform. Until recently, setting up Kafka required running Apache ZooKeeper alongside Kafka brokers. While powerful, ZooKeeper added operational complexity and introduced another distributed system that administrators had to manage. Beginning with recent Kafka releases, KRaft (Kafka Raft Metadata mode) removes this dependency by allowing Kafka to manage its own metadata internally. This makes installation simpler, reduces operational overhead, and improves scalability. In this guide, we'll install Apache Kafka 4.2 on Ubuntu 24.04.4 LTS (WSL2) , configure a single-node KRaft cluster, and walk through the complete lifecycle: Installing Kafka Understanding the Kafka architecture Configuring KRaft mode Starting the broker Creating topics Producing and consuming messages Troubleshooting common issues Understanding the purpose of each configuration parameter Rather than simply listing commands, I'll explain why each step is necessary so that you understand how Kafka works under the hood. What is Apache Kafka? Apache Kafka is a distributed event streaming platform designed to move data reliably and efficiently between applications. Instead of applications communicating directly with each other, they communicate through Kafka. A producing application writes messages to Kafka. Kafka stores those messages reliably. One or more consuming applications read those m
AI 资讯
Polling, SSE, or WebSockets for Mobile Upload Status?
After the browser transfers a file, the server may still scan, transcode, extract metadata, or generate previews. The interface needs status updates, but the most fashionable real-time transport is not automatically the most reliable choice for an event guest on a mobile browser. Start with the update contract Define states and transitions before choosing transport: accepted -> queued -> processing -> ready accepted -> rejected processing -> processing_error Every status response should include a monotonically increasing version or timestamp. The client can ignore events older than the state it already knows. Polling is a strong baseline HTTP polling works through proxies, resumes naturally after a page wake, and is easy to cache and rate-limit. Use adaptive intervals: one second just after acceptance, then back off to several seconds when processing takes longer. const delay = Math . min ( 8000 , 1000 * 2 ** slowChecks ); Add jitter when many guests finish at the same time. Stop when the state is terminal, the page is gone, or a server-provided retry time says to wait longer. SSE fits one-way progress Server-Sent Events are attractive when the server only pushes status. The browser reconnects with a last-event ID, and the wire format is simple. But mobile networks and some intermediaries silently kill idle connections. Send occasional heartbeats and treat reconnection as normal. The reconnect handler must fetch current state because events may have been missed beyond the server replay window. Do not keep one SSE stream per file. Subscribe by guest session or album and filter authorized upload IDs on the server. WebSockets add more responsibility WebSockets make sense when the client also sends frequent commands over the same channel or when a host moderation console needs many rapid updates. For a guest waiting on two files, they add connection authentication, heartbeat, reconnect, replay, and load-balancer complexity without much user benefit. Never rely on the so