Freshbooks Mcp

io.github.chrischall由 GitHub 帳號 chrischall 發布v1.3.1更新於 Nov 7, 2026

FreshBooks invoicing for Claude — invoices, clients, estimates, and payments

概覽

AI 產生的概覽

本機 MCP 伺服器,將 Claude 連上 FreshBooks,可瀏覽並在確認後建立發票、客戶、估價、付款、費用、專案與工時紀錄。

可以做什麼
  • 瀏覽與取得發票、客戶、估價、付款、型錄項目與費用,支援分頁與原生 FreshBooks 篩選參數
  • 建立與更新發票、客戶、估價與費用,登記付款,寄送或接受估價,每項寫入都需先確認
  • 列出與建立專案、記錄工時,並列出服務類型與費用類別
  • 透過通用列表/取得工具依名稱讀取其他各類會計資源
  • 解析已驗證身分及其帳戶、業務與 UUID 識別碼,並執行憑證健康檢查
  • 透過授權連結與交換工具完成 OAuth 登入
適用情境
適合讓助理查詢 FreshBooks 帳務資料,或在不開啟 FreshBooks 網頁的情況下草擬與登記發票、付款、費用與工時。最適合單一 FreshBooks 業務且你具備擁有者或管理員會計角色的情境。
執行需求
以 npm 套件形式在本機透過 stdio 執行(需 Node.js),僅支援桌面用戶端。需在 FreshBooks 開發者入口註冊 OAuth 應用程式,設定 FRESHBOOKS_CLIENT_ID 與 FRESHBOOKS_CLIENT_SECRET,並完成一次瀏覽器授權;FRESHBOOKS_REFRESH_TOKEN 在本機為選用,託管或舊式設定則為必需。選用:FRESHBOOKS_REDIRECT_URI、FRESHBOOKS_ACCOUNT_ID、FRESHBOOKS_BUSINESS_ID、FRESHBOOKS_TOKEN_STORE、FRESHBOOKS_REQUEST_TIMEOUT_MS。需能連線至 FreshBooks API。
安裝前請注意
會將 OAuth 憑證與會輪替的更新權杖存到本機儲存;儲存遺失只能重新引導,且兩個工具不可共用同一儲存。寫入類工具會建立與修改會計紀錄、登記付款,並可能寄送估價郵件給客戶;估價一旦接受無法透過 API 撤銷。寫入需要擁有者或管理員會計帳戶,否則會因權限失敗。確認行為取決於 MCP_CONFIRM_MODE 與 MCP_CONFIRM_ELICITATION。README 說明本專案由 AI 開發,使用需自行判斷。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Freshbooks Mcp,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

freshbooks-mcp

MCP server for FreshBooks — invoices, clients, estimates and payments, exposed to Claude as typed tools.

This project was developed and is maintained by AI (Claude Code). Use at your own discretion.

Install

sh
npm install -g @chrischall/freshbooks-mcp

Setup

FreshBooks is OAuth2 only — there is no API key and no personal access token, so a one-time browser authorization is required.

  1. Register an app at https://my.freshbooks.com/#/developer. The redirect URI must be HTTPS with no query string; https://localhost works and never needs to resolve.
  2. Note the Client ID and Client Secret.
  3. Complete the one-time authorization:
    • From the server itself (no script): set FRESHBOOKS_CLIENT_ID and FRESHBOOKS_CLIENT_SECRET, start it, then call freshbooks_auth_url, open the URL it returns, approve, and pass the whole URL you land on to freshbooks_auth_exchange. Its state must match a consent URL the server issued (valid 15 minutes, single-use), so a bare code, or a redirect URL from anyone else's authorization, is refused before its code is spent. Local sessions save the tokens directly to FRESHBOOKS_TOKEN_STORE and return only a success message. Restart the local MCP server after that message. No token needs to be copied into chat or configuration. Keep the same app credentials and store path after restart.
    • Hosted connections: mcp-host's authFlow still captures the refresh token from the exchange response and supplies it through its secret store. MCP_DATA_DIR identifies this hosted environment. Do not set it for a local coding-agent connection: hosted mode intentionally returns the token. Issued states are kept as digests under MCP_DATA_DIR, so a child that is respawned while the person approves can still complete the login. The registration's flow must show the person the authorize_url that freshbooks_auth_url returns. That URL carries the one-time state, and the exchange refuses any redirect without it. A static consent link typed into a step description has no state, so every new login or repair through it fails. Carry authorize_url from the freshbooks_auth_url step and show it with a step link (link: {from: 'authorize_url', hosts: ['auth.freshbooks.com']}, mcp-host 2.30.0 or later) on the step that prompts. That prompt must collect the whole redirect URL, not just its code. Make this change before a release with state checking reaches the hosted pin. Existing connections keep refreshing; only new logins and repairs depend on it.
    • Legacy/manual setup: the scripts in skills/freshbooks-curl print tokens. Run them only in a private terminal, never in an agent's captured terminal.
  4. Configure:
sh
FRESHBOOKS_CLIENT_ID=...FRESHBOOKS_CLIENT_SECRET=...FRESHBOOKS_REFRESH_TOKEN=...       # optional locally; if set during reauth, keep it setFRESHBOOKS_REDIRECT_URI=https://localhost   # optional; must match what you registeredFRESHBOOKS_TOKEN_STORE=~/.freshbooks-mcp/session.json   # optionalFRESHBOOKS_BUSINESS_ID=...         # optional; required for writes if you belong to several businessesFRESHBOOKS_ACCOUNT_ID=...          # optional; with FRESHBOOKS_BUSINESS_ID, confirms its accountId when FreshBooks omits itFRESHBOOKS_REQUEST_TIMEOUT_MS=30000  # optional; per-request deadline for API calls

In Claude Desktop, both are optional fields in the extension's settings. If FreshBooks returns no account_id for the chosen business, invoice/expense writes are refused (projects and time entries still work) until you set both IDs yourself.

⚠️ Refresh tokens rotate

FreshBooks issues a new refresh token on every refresh and immediately invalidates the old one. This server persists each rotation to FRESHBOOKS_TOKEN_STORE (mode 0600) before the refresh is considered complete, and prefers the stored token over the environment value — the stored one has rotated past it.

The login flow also writes a small file of pending consent state digests (mode 0600, entries expire after 15 minutes). Locally it sits beside the token store, at <FRESHBOOKS_TOKEN_STORE>.oauth-state.json (by default ~/.freshbooks-mcp/session.json.oauth-state.json). Hosted, it is $MCP_DATA_DIR/freshbooks-oauth-state.json. It is created on the first freshbooks_auth_url call, even if the login is never finished, and holds no tokens.

Two consequences worth knowing:

  • Do not point two tools at the same store. The MCP server and the freshbooks-curl skill keep separate state files on purpose; sharing one makes them spend each other's tokens and locks both out.
  • If the store is lost, re-run the bootstrap. A spent refresh token cannot be recovered.

Local sign-in without an environment token binds the private store to the app's client ID, secret, and redirect URI. Changing those requires a new sign-in. For existing environment-token setups, the stored rotation still takes precedence; changing FRESHBOOKS_REFRESH_TOKEN to a new value starts a new token chain. If you reauthorize locally while FRESHBOOKS_REFRESH_TOKEN is set, keep that same value configured: the saved login is bound to it. To remove the variable, first unset it and complete local sign-in again so the store is bound to the app instead. The local authorization exchange and save use the same lock as refreshes. A refresh writes only while it holds that lock, so it cannot overwrite a newer login after releasing the lock. Restart every local server using the store after reauthorization.

Tools

ToolPurpose
freshbooks_get_identityResolve accountId / businessId / businessUuid
freshbooks_auth_urlGet the consent URL to authorize this connection
freshbooks_auth_exchangeComplete authorization; save tokens privately in local sessions or return the refresh token to the hosted auth flow
freshbooks_healthcheckVerify the OAuth credential and FreshBooks reachability; distinguishes "no credential" from "rejected" from "FreshBooks is down"
freshbooks_list_invoices / freshbooks_get_invoiceBrowse and fetch invoices
freshbooks_list_clients / freshbooks_get_clientBrowse and fetch clients
freshbooks_list_estimates / freshbooks_get_estimateBrowse and fetch estimates
freshbooks_list_payments / freshbooks_get_paymentBrowse and fetch payments
freshbooks_list_items / freshbooks_get_itemBrowse and fetch catalogue items
freshbooks_create_clientCreate a client — confirmation-gated
freshbooks_create_invoiceCreate an invoice — confirmation-gated
freshbooks_update_invoiceUpdate an invoice — confirmation-gated
freshbooks_record_paymentRecord a payment against an invoice — confirmation-gated
freshbooks_accept_estimateAccept an estimate (action_accept) — confirmation-gated, idempotent
freshbooks_update_estimateUpdate an estimate's lines, notes, terms, presentation — confirmation-gated
freshbooks_send_estimateEmail an estimate to the client (action_email) — confirmation-gated
freshbooks_decline_estimateAlways fails: FreshBooks has no decline. Answers with the alternatives
freshbooks_list_expenses / freshbooks_get_expenseBrowse and fetch expenses
freshbooks_list_expense_categoriesCategories supplying categoryid for new expenses
freshbooks_create_expenseRecord an expense — confirmation-gated
freshbooks_list_projects / freshbooks_get_projectProjects (businessId-keyed)
freshbooks_create_projectCreate a project — confirmation-gated
freshbooks_list_time_entriesTracked time, with total_logged / total_unbilled
freshbooks_create_time_entryLog time in seconds — confirmation-gated
freshbooks_list_servicesBillable work types for projects and time entries
freshbooks_list_records / freshbooks_get_recordGeneric accessor for the accounting long tail (taxes, credit notes, invoice profiles, tasks, staff, gateways, bills, bill vendors, bill payments, other income)

Confirmation-gated means the tool asks you before it writes. A client that can show a confirmation prompt (Claude Code) shows one with exactly what would be sent. Elsewhere (claude.ai, Claude Desktop) the first call makes no network call and returns a preview of the method, path and body that would be sent, plus a single-use confirmToken; only a repeat call with that token and the same arguments performs the write. A token is refused if any argument changed since the preview (DRAFT_CHANGED), if it was already used (TOKEN_REUSED) or once it expires.

Confirmations

variabledefault
MCP_CONFIRM_MODEask-userWhat a write does on a client that cannot show a confirmation prompt (claude.ai, Claude Desktop). ask-user: two steps — the first call does nothing and returns a preview plus a token, and the model must get your approval in chat before calling again with it. auto: the same two steps, but the model may use the token after reviewing the preview itself. refuse: writes are refused on such clients. A client that can show prompts (Claude Code) always gets the real prompt, unless MCP_CONFIRM_ELICITATION=off. An unrecognised value is treated as refuse.
MCP_CONFIRM_ELICITATIONonoff never shows a confirmation prompt, so every client gets the MCP_CONFIRM_MODE behaviour. Set it for a client that says it can show prompts but never does (the write hangs — opencode 2.0.x). Any other value is treated as on, with a warning on stderr.
MCP_CONFIRM_TTL_SECONDS600How long a token stays valid.
MCP_CONFIRM_SECRETrandom per processSigning key; set it only if tokens must survive a server restart.

Estimate writes

Acceptance is an action on the estimate, not a status field: status (int), display_status and ui_status are computed and read-only, and they disagree with each other by design (a viewed estimate reads status: 3, display_status: "viewed", ui_status: "open"). Accepting is PUT estimates/estimates/{id} with {"estimate": {"action_accept": true}} — see docs/FRESHBOOKS-API.md for where that shape comes from.

  • Accept is idempotent. An estimate already accepted (or invoiced) comes back with changed: false and no write is sent — acceptance cannot be undone through the API, so a repeat call must not re-fire it.
  • There is no decline. FreshBooks' estimate statuses are draft / sent / viewed / replied / accepted / invoiced; no declined state, no action_deny, no estimate.decline webhook. freshbooks_decline_estimate exists only to say so and point at the alternatives, rather than leave an agent to invent a write that changes nothing.
  • Every write returns the re-fetched estimate, plus before / after state and changed / changedFields, so success is verified against the record rather than inferred from a 200. changed covers the status fields and the fields that write actually set, so a successful notes edit reports changed: true even though no status moves. On freshbooks_send_estimate it describes the record only — emailing an already-sent estimate moves nothing, and retrying on changed: false would send the client a second copy.

Writes require an owner/admin accounting account

FreshBooks separates the role you hold on a business from the role you hold on an accounting account. You can own a business that has no accounting account (account_id: null) while being only a client on the account you can actually see — in which case reads succeed and every write returns 403 Permission Denied, even though your OAuth token carries all the :write scopes.

freshbooks_get_identity reports accountRole and businessRole so this is visible up front. If accountRole is client, the invoicing write tools will not work against that account — that is an account permission, not a configuration problem.

Two things the API reports misleadingly

  • total counts records you may not be able to read. Expenses reported total: 16 while returning zero rows. List results attach a note when that happens, so it reads as a permission boundary rather than an empty account.
  • Projects and time tracking are keyed by businessId, not accountId, and paginate under a meta block instead of flat page/pages/total. They also work on a business with no accounting account at all.

The three identifiers

FreshBooks hands out three non-interchangeable ids, and using the wrong one returns a bare 404 that reads like a missing record:

IdentifierUsed by
accountId (alphanumeric)/accounting/account/…, /payments/account/…
businessId (integer)/projects/business/…, /timetracking/business/…
businessUuid (UUID)/accounting/businesses/…

Call freshbooks_get_identity first. Full API notes, including the four different error envelopes, are in docs/FRESHBOOKS-API.md.

Shell access without the server

skills/freshbooks-curl covers the same API from a shell with curl + jq, including the OAuth bootstrap and rotation-safe token handling.

Development

sh
npm installnpm run buildnpm test

License

MIT

來源:README.md,提交 5a5dbf2

工具

34

freshbooks_auth_url

Get the FreshBooks consent URL to authorize this connection. Open it, approve, and you'll land on the redirect URL — pass that whole URL to freshbooks_auth_exchange (its state parameter binds the approval to this server; valid 15 minutes, single-use). Read-only; contacts nothing.

無需輸入參數。

freshbooks_auth_exchange

Complete FreshBooks authorization. Local sessions save the credentials privately and return only status; hosted sessions return the refresh token to the host's credential-capture flow. Pass the whole redirect URL: its state must match a consent URL freshbooks_auth_url issued, so a bare code is refused. The code is SINGLE-USE — if this fails, authorize again rather than retrying it.

輸入參數code必填

freshbooks_get_identity

Resolve the authenticated FreshBooks user and their identifiers: accountId (alphanumeric, used by accounting and payments endpoints), businessId (integer, used by projects and time tracking) and businessUuid. The three are not interchangeable — using the wrong one returns a 404 rather than a useful error. When the identity belongs to several businesses, `businesses` lists them all; unless FRESHBOOKS_BUSINESS_ID picks one, writes are refused.

無需輸入參數。

freshbooks_healthcheck

Verify credentials and upstream reachability

Resolves the credential the way real tools do, then makes one authenticated request to api.freshbooks.com. Reports which source supplied the credential, whether api.freshbooks.com accepted it, the round-trip time, and a plain-English hint distinguishing 'no credential' from 'credential rejected' from 'a api.freshbooks.com-side problem'. Read-only; never returns the credential itself. Call this when a real tool fails and you want to know which hop broke.

無需輸入參數。

freshbooks_list_invoices

List invoices for the authenticated FreshBooks account — what clients owe, with each invoice's due date and status. Supports pagination and raw FreshBooks filter params. Returns items plus page/pages/total; view "brief" returns one line per invoice instead.

輸入參數pageviewsearchper_page

freshbooks_get_invoice

Get a single invoice by its numeric FreshBooks id.

輸入參數id必填

freshbooks_list_clients

List clients for the authenticated FreshBooks account. Supports pagination and raw FreshBooks filter params. Returns items plus page/pages/total.

輸入參數pagesearchper_page

freshbooks_get_client

Get a single client by its numeric FreshBooks id.

輸入參數id必填

freshbooks_list_estimates

List estimates for the authenticated FreshBooks account. Supports pagination and raw FreshBooks filter params. Returns items plus page/pages/total.

輸入參數pagesearchper_page

freshbooks_get_estimate

Get a single estimate by its numeric FreshBooks id.

輸入參數id必填

freshbooks_list_payments

List payments for the authenticated FreshBooks account. Supports pagination and raw FreshBooks filter params. Returns items plus page/pages/total.

輸入參數pagesearchper_page

freshbooks_get_payment

Get a single payment by its numeric FreshBooks id.

輸入參數id必填

freshbooks_list_items

List items for the authenticated FreshBooks account. Supports pagination and raw FreshBooks filter params. Returns items plus page/pages/total.

輸入參數pagesearchper_page

freshbooks_get_item

Get a single item by its numeric FreshBooks id.

輸入參數id必填

freshbooks_create_client

Create a client (customer) in FreshBooks. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call returns a preview and a confirmToken and makes no network call, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).

輸入參數emailfnamelnamefieldsconfirmTokenorganizationcurrency_code

freshbooks_create_invoice

Create an invoice for a client. Created invoices start as drafts. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call returns a preview and a confirmToken and makes no network call, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).

輸入參數linesnotesfieldscustomerid必填create_dateconfirmTokencurrency_codedue_offset_daysallow_non_client_recipients

freshbooks_update_invoice

Update an existing invoice. Only the supplied fields are sent. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call returns a preview and a confirmToken and makes no network call, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE). Note that changing an invoice out of draft can email it to the client. email_recipients in fields must be addresses on the invoice's client record unless allow_non_client_recipients is set, which is only for addresses the user named themselves.

輸入參數id必填fields必填confirmTokenallow_non_client_recipients

freshbooks_record_payment

Record a payment against an invoice. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call returns a preview and a confirmToken and makes no network call, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).

輸入參數datenotetypeamount必填invoiceid必填confirmToken

freshbooks_accept_estimate

Accept an estimate on behalf of the account, by sending FreshBooks' action_accept on the estimate (PUT estimates/estimates/{id} with {"estimate": {"action_accept": true}}). Acceptance is not reversible through the API — there is no un-accept action. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call returns a preview and a confirmToken and makes no network call, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE). Idempotent: an estimate already accepted or invoiced is returned unchanged with changed: false and no write is sent. Returns the re-fetched estimate.

輸入參數id必填confirmToken

freshbooks_decline_estimate

NOT SUPPORTED by the FreshBooks API — this tool always fails, and never sends a request. FreshBooks models no declined state for estimates: the status set is draft / sent / viewed / replied / accepted / invoiced, there is no action_deny or action_decline counterpart to action_accept in FreshBooks' own API collection, and there is no estimate.decline webhook event. The tool exists so this answers with the reason instead of a plausible-looking write that changes nothing. Call it to get the alternatives.

輸入參數id必填

freshbooks_update_estimate

Update an existing estimate. Only the supplied fields are sent; anything omitted is left alone. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call returns a preview and a confirmToken and makes no network call, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE). Supplying lines REPLACES the whole line set — include each existing line's lineid to keep it. Returns the re-fetched estimate.

輸入參數id必填linesnotestermsfieldspo_numbercustomeridcreate_datedescriptionconfirmTokenpresentationcurrency_code另有 2 個

freshbooks_send_estimate

Email an estimate to the client, by sending FreshBooks' action_email on the estimate (PUT estimates/estimates/{id} with {"estimate": {"action_email": true, …}}). This puts mail in a client's inbox. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call returns a preview and a confirmToken and makes no network call, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE). Omitting email_recipients lets FreshBooks use the client's own address. email_recipients must be addresses on the estimate's client record unless allow_non_client_recipients is set, which is only for addresses the user named themselves. Returns the re-fetched estimate.

輸入參數id必填bodysubjectconfirmTokenemail_recipientsallow_non_client_recipients

freshbooks_list_expenses

List expenses for the account, newest-first by default. Supports pagination and raw FreshBooks filters (e.g. {"search[categoryid]": 5}). If the response reports a total with no rows, the count includes records this identity cannot read — that is reported in the `note` field rather than looking like an empty account.

輸入參數pagesearchper_page

freshbooks_get_expense

Get a single expense by its numeric id.

輸入參數id必填

freshbooks_list_expense_categories

List expense categories, which supply the categoryid used when creating an expense.

輸入參數pageper_page

freshbooks_create_expense

Record an expense. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call returns a preview and a confirmToken and makes no network call, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).

輸入參數datenotesamount必填fieldsvendorbillableclientidcategoryidconfirmToken

freshbooks_list_projects

List projects for the business. Uses businessId (not accountId) and works even when the business has no accounting account.

輸入參數pageper_page

freshbooks_get_project

Get a single project by its numeric id.

輸入參數id必填

freshbooks_create_project

Create a project. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call returns a preview and a confirmToken and makes no network call, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).

輸入參數ratetitle必填fieldsdue_dateclient_iddescriptionfixed_priceconfirmTokenproject_type

freshbooks_list_time_entries

List tracked time entries for the business. The response envelope also carries total_logged and total_unbilled alongside the rows.

輸入參數pageper_page

freshbooks_create_time_entry

Log a time entry. Duration is in SECONDS. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call returns a preview and a confirmToken and makes no network call, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).

輸入參數notebillableduration必填client_idproject_idservice_idstarted_at必填confirmToken

freshbooks_list_services

List services (the billable work types available to projects and time entries).

輸入參數pageper_page

freshbooks_list_records

List any FreshBooks accounting resource by name — the generic reader covering the long tail (taxes, credit notes, invoice profiles, tasks, staff, gateways, bills, bill vendors, bill payments, other income, expense categories) alongside the ones with dedicated tools. Returns items plus page/pages/total. Some resources are gated by plan or account role and will report that rather than returning rows.

輸入參數pagesearchper_pageresource必填

freshbooks_get_record

Get a single record from any FreshBooks accounting resource by name and id. Ids are numeric on every resource mapped here; the schema also accepts a string so an id carried around as text still works.

輸入參數id必填resource必填

版本歷史

15
  1. v1.3.1最新Nov 4, 2026
  2. v1.2.2Oct 16, 2026
  3. v1.2.1Oct 10, 2026
  4. v1.2.0Oct 7, 2026
  5. v1.1.5Oct 5, 2026
  6. v1.1.4Oct 3, 2026
  7. v1.1.3Sep 30, 2026
  8. v1.1.2Sep 30, 2026
  9. v1.1.1Sep 25, 2026
  10. v1.1.0Sep 24, 2026
  11. v1.0.2Sep 23, 2026
  12. v1.0.1Sep 23, 2026
  13. v1.0.0Sep 20, 2026
  14. v0.7.0Sep 18, 2026
  15. v0.6.1Sep 16, 2026

評分

已滿足適用檢查項的 90%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分
  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具必需15 分
  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分